通用 GPIO 设备应用笔记

通用GPIO设备应用笔记

摘要

本应用笔记描述了如何使用RT-Thread的通用GPIO设备驱动,包括驱动的配置,相关API的应用。并给出了在正点原子STM32F4探索者开发板上验证的代码示例。

1本文的目的和结构

1.1本文的目的和背景

为了给用户提供操作GPIO的通用API,方便应用程序开发,RT-Thread中引入了通用GPIO设备驱动。并提供类似Arduino风格的API用于操作GPIO,如设置GPIO模式和输出电平,读取GPIO输入电平,配置GPIO外部中断。本文说明了如何使用RT-Thread的通用GPIO设备驱动。

1.2本文的结构

本文首先描述了RT-Thread通用GPIO设备驱动的基本情况,接下来给出了在正点原子STM32F4探索者开发板上验证的代码示例,最后详细描述了通用GPIO设备驱动API的参数取值和注意事项。

2问题阐述

RT-Thread提供了一套简单的I / O设备管理框架,它把I / O设备分成了三层进行处理:应用层,I / O设备管理层,硬件驱动层。应用程序通过RT-Thread的设备操作接口获得正确的设备驱动,然后通过这个设备驱动与底层I / O硬件设备进行数据(或控制)交互.RT-Thread提供给上层应用的是一个抽象的设备操作接口,给下层设备提供的是底层驱动框架。对于通用GPIO设备,应用程序既可以通过设备操作接口访问,又可以直接通过通用GPIO设备驱动来访问。一般来说,我们都是使用第二种方式,那么如何在RT-Thread中使用通用GPIO设备驱动从而操作GPIO呢?

通用 GPIO 设备应用笔记

3问题的解决

本文基于正点原子STM32F4探索者开发板,给出了通用GPIO设备的具体应用示例代码,包含管脚输入,输出和外部中断的使用方法。由于RT-Thread上层应用API的通用性,因此这些代码不局限于具体的硬件平台,用户可以轻松将它移植到其它平台上。正点原子STM32F4探索者开发板使用的MCU是STM32F407ZGT6,板载2颗LED和4个独立按键.LED分别连接到MCU的GPIOF9, GPIOF10,KEY0按键连接到GPIOE4,KEY1按键连接到GPIOE3,KEY2按键连接到GPIOE2,WK_UP按键连接到GPIOA0,2颗LED均为低电平点亮,独立按键KEY0,KEY1,KEY2按下为低电平; WK_UP按下为高电平。

通用 GPIO 设备应用笔记

3.1准备和配置工程

  1. 下载  RT-Thread源码

  2. 下载  GPIO设备驱动示例代码

  3. 进入  rt-thread\bsp\stm32f4xx-HAL 目录,在env命令行中输入menuconfig,进入配置界面,使用menuconfig工具(学习如何使用)配置工程。

(1)在menuconfig配置界面依次选择RT-Thread组件--->设备驱动程序--->使用通用GPIO设备驱动程序,如图所示:

通用 GPIO 设备应用笔记

(2)输入命令  scons --target=mdk5 -s 生成mdk5工程。将示例代码附带的main.c替换掉bsp中的main.c,如图所示:

通用 GPIO 设备应用笔记

(3)编译,下载程序,在终端输入list_device命令可以看到device是pin,类型是Miscellaneous Device就说明通用GPIO设备驱动添加成功了。

通用 GPIO 设备应用笔记

下面是3个通用GPIO设备驱动API应用示例,分别是:GPIO输出,GPIO输入,GPIO外部中断,这些代码在正点原子STM32F4探索者开发板上验证通过。

3.2 GPIO输出配置

示例1:配置GPIO为输出,点亮LED。根据原理图,GPIOF9连接到了板载红色LED,丝印为DS0; GPIOF10连接到了板载绿色LED,丝印为DS1.GPIOF9输出低电平则点亮DS0, GPIOF9输出高电平则DS0不亮; GPIOF10输出低电平则点亮DS1,GPIOF10输出高电平则DS1不亮。

