• 嵌入式:驱动开发 Day7


    作业:基于GPIO子系统,编写LED的驱动程序和应用程序

    驱动程序:myled.c

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. #include
    8. #include
    9. #include
    10. #include
    11. #include
    12. #include "head.h"
    13. struct cdev *cdev;
    14. unsigned int major = 0;
    15. unsigned int minor = 0;
    16. dev_t devno;
    17. module_param(major, uint, 0664); //方便命令行传递major的值
    18. struct class *cls;
    19. struct device *dev;
    20. struct device_node *dnode;
    21. struct gpio_desc *gpiono[3];
    22. //封装操作方法
    23. int mycdev_open(struct inode *inode, struct file *file)
    24. {
    25. int min = MINOR(inode->i_rdev); //根据打开的文件对应的设备号获取次设备号
    26. file->private_data = (void *)min;
    27. printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
    28. return 0;
    29. }
    30. long mycdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
    31. {
    32. int min = (int)file->private_data; //传递次设备号
    33. //设备文件和设备的绑定,根据次设备号决定打开的设备文件,根据打开的设备文件,控制相应的设备
    34. switch (min)
    35. {
    36. case 0: //控制LED1
    37. switch (cmd)
    38. {
    39. case LED_ON: //开灯
    40. gpiod_set_value(gpiono[0], 1);
    41. break;
    42. case LED_OFF: //关灯
    43. gpiod_set_value(gpiono[0], 0);
    44. break;
    45. }
    46. break;
    47. case 1: //控制LED2
    48. switch (cmd)
    49. {
    50. case LED_ON: //开灯
    51. gpiod_set_value(gpiono[1], 1);
    52. break;
    53. case LED_OFF: //关灯
    54. gpiod_set_value(gpiono[1], 0);
    55. break;
    56. }
    57. break;
    58. case 2: //控制LED3
    59. switch (cmd)
    60. {
    61. case LED_ON: //开灯
    62. gpiod_set_value(gpiono[2], 1);
    63. break;
    64. case LED_OFF: //关灯
    65. gpiod_set_value(gpiono[2], 0);
    66. break;
    67. }
    68. break;
    69. }
    70. return 0;
    71. }
    72. ssize_t mycdev_read(struct file *file, char *ubuf, size_t size, loff_t *lof)
    73. {
    74. return 0;
    75. }
    76. ssize_t mycdev_write(struct file *file, const char *ubuf, size_t size, loff_t *lof)
    77. {
    78. return 0;
    79. }
    80. int mycdev_close(struct inode *inode, struct file *file)
    81. {
    82. printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
    83. return 0;
    84. }
    85. //定义一个操作方法结构体对象并初始化
    86. struct file_operations fops = {
    87. .open = mycdev_open,
    88. .unlocked_ioctl = mycdev_ioctl,
    89. .read = mycdev_read,
    90. .write = mycdev_write,
    91. .release = mycdev_close,
    92. };
    93. static int __init mycdev_init(void)
    94. {
    95. //解析LED的设备树节点
    96. dnode = of_find_node_by_path("/myled");
    97. if (dnode == NULL)
    98. {
    99. printk("解析设备树节点失败\n");
    100. return -ENXIO;
    101. }
    102. //申请gpio对象
    103. int j;
    104. char nodename[20];
    105. for (j = 0; j < 3; j++)
    106. {
    107. sprintf(nodename, "led%d-gpio", j + 1);
    108. gpiono[j] = gpiod_get_from_of_node(dnode, nodename, 0, GPIOD_OUT_LOW, NULL);
    109. if (IS_ERR(gpiono[j]))
    110. {
    111. printk("申请gpiono%d失败\n", j);
    112. goto out0;
    113. }
    114. }
    115. int ret;
    116. //1.为字符设备驱动对象申请空间
    117. cdev = cdev_alloc();
    118. if (cdev == NULL)
    119. {
    120. printk("申请字符设备驱动对象空间失败\n");
    121. ret = -EFAULT;
    122. goto out1;
    123. }
    124. printk("申请字符设备驱动对象空间成功\n");
    125. //2.初始化字符设备驱动对象
    126. cdev_init(cdev, &fops);
    127. printk("初始化字符设备驱动对象成功\n");
    128. //3.申请设备号
    129. if (major > 0) //静态指定设备号
    130. {
    131. ret = register_chrdev_region(MKDEV(major, minor), 3, "myled");
    132. if (ret)
    133. {
    134. printk("静态申请设备号失败\n");
    135. goto out2;
    136. }
    137. }
    138. else if (major == 0)
    139. { //动态申请设备号
    140. ret = alloc_chrdev_region(&devno, minor, 3, "myled");
    141. if (ret)
    142. {
    143. printk("动态申请设备号失败\n");
    144. goto out2;
    145. }
    146. major = MAJOR(devno); //获取主设备号
    147. minor = MINOR(devno); //获取次设备号
    148. }
    149. printk("申请设备号成功 major= %d\n", major);
    150. //4.注册字符设备驱动对象
    151. ret = cdev_add(cdev, MKDEV(major, minor), 3);
    152. if (ret)
    153. {
    154. printk("注册字符设备驱动对象失败\n");
    155. goto out3;
    156. }
    157. printk("注册字符设备驱动对象成功\n");
    158. //向上提交目录信息
    159. cls = class_create(THIS_MODULE, "myled");
    160. if (IS_ERR(cls))
    161. {
    162. printk("向上提交目录失败\n");
    163. ret = -PTR_ERR(cls);
    164. goto out4;
    165. }
    166. printk("向上提交目录成功\n");
    167. //向上提交设备节点信息
    168. int i;
    169. for (i = 0; i < 3; i++)
    170. {
    171. dev = device_create(cls, NULL, MKDEV(major, i), NULL, "myled%d", i);
    172. if (IS_ERR(dev))
    173. {
    174. printk("向上提交设备节点信息失败\n");
    175. ret = -PTR_ERR(dev);
    176. goto out5;
    177. }
    178. }
    179. printk("向上提交设备信息成功\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. //注销字符设备驱动对象
    190. cdev_del(cdev);
    191. out3:
    192. //释放设备号
    193. unregister_chrdev_region(MKDEV(major, minor), 3);
    194. out2:
    195. //释放设备驱动对象空间
    196. kfree(cdev);
    197. out1:
    198. return ret;
    199. out0:
    200. //释放前一次申请成功gpiono编号
    201. for (--j; j >= 0; j--)
    202. {
    203. gpiod_put(gpiono[j]);
    204. }
    205. return -1;
    206. }
    207. static void __exit mycdev_exit(void)
    208. {
    209. int i;
    210. //释放设备节点信息
    211. for (i = 0; i < 3; i++)
    212. {
    213. device_destroy(cls, MKDEV(major, i));
    214. }
    215. //销毁目录
    216. class_destroy(cls);
    217. //注销字符设备驱动对象
    218. cdev_del(cdev);
    219. //释放设备号
    220. unregister_chrdev_region(MKDEV(major, minor), 3);
    221. //释放设备驱动对象空间
    222. kfree(cdev);
    223. //释放gpiono编号
    224. for (i = 0; i < 3; i++)
    225. {
    226. gpiod_put(gpiono[i]);
    227. }
    228. }
    229. module_init(mycdev_init);
    230. module_exit(mycdev_exit);
    231. MODULE_LICENSE("GPL");

    应用程序:test.c

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. #include
    8. #include
    9. #include "head.h"
    10. int main()
    11. {
    12. int a, b;
    13. char buf[128] = "";
    14. int fd1, fd2, fd3;
    15. while (1)
    16. {
    17. printf("请输入要控制的灯:0(LED1) 1(LED2) 2(LED3) >");
    18. scanf("%d", &a);
    19. switch (a)
    20. {
    21. case 0:
    22. fd1 = open("/dev/myled0", O_RDWR);
    23. if (fd1 < 0)
    24. {
    25. printf("设备文件打开失败\n");
    26. exit(-1);
    27. }
    28. printf("请输入对LED的控制命令:0(关灯) 1(开灯) >");
    29. scanf("%d", &b);
    30. switch (b)
    31. {
    32. case 0:
    33. ioctl(fd1, LED_OFF); //关灯
    34. close(fd1);
    35. break;
    36. case 1:
    37. ioctl(fd1, LED_ON); //开灯
    38. close(fd1);
    39. break;
    40. }
    41. break;
    42. case 1:
    43. fd2 = open("/dev/myled1", O_RDWR);
    44. if (fd2 < 0)
    45. {
    46. printf("设备文件打开失败\n");
    47. exit(-1);
    48. }
    49. printf("请输入对LED的控制命令:0(关灯) 1(开灯) >");
    50. scanf("%d", &b);
    51. switch (b)
    52. {
    53. case 0:
    54. ioctl(fd2, LED_OFF); //关灯
    55. close(fd2);
    56. break;
    57. case 1:
    58. ioctl(fd2, LED_ON); //开灯
    59. close(fd2);
    60. break;
    61. }
    62. break;
    63. case 2:
    64. fd3 = open("/dev/myled2", O_RDWR);
    65. if (fd3 < 0)
    66. {
    67. printf("设备文件打开失败\n");
    68. exit(-1);
    69. }
    70. printf("请输入对LED的控制命令:0(关灯) 1(开灯) >");
    71. scanf("%d", &b);
    72. switch (b)
    73. {
    74. case 0:
    75. ioctl(fd3, LED_OFF); //关灯
    76. close(fd3);
    77. break;
    78. case 1:
    79. ioctl(fd3, LED_ON); //开灯
    80. close(fd3);
    81. break;
    82. }
    83. break;
    84. }
    85. }
    86. close(fd1);
    87. close(fd2);
    88. close(fd3);
    89. return 0;
    90. }

    头文件: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

    现象:

  • 相关阅读:
    设计模式-享元模式
    opencv安装成功之后运行代码还是出错
    021-Qt 配置GitHub Copilot
    MySQL日志管理、备份与恢复
    java8-Stream流常用API
    WMS仓储管理系统的功能有哪些?
    ElasticSearch的文档、字段、映射和高级查询
    CSDN编程竞赛 ——— 第十期
    论文篇 | 2020-Facebook-DERT :利用transformers端到端的目标检测=>翻译及理解(持续更新中)
    计算机网络——传输层の选择题整理
  • 原文地址:https://blog.csdn.net/jx4252/article/details/132993438