OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 3563|回复: 0

《STM32MP157嵌入式Linux驱动开发指南》第二十四章 设备树下的LED驱动实验

[复制链接]

1070

主题

1081

帖子

2

精华

超级版主

Rank: 8Rank: 8

积分
4443
金钱
4443
注册时间
2019-5-8
在线时间
1199 小时
发表于 2021-6-24 11:32:33 | 显示全部楼层 |阅读模式
1)实验平台:正点原子STM32MP157开发板
2)  章节摘自【正点原子】《STM32MP157嵌入式Linux驱动开发指南》
3)购买链接:https://item.taobao.com/item.htm?&id=629270721801
4)全套实验源码+手册+视频下载地址:http://www.openedv.com/docs/boards/arm-linux/zdyzmp157.html
5)正点原子官方B站:https://space.bilibili.com/394620890
6)正点原子STM32MP157技术交流群:691905614 QQ群.png

原子哥.jpg

微信公众号.png


第二十四章 设备树下的LED驱动实验



上一章我们详细的讲解了设备树语法以及在驱动开发中常用的OF函数,本章我们就开始第一个基于设备树的Linux驱动实验。本章在第二十二章实验的基础上完成,只是将其驱动开发改为设备树形式而已。


24.1 设备树LED驱动原理
        在《第二十二章 新字符设备驱动实验》中,我们直接在驱动文件newchrled.c中定义有关寄存器物理地址,然后使用io_remap函数进行内存映射,得到对应的虚拟地址,最后操作寄存器对应的虚拟地址完成对GPIO的初始化。本章我们在第四十二章实验基础上完成,本章我们使用设备树来向Linux内核传递相关的寄存器物理地址,Linux驱动文件使用上一章讲解的OF函数从设备树中获取所需的属性值,然后使用获取到的属性值来初始化相关的IO。本章实验还是比较简单的,本章实验重点内容如下:
        ①、在stm32mp157d-atk.dts文件中创建相应的设备节点。
        ②、编写驱动程序(在第二十二章实验基础上完成),获取设备树中的相关属性值。
        ③、使用获取到的有关属性值来初始化LED所使用的GPIO。
24.2 硬件原理图分析
本实验的硬件原理参考21.2小节即可。
24.3 实验程序编写
24.3.1 修改设备树文件
        在根节点“/”下创建一个名为“stm32mp1_led”的子节点,打开stm32mp157d-atk.dts文件,在根节点“/”最后面输入如下所示内容:
示例代码44.3.1.1 stm32mp1_led节点
  1. 1    stm32mp1_led {
  2. 2        compatible = "atkstm32mp1-led";
  3. 3        status = "okay";
  4. 4        reg = <0X50000A28 0X04            /* RCC_MP_AHB4ENSETR            */
  5. 5                0X5000A000 0X04            /* GPIOI_MODER                  */
  6. 6                0X5000A004 0X04            /* GPIOI_OTYPER                 */
  7. 7                0X5000A008 0X04            /* GPIOI_OSPEEDR                */
  8. 8                0X5000A00C 0X04            /* GPIOI_PUPDR                  */
  9. 9                0X5000A018 0X04 >;         /* GPIOI_BSRR                   */
  10. 10   };
复制代码


        第2行,属性compatible设置stm32mp1_led节点兼容为“atkstm32mp1-led”。
        第3行,属性status设置状态为“okay”。
        第4~9行,reg属性,非常重要!reg属性设置了驱动里面所要使用的寄存器物理地址,比如第4行的“0X50000A28 0X04”表示STM32MP1的RCC_MP_AHB4ENSETR寄存器,其中寄存器地址为0X50000A28,长度为4个字节。
设备树修改完成以后输入如下命令重新编译一下stm32mp157d-atk.dts:
  1. make dtbs
复制代码