通用 GPIO 设备应用笔记

 

#define LED0 21 //PF9--21,在 drv_gpio.c 文件 pin_index pins[] 中查到 PF9 编号为 21
#define LED1 22 //PF10--22,在 drv_gpio.c 文件 pin_index pins[] 中查到 PF10 编号为 22
 void led_thread_entry(void* parameter)
{
    // 设置管脚为输出模式
    rt_pin_mode(LED0, PIN_MODE_OUTPUT);
    // 设置管脚为输出模式
    rt_pin_mode(LED1, PIN_MODE_OUTPUT);
    while (1)
    {
        // 输出低电平,LED0 亮
        rt_pin_write(LED0, PIN_LOW);
        // 输出低电平,LED1 亮
        rt_pin_write(LED1, PIN_LOW);
        // 挂起 500ms
        rt_thread_delay(rt_tick_from_millisecond(500));

        // 输出高电平,LED0 灭
        rt_pin_write(LED0, PIN_HIGH);
        // 输出高电平,LED1 灭
        rt_pin_write(LED1, PIN_HIGH);
        // 挂起 500ms
        rt_thread_delay(rt_tick_from_millisecond(500));
    }
}

在线程入口函数led_thread_entry里首先调用rt_pin_mode设置管脚模式为输出模式,然后就进入while(1)循环,间隔500ms调用rt_pin_write来改变GPIO输出电平。下面是创建线程的代码:

 

    rt_thread_t tid;// 线程句柄
    /* 创建 led 线程 */
    tid = rt_thread_create("led",
                    led_thread_entry,
                    RT_NULL,
                    1024,
                    3,
                    10);
    /* 创建成功则启动线程 */
    if (tid != RT_NULL)
        rt_thread_startup(tid);

编译,下载程序,我们将看到LED间隔500ms闪烁的现象。

3.3 GPIO输入配置

示例2:配置GPIOE3,GPIOE2为上拉输入,GPIOA0为下拉输入,检测按键信号。根据原理图,GPIOE3连接到按键KEY1,按键被按下时GPIOE3应读取到低电平,按键没有被按下时GPIOE3应读取到高电平; GPIOE2连接到按键KEY2,按键被按下时GPIOE2应读取到低电平,按键没有被按下时GPIOE2应读取到高电平; GPIOA0连接到按键WK_UP ,按键被按下时GPIOA0应读取到高电平,按键没有被按下时GPIOA0应读取到低电平。

通用 GPIO 设备应用笔记

 

#define KEY1    2   //PE3--2,在 drv_gpio.c 文件 pin_index pins[] 中查到 PE3 编号为 2
#define KEY2    1   //PE2--1,在 drv_gpio.c 文件 pin_index pins[] 中查到 PE2 编号为 1
#define WK_UP   34  //PA0--34,在 drv_gpio.c 文件 pin_index pins[] 中查到 PA0 编号为 34
void key_thread_entry(void* parameter)
{
    //PE2、PE3 设置上拉输入
    rt_pin_mode(KEY1, PIN_MODE_INPUT_PULLUP);
    rt_pin_mode(KEY2, PIN_MODE_INPUT_PULLUP);
    //PA0 设置为下拉输入
    rt_pin_mode(WK_UP, PIN_MODE_INPUT_PULLDOWN);

    while (1)
    {
        // 检测到低电平,即按键 1 按下了
        if (rt_pin_read(KEY1) == PIN_LOW)
        {
            rt_kprintf("key1 pressed!\n");
        }
        // 检测到低电平,即按键 2 按下了
        if (rt_pin_read(KEY2) == PIN_LOW)
        {
            rt_kprintf("key2 pressed!\n");
        }
        // 检测到高电平,即按键 wp 按下了
        if (rt_pin_read(WK_UP) == PIN_HIGH)
        {
            rt_kprintf("WK_UP pressed!\n");
        }
        // 挂起 10ms
        rt_thread_delay(rt_tick_from_millisecond(10));
    }
}

