STM32——中断

中断简介

中断定义 CPU执行程序中,由于发生了某种随机的事件(外部或内部),引起CPU暂时 中断正在运行的程序,转去执行一段特殊的服务程序(称为中断服务程序或 中断处理程序),以处理该事件,该事件处理完后又返回被中断的程序继续 执行,这一过程称为中断 对于单片机来说

  1. 中断事件发生
  2. 产生中断信号
  3. 单片机接收到中断信号,停止执行当前函数,跳转至中断处理函数
  4. 执行完中断处理函数后,单片机自动跳转回主函数执行被中断处

NVIC

NVIC是嵌套向量中断控制器,控制着整个芯片中断相关的功能,它跟内核紧密耦合,是内核里面的一个外设。同时掌控中断优先级和中断向量表。

中断流程

  1. 请求挂起寄存器的改变由外设导致
  2. 中断信号传递至NVIC,然后NVIC查找对应中断向量,执行中断处理函数
  3. 另外中断处理函数都需要去清除请求挂起寄存器对应位,避免中断处理函数被重复调用

HAL库的中断处理及回调函数实现流程

首先是外设发出中断请求信号,NVIC接收到后会根据中断向量表查询对应中断处理函数的入口,在HAL中是对应外设的外设number_IRQHandler,然后再进入HAL库统一的外设中断处理函数HAL_外设_IRQHandler,再调用对应的回调函数

NVIC嵌套向量中断控制器

NVIC会一直检测某一个中断线是否处于激活状态,当中断处理函数运行完成后,需要在中断处理函数中将请求挂起寄存器对应的位清除为0,避免NVIC一直检测到1,一直重复执行中断处理函数,而这一部分代码,在HAL库写的总IRQHandler中已经完成。

另外,NVIC对于中断还有中断优先级的设置,stm32f103c8t6中断优先级由NVIC的优先级寄存器里的4位决定。中断优先级分为抢占优先级和响应优先级(它们一起用这4位)

  1. 抢占优先级的级别越高,越能够抢占中断通道,优先执行中断  2. 响应优先级的级别越高,可以优先排队。
  2. 中断的优先级,都是数字越小,优先级越高

EXTI外部中断

EXTI外部中断实现

EXTI 可分为两大部分功能,一个是产生中断,另一个是产生事件,这两个功能从硬件上就有所不同。我们的主要关注点是中断。

  1. 边沿检测电路用来检测输入的电平信号的高低电平的转换,同时会根据 上升沿/下降沿触发选择寄存器来决定是否向后产生高电平信号
  2. 软件中断事件寄存器使用软件来模拟产生一个中断
  3. 请求挂起寄存器接收到高电平后会将对应通道的位置1(例如EXTI_9产生高电平进入请求挂起寄存器,则该寄存器会将第9位置1)
  4. 最后上面的线路就会进入NVIC(嵌套向量中断控制器)
  5. 下面的线路,脉冲发生器作为事件信号送到对应外设,这个脉冲信号可以给其他外设电路使用,比如定时器TIM、模拟数字转换器ADC等等,这样的脉冲信号一般用来触发TIM或者ADC开始转换 产生中断线路目的是把输入信号输入到NVIC,进一步会运行中断服务函数,实现功能,这样是软件级的。 产生事件线路目的就是传输一个脉冲信号给其他外设使用,并且是电路级别的信号传输,属于硬件级的。

调用流程

EXTI1为例,下降沿触发,当出现下降沿时,就会触发EXTI5的外部中断,进入EXTI9_5_IRQHandler,在EXTI9_5_IRQHandler中调用HAL_GPIO_EXTI_IRQHandler函数统一处理外部中断,在该函数中对挂起寄存器进行清0操作,并调用HAL_GPIO_EXTI_Callback中断回调函数

按键实现外部中断控制小灯亮灭

Cube配置

EXTI配置

打开中断

配置的是下降沿触发,检测下降沿,上拉模式

配置参数解释

  1. 上升沿触发:指的是当引脚电平从低电平(0)转变为高电平(1)时,触发事件
  2. 下降沿触发:指的是当引脚电平从高电平(1)转变为低电平(0)时,触发事件
  3. 双边沿触发:指的是当引脚的电平发生任何变化(即从低电平到高电平或从高电平到低电平)时,都会触发事件
  4. 上拉:指使引脚在未连接(悬空)时默认为 高电平(1)
  5. 下拉:指使引脚在未连接(悬空)时默认为 低电平(0)
GPIO配置

推挽模式,初始输出为Low低电平

按键消抖

