单片机问题总结:外设初始化必须放在时钟初始化之后
问题复现
最近配置IO口复用为EXTI外部中断功能,发现外部中断进不去,代码如下
static void UltrasonicGpioConfig(void)
{
GPIO_InitTypeDef GpioInitStructure;
RCC_APB2PeriphClockCmd(ULTRASONIC_GPIO_CLK | RCC_APB2Periph_AFIO, ENABLE);
GpioInitStructure.GPIO_Pin = ECHO_PIN;
GpioInitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(ECHO_PORT, &GpioInitStructure);
GpioInitStructure.GPIO_Pin = TRIG_PIN;
GpioInitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GpioInitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(TRIG_PORT, &GpioInitStructure);
}
static void UltrasonicModExtiConfig(void)
{
EXTI_InitTypeDef EXTIInitStructure;
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource1);
EXTIInitStructure.EXTI_Line = EXTI_Line1;
EXTIInitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTIInitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
EXTIInitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTIInitStructure);
}
UltrasonicModExtiConfig(); /**< 因为GPIO_EXTILineConfig()函数需要用到AFIO功能, 一定要放到AFIO时钟初始化的后面 */
UltrasonicGpioConfig(); /**< AFIO时钟初始化在这里 */
原因总结
原因就是 在外部中断初始化中GPIO_EXTILineConfig()函数需要用到AFIO功能,他在此配置了AFIO功能的寄存器,但是ARM的芯片,外设通常都是给了时钟后才能设置它的寄存器(即才能使用这个外设)。STM32、LPC1XXX等等都是这样,这么做的目的是为了省电,使用了所谓时钟门控的技术。寄存器是基于触发器的,触发器的赋值是一定需要时钟的,而寄存器的时钟是由总线时钟提供的,就是说没有总线时钟的话,你给寄存器值它是不会读入的。
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)));
}
错误改正
UltrasonicGpioConfig(); /**< AFIO时钟初始化在这里 */
UltrasonicModExtiConfig(); /**< 因为GPIO_EXTILineConfig()函数需要用到AFIO功能, 一定要放到AFIO时钟初始化的后面 */
**< 因为GPIO_EXTILineConfig()函数需要用到AFIO功能, 一定要放到AFIO时钟初始化的后面 */
可正常进入外部中断,超声波模块读取数据成功