GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource14)。将GPIOB_14开启外部中断复用。先看下手册。
可以看到,这是第四个外部中断配置寄存器,所以前面还有3个寄存器。因为,一共有16条中断线,对应16个GPIO_Pin。AFIO_EXTICR1配置Pin0-Pin3,AFIO_EXTICR2配置Pin4-Pin7,AFIO_EXTICR3配置Pin8-Pin11,AFIO_EXTICR4配置Pin12-15。每个寄存器的高16位都是保留的,而低16位分为4组,从低到高,每四位配置一个中断线,也就是对应Pin口,而这4位根据取不同的值就可以决定选择哪个端口,端口即ABCD等。
比如要让GPIOA的Pin_7引脚口作为外部中断引脚,则要配置AFIO_EXTICR2,配置对应的12到15位,值设为0000,即配置成功。这只是直接操作寄存器,比较简单,如果是通过库函数配置呢?下面开始研究GPIO_EXTILineConfig内部的代码。
void GPIO_EXTILineConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource)
{
uint32_t tmp = 0x00;
/* Check the parameters */
assert_param(IS_GPIO_EXTI_PORT_SOURCE(GPIO_PortSource));
assert_param(IS_GPIO_PIN_SOURCE(GPIO_PinSource));
tmp = ((uint32_t)0x0F) << (0x04 * (GPIO_PinSource & (uint8_t)0x03));
AFIO->EXTICR[GPIO_PinSource >> 0x02] &= ~tmp;
AFIO->EXTICR[GPIO_PinSource >> 0x02] |= (((uint32_t)GPIO_PortSource) << (0x04 * (GPIO_PinSource & (uint8_t)0x03)));
}
上面就是GPIO_EXTILineConfig内部的代码和对应参数的宏定义。
假设要配置GPIOB的Pin14为中断引脚。 GPIO_PortSourceGPIOB就是((uint8_t)0x01),GPIO_PinSource14就是((uint8_t)0x0E)。先计算tmp,tmp=0x0F<<(0x04*(0x0E&0x03))=0x0F00下一步的AFIO->EXTICR[GPIO_PinSource >> 0x02] &= ~tmp啥意思,其实这个箭头是访问结构体成员,这个方括号表示访问数组的某个元素,~表示按位取反。
如上图,AFIO是宏定义 ,将AFIO_BASE即AFIO基地址强转为AFIO_TypeDef *即AFIO_TypeDef类型的指针。也就是说,AFIO_TypeDef *指向了AFIO_TypeDef结构体,也就是AFIO指向了AFIO_TypeDef结构体,这才有了AFIO->EXTICR[]这个写法。
好了,继续计算AFIO->EXTICR[GPIO_PinSource >> 0x02] &= ~tmp。即AFIO->EXTICR[0x0E>>0x02]&=0xFF0F。得到AFIO->EXTICR[3]=AFIO->EXTICR[3]&0xFF0F,很明显,是要清除AFIO_EXTICR4的14中断线的四个bit位。因为是数组(下标从0开始),所以AFIO->EXTICR[3]表示第四个寄存器。
下一步的AFIO->EXTICR[GPIO_PinSource >> 0x02] |= (((uint32_t)GPIO_PortSource) << (0x04 * (GPIO_PinSource & (uint8_t)0x03)))。即AFIO->EXTICR[3]=AFIO->EXTICR[3] | (0x01<<(0x04*(0x0E&0x03))),结果为AFIO->EXTICR[3]=AFIO->EXTICR[3] | 0x0100,也就是将0001写入AFIO_EXTICR4的第8-11位,刚好是GPIOB的Pin14。