使用字符驱动注册+GPIO子系统实现点亮LED灯

本文档展示了一个Linux内核模块,用于创建字符设备驱动,动态分配设备号,注册设备节点,并通过GPIO接口控制LED灯。驱动实现包括open、read、write和close方法,允许用户通过读写设备文件来开关LED灯。同时,提供了一个用户空间的测试程序,用于交互式控制LED状态。设备树中定义了LED节点,供驱动程序获取GPIO引脚信息。
摘要由CSDN通过智能技术生成

.c 文件

#include <linux/init.h>
#include <linux/module.h>
#include<linux/of.h>
#include<linux/of_gpio.h>
#include<linux/gpio.h>
#include <linux/cdev.h>
#define N 1   //节点个数
int mycdev_open(struct inode *inode, struct file *file);  //对应的是open()
ssize_t mycdev_read(struct file *file, char __user *ubuf, size_t size, loff_t *loff);  //read()
ssize_t mycdev_write(struct file *file, const char __user *ubuf, size_t size, loff_t *loff);  //write()
int mycdev_close(struct inode *inode, struct file *file);  //close()
int led_init(void);
//操作方法结构体的初始化
struct file_operations fops={
    .open=mycdev_open,
    .read=mycdev_read,
    .write=mycdev_write,
    .release=mycdev_close,
};
char kbuf[128]={};  //定义数组用于存放和用户之间拷贝的数据
struct class *cls;  //句柄
struct device *dev;
struct cdev *cdev;  //1.分配字符设备对象
dev_t devno=300;    //静态指定设备号
int i,ret;
struct device_node* prent_node;
struct device_node* child_node;
int gpiono1,gpiono2,gpiono3;
//入口函数,当驱动安装的时候执行
static int __init demo_init(void)
{ 
    //2.给字符设备对象申请空间
    cdev = cdev_alloc();
    if(cdev==NULL)
    {
        printk("设备对象申请空间失败\n");
        goto ERR1;
    }
    printk("设备对象申请空间成功\n");
    //3.初始化设备驱动对象(部分)
    cdev_init(cdev, &fops);
    //4.动态指定设备号
    if(alloc_chrdev_region(&devno, 0, N, "my_led"))
    {
        printk("动态申请设备号失败\n");
        goto ERR2;
    }
    printk("动态指定设备号成功 \n");
    //5.注册设备驱动
    if(cdev_add(cdev, devno, N))
    {
        printk("注册驱动失败\n");
        goto ERR3;
        return -1;
    }
    printk("注册驱动成功\n");
    //6.自动创建节点
    //向上提交节点目录
    cls = class_create(THIS_MODULE,"MYLED");
    if(IS_ERR(cls))
    {
        printk("向上提交目录失败\n");
        goto ERR4;
    }
    printk("向上提交目录成功\n");
    for(i=0; i<N; i++)
    {
        //创建设备节点
        dev = device_create(cls,NULL,MKDEV(MAJOR(devno),i),NULL,"led%d",i);
        if(IS_ERR(dev))
        {
            printk("创建节点失败\n");
            goto ERR5;
        }
        printk("创建节点%d成功\n",MKDEV(MAJOR(devno),i));
    }
    led_init();     //led灯的初始化

    return 0;
ERR5:
    //销毁节点
    for(i-=N; i>=0;i--)
        device_destroy(cls,MKDEV(MAJOR(devno),i));  
    //销毁目录
    class_destroy(cls);   
ERR4:
    //注销字符设备驱动
    cdev_del(cdev);
ERR3:
    //注销设备号
    unregister_chrdev_region(devno, 3);
ERR2:
    //注销设备对象
    kfree(cdev);
ERR1:
    return -1;
}

//出口函数,卸载驱动的时候执行
static void __exit demo_exit(void)
{ 
    gpio_set_value(gpiono1,0);
    gpio_set_value(gpiono2,0);
    gpio_set_value(gpiono3,0);
    gpio_free(gpiono1);
    gpio_free(gpiono2);
    gpio_free(gpiono3);
    //注销设备节点
    device_destroy(cls,MKDEV(MAJOR(devno),0)); 
    //销毁目录
    class_destroy(cls);
    printk("销毁目录成功\n");
    //注销字符设备驱动
    cdev_del(cdev);
    printk("销毁字符驱动成功\n");
    //注销设备号
    unregister_chrdev_region(devno, 1);
    printk("销毁设备号成功\n");
    //注销设备驱动对象
    kfree(cdev);
    printk("销毁驱动对象成功\n");
}
module_init(demo_init);
module_exit(demo_exit);
MODULE_LICENSE("GPL");

