GPIO 驱动
本章将介绍 MS-RTOS GPIO 驱动开发及测试。
GPIO 是通用输入/输出端口(General Purpose I/O Ports)的英文缩写,通俗地说,就是一些引脚,可以通过它们输出高低电平或者通过它们读入引脚的状态:是高电平或是低电平。最基本的输出功能是由引脚输出高、低电平,实现开关控制,如把 GPIO 引脚接入 LED 灯,那就可以控制 LED 灯的亮灭,引脚接入到继电器或三极管,那就可以通过继电器或三极管控制外部大功率电路的通断。 最基本的输入功能是检测外部电平,如把 GPIO 引脚连接到按键,通过电平高低区分按键是否被按下。
1. GPIO 基础知识
1.1 硬件结构
通过 GPIO 硬件结构框图,就可以从整体上深入了解 GPIO 外设及它的各种应用模式,该图从最右端看起,最右端就是代表 STM32 芯片引出的 GPIO 引脚,其它部件都位于芯片内部。
推挽输出模式一般应用在输出电平 0 和 3.3 伏而且需要高速切换开关状态的场合。在 STM32 的应用中,除了必须用开漏模式的场合,我们都习惯使用推挽输出模式。开漏输出一般应用在 I2C、SMBUS 通讯等需要“线与”功能的总线电路中。除此之外,还用在电平不匹配的场合,如需要输出 5 伏的高电平,就可以在外部接一个上拉电阻,上拉电源为 5 伏,并且把 GPIO 设置为开漏模式,当输出高阻态时,由上拉电阻和电源向外输出 5 伏电平。可以输出高低电平,用于连接数字器件,高电平由 VDD 决定,低电平由 VSS 决定。推挽结构指两个三极管受两路互补的信号控制,总是在一个导通的时候另外一个截止,优点开关效率高,电流大,驱动能力强。输出高电平时,电流输出到负载,叫灌电流,可以理解成推,输出低电平时,负载电流流向芯片,叫拉电流,即挽。
1.2 工作模式
输入模式:
输入浮空(GPIO_Mode_IN_FLOATING):
通俗讲浮空就是浮在空中,就相当于此端口在默认情况下什么都不接,呈高阻态,这种设置在数据传输时用的比较多。浮空最大的特点就是电压的不确定性,它可能是 0V,也可能是 VCC,还可能是介于两者之间的某个值(最有可能) 浮空一般用来做 ADC 输入用,这样可以减少上下拉电阻对结果的影响。
输入上拉(GPIO_Mode_IPU):
上拉就是将不确定的信号通过一个电阻嵌位在高电平。电阻同时起到限流的作用。弱强只是上拉电阻的阻值不同,没有什么严格区分。
输入下拉(GPIO_Mode_IPD):
就是把电压拉低,拉到 GND。与上拉原理相似。
模拟输入(GPIO_Mode_AIN):
模拟输入是指传统方式的输入,数字输入是输入 PCM 数字信号,即 0、1 的二进制数字信号,通过数模转换,
转换成模拟信号,经前级放大进入功率放大器,功率放大器还是模拟的。
输出模式:
开漏输出(GPIO_Mode_Out_OD)
这里要注意 N-MOS 管,当设置输出的值为高电平的时候,N-MOS 管处于关闭状态,此时 I/O 端口的电平就不会由输出的高低电平决定,而是由 I/O 端口外部的上拉或者下拉决定;当设置输出的值为低电平的时候,N-MOS 管处于开启状态,此时 I/O 端口的电平就是低电平。同时,I/O 端口的电平也可以通过输入电路进行读取;注意,I/O 端口的电平不一定是输出的电平。
开漏复用功能(GPIO_Mode_AF_OD)
开漏复用输出模式,与开漏输出模式很是类似。只是输出的高低电平的来源,不是让 CPU 直接写输出数据寄存器,取而代之利用片上外设模块的复用功能输出来决定的。
推挽式输出(GPIO_Mode_Out_PP)
注意 P-MOS 管和 N-MOS 管,当设置输出的值为高电平的时候,P-MOS 管处于开启状态,N-MOS 管处于关闭状态,此时 I/O 端口的电平就由 P-MOS 管决定:高电平;当设置输出的值为低电平的时候,P-MOS 管处于关闭状态,N-MOS 管处于开启状态,此时 I/O 端口的电平就由 N-MOS 管决定:低电平。同时,I/O 端口的电平也可以通过输入电路进行读取;注意,此时 I/O 端口的电平一定是输出的电平。
推挽式复用功能(GPIO_Mode_AF_PP)
推挽复用输出模式,与推挽输出模式很是类似。只是输出的高低电平的来源,不是让 CPU 直接写输出数据寄存器,取而代之利用片上外设模块的复用功能输出来决定的。
2. GPIO 驱动框架
2.1 驱动相关数据结构
(1)ms_io_device_t
/*
* ms_io_device_t
*/
struct ms_io_device {
ms_io_name_node_t nnode;
ms_io_driver_t *drv;
ms_ptr_t ctx;
ms_atomic_t ref;
};
任何 IO 设备对象都应该采用包含的方式来继承 ms_io_device
对象。在实现 UART 驱动时,可以自定义一个设备结构体 uart_dev_t
,该结构体中包含一个 ms_io_device
类型的成员,还包含一个 privinfo_t
类型的成员;privinfo_t
由驱动开发人员定义,一般用于记录一些设备私有的状态信息和控制信息。
/*
* Private Info
*/
typedef struct {
ms_pollfd_t *slots[1];
ms_addr_t base;
ms_gpio_param_t param;
gpio_port_t gpio_port;
gpio_pin_t gpio_pin;
} privinfo_t;
/*
* GPIO Device
*/
typedef struct {
privinfo_t priv;
ms_io_device_t dev;
} gpio_dev_t;
(2)ms_io_driver_t
/*
* ms_io_driver_ops_t
*/
typedef struct {
ms_io_drv_type_t type;
int (*open) (ms_ptr_t ctx, ms_io_file_t *file, int oflag, ms_mode_t mode);
int (*close) (ms_ptr_t ctx, ms_io_file_t *file);
ms_ssize_t (*read) (ms_ptr_t ctx, ms_io_file_t *file,
ms_ptr_t buf, ms_size_t len);
ms_ssize_t (*write) (ms_ptr_t ctx, ms_io_file_t *file,
ms_const_ptr_t buf, ms_size_t len);
int (*ioctl) (ms_ptr_t ctx, ms_io_file_t *file, int cmd, ms_ptr_t arg);
} const ms_io_driver_ops_t;
/*
* ms_io_driver_t
*/
struct ms_io_driver {
ms_io_name_node_t nnode;
ms_io_driver_ops_t *ops;
};
任何 IO 设备驱动对象都应该采用包含的方式来继承 ms_io_driver
对象。ms_io_driver
结构体包含一个名字节点 ms_io_name_node_t
和一个设备文件操作接口集 ms_io_driver_ops_t
指针;名字节点用于匹配设备,文件操作接口用于支持 MS-RTOS IO 系统对设备的访问和控制。
/*
* Device operating function set
*/
static const ms_io_driver_ops_t gpio_drv_ops = {
.type = MS_IO_DRV_TYPE_CHR,
.open = __gpio_open,
.close = __gpio_close,
.read = __gpio_read,
.write = __gpio_write,
.ioctl = __gpio_ioctl,
.poll = __gpio_poll,
};
/*
* Device driver
*/
static ms_io_driver_t ck807_gpio_drv = {
.nnode = {
.name = "stm32f4_gpio",
},
.ops = &gpio_drv_ops,
};
2.2 驱动的注册和卸载
(1)GPIO 驱动开发流程:
获取必要的软硬件开发资源,了解设备的基本特性;
参照手册的相关流程和代码规范,编写寄存器相关宏定义,封装通用硬件操作接口;
申请必要的系统资源,根据默认参数初始化硬件的工作模式,实现中断处理函数;
实现
ms_io_driver_ops_t
中的必要操作接口,并向 MS-RTOS IO 系统注册驱动和设备节点;检查代码质量和代码风格,编写测试程序;
(2)驱动的注册和卸载接口:
ms_err_t ms_io_driver_register(ms_io_driver_t *drv);
ms_err_t ms_io_driver_unregister(ms_io_driver_t *drv); //此接口暂不开放
(3)设备节点的注册和卸载接口:
ms_err_t ms_io_device_register(ms_io_device_t *dev, const char *dev_path,
const char *drv_name, ms_ptr_t ctx);
ms_err_t ms_io_device_unregister(ms_io_device_t *dev);
2.3 GPIO ioctl 命令
以下仅列出几个最基本的命令,可以在 sdk/src/driver/ms_drv_gpio.h
文件中找到所有命令的定义。
命令 | 描述 | 参数 |
---|---|---|
MS_GPIO_CMD_SET_VAL | 设置 GPIO 引脚的输出值 | ms_uint8_t 指针 |
MS_GPIO_CMD_GET_VAL | 读取 GPIO 引脚的输入值 | ms_uint8_t 指针 |
MS_GPIO_CMD_SET_PARAM | 设置 GPIO 引脚的工作模式 | ms_gpio_param_t 指针 |
MS_GPIO_CMD_GET_PARAM | 获取 GPIO 引脚的工作模式 | ms_gpio_param_t 指针 |
(1)ms_gpio_param_t
typedef struct {
ms_uint8_t mode; // GPIO 引脚工作模式(输入输出模式、中断触发模式)
ms_uint8_t pull; // GPIO 引脚上下拉设置
ms_uint8_t speed; // GPIO 引脚工作在哪个范围的频率下
} ms_gpio_param_t;
- GPIO 引脚工作模式(mode)
可选配置 | 描述 |
---|---|
MS_GPIO_MODE_INPUT | Input Mode |
MS_GPIO_MODE_OUTPUT_PP | Output Push Pull Mode |
MS_GPIO_MODE_OUTPUT_OD | Output Open Drain Mode |
MS_GPIO_MODE_IRQ_RISING | External Interrupt Mode with Rising edge trigger detection |
MS_GPIO_MODE_IRQ_FALLING | External Interrupt Mode with Falling edge trigger detection |
MS_GPIO_MODE_IRQ_BOTH | External Interrupt Mode with Rising/Falling edge trigger detection |
MS_GPIO_MODE_IRQ_HIGH | External Interrupt Mode with High level detection |
MS_GPIO_MODE_IRQ_LOW | External Interrupt Mode with Low level detection |
- GPIO 引脚工作模式(pull)
可选配置 | 描述 |
---|---|
MS_GPIO_PULL_NONE | No Pull-up or Pull-down activation |
MS_GPIO_PULL_UP | Pull-up activation |
MS_GPIO_PULL_DOWN | Pull-down activation |
- GPIO 引脚工作模式(speed)
可选配置 | 描述 |
---|---|
MS_GPIO_SPEED_LOW | IO works at 2 MHz |
MS_GPIO_SPEED_MEDIUM | Range 12.5 MHz to 50 MHz |
MS_GPIO_SPEED_HIGH | Range 25 MHz to 100 MHz |
MS_GPIO_SPEED_VERY_HIGH | Range 50 MHz to 200 MHz |
2.4 GPIO 读写
应用程序在打开设备文件后,需要调用 ioctl
来获取 GPIO 的默认工作模式或设置 GPIO 的工作模式。当应用程序读取相应 GPIO 引脚的设备文件时,需要将至少一个 Byte 大小的缓冲传递给底层的驱动,驱动程序将判断当前操作的 GPIO 是否处于输入模式;如果处于输入模式则读取 GPIO 引脚的电平,并返回 GPIO 状态给应用程序 —— 0:代表低电平,1:代表高电平;如果不处于输入模式则,read
调用将返回 0 表示读取失败。
/*
* Read device
*/
static ms_ssize_t __gpio_read(ms_ptr_t ctx, ms_io_file_t *file, ms_ptr_t buf, ms_size_t len)
{
privinfo_t *priv = ctx;
ms_uint8_t *value = (ms_uint8_t *)buf;
if (__gpio_get_dir(priv) != GPIO_DIR_INPUT) {
ms_thread_set_errno(EINVAL);
return 0;
}
if (__gpio_get_val(priv) == GPIO_VALUE_LOW) {
*value = 0;
} else {
*value = 1;
}
return 1;
}
应用程序在写入相应 GPIO 引脚的设备文件时,驱动程序将判断当前操作的 GPIO 是否处于输出模式,如果处于输出模式则将根据写入的值来设置 GPIO 引脚的电平,0:代表低电平,1:代表高电平。
/*
* Write device
*/
static ms_ssize_t __gpio_write(ms_ptr_t ctx, ms_io_file_t *file, ms_const_ptr_t buf, ms_size_t len)
{
privinfo_t *priv = ctx;
ms_uint8_t *value = (ms_uint8_t *)buf;
if (__gpio_get_dir(priv) != GPIO_DIR_OUTPUT) {
ms_thread_set_errno(EINVAL);
return 0;
}
if (value[0]) {
__gpio_set_val(priv, GPIO_VALUE_HIGH);
} else {
__gpio_set_val(priv, GPIO_VALUE_LOW);
}
return 1;
}
2.5 GPIO select 支持
GPIO 设备支持输入、输出和异常事件的捕获。当监听输入输出事件时,即判断当前 GPIO 是否为输入或输出模式;当监听异常事件时,即判断当前 GPIO 是否有中断挂起。
/*
* Check device readable
*/
static ms_bool_t __gpio_readable_check(ms_ptr_t ctx)
{
privinfo_t *priv = ctx;
if (__gpio_get_dir(priv) == GPIO_DIR_INPUT) {
return MS_TRUE;
} else {
return MS_FALSE;
}
}
/*
* Check device writable
*/
static ms_bool_t __gpio_writable_check(ms_ptr_t ctx)
{
privinfo_t *priv = ctx;
if (__gpio_get_dir(priv) == GPIO_DIR_OUTPUT) {
return MS_TRUE;
} else {
return MS_FALSE;
}
}
/*
* Check device exception
*/
static ms_bool_t __gpio_except_check(ms_ptr_t ctx)
{
privinfo_t *priv = ctx;
return __gpio_is_irq_pending(priv);
}
/*
* Poll device
*/
static int __gpio_poll(ms_ptr_t ctx, ms_io_file_t *file,
ms_pollfd_t *fds, ms_bool_t setup)
{
privinfo_t *priv = ctx;
return ms_io_poll_helper(fds, priv->slots, MS_ARRAY_SIZE(priv->slots), setup, ctx,
__gpio_readable_check,
__gpio_writable_check,
__gpio_except_check);
}
2.6 GPIO 驱动示例
GPIO 驱动示例,仅作为参考。
#define __MS_IO
#include "config.h"
#include "ms_kern.h"
#include "ms_io_core.h"
/*
* Open device
*/
static int __gpio_open(ms_ptr_t ctx, ms_io_file_t *file, int oflag, ms_mode_t mode)
{
int ret;
if (ms_atomic_inc(MS_IO_DEV_REF(file)) == 1) {
ret = 0;
} else {
ms_atomic_dec(MS_IO_DEV_REF(file));
ms_thread_set_errno(EBUSY);
ret = -1;
}
return ret;
}
/*
* Close device
*/
static int __gpio_close(ms_ptr_t ctx, ms_io_file_t *file)
{
privinfo_t *priv = ctx;
ms_uint16_t pin = priv->pin;
GPIO_TypeDef *gpio_x = (GPIO_TypeDef *)priv->base;
if (ms_atomic_dec(MS_IO_DEV_REF(file)) == 0) {
HAL_GPIO_DeInit(gpio_x, pin);
}
return 0;
}
/*
* Read device
*/
static ms_ssize_t __gpio_read(ms_ptr_t ctx, ms_io_file_t *file, ms_ptr_t buf, ms_size_t len)
{
privinfo_t *priv = ctx;
ms_uint16_t pin = priv->pin;
ms_uint8_t *value = (ms_uint8_t *)buf;
GPIO_TypeDef *gpio_x = (GPIO_TypeDef *)priv->base;
GPIO_PinState state;
state = HAL_GPIO_ReadPin(gpio_x, pin);
*value = (state == GPIO_PIN_RESET) ? 0 : 1;
return sizeof(ms_uint8_t);
}
/*
* Write device
*/
static ms_ssize_t __gpio_write(ms_ptr_t ctx, ms_io_file_t *file, ms_const_ptr_t buf, ms_size_t len)
{
privinfo_t *priv = ctx;
ms_uint16_t pin = priv->pin;
GPIO_TypeDef *gpio_x = (GPIO_TypeDef *)priv->base;
GPIO_PinState state;
state = (*(ms_uint8_t *)buf) ? GPIO_PIN_SET : GPIO_PIN_RESET;
HAL_GPIO_WritePin(gpio_x, pin, state);
return sizeof(ms_uint8_t);
}
/*
* GPIO pin to pos [0...15]
*/
static ms_uint8_t __gpio_pin_to_pos(ms_uint16_t pin)
{
ms_uint8_t pos;
for (pos = 0; pos < GPIO_NUMBER; pos++) {
if (pin == (((ms_uint16_t)0x01) << pos)) {
break;
}
}
return pos;
}
/*
* Covert general flags to stm32 flags
*/
static ms_err_t __gpio_covert_to_hal_flag(const ms_gpio_param_t *param,
GPIO_InitTypeDef *init)
{
/*
* Covert mode
*/
switch (param->mode) {
case MS_GPIO_MODE_INPUT:
init->Mode = GPIO_MODE_INPUT;
break;
case MS_GPIO_MODE_OUTPUT_PP:
init->Mode = GPIO_MODE_OUTPUT_PP;
break;
case MS_GPIO_MODE_OUTPUT_OD:
init->Mode = GPIO_MODE_OUTPUT_OD;
break;
case MS_GPIO_MODE_IRQ_RISING:
init->Mode = GPIO_MODE_IT_RISING;
break;
case MS_GPIO_MODE_IRQ_FALLING:
init->Mode = GPIO_MODE_IT_FALLING;
break;
case MS_GPIO_MODE_IRQ_BOTH:
init->Mode = GPIO_MODE_IT_RISING_FALLING;
break;
default:
return MS_ERR;
}
/*
* Covert pull
*/
switch (param->pull) {
case MS_GPIO_PULL_NONE:
init->Pull = GPIO_NOPULL;
break;
case MS_GPIO_PULL_UP:
init->Pull = GPIO_PULLUP;
break;
case MS_GPIO_PULL_DOWN:
init->Pull = GPIO_PULLDOWN;
break;
default:
return MS_ERR;
}
/*
* Covert speed
*/
switch (param->speed) {
case MS_GPIO_SPEED_LOW:
init->Speed = GPIO_SPEED_FREQ_LOW;
break;
case MS_GPIO_SPEED_MEDIUM:
init->Speed = GPIO_SPEED_FREQ_MEDIUM;
break;
case MS_GPIO_SPEED_HIGH:
init->Speed = GPIO_SPEED_FREQ_HIGH;
break;
case MS_GPIO_SPEED_VERY_HIGH:
init->Speed = GPIO_SPEED_FREQ_VERY_HIGH;
break;
default:
return MS_ERR;
}
return MS_ERR_NONE;
}
/*
* Control device
*/
static int __gpio_ioctl(ms_ptr_t ctx, ms_io_file_t *file, int cmd, void *arg)
{
privinfo_t *priv = ctx;
ms_uint16_t pin = priv->pin;
GPIO_TypeDef *gpio_x = (GPIO_TypeDef *)priv->base;
GPIO_PinState state;
GPIO_InitTypeDef init;
ms_gpio_param_t *param;
int ret;
switch (cmd) {
case MS_GPIO_CMD_SET_VAL:
if (ms_access_ok(arg, sizeof(ms_uint8_t), MS_ACCESS_R)) {
state = (*(ms_uint8_t *)arg) ? GPIO_PIN_SET : GPIO_PIN_RESET;
HAL_GPIO_WritePin(gpio_x, pin, state);
ret = 0;
} else {
ms_thread_set_errno(EFAULT);
ret = -1;
}
break;
case MS_GPIO_CMD_GET_VAL:
if (ms_access_ok(arg, sizeof(ms_uint8_t), MS_ACCESS_W)) {
state = HAL_GPIO_ReadPin(gpio_x, pin);
*(ms_uint8_t *)arg = (state == GPIO_PIN_RESET) ? 0 : 1;
ret = 0;
} else {
ms_thread_set_errno(EFAULT);
ret = -1;
}
break;
case MS_GPIO_CMD_SET_PARAM:
if (ms_access_ok(arg, sizeof(ms_gpio_param_t), MS_ACCESS_R)) {
param = (ms_gpio_param_t *)arg;
init.Pin = pin;
if (__gpio_covert_to_hal_flag(param, &init) == MS_ERR_NONE) {
HAL_GPIO_Init(gpio_x, &init);
if (IS_GPIO_EXTI_MODE(init.Mode)) {
gpio_exti_line_priv[__gpio_pin_to_pos(pin)] = priv;
HAL_NVIC_SetPriority(gpio_exti_line_irqn[__gpio_pin_to_pos(pin)],
0x0F, 0x00);
HAL_NVIC_EnableIRQ(gpio_exti_line_irqn[__gpio_pin_to_pos(pin)]);
}
/*
* Save param in priv
*/
priv->mode = param->mode;
priv->pull = param->pull;
priv->speed = param->speed;
ret = 0;
} else {
ms_thread_set_errno(EINVAL);
ret = -1;
}
} else {
ms_thread_set_errno(EFAULT);
ret = -1;
}
break;
case MS_GPIO_CMD_GET_PARAM:
if (ms_access_ok(arg, sizeof(ms_gpio_param_t), MS_ACCESS_W)) {
param = (ms_gpio_param_t *)arg;
param->mode = priv->mode;
param->pull = priv->speed;
param->speed = priv->speed;
ret = 0;
} else {
ms_thread_set_errno(EFAULT);
ret = -1;
}
break;
default:
ms_thread_set_errno(EINVAL);
ret = -1;
break;
}
return ret;
}
/*
* Device notify
*/
static int __gpio_poll_notify(privinfo_t *priv, ms_pollevent_t event)
{
return ms_io_poll_notify_helper(priv->slots, MS_ARRAY_SIZE(priv->slots), event);
}
/*
* This function called by all external interrupt handles
*/
void HAL_GPIO_EXTI_Callback(ms_uint16_t GPIO_Pin)
{
privinfo_t *priv = gpio_exti_line_priv[__gpio_pin_to_pos(GPIO_Pin)];
__gpio_poll_notify(priv, POLLIN);
}
/*
* This function handles EXTI0 interrupt
*/
void EXTI0_IRQHandler(void)
{
(void)ms_int_enter();
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_0);
(void)ms_int_exit();
}
/*
* This function handles EXTI1 interrupt
*/
void EXTI1_IRQHandler(void)
{
(void)ms_int_enter();
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_1);
(void)ms_int_exit();
}
/*
* This function handles EXTI2 interrupt
*/
void EXTI2_IRQHandler(void)
{
(void)ms_int_enter();
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_2);
(void)ms_int_exit();
}
/*
* This function handles EXTI3 interrupt
*/
void EXTI3_IRQHandler(void)
{
(void)ms_int_enter();
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_3);
(void)ms_int_exit();
}
/*
* This function handles EXTI4 interrupt
*/
void EXTI4_IRQHandler(void)
{
(void)ms_int_enter();
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_4);
(void)ms_int_exit();
}
/*
* This function handles EXTI9-EXTI5 interrupts
*/
void EXTI9_5_IRQHandler(void)
{
(void)ms_int_enter();
if (__HAL_GPIO_EXTI_GET_IT(GPIO_PIN_9) != RESET) {
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_9);
}
if (__HAL_GPIO_EXTI_GET_IT(GPIO_PIN_8) != RESET) {
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_8);
}
if (__HAL_GPIO_EXTI_GET_IT(GPIO_PIN_7) != RESET) {
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_7);
}
if (__HAL_GPIO_EXTI_GET_IT(GPIO_PIN_6) != RESET) {
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_6);
}
if (__HAL_GPIO_EXTI_GET_IT(GPIO_PIN_5) != RESET) {
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_5);
}
(void)ms_int_exit();
}
/*
* This function handles EXTI15-EXTI10 interrupts
*/
void EXTI15_10_IRQHandler(void)
{
(void)ms_int_enter();
if (__HAL_GPIO_EXTI_GET_IT(GPIO_PIN_15) != RESET) {
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_15);
}
if (__HAL_GPIO_EXTI_GET_IT(GPIO_PIN_14) != RESET) {
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_14);
}
if (__HAL_GPIO_EXTI_GET_IT(GPIO_PIN_13) != RESET) {
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_13);
}
if (__HAL_GPIO_EXTI_GET_IT(GPIO_PIN_12) != RESET) {
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_12);
}
if (__HAL_GPIO_EXTI_GET_IT(GPIO_PIN_11) != RESET) {
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_11);
}
if (__HAL_GPIO_EXTI_GET_IT(GPIO_PIN_10) != RESET) {
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_10);
}
(void)ms_int_exit();
}
/*
* Check device readable
*/
static ms_bool_t __gpio_readable_check(ms_ptr_t ctx)
{
privinfo_t *priv = ctx;
ms_bool_t ret;
/*
* Check EXTI line pending bit
*/
if (__HAL_GPIO_EXTI_GET_IT(priv->pin) != RESET) {
ret = MS_TRUE;
} else {
ret = MS_FALSE;
}
return ret;
}
/*
* Poll device
*/
static int __gpio_poll(ms_ptr_t ctx, ms_io_file_t *file,
ms_pollfd_t *fds, ms_bool_t setup)
{
privinfo_t *priv = ctx;
return ms_io_poll_helper(fds, priv->slots, MS_ARRAY_SIZE(priv->slots), setup, ctx,
__gpio_readable_check, MS_NULL, MS_NULL);;
}
/*
* Device operating function set
*/
static const ms_io_driver_ops_t gpio_drv_ops = {
.type = MS_IO_DRV_TYPE_CHR,
.open = __gpio_open,
.close = __gpio_close,
.read = __gpio_read,
.write = __gpio_write,
.ioctl = __gpio_ioctl,
.poll = __gpio_poll,
};
/*
* Device driver
*/
static ms_io_driver_t gpio_drv = {
.nnode = {
.name = "stm32_gpio",
},
.ops = &gpio_drv_ops,
};
/*
* Register gpio device driver
*/
ms_err_t stm32_gpio_drv_register(void)
{
return ms_io_driver_register(&gpio_drv);
}
/*
* Create gpio device file
*/
ms_err_t stm32_gpio_dev_create(const char *path, ms_addr_t base, ms_uint8_t pin)
{
gpio_dev_t *dev;
ms_err_t err;
if (IS_GPIO_ALL_INSTANCE((void *)base) && IS_GPIO_ALL_PIN(pin)) {
dev = ms_kmalloc(sizeof(gpio_dev_t));
if (dev != MS_NULL) {
bzero(&dev->priv, sizeof(dev->priv));
dev->priv.base = base;
dev->priv.pin = pin;
err = ms_io_device_register(&dev->dev, path, "stm32_gpio", &dev->priv);
} else {
ms_thread_set_errno(ENOMEM);
err = MS_ERR;
}
} else {
ms_thread_set_errno(EINVAL);
err = MS_ERR;
}
return err;
}
3. GPIO 应用程序
3.1 通过按键来控制 LED 灯
打开 GPIO 设备文件,当按下按键时,对应的 LED 点亮:
#include <ms_rtos.h>
#include <string.h>
#include <driver/ms_drv_gpio.h>
#include "test/include/greatest.h"
#define GPIO_A00_DEV_FILE "/dev/gpio_a0"
#define LED0_GPIO_DEV_FILE "/dev/gpio_b1"
#define LED1_GPIO_DEV_FILE "/dev/gpio_b0"
#define KEY0_GPIO_DEV_FILE "/dev/gpio_h3"
#define KEY1_GPIO_DEV_FILE "/dev/gpio_h2"
#define KEY2_GPIO_DEV_FILE "/dev/gpio_c13"
#define GPIO_TEST_COUNT (100)
int main (int argc, char **argv)
{
int fd_ds0, fd_ds1;
int fd_key0, fd_key1, fd_key2;
ms_gpio_param_t param;
ms_uint8_t value;
ms_uint32_t test_count = GPIO_TEST_COUNT;
fd_ds0 = ms_io_open(LED0_GPIO_DEV_FILE, O_WRONLY, 0666);
fd_ds1 = ms_io_open(LED1_GPIO_DEV_FILE, O_WRONLY, 0666);
fd_key0 = ms_io_open(KEY0_GPIO_DEV_FILE, O_RDONLY, 0666);
fd_key1 = ms_io_open(KEY1_GPIO_DEV_FILE, O_RDONLY, 0666);
fd_key2 = ms_io_open(KEY2_GPIO_DEV_FILE, O_RDONLY, 0666);
if (fd_ds1 < 0 || fd_ds0 < 0 || fd_key1 < 0 || fd_key0 < 0 || fd_key2 < 0) {
ms_printf("[error]: fd_ds0=%d, fd_ds1=%d, fd_key0=%d, fd_key1=%d, fd_key2=%d\n",
fd_ds0, fd_ds1, fd_key0, fd_key1, fd_key2);
return (-1);
}
param.mode = MS_GPIO_MODE_OUTPUT_PP;
param.pull = MS_GPIO_PULL_UP;
param.speed = MS_GPIO_SPEED_HIGH;
ms_io_ioctl(fd_ds1, MS_GPIO_CMD_SET_PARAM, ¶m);
ms_io_ioctl(fd_ds0, MS_GPIO_CMD_SET_PARAM, ¶m);
param.mode = MS_GPIO_MODE_INPUT;
param.pull = MS_GPIO_PULL_UP;
param.speed = MS_GPIO_SPEED_HIGH;
ms_io_ioctl(fd_key1, MS_GPIO_CMD_SET_PARAM, ¶m);
ms_io_ioctl(fd_key0, MS_GPIO_CMD_SET_PARAM, ¶m);
while (test_count--) {
/*
* If press down KEY1, then light on LED1
*/
ms_io_read(fd_key1, &value, sizeof(value));
ms_io_write(fd_ds1, &value, sizeof(value));
/*
* If press down KEY0, then light on LED0
*/
ms_io_read(fd_key0, &value, sizeof(value));
ms_io_write(fd_ds0, &value, sizeof(value));
ms_thread_sleep_ms(200);
}
ms_io_close(fd_ds0);
ms_io_close(fd_ds1);
ms_io_close(fd_key0);
ms_io_close(fd_key1);
ms_io_close(fd_key2);
return (0);
}
3.2 使用 select 监听 GPIO 中断
打开 GPIO 设备文件,监听中断事件:
#include <ms_rtos.h>
#include <string.h>
#include <driver/ms_drv_gpio.h>
#include "test/include/greatest.h"
#define GPIO_A00_DEV_FILE "/dev/gpio_a0"
#define LED0_GPIO_DEV_FILE "/dev/gpio_b1"
#define LED1_GPIO_DEV_FILE "/dev/gpio_b0"
#define KEY0_GPIO_DEV_FILE "/dev/gpio_h3"
#define KEY1_GPIO_DEV_FILE "/dev/gpio_h2"
#define KEY2_GPIO_DEV_FILE "/dev/gpio_c13"
#define GPIO_TEST_COUNT (100)
int main (int argc, char **argv)
{
int ret;
int fd_key;
ms_gpio_param_t param;
ms_fd_set_t efds;
ms_timeval_t tv;
ms_uint32_t test_count = GPIO_TEST_COUNT;
fd_key = ms_io_open(GPIO_A00_DEV_FILE, O_RDONLY, 0666);
if (fd_key < 0) {
ms_printf("[error]: open device file %s failed!\n", GPIO_A00_DEV_FILE);
return (-1);
}
param.mode = MS_GPIO_MODE_IRQ_BOTH;
param.pull = MS_GPIO_PULL_NONE;
param.speed = MS_GPIO_SPEED_HIGH;
ms_io_ioctl(fd_key, MS_GPIO_CMD_SET_PARAM, ¶m);
while (test_count--) {
FD_ZERO(&efds);
FD_SET(fd_key, &efds);
tv.tv_sec = 2;
tv.tv_usec = 0;
ret = ms_io_select(fd_key + 1, NULL, NULL, &efds, &tv);
if (ret > 0 && FD_ISSET(fd_key, &efds)) {
ms_printf("[info]: recv external interrupt\n");
} else {
ms_printf("[info]: select gpio timeout\n");
}
}
ms_io_close(fd_key);
return (0);
}
附录(Appendix)
1. Reference
2. FAQ
(1)如何查看当前系统有哪些 GPIO 设备?
GPIO 设备驱动的注册和设备对象的创建是在 BSP 中完成的;MS-RTOS 中的所有设备文件都会存放在 /dev
目录下,用户可以在 shell 命令行中输入 ls /dev
命令来查看当前系统所支持的所有设备。