搜索
bottom↓
回复: 17

[SylixOS & iMXRT1050][3] GPIO测试例程

[复制链接]

出0入0汤圆

发表于 2018-12-19 16:57:47 | 显示全部楼层 |阅读模式
本帖最后由 科技猎人 于 2018-12-19 17:11 编辑

GPIO(General Purpose Input/Output),即通用输入/输出端口,以下简称I/O端口。I/O端口可提供输入、输出或中断三类功能,是嵌入式领域最常见最基础的硬件设备。
如果在应用程序中使用芯片的GPIO标识方法进行编程,既显得杂乱,也不利于软件的可移植性,因此SylixOS将所有的GPIO统一编号为数字,范围0~255,应用层通过该编号来索引需要操作的GPIO管脚。具体某个硬件平台有哪些可用的GPIO则由驱动来实现,在/dev/gpiofd目录下可查看到。
对于应用层来说,GPIO就是/dev/gpiofd目录下的某个设备文件,通过标准文件函数就能够操作。鉴于GPIO使用的一些固有特点,以及为了简化接口,SylixOS又进一步封装标准文件函数得到三个简洁的GPIO操作函数,文件关闭依旧使用close函数。

  1. #include <sys/gpiofd.h>
  2. int gpiofd(unsigned int gpio, int flags, int gpio_flags);
  3. int gpiofd_read(int fd, uint8_t *value);
  4. int gpiofd_write(int fd, uint8_t  value);
复制代码

函数gpiofd原型分析:
此函数成功时返回对应GPIO端口的文件描述符,失败时返回负数;
参数gpio为GPIO端口的唯一编号,该编号与具体的系统硬件相关,应用程序应该参考BSP包对GPIO端口编号的定义来正确选择;
参数flags与open函数的第二个参数意义相似,即可以是O_RDONLY、O_RDWR、O_RDWR等,但内部其实都是按照O_RDWR参数来操作的;
参数gpio_flags是与GPIO特性相关的标识,它可以是多个位标识的组合。参考下表:


注:当使用了GPIO_FLAG_TRIG_LEVE标识时,我们仅能用GPIO_FLAG_TRIG_FALL与GPIO_FLAG_TRIG_RISE中的一个与其组合使用,分别表示低电平触发和高电平触发。当没有使用GPIO_FLAG_TRIG_LEVE时,我们可以将GPIO_FLAG_TRIG_FALL和GPIO_FLAG_TRIG_RISE组合使用表示双边沿触发。
函数gpiofd_read读取一个GPIO端口的电平状态,只有0和1两个值,原型分析如下:
此函数成功返回0,失败返回错误码;
参数fd为GPIO端口对应的文件描述符;
输出参数value保存读取到的电平值,0表示低电平,1表示高电平。
函数gpiofd_write设置一个GPIO端口的电平状态,只有0和1两个值,原型分析如下:
此函数成功返回0,失败返回错误码;
参数fd为GPIO端口对应的文件描述符;
参数value为需要设置的电平值,0表示低电平,1表示高电平。


GPIO查询方式,例程源码位于/extExample/SylixOS/user/devExample/gpioExample.c
例程操作流程为:
使用 gpiofd 函数打开KEY08和LED02设备;
使用gpiofd_read函数读取KEY08按键输入状态;
使用gpiofd_write设置LED02输出状态;
退出时,使用close函数关闭打开的设备文件;

  1. INT  gpioExampleStart1 (VOID)
  2. {
  3.     INT      keyfd;
  4.     INT      ledfd;
  5.     uint8_t  value;

  6.     printf("Gpio example1. Waiting press the key SW8.\n");

  7.     keyfd = gpiofd(KEY08, O_RDWR, GPIO_FLAG_IN);  /*  打开 KEY 的 GPIO 文件       */
  8.     if (keyfd < 0) {
  9.         printf("open gpio %d failed!\n", KEY08);
  10.         return  (PX_ERROR);
  11.     }

  12.     ledfd = gpiofd(LED02, O_RDWR, GPIO_FLAG_OUT_INIT_LOW);              /*  打开 LED 的 GPIO 文件       */
  13.     if (ledfd < 0) {
  14.         printf("open gpio %d failed!\n", LED02);
  15.         close(keyfd);
  16.         return  (PX_ERROR);
  17.     }

  18.     while (1) {
  19.         gpiofd_read(keyfd, &value);                /*  读取当前按键值              */
  20.         printf("The key value = %d\n", value);
  21.         gpiofd_write(ledfd, value & 0x01);         /*  设置LED引脚输出             */
  22.         sleep(1);
  23.     }
  24.     close(keyfd);                                    /*  关闭 KEY 的 GPIO 文件       */
  25.     close(ledfd);                                    /*  关闭 LED 的 GPIO 文件       */

  26.     return  (ERROR_NONE);
  27. }
