当前位置:   article > 正文

【IMX6ULL驱动开发学习】08.马达驱动实战:驱动编写、手动注册平台设备和设备树添加节点信息_imx6ul驱动开发

imx6ul驱动开发

目录

一、使用设备树

1.1 修改设备树流程

二、手动创建平台设备 

三、总结(附驱动程序)


前情提要:【IMX6ULL驱动开发学习】07.驱动程序分离的思想之平台总线设备驱动模型和设备树_阿龙还在写代码的博客-CSDN博客

手动注册平台设备和设备树的目的都是为了构造platform_device结构体

一、使用设备树

之前的驱动编写方式,引脚信息在驱动程序里写死了,移植性差。而使用设备树,这样的程序编写方式便于在换了板子或引脚之后,只要修改设备树的节点信息即可,不需要看复杂的驱动程序,前提是有完备的驱动程序。

在驱动程序的入口函数里注册了gpio_platform_driver,

  1. module_init(gpio_drv_init);
  2. static int __init gpio_drv_init(void)
  3. {
  4. /* 注册platform_driver */
  5. return platform_driver_register(&gpio_platform_driver);
  6. }

gpio_platform_driver结构体中probe函数被调用,需要在设备树中找到与之匹配的设备节点信息,即设备节点信息也要含有这个语句.compatible = "100ask,gpiodemo"

  1. static const struct of_device_id gpio_dt_ids[] = {
  2. { .compatible = "100ask,gpiodemo", },
  3. { /* sentinel */ }
  4. };
  5. static struct platform_driver gpio_platform_driver = {
  6. .driver = {
  7. .name = "100ask_gpio_plat_drv",
  8. .of_match_table = gpio_dt_ids,
  9. },
  10. .probe = gpio_drv_probe,
  11. .remove = gpio_drv_remove,
  12. };

1.1 修改设备树流程

在内核目录下找到设备树文件,以我的路径为例,这里给出绝对路径

  • 修改设备树:vi /home/book/100ask_imx6ull-sdk/Linux-4.9.88/arch/arm/boot/dts/100ask_imx6ull-14x14.dts
  • 增加节点信息如下:
  1. motor {
  2. compatible = "100ask,gpiodemo";
  3. gpios = <&gpio4 19 GPIO_ACTIVE_HIGH>,
  4. <&gpio4 20 GPIO_ACTIVE_HIGH>,
  5. <&gpio4 21 GPIO_ACTIVE_HIGH>,
  6. <&gpio4 22 GPIO_ACTIVE_HIGH>;
  7. };
  • 一定要在/home/book/100ask_imx6ull-sdk/Linux-4.9.88目录下编译设备树:make dtbs
  • 复制到单板上,如下:
  1. PC:
  2. cp arch/arm/boot/dts/100ask_imx6ull-14x14.dtb ~/nfs_rootfs/
  3. 开发板:
  4. mount -t nfs -o nolock,vers=3 192.168.5.11:/home/book/nfs_rootfs /mnt
  5. cp /mnt/100ask_imx6ull-14x14.dtb /boot
  6. reboot
  • 查看设备树节点信息:ls /sys/firmware/devicetree/base/ 

  •  查看马达节点有没有被转换成平台设备(platform_device):ls /sys/bus/platform/devices/

 

 有这个motor文件表示有了对应的平台设备。

  • 进入motor文件查看,因为还没装驱动,所以没有显示驱动程序

  • 装载驱动后,查看motor文件: 说明驱动和设备都匹配上了

  • 最后就编写测试程序进行测试即可: 
./button_test /dev/motor ...

