• 【驱动开发】LED灯的亮灭——通过字符设备驱动的分步实现编写LED驱动,实现设备文件和设备的绑定


    头文件:

    1. #ifndef __HEAD_H__
    2. #define __HEAD_H__
    3. typedef struct
    4. {
    5. unsigned int MODER;
    6. unsigned int OTYPER;
    7. unsigned int OSPEEDR;
    8. unsigned int PUPDR;
    9. unsigned int IDR;
    10. unsigned int ODR;
    11. }gpio_t;
    12. //LED灯的寄存器地址
    13. #define LED1_ADDR 0X50006000
    14. #define LED2_ADDR 0X50007000
    15. #define LED3_ADDR 0X50006000
    16. #define RCC_ADDR 0X50000A28
    17. //构建LED开关的功能码,ioctl第三个参数
    18. #define LED_ON _IO('l',1)
    19. #define LED_OFF _IO('l',0)
    20. #endif

    驱动程序:

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include "head.h"
    7. #include
    8. #include
    9. #include
    10. struct cdev *cdev = NULL;
    11. unsigned major = 0;
    12. unsigned minor = 0;
    13. module_param(major, uint, 0664);
    14. dev_t devno;
    15. char kbuf[128] = {0};
    16. gpio_t *vir_led1;
    17. gpio_t *vir_led2;
    18. gpio_t *vir_led3;
    19. // 定义指针指向映射后的虚拟内存
    20. unsigned int *vir_rcc;
    21. struct class *cls;
    22. struct device *dev;
    23. // 封装操作方法
    24. int mycdev_open(struct inode *inode, struct file *file)
    25. {
    26. // 设备文件和设备绑定
    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 mycdev_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: // 控制LED1
    39. switch (cmd)
    40. {
    41. case LED_ON: // 开灯
    42. vir_led1->ODR |= (0x1 << 10);
    43. break;
    44. case LED_OFF: // 关灯
    45. vir_led1->ODR &= (~(0x1 << 10));
    46. break;
    47. }
    48. break;
    49. case 1: // 控制LED2
    50. switch (cmd)
    51. {
    52. case LED_ON: // 开灯
    53. vir_led2->ODR |= (0x1 << 10);
    54. break;
    55. case LED_OFF: // 关灯
    56. vir_led2->ODR &= (~(0x1 << 10));
    57. break;
    58. }
    59. break;
    60. case 2: // 控制LED3
    61. switch (cmd)
    62. {
    63. case LED_ON: // 开灯
    64. vir_led3->ODR |= (0x1 << 8);
    65. break;
    66. case LED_OFF: // 关灯
    67. vir_led3->ODR &= (~(0x1 << 8));
    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 = mycdev_ioctl,
    83. .release = mycdev_close,
    84. };
    85. int all_led_init(void)
    86. {
    87. // 寄存器地址的映射
    88. vir_led1 = ioremap(LED1_ADDR, sizeof(gpio_t));
    89. if (vir_led1 == NULL)
    90. {
    91. printk("ioremap filed:%d\n", __LINE__);
    92. return -ENOMEM;
    93. }
    94. vir_led2 = ioremap(LED2_ADDR, sizeof(gpio_t));
    95. if (vir_led2 == NULL)
    96. {
    97. printk("ioremap filed:%d\n", __LINE__);
    98. return -ENOMEM;
    99. }
    100. vir_led3 = vir_led1;
    101. vir_rcc = ioremap(RCC_ADDR, 4);
    102. if (vir_rcc == NULL)
    103. {
    104. printk("ioremap filed:%d\n", __LINE__);
    105. return -ENOMEM;
    106. }
    107. printk("物理地址映射成功\n");
    108. // 寄存器的初始化
    109. // rcc
    110. (*vir_rcc) |= (3 << 4);
    111. // led1
    112. vir_led1->MODER &= (~(3 << 20));
    113. vir_led1->MODER |= (1 << 20);
    114. vir_led1->ODR &= (~(1 << 10));
    115. // led2
    116. vir_led2->MODER &= (~(3 << 20));
    117. vir_led2->MODER |= (1 << 20);
    118. vir_led2->ODR &= (~(1 << 10));
    119. // led3
    120. vir_led3->MODER &= (~(3 << 16));
    121. vir_led1->MODER |= (1 << 16);
    122. vir_led1->ODR &= (~(1 << 8));
    123. printk("寄存器初始化成功\n");
    124. return 0;
    125. }
    126. static int __init mycdev_init(void)
    127. {
    128. // 1.申请一个对象空间cdev_alloc
    129. int ret;
    130. cdev = cdev_alloc();
    131. if (cdev == NULL)
    132. {
    133. printk("申请字符设备驱动对象失败\n");
    134. ret = -EFAULT;
    135. goto out1;
    136. }
    137. printk("字符设备驱动对象申请成功\n");
    138. // 2.初始化对象cdev_init
    139. cdev_init(cdev, &fops);
    140. // 3.申请设备号 register_chrdev_region()/alloc_chrdev_region()
    141. if (major == 0) // 动态申请
    142. {
    143. ret = alloc_chrdev_region(&devno, minor, 3, "mychrdev");
    144. if (ret)
    145. {
    146. printk("动态申请设备号失败\n");
    147. goto out2;
    148. }
    149. major = MAJOR(devno); // 根据设备号获取主设备号
    150. minor = MINOR(devno); // 根据设备号获取次设备号
    151. }
    152. else
    153. {
    154. ret = register_chrdev_region(MKDEV(major, minor), 3, "mychrdev");
    155. if (ret)
    156. {
    157. printk("静态指定设备号失败\n");
    158. goto out2;
    159. }
    160. }
    161. printk("设备号申请成功\n");
    162. // 4.注册驱动对象 cdev_add
    163. ret = cdev_add(cdev, MKDEV(major, minor), 3);
    164. if (ret)
    165. {
    166. printk("注册字符设备驱动对象失败\n");
    167. goto out3;
    168. }
    169. printk("注册字符设备驱动对象成功\n");
    170. // 相关寄存器地址映射及初始化
    171. all_led_init();
    172. // 5.向上提交目录 class_create
    173. cls = class_create(THIS_MODULE, "mychrdev");
    174. if (IS_ERR(cls))
    175. {
    176. printk("向上提交目录失败\n");
    177. goto out4;
    178. }
    179. printk("向上提交目录成功\n");
    180. // 6.向上提交设备节点信息 device_create
    181. int i;
    182. for (i = 0; i < 3; i++)
    183. {
    184. dev = device_create(cls, NULL, MKDEV(major, i), NULL, "mycdev%d", i);
    185. if (IS_ERR(dev))
    186. {
    187. printk("向上提交设备节点失败\n");
    188. goto out5;
    189. }
    190. }
    191. printk("向上提交设备节点信息成功\n");
    192. return 0;
    193. out5:
    194. // 将提交成功的节点信息释放
    195. for (--i; i >= 0; i--)
    196. {
    197. device_destroy(cls, MKDEV(major, i));
    198. }
    199. // 销毁目录
    200. class_destroy(cls);
    201. out4:
    202. cdev_del(cdev);
    203. out3:
    204. unregister_chrdev_region(MKDEV(major, minor), 3);
    205. out2:
    206. kfree(cdev);
    207. out1:
    208. return ret;
    209. }
    210. static void __exit mycdev_exit(void)
    211. {
    212. // 1.销毁设备节点信息
    213. int i;
    214. for (i = 0; i < 3; i++)
    215. {
    216. device_destroy(cls, MKDEV(major, i));
    217. }
    218. // 2.销毁目录
    219. class_destroy(cls);
    220. // 3.注销字符设备驱动对象
    221. cdev_del(cdev);
    222. // 4.释放设备号
    223. unregister_chrdev_region(MKDEV(major, minor), 3);
    224. // 5.释放申请到的字符设备驱动对象空间
    225. kfree(cdev);
    226. // 取消物理内存的映射
    227. iounmap(vir_led1);
    228. iounmap(vir_led2);
    229. iounmap(vir_rcc);
    230. }
    231. module_init(mycdev_init);
    232. module_exit(mycdev_exit);
    233. MODULE_LICENSE("GPL");

    应用程序: 

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. #include
    8. #include "head.h"
    9. #include
    10. int main(int argc, char const *argv[])
    11. {
    12. int a;
    13. char buf[128] = {0};
    14. int fd = open("/dev/mycdev0", O_RDWR);
    15. if (fd < 0)
    16. {
    17. printf("打开设备文件失败\n");
    18. return -1;
    19. }
    20. printf("打开设备文件成功\n");
    21. while (1)
    22. {
    23. // 从终端读取
    24. printf("请输入要实现的功能 ");
    25. printf("0(关灯) 1(开灯)\n");
    26. printf("请输入>>>");
    27. scanf("%d", &a);
    28. getchar();
    29. switch (a)
    30. {
    31. case 1:
    32. ioctl(fd, LED_ON); // 第三个参数为指针
    33. break;
    34. case 0:
    35. ioctl(fd, LED_OFF);
    36. break;
    37. }
    38. }
    39. close(fd);
    40. return 0;
    41. }

  • 相关阅读:
    MongoDB配置文件.conf配置项介绍
    导入导出问题
    Qt还是尽量使用UTF8编码
    记一次生产频繁发生FullGC问题
    2、自然语言和单词的分布式表示(中)
    Okhttp源码解析笔记
    【强化学习】时间循环最优决策:原理与Python实战
    嵌入式硬件设计实例:基于STM32的流水灯原理图和PCB设计
    【Machine Learning】17.多分类问题
    Linux文件系统
  • 原文地址:https://blog.csdn.net/m0_68542867/article/details/133998827