复制代码

上面以输入方式打开KEY的 GPIO 文件,以输出且初始值为低电平的方式打开了LED的 GPIO 文件,然后又以1秒的间隔读取按键值并设置LED状态。gpiofd_read和gpiofd_write都是不会阻塞的。
上面的方式仅能处理GPIO的输入输出,但无法使用其中断功能。我们知道,I/O多路复用(select)允许任务阻塞地等待一个或多个文件描述符满足指定状态(可读、可写或异常),SylixOS利用这一点,为应用程序提供了使用GPIO中断功能的方法。当一个具有中断功能的GPIO产生中断时,内核会唤醒所有通过调用select等待该GPIO文件描述符可读状态的线程,通知线程中断产生。当select正确返回时,线程处理相应的事务,这类似于完成了一次中断服务。
GPIO中断方式,例程源码位于/extExample/SylixOS/user/devExample/gpioExample.c
例程操作流程为:
使用 gpiofd 函数打开KEY08和LED02设备;
使用select函数等待KEY08下降沿中断;
使用gpiofd_write设置LED02输出状态;
退出时,使用close函数关闭打开的设备文件;


实验步骤
1.输入如下命令,启动查询方式的gpio例程:
[root@sylixos:/]# example  gpio
2.此时控制台每秒输出一次按键值,按下按键可看到按键值回变为0。LED则会按照按键状态改变而改变。

3.复位系统,输入命令,启动中断方式的gpio例程:
[root@sylixos:/]# example  gpio    -i
4.按下按键会触发下降沿中断,从而唤醒一次select函数。每按一次按键会打印出按动次数,同时LED变化一次。


