驱动DAY3

通过ioctl函数选择不同硬件的控制,LED 蜂鸣器 马达 风扇

  • test.c
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include "head.h"

int main(int argc, char const *argv[])
{
    int a, b;
    int led_fd = open("/dev/led0", O_RDWR);
    int motor_fd = open("/dev/motor", O_RDWR);
    int buzzer_fd = open("/dev/buzzer", O_RDWR);
    int fan_fd = open("/dev/fan", O_RDWR);
    if (led_fd < 0 || motor_fd < 0 || buzzer_fd < 0 || fan_fd < 0)
    {
        printf("打开设备文件失败\n");
        exit(-1);
    }
    printf("打开设备文件成功\n");


    while (1)
    {
    LOOP:
        printf("请选择设备(0(Quit)  1(Led1)  2(Led2)  3(Led3)  4(马达)  5(蜂鸣器)  6(风扇)  7(所有的灯)  8(所有设备)\n ");
        printf("请选择:");
        scanf("%d", &a);
        if (a < 0 || a > 8)
        {
            printf("输入错误,请重新输入!!!\n");
            goto LOOP;
        }
        getchar();
        if (a == 0)
        {
            close(led_fd);
            close(motor_fd);
            close(buzzer_fd);
            close(fan_fd);
            return 0;
        }
        printf("请选择设备的状态 0(关)  1(开)\n");
        printf("请选择:");
        scanf("%d", &b);
        if (b != 1 && b != 0)
        {
            printf("输入错误,请重新输入!!!\n");
            goto LOOP;
        }
        getchar();

        switch (b)
        {
        case 0:
            switch (a)
            {
            case 1:
                ioctl(led_fd, LED1_OFF, &a);
                break;
            case 2:
                ioctl(led_fd, LED2_OFF, &a);
                break;
            case 3:
                ioctl(led_fd, LED3_OFF, &a);
                break;
            case 4:
                ioctl(motor_fd, MOTOR_OFF, &a);
                break;
            case 5:
                ioctl(buzzer_fd, BUZZER_OFF, &a);
                break;
            case 6:
                ioctl(fan_fd, FAN_OFF, &a);
                break;
            case 7:
                ioctl(led_fd, LED1_OFF, &a);
                ioctl(led_fd, LED2_OFF, &a);
                ioctl(led_fd, LED3_OFF, &a);
                break;
            case 8:
                ioctl(led_fd, LED1_OFF, &a);
                ioctl(led_fd, LED2_OFF, &a);
                ioctl(led_fd, LED3_OFF, &a);
                ioctl(motor_fd, MOTOR_OFF, &a);
                ioctl(buzzer_fd, BUZZER_OFF, &a);
                ioctl(fan_fd, FAN_OFF, &a);
                break;
            }
            break;
        case 1:
            switch (a)
            {
            case 1:
                ioctl(led_fd, LED1_ON, &a);
                break;
            case 2:
                ioctl(led_fd, LED2_ON, &a);
                break;
            case 3:
                ioctl(led_fd, LED3_ON, &a);
                break;
            case 4:
                ioctl(motor_fd, MOTOR_ON, &a);
                break;
            case 5:
                ioctl(buzzer_fd, BUZZER_ON, &a);
                break;
            case 6:
                ioctl(fan_fd, FAN_ON, &a);
                break;
            case 7:
                ioctl(led_fd, LED1_ON, &a);
                ioctl(led_fd, LED2_ON, &a);
                ioctl(led_fd, LED3_ON, &a);
                break;
            case 8:
                ioctl(led_fd, LED1_ON, &a);
                ioctl(led_fd, LED2_ON, &a);
                ioctl(led_fd, LED3_ON, &a);
                ioctl(motor_fd, MOTOR_ON, &a);
                ioctl(buzzer_fd, BUZZER_ON, &a);
                ioctl(fan_fd, FAN_ON, &a);
                break;
            }
            break;
        default:
            break;
        }
    }
    close(led_fd);
    close(motor_fd);
    close(buzzer_fd);
    close(fan_fd);
    return 0;
}
  • head.h
#ifndef __HEAD_H__
#define __HEAD_H__ 
typedef struct{
    unsigned int MODER;
    unsigned int OTYPER;
    unsigned int OSPEEDR;
    unsigned int PUPDR;
    unsigned int IDR;
    unsigned int ODR;
    unsigned int BSRR;
    unsigned int LCKR;
    unsigned int AFRL;
    unsigned int AFRH;
    unsigned int BRR;
    unsigned int RES;
    unsigned int SECCFGR;
}gpio_t;

#define LED1_ADDR   0X50006000
#define LED2_ADDR   0X50007000
#define BUZZER_ADDR 0X50003000
#define RCC_ADDR    0X50000A28

//开
#define LED1_ON _IOW('l',1,int)
#define LED2_ON _IOW('y',1,int)
#define LED3_ON _IOW('z',1,int)
#define MOTOR_ON _IOW('m',1,int)
#define BUZZER_ON _IOW('b',1,int)
#define FAN_ON _IOW('f',1,int)
//关
#define LED1_OFF _IOW('l',0,int)
#define LED2_OFF _IOW('y',0,int)
#define LED3_OFF _IOW('z',0,int)
#define MOTOR_OFF _IOW('m',0,int)
#define BUZZER_OFF _IOW('b',0,int)
#define FAN_OFF _IOW('f',0,int)

