最简单的LED驱动

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
#include "asm-generic/errno-base.h"
#include "asm-generic/gpio.h"
#include "asm/uaccess.h"
#include <linux/module.h>
#include <linux/poll.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>
#include <linux/gpio/consumer.h>
#include <linux/platform_device.h>
#include <linux/of_gpio.h>
#include <linux/of_irq.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/slab.h>
#include <linux/fcntl.h>
#include <linux/timer.h>
 
//gpio结构体
struct gpio_desc{
    int gpio; //GPIO编号
    int irq;    //中断号
    char *name; //名字
} ;
 
//PI0 = LED0      PF3 = LED1
static struct gpio_desc gpios[2] = {
    {128, 0, "led0", },
    {83, 0, "led1", },
};
 
static int major = 0; //设备号
static struct class *gpio_class;
 
//读函数
static ssize_t gpio_drv_read (struct file *file, char __user *buf, size_t size, loff_t *offset)
{
    char tmp_buf[2];
    int err;
    int count = sizeof(gpios)/sizeof(gpios[0]); //计算GPIO个数
 
    if (size != 2) //应用层必须是读2个字节
        return -EINVAL;
 
    //先读一下应用层buf来判断需要读取哪个IO引脚
    err = copy_from_user(tmp_buf, buf, 1);
 
    if (tmp_buf[0] >= count) //判断读取的引脚号是否超出范围
        return -EINVAL;
     
    //获取GPIO引脚电平
    tmp_buf[1] = gpio_get_value(gpios[tmp_buf[0]].gpio);
 
    err = copy_to_user(buf, tmp_buf, 2); //返回给应用层
     
    return 2;
}
 
//写函数
static ssize_t gpio_drv_write(struct file *file, const char __user *buf, size_t size, loff_t *offset)
{
    unsigned char ker_buf[2];
    int err;
 
    if (size != 2) //应用层必须是写两个字节
        return -EINVAL;
 
    //读一下应用层buf来判断需要写哪个IO引脚
    err = copy_from_user(ker_buf, buf, size);
     
    if (ker_buf[0] >= sizeof(gpios)/sizeof(gpios[0]))//判断要写的引脚号是否超出范围
        return -EINVAL;
 
    //根据写指定的GPIO引脚与电平
    gpio_set_value(gpios[ker_buf[0]].gpio, ker_buf[1]);
    return 2;   
}
 
//字符操作集
static struct file_operations gpio_key_dev_fops = {
    .owner   = THIS_MODULE,
    .read    = gpio_drv_read,
    .write   = gpio_drv_write,
};
 
 
 
static int __init gpio_drv_init(void)
{
    int err;
    int i;
    //计算有几个gpio
    int count = sizeof(gpios)/sizeof(gpios[0]);
     
    printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
    for ( i = 0; i  < count; i++){
        err = gpio_request(gpios[i].gpio, gpios[i].name); //向内核申请gpio
        if (err < 0) {
            printk("can not request gpio %s %d\n", gpios[i].name, gpios[i].gpio);
            return -ENODEV;
        }
        //配置GPIO为输出,默认电平为高
        gpio_direction_output(gpios[i].gpio, 1);
    }
    //注册字符设备
    major = register_chrdev(0, "100ask_led", &gpio_key_dev_fops);
    //创建类
    gpio_class = class_create(THIS_MODULE, "100ask_led_class");
    if (IS_ERR(gpio_class)) {
        printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
        unregister_chrdev(major, "100ask_led_class");
        return PTR_ERR(gpio_class);
    }
    //创建设备
    device_create(gpio_class, NULL, MKDEV(major, 0), NULL, "100ask_led");
 
    return err;
}
 
//驱动出口函数
//注意:注销时一定要后注册(创建)的要先注销(摧毁)
static void __exit gpio_drv_exit(void)
{
    int i;
    int count = sizeof(gpios)/sizeof(gpios[0]);
    //摧毁设备
    device_destroy(gpio_class, MKDEV(major, 0));
    //摧毁类
    class_destroy(gpio_class);
    //注销字符设备
    unregister_chrdev(major, "100ask_led");
 
    for(i = 0; i < count; i++){
        //释放GPIO
        gpio_free(gpios[i].gpio);
    }
}
 
module_init(gpio_drv_init);
module_exit(gpio_drv_exit);
 
MODULE_LICENSE("GPL");

  

应用层

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <poll.h>
#include <signal.h>
 
static int fd;
 
int main(int argc, char * argv[])
{
    int ret;
    char buf[2];
    int i;
    if(argc < 2){
        printf("Usage: %s <0|1|2|...> [on | off]\n", argv[0]);
        return -1;
    }
 
    fd = open("/dev/100ask_led", O_RDWR);
    if(fd < 0){
        printf("can not open file /dev/100ask_led\n");
        return -1;
    }
 
    if(argc == 3){
        buf[0] = strtol(argv[1], NULL, 0);
        if (strcmp(argv[2], "on") == 0)
            buf[1] = 0;
        else
            buf[1] = 1;
         
        ret = write(fd, buf, 2);
        if(ret < 0){
            printf("write error\r\n");
        }
    }
    else
    {
        buf[0] = strtol(argv[1], NULL, 0);
        ret = read(fd, buf, 2);
        if (ret == 2)
        {
            printf("led %d status is %s\n", buf[0], buf[1] == 0 ? "on" : "off");
        }
    }
     
    close(fd);
 
    return 0;
}

  

posted @   flyke  阅读(47)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异
点击右上角即可分享
微信分享提示