目录
- 1 获取引脚编号
- 2 设置引脚模式
- 3 设置引脚电平
- 4 绑定 PIN 中断回调函数
- 5 使能引脚中断
- 6 总结
- 7 PIN 设备使用示例
关于 RT-Thread 的 PIN 设备驱动应用层面的介绍可以直接参考 RT-Thread 的官网:PIN 设备
下面结合相关源代码分析一下 PIN 设备驱动。
1 获取引脚编号
首先拿到标准的 BSP 后,main.c 里面会有一个 IO 口的操作,内容如下:
#define LED0_PIN 24
int main(void)
{
int count = 1;
rt_pin_mode(LED0_PIN, PIN_MODE_OUTPUT);
while (count++)
{
rt_pin_write(LED0_PIN, PIN_HIGH);
rt_thread_mdelay(500);
rt_pin_write(LED0_PIN, PIN_LOW);
rt_thread_mdelay(500);
}
return RT_EOK;
}
RT-Thread 提供的引脚编号需要和芯片的引脚号区分开来,它们并不是同一个概念,引脚编号由 PIN 设备驱动程序定义,和具体的芯片相关。有2种方式可以获取引脚编号:使用宏定义或者查看PIN 驱动文件。
第 1 种:使用宏定义
如果使用的是 STM32 芯片,可以直接使用宏定义来获取引脚编号:
GET_PIN(port, pin)
比如,我现在要获得 PB8 口,则使用 GET_PIN(B, 8), 即可
GET_PIN() 在 drv_gpio.h 文件中,内容如下:
#define GET_PIN(PORTx,PIN) (rt_base_t)((16 * ( ((rt_base_t)__STM32_PORT(PORTx) - (rt_base_t)GPIOA_BASE)/(0x0400UL) )) + PIN)
其中 __STM32_PORT(),也在 drv_gpio.h 文件中,内容如下:
#define __STM32_PORT(port) GPIO##port##_BASE
所以 GET_PIN(B, 8),展开如下:
(rt_base_t)((16 * ((rt_base_t)GPIOB_BASE - (rt_base_t)GPIOA_BASE)/(0x0400UL))) + 8)
rt_base_t 定义在 rtdef.h 中,内容如下:
![在这里插入图片描述](https://img-blog.csdnimg.cn/20210507184545925.png#pic_center)
而 GPIOB_BASE 就定义在 ST 官方 HAL 库中,在 stm32f103xe.h(和具体的芯片有关),如下图:
![在这里插入图片描述](https://img-blog.csdnimg.cn/20210507184831904.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0NTRE5fR2FvXzE2,size_16,color_FFFFFF,t_70#pic_center)
第二种:查看驱动文件
这里的驱动文件是指 drv_gpio.c 文件中的 pins[],内容如下:
__STM32_PIN() 也是一个宏定义,定义在 drv_gpio.h 中,内容如下:
![在这里插入图片描述](https://img-blog.csdnimg.cn/20210507185411166.png#pic_center)
__STM32_PIN() 中的 index 参数是 芯片的引脚。例如,__STM32_PIN(24, B, 8),就可以得到 PB8 的引脚编号为 24。所以在 main.c 函数中有定义 LED0_PIN 24
而结构体 struct pin_index
也定义在 drv_gpio.h 中,内容如下:
struct pin_index
{
int index;
GPIO_TypeDef *gpio;
uint32_t pin;
};
2 设置引脚模式
之前不用 RT-Thread 时,要使用 IO 口时,需要先初始化 IO 口,比如设置 IO 的复用功能、输入/输出等功能。而在 RT-Thread 操作系统中,设置引脚模式使用的是如下函数:
void rt_pin_mode(rt_base_t pin, rt_base_t mode);
此函数定义在 pin.c 文件中,内容如下:
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);
}
可见 rt_pin_mode() 函数是调用 _hw_pin.ops->pin_mode() 函数来完成初始化的。接下来需要了解结构体 _hw_pin 及其成员变量 ops 和 ops 所定义的函数指针。
(1)、_hw_pin 结构体定义在 pin.c 文件中,是全局静态变量,如下图:
![在这里插入图片描述](https://img-blog.csdnimg.cn/20210507205804823.png#pic_center)
接下来看一下 struct rt_device_pin
, 此结构体定义在 pin.h 中,内容如下:
struct rt_device_pin
{
struct rt_device parent;
const struct rt_pin_ops *ops;
};
由此可以看出,rt_device_pin 继承于 rt_device,那来看下定义在 rtdef.h 文件中的 rt_device,内容如下:
struct rt_device
{
struct rt_object parent;
enum rt_device_class_type type;
rt_uint16_t flag;
rt_uint16_t open_flag;
rt_uint8_t ref_count;
rt_uint8_t device_id;
rt_err_t (*rx_indicate)(rt_device_t dev, rt_size_t size);
rt_err_t (*tx_complete)(rt_device_t dev, void *buffer);
#ifdef RT_USING_DEVICE_OPS
const struct rt_device_ops *ops;
#else
rt_err_t (*init) (rt_device_t dev);
rt_err_t (*open) (rt_device_t dev, rt_uint16_t oflag);
rt_err_t (*close) (rt_device_t dev);
rt_size_t (*read) (rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size);
rt_size_t (*write) (rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size);
rt_err_t (*control)(rt_device_t dev, int cmd, void *args);
#endif
#if defined(RT_USING_POSIX)
const struct dfs_file_ops *fops;
struct rt_wqueue wait_queue;
#endif
void *user_data;
};
可以看出 rt_device 又继承于 rt_object , 而 rt_object 同样定义在 rtdef.h 中,内容如下:
struct rt_object
{
char name[RT_NAME_MAX];
rt_uint8_t type;
rt_uint8_t flag;
#ifdef RT_USING_MODULE
void *module_id;
#endif
rt_list_t list;
};
typedef struct rt_object *rt_object_t;
rt_object 是 RT-Thread 内核中最基本的对象,其它对象都是继承于 rt_object,然后扩展出自己的成员变量。
接下来再看下 _hw_pin 的成员变量 ops,ops 是结构体 rt_pin_ops 类型,而 rt_pin_ops 结构体定义在 pin.h 文件中,内容如下:
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_pin_ops 结构体成员变量中的函数指针相当于设备模型的设备管理层,是应用层通向设备驱动层的桥梁。
最终展开 _hw_pin 全部内容如下:
![在这里插入图片描述](https://img-blog.csdnimg.cn/20210507214437349.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0NTRE5fR2FvXzE2,size_16,color_FFFFFF,t_70#pic_center)
由此知道,对引脚的操作都是利用 _hw_pin 的成员变量 ops 中的函数指针来完成的。如设置引脚模式就是 rt_pin_mode() 函数中的 _hw_pin.ops->pin_mode(&_hw_pin.parent, pin, mode); 来实现的。但 pin_mode() 是函数指针,实现这个函数的是谁?
这里就要用到 int rt_device_pin_register(const char *name, const struct rt_pin_ops *ops, void *user_data)
设备引脚注册函数了,此函数定义在 pin.c 中,具体内容如下:
int rt_device_pin_register(const char *name, const struct rt_pin_ops *ops, void *user_data)
{
_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
_hw_pin.ops = ops;
_hw_pin.parent.user_data = user_data;
rt_device_register(&_hw_pin.parent, name, RT_DEVICE_FLAG_RDWR);
return 0;
}
上面的代码先对 _hw_pin 进行初始化,再调用 rt_device_register() 函数将 _hw_pin 这个 PIN 设备注册到内核中,由 rt_list_t 双向链表来管理。而第 18 行可知,_hw_pin.ops 里的函数指针所指向的函数实体是由调用者传进去的 ops 决定的。那是谁在调用 rt_device_pin_register() 这个函数?全局搜索一下不就知道了。最终发现是在 drv_gpio.c 文件中的 int rt_hw_pin_init(void) 调用的。内容如下:
int rt_hw_pin_init(void)
{
#if defined(__HAL_RCC_GPIOA_CLK_ENABLE)
__HAL_RCC_GPIOA_CLK_ENABLE();
#endif
#if defined(__HAL_RCC_GPIOB_CLK_ENABLE)
__HAL_RCC_GPIOB_CLK_ENABLE();
#endif
#if defined(__HAL_RCC_GPIOC_CLK_ENABLE)
__HAL_RCC_GPIOC_CLK_ENABLE();
#endif
#if defined(__HAL_RCC_GPIOD_CLK_ENABLE)
__HAL_RCC_GPIOD_CLK_ENABLE();
#endif
#if defined(__HAL_RCC_GPIOE_CLK_ENABLE)
__HAL_RCC_GPIOE_CLK_ENABLE();
#endif
#if defined(__HAL_RCC_GPIOF_CLK_ENABLE)
__HAL_RCC_GPIOF_CLK_ENABLE();
#endif
#if defined(__HAL_RCC_GPIOG_CLK_ENABLE)
#ifdef SOC_SERIES_STM32L4
HAL_PWREx_EnableVddIO2();
#endif
__HAL_RCC_GPIOG_CLK_ENABLE();
#endif
#if defined(__HAL_RCC_GPIOH_CLK_ENABLE)
__HAL_RCC_GPIOH_CLK_ENABLE();
#endif
#if defined(__HAL_RCC_GPIOI_CLK_ENABLE)
__HAL_RCC_GPIOI_CLK_ENABLE();
#endif
#if defined(__HAL_RCC_GPIOJ_CLK_ENABLE)
__HAL_RCC_GPIOJ_CLK_ENABLE();
#endif
#if defined(__HAL_RCC_GPIOK_CLK_ENABLE)
__HAL_RCC_GPIOK_CLK_ENABLE();
#endif
return rt_device_pin_register("pin", &_stm32_pin_ops, RT_NULL);
}
rt_hw_pin_init() 此函数就是初始化引脚用的,可以看到,此函数先将各引脚时钟使能,然后调用 rt_device_pin_register("pin", &_stm32_pin_ops, RT_NULL);
可以看到 PIN 设备的名字为 “pin",PIN 设备用到的 ops 为 _stm32_pin_ops,不用说,直接找到 _stm32_pin_ops 一看究竟,此结构体也定义在 drv_gpio.c 文件中,内容如下:
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,
};
由此,我们可以把 _hw_pin 设备中的 ops 操作函数一一对应,如下:
_hw_pin | 对应结果 |
---|
_hw_pin.ops->pin_mode | stm32_pin_mode |
_hw_pin.ops->pin_write | stm32_pin_write |
_hw_pin.ops->pin_read | stm32_pin_read |
_hw_pin.ops->pin_attach_irq | stm32_pin_attach_irq |
_hw_pin.ops->pin_dettach_irq | stm32_pin_dettach_irq |
_hw_pin.ops->pin_irq_enable | stm32_pin_irq_enable |
所以 执行 _hw_pin.ops->pin_mode(&_hw_pin.parent, pin, mode);
这句代码,实际就是执行 stm32_pin_mode() 函数(其它函数原理一样)。
那接下来就看下 stm32_pin_mode() 实现了个啥?stm32_pin_mode() 还是定义在 drv_gpio.c(关于gpio的驱动程序都是在 drv_gpio.c 文件中实现),内容如下:
static void stm32_pin_mode(rt_device_t dev, rt_base_t pin, rt_base_t mode)
{
const struct pin_index *index;
GPIO_InitTypeDef GPIO_InitStruct;
index = get_pin(pin);
if (index == RT_NULL)
{
return;
}
GPIO_InitStruct.Pin = index->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)
{
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
}
else if (mode == PIN_MODE_INPUT)
{
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
}
else if (mode == PIN_MODE_INPUT_PULLUP)
{
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_PULLUP;
}
else if (mode == PIN_MODE_INPUT_PULLDOWN)
{
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_PULLDOWN;
}
else if (mode == PIN_MODE_OUTPUT_OD)
{
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;
GPIO_InitStruct.Pull = GPIO_NOPULL;
}
HAL_GPIO_Init(index->gpio, &GPIO_InitStruct);
}
这个函数我想不用再说了,STM32 裸机程序中都是这样写的。函数是利用形参 mode 的取值不同,对引脚实现不能的初始化,其中 mode 的取值如下:
#define PIN_MODE_OUTPUT 0x00
#define PIN_MODE_INPUT 0x01
#define PIN_MODE_INPUT_PULLUP 0x02
#define PIN_MODE_INPUT_PULLDOWN 0x03
#define PIN_MODE_OUTPUT_OD 0x04
到这里简单总结一下:
![在这里插入图片描述](https://img-blog.csdnimg.cn/20210507222622889.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0NTRE5fR2FvXzE2,size_16,color_FFFFFF,t_70#pic_center)
到这里还没完,我们还要知道是谁调用了 rt_hw_pin_init() 函数,这样才能使 PIN 设备驱动完整。
这很简单,直接全局搜索,发现是 rt_hw_board_init() 函数调用的。就这样往前推,可以看到程序启动到注册完成 PIN 设备的流程如下图:
![在这里插入图片描述](https://img-blog.csdnimg.cn/20210507223851947.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0NTRE5fR2FvXzE2,size_16,color_FFFFFF,t_70#pic_center)
3 设置引脚电平
这里应用层调用 rt_pin_write() 来对 pin 电平设置,如下:
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);
}
由第 2 节知道 ,这里的 pin_wirte(),实际执行的是 stm32_pin_write,其函数内容如下:
static void stm32_pin_write(rt_device_t dev, rt_base_t pin, rt_base_t value)
{
const struct pin_index *index;
index = get_pin(pin);
if (index == RT_NULL)
{
return;
}
HAL_GPIO_WritePin(index->gpio, index->pin, (GPIO_PinState)value);
}
可以看到,最终是调用 HAL 库中的 HAL_GPIO_WritePin() 函数来对 gpio 操作。
而 rt_pin_read() 与 rt_pin_write() 实现方式一样,不再多说。接下来看下中断部分。
4 绑定 PIN 中断回调函数
若要使用到引脚的中断功能,可以使用如下函数将某个引脚配置为某种中断触发模式并绑定一个中断回调函数到对应引脚,当引脚中断发生时,就会执行回调函数:
rt_err_t rt_pin_attach_irq(rt_int32_t pin, rt_uint32_t mode,
void (*hdr)(void *args), void *args);
我们知道 ,此函数会调用如下函数:
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)
其定义在 drv_gpio.c 文件中,实现内容如下:
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)
{
const struct pin_index *index;
rt_base_t level;
rt_int32_t irqindex = -1;
index = get_pin(pin);
if (index == RT_NULL)
{
return RT_ENOSYS;
}
irqindex = bit2bitno(index->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 == 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;
}
由上面函数可以看到,STM32 的 PIN 中断被组织到 pin_irq_hdr_tab[] 结构体数组中,共 16 个,其初始内容如下:
static struct rt_pin_irq_hdr pin_irq_hdr_tab[] =
{
{-1, 0, RT_NULL, RT_NULL},
{-1, 0, RT_NULL, RT_NULL},
{-1, 0, RT_NULL, RT_NULL},
{-1, 0, RT_NULL, RT_NULL},
{-1, 0, RT_NULL, RT_NULL},
{-1, 0, RT_NULL, RT_NULL},
{-1, 0, RT_NULL, RT_NULL},
{-1, 0, RT_NULL, RT_NULL},
{-1, 0, RT_NULL, RT_NULL},
{-1, 0, RT_NULL, RT_NULL},
{-1, 0, RT_NULL, RT_NULL},
{-1, 0, RT_NULL, RT_NULL},
{-1, 0, RT_NULL, RT_NULL},
{-1, 0, RT_NULL, RT_NULL},
{-1, 0, RT_NULL, RT_NULL},
{-1, 0, RT_NULL, RT_NULL},
};
其结构体 rt_pin_irq_hdr 的4个成员如下:
struct rt_pin_irq_hdr
{
rt_int16_t pin;
rt_uint16_t mode;
void (*hdr)(void *args);
void *args;
};
这4个成员变量就是函数 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)
的其中后4个参数。此函数的主要目的就是设置某个 PIN 引脚中断相关信息(中断触发方式、中断回调函数、中断回调函数的参数)。
其中中断触发方式形参 mode 的取值如下(在 pin.h 中定义):
#define PIN_IRQ_MODE_RISING 0x00
#define PIN_IRQ_MODE_FALLING 0x01
#define PIN_IRQ_MODE_RISING_FALLING 0x02
#define PIN_IRQ_MODE_HIGH_LEVEL 0x03
#define PIN_IRQ_MODE_LOW_LEVEL 0x04
5 使能引脚中断
应用层调用 rt_err_t rt_pin_irq_enable(rt_base_t pin, rt_uint32_t enabled); 设备管理层调用 static rt_err_t stm32_pin_dettach_irq(struct rt_device *device, rt_int32_t pin)
,其函数内容如下:
static rt_err_t stm32_pin_irq_enable(struct rt_device *device, rt_base_t pin,
rt_uint32_t enabled)
{
const struct pin_index *index;
const struct pin_irq_map *irqmap;
rt_base_t level;
rt_int32_t irqindex = -1;
GPIO_InitTypeDef GPIO_InitStruct;
index = get_pin(pin);
if (index == RT_NULL)
{
return RT_ENOSYS;
}
if (enabled == PIN_IRQ_ENABLE)
{
irqindex = bit2bitno(index->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 = index->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(index->gpio, &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(index->pin);
if (irqmap == RT_NULL)
{
return RT_ENOSYS;
}
level = rt_hw_interrupt_disable();
HAL_GPIO_DeInit(index->gpio, index->pin);
pin_irq_enable_mask &= ~irqmap->pinbit;
#if defined(SOC_SERIES_STM32F0) || defined(SOC_SERIES_STM32G0)
#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;
}
此函数前 26行都是在检查参数的合法性,然后根据参数 enabled 判断是 PIN_IRQ_ENABLE 还是 PIN_IRQ_DISABLE;若是 PIN_IRQ_ENABLE 再根据 pin_irq_hdr_tab[irqindex].mode 设置的中断触发方式来分情况配置引脚。然后设置中断优先级,使用中断等。若是 PIN_IRQ_DISABLE 则去初始化,失能中断等。
之后就是硬件判断,若引脚有符合的中断触发,则 void EXTIx_IRQHandler(void) 会触发,如下:
void EXTI0_IRQHandler(void)
{
rt_interrupt_enter();
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_0);
rt_interrupt_leave();
}
void EXTI1_IRQHandler(void)
{
rt_interrupt_enter();
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_1);
rt_interrupt_leave();
}
void EXTI2_IRQHandler(void)
{
rt_interrupt_enter();
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_2);
rt_interrupt_leave();
}
void EXTI3_IRQHandler(void)
{
rt_interrupt_enter();
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_3);
rt_interrupt_leave();
}
void EXTI4_IRQHandler(void)
{
rt_interrupt_enter();
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_4);
rt_interrupt_leave();
}
void EXTI9_5_IRQHandler(void)
{
rt_interrupt_enter();
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_5);
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_6);
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_7);
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_8);
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_9);
rt_interrupt_leave();
}
void EXTI15_10_IRQHandler(void)
{
rt_interrupt_enter();
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_10);
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_11);
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_12);
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_13);
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_14);
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_15);
rt_interrupt_leave();
}
假如,这时中断到达,执行 EXTI0_IRQHandler() 中断处理函数,则 HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_0) 会调用,接着执行 HAL 库中的中断回调函数 HAL_GPIO_EXTI_Callback(GPIO_Pin),此函数由用户实现,实现如下:
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
pin_irq_hdr(bit2bitno(GPIO_Pin));
}
所以,中断到达后,pin_irq_hdr(bit2bitno(GPIO_Pin)) 会执行。
pin_irq_hdr() 定义为内联函数,可以提高执行效率,内容如下:
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);
}
}
所以,转了一大圏,最终是执行用户在 rt_err_t rt_pin_attach_irq(rt_int32_t pin, rt_uint32_t mode, void (*hdr)(void *args), void *args)
中传入的回调函数 void (*hdr)(void *args)
。
6 总结
![在这里插入图片描述](https://img-blog.csdnimg.cn/20210507235834888.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0NTRE5fR2FvXzE2,size_16,color_FFFFFF,t_70#pic_center)
7 PIN 设备使用示例
PIN 设备的具体使用方式可以参考如下示例代码,示例代码的主要步骤如下:
设置蜂鸣器对应引脚为输出模式,并给一个默认的低电平状态。
设置按键 0 和 按键1 对应引脚为输入模式,然后绑定中断回调函数并使能中断。
按下按键 0 蜂鸣器开始响,按下按键 1 蜂鸣器停止响。
#include <rtthread.h>
#include <rtdevice.h>
#ifndef BEEP_PIN_NUM
#define BEEP_PIN_NUM 35
#endif
#ifndef KEY0_PIN_NUM
#define KEY0_PIN_NUM 55
#endif
#ifndef KEY1_PIN_NUM
#define KEY1_PIN_NUM 56
#endif
void beep_on(void *args)
{
rt_kprintf("turn on beep!\n");
rt_pin_write(BEEP_PIN_NUM, PIN_HIGH);
}
void beep_off(void *args)
{
rt_kprintf("turn off beep!\n");
rt_pin_write(BEEP_PIN_NUM, PIN_LOW);
}
static void pin_beep_sample(void)
{
rt_pin_mode(BEEP_PIN_NUM, PIN_MODE_OUTPUT);
rt_pin_write(BEEP_PIN_NUM, PIN_LOW);
rt_pin_mode(KEY0_PIN_NUM, PIN_MODE_INPUT_PULLUP);
rt_pin_attach_irq(KEY0_PIN_NUM, PIN_IRQ_MODE_FALLING, beep_on, RT_NULL);
rt_pin_irq_enable(KEY0_PIN_NUM, PIN_IRQ_ENABLE);
rt_pin_mode(KEY1_PIN_NUM, PIN_MODE_INPUT_PULLUP);
rt_pin_attach_irq(KEY1_PIN_NUM, PIN_IRQ_MODE_FALLING, beep_off, RT_NULL);
rt_pin_irq_enable(KEY1_PIN_NUM, PIN_IRQ_ENABLE);
}
MSH_CMD_EXPORT(pin_beep_sample, pin beep sample);
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)