[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
*********************************************************************************************************/
呃。。。图片位置又错乱了 能老老实实把翼辉的IDE装上的,都是真爱 看起来不错,QT需要在LINUX下操作? ground 发表于 2018-12-19 17:26
看起来不错,QT需要在LINUX下操作?
SylixOS支持QT。和Linux没啥关系。 本帖最后由 knight_hu 于 2018-12-20 21:31 编辑
我正好前段时间试了下用这个gpio通过select中断方式测试对外部信号响应的情况,对1ms的信号响应抖动非常大,就是通过示波器产生一个方波给到GPIO去中断,然后任务里面操作另一个GPIO用示波器余晖看响应的延迟和抖动,发现延迟最大要到到50us,整个响应就在0-50us里面抖动,请问有没有响应更快,更稳定的API或者使用方法呢? 谢谢分享,学习了 大神能教教我么? knight_hu 发表于 2018-12-20 21:29
我正好前段时间试了下用这个gpio通过select中断方式测试对外部信号响应的情况,对1ms的信号响应抖动非常大 ...
是在任务里等地中断并发出信号?
任务是会被中断程序或其他高优先级任务打断的。
所以想提高实时性线程的优先级,要更高实时性可以在驱动的中断里处理。 还有两个问题,
1、SylixOS处理中断的话,是不是也是先关中断然后执行API_InterVectorConnect挂上去的中断任务的呢?如果是的话,如果某一个中断执行代码太多就有可能错过其他中断?
2、如果还是采用中断中发送信号量到处理任务,那这个处理任务响应速度要提高的话,唯一的方法就是提高它本身的优先级,以便尽快抢占到资源去处理? knight_hu 发表于 2018-12-21 13:09
还有两个问题,
1、SylixOS处理中断的话,是不是也是先关中断然后执行API_InterVectorConnect挂上去的中断 ...
不会一直关中断,中断可以抢占。驱动设计中自然是要求中断执行时间越短越好,否则会影响实时性。
提高任务优先级才能保证不被其他任务打断。 共享下ide whatcanitbe 发表于 2018-12-21 13:44
共享下ide
需要到官网申请,申请页面http://www.acoinfo.com/html/experience.php 谢谢,那我这两个方法都试试看 knight_hu 发表于 2018-12-21 13:49
谢谢,那我这两个方法都试试看
方便问一下是对于SylixOS是学习研究,还是做实际项目,哪个领域的项目? 本帖最后由 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); knight_hu 发表于 2018-12-21 15:05
我也是在学习,用的开源的工具,刚碰到个问题,任务里面用中断方式去做的话,运行几秒钟就会出现jobQueueAd ...
应该是中断太频繁了,你调低频率试试 好的,我如果直接用API_InterVectorConnect把中断任务插入到中断列表去处理的话,会报下面这个错误{:cry:}
_Schedule() bug: scheduler candidate serious error, ptcb 0x8083bec0, name "t_netproto", status 0x5
页:
[1]