在线程入口函数key_thread_entry里首先调用rt_pin_mode设置管脚GPIOE3为上拉输入模式。这样当用户按下按键KEY1时,GPIOE3读取到的电平是低电平;按键未被按下时,GPIOE3读取到了电平是高电平。然后进入while(1)循环,调用rt_pin_read读取管脚GPIOE3电平,如果读取到低电平则表示按键KEY1被按下,就在终端打印字符串“key1按下!“。每隔10ms检测一次按键输入情况。下面是创建线程的代码:

 

   rt_thread_t tid;
  /* 创建 key 线程 */
    tid = rt_thread_create("key",
                    key_thread_entry,
                    RT_NULL,
                    1024,
                    2,
                    10);
    /* 创建成功则启动线程 */
    if (tid != RT_NULL)
        rt_thread_startup(tid);

编译,下载程序,我们按下开发板上的用户按键,终端将打印提示字符。

3.4 GPIO中断配置

示例3:配置GPIO为外部中断模式,下降沿触发,检测按键信号。根据原理图,GPIOE4连接到按键KEY0,按键被按下时MCU应探测到电平下降沿。

 

#define KEY0    3   //PE4--3,在 gpio.c 文件 pin_index pins[] 中查到 PE4 编号为 3
void hdr_callback(void *args)// 回调函数
{
    char *a = args;// 获取参数
    rt_kprintf("key0 down! %s\n",a);
}