二、手动创建平台设备 

  • 编写dev.c文件
  1. #include <linux/module.h>
  2. #include <linux/poll.h>
  3. #include <linux/delay.h>
  4. #include <linux/fs.h>
  5. #include <linux/errno.h>
  6. #include <linux/miscdevice.h>
  7. #include <linux/kernel.h>
  8. #include <linux/major.h>
  9. #include <linux/mutex.h>
  10. #include <linux/proc_fs.h>
  11. #include <linux/seq_file.h>
  12. #include <linux/stat.h>
  13. #include <linux/init.h>
  14. #include <linux/device.h>
  15. #include <linux/tty.h>
  16. #include <linux/kmod.h>
  17. #include <linux/gfp.h>
  18. #include <linux/gpio/consumer.h>
  19. #include <linux/platform_device.h>
  20. #include <linux/of_gpio.h>
  21. #include <linux/of_irq.h>
  22. #include <linux/interrupt.h>
  23. #include <linux/irq.h>
  24. #include <linux/slab.h>
  25. #include <linux/fcntl.h>
  26. #include <linux/timer.h>
  27. static struct resource my_drv_resource[] = {
  28. {
  29. .start = 115,
  30. .end = 115,
  31. .flags = IORESOURCE_IRQ,
  32. },
  33. {
  34. .start = 116,
  35. .end = 116,
  36. .flags = IORESOURCE_IRQ,
  37. },
  38. {
  39. .start = 117,
  40. .end = 117,
  41. .flags = IORESOURCE_IRQ,
  42. },
  43. {
  44. .start = 118,
  45. .end = 118,
  46. .flags = IORESOURCE_IRQ,
  47. },
  48. };
  49. static struct platform_device gpio_platform_device = {
  50. .name = "100ask_gpio_plat_drv",
  51. .id = 0,
  52. .num_resources = ARRAY_SIZE(my_drv_resource),// =4 宏来计算数组大小
  53. .resource = my_drv_resource,
  54. };
  55. static int __init gpio_dev_init(void)
  56. {
  57. /* 注册platform_driver */
  58. return platform_device_register(&gpio_platform_device);
  59. }
  60. static void __exit gpio_dev_exit(void)
  61. {
  62. /* 反注册platform_driver */
  63. platform_device_unregister(&gpio_platform_device);
  64. }
  65. /* 7. 其他完善:提供设备信息,自动创建设备节点 */
  66. module_init(gpio_dev_init);
  67. module_exit(gpio_dev_exit);
  68. MODULE_LICENSE("GPL");

 其中,由这个名字"100ask_gpio_plat_drv"找到与之匹配的平台驱动(platform_driver)

  1. static struct platform_device gpio_platform_device = {
  2. .name = "100ask_gpio_plat_drv",
  3. .id = 0,
  4. .num_resources = ARRAY_SIZE(my_drv_resource),// =4 宏来计算数组大小
  5. .resource = my_drv_resource,
  6. };
  •  修改Makefile文件
  1. KERN_DIR = /home/book/100ask_imx6ull-sdk/Linux-4.9.88 # 板子所用内核源码的目录
  2. all:
  3. make -C $(KERN_DIR) M=`pwd` modules
  4. $(CROSS_COMPILE)gcc -o button_test button_test.c
  5. clean:
  6. make -C $(KERN_DIR) M=`pwd` modules clean
  7. rm -rf modules.order button_test
  8. # 参考内核源码drivers/char/ipmi/Makefile
  9. # 要想把a.c, b.c编译成ab.ko, 可以这样指定:
  10. # ab-y := a.o b.o
  11. # obj-m += ab.o
  12. obj-m += gpio_drv.o
  13. obj-m += gpio_dev.o
  •  编译:make
  • 设备树节点信息取消:因为内核里有了设备树节点信息,需要改回去或者添加disabled状态
  1. motor {
  2. compatible = "100ask,gpiodemo";
  3. gpios = <&gpio4 19 GPIO_ACTIVE_HIGH>,
  4. <&gpio4 20 GPIO_ACTIVE_HIGH>,
  5. <&gpio4 21 GPIO_ACTIVE_HIGH>,
  6. <&gpio4 22 GPIO_ACTIVE_HIGH>;
  7. status = "disabled";
  8. };
  • 编译:make dtbs
  • 更新设备树:
  1. PC:
  2. cp arch/arm/boot/dts/100ask_imx6ull-14x14.dtb ~/nfs_rootfs/
  3. 开发板:
  4. mount -t nfs -o nolock,vers=3 192.168.5.11:/home/book/nfs_rootfs /mnt
  5. cp /mnt/100ask_imx6ull-14x14.dtb /boot
  6. reboot
  • 查看设备树节点信息:ls /sys/firmware/devicetree/base/

        状态为禁止状态,设备树节点信息被我们禁用了 

  • 查看马达节点有没有被转换成平台设备(platform_device):ls /sys/bus/platform/devices/,这里肯定也是没有的

  •  装载驱动程序、查看是否匹配上、查看设备节点

 这个平台驱动支持名为"100ask_gpio_plat_drv"的设备,设备节点也出来了

三、总结(附驱动程序)

