• 【Linux驱动开发】点亮一个LED的三种字符设备驱动框架


    目录

    硬件原理图分析 

    GPIO3_IO03的引脚配置及原理分析

    1、 寄存器映射版本

     led.c

    2、设备树版本

    led.c

    imx6ull-alientek-emmc.dts

    3、pinctrl、gpio子系统版本

    led.c

    imx6ull-alientek-emmc.dts

    三个字符驱动版本都通用的LED点灯应用程序App.c

    Makefile工程管理器源码

    实验现象

    总结


    三种驱动框架分别为:

    ①寄存器映射版本

    ②设备树版本

    ③pinctrl、gpio子系统版本

    应用程序可共用同一个

    CPU:NXP的I.MX6ULL,基于ARM Cortex-A7架构;

    开发板:正点原子I.MX6U-ALPHA;

    linux内核:linux-4.1.15‘;

    硬件原理图分析 

     

            板子的LED0接到了GPIO3_IO03引脚上,当GPIO3_IO03输出为低电平(0)时二极管导通LED0就会被点亮;当GPIO3_IO03输出为高电平(1)时二极管截止LED0就会熄灭;

     (一个小小的建议:初学者一定要养成多动手翻看参考手册和原理图的习惯,这样今后的学习过程更容易举一反三)

    GPIO3_IO03的引脚配置及原理分析

    <1>使能 GPIO1时钟

    将寄存器CCM_CCGR1(物理地址:0x020C406C)的 bit27 和 bit26 这两个位,置成11;

     

    <2>设置 GPIO1_IO03的复用功能为GPIO模式

    往寄存器IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03中写入0x5即可,对应下图中的ALT5

    <3>配置 GPIO1_IO03 的电气属性

    往寄存器“IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO03"写入0x10B0(前十六位为: 0001 0000 1011 0000);

    设置 IO的上下拉、速度等等。

    0x10B0(前十六位 0001 0000 1011 0000)的含义​​​​​​

    <4>设置GPIO1_IO03作为输出功能

    把寄存器 GPIO1_GDIR 的 bit3 置 1 ,表示GPIO作输出

    <5>控制GPIO1_IO03输出电平

    向 GPIO1_DR寄存器的 bit3 写入0即可控制 GPIO1_IO03输出低电平(开灯),写入1即可控制 GPIO1_IO03输出高电平(熄灯)

    1、 寄存器映射版本

     led.c

    1. /*
    2. * @Author: imysy_22
    3. * @Description: led灯字符型驱动程序寄存器映射版本
    4. * @Date: 2022-10-17 12:46:27
    5. * @LastEditTime: 2022-10-17 00:00:09
    6. * @FilePath: /undefined/home/imysy22/IMX6U/rootfs/home/root/MyDrivers/2_led_chrdev/common_version/led.c
    7. */
    8. #include
    9. #include
    10. #include
    11. #include
    12. #include
    13. #include
    14. #include
    15. #include
    16. #define DevName "Mychr_led"
    17. static int DevMajor = 11;
    18. static int DevMinor = 0;
    19. /* 要点亮一个led灯所要操作的寄存器地址 */
    20. #define CCM_CCGR1_BASE 0x020C406C
    21. #define SW_MUX_GPIO1_IO03_BASE 0x020E0068
    22. #define SW_PAD_GPIO1_IO03_BASE 0x020E02F4
    23. #define GPIO1_DR_BASE 0x0209C000
    24. #define GPIO1_GDIR_BASE 0x0209C004
    25. /* 寄存器物理地址映射的虚拟地址 */
    26. static void __iomem *IMX6U_CCM_CCGR1;
    27. static void __iomem *SW_MUX_GPIO1_IO03;
    28. static void __iomem *SW_PAD_GPIO1_IO03;
    29. static void __iomem *GPIO1_DR;
    30. static void __iomem *GPIO1_GDIR;
    31. /**
    32. * @description: 初始化、配置好寄存器
    33. */
    34. static void led_ioremap(void)
    35. {
    36. unsigned long RegVal = 0;
    37. /* 1、把寄存器的物理地址映射到虚拟地址 */
    38. IMX6U_CCM_CCGR1 = ioremap(CCM_CCGR1_BASE, 4); //4代表4个字节,4x8=32位寄存器
    39. SW_MUX_GPIO1_IO03 = ioremap(SW_MUX_GPIO1_IO03_BASE, 4);
    40. SW_PAD_GPIO1_IO03 = ioremap(SW_PAD_GPIO1_IO03_BASE, 4);
    41. GPIO1_DR = ioremap(GPIO1_DR_BASE, 4);
    42. GPIO1_GDIR = ioremap(GPIO1_GDIR_BASE, 4);
    43. /* 2、开启GPIO1的时钟 */
    44. RegVal = readl(IMX6U_CCM_CCGR1);
    45. RegVal &= ~(0x3 << 26);
    46. RegVal |= (0x3 << 26);
    47. writel(RegVal, IMX6U_CCM_CCGR1);
    48. /* 3、把GPIO1_IO3复用成gpio */
    49. writel(0x5, SW_MUX_GPIO1_IO03);
    50. /* 4、设置io属性 */
    51. writel(0x10B0, SW_PAD_GPIO1_IO03);
    52. /* 5、配置成输出功能 */
    53. RegVal = readl(GPIO1_GDIR);
    54. RegVal &= ~(0x1 << 3);
    55. RegVal |= (0x1 << 3);
    56. writel(RegVal, GPIO1_GDIR);
    57. /* 6、默认输出高电平,关闭led */
    58. RegVal = readl(GPIO1_DR);
    59. RegVal &= ~(0x1 << 3);
    60. RegVal |= (0x1 << 3);
    61. writel(RegVal, GPIO1_DR);
    62. }
    63. static void led_on(void)
    64. {
    65. unsigned long RegVal = 0;
    66. RegVal = readl(GPIO1_DR);
    67. RegVal &= ~(0x1 << 3);
    68. writel(RegVal, GPIO1_DR);
    69. }
    70. static void led_off(void)
    71. {
    72. unsigned long RegVal = 0;
    73. RegVal = readl(GPIO1_DR);
    74. RegVal |= (0x1 << 3);
    75. writel(RegVal, GPIO1_DR);
    76. }
    77. struct led_dev {
    78. struct cdev mdev;
    79. struct class *pcls;
    80. struct device *pdev;
    81. };
    82. struct led_dev gmydev;
    83. /**
    84. * @description:
    85. * @param {inode} *pnode
    86. * @param {file} *pfile 设备文件,file结构体有个叫做private_data的成员变量,一般在open的时候将private_data指向设备结构体。
    87. * @return {*}
    88. */
    89. static int chr_led_open(struct inode *pnode,struct file *pfile)
    90. {
    91. pfile->private_data = (void *) (container_of(pnode->i_cdev, struct led_dev, mdev)); //设置私有数据,后续可以直接找到gmydev的地址
    92. return 0;
    93. }
    94. static int chr_led_release(struct inode *pnode,struct file *pfile)
    95. {
    96. return 0;
    97. }
    98. static ssize_t chr_led_write(struct file *pfile,const char __user *puser,size_t size,loff_t *p_pos)
    99. {
    100. int ret = 0;
    101. char buf[1] = {0};
    102. unsigned char led_state;
    103. if((ret = copy_from_user(buf, puser, size)))
    104. {
    105. printk("copy_from_user failed...\n");
    106. return -1;
    107. }
    108. led_state = buf[0];
    109. if(led_state == 0)
    110. {
    111. led_on(); //低电平开灯
    112. }
    113. if(led_state == 1)
    114. {
    115. led_off(); //高电平关灯
    116. }
    117. return 1;
    118. }
    119. struct file_operations myops = {
    120. .owner = THIS_MODULE,
    121. .open = chr_led_open,
    122. .release = chr_led_release,
    123. .write = chr_led_write,
    124. };
    125. static int __init chr_led_init(void)
    126. {
    127. int ret = 0;
    128. dev_t devno = MKDEV(DevMajor, DevMinor);
    129. led_ioremap(); //配置好寄存器
    130. ret = register_chrdev_region(devno, 1, DevName);
    131. if(ret)
    132. {
    133. ret = alloc_chrdev_region(&devno, DevMinor, 1, DevName);
    134. if(ret)
    135. {
    136. printk("register failed...\n");
    137. return -1;
    138. }
    139. DevMajor = MAJOR(devno);
    140. }
    141. cdev_init(&gmydev.mdev, &myops);
    142. gmydev.mdev.owner = THIS_MODULE;
    143. cdev_add(&gmydev.mdev, devno, 1);
    144. gmydev.pcls = class_create(THIS_MODULE, DevName);
    145. if(IS_ERR(gmydev.pcls))
    146. {
    147. printk("class_create failed...\n");
    148. cdev_del(&gmydev.mdev);
    149. unregister_chrdev_region(devno, 1);
    150. return -1;
    151. }
    152. gmydev.pdev = device_create(gmydev.pcls, NULL, devno, NULL, DevName);
    153. if(gmydev.pdev == NULL)
    154. {
    155. printk("device_create failed...\n");
    156. class_destroy(gmydev.pcls);
    157. cdev_del(&gmydev.mdev);
    158. unregister_chrdev_region(devno, 1);
    159. return -1;
    160. }
    161. printk("New character device(%d:%d) added successfully.\n", DevMajor, DevMinor);
    162. return 0;
    163. }
    164. static void __exit chr_led_exit(void)
    165. {
    166. dev_t devno = MKDEV(DevMajor, DevMinor);
    167. /* 取消映射 */
    168. iounmap(IMX6U_CCM_CCGR1);
    169. iounmap(SW_MUX_GPIO1_IO03);
    170. iounmap(SW_PAD_GPIO1_IO03);
    171. iounmap(GPIO1_DR);
    172. iounmap(GPIO1_GDIR);
    173. device_destroy(gmydev.pcls, devno);
    174. class_destroy(gmydev.pcls);
    175. cdev_del(&gmydev.mdev);
    176. unregister_chrdev_region(devno, 1);
    177. }
    178. module_init(chr_led_init);
    179. module_exit(chr_led_exit);
    180. MODULE_LICENSE("GPL");
    181. MODULE_AUTHOR("imysy_22-2022.10.17");

    2、设备树版本

    led.c

    1. /*
    2. * @Author: imysy_22
    3. * @Description: led灯字符型驱动程序设备树版本
    4. * @Date: 2022-10-16 15:37:01
    5. * @LastEditTime: 2022-10-18 17:31:31
    6. * @FilePath: /undefined/home/imysy22/IMX6U/rootfs/home/root/MyDrivers/2_led_chrdev/device_tree_version/led.c
    7. */
    8. #include
    9. #include
    10. #include
    11. #include
    12. #include
    13. #include
    14. #include
    15. #include
    16. #include
    17. #include
    18. #include
    19. #include
    20. #include
    21. #define DevName "Mychr_led"
    22. //static int gmydev.major = 13;
    23. //static int gmydev.minor = 0;
    24. static void __iomem *IMX6U_CCM_CCGR1;
    25. static void __iomem *SW_MUX_GPIO1_IO03;
    26. static void __iomem *SW_PAD_GPIO1_IO03;
    27. static void __iomem *GPIO1_DR;
    28. static void __iomem *GPIO1_GDIR;
    29. struct led_dev {
    30. int major;
    31. int minor;
    32. dev_t devno;
    33. struct cdev mdev;
    34. struct class *pcls;
    35. struct device *pdev;
    36. struct device_node *pnd;
    37. struct property *proper;
    38. };
    39. struct led_dev gmydev;
    40. /**
    41. * @description: 初始化、配置好寄存器
    42. */
    43. static void led_ioremap(struct led_dev gmydev, u32 *RegData);
    44. static void led_on(void);
    45. static void led_off(void);
    46. /**
    47. * @description:
    48. * @param {inode} *pnode
    49. * @param {file} *pfile 设备文件,file结构体有个叫做private_data的成员变量,一般在open的时候将private_data指向设备结构体。
    50. * @return {*}
    51. */
    52. static int chr_led_open(struct inode *pnode,struct file *pfile)
    53. {
    54. pfile->private_data = (void *) (container_of(pnode->i_cdev, struct led_dev, mdev));
    55. return 0;
    56. }
    57. static int chr_led_release(struct inode *pnode,struct file *pfile)
    58. {
    59. return 0;
    60. }
    61. static ssize_t chr_led_write(struct file *pfile,const char __user *puser,size_t size,loff_t *p_pos)
    62. {
    63. int ret = 0;
    64. char buf[1] = {0};
    65. unsigned char led_state;
    66. if((ret = copy_from_user(buf, puser, size)))
    67. {
    68. printk("copy_from_user failed...\n");
    69. return -1;
    70. }
    71. led_state = buf[0];
    72. if(led_state == 0)
    73. {
    74. led_on(); //低电平开灯
    75. }
    76. if(led_state == 1)
    77. {
    78. led_off(); //高电平关灯
    79. }
    80. return 1;
    81. }
    82. struct file_operations myops = {
    83. .owner = THIS_MODULE,
    84. .open = chr_led_open,
    85. .release = chr_led_release,
    86. .write = chr_led_write,
    87. };
    88. static int __init chr_led_init(void)
    89. {
    90. int ret = 0;
    91. const char *str;
    92. u32 RegData[14] = {0};
    93. unsigned char i = 0;
    94. /* 获取设备树中的属性数据 */
    95. /* 1、获取设备节点:alpha-led */
    96. if((gmydev.pnd = of_find_node_by_path("/alpha-led")) == NULL)
    97. {
    98. printk("of_find_node_by_path failed...\n");
    99. return -EINVAL;
    100. }
    101. /* 2、获取compatible属性内容 */
    102. if((gmydev.proper = of_find_property(gmydev.pnd, "atkalpha-led", NULL)) == NULL)
    103. {
    104. printk("of_find_property failed...\n");
    105. //return -1;
    106. }
    107. /* 3、获取status属性内容 */
    108. if((ret = of_property_read_string(gmydev.pnd, "status", &str)) < 0)
    109. {
    110. printk("of_property_read_string failed...\n");
    111. //return -1;
    112. }
    113. printk("status = %s\n", str);
    114. /* 4、获取reg属性内容 */
    115. if((ret = of_property_read_u32_array(gmydev.pnd, "reg", RegData, 10)) < 0)
    116. {
    117. printk("of_property_read_u32_array failed...\n");
    118. //return -1;
    119. }
    120. printk("reg data :\n");
    121. for(i = 0;i < 10; i++)
    122. printk("%#x ", RegData[i]);
    123. printk("\n");
    124. /* static void led_ioremap(void)函数 */
    125. led_ioremap(gmydev, RegData);
    126. gmydev.major = 13;
    127. gmydev.minor = 0;
    128. gmydev.devno = MKDEV(gmydev.major, gmydev.minor);
    129. ret = register_chrdev_region(gmydev.devno, 1, DevName);
    130. if(ret)
    131. {
    132. ret = alloc_chrdev_region(&gmydev.devno, gmydev.minor, 1, DevName);
    133. if(ret)
    134. {
    135. printk("register failed...\n");
    136. return -1;
    137. }
    138. gmydev.major = MAJOR(gmydev.devno);
    139. gmydev.minor = MINOR(gmydev.devno);
    140. }
    141. cdev_init(&gmydev.mdev, &myops);
    142. gmydev.mdev.owner = THIS_MODULE;
    143. cdev_add(&gmydev.mdev, gmydev.devno, 1);
    144. gmydev.pcls = class_create(THIS_MODULE, DevName);
    145. if(IS_ERR(gmydev.pcls))
    146. {
    147. printk("class_create failed...\n");
    148. cdev_del(&gmydev.mdev);
    149. unregister_chrdev_region(gmydev.devno, 1);
    150. return -1;
    151. }
    152. gmydev.pdev = device_create(gmydev.pcls, NULL, gmydev.devno, NULL, DevName);
    153. if(gmydev.pdev == NULL)
    154. {
    155. printk("device_create failed...\n");
    156. class_destroy(gmydev.pcls);
    157. cdev_del(&gmydev.mdev);
    158. unregister_chrdev_region(gmydev.devno, 1);
    159. return -1;
    160. }
    161. printk("New character device(%d:%d) added successfully.\n", gmydev.major, gmydev.minor);
    162. return 0;
    163. }
    164. static void __exit chr_led_exit(void)
    165. {
    166. /* 取消映射 */
    167. iounmap(IMX6U_CCM_CCGR1);
    168. iounmap(SW_MUX_GPIO1_IO03);
    169. iounmap(SW_PAD_GPIO1_IO03);
    170. iounmap(GPIO1_DR);
    171. iounmap(GPIO1_GDIR);
    172. device_destroy(gmydev.pcls, gmydev.devno);
    173. class_destroy(gmydev.pcls);
    174. cdev_del(&gmydev.mdev);
    175. unregister_chrdev_region(gmydev.devno, 1);
    176. }
    177. static void led_ioremap(struct led_dev gmydev, u32 *RegData)
    178. {
    179. unsigned long RegVal = 0;
    180. /* 1、把寄存器的物理地址映射到虚拟地址 */
    181. #if 1
    182. IMX6U_CCM_CCGR1 = ioremap(RegData[0], RegData[1]); //4代表4个字节,4x8=32位寄存器
    183. SW_MUX_GPIO1_IO03 = ioremap(RegData[2], RegData[3]);
    184. SW_PAD_GPIO1_IO03 = ioremap(RegData[4], RegData[5]);
    185. GPIO1_DR = ioremap(RegData[6], RegData[7]);
    186. GPIO1_GDIR = ioremap(RegData[8], RegData[9]);
    187. #else
    188. IMX6U_CCM_CCGR1 = of_ioremap(RegData[0], RegData[1]); //4代表4个字节,4x8=32位寄存器
    189. SW_MUX_GPIO1_IO03 = of_ioremap(RegData[2], RegData[3]);
    190. SW_PAD_GPIO1_IO03 = of_ioremap(RegData[4], RegData[5]);
    191. GPIO1_DR = of_ioremap(RegData[6], RegData[7]);
    192. GPIO1_GDIR = of_ioremap(RegData[8], RegData[9]);
    193. #endif
    194. /* 2、开启GPIO1的时钟 */
    195. RegVal = readl(IMX6U_CCM_CCGR1);
    196. RegVal &= ~(0x3 << 26);
    197. RegVal |= (0x3 << 26);
    198. writel(RegVal, IMX6U_CCM_CCGR1);
    199. /* 3、把GPIO1_IO3复用成gpio */
    200. writel(0x5, SW_MUX_GPIO1_IO03);
    201. /* 4、设置io属性 */
    202. writel(0x10B0, SW_PAD_GPIO1_IO03);
    203. /* 5、配置成输出功能 */
    204. RegVal = readl(GPIO1_GDIR);
    205. RegVal &= ~(0x1 << 3);
    206. RegVal |= (0x1 << 3);
    207. writel(RegVal, GPIO1_GDIR);
    208. /* 6、默认输出高电平,关闭led */
    209. RegVal = readl(GPIO1_DR);
    210. RegVal &= ~(0x1 << 3);
    211. RegVal |= (0x1 << 3);
    212. writel(RegVal, GPIO1_DR);
    213. }
    214. static void led_on(void)
    215. {
    216. unsigned long RegVal = 0;
    217. RegVal = readl(GPIO1_DR);
    218. RegVal &= ~(0x1 << 3);
    219. writel(RegVal, GPIO1_DR);
    220. }
    221. static void led_off(void)
    222. {
    223. unsigned long RegVal = 0;
    224. RegVal = readl(GPIO1_DR);
    225. RegVal |= (0x1 << 3);
    226. writel(RegVal, GPIO1_DR);
    227. }
    228. module_init(chr_led_init);
    229. module_exit(chr_led_exit);
    230. MODULE_LICENSE("GPL");
    231. MODULE_AUTHOR("imysy_22-2022.10.16");

    imx6ull-alientek-emmc.dts

    需要在 “ / ” 根节点下增加一个“alpha-led”子节点

    1. alpha-led { /* LED第一代:《设备树版》 */
    2. #address-cells = <1>; /* 用于描述子节点的地址信息,即reg属性中,地址信息占几个uint32 */
    3. #size-cells = <1>; /* 地址长度占几个u32,单位为u32(4字节) */
    4. compatible = "atkalpha-led";
    5. status = "okey";
    6. reg = < 0X020C406C 0X04 /* CCM_CCGR1_BASE */
    7. 0X020E0068 0X04 /* SW_MUX_GPIO1_IO03_BASE */
    8. 0X020E02F4 0X04 /* SW_PAD_GPIO1_IO03_BASE */
    9. 0X0209C000 0X04 /* GPIO1_DR_BASE */
    10. 0X0209C004 0X04 /* GPIO1_GDIR_BASE */
    11. >;
    12. };

    3、pinctrl、gpio子系统版本

    led.c

    1. /*
    2. * @Author: imysy_22
    3. * @Description: led灯字符型驱动程序pinctrl_gpio子系统版本
    4. * @Date: 2022-10-18 23:12:03
    5. * @LastEditTime: 2022-10-18 23:48:27
    6. * @FilePath: /undefined/home/imysy22/IMX6U/Linux_Drivers/2_led_chrdev/pinctrl_gpio_version/led.c
    7. */
    8. #include
    9. #include
    10. #include
    11. #include
    12. #include
    13. #include
    14. #include
    15. #include
    16. #include
    17. #include
    18. #include
    19. #include
    20. #include
    21. #define DevName "gpioled"
    22. struct gpioled_dev {
    23. int major;
    24. int minor;
    25. int led_gpio; /* led所使用的gpio编号 */
    26. dev_t devno;
    27. struct cdev mdev;
    28. struct class *pcls;
    29. struct device *pdev;
    30. struct device_node *pnd;
    31. struct property *proper;
    32. };
    33. struct gpioled_dev gmydev;
    34. static void led_on(struct file *pfile);
    35. static void led_off(struct file *pfile);
    36. /**
    37. * @description:
    38. * @param {inode} *pnode
    39. * @param {file} *pfile 设备文件,file结构体有个叫做private_data的成员变量,一般在open的时候将private_data指向设备结构体。
    40. * @return {*}
    41. */
    42. static int chr_led_open(struct inode *pnode,struct file *pfile)
    43. {
    44. pfile->private_data = (void *) (container_of(pnode->i_cdev, struct gpioled_dev, mdev));
    45. return 0;
    46. }
    47. static int chr_led_release(struct inode *pnode,struct file *pfile)
    48. {
    49. return 0;
    50. }
    51. static ssize_t chr_led_write(struct file *pfile,const char __user *puser,size_t size,loff_t *p_pos)
    52. {
    53. int ret = 0;
    54. char buf[1] = {0};
    55. unsigned char led_state;
    56. if((ret = copy_from_user(buf, puser, size)))
    57. {
    58. printk("copy_from_user failed...\n");
    59. return -1;
    60. }
    61. led_state = buf[0];
    62. if(led_state == 0)
    63. {
    64. led_on(pfile); //低电平开灯
    65. }
    66. if(led_state == 1)
    67. {
    68. led_off(pfile); //高电平关灯
    69. }
    70. return 1;
    71. }
    72. struct file_operations myops = {
    73. .owner = THIS_MODULE,
    74. .open = chr_led_open,
    75. .release = chr_led_release,
    76. .write = chr_led_write,
    77. };
    78. static int __init chr_led_init(void)
    79. {
    80. int ret = 0;
    81. /*
    82. 获取设备树中的属性数据
    83. 前面操作设备树节点这几步操作和设备树版本的不一样
    84. */
    85. /* 1、获取设备节点:gpioled */
    86. if((gmydev.pnd = of_find_node_by_path("/gpioled")) == NULL)
    87. {
    88. printk("of_find_node_by_path failed...\n");
    89. return -EINVAL;
    90. }
    91. /* 2、获取gpio子系统中设备树中的gpio的电气属性,并得到Led所使用的GPIO编号 */
    92. if((gmydev.led_gpio = of_get_named_gpio(gmydev.pnd, "led-gpio", 0)) < 0)
    93. {
    94. printk("of_get_named_gpio failed...\n");
    95. return -EINVAL;
    96. }
    97. printk("led-gpio num = %d\n", gmydev.led_gpio);
    98. /* 3、设置GPIO1_IO03为输出,并输出高电平,默认熄灭led灯 */
    99. if((ret = gpio_direction_output(gmydev.led_gpio, 1)) < 0)
    100. {
    101. printk("gpio_direction_output failed...\n");
    102. return -EINVAL;
    103. }
    104. printk("**LED0 initialzation successed***\n");
    105. /* 注册设备流程和设备树版本的LED一样 */
    106. gmydev.major = 14;
    107. gmydev.minor = 0;
    108. gmydev.devno = MKDEV(gmydev.major, gmydev.minor);
    109. ret = register_chrdev_region(gmydev.devno, 1, DevName);
    110. if(ret)
    111. {
    112. ret = alloc_chrdev_region(&gmydev.devno, gmydev.minor, 1, DevName);
    113. if(ret)
    114. {
    115. printk("register failed...\n");
    116. return -1;
    117. }
    118. gmydev.major = MAJOR(gmydev.devno);
    119. gmydev.minor = MINOR(gmydev.devno);
    120. }
    121. cdev_init(&gmydev.mdev, &myops);
    122. gmydev.mdev.owner = THIS_MODULE;
    123. cdev_add(&gmydev.mdev, gmydev.devno, 1);
    124. gmydev.pcls = class_create(THIS_MODULE, DevName);
    125. if(IS_ERR(gmydev.pcls))
    126. {
    127. printk("class_create failed...\n");
    128. cdev_del(&gmydev.mdev);
    129. unregister_chrdev_region(gmydev.devno, 1);
    130. return -1;
    131. }
    132. gmydev.pdev = device_create(gmydev.pcls, NULL, gmydev.devno, NULL, DevName);
    133. if(gmydev.pdev == NULL)
    134. {
    135. printk("device_create failed...\n");
    136. class_destroy(gmydev.pcls);
    137. cdev_del(&gmydev.mdev);
    138. unregister_chrdev_region(gmydev.devno, 1);
    139. return -1;
    140. }
    141. printk("New character device(%d:%d) added successfully.\n", gmydev.major, gmydev.minor);
    142. return 0;
    143. }
    144. static void __exit chr_led_exit(void)
    145. {
    146. device_destroy(gmydev.pcls, gmydev.devno);
    147. class_destroy(gmydev.pcls);
    148. cdev_del(&gmydev.mdev);
    149. unregister_chrdev_region(gmydev.devno, 1);
    150. }
    151. static void led_on(struct file *pfile)
    152. {
    153. struct gpioled_dev *pmydev = pfile->private_data;
    154. gpio_set_value(pmydev->led_gpio, 0);
    155. }
    156. static void led_off(struct file *pfile)
    157. {
    158. struct gpioled_dev *pmydev = pfile->private_data;
    159. gpio_set_value(pmydev->led_gpio, 1);
    160. }
    161. module_init(chr_led_init);
    162. module_exit(chr_led_exit);
    163. MODULE_LICENSE("GPL");
    164. MODULE_AUTHOR("imysy_22-2022.10.16");

    imx6ull-alientek-emmc.dts

    在" / "根节点部的 iomuxc 节点的“imx6ul-evk”子节点下创建一个名为“pinctrl_led”子子节点(套娃)

    1. pinctrl_led: ledgrp { /* LED第二代:《pinctrl和gpio子系统版》 */
    2. fsl,pins = <
    3. MX6UL_PAD_GPIO1_IO03__GPIO1_IO03 0x10B0
    4. >;
    5. };

    其中,MX6UL_PAD_GPIO1_IO03__GPIO1_IO03这个宏的定义在文件arch/arm/boot/dts/imx6ul-pinfunc.h中。

    再在" / "根节点部创建一个“gpioled”节点

    1. gpioled {
    2. #address-cells = <1>;
    3. #size-cells = <1>;
    4. compatible = "atkalpha-gpioled";
    5. pinctrl-names = "default";
    6. pinctrl-0 = <&pinctrl_led>;
    7. led-gpio = <&gpio1 3 GPIO_ACTIVE_LOW>;
    8. status = "okey";
    9. };

    添加完后,一定不要忘记检查PIN是否正被其他外设使用!!!

    在设备树中搜索“ GPIO1_IO03 ”,如果有别的设备节点在也在使用这个PIN的话,屏蔽它

    pinctrl_tsc节点是TSC(电阻触摸屏接口)的pinctrl节点,从第480行可以看出,默认情况下GPIO1_IO03作为了TSC外设的PIN。所以我们需要将第480行屏蔽掉!和C语言一样,在要屏蔽的内容前后加上“/*”和“*/”符号即可。实际上我们目前并不会用到这个节点,所以我干脆把它的PIN全屏蔽了。

    编译设备树

     进入Linux顶层源码目录,生成imx6ull-alientek-emmc.dts并重启开发板时加载这个新的设备树

    正确的现象:/proc/device-tree目录下生成了我们的“ gpioled ”文件,进入文件后会自动生成我们之前在设备树里 “gpioled” 节点中的'#address-cells'  compatible  led-gpio  name  pinctrl-0  pinctrl-names  '#size-cells'  status属性

    三个字符驱动版本都通用的LED点灯应用程序App.c

    第32行的open函数需根据不同的驱动程序申请的字符设备名字不同进行修改

    1. /*
    2. * @Author: imysy_22
    3. * @Description: 三个字符驱动版本都统用的LED点灯应用程序
    4. * @Date: 2022-10-17 12:46:27
    5. * @LastEditTime: 2022-10-17 14:15:19
    6. * @FilePath: /undefined/home/imysy22/IMX6U/Linux_Drivers/2_led_chrdev/common_version/App.c
    7. */
    8. #include
    9. #include
    10. #include
    11. #include
    12. #include
    13. #include
    14. int main(int argc, const char *argv[])
    15. {
    16. int fd = -1;
    17. int ret;
    18. unsigned char led_state[1];
    19. /* 低电平点灯,高电平熄灭 */
    20. if(argc != 2)
    21. {
    22. printf("*********************************************\n");
    23. printf("* Please input : ./App 0-led_on / 1-led_off *\n");
    24. printf("*********************************************\n");
    25. return -1;
    26. }
    27. if((fd = open("/dev/Mychr_led", O_WRONLY)) < 0)
    28. {
    29. perror("open");
    30. return -1;
    31. }
    32. printf("***open success***\n\n\n");
    33. led_state[0] = atoi(argv[1]);
    34. /* 如果是点灯 */
    35. if(led_state[0] == 0)
    36. {
    37. if((ret = write(fd, led_state, 1)) < 0)
    38. {
    39. perror("write");
    40. }
    41. printf("Led is ON!\n");
    42. }
    43. else if(led_state[0] == 1)
    44. {
    45. if((ret = write(fd, led_state, 1)) < 0)
    46. {
    47. perror("write");
    48. }
    49. printf("Led is OFF!\n");
    50. }
    51. else
    52. {
    53. printf("You can only input 0 or 1!\n");
    54. close(fd);
    55. return -1;
    56. }
    57. close(fd);
    58. return 0;
    59. }

    Makefile工程管理器源码

    1. CONFIG_MODULE_SIG=n #加了这一行就不会报错:signature and/or required key missing - tainting kernel
    2. ifeq ($(KERNELRELEASE),)
    3. ifeq ($(ARCH),arm)
    4. KERNELDIR ?= /home/imysy22/IMX6U/linux-imx-rel_imx_4.1.15_2.1.0_ga
    5. CC = /home/imysy22/LicheePi/buildroot-2017.08.1/output/host/bin/arm-linux-gnueabihf-gcc
    6. else
    7. KERNELDIR ?= /lib/modules/$(shell uname -r)/build
    8. endif
    9. PWD := $(shell pwd)
    10. modules:
    11. $(MAKE) -C $(KERNELDIR) M=$(PWD) modules
    12. modules_install:
    13. $(MAKE) -C $(KERNELDIR) M=$(PWD) modules INSTALL_MOD_PATH=$(ROOTFS) modules_install
    14. clean:
    15. rm -rf *.o *.ko .*.cmd *.mod.* modules.order Module.symvers .tmp_versions
    16. else
    17. obj-m += led.o
    18. endif

    实验现象

    三个模块的实验现象都差不多,只不过insmod led.ko后printk打印的内容不一样而已

    # ./App 0    //低电平点亮

    # ./App 1    //高电平熄灭

    总结

         ☆ * .  ☆      
       . ∧_∧   ∩ * ☆      
    * ☆ ( `∀‘)/ .      
     . ⊂   ノ* ☆          
    ☆ * (つ ノ .☆
       (ノ

    哈哈用超级复杂的方法点亮led灯,虽然这个实验现象非常low,但是用三种不同的驱动框架来点灯还是着实学不到了不少啊!!! 

  • 相关阅读:
    List集合的对象传输的两种方式
    简单选择排序(数据结构)
    2023/10/30-LED灯驱动开发
    Spring、MyBatis、Druid、MySQL不使用事务执行SQL语句分析
    常用的英文缩写总结,专业术语
    REVERSE-COMPETITION-HWS-5TH-2022
    CloudCompare中CCCoreLib模块介绍
    【AI视野·今日NLP 自然语言处理论文速览 第五十五期】Mon, 16 Oct 2023
    Linux学习记录——사 权限与工具
    小学数学深度教学论文
  • 原文地址:https://blog.csdn.net/imysy_22_/article/details/127399319