void irq_thread_entry(void* parameter)
{
    // 上拉输入
    rt_pin_mode(KEY0, PIN_MODE_INPUT_PULLUP);
    // 绑定中断,下降沿模式,回调函数名为 hdr_callback
    rt_pin_attach_irq(KEY0, PIN_IRQ_MODE_FALLING, hdr_callback, (void*)"callback
args");
    // 使能中断
    rt_pin_irq_enable(KEY0, PIN_IRQ_ENABLE);

}

在线程入口函数irq_thread_entry里首先调用rt_pin_attach_irq设置管脚GPIOE4为下降沿中断模式,并绑定了中断回调函数,还传入了字符串“callback args”。然后调用rt_pin_irq_enable使能中断,这样按键KEY0被按下时MCU会检测到电平下降沿,触发外部中断,在中断服务程序中会调用回调函数hdr_callback,在回调函数中打印传入的参数和提示信息。下面是创建线程的代码:

 

    rt_thread_t tid;// 线程句柄
    /* 创建 irq 线程 */
    tid = rt_thread_create("exirq",
                    irq_thread_entry,
                    RT_NULL,
                    1024,
                    4,
                    10);
    /* 创建成功则启动线程 */
    if (tid != RT_NULL)
        rt_thread_startup(tid);

编译,下载程序,我们按下键KEY0,终端将打印提示字符。

3.5 I / O设备管理框架和通用GPIO设备的联系

RT-Thread自动初始化功能依次调用rt_hw_pin_init ===> rt_device_pin_register ===> rt_device_register完成了GPIO硬件初始化.rt_device_register注册设备类型为RT_Device_Class_Miscellaneous,即杂类设备,从而我们就可以使用统一的API操作GPIO。

通用 GPIO 设备应用笔记

更多关于I / O设备管理框架的说明和串口驱动实现细节,请参考“RT-Thread编程手册” 第6章I / O设备管理

在线查看地址:链接

4参考

4.1本文所有相关的API

用户应用代码要使用RT-Thread GPIO驱动接口需在menuconfig中开启GPIO驱动,引用头文件  rtdevice.h

通用 GPIO 设备应用笔记

4.1.1 API列表

API 头文件
rt_pin_mode rt-thread\components\drivers\include\drivers\pin.h
rt_pin_write rt-thread\components\drivers\include\drivers\pin.h
rt_pin_read rt-thread\components\drivers\include\drivers\pin.h
rt_pin_attach_irq rt-thread\components\drivers\include\drivers\pin.h
rt_pin_detach_irq rt-thread\components\drivers\include\drivers\pin.h
rt_pin_irq_enable rt-thread\components\drivers\include\drivers\pin.h

4.1.2核心API详解

4.1.2.1 RT_PIN_MODE()

函数原型

 

    void rt_pin_mode(rt_base_t pin, rt_base_t mode)

函数参数

参数 描述
管脚编号
模式 模式

报道查看函数  无

此函数可设置管脚模式。

管脚编号,由驱动定义,在  drv_gpio.c 的pin_index pins []中可以找到,以本文使用的STM32F407ZGT6为例,该芯片管脚数为100,在pin_index pins []中可以找到如下代码:

通用 GPIO 设备应用笔记

其中STM32_PIN(1,E,2)表示GPIOE2的编号为1,STM32_PIN(9,C,15)表示GPIOC15的编号为9,以此类推.STM32_PIN()的第一个参数为管脚编号,第二个参数为端口,第三个参数为管脚号。

模式可取如下5种之一:

PIN_MODE_OUTPUT输出,具体模式看drv_gpio.c源码实现,本文使用的是推挽输出PIN_MODE_INPUT输入PIN_MODE_INPUT_PULLUP上拉输入PIN_MODE_INPUT_PULLDOWN下拉输入PIN_MODE_OUTPUT_OD开漏输出

4.1.2.2 RT_PIN_WRITE()

函数原型

 

void rt_pin_write(rt_base_t pin, rt_base_t value)

函数参数

参数 描述
管脚编号
电平逻辑值,可取2种值之一,PIN_LOW低电平,PIN_HIGH高电平

报道查看函数  无

此函数可设置管脚输出电平。

4.1.2.3 RT_PIN_READ()

函数原型

 

int  rt_pin_read(rt_base_t pin)

函数参数

参数 描述
管脚编号

函数返回

返回值 描述
PIN_LOW 低电平
PIN_HIGH 高电平

此函数可读取输入管脚电平。

4.1.2.4 RT_PIN_ATTACH_IRQ()

函数原型

 

    rt_err_t rt_pin_attach_irq( rt_int32_t pin, rt_uint32_t mode,
                                  void (*hdr)(void *args), void  *args)

函数参数

参数 描述
管脚编号
模式 中断触发模式
HDR 中断回调函数,用户需要自行定义这个函数,其返回值为void
ARGS 中断回调函数的参数,不需要时设置为RT_NULL

函数返回

返回值 描述
RT_EOK 成功
RT_ENOSYS 无系统
RT_EBUSY

中断触发模式可取以下3种值之一:

PIN_IRQ_MODE_RISING上升沿触发PIN_IRQ_MODE_FALLING下降沿触发PIN_IRQ_MODE_RISING_FALLING边沿触发(上升沿和下降沿都触发)

此函数可绑定中断回调函数。

绑定中断回调函数传递字符串示例:

 

rt_pin_attach_irq(3, PIN_IRQ_MODE_FALLING,hdr_callback, (void*)"callback args");
void hdr_callback(void *args)
{
    char *a = args;

    rt_kprintf("%s",a);

}

输出为“callback args”。

传递数值示例:

 

rt_pin_attach_irq(3, PIN_IRQ_MODE_FALLING,hdr_callback, (void*)6);
void hdr_callback(void *args)
{
    Int a = (int)args;

    rt_kprintf("%d",a);
}

输出为6。

4.1.2.5 RT_PIN_DETACH_IRQ()

函数原型

 

rt_err_t rt_pin_detach_irq(rt_int32_t pin)

函数参数

参数 描述
管脚编号

函数返回

返回值 描述
RT_EOK 成功
RT_ENOSYS 出错

此函数可使管脚中断回调函数脱离。

4.1.2.6 RT_PIN_IRQ_ENABLE()

函数原型

 

rt_err_t rt_pin_irq_enable(rt_base_t pin, rt_uint32_t enabled)

函数参数

参数 描述
管脚编号
启用 状态,可取2种值之一:PIN_IRQ_ENABLE开启,PIN_IRQ_DISABLE关闭

函数返回

返回值 描述
RT_EOK 成功
RT_ENOSYS 出错