STM32F429 外部中断边沿
STM32F429 External Interrupt Edge
我正在使用 STM32F429I-Discovery 板,该板有一个连接到 PA0 的按钮,而 PA0 又连接到外部中断线 0 (EXTI0)。
使用 HAL 库,我可以使用外部中断在下降沿或上升沿切换 LED。例如,LED 要么在我按下按钮后立即改变状态,要么在我松开按钮后才改变状态。
我想做的是在上升沿中断,启动定时器,然后再次在下降沿中断,停止定时器。我不知道如何做到这一点?
还有一个选项可以在上升沿和下降沿触发。我不知道是否应该只有一个中断,然后我弄清楚它是上升沿还是下降沿(可能通过直接访问寄存器),或者是否应该有两个配置的中断 - 一个作为上升沿,一个作为下降沿边缘?
下面是外部中断代码;首先将 GPIO 设置为外部中断,然后检测中断,然后处理中断(回调)。
static void EXTILine0_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
/* Enable GPIOA clock */
__HAL_RCC_GPIOA_CLK_ENABLE();
/* Configure PA0 pin as input floating */
GPIO_InitStructure.Mode = GPIO_MODE_IT_FALLING;
GPIO_InitStructure.Pull = GPIO_NOPULL;
GPIO_InitStructure.Pin = GPIO_PIN_0;
HAL_GPIO_Init(GPIOA, &GPIO_InitStructure);
/* Enable and set EXTI Line0 Interrupt to the lowest priority */
HAL_NVIC_SetPriority(EXTI0_IRQn, 2, 0);
HAL_NVIC_EnableIRQ(EXTI0_IRQn);
}
/* Clears the interrupt after calling this I think */
void EXTI0_IRQHandler(void)
{
HAL_GPIO_EXTI_IRQHandler(KEY_BUTTON_PIN);
}
/**
* @brief EXTI line detection callbacks
* @param GPIO_Pin: Specifies the pins connected EXTI line
* @retval None
*/
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
if(GPIO_Pin == KEY_BUTTON_PIN)
{
/* Toggle LED3 */
BSP_LED_Toggle(LED3);
}
}
有人可以指出我如何才能做到这一点吗?
首先,您必须设置一个定时器,例如
TIM_HandleTypeDef htim1;
void TIM1_Init(void)
{
TIM_ClockConfigTypeDef sClockSourceConfig;
TIM_MasterConfigTypeDef sMasterConfig;
htim1.Instance = TIM1;
htim1.Init.Prescaler = 71;
htim1.Init.CounterMode = TIM_COUNTERMODE_UP;
htim1.Init.Period = 65535;
htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim1.Init.RepetitionCounter = 0;
HAL_TIM_Base_Init(&htim1);
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
HAL_TIM_ConfigClockSource(&htim1, &sClockSourceConfig);
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
HAL_TIMEx_MasterConfigSynchronization(&htim1, &sMasterConfig);
}
代码取自STM32F1,可能需要稍微适应一下,看看HAL handbook。
上升沿和下降沿的中断是相同的,因此您必须在中断处理程序中检查引脚的状态。
开始计时
HAL_TIM_Base_Start(&htim1);
并停止
HAL_TIM_Base_Stop(&htim1);
计数器值存储在
TIM1->CNT
你要找的叫"input capture",可以直接用定时器实现,不需要外部中断。在 STM32F429 上,PA0 内部映射到定时器 2 通道 1。
sConfigIC 结构负责处理与输入捕获相关的配置。
初始化看起来像这样:
/* TIM2 init function */
void MX_TIM2_Init(void)
{
TIM_ClockConfigTypeDef sClockSourceConfig;
TIM_MasterConfigTypeDef sMasterConfig;
TIM_IC_InitTypeDef sConfigIC;
/* Peripheral clock enable */
__TIM2_CLK_ENABLE();
/* Peripheral interrupt init*/
HAL_NVIC_SetPriority(TIM2_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(TIM2_IRQn);
/**TIM2 GPIO Configuration
PA0/WKUP ------> TIM2_CH1
*/
GPIO_InitStruct.Pin = GPIO_PIN_0;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_LOW;
GPIO_InitStruct.Alternate = GPIO_AF1_TIM2;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
htim2.Instance = TIM2;
htim2.Init.Prescaler = 0;
htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
htim2.Init.Period = 0xFFFFFFFF;
htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
HAL_TIM_Base_Init(&htim2);
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig);
HAL_TIM_IC_Init(&htim2);
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig);
/* Input capture stuff HERE
Change polarity as needed */
sConfigIC.ICPolarity = TIM_INPUTCHANNELPOLARITY_BOTHEDGE;
sConfigIC.ICSelection = TIM_ICSELECTION_DIRECTTI;
sConfigIC.ICPrescaler = TIM_ICPSC_DIV1;
sConfigIC.ICFilter = 0;
HAL_TIM_IC_ConfigChannel(&htim2, &sConfigIC, TIM_CHANNEL_1);
}
还有中断函数:
/* IRQ */
void TIM2_IRQHandler(void)
{
// Check for interrupt flags here
}
在中断中,您必须检查 CC1IF 标志。计时器值存储在称为 CCR1 的捕获和比较寄存器中。
/编辑
不要忘记启动定时器和输入捕获通道:
HAL_TIM_IC_Start_IT(&htim2, TIM_CHANNEL_1);
这会启用定时器、相应的输入捕获通道和中断。
一些背景知识:使用 STM32F429I,下面的代码显示您按下蓝色用户按钮的时间,计数以毫秒为单位。 PCB 有一个硬件去抖动电路,所以我能得到的最快响应大约是 50 毫秒。
如前所述,PA0 连接到 EXTILine0。
我将 PA0 线路设置为在上升沿和下降沿中断。
static void EXTILine0_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
/* Enable GPIOA clock */
__HAL_RCC_GPIOA_CLK_ENABLE();
/* Configure PA0 pin as input floating */
GPIO_InitStructure.Mode = GPIO_MODE_IT_RISING_FALLING;
GPIO_InitStructure.Pull = GPIO_NOPULL;
GPIO_InitStructure.Pin = GPIO_PIN_0;
HAL_GPIO_Init(GPIOA, &GPIO_InitStructure);
/* Enable and set EXTI Line0 Interrupt to the lowest priority */
HAL_NVIC_SetPriority(EXTI0_IRQn, 2, 0);
HAL_NVIC_EnableIRQ(EXTI0_IRQn);
}
然后发生中断时,我读取当前存储在 HAL_GetTick() 中的计数数量,此函数每 1 毫秒计时一次。我还读取引脚是高电平还是低电平以确定中断是在下降沿还是上升沿。
uint16_t beginCount;
uint16_t stopCount;
void EXTI0_IRQHandler(void)
{
uint16_t var;
var = HAL_GetTick();
uint16_t calcCount = 0;
unsigned char buffer[10];
BSP_LCD_Clear(LCD_COLOR_WHITE);
// The Pin Goes high when the pushbutton is pressed.
if (HAL_GPIO_ReadPin(GPIOA, KEY_BUTTON_PIN) == 0x01)
{
beginCount = 0;
beginCount = var;
BSP_LCD_SetTextColor(LCD_COLOR_GREEN);
BSP_LCD_DisplayStringAtLine(6, "Rising Edge" );
}
else
{
stopCount = 0;
stopCount = var;
BSP_LCD_SetTextColor(LCD_COLOR_RED);
BSP_LCD_DisplayStringAtLine(7, (uint8_t*)"Falling Edge");
// Calculate Counts and covert to seconds - What if the counter overflows?
calcCount = stopCount - beginCount;
sprintf(buffer, "%d", calcCount); // Convert the integer to string and put it in variable buffer
BSP_LCD_DisplayStringAtLine(8, (&buffer) ); // Display the value stored at buffer's location
}
HAL_GPIO_EXTI_IRQHandler(KEY_BUTTON_PIN);
}
最后,中断回调触发并仅切换板上的 LED。
计数器只能达到 65 秒左右,之后它会溢出,我的 'calculated' 时间就会不正确。这种方法适用于我打算用它做的事情。我想以几毫秒的精度测量 20-300 毫秒。如果定时器在两次测量之间溢出,我仍然需要捕捉。
这种方法有什么根本性的错误吗?我对 C 不是很有经验,对 STM32 也完全没有。
我正在使用 STM32F429I-Discovery 板,该板有一个连接到 PA0 的按钮,而 PA0 又连接到外部中断线 0 (EXTI0)。
使用 HAL 库,我可以使用外部中断在下降沿或上升沿切换 LED。例如,LED 要么在我按下按钮后立即改变状态,要么在我松开按钮后才改变状态。
我想做的是在上升沿中断,启动定时器,然后再次在下降沿中断,停止定时器。我不知道如何做到这一点?
还有一个选项可以在上升沿和下降沿触发。我不知道是否应该只有一个中断,然后我弄清楚它是上升沿还是下降沿(可能通过直接访问寄存器),或者是否应该有两个配置的中断 - 一个作为上升沿,一个作为下降沿边缘?
下面是外部中断代码;首先将 GPIO 设置为外部中断,然后检测中断,然后处理中断(回调)。
static void EXTILine0_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
/* Enable GPIOA clock */
__HAL_RCC_GPIOA_CLK_ENABLE();
/* Configure PA0 pin as input floating */
GPIO_InitStructure.Mode = GPIO_MODE_IT_FALLING;
GPIO_InitStructure.Pull = GPIO_NOPULL;
GPIO_InitStructure.Pin = GPIO_PIN_0;
HAL_GPIO_Init(GPIOA, &GPIO_InitStructure);
/* Enable and set EXTI Line0 Interrupt to the lowest priority */
HAL_NVIC_SetPriority(EXTI0_IRQn, 2, 0);
HAL_NVIC_EnableIRQ(EXTI0_IRQn);
}
/* Clears the interrupt after calling this I think */
void EXTI0_IRQHandler(void)
{
HAL_GPIO_EXTI_IRQHandler(KEY_BUTTON_PIN);
}
/**
* @brief EXTI line detection callbacks
* @param GPIO_Pin: Specifies the pins connected EXTI line
* @retval None
*/
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
if(GPIO_Pin == KEY_BUTTON_PIN)
{
/* Toggle LED3 */
BSP_LED_Toggle(LED3);
}
}
有人可以指出我如何才能做到这一点吗?
首先,您必须设置一个定时器,例如
TIM_HandleTypeDef htim1;
void TIM1_Init(void)
{
TIM_ClockConfigTypeDef sClockSourceConfig;
TIM_MasterConfigTypeDef sMasterConfig;
htim1.Instance = TIM1;
htim1.Init.Prescaler = 71;
htim1.Init.CounterMode = TIM_COUNTERMODE_UP;
htim1.Init.Period = 65535;
htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim1.Init.RepetitionCounter = 0;
HAL_TIM_Base_Init(&htim1);
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
HAL_TIM_ConfigClockSource(&htim1, &sClockSourceConfig);
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
HAL_TIMEx_MasterConfigSynchronization(&htim1, &sMasterConfig);
}
代码取自STM32F1,可能需要稍微适应一下,看看HAL handbook。
上升沿和下降沿的中断是相同的,因此您必须在中断处理程序中检查引脚的状态。
开始计时
HAL_TIM_Base_Start(&htim1);
并停止
HAL_TIM_Base_Stop(&htim1);
计数器值存储在
TIM1->CNT
你要找的叫"input capture",可以直接用定时器实现,不需要外部中断。在 STM32F429 上,PA0 内部映射到定时器 2 通道 1。
sConfigIC 结构负责处理与输入捕获相关的配置。
初始化看起来像这样:
/* TIM2 init function */
void MX_TIM2_Init(void)
{
TIM_ClockConfigTypeDef sClockSourceConfig;
TIM_MasterConfigTypeDef sMasterConfig;
TIM_IC_InitTypeDef sConfigIC;
/* Peripheral clock enable */
__TIM2_CLK_ENABLE();
/* Peripheral interrupt init*/
HAL_NVIC_SetPriority(TIM2_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(TIM2_IRQn);
/**TIM2 GPIO Configuration
PA0/WKUP ------> TIM2_CH1
*/
GPIO_InitStruct.Pin = GPIO_PIN_0;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_LOW;
GPIO_InitStruct.Alternate = GPIO_AF1_TIM2;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
htim2.Instance = TIM2;
htim2.Init.Prescaler = 0;
htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
htim2.Init.Period = 0xFFFFFFFF;
htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
HAL_TIM_Base_Init(&htim2);
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig);
HAL_TIM_IC_Init(&htim2);
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig);
/* Input capture stuff HERE
Change polarity as needed */
sConfigIC.ICPolarity = TIM_INPUTCHANNELPOLARITY_BOTHEDGE;
sConfigIC.ICSelection = TIM_ICSELECTION_DIRECTTI;
sConfigIC.ICPrescaler = TIM_ICPSC_DIV1;
sConfigIC.ICFilter = 0;
HAL_TIM_IC_ConfigChannel(&htim2, &sConfigIC, TIM_CHANNEL_1);
}
还有中断函数:
/* IRQ */
void TIM2_IRQHandler(void)
{
// Check for interrupt flags here
}
在中断中,您必须检查 CC1IF 标志。计时器值存储在称为 CCR1 的捕获和比较寄存器中。
/编辑
不要忘记启动定时器和输入捕获通道:
HAL_TIM_IC_Start_IT(&htim2, TIM_CHANNEL_1);
这会启用定时器、相应的输入捕获通道和中断。
一些背景知识:使用 STM32F429I,下面的代码显示您按下蓝色用户按钮的时间,计数以毫秒为单位。 PCB 有一个硬件去抖动电路,所以我能得到的最快响应大约是 50 毫秒。
如前所述,PA0 连接到 EXTILine0。 我将 PA0 线路设置为在上升沿和下降沿中断。
static void EXTILine0_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
/* Enable GPIOA clock */
__HAL_RCC_GPIOA_CLK_ENABLE();
/* Configure PA0 pin as input floating */
GPIO_InitStructure.Mode = GPIO_MODE_IT_RISING_FALLING;
GPIO_InitStructure.Pull = GPIO_NOPULL;
GPIO_InitStructure.Pin = GPIO_PIN_0;
HAL_GPIO_Init(GPIOA, &GPIO_InitStructure);
/* Enable and set EXTI Line0 Interrupt to the lowest priority */
HAL_NVIC_SetPriority(EXTI0_IRQn, 2, 0);
HAL_NVIC_EnableIRQ(EXTI0_IRQn);
}
然后发生中断时,我读取当前存储在 HAL_GetTick() 中的计数数量,此函数每 1 毫秒计时一次。我还读取引脚是高电平还是低电平以确定中断是在下降沿还是上升沿。
uint16_t beginCount;
uint16_t stopCount;
void EXTI0_IRQHandler(void)
{
uint16_t var;
var = HAL_GetTick();
uint16_t calcCount = 0;
unsigned char buffer[10];
BSP_LCD_Clear(LCD_COLOR_WHITE);
// The Pin Goes high when the pushbutton is pressed.
if (HAL_GPIO_ReadPin(GPIOA, KEY_BUTTON_PIN) == 0x01)
{
beginCount = 0;
beginCount = var;
BSP_LCD_SetTextColor(LCD_COLOR_GREEN);
BSP_LCD_DisplayStringAtLine(6, "Rising Edge" );
}
else
{
stopCount = 0;
stopCount = var;
BSP_LCD_SetTextColor(LCD_COLOR_RED);
BSP_LCD_DisplayStringAtLine(7, (uint8_t*)"Falling Edge");
// Calculate Counts and covert to seconds - What if the counter overflows?
calcCount = stopCount - beginCount;
sprintf(buffer, "%d", calcCount); // Convert the integer to string and put it in variable buffer
BSP_LCD_DisplayStringAtLine(8, (&buffer) ); // Display the value stored at buffer's location
}
HAL_GPIO_EXTI_IRQHandler(KEY_BUTTON_PIN);
}
最后,中断回调触发并仅切换板上的 LED。 计数器只能达到 65 秒左右,之后它会溢出,我的 'calculated' 时间就会不正确。这种方法适用于我打算用它做的事情。我想以几毫秒的精度测量 20-300 毫秒。如果定时器在两次测量之间溢出,我仍然需要捕捉。
这种方法有什么根本性的错误吗?我对 C 不是很有经验,对 STM32 也完全没有。