• 驱动开发4 使用字符设备驱动的分步实现编写LED驱动(LED亮灯)


    一、思维导图

    二、通过字符设备驱动的分步实现编写LED驱动,另外实现特备文件和设备的绑定

    应用程序 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(int argc, char const *argv[])
    11. {
    12. char buf[128]={0};
    13. int a;
    14. int fd=open("/dev/mycdev0",O_RDWR);
    15. if(fd<0)
    16. {
    17. printf("打开设备文件失败\n");
    18. exit(-1);
    19. }
    20. while(1)
    21. {
    22. //从终端读取
    23. printf("请输入要实现的功能 ");
    24. printf("0(关灯) 1(开灯)\n");
    25. printf("请输入>");
    26. scanf("%d",&a);
    27. switch(a)
    28. {
    29. case 1:
    30. ioctl(fd,LED_ON);
    31. break;
    32. case 0:
    33. ioctl(fd,LED_OFF);
    34. break;
    35. }
    36. }
    37. close(fd);
    38. return 0;
    39. }

    头文件 head.h

    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. #define PHY_LED1_ADDR 0X50006000
    13. #define PHY_LED2_ADDR 0X50007000
    14. #define PHY_LED3_ADDR 0X50006000
    15. #define PHY_RCC_ADDR 0X50000A28
    16. // 构建开灯关灯的功能码
    17. #define LED_ON _IO('l', 1)
    18. #define LED_OFF _IO('l', 0)
    19. #endif

    驱动程序 mycdev.c

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

    Makefile

    1. modname ?= demo
    2. arch ?= arm
    3. ifeq ($(arch),arm) #通过命令行传过来的架构决定怎么编译
    4. #KERBELDIR保存开发板内核源码路径
    5. KERNELDIR := /home/ubuntu/FSMP1A/linux-stm32mp-5.10.61-stm32mp-r2-r0/linux-5.10.61
    6. else
    7. #保存UBUNTU内核源码路径
    8. KERNELDIR := /lib/modules/$(shell uname -r)/build
    9. endif
    10. #PWD保存当前内核模块的路径
    11. PWD := $(shell pwd)
    12. all:
    13. #make modules是模块化编译命令
    14. #make -C $(KERNLEDIR) 执行make之前先切换到KERNELDIR对应的路径
    15. #M=$(PWD)表示进行模块化编译的路径是PWD保存的路径
    16. make -C $(KERNELDIR) M=$(PWD) modules
    17. clean:
    18. #编译清除
    19. make -C $(KERNELDIR) M=$(PWD) clean
    20. #将obj-m保存的文件单独链接为内核模块
    21. obj-m := $(modname).o

    效果实现

  • 相关阅读:
    8月更新 | Java on Azure Tooling
    电脑系统没有standard tcp/ip port端口的处理操作
    ElementUI validate 验证结果错误的问题解决过程
    2022 年首届钉钉杯大学生大数据挑战赛初赛的Baseline
    01 【版本控制和Git的安装介绍】
    Linux下安装Docker(centOS 8)
    一家公司做了两年软件测试,只会功能测试,现在已经感到危机感了,那如何摆脱困境呢?
    研究生总结
    Master PDF Editor v5.9.70便携版
    2017年网易校招Java面试题
  • 原文地址:https://blog.csdn.net/Smallxu_/article/details/133997194