• 华清远见嵌入式学习——驱动开发——作业1


    作业要求:

    通过字符设备驱动分步注册过程实现LED驱动的编写,编写应用程序测试,发布到CSDN

    作业答案:

    运行效果:

    驱动代码:

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

    应用程序:

    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. int a,b;
    13. int fd=open("/dev/myled0",O_RDWR);
    14. if(fd<0)
    15. {
    16. printf("打开设备文件失败\n");
    17. exit(-1);
    18. }
    19. while(1)
    20. {
    21. //从终端读取
    22. printf("请输入要实现的功能\n");
    23. printf("0(关灯) 1(开灯)\n");
    24. printf("请输入>");
    25. scanf("%d",&a);
    26. printf("请输入要控制的灯\n");
    27. printf("1(LED1) 2(LED2) 3(LED3)\n");
    28. printf("请输入>");
    29. scanf("%d",&b);
    30. switch(a)
    31. {
    32. case 1:
    33. ioctl(fd,LED_ON,&b);
    34. break;
    35. case 0:
    36. ioctl(fd,LED_OFF,&b);
    37. break;
    38. }
    39. }
    40. close(fd);
    41. return 0;
    42. }

    头文件:

    1. #ifndef __HEAD_H__
    2. #define __HEAD_H__
    3. typedef struct{
    4. unsigned int MODER;
    5. unsigned int OTYPER;
    6. unsigned int OSPEEDR;
    7. unsigned int PUPDR;
    8. unsigned int IDR;
    9. unsigned int ODR;
    10. }gpio_t;
    11. #define PHY_LED1_ADDR 0X50006000
    12. #define PHY_LED2_ADDR 0X50007000
    13. #define PHY_LED3_ADDR 0X50006000
    14. #define PHY_RCC_ADDR 0X50000A28
    15. //构建功能码
    16. #define LED_ON _IOW('l',1,int)
    17. #define LED_OFF _IOW('l',0,int)
    18. #endif

  • 相关阅读:
    Maven下载、安装、配置教程
    Docker学习笔记
    开发Qt上位机获取阿里云IOT设备数据(开发上位机对接阿里云IOT平台)
    Redis主从复制的核心原理
    输入一个字符串,统计其中字符A的数量并且输出,输入共有一行,为一个不带空格的字符串(其中字符数不超过100),输出一行,包含一个整数,为输入字符串中的A的数量
    人人都会Kubernetes(二):使用KRM实现快速部署服务,并且通过域名发布
    黑马点评--分布式锁
    tiup cluster import
    (十三)【Jmeter】线程(Threads(Users))之tearDown 线程组
    vue3 globalData 的使用方法
  • 原文地址:https://blog.csdn.net/qq_54502935/article/details/136155088