#endif 
  • mychrdev.c
#include 
#include 
#include 
#include 
#include 
#include 
#include "head.h"

struct class *cls;
struct class *cls1;
struct class *cls2;
struct class *cls3;
struct device *dev;
int major, major1, major2, major3; // 用于保存主设备号

gpio_t *vir_led1;
gpio_t *vir_led2;
gpio_t *vir_led3;
gpio_t *vir_motor;
gpio_t *vir_buzzer;
gpio_t *vir_fan;
unsigned int *vir_rcc;

// 封装操作方法
int mycdev_open(struct inode *inode, struct file *file)
{
    printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
    return 0;
}

long mycdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
    int which;
    // 通过拷贝函数获取ioctl第三个参数对应空间的数值
    int ret = copy_from_user(&which, (void *)arg, 4);
    if (ret)
    {
        printk("拷贝用户空间数据失败\n");
        return -EIO;
    }
    // 进行功能控制
    switch (cmd)
    {
    // 关
    case LED1_OFF: // LED1
        vir_led1->ODR &= (~(1 << 10));
        break;
    case LED2_OFF: // LED2
        vir_led2->ODR &= (~(1 << 10));
        break;
    case LED3_OFF: // LED3
        vir_led3->ODR &= (~(1 << 8));
        break;
    case MOTOR_OFF: // 马达
        vir_motor->ODR &= (~(1 << 6));
        break;
    case BUZZER_OFF: // 蜂鸣器
        vir_buzzer->ODR &= (~(1 << 6));
        break;
    case FAN_OFF: // 风扇
        vir_fan->ODR &= (~(1 << 9));
        break;
    // 开
    case LED1_ON:
        vir_led1->ODR |= (1 << 10);
        break;
    case LED2_ON:
        vir_led2->ODR |= (1 << 10);
        break;
    case LED3_ON:
        vir_led3->ODR |= (1 << 8);
        break;
    case MOTOR_ON:
        vir_motor->ODR |= (1 << 6);
        break;
    case BUZZER_ON:
        vir_buzzer->ODR |= (1 << 6);
        break;
    case FAN_ON:
        vir_fan->ODR |= (1 << 9);
        break;
    }
    return 0;
}

int mycdev_close(struct inode *inode, struct file *file)
{
    printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
    return 0;
}
// 定义操作方法结构体变量并完成初始化
struct file_operations fops =
    {
        .open = mycdev_open,
        .unlocked_ioctl = mycdev_ioctl,
        .release = mycdev_close,
};