完整代码
  1. /*********************************************************************************************************
  2. **
  3. **                                    中国软件开源组织
  4. **
  5. **                                   嵌入式实时操作系统
  6. **
  7. **                                SylixOS(TM)  LW : long wing
  8. **
  9. **                               Copyright All Rights Reserved
  10. **
  11. **--------------文件信息--------------------------------------------------------------------------------
  12. **
  13. ** 文   件   名: gpioExample.c
  14. **
  15. ** 创   建   人: Hou.JinYu (侯进宇)
  16. **
  17. ** 文件创建日期: 2017 年 12 月 13 日
  18. **
  19. ** 描        述: gpio 例程。因为连接板载 LED01 的引脚同时也连接了 enet 的复位脚,硬件冲突故不能使用。
  20. **               LED02 只是一个空闲的gpio(为J22.7),需要用户连接LED或万用表来检测电平变化。
  21. *********************************************************************************************************/
  22. #include <stdio.h>
  23. #include <unistd.h>
  24. #include <sys/select.h>
  25. #include <sys/gpiofd.h>
  26. #include "gpio/gpio.h"
  27. #include "config.h"
  28. /*********************************************************************************************************
  29.    引脚宏定义
  30. ********************************************************************************************************/
  31. #define KEY08        GPIO_E_00                                          /*  GPIO5--00                   */
  32. #define LED01        GPIO_A_09                                          /*  GPIO1--09                   */
  33. #define LED02        GPIO_A_18                                          /*  GPIO1--18                   */
  34. /*********************************************************************************************************
  35. ** 函数名称: gpioExampleStart1
  36. ** 功能描述: gpio 轮询读取例程
  37. ** 输 入  : NONE
  38. ** 输 出  : ERROR_CODE
  39. ** 全局变量:
  40. ** 调用模块:
  41. *********************************************************************************************************/
  42. static  INT  gpioExampleStart1 (VOID)
  43. {
  44.     INT      keyfd;
  45.     INT      ledfd;
  46.     uint8_t  value;

  47.     printf("Gpio poll example. Waiting press the key SW8.\n");

  48.     keyfd = gpiofd(KEY08, O_RDWR, GPIO_FLAG_IN);                        /*  打开 KEY 的 GPIO 文件       */
  49.     if (keyfd < 0) {
  50.         printf("open gpio %d failed!\n", KEY08);
  51.         return  (PX_ERROR);
  52.     }

  53.     ledfd = gpiofd(LED02, O_RDWR, GPIO_FLAG_OUT_INIT_LOW);              /*  打开 LED 的 GPIO 文件       */
  54.     if (ledfd < 0) {
  55.         printf("open gpio %d failed!\n", LED02);
  56.         close(keyfd);
  57.         return  (PX_ERROR);
  58.     }

  59.     while (1) {
  60.         gpiofd_read(keyfd, &value);                                     /*  读取当前按键值              */
  61.         printf("The key value = %d\n", value);
  62.         gpiofd_write(ledfd, value & 0x01);                              /*  设置LED引脚输出             */
  63.         sleep(1);
  64.     }
  65.     close(keyfd);                                                       /*  关闭 KEY 的 GPIO 文件       */
  66.     close(ledfd);                                                       /*  关闭 LED 的 GPIO 文件       */

  67.     return  (ERROR_NONE);
  68. }
  69. /*********************************************************************************************************
  70. ** 函数名称: gpioExampleStart2
  71. ** 功能描述: gpio 中断例程
  72. ** 输 入  : NONE
  73. ** 输 出  : ERROR_CODE
  74. ** 全局变量:
  75. ** 调用模块:
  76. *********************************************************************************************************/
  77. static  INT  gpioExampleStart2 (VOID)
  78. {
  79.     INT      n = 0;
  80.     fd_set   fdset;
  81.     INT      ret;
  82.     INT      keyfd;
  83.     INT      ledfd;

  84.     printf("Gpio interrupt example. Waiting press the key SW8.\n");

  85.     keyfd = gpiofd(KEY08, O_RDWR, GPIO_FLAG_TRIG_FALL);                 /*  打开 KEY 的 GPIO 文件       */
  86.     if (keyfd < 0) {
  87.         printf("open gpio %d failed!\n", KEY08);
  88.         return  (PX_ERROR);
  89.     }

  90.     ledfd = gpiofd(LED02, O_RDWR, GPIO_FLAG_OUT_INIT_LOW);              /*  打开 LED 的 GPIO 文件       */
  91.     if (ledfd < 0) {
  92.         printf("open gpio %d failed!\n", LED02);
  93.         close(keyfd);
  94.         return  (PX_ERROR);
  95.     }

  96.     FD_ZERO(&fdset);

  97.     while (1) {
  98.         FD_SET(keyfd, &fdset);
  99.         ret = select(keyfd + 1, &fdset, NULL, NULL, NULL);              /*  调用 select 等待按键被按下  */
  100.         if (ret == 1) {
  101.             printf("The key effective, n = %d\n", n++);
  102.             gpiofd_write(ledfd, n & 0x01);                              /*  设置LED引脚输出             */
  103.         } else if (ret < 0) {
  104.             printf("select error!\n");
  105.             break;
  106.         }
  107.     }
  108.     close(keyfd);                                                       /*  关闭 KEY 的 GPIO 文件       */
  109.     close(ledfd);                                                       /*  关闭 LED 的 GPIO 文件       */

  110.     return  (ERROR_NONE);
  111. }
  112. /*********************************************************************************************************
  113. ** 函数名称: mainExampleStart
  114. ** 功能描述: uart 测试启动函数
  115. ** 输 入  : 设备号,使用方式 ./uart_test /dev/ttyS1
  116. ** 输 出  : 无
  117. ** 全局变量:
  118. ** 调用模块:
  119. *********************************************************************************************************/
  120. static  INT  gpioExampleStart (INT  iArgC, PCHAR  ppcArgV[])
  121. {
  122.     int c;
  123.     int interruptTypy = 0;

  124.     if (iArgC < 1) {
  125.         return  (0);
  126.     }

  127.     while (1) {
  128.         c = getopt(iArgC, ppcArgV, "i");
  129.         if (c == -1) {
  130.             break;
  131.         }

  132.         switch (c) {
  133.         case 'i':
  134.             interruptTypy = 1;
  135.             break;

  136.         default:
  137.             return  (PX_ERROR);
  138.         }
  139.     }

  140.     if (interruptTypy) {
  141.         gpioExampleStart2();
  142.     } else {
  143.         gpioExampleStart1();
  144.     }

  145.     return  (0);
  146. }
  147. SHELL_CMD_REG("gpio", gpioExampleStart);
  148. /*********************************************************************************************************
  149.   END
  150. *********************************************************************************************************/
复制代码

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

阿莫论坛20周年了!感谢大家的支持与爱护!!

月入3000的是反美的。收入3万是亲美的。收入30万是移民美国的。收入300万是取得绿卡后回国,教唆那些3000来反美的!

出0入0汤圆

 楼主| 发表于 2018-12-19 17:12:41 | 显示全部楼层
呃。。。图片位置又错乱了

出0入0汤圆

发表于 2018-12-19 17:23:42 | 显示全部楼层
能老老实实把翼辉的IDE装上的,都是真爱

出50入0汤圆

发表于 2018-12-19 17:26:27 | 显示全部楼层
看起来不错,QT需要在LINUX下操作?

