1 中断流向图
图 1 GPIO外部中断
如上图所示,【1】代表的是外部的GPIO引脚,可以看出,GPIO_PORTx_PINy,中PIN号相同的引脚共用一组中断线(如图中所示的【3】)。【2】代表选择该组的那个引脚作为中断引脚,因此,GPIO_PORTx_PINy 中同个PIN号种只能有一个PORT能够产生中断。例如,PA0和PB0,PCO,PD0等只能有一个能配置成中断,如果程序中配置了PA0,然后又配置PB0那么只有PB0能够产生中断。【4】为中断屏蔽寄存器,如果屏蔽了该中断,也不能使得【5】PR中断挂起寄存器置1,也就不能触发中断函数了。
2 中断的配置
◎ 将相应的GPIO引脚设置成输入;
◎ 将设置中断触发的边沿方式,有上升沿触发,下降沿触发和上下边沿触发三种方式;
◎ 使能NVIC 中断号的中断;
◎ 使能中断屏蔽寄存器;
◎ 编写中断服务函数;
3 中断服务函数的编写
中断服务函数采用的是回调的形式,也就是上层按照底层的接口形式,编写服务函数,然后以函数指针的方式传递到底层,从而实现上层向底层的传递(依赖倒置)。
GPIO_SetEventCallback(GPIO_PD4,GPIO_PIN4_EventCallback);
void GPIO_PIN4_EventCallback(uint32_t gpioNum)//PD4:LIN;lin,PC4:IGN2
{
gLINWakeSignal = 1;//record interrupt Reason
if(GPIO_PD4 == gpioNum)
{
gLINWakeSignal = 1; /*【疑问1】这里是有疑问的,后面讲*/
}
else
{
;
}
GPIO_ClearPendingExtInt(GPIO_PC4);//clear interrupt flag
}
这个函数中,首先将将标志置1(因为这是一个将LIN的引脚转换为普通IO口的程序,所以变量名字可能不太理解,这个跟实际项目有关,请忽略),然后清除中断挂起标志。
这里着重介绍下,这种回调机制,因为这可以很好的实现底层与上层隔离。首先我们可以从数据手册上可以看到虽然外部GPIO中断线有16个,但是,中断入口只有7个,如下图所示。
因此可以设置一个函数7个大小的函数指针数组。
如:
typedef void (*GPIO_EventCallback)(uint32_t gpioNum);
#define GPIO_INT_NUM 7
GPIO_EventCallback g_gpioCallbackArray[GPIO_INT_NUM] = {(GPIO_EventCallback)NULL};
在每个真正的中断入口函数中,来执行这个回调函数(gpioCallbackArray[X])。以EXTI0_IRQHandler为例。
如下,其他中断入口类同。
void EXTI0_IRQHandler(void)
{
uint32_t tmpGPIONum = GPIO_GetPendingExtIntGPIONum(0);
if (g_gpioCallbackArray[0])
{
g_gpioCallbackArray[0](tmpGPIONum);
}
GPIO_ClearPendingExtInt(tmpGPIONum);
}
而gpioCallbackArray[X]中的内容,便是由配置注册而来。如下的注册函数。
int32_t GPIO_SetEventCallback(uint32_t gpioNum, const GPIO_EventCallback callback)
{
uint8_t callbackIndex = 0;
uint32_t tmpGPIOGroupInternalNum = gpioNum % GPIO_ONE_GROUP_NUM;
if (gpioNum > MAX_PIN_NUM)
{
return -1;
}
/* set gpio callback function pointer */
if (tmpGPIOGroupInternalNum > 9u)
{
callbackIndex = 6;
}
else if (tmpGPIOGroupInternalNum > 4u)
{
callbackIndex = 5;
}
else
{
callbackIndex = tmpGPIOGroupInternalNum;
}
g_gpioCallbackArray[callbackIndex] = callback;
return 0;
}
总结下这种程序的调用方式:
4 GPIO唤醒与中断的差别之处
在调试程序的时候发现,在MCU没有睡眠时,如果开启GPIO中断,中断挂起标志位是会置1的,但是如果在MCU睡眠之后,通过配置的中断IO口来唤醒,发现中断标志位不会置1,但是此时还是可以唤醒的。因此如果是唤醒判断的话,需要额外的添加一个条件,才能清除的知道中断进去的原因是什么。
另外本程序因为LIN的引脚与另外一个唤醒引脚共用同个中断入口,因此后面不得将LIN总线的普通IO口唤醒更改了LIN的模块唤醒。其中LIN模块自带模块的唤醒状态机。在MCU没有睡眠时,唤醒LIN会让LIN模块的唤醒标志位置1,但是当MCU睡眠后,LIN总线的唤醒,能够让MCU唤醒但是不会置为LIN模块的唤醒标志的。
5 输出时IO口上下拉电阻配置
常规的理解端口的配置大概分为,浮空输入,上拉输入,下拉输入,推挽输出,开楼输出。但是在输出的时候配置成上下拉回是什么状态呢?可以明确的是,配置成输出,端口的上下拉电阻还是可以配置的(该单片机)。
可以看手册中的描述如下:
所有GPIO 引脚都具有弱内部上拉和下拉电阻,可以激活或不激活,具体取决于GPIOx_PU 和GPIOx_PD 寄存器中的值。因此与输入输出无关,都可以配置上下拉电阻,因此在功耗调试的时候需要留意。不要认为无影响,随便配置上下拉。