STM32F407VET6 底层驱动之外中断寄存器封装
外中断模块中实现了所有中断线的中断,使用的时候需要将与中断线对应的回调函数注册到模块中就行了,所有与单片机相关的操作均在模块内部完成。
1、外中断封装接口如下:
a、外中断使能配置:unsigned int exti_enable(eGpioType_t gpio, ePinType_t pin, eExtiType_t trim, unsigned char prio, void (*pCallBack)(void * const parg), void const *parg)
b、外中断禁能配置:void exti_disable(eGpioType_t gpio, ePinType_t pin)
2、外中断对外开放的枚举类型如下:
// GPIO外中断触发类型
typedef enum _eExtiType
{
eEXTI_BOTH_EDGE, // 任意电平触发
eEXTI_RISING_EDGE, // 上升沿触发
eEXTI_FALLING_EDGE, // 下降沿触发
}eExtiType_t;
3、外中断代码实现如下:
// GPIO外中断线类型
typedef enum _eExtiLineType
{
eEXTI0,
eEXTI1,
eEXTI2,
eEXTI3,
eEXTI4,
eEXTI5,
eEXTI6,
eEXTI7,
eEXTI8,
eEXTI9,
eEXTI10,
eEXTI11,
eEXTI12,
eEXTI13,
eEXTI14,
eEXTI15,
eEXTI_COUNT, // 注:这不是中断线类型,不可删除,仅用做数量统计
}eExtiLineType_t;
// 外中断回调函数数据结构
#pragma pack(push, 1)
typedef struct _sExtiDev
{
void *parg;
void (*pCallBack)(void * const parg);
}sExtiDev_t;
#pragma pack(pop)
sExtiDev_t sExtiDev[eEXTI_COUNT] = {NULL};
* 函数功能:外中断优先级设置(组4),注意优先级不能超过设
定的组的范围否则会有意想不到的错误。组划分如下:
组0:0位抢占优先级,4位响应优先级
组1:1位抢占优先级,3位响应优先级
组2:2位抢占优先级,2位响应优先级
组3:3位抢占优先级,1位响应优先级
组4:4位抢占优先级,0位响应优先级
抢占优先级和响应优先级的数值越小,优先级越高,处理器优
先比较抢占优先级然后再看响应优先级。
* 形 参:prio:抢占优先级(分组4的响应优先级固定为0)
channel:中断通道
* 返 回 值:无
********************************************************/
static void exti_nvic_set(unsigned char prio, unsigned char channel)
{
// unsigned int temp, temp1;
// 设置分组
unsigned char nvic_group = 4; // 0 - 4
// temp1 = (~nvic_group) & 0x07; // 取后三位
// temp1 = temp1 << 8;
// temp = SCB->AIRCR; // 读取先前的设置
// temp &= 0x0000F8FF; // 清空先前分组
// temp |= 0x05FA0000; // 写入钥匙
// temp |= temp1;
// SCB->AIRCR = temp; // 设置分组
unsigned int temp;
unsigned char sub_prio = 0; // 分组4的响应优先级固定为0
// 注:优先级的设置必须要大于configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY
// 否则不能在中断服务函数中调用FreeRTOS相关的API函数
if(prio <= 5)
{
prio = 6;
}
else if(prio > 15)
{
prio = 15;
}
temp = prio << (4 - nvic_group);
temp |= sub_prio & (0x0F >> nvic_group);
temp = temp & 0xF; // 取低四位
NVIC->ISER[channel >> 5] |= 1 << (channel % 32); // 使能中断位(要清除的话,设置ICER对应位为1即可)
NVIC->IP[channel] |= temp << 4; // 设置响应优先级和抢断优先级
}
* 函数功能:外部中断配置函数,每次只能配置一个引脚(配置外中断前需先配置GPIO为输入模式)
(只针对通用IO,配置完立即使能中断,配置之后需重新注册回调函数)
* 形 参:gpio:指定GPIO端口
pin:指定GPIO引脚
trim:中断触发模式
prio:中断优先级(范围:6 - 15,数值越小优先级越高)
pCallBack:回调函数指针
parg:回调函数的参数
* 返 回 值:0=成功
1=回调函数指针为NULL
2=GPIO引脚错误
********************************************************/
unsigned int exti_enable(eGpioType_t gpio, ePinType_t pin, eExtiType_t trim, unsigned char prio, void (*pCallBack)(void * const parg), void const *parg)
{
unsigned char exti_line = 31U - __CLZ(pin);
unsigned char exti_offset = (exti_line % 4) << 2;
if(pCallBack == NULL)
{
return 1;
}
// 引脚初始化
if(trim == eEXTI_FALLING_EDGE)
{
gpio_config(gpio, pin, eGPIO_IN_UP, 1); // 下降沿触发就将引脚上拉,默认输出高电平
}
else if(trim == eEXTI_RISING_EDGE)
{
gpio_config(gpio, pin, eGPIO_IN_DP, 0); // 上升沿触发就将引脚下拉,默认输出低电平
}
else
{
gpio_config(gpio, pin, eGPIO_IN_NP, 0); // 上升和下降沿都触发就将引脚设置无上下拉,默认输出低电平
}
RCC->APB2ENR |= 0x1 << 14; // 使能SYSCFG时钟
SYSCFG->EXTICR[exti_line >> 2] &= ~(0x000F << exti_offset); // 清除原来设置
SYSCFG->EXTICR[exti_line >> 2] |= gpio << exti_offset; // EXTI.BITx映射到GPIOx.BITx
if(trim == eEXTI_FALLING_EDGE)
{
EXTI->FTSR |= pin; // 下降沿触发
}
else if(trim == eEXTI_RISING_EDGE)
{
EXTI->RTSR |= pin; // 上升沿触发
}
else if(trim == eEXTI_BOTH_EDGE)
{
EXTI->FTSR |= pin; // 下降沿触发
EXTI->RTSR |= pin; // 上升沿触发
}
unsigned char IRQn = 0;
switch(exti_line)
{
case eEXTI0: IRQn = EXTI0_IRQn; break;
case eEXTI1: IRQn = EXTI1_IRQn; break;
case eEXTI2: IRQn = EXTI2_IRQn; break;
case eEXTI3: IRQn = EXTI3_IRQn; break;
case eEXTI4: IRQn = EXTI4_IRQn; break;
case eEXTI5:
case eEXTI6:
case eEXTI7:
case eEXTI8:
case eEXTI9: IRQn = EXTI9_5_IRQn; break;
case eEXTI10:
case eEXTI11:
case eEXTI12:
case eEXTI13:
case eEXTI14:
case eEXTI15: IRQn = EXTI15_10_IRQn; break;
default : return 2;
}
sExtiDev[exti_line].parg = (void *)parg;
sExtiDev[exti_line].pCallBack = pCallBack;
exti_nvic_set(prio, IRQn); // 设置中断优先级
return 0;
}
* 函数功能:外部中断配置函数(只针对通用IO)
* 形 参:gpio:指定GPIO端口
pin:指定GPIO引脚
trim:中断触发模式
* 返 回 值:无
********************************************************/
void exti_disable(eGpioType_t gpio, ePinType_t pin)
{
unsigned char exti_line = 31U - __CLZ(pin);
unsigned char exti_offset = (exti_line % 4) << 2;
SYSCFG->EXTICR[exti_line >> 2] &= ~(0x000F << exti_offset); // 清除原来设置
SYSCFG->EXTICR[exti_line >> 2] |= gpio << exti_offset; // EXTI.BITx映射到GPIOx.BITx
EXTI->IMR |= ~pin; // 关闭指定引脚上的中断(如果要使能中断,则反操作即可)
}
* 函数功能:外部中断回调函数(中断专用)
* 形 参:line:指定的外中断线
* 返 回 值:无
********************************************************/
static void exti_isr_callback(eExtiLineType_t line)
{
if(sExtiDev[line].pCallBack != NULL)
{
// 执行中断回调函数
sExtiDev[line].pCallBack(sExtiDev[line].parg);
}
EXTI->PR = 0x1UL << line; // 清除中断标志位
}
* 函数功能:外部中断服务函数
* 形 参:无
* 返 回 值:无
********************************************************/
void EXTI0_IRQHandler(void)
{
exti_isr_callback(eEXTI0);
}
/********************************************************
* 函数功能:外部中断服务函数
* 形 参:无
* 返 回 值:无
********************************************************/
void EXTI1_IRQHandler(void)
{
exti_isr_callback(eEXTI1);
}
/********************************************************
* 函数功能:外部中断服务函数
* 形 参:无
* 返 回 值:无
********************************************************/
void EXTI2_IRQHandler(void)
{
exti_isr_callback(eEXTI2);
}
/********************************************************
* 函数功能:外部中断服务函数
* 形 参:无
* 返 回 值:无
********************************************************/
void EXTI3_IRQHandler(void)
{
exti_isr_callback(eEXTI3);
}
/********************************************************
* 函数功能:外部中断服务函数
* 形 参:无
* 返 回 值:无
********************************************************/
void EXTI4_IRQHandler(void)
{
exti_isr_callback(eEXTI4);
}
/********************************************************
* 函数功能:外部中断服务函数
* 形 参:无
* 返 回 值:无
********************************************************/
void EXTI9_5_IRQHandler(void)
{
unsigned int pr = EXTI->PR;
if(pr & (0x1UL << eEXTI5))
{
exti_isr_callback(eEXTI5);
}
else if(pr & (0x1UL << eEXTI6))
{
exti_isr_callback(eEXTI6);
}
else if(pr & (0x1UL << eEXTI7))
{
exti_isr_callback(eEXTI7);
}
else if(pr & (0x1UL << eEXTI8))
{
exti_isr_callback(eEXTI8);
}
else if(pr & (0x1UL << eEXTI9))
{
exti_isr_callback(eEXTI9);
}
else
{
// 未知中断线,不可能会发生
}
}
/********************************************************
* 函数功能:外部中断服务函数
* 形 参:无
* 返 回 值:无
********************************************************/
void EXTI15_10_IRQHandler(void)
{
unsigned int pr = EXTI->PR;
if(pr & (0x1UL << eEXTI10))
{
exti_isr_callback(eEXTI10);
}
else if(pr & (0x1UL << eEXTI11))
{
exti_isr_callback(eEXTI11);
}
else if(pr & (0x1UL << eEXTI12))
{
exti_isr_callback(eEXTI12);
}
else if(pr & (0x1UL << eEXTI13))
{
exti_isr_callback(eEXTI13);
}
else if(pr & (0x1UL << eEXTI14))
{
exti_isr_callback(eEXTI14);
}
else if(pr & (0x1UL << eEXTI15))
{
exti_isr_callback(eEXTI15);
}
else
{
// 未知中断线,不可能会发生
}
}