static int __init mycdev_init(void)
{
    int i;
    // 字符设备驱动注册
    major = register_chrdev(0, "led", &fops);
    major1 = register_chrdev(0, "motor", &fops);
    major2 = register_chrdev(0, "buzzer", &fops);
    major3 = register_chrdev(0, "fan", &fops);
    if (major < 0)
    {
        printk("led设备驱动注册失败\n");
        return major;
    }
    if (major1 < 0)
    {
        printk("马达设备驱动注册失败\n");
        return major1;
    }
    if (major2 < 0)
    {
        printk("蜂鸣器设备驱动注册失败\n");
        return major2;
    }
    if (major3 < 0)
    {
        printk("风扇设备驱动注册失败\n");
        return major3;
    }
    printk("字符设备驱动注册成功%d %d %d %d\n", major, major1, major2, major3);

    // 向上提交目录
    cls = class_create(THIS_MODULE, "led");
    cls1 = class_create(THIS_MODULE, "motor");
    cls2 = class_create(THIS_MODULE, "buzzer");
    cls3 = class_create(THIS_MODULE, "fan");
    if (IS_ERR(cls))
    {
        printk("向上提交led目录失败\n");
        return -PTR_ERR(cls);
    }
    if (IS_ERR(cls1))
    {
        printk("向上提交马达目录失败\n");
        return -PTR_ERR(cls1);
    }
    if (IS_ERR(cls2))
    {
        printk("向上提交目录蜂鸣器失败\n");
        return -PTR_ERR(cls2);
    }
    if (IS_ERR(cls3))
    {
        printk("向上提交风扇目录失败\n");
        return -PTR_ERR(cls3);
    }
    printk("向上提交目录成功\n");
    // 向上提交设备节点信息,为三盏灯创建三个设备文件
    for (i = 0; i < 3; i++)
    {
        dev = device_create(cls, NULL, MKDEV(major, i), NULL, "led%d", i);
        if (IS_ERR(dev))
        {
            printk("向上提交led%d设备节点信息失败\n",i);
            return -PTR_ERR(dev);
        }
        printk("向上提交led%d设备节点信息成功\n", i);
    }
    dev = device_create(cls1, NULL, MKDEV(major1, 0), NULL, "motor");
    if (IS_ERR(dev))
    {
        printk("向上提交马达设备节点信息失败\n");
        return -PTR_ERR(dev);
    }
    printk("向上提交马达设备节点信息成功\n");
    dev = device_create(cls2, NULL, MKDEV(major2, 0), NULL, "buzzer");
    if (IS_ERR(dev))
    {
        printk("向上提交蜂鸣器设备节点信息失败\n");
        return -PTR_ERR(dev);
    }
    printk("向上提交蜂鸣器设备节点信息成功\n");
    dev = device_create(cls3, NULL, MKDEV(major3, 0), NULL, "fan");
    if (IS_ERR(dev))
    {
        printk("向上提交风扇设备节点信息失败\n");
        return -PTR_ERR(dev);
    }
    printk("向上提交风扇设备节点信息成功\n");
    // 映射物理寄存器
    vir_led1 = ioremap(LED1_ADDR, sizeof(gpio_t));
    vir_led2 = ioremap(LED2_ADDR, sizeof(gpio_t));
    vir_led3 = vir_led1;
    vir_motor = vir_led2;
    vir_buzzer = ioremap(BUZZER_ADDR, sizeof(gpio_t));
    vir_fan = vir_led1;
    if (vir_led1 == NULL || vir_led2 == NULL || vir_buzzer == NULL)
    {
        printk("gpio寄存器地址映射表失败\n");
        return -EFAULT;
    }
    printk("gpio寄存器地址映射表成功\n");

    vir_rcc = ioremap(RCC_ADDR, 4);
    if (vir_rcc == NULL)
    {
        printk("RCC寄存器地址映射表失败\n");
        return -EFAULT;
    }
    printk("RCC寄存器地址映射成功\n");

    // rcc
    (*vir_rcc) |= (0x19 << 1);
    // led1
    vir_led1->MODER &= (~(3 << 20));
    vir_led1->MODER |= (1 << 20);
    vir_led1->ODR &= (~(1 << 10));
    // led2
    vir_led2->MODER &= (~(3 << 20));
    vir_led2->MODER |= (1 << 20);
    vir_led2->ODR &= (~(1 << 10));
    // led3
    vir_led3->MODER &= (~(3 << 16));
    vir_led1->MODER |= (1 << 16);
    vir_led1->ODR &= (~(1 << 8));
    // 马达
    vir_motor->MODER &= (~(3 << 12));
    vir_motor->MODER |= (1 << 12);
    vir_motor->ODR &= (~(0x1 << 6));
    // 蜂鸣器
    vir_buzzer->MODER &= (~(3 << 12));
    vir_buzzer->MODER |= (1 << 12);
    vir_buzzer->ODR &= (~(1 << 6));
    // 风扇
    vir_fan->MODER &= (~(3 << 18));
    vir_fan->MODER |= (1 << 18);
    vir_fan->ODR &= (~(1 << 9));
    printk("寄存器初始化成功\n");
    return 0;
}
static void __exit mycdev_exit(void)
{
    int i;
    // 取消寄存器地址映射
    iounmap(vir_led1);
    iounmap(vir_led2);
    iounmap(vir_led3);
    iounmap(vir_motor);
    iounmap(vir_buzzer);
    iounmap(vir_fan);
    iounmap(vir_rcc);
    printk("取消寄存器地址映射成功\n");
    // 销毁设备节点信息
    for (i = 0; i < 3; i++)
    {
        device_destroy(cls, MKDEV(major, i));
    }
    device_destroy(cls1, MKDEV(major1, 0));
    device_destroy(cls2, MKDEV(major2, 0));
    device_destroy(cls3, MKDEV(major3, 0));
    printk("销毁设备节点信息成功\n");
    // 销毁目录信息
    class_destroy(cls);
    class_destroy(cls1);
    class_destroy(cls2);
    class_destroy(cls3);
    printk("销毁目录信息成功\n");
    // 字符设备驱动的注销
    unregister_chrdev(major, "led");
    unregister_chrdev(major1, "motor");
    unregister_chrdev(major2, "buzzer");
    unregister_chrdev(major3, "fan");
    printk("注销设备驱动成功\n");
}
module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");
  • Makefile
arch?=arm
filename?=mychrdev
#指定内核顶层目录的路径
ifeq ($(arch),arm)
KERNELDIR:=/home/ubuntu/FSMP1A/linux-stm32mp-5.10.61-stm32mp-r2-r0/linux-5.10.61/   #编译为ARM架构的内核路径
else
KERNELDIR:=/lib/modules/$(shell uname -r)/build  #编译生成x86架构文件的内核路径
endif
#指定当前源码所在的路径
PWD:=$(shell pwd)  #将shell命令pwd的执行结果赋值给变量PWD

all:
#make modules表示进行模块化编译
#make -C $(KERNELDIR)先切换路径到KERNELDIR下,按照这个路径下Makefile的规则进行make
#M=$(PWD)指定模块化编译的路径
	make -C $(KERNELDIR) M=$(PWD) modules
clean:
#编译清除
	make -C $(KERNELDIR) M=$(PWD) clean
#将指定的.o文件独立链接为模块文件
obj-m:=$(filename).o

你可能感兴趣的:(c语言)