在应用层使用GPIO

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/ScilogyHunter/article/details/100108086

接口说明

GPIO(General Purpose Input/Output)即通用输入输出端口,以下简称 I/O 端口。I/O 端口可提供输入、输出或中断三类功能,是嵌入式领域最常见,最基础的硬件设备。

在介绍SylixOS中的GPIO驱动如何编写前,我们先来了解下应用层是如何调用GPIO的,要先会用之后,才能更好的去开发驱动以及进行测试。对于应用层来说,GPIO就是 /dev/gpiofd 目录下的某个设备文件,通过标准文件函数就能够操作。鉴于GPIO使用的一些固有特点,以及为了简化接口,SylixOS又进一步封装标准文件函数得到三个简洁的GPIO操作函数,文件关闭依旧使用close函数。

/*********************************************************************************************************
** 函数名称: gpiofd
** 功能描述: 打开 gpiofd 文件
** 输 入  : gpio           gpio 号
**           flags          打开标志 GFD_CLOEXEC / GFD_NONBLOCK
**           gpio_flags     gpio 属性标志
** 输 出  : gpiofd 文件描述符
*********************************************************************************************************/
int gpiofd(unsigned int gpio, int flags, int gpio_flags);
/*********************************************************************************************************
** 函数名称: gpiofd_read
** 功能描述: 读取 gpiofd 文件
** 输 入  : fd        文件描述符
**           value     读取缓冲
** 输 出  : 0 : 成功  -1 : 失败
*********************************************************************************************************/
int gpiofd_read(int fd, uint8_t *value);
/*********************************************************************************************************
** 函数名称: gpiofd_write
** 功能描述: 写 gpiofd 文件
** 输 入  : fd        文件描述符
**           value     写入数据
** 输 出  : 0 : 成功  -1 : 失败
*********************************************************************************************************/
int gpiofd_write(int fd, uint8_t  value);
  • 使用这三个操作函数需要先包含头文件 <sys/gpiofd.h>
  • 函数 gpiofd 用于打开一个GPIO设备文件,但参数 gpio 并不是文件名而是数字编号。为提高可读性GPIO数字编号可以通过宏定义来表示,另一篇博客 《SylixOS中GPIO序号宏定义》中有详细实现及说明。
  • 参数flagsopen 函数的第二个参数意义相似,即可以是 O_RDONLY,O_RDWR,O_RDWR 等,但内部其实都是按照 O_RDWR 参数来操作的;
  • 参数 gpio_flags 是与GPIO特性相关的标识,它可以是多个位标识的组合。参考下表:
位标识名称 解释
GPIO_FLAG_DIR_OUT 设置GPIO为输出功能
GPIO_FLAG_DIR_IN 设置GPIO为输入功能
GPIO_FLAG_IN 与GPIO_FLAG_DIR_IN相同
GPIO_FLAG_OUT_INIT_LOW 设置GPIO为输出功能,同时初始化输出低电平
GPIO_FLAG_OUT_INIT_HIGH 设置GPIO为输出功能,同时初始化输出高电平
GPIO_FLAG_OPEN_DRAIN 设置GPIO输出为漏极输出模式
GPIO_FLAG_OPEN_SOURCE 设置GPIO输出为源极输出模式
GPIO_FLAG_PULL_DEFAULT 使用默认上拉/下拉模式
GPIO_FLAG_PULL_UP 使用上拉电阻模式
GPIO_FLAG_PULL_DOWN 使用下拉电阻模式
GPIO_FLAG_PULL_DISABLE 禁止上拉/下拉模式
GPIO_FLAG_TRIG_FALL 设置GPIO为中断功能,并且下降沿触发中断
GPIO_FLAG_TRIG_RISE 设置GPIO为中断功能,并且上升沿触发中断
GPIO_FLAG_TRIG_LEVEL 设置GPIO为中断功能,并且电平触发中断

要想使用GPIO的中断功能,首先要在打开设备时设置其 gpio_flags 参数为响应的中断模式。其次因为 GPIO 设备文件读写都是非阻塞的,还要借助 select 函数进行阻塞及超时功能。当一个具有中断功能的GPIO产生中断时,内核会唤醒所有通过调用select等待该GPIO文件描述符可读状态的线程,通知线程中断产生。当select正确返回时,线程处理相应的事务,这类似于完成了一次中断服务。
注意当使用了 GPIO_FLAG_TRIG_LEVE 标识时,我们仅能用 GPIO_FLAG_TRIG_FALLGPIO_FLAG_TRIG_RISE 中的一个与其组合使用,分别表示低电平触发和高电平触发。当没有使用 GPIO_FLAG_TRIG_LEVE 时,我们可以将 GPIO_FLAG_TRIG_FALLGPIO_FLAG_TRIG_RISE 组合使用表示双边沿触发。

完整例程

/*********************************************************************************************************
**
**                                    中国软件开源组织
**
**                                   嵌入式实时操作系统
**
**                                SylixOS(TM)  LW : long wing
**
**                               Copyright All Rights Reserved
**
**--------------文件信息--------------------------------------------------------------------------------
**
** 文   件   名: gpioExample.c
**
** 创   建   人: Hou.JinYu (侯进宇)
**
** 文件创建日期: 2017 年 12 月 13 日
**
** 描        述: gpio 例程,针对NXP i.MX-RT1050 EVK开发版。
**               因为连接板载 LED01 的引脚同时也连接了 enet 的复位脚,硬件冲突故不能使用。
**               LED02 只是一个空闲的gpio(为J22.7),需要用户连接LED或万用表来检测电平变化。
*********************************************************************************************************/
#include <stdio.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                   */
/*********************************************************************************************************
** 函数名称: gpioExample1
** 功能描述: gpio 轮询读取例程
** 输    入: NONE
** 输    出: ERROR_CODE
*********************************************************************************************************/
static  int  gpioExample1 (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);
}
SHELL_CMD_REG("gpioExample1", gpioExample1);
/*********************************************************************************************************
** 函数名称: gpioExample2
** 功能描述: gpio 中断例程
** 输    入: NONE
** 输    出: ERROR_CODE
*********************************************************************************************************/
static  int  gpioExample2 (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);
}
SHELL_CMD_REG("gpioExample2", gpioExample2);
/*********************************************************************************************************
  END
*********************************************************************************************************/

实验步骤

1.输入如命令 gpioExample1,启动查询方式的gpio例程;
2.此时控制台每秒输出一次按键值,按下按键可看到按键值会变为0。LED引脚也会按照按键状态改变而改变。
在这里插入图片描述
3.复位系统,输入命令 gpioExample2,启动中断方式的gpio例程;
4.按下按键会触发下降沿中断,从而唤醒一次select函数。每按一次按键会打印出按动次数,同时LED变化一次。
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/ScilogyHunter/article/details/100108086