这个驱动程序,在入口函数里注册了平台驱动,这个平台驱动可以支持来自设备树的平台设备.compatible = "100ask,gpiodemo",和来自自己手动创建的平台设备.name = "100ask_gpio_plat_drv"。匹配规则有4种,这里用的是最简单的。当平台driver匹配到平台device时,驱动程序里的probe函数就被调用,probe函数里可以从设备树里得到引脚信息,也可以从手动注册的平台device里得到所谓的资源。

  1. static const struct of_device_id gpio_dt_ids[] = {
  2. { .compatible = "100ask,gpiodemo", },
  3. { /* sentinel */ }
  4. };
  5. static struct platform_driver gpio_platform_driver = {
  6. .driver = {
  7. .name = "100ask_gpio_plat_drv",
  8. .of_match_table = gpio_dt_ids,//设备树信息
  9. },
  10. .probe = gpio_drv_probe,
  11. .remove = gpio_drv_remove,
  12. };

 驱动程序:

  1. #include <linux/module.h>
  2. #include <linux/poll.h>
  3. #include <linux/delay.h>
  4. #include <linux/fs.h>
  5. #include <linux/errno.h>
  6. #include <linux/miscdevice.h>
  7. #include <linux/kernel.h>
  8. #include <linux/major.h>
  9. #include <linux/mutex.h>
  10. #include <linux/proc_fs.h>
  11. #include <linux/seq_file.h>
  12. #include <linux/stat.h>
  13. #include <linux/init.h>
  14. #include <linux/device.h>
  15. #include <linux/tty.h>
  16. #include <linux/kmod.h>
  17. #include <linux/gfp.h>
  18. #include <linux/gpio/consumer.h>
  19. #include <linux/platform_device.h>
  20. #include <linux/of_gpio.h>
  21. #include <linux/of_irq.h>
  22. #include <linux/interrupt.h>
  23. #include <linux/irq.h>
  24. #include <linux/slab.h>
  25. #include <linux/fcntl.h>
  26. #include <linux/timer.h>
  27. struct gpio_desc{
  28. int gpio;
  29. int irq;
  30. char name[128];
  31. int key;
  32. struct timer_list key_timer;
  33. } ;
  34. static struct gpio_desc *gpios;
  35. static int count;
  36. /* 主设备号 */
  37. static int major = 0;
  38. static struct class *gpio_class;
  39. /* 马达引脚设置数字 */
  40. static int g_motor_pin_ctrl[8]= {0x2,0x3,0x1,0x9,0x8,0xc,0x4,0x6};
  41. static int g_motor_index = 0;
  42. void set_pins_for_motor(int index)
  43. {
  44. int i;
  45. for (i = 0; i < 4; i++)
  46. {
  47. gpio_set_value(gpios[i].gpio, g_motor_pin_ctrl[index] & (1<<i) ? 1 : 0);
  48. }
  49. }
  50. void disable_motor(void)
  51. {
  52. int i;
  53. for (i = 0; i < 4; i++)
  54. {
  55. gpio_set_value(gpios[i].gpio, 0);
  56. }
  57. }
  58. /* int buf[2];
  59. * buf[0] = 步进的次数, > 0 : 逆时针步进; < 0 : 顺时针步进
  60. * buf[1] = mdelay的时间
  61. */
  62. static ssize_t motor_write(struct file *file, const char __user *buf, size_t size, loff_t *offset)
  63. {
  64. int ker_buf[2];
  65. int err;
  66. int step;
  67. if (size != 8)
  68. return -EINVAL;
  69. err = copy_from_user(ker_buf, buf, size);
  70. if (ker_buf[0] > 0)
  71. {
  72. /* 逆时针旋转 */
  73. for (step = 0; step < ker_buf[0]; step++)
  74. {
  75. set_pins_for_motor(g_motor_index);
  76. mdelay(ker_buf[1]);
  77. g_motor_index--;
  78. if (g_motor_index == -1)
  79. g_motor_index = 7;
  80. }
  81. }
  82. else
  83. {
  84. /* 顺时针旋转 */
  85. ker_buf[0] = 0 - ker_buf[0];
  86. for (step = 0; step < ker_buf[0]; step++)
  87. {
  88. set_pins_for_motor(g_motor_index);
  89. mdelay(ker_buf[1]);
  90. g_motor_index++;
  91. if (g_motor_index == 8)
  92. g_motor_index = 0;
  93. }
  94. }
  95. /* 改进:旋转到位后让马达不再消耗电源 */
  96. disable_motor();
  97. return 8;
  98. }
  99. /* 定义自己的file_operations结构体 */
  100. static struct file_operations gpio_key_drv = {
  101. .owner = THIS_MODULE,
  102. .write = motor_write,
  103. };
  104. /* 在入口函数 */
  105. static int gpio_drv_probe(struct platform_device *pdev)
  106. {
  107. int err = 0;
  108. int i;
  109. struct device_node *np = pdev->dev.of_node;
  110. struct resource *res;
  111. printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
  112. /* 从platfrom_device获得引脚信息
  113. * 1. pdev来自c文件
  114. * 2. pdev来自设备树
  115. */
  116. if (np)
  117. {
  118. /* pdev来自设备树 : 示例
  119. reg_usb_ltemodule: regulator@1 {
  120. compatible = "100ask,gpiodemo";
  121. gpios = <&gpio5 5 GPIO_ACTIVE_HIGH>, <&gpio5 3 GPIO_ACTIVE_HIGH>;
  122. };
  123. */
  124. count = of_gpio_count(np);
  125. if (!count)
  126. return -EINVAL;
  127. gpios = kmalloc(count * sizeof(struct gpio_desc), GFP_KERNEL);
  128. for (i = 0; i < count; i++)
  129. {
  130. gpios[i].gpio = of_get_gpio(np, i);
  131. sprintf(gpios[i].name, "%s_pin_%d", np->name, i);
  132. }
  133. }
  134. else
  135. {
  136. /* pdev来自c文件
  137. static struct resource omap16xx_gpio3_resources[] = {
  138. {
  139. .start = 115,
  140. .end = 115,
  141. .flags = IORESOURCE_IRQ,
  142. },
  143. {
  144. .start = 118,
  145. .end = 118,
  146. .flags = IORESOURCE_IRQ,
  147. }, };
  148. */
  149. count = 0;
  150. while (1)
  151. {
  152. res = platform_get_resource(pdev, IORESOURCE_IRQ, count);
  153. if (res)
  154. {
  155. count++;
  156. }
  157. else
  158. {
  159. break;
  160. }
  161. }
  162. if (!count)
  163. return -EINVAL;
  164. gpios = kmalloc(count * sizeof(struct gpio_desc), GFP_KERNEL);
  165. for (i = 0; i < count; i++)
  166. {
  167. res = platform_get_resource(pdev, IORESOURCE_IRQ, i);
  168. gpios[i].gpio = res->start;
  169. sprintf(gpios[i].name, "%s_pin_%d", pdev->name, i);
  170. }
  171. }
  172. for (i = 0; i < count; i++)
  173. {
  174. err = gpio_request(gpios[i].gpio, gpios[i].name);
  175. gpio_direction_output(gpios[i].gpio, 0);
  176. }
  177. /* 注册file_operations */
  178. major = register_chrdev(0, "100ask_gpio_key", &gpio_key_drv); /* /dev/gpio_desc */
  179. gpio_class = class_create(THIS_MODULE, "100ask_gpio_key_class");
  180. if (IS_ERR(gpio_class)) {
  181. printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
  182. unregister_chrdev(major, "100ask_gpio_key");
  183. return PTR_ERR(gpio_class);
  184. }
  185. device_create(gpio_class, NULL, MKDEV(major, 0), NULL, "motor"); /* /dev/motor */
  186. return err;
  187. }
  188. /* 有入口函数就应该有出口函数:卸载驱动程序时,就会去调用这个出口函数
  189. */
  190. static int gpio_drv_remove(struct platform_device *pdev)
  191. {
  192. int i;
  193. printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
  194. device_destroy(gpio_class, MKDEV(major, 0));
  195. class_destroy(gpio_class);
  196. unregister_chrdev(major, "100ask_gpio_key");
  197. for (i = 0; i < count; i++)
  198. {
  199. gpio_free(gpios[i].gpio);
  200. }
  201. return 0;
  202. }
  203. static const struct of_device_id gpio_dt_ids[] = {
  204. { .compatible = "100ask,gpiodemo", },
  205. { /* sentinel */ }
  206. };
  207. static struct platform_driver gpio_platform_driver = {
  208. .driver = {
  209. .name = "100ask_gpio_plat_drv",
  210. .of_match_table = gpio_dt_ids,//设备树信息
  211. },
  212. .probe = gpio_drv_probe,
  213. .remove = gpio_drv_remove,
  214. };
  215. static int __init gpio_drv_init(void)
  216. {
  217. /* 注册platform_driver */
  218. return platform_driver_register(&gpio_platform_driver);
  219. }
  220. static void __exit gpio_drv_exit(void)
  221. {
  222. /* 反注册platform_driver */
  223. platform_driver_unregister(&gpio_platform_driver);
  224. }
  225. /* 7. 其他完善:提供设备信息,自动创建设备节点 */
  226. module_init(gpio_drv_init);
  227. module_exit(gpio_drv_exit);
  228. MODULE_LICENSE("GPL");

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Gausst松鼠会/article/detail/688808
推荐阅读
相关标签
  

闽ICP备14008679号