完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
GPIO是驱动当中最常用的资源,操作GPIO是最基本的操作,本节通过操作GPIO实现对LED灯的亮灭操作,驱动是在rk3288 android7.1 kernel4.4当中实现的。驱动实现解析dts的gpio,创建设备节点,创建工作队列实现闪灯效果(一秒切换一种颜色【蓝色和红色交替】)。
一、首先查看原理图,确定控制led的是哪个gpio 从原理图上可以看到,连接到主控的gpio是GPIO5_B2,所有我们只需要拉高或者拉低就可以控制LED灯了。拉高GPIO5_B2可以使三极管Q10导通,这样LED D31就亮了;拉低GPIO5_B2可以使三极管Q9断开,使三极管Q8导通,这样LED D32就亮了。 总结一下就是GPIO5_B2拉高亮红灯,拉低亮绿灯[开发板上焊接的是蓝灯]。驱动通过拉高拉低gpio控制led。 二、代码实现部分 1、dts部分,添加gpio的资源描述 文件:arch/arm/boot/dts/rk3288-evb-android-rk808-mipi.dts led_ctrl { status = "okay"; compatible = "rk3288,led_ctrl"; gpio_led = <&gpio5 10 GPIO_ACTIVE_HIGH>; //GPIO5_B2 }; 2、驱动部分。 文件:drivers/Makefile,在该文件添加编译对应驱动文件夹语句: obj-y += led_ctrl/ 文件:drivers/led_ctrl/Makefile(添加Makefile文件和编译语句) obj-y += led_ctrl.o 文件:drivers/led_ctrl/led_ctrl.c #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static int gpio_led; //定义一个GPIO变量,用gpio_led表示 static ssize_t gpio_led_show(struct device *dev, struct device_attribute *attr, char *buf) { return sprintf(buf, "%dn", gpio_get_value(gpio_led)); //获取GPIO的电平,1为高电平,0为低电平 } static ssize_t gpio_led_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { int val; int ret; ret = kstrtoint(buf, 10, &val); //输入一个字符然后转换成10进制整形数 if (ret) { printk("%s: kstrtoint error return %dn", __func__, ret); return -1; } if (val== 1) { //如果echo 1 到节点则调用 printk("-czd-: _%s_ :gpio pull upn", __func__); gpio_direction_output(gpio_led, val); //设置GPIO电平为高电平 } else if (val == 0) { //如果echo 0 到节点则调用 printk("-czd-: _%s_ :gpio pull downn", __func__); gpio_direction_output(gpio_led, val); //设置GPIO电平为低电平 } else { printk("I only support 0 or 1 to ctrl ledn"); } return size; } static DEVICE_ATTR(gpio_led, 0664, gpio_led_show, gpio_led_store); //调用DEVICE_ATTR创建设备节点 static int led_ctrl_probe(struct platform_device *pdev) //compatible的属性和dts的compatible一致,就会调用probe函数 { struct device_node *led_ctrl_node = pdev->dev.of_node; enum of_gpio_flags flags; int gpio_value; printk("[%d] enter %s start..n", __LINE__, __func__); //printk打印,kernel一般调用这个函数打印log gpio_led = of_get_named_gpio_flags(led_ctrl_node, "gpio_led", 0, &flags); //解析dts的gpio printk("gpio_led is %d --n", gpio_led); gpio_value = (flags == GPIO_ACTIVE_HIGH)? 1:0; if (!gpio_is_valid(gpio_led)) { //判断GPIO是否合法能用 printk("gpio_led: %d is invalidn", gpio_led); return -ENODEV; } if (gpio_request(gpio_led, "gpio_led")) { //申请GPIO口资源 printk("gpio_led: %d request failed!n", gpio_led); gpio_free(gpio_led); //如果申请失败,要释放GPIO的占用 return -ENODEV; } gpio_direction_output(gpio_led, !gpio_value); //设置GPIO初始电平为低电平 printk("gpio_led pin level is %dn", !gpio_value); //这里gpio_value取反!gpio_value,是低电平 device_create_file(&pdev->dev, &dev_attr_gpio_led); printk("[%d]: ___%s___ sucess!n", __LINE__, __func__); return 0; } static int led_ctrl_remove(struct platform_device *pdv) { printk("___%s___n", __func__); return 0; } static struct of_device_id led_ctrl_match_table[] = { { .compatible = "led_ctrl",}, {}, }; static struct platform_driver led_ctrl_driver = { .driver = { .name = "led_ctrl", .owner = THIS_MODULE, .of_match_table = led_ctrl_match_table, }, .probe = led_ctrl_probe, .remove = led_ctrl_remove, }; static int led_ctrl_init(void) { printk("#led_ctrl#: ___%s___n", __func__); platform_driver_register(&led_ctrl_driver); return 0; } static void led_ctrl_exit(void) { printk("#led_ctrl#: ___%s___,n", __func__); cancel_delayed_work_sync(&gpioled_event); //取消延时工作队列 platform_driver_unregister(&led_ctrl_driver); } module_init(led_ctrl_init); //模块加载函数 module_exit(led_ctrl_exit); //模块卸载函数 MODULE_AUTHOR("czd,"); MODULE_DESCRIPTION("Driver for control sysled"); MODULE_LICENSE("GPL"); //许可声明, 描述内核模块的许可权限,如果不声明LICENSE,模块被加载时,将收到kernel tainted的警告 |
|
|
|
以上就是创建设备节点,然后通过节点操作【read/write】GPIO的上下电实现led的亮灭的简单实现代码,串口或者adb工具操作对应节点文件gpio_led控制gpio电平:
rk3288:/ # su rk3288:/ # echo 0 > sys/devices/platform/led_ctrl/gpio_led //往*** rk3288:/ # cat sys/devices/platform/led_ctrl/gpio_led // cat查看一下gpio电平 0 rk3288:/ # echo 1 > sys/devices/platform/led_ctrl/gpio_led //往节点写入1,亮红灯 rk3288:/ # cat sys/devices/platform/led_ctrl/gpio_led // cat查看一下gpio电平 1 rk3288:/ # 注意:在安卓开发过程需要给节点的权限: 可以创建个脚本(*.sh文件)运行,然后在脚本里添加节点的权限: 例如apk/system/bin/lowmem_manage.sh文件中添加以下语句: chmod 666 sys/devices/platform/led_ctrl/gpio_led 或者在init*.rc文件当中添加赋权限的语句。 为了实现闪灯效果,我这里使用了工作队列来实现,具体逻辑:创建一个工作队列,然后在工作队列去操作GPIO的上下电,并且在工作队列函数中加入定时器,时间一到,又重新开始执行工作队列里面的操作,如此循环,实现led闪灯效果。 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static int gpio_led; //定义一个GPIO变量,用gpio_led表示 static int rb_switch_value = 0;//定义一个GPIO电平变量值 static struct delayed_work gpioled_event; //定义一个delay_work static void gpioled_event_workq(struct work_struct *work) //定义你要延时执行的工作队列函数 { printk("#%s#: led color blue or red,rb_switch_value=%dn", __func__, rb_switch_value); gpio_direction_output(gpio_led, rb_switch_value); //设置GPIO电平 rb_switch_value = !rb_switch_value; //设置rb_switch_value取反,以此实现高低电平切换设置 schedule_delayed_work(&gpioled_event, msecs_to_jiffies(1000)); //添加这句之后每隔1秒执行一次 } static ssize_t gpio_led_show(struct device *dev, struct device_attribute *attr, char *buf) { return sprintf(buf, "%dn", gpio_get_value(gpio_led)); //获取GPIO的电平,1为高电平,0为低电平 } static ssize_t gpio_led_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { int val; int ret; ret = kstrtoint(buf, 10, &val); //输入一个字符然后转换成10进制整形数 if (ret) { printk("%s: kstrtoint error return %dn", __func__, ret); return -1; } if (val== 1) { //如果echo 1 到节点则调用 printk("-czd-: _%s_ :gpio pull upn", __func__); gpio_direction_output(gpio_led, val); //设置GPIO电平为高电平 } else if (val == 0) { //如果echo 0 到节点则调用 printk("-czd-: _%s_ :gpio pull downn", __func__); gpio_direction_output(gpio_led, val); //设置GPIO电平为低电平 } else { printk("I only support 0 or 1 to ctrl ledn"); } return size; } static DEVICE_ATTR(gpio_led, 0664, gpio_led_show, gpio_led_store); //调用DEVICE_ATTR创建设备节点 static int led_ctrl_probe(struct platform_device *pdev) //compatible的属性和dts的compatible一致,就会调用probe函数 { struct device_node *led_ctrl_node = pdev->dev.of_node; enum of_gpio_flags flags; int gpio_value; printk("[%d] enter %s start..n", __LINE__, __func__); //printk打印,kernel一般调用这个函数打印log gpio_led = of_get_named_gpio_flags(led_ctrl_node, "gpio_led", 0, &flags); //解析dts的gpio printk("gpio_led is %d --n", gpio_led); gpio_value = (flags == GPIO_ACTIVE_HIGH)? 1:0; if (!gpio_is_valid(gpio_led)) { //判断GPIO是否合法能用 printk("gpio_led: %d is invalidn", gpio_led); return -ENODEV; } if (gpio_request(gpio_led, "gpio_led")) { //申请GPIO口资源 printk("gpio_led: %d request failed!n", gpio_led); gpio_free(gpio_led); //如果申请失败,要释放GPIO的占用 return -ENODEV; } gpio_direction_output(gpio_led, !gpio_value); //设置GPIO初始电平为低电平 printk("gpio_led pin level is %dn", !gpio_value); //这里gpio_value取反!gpio_value,是低电平 device_create_file(&pdev->dev, &dev_attr_gpio_led); INIT_DELAYED_WORK(&gpioled_event, gpioled_event_workq); //初始化工作队列 schedule_delayed_work(&gpioled_event, msecs_to_jiffies(2000)); //添加到延时工作队列,这里延时2秒 printk("[%d]: ___%s___ sucess!n", __LINE__, __func__); return 0; } static int led_ctrl_remove(struct platform_device *pdv) { printk("___%s___n", __func__); return 0; } static struct of_device_id led_ctrl_match_table[] = { { .compatible = "led_ctrl",}, {}, }; static struct platform_driver led_ctrl_driver = { .driver = { .name = "led_ctrl", .owner = THIS_MODULE, .of_match_table = led_ctrl_match_table, }, .probe = led_ctrl_probe, .remove = led_ctrl_remove, }; static int led_ctrl_init(void) { printk("#led_ctrl#: ___%s___n", __func__); platform_driver_register(&led_ctrl_driver); return 0; } static void led_ctrl_exit(void) { printk("#led_ctrl#: ___%s___,n", __func__); cancel_delayed_work_sync(&gpioled_event); //取消延时工作队列 platform_driver_unregister(&led_ctrl_driver); } module_init(led_ctrl_init); //模块加载函数 module_exit(led_ctrl_exit); //模块卸载函数 MODULE_AUTHOR("czd,"); MODULE_DESCRIPTION("Driver for control sysled"); MODULE_LICENSE("GPL"); //许可声明, 描述内核模块的许可权限,如果不声明LICENSE,模块被加载时,将收到kernel tainted的警告。 |
|
|
|
你正在撰写答案
如果你是对答案或其他答案精选点评或询问,请使用“评论”功能。
1880 浏览 1 评论
synopsys 的design ware:DW_fpv_div,浮点数除法器,默认32位下,想提升覆盖率(TMAX),如果用功能case去提升覆盖率呢?
2346 浏览 1 评论
RK3588 GStreamer调试四路鱼眼摄像头四宫格显示报错
5152 浏览 1 评论
【飞凌嵌入式OK3576-C开发板体验】RKNN神经网络-YOLO图像识别
254 浏览 0 评论
【飞凌嵌入式OK3576-C开发板体验】SSH远程登录网络配置及CAN通讯
1336 浏览 0 评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-11-20 17:25 , Processed in 0.533594 second(s), Total 44, Slave 38 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号