• 驱动开发,stm32mp157a开发板的led灯控制实验(再优化),使用ioctl函数,通过字符设备驱动分步注册方式编写LED驱动,完成设备文件和设备的绑定


     1.实验目的

            编写LED灯的驱动,在应用程序中编写控制LED灯亮灭的代码逻辑实现LED灯功能的控制;

     

     2. LED灯相关寄存器分析

     

    LED1->PE10 LED1亮灭:

    RCC寄存器[4]->1 0X50000A28

    GPIOE_MODER[21:20]->01 (输出) 0X50006000

    GPIOE_ODR[10]->1(输出高电平) 0(输出低电平)0X50006014

    LED2->PF10 LED2亮灭:

    RCC寄存器[5]->1 0X50000A28

    GPIOE_MODER[21:20]->01 (输出) 0X50006000

    GPIOE_ODR[10]->1(输出高电平) 0(输出低电平)0X50006014

    LED3->PE8 LED3亮灭:

    RCC寄存器[4]->1 0X50000A28

    GPIOE_MODER[17:16]->01 (输出) 0X50006000

    GPIOE_ODR[8]->1(输出高电平) 0(输出低电平)0X50006014

    GPIOE_OTYPER默认为00

    GPIOE_PUPDR默认为0

    GPIOE_OSPEEDR默认为00

     

     3.字符设备驱动内部注册过程 

    • 分配struct cdev对象空间
    • 初始化struct cdev对象
    • 设备号的申请(静态/动态申请)
    • 注册cdev对象

     

     4.编写代码

    ---Makefile---工程管理文件

    1. modname?=demo
    2. arch?=arm
    3. ifeq ($(arch),arm)
    4. KERNELDIR:= /home/ubuntu/FSMP1A/linux-stm32mp-5.10.61-stm32mp-r2-r0/linux-5.10.61 #编译生成ARM架构
    5. else
    6. KERNELDIR:=/lib/modules/$(shell uname -r)/build #编译生成X86架构
    7. endif
    8. PWD:=$(shell pwd) #模块化编译文件路径
    9. all:
    10. make -C $(KERNELDIR) M=$(PWD) modules
    11. clean:
    12. make -C $(KERNELDIR) M=$(PWD) clean
    13. obj-m:=$(modname).o

    ---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. //LED1和LED3寄存器地址
    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

    ---cdev.c---驱动程序

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

    ---test.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 "head.h"
    9. #include <sys/ioctl.h>
    10. int main(int argc, char const *argv[])
    11. {
    12. int a;
    13. char buf[128] = {0};
    14. int fd = open("/dev/myled0", O_RDWR);
    15. if (fd < 0)
    16. {
    17. printf("设备文件打开失败\n");
    18. exit(-1);
    19. }
    20. while (1)
    21. {
    22. printf("请输入对LED灯的控制:1(开灯) 0(关灯)>> ");
    23. scanf("%d",&a);
    24. getchar();
    25. switch (a)
    26. {
    27. case 1:
    28. ioctl(fd, LED_ON); // 第三个参数为指针
    29. break;
    30. case 0:
    31. ioctl(fd, LED_OFF);
    32. break;
    33. }
    34. }
    35. close(fd);
    36. return 0;
    37. }

     

    5.测试现象

             int fd = open("/dev/myled0", O_RDWR);

            测试程序中只打开一个设备文件,对应LED1的次设备号,所以只控制LED1灯的亮灭

  • 相关阅读:
    Nifi01概念
    微信小程序分享、转发朋友、分享朋友圈使用整理
    css3的attr函数使用,加载unicode矢量图标
    机器学习算法 —— 1. K近邻算法
    容错限流框架之Hystrix上
    Flask学习笔记(七)
    专属程序员的浪漫七夕
    AUGMENTING LOGICAL REASONING CAPABILITIES WITH LARGE LANGUAGE MODELS
    3D数字孪生:从3D数据采集到3D内容分析
    Linux--socket编程--服务端代码
  • 原文地址:https://blog.csdn.net/weixin_46260677/article/details/132864440