int mycdev_open(struct inode *inode, struct file *file)
{
      printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
      return 0;
}
ssize_t mycdev_read(struct file *file, char __user *ubuf, size_t size, loff_t *loff)
{
    //size参数是用户期待读到的字节长度
    if(size>sizeof(kbuf))
    size=sizeof(kbuf);
    ret=copy_to_user(ubuf,kbuf,size);
    if(ret)
    {
        printk("数据从内核向用户拷贝失败\n");
        return -EIO;
    }
    return size;
}
ssize_t mycdev_write(struct file *file, const char __user *ubuf, size_t size, loff_t *loff)
{
    if(size>sizeof(kbuf))
    size=sizeof(kbuf);
    ret=copy_from_user(kbuf,ubuf,size);
    if(ret)
    {
        printk("数据从内核向用户拷贝失败\n");
        return -EIO;
    }
    printk("内核接收到的数据是:%s\n",kbuf);
    switch (kbuf[0])
    {
    case '1'://亮
            switch (kbuf[1])
            {
            case '1':
                gpio_set_value(gpiono1,1);
                break;
            case '2':
                gpio_set_value(gpiono2,1);
                break;
            case '3':
                gpio_set_value(gpiono3,1);
                break;
            default:
                break;
            } 
            break;
    case '0'://灭
            switch (kbuf[1])
            {
            case '1':
                gpio_set_value(gpiono1,0);
                break;
            case '2':
                gpio_set_value(gpiono2,0);
                break;
            case '3':
                gpio_set_value(gpiono3,0);
                break;
            default:
                break;
            } 
            break; 
    default:
        break;
    }
    return size;
}
int mycdev_close(struct inode *inode, struct file *file)
{
    printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
    return 0;
}
int  led_init(void)
{
    prent_node = of_find_node_by_path("/myleds");
    if(NULL==prent_node)
    {
        printk("获取父节点信息失败\n");
        return -1;
    }
    child_node = of_get_child_by_name(prent_node,"extend_leds");
    if(NULL==child_node)
    {
        printk("获取子节点信息失败\n");
        return -1;
    }

    gpiono1 = of_get_named_gpio(child_node, "led1", 0);
    if(gpiono1<0)
    {
        printk("申请LED1GPIO编号失败\n");
        return -1;
    }
     gpiono2 = of_get_named_gpio(child_node, "led2", 0);
    if(gpiono1<0)
    {
        printk("申请LED1GPIO编号失败\n");
        return -1;
    }
     gpiono3 = of_get_named_gpio(child_node, "led3", 0);
    if(gpiono1<0)
    {
        printk("申请LED1GPIO编号失败\n");
        return -1;
    }

    if(gpio_request(gpiono1,NULL))
    {
        printk("LED1使用权限申请失败\n");
        return -1;
    }
    if(gpio_request(gpiono2,NULL))
    {
        printk("LED1使用权限申请失败\n");
        return -1;
    }
    if(gpio_request(gpiono3,NULL))
    {
        printk("LED1使用权限申请失败\n");
        return -1;
    }
    //方向设置为输出方向
    gpio_direction_output(gpiono1, 0);
    gpio_direction_output(gpiono2, 0);
    gpio_direction_output(gpiono3, 0);
    //初始电平为低电平
    gpio_set_value(gpiono1,0);
    gpio_set_value(gpiono2,0);
    gpio_set_value(gpiono3,0);

    return 0;
}

测试文件  .c

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc, char const *argv[])
{
    //打开节点
    int fd = open("/dev/led0",O_RDWR);
    if(fd<0)
    {
        printf("打开设备节点失败\n");
        return -1;
    }
    char contrl[2];//第一个字符表示亮灭,第二个字符表示操控那一盏灯
    while(1)
    {
        printf("请输入控制方式: 1:开  0:关\n");
        contrl[0] = getchar();
        while (getchar()!='\n');  
        if(contrl[0]=='1')//开灯
        {
            printf("请输入要开那盏灯: 1:LED1 2:LED2 3:LED3\n");
            contrl[1] = getchar();
            while (getchar()!='\n'); 
            write(fd, contrl, 2);
        }else//关灯
        {
            printf("请输入要关那盏灯: 1:LED1 2:LED2 3:LED3\n");
            contrl[1] = getchar();
            while (getchar()!='\n'); 
            write(fd, contrl, 2);
        }
    }
    close(fd);
    return 0;
}

设备树添加的节点:

leds{
		extend-leds{
			led1=<&gpioe 10 0>;
			led2=<&gpiof 10 0>;
			led3=<&gpioe 8 0>;
		};
        core-leds{
			led1=<&gpioz 5 0>;
			led2=<&gpioz 6 0>;
			led3=<&gpioz 7 0>;
		};
	};	

 测试结果:

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

傾语

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值