科技猎人 发表于 2018-12-19 16:57:47

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

本帖最后由 科技猎人 于 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函数。

#include <sys/gpiofd.h>
int gpiofd(unsigned int gpio, int flags, int gpio_flags);
int gpiofd_read(int fd, uint8_t *value);
int gpiofd_write(int fd, uint8_tvalue);
函数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函数关闭打开的设备文件;

INTgpioExampleStart1 (VOID)
{
    INT      keyfd;
    INT      ledfd;
    uint8_tvalue;

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

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

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

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

    return(ERROR_NONE);
}
上面以输入方式打开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例程:
# examplegpio
2.此时控制台每秒输出一次按键值,按下按键可看到按键值回变为0。LED则会按照按键状态改变而改变。

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


完整代码
/*********************************************************************************************************
**
**                                    中国软件开源组织
**
**                                 嵌入式实时操作系统
**
**                              SylixOS(TM)LW : long wing
**
**                               Copyright All Rights Reserved
**
**--------------文件信息--------------------------------------------------------------------------------
**
** 文   件   名: gpioExample.c
**
** 创   建   人: Hou.JinYu (侯进宇)
**
** 文件创建日期: 2017 年 12 月 13 日
**
** 描      述: gpio 例程。因为连接板载 LED01 的引脚同时也连接了 enet 的复位脚,硬件冲突故不能使用。
**               LED02 只是一个空闲的gpio(为J22.7),需要用户连接LED或万用表来检测电平变化。
*********************************************************************************************************/
#include <stdio.h>
#include <unistd.h>
#include <sys/select.h>
#include <sys/gpiofd.h>
#include "gpio/gpio.h"
#include "config.h"
/*********************************************************************************************************
   引脚宏定义
********************************************************************************************************/
#define KEY08      GPIO_E_00                                          /*GPIO5--00                   */
#define LED01      GPIO_A_09                                          /*GPIO1--09                   */
#define LED02      GPIO_A_18                                          /*GPIO1--18                   */
/*********************************************************************************************************
** 函数名称: gpioExampleStart1
** 功能描述: gpio 轮询读取例程
** 输 入: NONE
** 输 出: ERROR_CODE
** 全局变量:
** 调用模块:
*********************************************************************************************************/
staticINTgpioExampleStart1 (VOID)
{
    INT      keyfd;
    INT      ledfd;
    uint8_tvalue;

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

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

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

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

    return(ERROR_NONE);
}
/*********************************************************************************************************
** 函数名称: gpioExampleStart2
** 功能描述: gpio 中断例程
** 输 入: NONE
** 输 出: ERROR_CODE
** 全局变量:
** 调用模块:
*********************************************************************************************************/
staticINTgpioExampleStart2 (VOID)
{
    INT      n = 0;
    fd_set   fdset;
    INT      ret;
    INT      keyfd;
    INT      ledfd;

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

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

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

    FD_ZERO(&fdset);

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

    return(ERROR_NONE);
}
/*********************************************************************************************************
** 函数名称: mainExampleStart
** 功能描述: uart 测试启动函数
** 输 入: 设备号,使用方式 ./uart_test /dev/ttyS1
** 输 出: 无
** 全局变量:
** 调用模块:
*********************************************************************************************************/
staticINTgpioExampleStart (INTiArgC, PCHARppcArgV[])
{
    int c;
    int interruptTypy = 0;

    if (iArgC < 1) {
      return(0);
    }

    while (1) {
      c = getopt(iArgC, ppcArgV, "i");
      if (c == -1) {
            break;
      }

      switch (c) {
      case 'i':
            interruptTypy = 1;
            break;

      default:
            return(PX_ERROR);
      }
    }

    if (interruptTypy) {
      gpioExampleStart2();
    } else {
      gpioExampleStart1();
    }

    return(0);
}
SHELL_CMD_REG("gpio", gpioExampleStart);
/*********************************************************************************************************
END
*********************************************************************************************************/

科技猎人 发表于 2018-12-19 17:12:41

呃。。。图片位置又错乱了

RAMILE 发表于 2018-12-19 17:23:42

能老老实实把翼辉的IDE装上的,都是真爱

ground 发表于 2018-12-19 17:26:27

看起来不错,QT需要在LINUX下操作?

科技猎人 发表于 2018-12-19 17:30:15

ground 发表于 2018-12-19 17:26
看起来不错,QT需要在LINUX下操作?

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

knight_hu 发表于 2018-12-20 21:29:07

本帖最后由 knight_hu 于 2018-12-20 21:31 编辑

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

TKZXJ 发表于 2018-12-20 21:30:26

谢谢分享,学习了

knight_hu 发表于 2018-12-20 21:32:05

大神能教教我么?

科技猎人 发表于 2018-12-21 09:24:23

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

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

knight_hu 发表于 2018-12-21 13:09:34

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

科技猎人 发表于 2018-12-21 13:41:37

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

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

whatcanitbe 发表于 2018-12-21 13:44:10

共享下ide

科技猎人 发表于 2018-12-21 13:45:35

whatcanitbe 发表于 2018-12-21 13:44
共享下ide

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

knight_hu 发表于 2018-12-21 13:49:54

谢谢,那我这两个方法都试试看

科技猎人 发表于 2018-12-21 13:52:28

knight_hu 发表于 2018-12-21 13:49
谢谢,那我这两个方法都试试看

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

knight_hu 发表于 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);

科技猎人 发表于 2018-12-21 15:16:07

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

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

knight_hu 发表于 2018-12-21 15:35:16

好的,我如果直接用API_InterVectorConnect把中断任务插入到中断列表去处理的话,会报下面这个错误{:cry:}
_Schedule() bug: scheduler candidate serious error, ptcb 0x8083bec0, name "t_netproto", status 0x5
页: [1]
查看完整版本: [SylixOS & iMXRT1050][3] GPIO测试例程