|
本帖最后由 科技猎人 于 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_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函数关闭打开的设备文件;
- INT gpioExampleStart1 (VOID)
- {
- INT keyfd;
- INT ledfd;
- uint8_t value;
- 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例程:
[root@sylixos:/]# example gpio
2.此时控制台每秒输出一次按键值,按下按键可看到按键值回变为0。LED则会按照按键状态改变而改变。
3.复位系统,输入命令,启动中断方式的gpio例程:
[root@sylixos:/]# example gpio -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
- ** 全局变量:
- ** 调用模块:
- *********************************************************************************************************/
- static INT gpioExampleStart1 (VOID)
- {
- INT keyfd;
- INT ledfd;
- uint8_t value;
- 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
- ** 全局变量:
- ** 调用模块:
- *********************************************************************************************************/
- static INT gpioExampleStart2 (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
- ** 输 出 : 无
- ** 全局变量:
- ** 调用模块:
- *********************************************************************************************************/
- static INT gpioExampleStart (INT iArgC, PCHAR ppcArgV[])
- {
- 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
- *********************************************************************************************************/
复制代码 |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?注册
x
阿莫论坛20周年了!感谢大家的支持与爱护!!
月入3000的是反美的。收入3万是亲美的。收入30万是移民美国的。收入300万是取得绿卡后回国,教唆那些3000来反美的!
|