编译完成以后得到stm32mp157d-atk.dtb,使用新的stm32mp157d-atk.dtb启动Linux内核。Linux启动成功以后进入到/proc/device-tree/目录中查看是否有“stm32mp1_led”这个节点,结果如图24.3.1.1所示:
第二十四章 设备树下的LED驱动实验1524.png
图24.3.1.1 stm32mp1_led节点
如果没有“stm32mp1_led”节点的话请重点检查下面两点:
①、检查设备树修改是否成功,也就是stm32mp1_led节点是否为根节点“/”的子节点。
②、检查是否使用新的设备树启动的Linux内核。
可以进入到图24.3.1.1中的stm32mpl_led目录中,查看一下都有哪些属性文件,结果如图24.3.1.2所示:
第二十四章 设备树下的LED驱动实验1716.png
图24.3.1.2 stm32mp1_led节点文件
大家可以用cat命令查看一下compatible、status等属性值是否和我们设置的一致。
24.3.2 LED灯驱动程序编写
设备树准备好以后就可以编写驱动程序了,本章实验在第二十二章实验驱动文件newchrled.c的基础上修改而来。新建名为“4_dtsled”文件夹,然后在4_dtsled文件夹里面创建vscode工程,工作区命名为“dtsled”。工程创建好以后新建dtsled.c文件,在dtsled.c里面输入如下内容:
示例代码24.3.2.1 dtsled.c文件内容
  1. 1   #include <linux/types.h>
  2. 2   #include <linux/kernel.h>
  3. 3   #include <linux/delay.h>
  4. 4   #include <linux/ide.h>
  5. 5   #include <linux/init.h>
  6. 6   #include <linux/module.h>
  7. 7   #include <linux/errno.h>
  8. 8   #include <linux/gpio.h>
  9. 9   #include <linux/cdev.h>
  10. 10  #include <linux/device.h>
  11. 11  #include <linux/of.h>
  12. 12  #include <linux/of_address.h>
  13. 13  #include <asm/mach/map.h>
  14. 14  #include <asm/uaccess.h>
  15. 15  #include <asm/io.h>
  16. 16
  17. 17  /***************************************************************
  18. 18  Copyright &#169; ALIENTEK Co., Ltd. 1998-2029. All rights reserved.
  19. 19  文件名                  : dtsled.c
  20. 20  作者              : 正点原子Linux团队
  21. 21  版本              : V1.0
  22. 22  描述              : LED驱动文件。
  23. 23  其他              : 无
  24. 24  论坛              : <a href="www.openedv.com" target="_blank">www.openedv.com</a>
  25. 25  日志              : 初版V1.0 2020/12/19 正点原子Linux团队创建
  26. 26  ***************************************************************/
  27. 27  #define DTSLED_CNT               1                   /* 设备号个数         */
  28. 28  #define DTSLED_NAME             "dtsled"          /* 名字                 */
  29. 29  #define LEDOFF                   0           /* 关灯                         */
  30. 30  #define LEDON                    1           /* 开灯                         */
  31. 31
  32. 32  /* 映射后的寄存器虚拟地址指针 */
  33. 33  static void __iomem *MPU_AHB4_PERIPH_RCC_PI;
  34. 34  static void __iomem *GPIOI_MODER_PI;
  35. 35  static void __iomem *GPIOI_OTYPER_PI;
  36. 36  static void __iomem *GPIOI_OSPEEDR_PI;
  37. 37  static void __iomem *GPIOI_PUPDR_PI;
  38. 38  static void __iomem *GPIOI_BSRR_PI;
  39. 39
  40. 40  /* dtsled设备结构体 */
  41. 41  struct dtsled_dev{
  42. 42      dev_t devid;                    /* 设备号             */
  43. 43      struct cdev cdev;               /* cdev             */
  44. 44      struct class *class;           /* 类                      */
  45. 45      struct device *device;          /* 设备                    */
  46. 46      int major;                      /* 主设备号           */
  47. 47      int minor;                      /* 次设备号           */
  48. 48      struct device_node  *nd; /* 设备节点         */
  49. 49  };
  50. 50
  51. 51  struct dtsled_dev dtsled;           /* led设备         */
  52. 52
  53. 53  /*
  54. 54   * @description          : LED打开/关闭
  55. 55   * [url=home.php?mod=space&uid=271674]@param[/url] - sta          : LEDON(0) 打开LED,LEDOFF(1) 关闭LED
  56. 56   * @return               : 无
  57. 57   */
  58. 58  void led_switch(u8 sta)
  59. 59  {
  60. 60      u32 val = 0;
  61. 61      if(sta == LEDON) {
  62. 62          val = readl(GPIOI_BSRR_PI);
  63. 63          val |= (1 << 16);   
  64. 64          writel(val, GPIOI_BSRR_PI);
  65. 65      }else if(sta == LEDOFF) {
  66. 66          val = readl(GPIOI_BSRR_PI);
  67. 67          val|= (1 << 0);
  68. 68          writel(val, GPIOI_BSRR_PI);
  69. 69      }   
  70. 70  }
  71. 71
  72. 72  /*
  73. 73   * @description         : 取消映射
  74. 74   * @return                : 无
  75. 75   */
  76. 76  void led_unmap(void)
  77. 77  {
  78. 78            /* 取消映射 */
  79. 79      iounmap(MPU_AHB4_PERIPH_RCC_PI);
  80. 80      iounmap(GPIOI_MODER_PI);
  81. 81      iounmap(GPIOI_OTYPER_PI);
  82. 82      iounmap(GPIOI_OSPEEDR_PI);
  83. 83      iounmap(GPIOI_PUPDR_PI);
  84. 84      iounmap(GPIOI_BSRR_PI);
  85. 85  }
  86. 86
  87. 87  /*
  88. 88   * @description          : 打开设备
  89. 89   * @param – inode        : 传递给驱动的inode
  90. 90   * @param - filp         : 设备文件,file结构体有个叫做private_data的成员变量
  91. 91   *                    一般在open的时候将private_data指向设备结构体。
  92. 92   * @return                : 0 成功;其他 失败
  93. 93   */
  94. 94  static int led_open(struct inode *inode, struct file *filp)
  95. 95  {
  96. 96      filp->private_data = &dtsled; /* 设置私有数据 */
  97. 97      return 0;
  98. 98  }
  99. 99
  100. 100 /*
  101. 101  * @description          : 从设备读取数据
  102. 102  * @param - filp         : 要打开的设备文件(文件描述符)
  103. 103  * @param - buf          : 返回给用户空间的数据缓冲区
  104. 104  * @param - cnt          : 要读取的数据长度
  105. 105  * @param - offt         : 相对于文件首地址的偏移
  106. 106  * @return                : 读取的字节数,如果为负值,表示读取失败
  107. 107  */
  108. 108 static ssize_t led_read(struct file *filp, char __user *buf,
  109. size_t cnt, loff_t *offt)
  110. 109 {
  111. 110     return 0;
  112. 111 }
  113. 112
  114. 113 /*
  115. 114  * @description         : 向设备写数据
  116. 115  * @param - filp         : 设备文件,表示打开的文件描述符
  117. 116  * @param - buf          : 要写给设备写入的数据
  118. 117  * @param - cnt          : 要写入的数据长度
  119. 118  * @param - offt         : 相对于文件首地址的偏移
  120. 119  * @return                : 写入的字节数,如果为负值,表示写入失败
  121. 120  */
  122. 121 static ssize_t led_write(struct file *filp, const char __user *buf,
  123.                   size_t cnt, loff_t *offt)
  124. 122 {
  125. 123     int retvalue;
  126. 124     unsigned char databuf[1];
  127. 125     unsigned char ledstat;
  128. 126
  129. 127     retvalue = copy_from_user(databuf, buf, cnt);
  130. 128     if(retvalue < 0) {
  131. 129         printk("kernel write failed!\r\n");
  132. 130         return -EFAULT;
  133. 131     }
  134. 132
  135. 133     ledstat = databuf[0];       /* 获取状态值                 */
  136. 134
  137. 135     if(ledstat == LEDON) {  
  138. 136         led_switch(LEDON);              /* 打开LED灯                 */
  139. 137     } else if(ledstat == LEDOFF) {
  140. 138         led_switch(LEDOFF);                 /* 关闭LED灯                 */
  141. 139     }
  142. 140     return 0;
  143. 141 }
  144. 142
  145. 143 /*
  146. 144  * @description          : 关闭/释放设备
  147. 145  * @param – filp        : 要关闭的设备文件(文件描述符)
  148. 146  * @return               : 0 成功;其他 失败
  149. 147  */
  150. 148 static int led_release(struct inode *inode, struct file *filp)
  151. 149 {
  152. 150     return 0;
  153. 151 }
  154. 152
  155. 153 /* 设备操作函数 */
  156. 154 static struct file_operations dtsled_fops = {
  157. 155     .owner = THIS_MODULE,
  158. 156     .open = led_open,
  159. 157     .read = led_read,
  160. 158     .write = led_write,
  161. 159     .release =  led_release,
  162. 160 };
  163. 161
  164. 162 /*
  165. 163  * @description         : 驱动出口函数
  166. 164  * @param               : 无
  167. 165  * @return              : 无
  168. 166  */
  169. 167 static int __init led_init(void)
  170. 168 {
  171. 169     u32 val = 0;
  172. 170     int ret;
  173. 171     u32 regdata[12];
  174. 172     const char *str;
  175. 173     struct property *proper;
  176. 174
  177. 175     /* 获取设备树中的属性数据                         */
  178. 176     /* 1、获取设备节点:stm32mp1_led         */
  179. 177     dtsled.nd = of_find_node_by_path("/stm32mp1_led");
  180. 178     if(dtsled.nd == NULL) {
  181. 179         printk("stm32mp1_led node nost find!\r\n");
  182. 180         return -EINVAL;
  183. 181     } else {
  184. 182         printk("stm32mp1_lcd node find!\r\n");
  185. 183     }
  186. 184
  187. 185     /* 2、获取compatible属性内容 */
  188. 186     proper = of_find_property(dtsled.nd, "compatible", NULL);
  189. 187     if(proper == NULL) {
  190. 188         printk("compatible property find failed\r\n");
  191. 189     } else {
  192. 190         printk("compatible = %s\r\n", (char*)proper->value);
  193. 191     }
  194. 192
  195. 193     /* 3、获取status属性内容 */
  196. 194     ret = of_property_read_string(dtsled.nd, "status", &str);
  197. 195     if(ret < 0){
  198. 196         printk("status read failed!\r\n");
  199. 197     } else {
  200. 198         printk("status = %s\r\n",str);
  201. 199     }
  202. 200
  203. 201     /* 4、获取reg属性内容 */
  204. 202     ret = of_property_read_u32_array(dtsled.nd, "reg", regdata, 12);
  205. 203     if(ret < 0) {
  206. 204         printk("reg property read failed!\r\n");
  207. 205     } else {
  208. 206         u8 i = 0;
  209. 207         printk("reg data:\r\n");
  210. 208         for(i = 0; i < 12; i++)
  211. 209             printk("%#X ", regdata<i>);
  212. 210         printk("\r\n");
  213. 211     }
  214. 212
  215. 213     /* 初始化LED                         */
  216. 214     /* 1、寄存器地址映射         */
  217. 215     MPU_AHB4_PERIPH_RCC_PI = of_iomap(dtsled.nd, 0);
  218. 216     GPIOI_MODER_PI = of_iomap(dtsled.nd, 1);
  219. 217     GPIOI_OTYPER_PI = of_iomap(dtsled.nd, 2);
  220. 218     GPIOI_OSPEEDR_PI = of_iomap(dtsled.nd, 3);
  221. 219     GPIOI_PUPDR_PI = of_iomap(dtsled.nd, 4);
  222. 220     GPIOI_BSRR_PI = of_iomap(dtsled.nd, 5);
  223. 221
  224. 222     /* 2、使能PI时钟 */
  225. 223     val = readl(MPU_AHB4_PERIPH_RCC_PI);
  226. 224     val &= ~(0X1 << 8);         /* 清除以前的设置         */
  227. 225     val |= (0X1 << 8);          /* 设置新值                         */
  228. 226     writel(val, MPU_AHB4_PERIPH_RCC_PI);
  229. 227
  230. 228     /* 3、设置PI0通用的输出模式。*/
  231. 229     val = readl(GPIOI_MODER_PI);
  232. 230     val &= ~(0X3 << 0);                 /* bit0:1清零                 */
  233. 231     val |= (0X1 << 0);                  /* bit0:1设置01         */
  234. 232     writel(val, GPIOI_MODER_PI);
  235. 233
  236. 234     /* 3、设置PI0为推挽模式。*/
  237. 235     val = readl(GPIOI_OTYPER_PI);
  238. 236     val &= ~(0X1 << 0);                 /* bit0清零,设置为上拉*/
  239. 237     writel(val, GPIOI_OTYPER_PI);
  240. 238
  241. 239     /* 4、设置PI0为高速。*/
  242. 240     val = readl(GPIOI_OSPEEDR_PI);
  243. 241     val &= ~(0X3 << 0);                 /* bit0:1 清零                 */
  244. 242     val |= (0x2 << 0);                 /* bit0:1 设置为10                */
  245. 243     writel(val, GPIOI_OSPEEDR_PI);
  246. 244
  247. 245     /* 5、设置PI0为上拉。*/
  248. 246     val = readl(GPIOI_PUPDR_PI);
  249. 247     val &= ~(0X3 << 0);                 /* bit0:1 清零                        */
  250. 248     val |= (0x1 << 0);                 /* bit0:1 设置为01                */
  251. 249     writel(val,GPIOI_PUPDR_PI);
  252. 250
  253. 251     /* 6、默认关闭LED */
  254. 252     val = readl(GPIOI_BSRR_PI);
  255. 253     val |= (0x1 << 0);
  256. 254     writel(val, GPIOI_BSRR_PI);
  257. 255
  258. 256     /* 注册字符设备驱动 */
  259. 257     /* 1、创建设备号 */
  260. 258     if (dtsled.major) {             /*  定义了设备号                 */
  261. 259         dtsled.devid = MKDEV(dtsled.major, 0);
  262. 260         ret = register_chrdev_region(dtsled.devid, DTSLED_CNT, DTSLED_NAME);
  263. 261         if(ret < 0) {
  264. 262             pr_err("cannot register %s char driver [ret=%d]\n",DTSLED_NAME, DTSLED_CNT);
  265. 263             goto fail_map;
  266. 264         }
  267. 265     } else {                           /* 没有定义设备号                 */
  268. 266         ret = alloc_chrdev_region(&dtsled.devid, 0, DTSLED_CNT,
  269.                         DTSLED_NAME);   /* 申请设备号 */
  270. 267         if(ret < 0) {
  271. 268             pr_err("%s Couldn't alloc_chrdev_region, ret=%d\r\n",
  272. DTSLED_NAME, ret);
  273. 269             goto fail_map;
  274. 270         }
  275. 271         dtsled.major = MAJOR(dtsled.devid); /* 获取分配号的主设备号 */
  276. 272         dtsled.minor = MINOR(dtsled.devid); /* 获取分配号的次设备号 */
  277. 273
  278. 274     }
  279. 275     printk("dtsled major=%d,minor=%d\r\n",dtsled.major,
  280. dtsled.minor);  
  281. 276     
  282. 277     /* 2、初始化cdev */
  283. 278     dtsled.cdev.owner = THIS_MODULE;
  284. 279     cdev_init(&dtsled.cdev, &dtsled_fops);
  285. 280     
  286. 281     /* 3、添加一个cdev */
  287. 282     ret = cdev_add(&dtsled.cdev, dtsled.devid, DTSLED_CNT);
  288. 283     if(ret < 0)
  289. 284         goto del_unregister;
  290. 285
  291. 286     /* 4、创建类                 */
  292. 287     dtsled.class = class_create(THIS_MODULE, DTSLED_NAME);
  293. 288     if (IS_ERR(dtsled.class)) {
  294. 289         goto del_cdev;
  295. 290     }
  296. 291
  297. 292     /* 5、创建设备                 */
  298. 293     dtsled.device = device_create(dtsled.class, NULL, dtsled.devid,
  299. NULL, DTSLED_NAME);
  300. 294     if (IS_ERR(dtsled.device)) {
  301. 295         goto destroy_class;
  302. 296     }
  303. 297
  304. 298     return 0;
  305. 299
  306. 300 destroy_class:
  307. 301     class_destroy(dtsled.class);
  308. 302 del_cdev:
  309. 303     cdev_del(&dtsled.cdev);
  310. 304 del_unregister:
  311. 305     unregister_chrdev_region(dtsled.devid, DTSLED_CNT);
  312. 306 fail_map:
  313. 307     led_unmap();
  314. 308     return -EIO;
  315. 309 }
  316. 310
  317. 311 /*
  318. 312  * @description         : 驱动出口函数
  319. 313  * @param               : 无
  320. 314  * @return              : 无
  321. 315  */
  322. 316 static void __exit led_exit(void)
  323. 317 {
  324. 318     /* 取消映射                 */
  325. 319     led_unmap();
  326. 320
  327. 321     /* 注销字符设备驱动 */
  328. 322     cdev_del(&dtsled.cdev);        /*  删除cdev */
  329. 323     unregister_chrdev_region(dtsled.devid, DTSLED_CNT); /*注销*/
  330. 324
  331. 325     device_destroy(dtsled.class, dtsled.devid);
  332. 326     class_destroy(dtsled.class);
  333. 327 }
  334. 328
  335. 329 module_init(led_init);
  336. 330 module_exit(led_exit);
  337. 331 MODULE_LICENSE("GPL");
  338. 332 MODULE_AUTHOR("ALIENTEK");
  339. 333 MODULE_INFO(intree, "Y");</i>
