- 前言
- 一、代码文件
- 1.led.c文件
- 2.switch.c文件
- 3.Makefile文件
- 4.app.c文件
- 二、运行结果
- 总结
本文的主要内容是通过一个综合案例,即按键控制led的亮灭来将之前学过的中断、等待队列、pinctrl和gpio子系统、设备树、platform平台总线以及杂项设备驱动等内容串联起来。
本文以Linux下点亮开发板上通过uart外接的led灯、Linux下的中断介绍及其应用实验——按键响应、Linux下的等待队列及其案例为基础。
一、代码文件 1.led.c文件
#include2.switch.c文件#include #include #include #include #include #include #include #include #include #include int size; u32 out_values[2] = {0}; const char *str; struct device_node *test_device_node; struct property *test_node_property; unsigned int *vir_addr; int uart3_gpio = 0; ssize_t misc_write (struct file *file, const char __user *ubuf, size_t size, loff_t *loff_t) { char kbuf[64] = {0}; //从应用层复制字符串到内核层 if(copy_from_user(kbuf,ubuf,size)!=0) { printk("copy_from_user error!n"); return -1; } printk("value is: %d.n", kbuf[0]); //打印从应用层传输到内核层的值 if(kbuf[0] == 1){ gpio_set_value(uart3_gpio, 1); //设置输出值为高电平 printk("The led is turned on!n"); } else if(kbuf[0] == 0){ gpio_set_value(uart3_gpio, 0); //设置输出值为低电平 printk("The led is turned off!n"); } return 0; } struct file_operations misc_fops = { //在结构体中引入相关函数 .owner = THIS_MODULE, .write = misc_write }; struct miscdevice misc_dev={ .minor = MISC_DYNAMIC_MINOR, //次设备号 .name = "misc_device_led", //设备节点的名字 .fops = &misc_fops }; int dts_probe(struct platform_device *pdev) { int ret = 0; printk("dts_probe matching ok!n"); printk("node name is %s.n", pdev->dev.of_node->name); //直接读取节点名字的方法 test_device_node = of_find_node_by_path("/test"); //在设备树节点中查找test这个节点 if(test_device_node == NULL){ printk("of_find_node_by_path is error!n"); return -1; } uart3_gpio = of_get_named_gpio(test_device_node, "uart3-gpio", 0); //设备树中定义的名字 if(uart3_gpio < 0) { printk("of_get_named_gpio is error!n"); return -1; } printk("uart3_gpio is %d.n", uart3_gpio); ret = gpio_request(uart3_gpio, "gpio_led"); if(ret < 0) { printk("gpio_request is error!n"); return -1; } gpio_direction_output(uart3_gpio, 0); //设置gpio为输出类型,默认的输出值设置为0,即驱动加载后led不亮 ret = misc_register(&misc_dev); if(ret < 0) { printk("miscdevice registered error!n"); return -1; } return 0; } int dts_remove(struct platform_device *pdev) { printk("dts_remove!n"); return 0; } const struct platform_device_id dts_idtable = { .name = "dts_test" //匹配优先级 第二 }; const struct of_device_id of_match_table_test[] = { {.compatible = "test123"}, //匹配优先级 第一 {} }; struct platform_driver dts_device = { .probe = dts_probe, .remove = dts_remove, .driver = { .owner = THIS_MODULE, .name = "dts123", //匹配优先级 第三 .of_match_table = of_match_table_test }, .id_table = &dts_idtable }; static int dts_driver_init(void) { int ret = 0; ret = platform_driver_register(&dts_device); if(ret < 0) { printk("platform_driver_register error!n"); return ret; } printk("platform_driver_register ok!n"); return 0; } static int dts_driver_exit(void) { printk("dts_driver_exit!n"); gpio_free(uart3_gpio); misc_deregister(&misc_dev); platform_driver_unregister(&dts_device); } module_init(dts_driver_init); module_exit(dts_driver_exit); MODULE_LICENSE("GPL");
#include3.Makefile文件#include #include #include #include #include #include #include #include #include "linux/miscdevice.h" #include "linux/fs.h" #include "linux/uaccess.h" #include "linux/wait.h" #include "linux/sched.h" #include #include struct device_node *test_device_node; struct property *test_node_property; DECLARE_WAIT_QUEUE_HEAD(key_wq); // 定义并初始化等待队列头 int gpio_num; int irq; int value = 0; int wq_flags = 0; //标志位 int misc_open(struct inode *inode, struct file *file) { printk("hello misc_open!n"); return 0; } int misc_release(struct inode *inode, struct file *file) { printk("misc_release bye!n"); return 0; } ssize_t misc_read (struct file *file, char __user *ubuf, size_t size, loff_t *loff_t) { wait_event_interruptible(key_wq, wq_flags); if(copy_to_user(ubuf,&value,sizeof(value))!=0) { printk("copy_to_user error!n"); return -1; } wq_flags = 0; return 0; } struct file_operations misc_fops = { //在结构体中引入相关函数 .owner = THIS_MODULE, .open = misc_open, .release = misc_release, .read = misc_read, }; struct miscdevice misc_dev={ .minor = MISC_DYNAMIC_MINOR, //次设备号 .name = "misc_device_switch", //设备节点的名字 .fops = &misc_fops }; irq_handler_t test_key(int irq, void *args) { value = ! value; wq_flags = 1; //这里只有先置1,下面的函数才能唤醒 wake_up(&key_wq); //唤醒 return IRQ_HANDLED; } int dts_probe(struct platform_device *pdev) { int ret = 0; printk("dts_probe matching ok!n"); test_device_node = of_find_node_by_path("/test_key"); //在设备树节点中查找test_key这个节点 if(test_device_node == NULL){ printk("of_find_node_by_path is error!n"); return -1; } gpio_num = of_get_named_gpio(test_device_node,"key-gpio",0); if(gpio_num < 0){ printk("of_get_named_gpio is error!n"); return -1; } gpio_direction_input(gpio_num); //输入 //irq = gpio_to_irq(gpio_num); irq = irq_of_parse_and_map(test_device_node,0); //与上面这句代码的作用相同 printk("irq is %dn", irq); ret = request_irq(irq, test_key, IRQF_TRIGGER_RISING, "test_key", NULL); //IRQF_TRIGGER_RISING为上升沿触发,定义在/linux-4.1.15/include/linux/interrupt.h中 if(ret < 0){ printk("request_irq is error!n"); return -1; } return 0; } int dts_remove(struct platform_device *pdev) { printk("dts_remove!n"); return 0; } const struct platform_device_id dts_idtable = { .name = "dts_test1" //匹配优先级 第二 }; const struct of_device_id of_match_table_test[] = { {.compatible = "led_keys"}, //匹配优先级 第一 {} }; struct platform_driver dts_device = { .probe = dts_probe, .remove = dts_remove, .driver = { .owner = THIS_MODULE, .name = "dts_test2", //匹配优先级 第三 .of_match_table = of_match_table_test }, .id_table = &dts_idtable }; static int dts_driver_init(void) { int ret = 0; ret = platform_driver_register(&dts_device); if(ret < 0) { printk("platform_driver_register error!n"); return ret; } printk("platform_driver_register ok!n"); ret = misc_register(&misc_dev); if(ret < 0) { printk("miscdevice registered error!n"); return -1; } printk("miscdevice registered successfully!n"); return 0; } static int dts_driver_exit(void) { printk("dts_driver_exit!n"); free_irq(irq,NULL); platform_driver_unregister(&dts_device); misc_deregister(&misc_dev); } module_init(dts_driver_init); module_exit(dts_driver_exit); MODULE_LICENSE("GPL");
obj-m += led.o switch.o //obj-m += led.o //obj-m += switch.o //这样分开写也是可以的 KDIR:=/linux/linux-4.1.15 PWD?=$(shell pwd) all: make -C $(KDIR) M=$(PWD) modules clean: make -C $(KDIR) M=$(PWD) clean4.app.c文件
#include#include #include #include #include int main(int argc, char *argv[]) { int fd_r,fd_w; int value; fd_r = open("/dev/misc_device_switch", O_RDWR); fd_w = open("/dev/misc_device_led", O_RDWR); if(fd_r < 0){ perror("open errorn"); return fd_r; } if(fd_w < 0){ perror("open errorn"); return fd_w; } while(1){ read(fd_r, &value, sizeof(value)); write(fd_w, &value, sizeof(value)); } return 0; }
二、运行结果
编写好以上代码后进行编译,将生成的驱动文件led.ko和switch.ko以及通过arm编译的app文件发送至开发板。
先加载两个驱动,然后运行app程序,此时按下按键,led会亮起来,再按一次,led会熄灭,如此反复执行,开发板也会打印与led亮灭相对应的信息。
按下按键之后led点亮。
按下按键之后led熄灭。
总结
以上就是Linux下通过按键控制led亮灭综合案例的所有内容了,其涉及到之前学过的中断、等待队列、pinctrl和gpio子系统、设备树、platform平台总线以及杂项设备驱动等内容,需要好好体会各内容之间的相互联系。



