• GPIO子系统编写LED灯的驱动、linux内核定时器


    一、GPIO子系统

    1.概念:

    一个芯片厂商生产出芯片后会给linux提供一个当前芯片中gpio外设的驱动,我们当前只需要调用对应的厂商驱动即可完成硬件的控制。而linux内核源码中的gpio厂商驱动有很多,这里linux内核对厂商驱动做了一些封装,提供了一系列的API,我们在自己编写的设备驱动中只需要调用这些API即可访问对应的厂商驱动,进而完成GPIO的控制

    2.API

    添加LED的设备树节点

    myled

    {

            led1-gpio=<&gpioe 10 0>;//10表示使用的gpioe第几个管脚 0,表示gpio默认属性

            led2-gpio=<&gpiof 10 0>;

            led3-gpio=<&gpioe 8 0>;

    };

    或者

    myled{ led-gpios=<&gpioe 10 0>,<&gpiof 10 0>,<&gpioe 8 0>; };

    执行make dtbs编译设备树,将编译生成的设备树镜像拷贝到~/tftpboot目录下,重启开发板

    1)解析GPIO相关的设备树节点

            struct device_node *of_find_node_by_path(const char *path)

    2)根据解析的GPIO相关节点信息获取GPIO编号

            #include

             int of_get_named_gpio(struct device_node *np(设备树节点指针),const char *propname(gpio编号信息对应的键名), int index(属性键值对中的索引号))

    3)向内核申请要使用的GPIO编号

            #include

            int gpio_request(unsigned gpio, const char *label)

    4)将gpio编号对应的gpio管脚设置为输出

            int gpio_direction_output(unsigned gpio, int value)

    5)设置gpio编号对应的gpio管脚 输出高低电平

            void gpio_set_value(unsigned gpio, int value)

    6)获取gpio编号对应到的GPIO引脚状态值

            int gpio_get_value(unsigned gpio)

    7)释放GPIO编号

            void gpio_free(unsigned gpio)

    3.新版API

    核心不再是GPIO编号,而是GPIO对象

    1)在设备树节点中解析出GPIO对象,并向内核申请

    struct gpio_desc *gpiod_get_from_of_node(struct device_node *node,

                            const char *propname, int index, enum gpiod_flags dflags, const char *label)

    2)int gpiod_direction_output(struct gpio_desc *desc, int value)

         int gpiod_direction_input(struct gpio_desc *desc)

         void gpiod_set_value(struct gpio_desc *desc, int value)

         int gpiod_get_value(const struct gpio_desc *desc)

         void gpiod_put(struct gpio_desc *desc)//释放gpi对象指针

    二、linux内核定时器

    定时时间到达之后可以执行当前的定时器处理函数

    1..jiffies

            内核中用于保存内核节拍数的一个变量。它的值从内核启动开始就不断从0开始增加。

    2.内核频率

            内核节拍数一秒钟增加的数量被称为内核的频率,内核的频率在内核顶层目录下的.config文件中被设置

    3.定时器启用相关的API

    1)分配定时器对象

            struct timer_list mytimer;

    2)初始化定时器对象

            void timer_setup(struct timer_list *timer, void (*func)(struct timer_list *), unsigned int flags);

    3)注册定时器对象并启用定时器

           void add_timer(struct timer_list *timer) 

    4)再次启用定时器

            int mod_timer(struct timer_list *timer, unsigned long expires)

    5)注销定时器对象

            int del_timer(struct timer_list *timer)

    三、任务

    GPIO子系统编写LED灯的驱动(使用新版API)

    head.h
    1. #ifndef __HEAD_H__
    2. #define __HEAD_H__
    3. //构建LED开关功能码,添加ioctl第三个参数
    4. #define LED_ON _IO('l',1)
    5. #define LED_OFF _IO('l',0)
    6. #endif
    myled.c
    1. #include <linux/init.h>
    2. #include <linux/module.h>
    3. #include <linux/of.h>
    4. #include <linux/of_gpio.h>
    5. #include <linux/gpio.h>
    6. #include <linux/timer.h>
    7. #include<linux/cdev.h>
    8. #include<linux/fs.h>
    9. #include<linux/device.h>
    10. #include<linux/uaccess.h>
    11. #include<linux/slab.h>
    12. #include<linux/io.h>
    13. #include"head.h"
    14. struct cdev* cdev;
    15. unsigned int major=0;
    16. unsigned int minor=0;
    17. dev_t devno;
    18. module_param(major,uint,0664);
    19. struct class* cls;
    20. struct device* dev;
    21. struct device_node *dnode;
    22. struct gpio_desc* gpiono1;
    23. struct gpio_desc* gpiono2;
    24. struct gpio_desc* gpiono3;
    25. //封装操作的方法
    26. int mycdev_open(struct inode *inode, struct file *file)
    27. {
    28. int min=MINOR(inode->i_rdev);
    29. file->private_data=(void *)min;
    30. printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
    31. return 0;
    32. }
    33. long mydev_ioctl(struct file* file,unsigned int cmd,unsigned long arg)
    34. {
    35. int min=(int)file->private_data;
    36. switch(min)
    37. {
    38. case 0:
    39. switch(cmd)
    40. {
    41. case LED_ON: //开灯
    42. gpiod_set_value(gpiono1,1);
    43. break;
    44. case LED_OFF: //关灯
    45. gpiod_set_value(gpiono1,0);
    46. break;
    47. }
    48. break;
    49. case 1:
    50. switch(cmd)
    51. {
    52. case LED_ON: //开灯
    53. gpiod_set_value(gpiono2,1);
    54. break;
    55. case LED_OFF: //关灯
    56. gpiod_set_value(gpiono2,0);
    57. break;
    58. }
    59. break;
    60. case 2:
    61. switch(cmd)
    62. {
    63. case LED_ON: //开灯
    64. gpiod_set_value(gpiono3,1);
    65. break;
    66. case LED_OFF: //关灯
    67. gpiod_set_value(gpiono3,0);
    68. break;
    69. }
    70. break;
    71. }
    72. return 0;
    73. }
    74. int mycdev_close(struct inode *inode, struct file *file)
    75. {
    76. printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
    77. return 0;
    78. }
    79. //定义操作方法结构体变量并赋值
    80. struct file_operations fops={
    81. .open=mycdev_open,
    82. .unlocked_ioctl=mydev_ioctl,
    83. .release=mycdev_close,
    84. };
    85. static int __init mycdev_init(void)
    86. {
    87. int ret;
    88. //为字符设备驱动对象申请空间
    89. cdev=cdev_alloc();
    90. if(cdev==NULL)
    91. {
    92. printk("字符设备驱动对象申请空间失败\n");
    93. ret=-EFAULT;
    94. goto out1;
    95. }
    96. printk("申请对象空间成功\n");
    97. //初始化字符设备驱动对象
    98. cdev_init(cdev,&fops);
    99. //申请设备号
    100. if(major>0)//静态指定设备号
    101. {
    102. ret=register_chrdev_region(MKDEV(major,minor),3,"myled");
    103. if(ret)
    104. {
    105. printk("静态申请设备号失败\n");
    106. goto out2;
    107. }
    108. }
    109. else if(major==0)//动态申请设备号
    110. {
    111. ret=alloc_chrdev_region(&devno,minor,3,"myled");
    112. if(ret)
    113. {
    114. printk("动态申请设备号失败\n");
    115. goto out2;
    116. }
    117. major=MAJOR(devno);//获取主设备号
    118. minor=MINOR(devno);//获取次设备号
    119. }
    120. printk("申请设备号成功\n");
    121. //注册字符设备驱动对象
    122. ret=cdev_add(cdev,MKDEV(major,minor),3);
    123. if(ret)
    124. {
    125. printk("注册字符设备驱动对象失败\n");
    126. goto out3;
    127. }
    128. printk("注册字符设备驱动对象成功\n");
    129. //向上提交目录信息
    130. cls=class_create(THIS_MODULE,"myled");
    131. if(IS_ERR(cls))
    132. {
    133. printk("向上提交目录失败\n");
    134. ret=-PTR_ERR(cls);
    135. goto out4;
    136. }
    137. printk("向上提交目录成功\n");
    138. //向上提交设备节点信息
    139. int i;
    140. for(i=0;i<3;i++)
    141. {
    142. dev=device_create(cls,NULL,MKDEV(major,i),NULL,"myled%d",i);
    143. if(IS_ERR(dev))
    144. {
    145. printk("向上提交设备节点信息失败\n");
    146. ret=-PTR_ERR(dev);
    147. goto out5;
    148. }
    149. }
    150. printk("向上提交设备信息成功\n");
    151. dnode=of_find_node_by_path("/myled");
    152. if(dnode==NULL)
    153. {
    154. printk("解析设备树节点失败\n");
    155. return -ENXIO;
    156. }
    157. printk("解析GPIO信息成功\n");
    158. //申请gpio对象
    159. gpiono1=gpiod_get_from_of_node(dnode,"led1-gpio",0,GPIOD_OUT_LOW,NULL);
    160. if(IS_ERR(gpiono1))
    161. {
    162. printk("申请gpio对象失败\n");
    163. return -ENXIO;
    164. }
    165. printk("申请led1-gpio信息对象成功\n");
    166. gpiono2=gpiod_get_from_of_node(dnode,"led2-gpio",0,GPIOD_OUT_LOW,NULL);
    167. if(IS_ERR(gpiono2))
    168. {
    169. printk("申请gpio对象失败\n");
    170. return -ENXIO;
    171. }
    172. printk("申请led2-gpio信息对象成功\n");
    173. gpiono3=gpiod_get_from_of_node(dnode,"led3-gpio",0,GPIOD_OUT_LOW,NULL);
    174. if(IS_ERR(gpiono3))
    175. {
    176. printk("申请gpio对象失败\n");
    177. return -ENXIO;
    178. }
    179. printk("申请led3-gpio信息对象成功\n");
    180. return 0;
    181. out5:
    182. //释放前一次提交成功的设备信息
    183. for(--i;i>=0;i--)
    184. {
    185. device_destroy(cls,MKDEV(major,i));
    186. }
    187. class_destroy(cls);//释放目录
    188. out4:
    189. cdev_del(cdev);
    190. out3:
    191. unregister_chrdev_region(MKDEV(major,minor),3);
    192. out2:
    193. kfree(cdev);
    194. out1:
    195. return ret;
    196. }
    197. static void __exit mycdev_exit(void)
    198. {
    199. //灭灯
    200. gpiod_set_value(gpiono1,0);
    201. gpiod_set_value(gpiono2,0);
    202. gpiod_set_value(gpiono3,0);
    203. //释放gpio编号
    204. gpiod_put(gpiono1);
    205. gpiod_put(gpiono2);
    206. gpiod_put(gpiono3);
    207. //释放节点信息
    208. int i;
    209. for(i=0;i<3;i++)
    210. {
    211. device_destroy(cls,MKDEV(major,i));
    212. }
    213. //销毁目录
    214. class_destroy(cls);
    215. //注销驱动对象
    216. cdev_del(cdev);
    217. //释放设备号
    218. unregister_chrdev_region(MKDEV(major,minor),3);
    219. //释放对象空间
    220. kfree(cdev);
    221. }
    222. module_init(mycdev_init);
    223. module_exit(mycdev_exit);
    224. MODULE_LICENSE("GPL");
    proc.c
    1. #include <stdio.h>
    2. #include <sys/types.h>
    3. #include <sys/stat.h>
    4. #include <fcntl.h>
    5. #include <unistd.h>
    6. #include <stdlib.h>
    7. #include <string.h>
    8. #include <sys/ioctl.h>
    9. #include "head.h"
    10. int main(int argc, const char *argv[])
    11. {
    12. char buf[128] = "";
    13. int a;
    14. int fd;
    15. while (1)
    16. {
    17. printf("请选择要打开的灯(1,2,3)\n");
    18. scanf(" %d", &a);
    19. switch (a)
    20. {
    21. case 1:
    22. fd = open("/dev/myled0", O_RDWR);
    23. if (fd < 0)
    24. {
    25. printf("设备文件打开失败\n");
    26. exit(-1);
    27. }
    28. printf("打开文件myled0成功\n");
    29. break;
    30. case 2:
    31. fd = open("/dev/myled1", O_RDWR);
    32. if (fd < 0)
    33. {
    34. printf("设备文件打开失败\n");
    35. exit(-1);
    36. }
    37. printf("打开文件myled1成功\n");
    38. break;
    39. case 3:
    40. fd = open("/dev/myled2", O_RDWR);
    41. if (fd < 0)
    42. {
    43. printf("设备文件打开失败\n");
    44. exit(-1);
    45. }
    46. printf("打开文件myled2成功\n");
    47. break;
    48. default:
    49. printf("请输入范围内的数\n");
    50. }
    51. int b;
    52. printf("请开灯关灯(0/1)\n");
    53. scanf(" %d",&b);
    54. switch(b)
    55. {
    56. case 1:
    57. ioctl(fd,LED_ON);
    58. break;
    59. case 0:
    60. ioctl(fd,LED_OFF);
    61. break;
    62. default:
    63. printf("请输入'0'或'1'\n");
    64. }
    65. }
    66. close(fd);
    67. printf("关闭文件\n");
    68. return 0;
    69. }
    测试过程:

    make arch=arm modname=myled

    arm-linux-gnueabihf-gcc proc.c

     cp a.out ~/nfs/rootfs

    cp myled.ko ~/nfs/rootfs

    insmod myled.ko 

     ./a.out 

     rmmod myled3

    测试现象:

    linux内核定时器实例代码

    1. #include <linux/init.h>
    2. #include <linux/module.h>
    3. #include <linux/of.h>
    4. #include <linux/of_gpio.h>
    5. #include <linux/gpio.h>
    6. #include <linux/timer.h>
    7. struct device_node *dnode;
    8. struct gpio_desc* gpiono;
    9. //分配定时器对象
    10. struct timer_list mytimer;
    11. //设置一个定时器处理函数
    12. void mytimer_func(struct timer_list* timer)
    13. {
    14. //LED1一秒亮一秒灭
    15. gpiod_set_value(gpiono,!gpiod_get_value(gpiono));
    16. //再次启动定时器
    17. mod_timer(timer,jiffies+HZ);
    18. }
    19. static int __init mycdev_init(void)
    20. {
    21. dnode=of_find_node_by_path("/myled");
    22. if(dnode==NULL)
    23. {
    24. printk("解析设备树节点失败\n");
    25. return -ENXIO;
    26. }
    27. printk("解析GPIO信息成功\n");
    28. //申请gpio对象
    29. gpiono=gpiod_get_from_of_node(dnode,"led1-gpio",0,GPIOD_OUT_LOW,NULL);
    30. if(IS_ERR(gpiono))
    31. {
    32. printk("申请gpio对象失败\n");
    33. return -ENXIO;
    34. }
    35. printk("申请gpio信息对象成功\n");
    36. //初始化定时器对象
    37. timer_setup(&mytimer,mytimer_func,0);
    38. mytimer.expires=jiffies+HZ;
    39. //注册定时器
    40. add_timer(&mytimer);
    41. return 0;
    42. }
    43. static void __exit mycdev_exit(void)
    44. {
    45. //注销定时器
    46. del_timer(&mytimer);
    47. //灭灯
    48. gpiod_set_value(gpiono,0);
    49. //释放gpio编号
    50. gpiod_put(gpiono);
    51. }
    52. module_init(mycdev_init);
    53. module_exit(mycdev_exit);
    54. MODULE_LICENSE("GPL");

  • 相关阅读:
    MySQL进阶篇(2)—SQL优化、视图/存储过程/触发器
    as keyof GlobalStore
    torch_vision(一):数据增强和转换模块torchvision.transforms
    python机器学习入门之pandas的使用(超详细,必看)
    【Java基础】java.lang包中不能被继承的类
    LSTM基础
    python类属性和实例属性的注意点
    js之原型链
    C++ Reference: Standard C++ Library reference: C Library: cmath: nearbyint
    C语言之__attribute__((visibility(“default“)))等gcc flag讲解(六十二)
  • 原文地址:https://blog.csdn.net/m0_72852022/article/details/132982859