方式1
1
2
3
4
5
6
7
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) {//外部中断回调函数
    if( GPIO_Pin == GPIO_PIN_5 ) {
	    //HAL_Delay(10);
	    for( int i = 0 ; i <= 72000*100 ; ++ i );//空循环延时
	    HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_2);
    }
}
方式2
1
2
3
4
5
6
7
8
9
int lastTick = 0 ;
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) {//外部中断回调函数
	if( GPIO_Pin == GPIO_PIN_5 ) {
		if( HAL_GetTick()-lastTick > 150 ) {
			HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_2);
			lastTick = HAL_GetTick();
		}
	}
}

代码实现

1
2
3
4
5
6
7
8
9
int lastTick = 0 ;
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) {//外部中断回调函数
	if( GPIO_Pin == GPIO_PIN_5 ) {
		if( HAL_GetTick()-lastTick > 150 ) {
			HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_2);
			lastTick = HAL_GetTick();
		}
	}
}

TIM定时器定时中断

f103c8t6中,TIM1是高级定时器,TIM2TIM3TIM4是通用计时器 下面例子中配置的是TIM1,实现每秒翻转1次小灯亮灭

调用流程

触发更新中断后进入更新中断服务函数TIM1_UP_IRQHandler,其中调用HAL_TIM_IRQHandler,在该函数中调用了HAL_TIM_PeriodElapsedCallback回调函数

CubeMX参数配置

使用内部时钟模式

参数配置如下,实现1s触发一次定时器更新中断

  1. Prescaker:预分频,实际分频数是number+1
  2. Counter Mode:计数模式设置
  3. Counter Period:自动重装载计数器计数值
  4. Internal Clock Division:内部时钟预分频
  5. Repetiton Counter:重复计数器 (RCR -8 bits),属于高级控制寄存器专用寄存器位,利用它可以非常容易控制输出 PWM 的个数,与内部时钟模式无关
  6. auto-reload preload:是否开启影子寄存器模式

还需要开启定时器更新中断

代码实现

用的PA2GPIO输出

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) {
	if( htim == &htim1 ) {
		HAL_GPIO_TogglePin(GPIOA,GPIO_PIN_2);
	}
}

int main(void)
{
  /* USER CODE BEGIN 1 */
  /* USER CODE END 1 */
  /* MCU Configuration--------------------------------------------------------*/
  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();
  /* USER CODE BEGIN Init */
  /* USER CODE END Init */
  /* Configure the system clock */
  SystemClock_Config();
  /* USER CODE BEGIN SysInit */
  /* USER CODE END SysInit */
  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_TIM1_Init();
  /* USER CODE BEGIN 2 */
	HAL_TIM_Base_Start_IT(&htim1);
  /* USER CODE END 2 */
  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

USART中断

USART的轮询模式会阻塞程序运行,浪费CPU资源

轮询模式

CPU不断查询发送数据寄存器或者接受数据寄存器导致程序的阻塞

发送

UART底层有发送移位寄存器和发送数据寄存器,发送数据时,需要CPU将发送数据寄存器的数据移动至发送移位寄存器,然后UART按照指定的波特率发送数据,CPU则不断查询数据发送寄存器是否空,如果空则需要移动数据进来

接收

UART底层有接收移位寄存器和接收数据寄存器,接收数据时,UART按照指定的波特率接收数据至接收移位寄存器,然后数据会存储在接收数据寄存器,CPU则不断查询数据接收寄存器是否有数据,如果有数据,CPU会把寄存器的数据移动至我们用来接收数据的变量的内存里

HAL_UART_Receive执行完成,我们就可以知道数据接收完成

中断模式

底层中断实现数据每传送一字节,召回CPU继续运输数据,不需要我们考虑,HAL库底层已经帮我们实现 上层中断实现数据接收完成后的数据处理,通过中断回调函数实现,需要我们自己设计

发送

CPU将数据送入 发送数据寄存器,然后就去执行其他代码,当发送移位寄存器的数据发送后,发送数据寄存器空,会触发发送数据寄存器空中断,CPU再次将数据送入 发送数据寄存器,然后又去执行其他代码,所有数据发送完成后会触发 传送完成中断,调用HAL_UART_TxCpltCallback中断回调函数

接收

CPU在处理其他代码,接收移位寄存器将一帧数据移动至 接受数据寄存器后,会触发 接收数据寄存器非空中断,CPU会回来将 接收数据寄存器 的数据移动至我们用来接收数据的变量的内存里,然后又去执行其他代码,所有数据完成后会调用_HAL_UART_RxCpltCallback_中断回调函数处理接收的数据 而执行HAL_UART_Receive_IT后,我们不能直接对数据继续处理,数据还没接收完成,因为CPU不会进入阻塞态,会去执行其他代码,需要通过HAL_UART_RxCpltCallback回调函数处理接收的数据

experience
使用 Hugo 构建
主题 StackJimmy 设计