复制代码


dtsled.c文件中的内容和第二十二章的newchrled.c文件中的内容基本一样,只是dtsled.c中包含了处理设备树的代码,我们重点来看一下这部分代码。
第48行,在设备结构体dtsled_dev中添加了成员变量nd,nd是device_node结构体类型指针,表示设备节点。如果我们要读取设备树某个节点的属性值,首先要先得到这个节点,一般在设备结构体中添加device_node指针变量来存放这个节点。
第177~183行,通过of_find_node_by_path函数得到stm32mp1_led节点,后续其他的OF函数要使用device_node。
第186~191行,通过of_find_property函数获取stm32mp1_led节点的compatible属性,返回值为property结构体类型指针变量,property的成员变量value表示属性值。
第194~199行,通过of_property_read_string函数获取stm32mp1_led节点的status属性值。
第202~211行,通过of_property_read_u32_array函数获取stm32mp1_led节点的reg属性所有值,并且将获取到的值都存放到regdata数组中。第209行将获取到的reg属性值依次输出到终端上。
第215~220行,使用of_iomap函数一次性完成读取reg属性以及内存映射,of_iomap函数是设备树推荐使用的OF函数。
24.3.3 编写测试APP
        本章直接使用第二十二章的测试APP,将上一章的ledApp.c文件复制到本章实验工程下即可。
