• 驱动开发,stm32mp157a开发板的led灯控制实验(优化),使用ioctl函数,让write/read函数的专注读写功能


     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.编写代码

    ---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第三个参数int
    18. #define LED_ON _IOW('l',1,int)
    19. #define LED_OFF _IOW('l',0,int)
    20. #endif
    ---led_drive.c---驱动程序
    1. #include <linux/init.h>
    2. #include <linux/module.h>
    3. #include <linux/fs.h>
    4. #include <linux/io.h>
    5. #include "head.h"
    6. #include <linux/device.h>
    7. char kbuf[128] = {0};
    8. unsigned int major;
    9. gpio_t *vir_led1;
    10. gpio_t *vir_led2;
    11. gpio_t *vir_led3;
    12. unsigned int *vir_rcc;
    13. struct class *cls;
    14. struct device *dev;
    15. // 封装操作方法
    16. int mycdev_open(struct inode *inode, struct file *file)
    17. {
    18. printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
    19. return 0;
    20. }
    21. long mycdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
    22. {
    23. int value;
    24. //获取arg对应用户空间中的值
    25. int ret = copy_from_user(&value,(void*)arg,4);
    26. if(ret)
    27. {
    28. printk("从用户空间获取数据失败\n");
    29. return -EIO;
    30. }
    31. switch (cmd)
    32. {
    33. case LED_ON: // 开灯
    34. switch (value)
    35. {
    36. case 1: // LED1
    37. vir_led1->ODR |= (0x1 << 10);
    38. break;
    39. case 2:
    40. vir_led2->ODR |= (0x1 << 10);
    41. break;
    42. case 3:
    43. vir_led3->ODR |= (0x1 << 8);
    44. break;
    45. }
    46. break;
    47. case LED_OFF: // 关灯
    48. switch (value)
    49. {
    50. case 1: // LED1
    51. vir_led1->ODR &= (~(0x1 << 10));
    52. break;
    53. case 2:
    54. vir_led2->ODR &= (~(0x1 << 10));
    55. break;
    56. case 3:
    57. vir_led3->ODR &= (~(0x1 << 8));
    58. break;
    59. }
    60. break;
    61. }
    62. return 0;
    63. }
    64. int mycdev_close(struct inode *inode, struct file *file)
    65. {
    66. printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
    67. return 0;
    68. }
    69. struct file_operations fops = {
    70. .open = mycdev_open,
    71. .unlocked_ioctl = mycdev_ioctl,
    72. .release = mycdev_close,
    73. };
    74. // 相关寄存器地址映射及初始化
    75. int all_led_init(void)
    76. {
    77. // 相关寄存器的内存映射
    78. vir_led1 = ioremap(LED1_ADDR, sizeof(gpio_t));
    79. if (vir_led1 == NULL)
    80. {
    81. printk("物理内存映射失败%d\n", __LINE__);
    82. return -ENOMEM;
    83. }
    84. vir_led2 = ioremap(LED2_ADDR, sizeof(gpio_t));
    85. if (vir_led2 == NULL)
    86. {
    87. printk("物理内存映射失败%d\n", __LINE__);
    88. return -ENOMEM;
    89. }
    90. vir_led3 = vir_led1;
    91. vir_rcc = ioremap(RCC_ADDR, 4);
    92. if (vir_rcc == NULL)
    93. {
    94. printk("物理内存映射失败%d\n", __LINE__);
    95. return -ENOMEM;
    96. }
    97. printk("寄存器内存映射成功\n");
    98. // 硬件寄存器的初始化
    99. (*vir_rcc) |= (0x3 << 4);
    100. // LED1
    101. vir_led1->MODER &= (~(0x3 << 20));
    102. vir_led1->MODER |= (0x1 << 20);
    103. vir_led1->ODR &= (~(0x1 << 10));
    104. // LED2
    105. vir_led2->MODER &= (~(0x3 << 20));
    106. vir_led2->MODER |= (0x1 << 20);
    107. vir_led2->ODR &= (~(0x1 << 10));
    108. // LED3
    109. vir_led3->MODER &= (~(0x3 << 16));
    110. vir_led3->MODER |= (0x1 << 16);
    111. vir_led3->ODR &= (~(0x1 << 8));
    112. printk("寄存器初始化成功\n");
    113. return 0;
    114. }
    115. // 入口函数
    116. static int __init mycdev_init(void)
    117. {
    118. major = register_chrdev(0, "mychrdev", &fops);
    119. if (major < 0)
    120. {
    121. printk("字符设备驱动注册失败\n");
    122. return major;
    123. }
    124. printk("字符设备驱动注册成功:major=%d\n", major);
    125. // 寄存器映射及初始化
    126. all_led_init();
    127. // 向上提交目录
    128. cls = class_create(THIS_MODULE, "mychrdev");
    129. if (IS_ERR(cls))
    130. {
    131. printk("向上提交目录失败\n");
    132. return -PTR_ERR(cls);
    133. }
    134. printk("向上提交目录成功\n");
    135. // 向上提交设备节点信息
    136. int i;
    137. for (i = 0; i < 3; i++)
    138. {
    139. dev = device_create(cls, NULL, MKDEV(major, i), NULL, "mychrdev%d", i);
    140. if (IS_ERR(dev))
    141. {
    142. printk("向上提交设备节点信息失败\n");
    143. return -PTR_ERR(dev);
    144. }
    145. }
    146. printk("向上提交设备节点信息成功\n");
    147. return 0;
    148. }
    149. // 出口函数
    150. static void __exit mycdev_exit(void)
    151. {
    152. // 销毁设备节点信息
    153. int i;
    154. for (i = 0; i < 3; i++)
    155. {
    156. device_destroy(cls, MKDEV(major, i));
    157. }
    158. // 销毁目录信息
    159. class_destroy(cls);
    160. // 取消物理内存的映射
    161. iounmap(vir_led1);
    162. iounmap(vir_led2);
    163. iounmap(vir_rcc);
    164. // 字符设备驱动注销
    165. unregister_chrdev(major, "mychrdev");
    166. }
    167. // 声明
    168. // 入口函数地址
    169. module_init(mycdev_init);
    170. // 出口函数地址
    171. module_exit(mycdev_exit);
    172. // 遵循的GPL协议
    173. 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,b;
    13. char buf[128] = {0};
    14. int fd = open("/dev/mychrdev0", 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. printf("请输入要控制的灯:1(LED1) 2(LED2) 3(LED3)>> ");
    26. scanf("%d",&b);
    27. //getchar();
    28. switch(a)
    29. {
    30. case 1:
    31. ioctl(fd,LED_ON,&b); //第三个参数为指针
    32. break;
    33. case 0:
    34. ioctl(fd,LED_OFF,&b);
    35. break;
    36. }
    37. }
    38. close(fd);
    39. return 0;
    40. }

    4.测试现象

  • 相关阅读:
    【AI绘画】免费GPU Tesla A100 32G算力部署Stable Diffusion
    改善深层神经网络 - TensorFlow入门
    【6】c++设计模式——>UML表示类之间的依赖关系
    【面试题精讲】Java包装类缓存机制
    Nginx的http启动流程分析
    Java定时任务最详细讲解(普通项目,Spring项目)
    (2022最新)Java毕业设计参考题目-题目新颖(值得收藏)
    java废旧物品回收管理系统计算机毕业设计MyBatis+系统+LW文档+源码+调试部署
    多表操作-外键级联操作
    Neo4j和Cypher快速入门
  • 原文地址:https://blog.csdn.net/weixin_46260677/article/details/132859076