出0入0汤圆

 楼主| 发表于 2018-12-19 17:30:15 | 显示全部楼层
ground 发表于 2018-12-19 17:26
看起来不错,QT需要在LINUX下操作?

SylixOS支持QT。和Linux没啥关系。

出0入0汤圆

发表于 2018-12-20 21:29:07 | 显示全部楼层
本帖最后由 knight_hu 于 2018-12-20 21:31 编辑

我正好前段时间试了下用这个gpio通过select中断方式测试对外部信号响应的情况,对1ms的信号响应抖动非常大,就是通过示波器产生一个方波给到GPIO去中断,然后任务里面操作另一个GPIO用示波器余晖看响应的延迟和抖动,发现延迟最大要到到50us,整个响应就在0-50us里面抖动,请问有没有响应更快,更稳定的API或者使用方法呢?

出0入0汤圆

发表于 2018-12-20 21:30:26 | 显示全部楼层
谢谢分享,学习了

出0入0汤圆

发表于 2018-12-20 21:32:05 | 显示全部楼层
大神能教教我么?

出0入0汤圆

 楼主| 发表于 2018-12-21 09:24:23 | 显示全部楼层
knight_hu 发表于 2018-12-20 21:29
我正好前段时间试了下用这个gpio通过select中断方式测试对外部信号响应的情况,对1ms的信号响应抖动非常大 ...

是在任务里等地中断并发出信号?
任务是会被中断程序或其他高优先级任务打断的。
所以想提高实时性线程的优先级,要更高实时性可以在驱动的中断里处理。

出0入0汤圆

发表于 2018-12-21 13:09:34 | 显示全部楼层
还有两个问题,
1、SylixOS处理中断的话,是不是也是先关中断然后执行API_InterVectorConnect挂上去的中断任务的呢?如果是的话,如果某一个中断执行代码太多就有可能错过其他中断?
2、如果还是采用中断中发送信号量到处理任务,那这个处理任务响应速度要提高的话,唯一的方法就是提高它本身的优先级,以便尽快抢占到资源去处理?

出0入0汤圆

 楼主| 发表于 2018-12-21 13:41:37 | 显示全部楼层
knight_hu 发表于 2018-12-21 13:09
还有两个问题,
1、SylixOS处理中断的话,是不是也是先关中断然后执行API_InterVectorConnect挂上去的中断 ...

不会一直关中断,中断可以抢占。驱动设计中自然是要求中断执行时间越短越好,否则会影响实时性。
提高任务优先级才能保证不被其他任务打断。

出100入85汤圆

发表于 2018-12-21 13:44:10 来自手机 | 显示全部楼层
共享下ide

出0入0汤圆

 楼主| 发表于 2018-12-21 13:45:35 | 显示全部楼层

需要到官网申请,申请页面http://www.acoinfo.com/html/experience.php

出0入0汤圆

发表于 2018-12-21 13:49:54 | 显示全部楼层
谢谢,那我这两个方法都试试看

出0入0汤圆

 楼主| 发表于 2018-12-21 13:52:28 | 显示全部楼层
knight_hu 发表于 2018-12-21 13:49
谢谢,那我这两个方法都试试看

方便问一下是对于SylixOS是学习研究,还是做实际项目,哪个领域的项目?

出0入0汤圆

发表于 2018-12-21 15:05:37 | 显示全部楼层
本帖最后由 knight_hu 于 2018-12-21 15:07 编辑

我也是在学习,用的开源的工具,刚碰到个问题,任务里面用中断方式去做的话,运行几秒钟就会出现jobQueueAdd() error: job message lost。是什么情况,中断太快响应不过来?
现在中断是500us来一个,任务堆栈应该也够大了吧?       
        Lw_ThreadAttr_Build(&threadattr,
                                                512 * LW_CFG_KB_SIZE,
                                                LW_PRIO_CRITICAL,
                                                LW_OPTION_THREAD_STK_CHK,
                                                LW_NULL);

出0入0汤圆

 楼主| 发表于 2018-12-21 15:16:07 | 显示全部楼层
knight_hu 发表于 2018-12-21 15:05
我也是在学习,用的开源的工具,刚碰到个问题,任务里面用中断方式去做的话,运行几秒钟就会出现jobQueueAd ...

应该是中断太频繁了,你调低频率试试

出0入0汤圆

发表于 2018-12-21 15:35:16 | 显示全部楼层
好的,我如果直接用API_InterVectorConnect把中断任务插入到中断列表去处理的话,会报下面这个错误
_Schedule() bug: scheduler candidate serious error, ptcb 0x8083bec0, name "t_netproto", status 0x5
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-4-25 18:02

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

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