24.4.1 编译驱动程序和测试APP
        1、编译驱动程序
        编写Makefile文件,本章实验的Makefile文件和第二十章实验基本一样,只是将obj-m变量的值改为dtsled.o,Makefile内容如下所示:
示例代码24.4.1.1 Makefile文件
  1. 1  KERNELDIR := /home/zuozhongkai/linux/my_linux/linux-5.4.31
  2. ......
  3. 4  obj-m := dtsled.o
  4. ......
  5. 11 clean:
  6. 12  $(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean
复制代码


第4行,设置obj-m变量的值为dtsled.o。
        输入如下命令编译出驱动模块文件:
  1. make -j32
复制代码


        编译成功以后就会生成一个名为“dtsled.ko”的驱动模块文件。
        2、编译测试APP
        输入如下命令编译测试ledApp.c这个测试程序:
  1. arm-none-linux-gnueabihf-gcc ledApp.c -o ledApp
复制代码


        编译成功以后就会生成ledApp这个应用程序。
24.4.2 运行测试
        将上一小节编译出来的dtsled.ko和ledApp这两个文件拷贝到rootfs/lib/modules/5.4.31目录中,重启开发板,进入到目录lib/modules/5.4.31中,输入如下命令加载dtsled.ko驱动模块:
  1. depmod                                //第一次加载驱动的时候需要运行此命令
  2. modprobe dtsled                //加载驱动
复制代码


        驱动加载成功以后会在终端中输出一些信息,如图24.4.2.1所示:
第二十四章 设备树下的LED驱动实验13394.png
图24.4.2.1 驱动加载成功以后输出的信息
        从图24.4.2.1可以看出,stm32mp1_led这个节点找到了,并且compatible属性值为“atkstm32mp1-led”,status属性值为“okay”,reg属性的值为“0X50000A28 0X4 0X5000A000 0X4 0X5000A004 0X4 0X5000A008 0X4 0X5000A00C 0X4 0X5000A018 0X4”,这些都和我们设置的设备树一致。
        驱动加载成功以后就可以使用ledApp软件来测试驱动是否工作正常,输入如下命令打开LED灯:
  1. ./ledApp /dev/dtsled 1                //打开LED灯
复制代码


        输入上述命令以后观察开发板上的红色LED灯是否点亮,如果点亮的话说明驱动工作正常。在输入如下命令关闭LED灯:
  1. ./ledApp  /dev/dtsled 0                //关闭LED灯
复制代码


        输入上述命令以后观察开发板上的红色LED灯是否熄灭。如果要卸载驱动的话输入如下命令即可:
  1. rmmod dtsled.ko
复制代码


正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2024-6-10 20:58

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表