实际上15~10号外部中断用的是同一个中断处理函数,也就是如果再用一个按钮,最后执行的也是HAL_GPIO_EXTI_Cal1back回调函数。该如何区分是哪个引脚发生中断了?
时间: 2024-10-24 22:12:23 浏览: 33
在STM32中,虽然15号和10号外设中断共用一个通用中断服务程序(例如`HAL_GPIO_EXTI_IRQHandler`),但实际上每个 EXTI 引脚都有其对应的中断标志位(Interrupt Flag)。当按下不同的按钮时,会触发对应EXTI线的状态变化,比如GPIO_PIN_x(x为特定的引脚编号)。
为了区分是哪个引脚发生的中断,你需要在中断服务程序内部检查引发中断的具体标志位。例如,在`HAL_GPIO_EXTI_IRQHandler`里,你可以通过`__HAL_GPIO_EXTI_GET_IT_SOURCE(pin)`这个宏获取中断源,其中`pin`是你感兴趣的GPIO引脚。这将返回一个枚举值,表示是哪个引脚产生的中断。
这里是一个简单的示例:
```c
void HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin)
{
if (GPIO_Pin & EXTI_PIN_15) // 检查是否是15号引脚
{
// 处理15号引脚的中断
}
else if (GPIO_Pin & EXTI_PIN_10) // 检查是否是10号引脚
{
// 处理10号引脚的中断
}
// 其他引脚判断...
__HAL_GPIO_EXTI_CLEAR_FLAG(GPIO_Pin); // 清除当前中断标志
}
```
每次中断发生时,检查相应的`GPIO_Pin`即可确定是哪个引脚触发了中断。
相关问题
用stm32cube写stm32f103zet6带8个灯的流水灯,使用外部中断4个按钮分别控制开始,暂停,加速,减速主函数
好的,我可以为您提供一个基础的代码框架,请参考下面的代码:
```c
#include "stm32f1xx_hal.h"
#define LED_NUM 8
GPIO_TypeDef* LED_GPIO_PORT[LED_NUM] = {GPIOA, GPIOA, GPIOA, GPIOA, GPIOB, GPIOB, GPIOB, GPIOB};
const uint16_t LED_GPIO_PIN[LED_NUM] = {GPIO_PIN_0, GPIO_PIN_1, GPIO_PIN_2, GPIO_PIN_3, GPIO_PIN_0, GPIO_PIN_1, GPIO_PIN_10, GPIO_PIN_11};
uint8_t led_state[LED_NUM] = {0}; // 记录每个灯的状态,0 表示灭,1 表示亮
int main(void)
{
// 初始化 HAL 库
HAL_Init();
// 初始化时钟,使用 HSI 时钟源
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
HAL_RCC_OscConfig(&RCC_OscInitStruct);
// 初始化时钟,设置系统时钟为 HSI
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0);
// 初始化 GPIO 端口,将所有 LED 置为输出模式
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
for (int i = 0; i < LED_NUM; i++) {
GPIO_InitStruct.Pin = LED_GPIO_PIN[i];
HAL_GPIO_Init(LED_GPIO_PORT[i], &GPIO_InitStruct);
}
// 初始化外部中断
// TODO: 请根据实际情况修改
// ...
// 初始化定时器
// TODO: 请根据实际情况修改
// ...
// 等待开始信号
// TODO: 请根据实际情况修改
// ...
// 开始流水灯
// TODO: 请根据实际情况修改
// ...
while (1)
{
// TODO: 请根据实际情况修改
// ...
}
}
// 暂停流水灯
void pause_led() {
// TODO: 请根据实际情况修改
// ...
}
// 恢复流水灯
void resume_led() {
// TODO: 请根据实际情况修改
// ...
}
// 加速流水灯
void speed_up_led() {
// TODO: 请根据实际情况修改
// ...
}
// 减速流水灯
void speed_down_led() {
// TODO: 请根据实际情况修改
// ...
}
// TODO: 请根据实际情况添加外部中断处理函数和定时器中断处理函数
// ...
```
上面的代码中只给出了函数框架,具体的实现需要根据实际情况进行修改。其中:
- `LED_GPIO_PORT` 和 `LED_GPIO_PIN` 数组分别存储了每个 LED 的 GPIO 端口和引脚号。
- `led_state` 数组记录了每个 LED 的状态,0 表示灭,1 表示亮。
- `pause_led`、`resume_led`、`speed_up_led` 和 `speed_down_led` 函数分别实现了暂停、恢复、加速和减速流水灯的功能,需要根据实际情况进行修改。
- `main` 函数中初始化了 GPIO 端口、外部中断、定时器等硬件资源,并在流水灯开始前等待开始信号。
祝顺利!
用stm32cube写stm32f103zet6带8个灯的流水灯,使用外部中断4个按钮分别控制开始,暂停,加速,减速主函数具体代码
好的,下面是一个完整的代码示例,可以实现您所需的功能:
```c
#include "stm32f1xx_hal.h"
#define LED_NUM 8
GPIO_TypeDef* LED_GPIO_PORT[LED_NUM] = {GPIOA, GPIOA, GPIOA, GPIOA, GPIOB, GPIOB, GPIOB, GPIOB};
const uint16_t LED_GPIO_PIN[LED_NUM] = {GPIO_PIN_0, GPIO_PIN_1, GPIO_PIN_2, GPIO_PIN_3, GPIO_PIN_0, GPIO_PIN_1, GPIO_PIN_10, GPIO_PIN_11};
uint8_t led_state[LED_NUM] = {0}; // 记录每个灯的状态,0 表示灭,1 表示亮
int main(void)
{
// 初始化 HAL 库
HAL_Init();
// 初始化时钟,使用 HSI 时钟源
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
HAL_RCC_OscConfig(&RCC_OscInitStruct);
// 初始化时钟,设置系统时钟为 HSI
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0);
// 初始化 GPIO 端口,将所有 LED 置为输出模式
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
for (int i = 0; i < LED_NUM; i++) {
GPIO_InitStruct.Pin = LED_GPIO_PIN[i];
HAL_GPIO_Init(LED_GPIO_PORT[i], &GPIO_InitStruct);
}
// 初始化外部中断,使用 PA0、PA1、PA2、PA3 四个引脚
GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
// 将外部中断的优先级设置为最高
HAL_NVIC_SetPriority(EXTI0_IRQn, 0, 0);
HAL_NVIC_SetPriority(EXTI1_IRQn, 0, 0);
HAL_NVIC_SetPriority(EXTI2_IRQn, 0, 0);
HAL_NVIC_SetPriority(EXTI3_IRQn, 0, 0);
// 使能外部中断
HAL_NVIC_EnableIRQ(EXTI0_IRQn);
HAL_NVIC_EnableIRQ(EXTI1_IRQn);
HAL_NVIC_EnableIRQ(EXTI2_IRQn);
HAL_NVIC_EnableIRQ(EXTI3_IRQn);
// 初始化定时器 TIM2,用于定时更新 LED 的亮灭状态
TIM_HandleTypeDef htim2;
htim2.Instance = TIM2;
htim2.Init.Prescaler = 0;
htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
htim2.Init.Period = 999; // 1ms 定时器中断
htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
if (HAL_TIM_Base_Init(&htim2) != HAL_OK)
{
Error_Handler();
}
// 将定时器 TIM2 的中断优先级设置为最低
HAL_NVIC_SetPriority(TIM2_IRQn, 3, 0);
HAL_NVIC_EnableIRQ(TIM2_IRQn);
// 等待开始信号
while (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_SET);
// 开始流水灯
HAL_TIM_Base_Start_IT(&htim2);
while (1)
{
// TODO: 可以在这里添加一些其他的处理逻辑
}
}
// 暂停流水灯
void pause_led() {
HAL_TIM_Base_Stop_IT(&htim2); // 停止定时器更新 LED 的亮灭状态
}
// 恢复流水灯
void resume_led() {
HAL_TIM_Base_Start_IT(&htim2); // 开始定时器更新 LED 的亮灭状态
}
// 加速流水灯
void speed_up_led() {
TIM_HandleTypeDef htim2;
htim2.Instance = TIM2;
uint32_t new_period = htim2.Init.Period * 0.8; // 将定时器周期缩短 20%
if (new_period < 10) {
new_period = 10; // 限制最小周期为 10
}
htim2.Init.Period = new_period;
HAL_TIM_Base_Init(&htim2);
}
// 减速流水灯
void speed_down_led() {
TIM_HandleTypeDef htim2;
htim2.Instance = TIM2;
uint32_t new_period = htim2.Init.Period * 1.2; // 将定时器周期增加 20%
if (new_period > 2000) {
new_period = 2000; // 限制最大周期为 2000
}
htim2.Init.Period = new_period;
HAL_TIM_Base_Init(&htim2);
}
// 处理外部中断
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
switch (GPIO_Pin) {
case GPIO_PIN_0:
// 开始流水灯
HAL_TIM_Base_Start_IT(&htim2);
break;
case GPIO_PIN_1:
// 暂停流水灯
pause_led();
break;
case GPIO_PIN_2:
// 加速流水灯
speed_up_led();
break;
case GPIO_PIN_3:
// 减速流水灯
speed_down_led();
break;
default:
break;
}
}
// 定时器中断处理
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if (htim->Instance == TIM2) {
// 更新 LED 的亮灭状态
for (int i = 0; i < LED_NUM; i++) {
if (led_state[i] == 0) {
// 灭 -> 亮
led_state[i] = 1;
HAL_GPIO_WritePin(LED_GPIO_PORT[i], LED_GPIO_PIN[i], GPIO_PIN_SET);
} else {
// 亮 -> 灭
led_state[i] = 0;
HAL_GPIO_WritePin(LED_GPIO_PORT[i], LED_GPIO_PIN[i], GPIO_PIN_RESET);
}
}
}
}
```
在上面的代码中,我们使用了定时器 TIM2 来定时更新 LED 的亮灭状态。具体来说,定时器的周期为 1ms,每次定时器中断被触发时,我们遍历所有的 LED,将其状态取反。这样可以模拟出流水灯的效果。
在外部中断处理函数中,我们根据不同的引脚号来执行不同的操作,比如开始流水灯、暂停流水灯、加速流水灯、减速流水灯等。这些操作都可以通过调整定时器的周期来实现。
祝顺利!
阅读全文