目录
1、PIN设备说明
rtthread通过pin.c和pin.h两个文件进行pin设备的管理。通过pin.h中的结构体rt_device_pin进行pin设备的定义,pin设备继承自设备基类rt_device,rt_device继承自rt_object基类,继承关系如下
PIN设备通过结构体的定义实现了对rt_device设备基类的继承,结构体中的成员rt_pin_ops来实现pin设备的具体操作实现。
struct rt_device_pin
{
struct rt_device parent;
const struct rt_pin_ops *ops;
};
struct rt_pin_ops
{
void (*pin_mode)(struct rt_device *device, rt_base_t pin, rt_base_t mode);
void (*pin_write)(struct rt_device *device, rt_base_t pin, rt_base_t value);
int (*pin_read)(struct rt_device *device, rt_base_t pin);
rt_err_t (*pin_attach_irq)(struct rt_device *device, rt_int32_t pin,
rt_uint32_t mode, void (*hdr)(void *args), void *args);
rt_err_t (*pin_detach_irq)(struct rt_device *device, rt_int32_t pin);
rt_err_t (*pin_irq_enable)(struct rt_device *device, rt_base_t pin, rt_uint32_t enabled);
rt_base_t (*pin_get)(const char *name);
};
2、PIN设备的初始化及注册
启动阶段rtthread会根据是否进行了RT_USING_PIN定义,在hw_board_init函数中进行pin设备的初始化,在rt_hw-pin_init函数中首先进行了时钟的初始化,最终调用函数rt_device_pin_register来实现STM32的IO和pin设备的关联及设备的挂载。
/*
进行PIN设备的结构体定义
*/
static struct rt_device_pin _hw_pin;
int rt_device_pin_register(const char *name, const struct rt_pin_ops *ops, void *user_data)
{
/*
PIN设备的父设备,设备基类的类型进行定义,定义为RT_Device_Class_Miscellaneous,杂类
*/
_hw_pin.parent.type = RT_Device_Class_Miscellaneous;
/*
收发回调函数为空
*/
_hw_pin.parent.rx_indicate = RT_NULL;
_hw_pin.parent.tx_complete = RT_NULL;
/*
设备基类的初始化灯相关函数指针赋值。
*/
#ifdef RT_USING_DEVICE_OPS
_hw_pin.parent.ops = &pin_ops;
#else
_hw_pin.parent.init = RT_NULL;
_hw_pin.parent.open = RT_NULL;
_hw_pin.parent.close = RT_NULL;
_hw_pin.parent.read = _pin_read;
_hw_pin.parent.write = _pin_write;
_hw_pin.parent.control = _pin_control;
#endif
/*
PIN设备ops,即STM32的具体实现方式进行赋值。
*/
_hw_pin.ops = ops;
_hw_pin.parent.user_data = user_data;
/*
设备注册
*/
/* register a character device */
rt_device_register(&_hw_pin.parent, name, RT_DEVICE_FLAG_RDWR);
return 0;
}
rt_device_pin_register("pin", &_stm32_pin_ops, RT_NULL)中参数_stm32_pin_ops为rt_pin_ops,具体定义内容如下,实现了GPIO的模式配置,输入输出控制,中断控制和管脚查找等功能在STM32下得具体实现方式函数指针的定义。
const static struct rt_pin_ops _stm32_pin_ops =
{
stm32_pin_mode,
stm32_pin_write,
stm32_pin_read,
stm32_pin_attach_irq,
stm32_pin_dettach_irq,
stm32_pin_irq_enable,
stm32_pin_get,
};
3、PIN设备的操作
PIN设备对外提供如下接口函数
函数 | 描述 |
---|---|
rt_pin_get() | 获取引脚编号 |
rt_pin_mode() | 设置引脚模式 |
rt_pin_write() | 设置引脚电平 |
rt_pin_read() | 读取引脚电平 |
rt_pin_attach_irq() | 绑定引脚中断回调函数 |
rt_pin_irq_enable() | 使能引脚中断 |
rt_pin_detach_irq() | 脱离引脚中断回调函数 |
3.1 获取管脚编号的实现
RT-Thread 提供的引脚编号需要和芯片的引脚号区分开来,它们并不是同一个概念,引脚编号由 PIN 设备驱动程序定义,和具体的芯片相关。管脚序号是后续其他输入输出中断函数实现的一个重要参数。STM32的驱动中对GPIOA(0-15)到GPIOx(0-15)进行了按顺序的编号操作。
RTT官方文档描述针对STM32的GPIO驱动drv_gpio.c中针对管脚编号提供了三种方式进行实现:
3.1.1使用API
此处需要注意
旧版本的drv_gpio.c文件在_stm32_pin_ops 中并没有定义函数stm32_pin_get,的实现,所以旧版本的驱动无法使用API获取到管脚编号。新建工程时,系统使用的驱动任为旧版本的
gpio驱动所以需要更新,可以从gitee进行文件下载bsp/stm32/libraries/HAL_Drivers/drv_gpio.c · RT-Thread/rt-thread - Gitee.com。具体实现方式函数如下
/*
根据输入字符串的端口号A-Z,转换为数值0-25
根据输入字符串的管脚号0-15,转换为数值0-15
将两者结合端口号为高位,管脚号为地位
*/
static rt_base_t stm32_pin_get(const char *name)
{
rt_base_t pin = 0;
int hw_port_num, hw_pin_num = 0;
int i, name_len;
name_len = rt_strlen(name);
if ((name_len < 4) || (name_len >= 6)) //进行字符串长度验证PA.0 PA.15 最短4,最长5
{
return -RT_EINVAL;
}
if ((name[0] != 'P') || (name[2] != '.')) //字符串第一个必须为P 第三个必须为.
{
return -RT_EINVAL;
}
if ((name[1] >= 'A') && (name[1] <= 'Z')) //端口范围在A-Z
{
hw_port_num = (int)(name[1] - 'A'); //端口编号计算,A-Z转换为0-25
}
else
{
return -RT_EINVAL;
}
for (i = 3; i < name_len; i++) //根据字符串的第4个进行编号转换PA.0=0 PA.15 = 15
{
hw_pin_num *= 10;
hw_pin_num += name[i] - '0';
}
pin = PIN_NUM(hw_port_num, hw_pin_num); //进行最后引脚编号的处理
return pin;
}
最终调用宏定义如下来进行引脚编号的获取
#define PIN_NUM(port, no) (((((port) & 0xFu) << 4) | ((no) & 0xFu)))
测算结果如下表,后续IO以此类推
name | port | no | result | name | port | no | result |
PA.0 | 0 | 0 | 0 | PB.0 | 1 | 0 | 16 |
PA.1 | 0 | 1 | 1 | PB.1 | 1 | 1 | 17 |
PA.2 | 0 | 2 | 2 | PB.2 | 1 | 2 | 18 |
PA.3 | 0 | 3 | 3 | PB.3 | 1 | 3 | 19 |
PA.4 | 0 | 4 | 4 | PB.4 | 1 | 4 | 20 |
PA.5 | 0 | 5 | 5 | PB.5 | 1 | 5 | 21 |
PA.6 | 0 | 6 | 6 | PB.6 | 1 | 6 | 22 |
PA.7 | 0 | 7 | 7 | PB.7 | 1 | 7 | 23 |
PA.8 | 0 | 8 | 8 | PB.8 | 1 | 8 | 24 |
PA.9 | 0 | 9 | 9 | PB.9 | 1 | 9 | 25 |
PA.10 | 0 | 10 | 10 | PB.10 | 1 | 10 | 26 |
PA.11 | 0 | 11 | 11 | PB.11 | 1 | 11 | 27 |
PA.12 | 0 | 12 | 12 | PB.12 | 1 | 12 | 28 |
PA.13 | 0 | 13 | 13 | PB.13 | 1 | 13 | 29 |
PA.14 | 0 | 14 | 14 | PB.14 | 1 | 14 | 30 |
PA.15 | 0 | 15 | 15 | PB.15 | 1 | 15 | 31 |
3.1.2使用宏定义
针对STM32,RTT提供了宏定义GET_PIN来进行管脚编号的获取,再未更新drv_gpio驱动前,该定义再drv_common.h中进行了定义,更新驱动后在drv_gpio.h中也进行了定义,两者定义相同。内容如下。
//drv_common.h中的宏定义
#define __STM32_PORT(port) GPIO##port##_BASE
#define GET_PIN(PORTx,PIN) (rt_base_t)((16 * ( ((rt_base_t)__STM32_PORT(PORTx) - (rt_base_t)GPIOA_BASE)/(0x0400UL) )) + PIN)
//drv_gpio.h中的宏定义
#define __STM32_PORT(port) GPIO##port##_BASE
#if defined(SOC_SERIES_STM32MP1)
#define GET_PIN(PORTx,PIN) (GPIO##PORTx == GPIOZ) ? (176 + PIN) : ((rt_base_t)((16 * ( ((rt_base_t)__STM32_PORT(PORTx) - (rt_base_t)GPIOA_BASE)/(0x1000UL) )) + PIN))
#else
#define GET_PIN(PORTx,PIN) (rt_base_t)((16 * ( ((rt_base_t)__STM32_PORT(PORTx) - (rt_base_t)GPIOA_BASE)/(0x0400UL) )) + PIN)
#endif
如上图,对GET_PIN宏定义进行了重复定义,可以看出drv_gpio.h中对宏定义进行了预编译,包含了STM32MP1的支持。
全局搜索GET_PIN的使用情况如下
board.h、drv_gpio.h和drv_usart.c中均进行了drv_common.h的包含。仅在drv_gpio.c文件中进行了drv_gpio.h的包含。所以我们可以将drv_gpio.h中关于GET_PIN的相关宏定义进行删除(不涉及STM32MP1的使用)。来保证宏定义的唯一性。
宏定义根据参数portx进行管脚端口的地址获取,将A-Z分别转换为0-25。参数PIN为端口下得具体IO。最终转换结果为端口序号*16+IO编号。与API转换结果相同。
宏定义 | PORTx | 16 * ( ((rt_base_t)__STM32_PORT(PORTx) - (rt_base_t)GPIOA_BASE)/(0x0400UL) ) | PIN | result | |
GET_PIN(A, 0) | GPIOA_BASE | D3_AHB1PERIPH_BASE + 0x0000UL | 0 | 0 | 0 |
GET_PIN(A, 1) | GPIOA_BASE | D3_AHB1PERIPH_BASE + 0x0000UL | 0 | 0 | 0 |
GET_PIN(A, 2) | GPIOA_BASE | D3_AHB1PERIPH_BASE + 0x0000UL | 0 | 0 | 0 |
GET_PIN(A, 3) | GPIOA_BASE | D3_AHB1PERIPH_BASE + 0x0000UL | 0 | 0 | 0 |
GET_PIN(B, 0) | GPIOB_BASE | D3_AHB1PERIPH_BASE + 0x0400UL | 1 | 0 | 16 |
GET_PIN(B, 1) | GPIOB_BASE | D3_AHB1PERIPH_BASE + 0x0400UL | 1 | 1 | 17 |
GET_PIN(B, 2) | GPIOB_BASE | D3_AHB1PERIPH_BASE + 0x0400UL | 1 | 2 | 18 |
3.1.3查看驱动文件
旧版本的drv_gpio驱动提供了管脚与编号的对应定义,可以通过查看直接进行管脚序号的定义。
新版本驱动更新了管脚编号识别逻辑,不在提供该驱动文件查看的方式。
3.2 设置引脚模式
rtt通过rt_pin_mode进行管脚模式的配置
void rt_pin_mode(rt_base_t pin, rt_base_t mode)
{
RT_ASSERT(_hw_pin.ops != RT_NULL);
_hw_pin.ops->pin_mode(&_hw_pin.parent, pin, mode);
}
最终调用的函数为pin设备类的ops下的pin_mode函数,在初始阶段该函数指针设置为了stm32_pin_mode,具体实现如下
/*
1、管脚序号的识别
通过宏定义
#define PIN_STPIN(pin) ((uint16_t)(1u << PIN_NO(pin))) 转换为位信息
#define PIN_NO(pin) ((uint8_t)((pin) & 0xFu)) 转换为0-15
来进行序号识别
2、端口识别
#define PIN_STPORT(pin) ((GPIO_TypeDef *)(GPIOA_BASE + (0x400u * PIN_PORT(pin))))
来进行引脚编号到端口结构体地址的转换。
3、最终调用库函数进行GPIO的配置。
*/
static void stm32_pin_mode(rt_device_t dev, rt_base_t pin, rt_base_t mode)
{
GPIO_InitTypeDef GPIO_InitStruct;
//验证端口是否大于最大端口,是否合法
if (PIN_PORT(pin) >= PIN_STPORT_MAX)
{
return;
}
//配置端口IO为默认推挽输出,不上下拉,输出速度HIGH
GPIO_InitStruct.Pin = PIN_STPIN(pin);
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
//根据实际传入参数的模式来进行配置
if (mode == PIN_MODE_OUTPUT)
{
/* output setting */
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
}
else if (mode == PIN_MODE_INPUT)
{
/* input setting: not pull. */
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
}
else if (mode == PIN_MODE_INPUT_PULLUP)
{
/* input setting: pull up. */
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_PULLUP;
}
else if (mode == PIN_MODE_INPUT_PULLDOWN)
{
/* input setting: pull down. */
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_PULLDOWN;
}
else if (mode == PIN_MODE_OUTPUT_OD)
{
/* output setting: od. */
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;
GPIO_InitStruct.Pull = GPIO_NOPULL;
}
//调用库函数进行初始化
HAL_GPIO_Init(PIN_STPORT(pin), &GPIO_InitStruct);
}
3.3 输出控制
rtt通过rt_pin_write进行管脚模式的配置。
void rt_pin_write(rt_base_t pin, rt_base_t value)
{
RT_ASSERT(_hw_pin.ops != RT_NULL);
_hw_pin.ops->pin_write(&_hw_pin.parent, pin, value);
}
最终调用的函数为pin设备类的ops下的pin_write函数,在初始阶段该函数指针设置为了stm32_pin_write,具体实现如下
/*
1、管脚序号的识别
通过宏定义
#define PIN_STPIN(pin) ((uint16_t)(1u << PIN_NO(pin))) 转换为位信息
#define PIN_NO(pin) ((uint8_t)((pin) & 0xFu)) 转换为0-15
来进行序号识别
2、端口识别
#define PIN_STPORT(pin) ((GPIO_TypeDef *)(GPIOA_BASE + (0x400u * PIN_PORT(pin))))
来进行引脚编号到端口结构体地址的转换。
3、最终调用库函数进行GPIO的配置。
*/
static void stm32_pin_write(rt_device_t dev, rt_base_t pin, rt_base_t value)
{
GPIO_TypeDef *gpio_port;
uint16_t gpio_pin;
if (PIN_PORT(pin) < PIN_STPORT_MAX)
{
//获取端口
gpio_port = PIN_STPORT(pin);
//获取IO编号
gpio_pin = PIN_STPIN(pin);
//库函数输出
HAL_GPIO_WritePin(gpio_port, gpio_pin, (GPIO_PinState)value);
}
}
3.4 输入获取
rtt通过rt_pin_read进行管脚模式的配置。
int rt_pin_read(rt_base_t pin)
{
RT_ASSERT(_hw_pin.ops != RT_NULL);
return _hw_pin.ops->pin_read(&_hw_pin.parent, pin);
}
最终调用的函数为pin设备类的ops下的pin_read函数,在初始阶段该函数指针设置为了stm32_pin_read,具体实现如下
/*
1、管脚序号的识别
通过宏定义
#define PIN_STPIN(pin) ((uint16_t)(1u << PIN_NO(pin))) 转换为位信息
#define PIN_NO(pin) ((uint8_t)((pin) & 0xFu)) 转换为0-15
来进行序号识别
2、端口识别
#define PIN_STPORT(pin) ((GPIO_TypeDef *)(GPIOA_BASE + (0x400u * PIN_PORT(pin))))
来进行引脚编号到端口结构体地址的转换。
3、最终调用库函数进行GPIO的配置。
*/
static int stm32_pin_read(rt_device_t dev, rt_base_t pin)
{
GPIO_TypeDef *gpio_port;
uint16_t gpio_pin;
int value = PIN_LOW;
if (PIN_PORT(pin) < PIN_STPORT_MAX)
{
gpio_port = PIN_STPORT(pin);
gpio_pin = PIN_STPIN(pin);
value = HAL_GPIO_ReadPin(gpio_port, gpio_pin);
}
return value;
}
3.5 中断回调的绑定
rtt通过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)
{
RT_ASSERT(_hw_pin.ops != RT_NULL);
if(_hw_pin.ops->pin_attach_irq)
{
return _hw_pin.ops->pin_attach_irq(&_hw_pin.parent, pin, mode, hdr, args);
}
return -RT_ENOSYS;
}
最终调用的函数为pin设备类的ops下的pin_attach_irq函数,在初始阶段该函数指针设置为了stm32_pin_attach_irq,具体实现如下
/*
该函数不进行底层STM32的外部中断的实际操作。
进行了pin设备的中断相关结构体赋值。
*/
static rt_err_t stm32_pin_attach_irq(struct rt_device *device, rt_int32_t pin, rt_uint32_t mode, void (*hdr)(void *args), void *args)
{
rt_base_t level;
rt_int32_t irqindex = -1;
//识别端口号是否正确
if (PIN_PORT(pin) >= PIN_STPORT_MAX)
{
return -RT_ENOSYS;
}
/*
进行中断序号的识别
1、通过管脚序号进行了中断序号的识别,首先通过PIN_STDPIN进行了位的转换
2、通过函数bit2bitno实现了位序号的识别。
3、实际可以通过PIN_NO来替代上述流程来进行识别
*/
irqindex = bit2bitno(PIN_STPIN(pin));
/*
判断中断序号是否合法
*/
if (irqindex < 0 || irqindex >= ITEM_NUM(pin_irq_map))
{
return RT_ENOSYS;
}
/*
进行pin设备的中断模式和回调函数的赋值
*/
level = rt_hw_interrupt_disable();
if (pin_irq_hdr_tab[irqindex].pin == pin &&
pin_irq_hdr_tab[irqindex].hdr == hdr &&
pin_irq_hdr_tab[irqindex].mode == mode &&
pin_irq_hdr_tab[irqindex].args == args)
{
rt_hw_interrupt_enable(level);
return RT_EOK;
}
if (pin_irq_hdr_tab[irqindex].pin != -1)
{
rt_hw_interrupt_enable(level);
return RT_EBUSY;
}
pin_irq_hdr_tab[irqindex].pin = pin;
pin_irq_hdr_tab[irqindex].hdr = hdr;
pin_irq_hdr_tab[irqindex].mode = mode;
pin_irq_hdr_tab[irqindex].args = args;
rt_hw_interrupt_enable(level);
return RT_EOK;
}
3.6 中断回调的解绑
rtt通过rt_pin_detach_irq进行管脚模式的配置。
rt_err_t rt_pin_detach_irq(rt_int32_t pin)
{
RT_ASSERT(_hw_pin.ops != RT_NULL);
if(_hw_pin.ops->pin_detach_irq)
{
return _hw_pin.ops->pin_detach_irq(&_hw_pin.parent, pin);
}
return -RT_ENOSYS;
}
最终调用的函数为pin设备类的ops下的pin_detach_irq函数,在初始阶段该函数指针设置为了stm32_pin_dettach_irq,具体实现如下
static rt_err_t stm32_pin_dettach_irq(struct rt_device *device, rt_int32_t pin)
{
rt_base_t level;
rt_int32_t irqindex = -1;
//识别输入端口是否合法
if (PIN_PORT(pin) >= PIN_STPORT_MAX)
{
return -RT_ENOSYS;
}
/*
进行中断序号的识别
1、通过管脚序号进行了中断序号的识别,首先通过PIN_STDPIN进行了位的转换
2、通过函数bit2bitno实现了位序号的识别。
3、实际可以通过PIN_NO来替代上述流程来进行识别
*/
irqindex = bit2bitno(PIN_STPIN(pin));
/*
判断中断序号是否合法
*/
if (irqindex < 0 || irqindex >= ITEM_NUM(pin_irq_map))
{
return RT_ENOSYS;
}
/*
中断模式和中断管脚回调函数的复位
*/
level = rt_hw_interrupt_disable();
if (pin_irq_hdr_tab[irqindex].pin == -1)
{
rt_hw_interrupt_enable(level);
return RT_EOK;
}
pin_irq_hdr_tab[irqindex].pin = -1;
pin_irq_hdr_tab[irqindex].hdr = RT_NULL;
pin_irq_hdr_tab[irqindex].mode = 0;
pin_irq_hdr_tab[irqindex].args = RT_NULL;
rt_hw_interrupt_enable(level);
return RT_EOK;
}
3.7 中断的使能和禁用
rtt通过rt_pin_irq_enable进行管脚模式的配置。
rt_err_t rt_pin_irq_enable(rt_base_t pin, rt_uint32_t enabled)
{
RT_ASSERT(_hw_pin.ops != RT_NULL);
if(_hw_pin.ops->pin_irq_enable)
{
return _hw_pin.ops->pin_irq_enable(&_hw_pin.parent, pin, enabled);
}
return -RT_ENOSYS;
}
最终调用的函数为pin设备类的ops下的pin_irq_enable函数,在初始阶段该函数指针设置为了stm32_pin_irq_enable,具体实现如下
static rt_err_t stm32_pin_irq_enable(struct rt_device *device, rt_base_t pin,rt_uint32_t enabled)
{
const struct pin_irq_map *irqmap;
rt_base_t level;
rt_int32_t irqindex = -1;
GPIO_InitTypeDef GPIO_InitStruct;
//识别IO是否合法
if (PIN_PORT(pin) >= PIN_STPORT_MAX)
{
return -RT_ENOSYS;
}
//使能中断
if (enabled == PIN_IRQ_ENABLE)
{
//获取判断中断序号是否合法
irqindex = bit2bitno(PIN_STPIN(pin));
if (irqindex < 0 || irqindex >= ITEM_NUM(pin_irq_map))
{
return RT_ENOSYS;
}
level = rt_hw_interrupt_disable();
if (pin_irq_hdr_tab[irqindex].pin == -1)
{
rt_hw_interrupt_enable(level);
return RT_ENOSYS;
}
//查表获取中断序号对应的内容
irqmap = &pin_irq_map[irqindex];
/*中断具体配置*/
GPIO_InitStruct.Pin = PIN_STPIN(pin);
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
switch (pin_irq_hdr_tab[irqindex].mode)
{
case PIN_IRQ_MODE_RISING:
GPIO_InitStruct.Pull = GPIO_PULLDOWN;
GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING;
break;
case PIN_IRQ_MODE_FALLING:
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING;
break;
case PIN_IRQ_MODE_RISING_FALLING:
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING_FALLING;
break;
}
HAL_GPIO_Init(PIN_STPORT(pin), &GPIO_InitStruct);
//设置中断优先级
HAL_NVIC_SetPriority(irqmap->irqno, 5, 0);
HAL_NVIC_EnableIRQ(irqmap->irqno);
pin_irq_enable_mask |= irqmap->pinbit;
rt_hw_interrupt_enable(level);
}
//禁用中断
else if (enabled == PIN_IRQ_DISABLE)
{
irqmap = get_pin_irq_map(PIN_STPIN(pin));
if (irqmap == RT_NULL)
{
return RT_ENOSYS;
}
level = rt_hw_interrupt_disable();
//复位管脚
HAL_GPIO_DeInit(PIN_STPORT(pin), PIN_STPIN(pin));
pin_irq_enable_mask &= ~irqmap->pinbit;
#if defined(SOC_SERIES_STM32F0) || defined(SOC_SERIES_STM32G0)
if ((irqmap->pinbit >= GPIO_PIN_0) && (irqmap->pinbit <= GPIO_PIN_1))
{
if (!(pin_irq_enable_mask & (GPIO_PIN_0 | GPIO_PIN_1)))
{
HAL_NVIC_DisableIRQ(irqmap->irqno);
}
}
else if ((irqmap->pinbit >= GPIO_PIN_2) && (irqmap->pinbit <= GPIO_PIN_3))
{
if (!(pin_irq_enable_mask & (GPIO_PIN_2 | GPIO_PIN_3)))
{
HAL_NVIC_DisableIRQ(irqmap->irqno);
}
}
else if ((irqmap->pinbit >= GPIO_PIN_4) && (irqmap->pinbit <= GPIO_PIN_15))
{
if (!(pin_irq_enable_mask & (GPIO_PIN_4 | GPIO_PIN_5 | GPIO_PIN_6 | GPIO_PIN_7 | GPIO_PIN_8 | GPIO_PIN_9 |
GPIO_PIN_10 | GPIO_PIN_11 | GPIO_PIN_12 | GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15)))
{
HAL_NVIC_DisableIRQ(irqmap->irqno);
}
}
else
{
HAL_NVIC_DisableIRQ(irqmap->irqno);
}
#else
if ((irqmap->pinbit >= GPIO_PIN_5) && (irqmap->pinbit <= GPIO_PIN_9))
{
if (!(pin_irq_enable_mask & (GPIO_PIN_5 | GPIO_PIN_6 | GPIO_PIN_7 | GPIO_PIN_8 | GPIO_PIN_9)))
{
HAL_NVIC_DisableIRQ(irqmap->irqno);
}
}
else if ((irqmap->pinbit >= GPIO_PIN_10) && (irqmap->pinbit <= GPIO_PIN_15))
{
if (!(pin_irq_enable_mask & (GPIO_PIN_10 | GPIO_PIN_11 | GPIO_PIN_12 | GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15)))
{
HAL_NVIC_DisableIRQ(irqmap->irqno);
}
}
else
{
HAL_NVIC_DisableIRQ(irqmap->irqno);
}
#endif
rt_hw_interrupt_enable(level);
}
else
{
return -RT_ENOSYS;
}
return RT_EOK;
}
3.8 中断回调函数的实现
在使能了外部中断后,STM32的底层在中断触发后会进行中断函数的调用如下函数。
- EXTI0_IRQHandler~EXTI4_IRQHandler
- EXTI9_5_IRQHandler
- EXTI15_10_IRQHandler
函数内部调用为HAL_GPIO_EXTI_IRQHandler。在该函数内部最终调用了回调函数HAL_GPIO_EXTI_Callback来进行回调函数的实现。
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
pin_irq_hdr(bit2bitno(GPIO_Pin));
}
如上回调函数调用了pin_irq_hdr进行了与pin设备所绑定的回调函数的关联。
rt_inline void pin_irq_hdr(int irqno)
{
if (pin_irq_hdr_tab[irqno].hdr)
{
pin_irq_hdr_tab[irqno].hdr(pin_irq_hdr_tab[irqno].args);
}
}