IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 嵌入式 -> RT-Thread 中断管理(学习笔记) -> 正文阅读

[嵌入式]RT-Thread 中断管理(学习笔记)

本文参考自[野火EmbedFire]《RT-Thread内核实现与应用开发实战——基于STM32》,仅作为个人学习笔记。更详细的内容和步骤请查看原文(可到野火资料下载中心下载)

异常与中断的基本概念

异常

异常是指任何打断处理器正常执行,并且迫使处理器进入一个由有特权的特殊指令执行的事件。
异常通常可以分为两类:同步异常和异步异常。

由内部事件(相处理器指令运行产生的事件)引起的异常称为异步异常,例如造成被零除的算术运算引发的一个异常。

异步异常主要指由于外部异常源产生的异常,是一个由外部硬件装置产生的事件引起的异步异常。

同步异常事件是由于执行某些指令而从处理器内部产生的,而异步异常事件的来源是外部硬件装置。例如按下设备某个按钮产生的事件。它们的区别还在于,同步异常触发后,系统必须立即进行处理而不能够依然执行原有的程序指令步骤;而异步异常则可以延缓处理甚至忽略。

中断

中断属于异步异常。所谓中断是指中央处理器正在处理某件事的时候,外部发生了某一事件,请求 CPU 快速处理,CPU 暂停当前的工作,转而处理所发生的事件,处理完成后,再回到原来被中断的地方,继续原来的工作。

中断能打断线程的运行,无论该线程具有什么样的优先级,因此中断一般用于处理比较紧急的事件,而且只做简单处理。

RT-Thread 源码中有许多处临界段的地方,临界段虽然保护了关键代码的执行不被打断,但也影响系统的实时,任何使用了操作系统的中断响应都不会比裸机快。

RT-Thread 的中断管理支持:

  • 开/关中断
  • 恢复中断
  • 中断使能
  • 中断屏蔽

中断的介绍

与中断相关的硬件可以划分为三类:外设、中断控制器、CPU 本身。

外设:当外设需要请求 CPU 时,产生一个中断信号,该信号连接至中断控制器。

中断控制器:中断控制器是 CPU 众多外设中的一个,它一方面接收其它外设中断信号的输入,另一方面,它会发出中断信号给 CPU。可以通过对中断控制器编程实现对中断源的优先级、触发方式、打开和关闭源等设置操作。在 Cortex-M 系列控制器中常用的中断控制器是 NVIC(内嵌向量中断控制器 Nested Vectored Interrupt Controller)。

CPU:CPU 会响应中断源的请求,中断当前正在执行的线程,转而执行中断处理程序。NVIC 最多支持 240 个中断,每个中断最多 256 个优先级。

——原文

中断管理的运作机制

中断产生时,处理机将按如下的顺序执行:

  1. 保存当前处理机状态信息
  2. 载入异常或中断处理函数到 PC 寄存器
  3. 把控制权转交给处理函数并开始执行
  4. 当处理函数执行完成时,恢复处理器状态信息
  5. 从异常或中断中返回到前一个程序的执行点

中断发生的环境有两种情况:在线程的上下文中,在中断服务函数处理上下文中。

中断发生在线程的上下文中

线程在运行时,如果出现了中断,无论中断优先级多大,都会打断当前线程的执行,当中断服务函数处理完成后才会恢复线程的上下文环境,继续运行线程。

图片来源:《RT-Thread内核实现与应用开发实战》

在这里插入图片描述

中断发生在中断服务函数上下文中

在执行中断服务例程的过程中,如果有更高优先级的中断源触发中断,由于当前处于中断处理上下文环境中,根据不同的处理器架构可能有不同的处理方式,比如新的中断挂起等待,直到当前中断处理离开后再响应;或新的高优先级中断打断当前中断处理过程,直接去响应新的中断源。后面这种情况被称为中断嵌套。

图片来源:《RT-Thread内核实现与应用开发实战》

在这里插入图片描述

中断管理讲解

ARM Cortex-M 内核的中断不受 RT-Thread 管理,即中断的配置、使能以及中断服务函数等代码都需要我们手动编写。在中断服务函数中如果要使用 RT-Thread 的 IPC 通信机制,一般建议使用信号量、消息或事件标志组等标志事件,即只在中断里改变标志值,具体处理操作放到线程中执行。

ARM Cortex-M 内核支持中断嵌套功能,在处理低优先级中断时如果有一个高优先级的中断触发,处理器会打断当前运行的中断服务程序。Cortex-M 和 其他 ARM 内核的一个很大的区别是:Cortex-M 的中断嵌套里,被打断的中断服务程序的上下文会被保存在中断栈中(硬件寄存器),即由硬件管理,而其他 ARM 处理器一般都需要依赖软件来保存上下文。

另外,在 Cortex-M 处理器上,所有中断都采用中断向量表的方式来管理,当一个中断触发时,处理器会直接判断是哪个中断源,然后直接跳转到相应的固定位置执行。而在 ARM7、ARM9 中,一般是先跳转进入 IRQ 入口,然后再由软件进行判断是哪个中断源触发,获得了相应的终端服务函数入口地址后,再进行处理。

下面是 STM32F10x 的中断向量表:

__Vectors       DCD     __initial_sp               ; Top of Stack
                DCD     Reset_Handler              ; Reset Handler
                DCD     NMI_Handler                ; NMI Handler
                DCD     HardFault_Handler          ; Hard Fault Handler
                DCD     MemManage_Handler          ; MPU Fault Handler
                DCD     BusFault_Handler           ; Bus Fault Handler
                DCD     UsageFault_Handler         ; Usage Fault Handler
                DCD     0                          ; Reserved
                DCD     0                          ; Reserved
                DCD     0                          ; Reserved
                DCD     0                          ; Reserved
                DCD     SVC_Handler                ; SVCall Handler
                DCD     DebugMon_Handler           ; Debug Monitor Handler
                DCD     0                          ; Reserved
                DCD     PendSV_Handler             ; PendSV Handler
                DCD     SysTick_Handler            ; SysTick Handler

                ; External Interrupts
                DCD     WWDG_IRQHandler            ; Window Watchdog
                DCD     PVD_IRQHandler             ; PVD through EXTI Line detect
                DCD     TAMPER_IRQHandler          ; Tamper
                DCD     RTC_IRQHandler             ; RTC
                DCD     FLASH_IRQHandler           ; Flash
                DCD     RCC_IRQHandler             ; RCC
                DCD     EXTI0_IRQHandler           ; EXTI Line 0
                DCD     EXTI1_IRQHandler           ; EXTI Line 1
                DCD     EXTI2_IRQHandler           ; EXTI Line 2
                DCD     EXTI3_IRQHandler           ; EXTI Line 3
                DCD     EXTI4_IRQHandler           ; EXTI Line 4
                DCD     DMA1_Channel1_IRQHandler   ; DMA1 Channel 1
                DCD     DMA1_Channel2_IRQHandler   ; DMA1 Channel 2
                DCD     DMA1_Channel3_IRQHandler   ; DMA1 Channel 3
                DCD     DMA1_Channel4_IRQHandler   ; DMA1 Channel 4
                DCD     DMA1_Channel5_IRQHandler   ; DMA1 Channel 5
                DCD     DMA1_Channel6_IRQHandler   ; DMA1 Channel 6
                DCD     DMA1_Channel7_IRQHandler   ; DMA1 Channel 7
                DCD     ADC1_2_IRQHandler          ; ADC1 & ADC2
                DCD     USB_HP_CAN1_TX_IRQHandler  ; USB High Priority or CAN1 TX
                DCD     USB_LP_CAN1_RX0_IRQHandler ; USB Low  Priority or CAN1 RX0
                DCD     CAN1_RX1_IRQHandler        ; CAN1 RX1
                DCD     CAN1_SCE_IRQHandler        ; CAN1 SCE
                DCD     EXTI9_5_IRQHandler         ; EXTI Line 9..5
                DCD     TIM1_BRK_IRQHandler        ; TIM1 Break
                DCD     TIM1_UP_IRQHandler         ; TIM1 Update
                DCD     TIM1_TRG_COM_IRQHandler    ; TIM1 Trigger and Commutation
                DCD     TIM1_CC_IRQHandler         ; TIM1 Capture Compare
                DCD     TIM2_IRQHandler            ; TIM2
                DCD     TIM3_IRQHandler            ; TIM3
                DCD     TIM4_IRQHandler            ; TIM4
                DCD     I2C1_EV_IRQHandler         ; I2C1 Event
                DCD     I2C1_ER_IRQHandler         ; I2C1 Error
                DCD     I2C2_EV_IRQHandler         ; I2C2 Event
                DCD     I2C2_ER_IRQHandler         ; I2C2 Error
                DCD     SPI1_IRQHandler            ; SPI1
                DCD     SPI2_IRQHandler            ; SPI2
                DCD     USART1_IRQHandler          ; USART1
                DCD     USART2_IRQHandler          ; USART2
                DCD     USART3_IRQHandler          ; USART3
                DCD     EXTI15_10_IRQHandler       ; EXTI Line 15..10
                DCD     RTCAlarm_IRQHandler        ; RTC Alarm through EXTI Line
                DCD     USBWakeUp_IRQHandler       ; USB Wakeup from suspend
                DCD     TIM8_BRK_IRQHandler        ; TIM8 Break
                DCD     TIM8_UP_IRQHandler         ; TIM8 Update
                DCD     TIM8_TRG_COM_IRQHandler    ; TIM8 Trigger and Commutation
                DCD     TIM8_CC_IRQHandler         ; TIM8 Capture Compare
                DCD     ADC3_IRQHandler            ; ADC3
                DCD     FSMC_IRQHandler            ; FSMC
                DCD     SDIO_IRQHandler            ; SDIO
                DCD     TIM5_IRQHandler            ; TIM5
                DCD     SPI3_IRQHandler            ; SPI3
                DCD     UART4_IRQHandler           ; UART4
                DCD     UART5_IRQHandler           ; UART5
                DCD     TIM6_IRQHandler            ; TIM6
                DCD     TIM7_IRQHandler            ; TIM7
                DCD     DMA2_Channel1_IRQHandler   ; DMA2 Channel1
                DCD     DMA2_Channel2_IRQHandler   ; DMA2 Channel2
                DCD     DMA2_Channel3_IRQHandler   ; DMA2 Channel3
                DCD     DMA2_Channel4_5_IRQHandler ; DMA2 Channel4 & Channel5
__Vectors_End

RT-Thread 在 Cortex-M 处理器上的中断处理和裸机相同,当用户需要自定义中断服务程序时,只需要定义相同名称的函数来覆盖弱化符号即可。

中断管理实验

野火例程没有现成的带按键和串口的工程,需要我们自己来整理,

首先,将 bsp_usart.c 中 串口初始化函数里的添加中断的配置:

在这里插入图片描述

从野火的 “18-EXTI—外部中断事件控制器” 实验工程中拷贝 bsp_exti 文件夹到我们的工程中,我根据我的开发板原理图,进行了适当的修改,修改后的源代码和头文件如下:

bsp_exti.c

#include "bsp_exti.h"

static void EXTI_NVIC_Config(void)
{
	NVIC_InitTypeDef NVIC_InitStruct;
	
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
	
	NVIC_InitStruct.NVIC_IRQChannel = KEY0_IRQn;
	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;
	NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;
	NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
	NVIC_Init(&NVIC_InitStruct);
	
	
	NVIC_InitStruct.NVIC_IRQChannel = WK_UP_IRQn;
	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;
	NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;
	NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
	NVIC_Init(&NVIC_InitStruct);
}

void EXIT_Key_Config(void)
{
	GPIO_InitTypeDef  GPIO_InitStruct;
	EXTI_InitTypeDef  EXTI_InitStruct;
	
	// 配置中断优先级
	EXTI_NVIC_Config();
	
	// 初始化GPIO
	RCC_APB2PeriphClockCmd(KEY0_INT_GPIO_CLK | WK_UP_INT_GPIO_CLK, ENABLE);
	GPIO_InitStruct.GPIO_Pin = KEY0_INT_GPIO_PIN;
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPD;		
	GPIO_Init(KEY0_INT_GPIO_PORT, &GPIO_InitStruct);	
	
	GPIO_InitStruct.GPIO_Pin = WK_UP_INT_GPIO_PIN;
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPD;		
	GPIO_Init(WK_UP_INT_GPIO_PORT, &GPIO_InitStruct);	
	
	// 初始化EXTI
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
	
	GPIO_EXTILineConfig(KEY0_INT_GPIO_PORT_SRC, KEY0_INT_GPIO_PIN_SRC);
	
	EXTI_InitStruct.EXTI_Line = KEY0_INT_EXTI_LINE;
	EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt;
	EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Rising;
	EXTI_InitStruct.EXTI_LineCmd = ENABLE;
	EXTI_Init(&EXTI_InitStruct);	
	
	
	GPIO_EXTILineConfig(WK_UP_INT_GPIO_PORT_SRC, WK_UP_INT_GPIO_PIN_SRC);
	
	EXTI_InitStruct.EXTI_Line = WK_UP_INT_EXTI_LINE;
	EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt;
	EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Rising;
	EXTI_InitStruct.EXTI_LineCmd = ENABLE;
	EXTI_Init(&EXTI_InitStruct);	
}

bsp_exti.h

#ifndef __BSP_EXTI_H
#define __BSP_EXTI_H

#include "stm32f10x.h"

#define KEY0_INT_GPIO_PIN              GPIO_Pin_4
#define KEY0_INT_GPIO_PORT             GPIOE
#define KEY0_INT_GPIO_CLK              RCC_APB2Periph_GPIOE
#define KEY0_INT_GPIO_PORT_SRC         GPIO_PortSourceGPIOE
#define KEY0_INT_GPIO_PIN_SRC          GPIO_PinSource4
#define KEY0_INT_EXTI_LINE             EXTI_Line4
#define KEY0_IRQHandler                EXTI4_IRQHandler
#define KEY0_IRQn                      EXTI4_IRQn

#define WK_UP_INT_GPIO_PIN             GPIO_Pin_0
#define WK_UP_INT_GPIO_PORT            GPIOA
#define WK_UP_INT_GPIO_CLK             RCC_APB2Periph_GPIOA
#define WK_UP_INT_GPIO_PORT_SRC        GPIO_PortSourceGPIOA
#define WK_UP_INT_GPIO_PIN_SRC         GPIO_PinSource0
#define WK_UP_INT_EXTI_LINE            EXTI_Line0
#define WK_UP_IRQHandler               EXTI0_IRQHandler
#define WK_UP_IRQn                     EXTI0_IRQn


void EXIT_Key_Config(void);

#endif /* __BSP_EXTI_H */

修改 board.c 文件,将 EXIT_Key_Config(); 加入到 rt_hw_board_init() 函数中:

在这里插入图片描述

下面是实验代码:

#include "board.h"
#include "rtthread.h"
#include <string.h>


// 定义线程控制块指针
static rt_thread_t usart_thread = RT_NULL;
static rt_thread_t key_thread = RT_NULL;

// 定义消息队列控制块
rt_mq_t test_mq = RT_NULL;

// 定义信号量控制块
rt_sem_t test_sem = RT_NULL;

// 串口数据
#define USART_RX_BUFF_SIZE 1024
char Usart_Rx_Buf[USART_RX_BUFF_SIZE];
rt_uint32_t Usart_Cnt;

// 按键标志
rt_uint32_t send_data1 = 0;
rt_uint32_t send_data2 = 1;


/******************************************************************************
* @ 函数名  : usart_thread_entry
* @ 功  能  : 串口线程入口函数
* @ 参  数  : parameter 外部传入的参数
* @ 返回值  : 无
******************************************************************************/
static void usart_thread_entry(void *parameter)
{
	rt_err_t uwRet = RT_EOK;
	
	while(1)
	{
		uwRet = rt_sem_take(test_sem, 0); // 获取串口中断的信号量,不等待
		
		if(RT_EOK == uwRet)
		{
			rt_kprintf("收到数据:%s\n", Usart_Rx_Buf);
			memset(Usart_Rx_Buf, 0, USART_RX_BUFF_SIZE);
			Usart_Cnt = 0;
		}
	}
}

/******************************************************************************
* @ 函数名  : key_thread_entry
* @ 功  能  : 按键线程入口函数
* @ 参  数  : parameter 外部传入的参数
* @ 返回值  : 无
******************************************************************************/
static void key_thread_entry(void *parameter)
{
	rt_err_t uwRet = RT_EOK;
	uint32_t r_queue;
	
	while(1)
	{
		// 队列读取(接收),等待时间为阻塞等待
		uwRet = rt_mq_recv(test_mq, &r_queue, sizeof(r_queue), RT_WAITING_FOREVER);
		
		if(RT_EOK == uwRet)
			rt_kprintf("触发中断的是 %s.\n", r_queue ? "WK_UP" : "KEY0");
		else
			rt_kprintf("数据接收错误,错误码:0x%1x\n", uwRet);
	}
}

int main(void)
{
	// 硬件初始化和RTT的初始化已经在component.c中的rtthread_startup()完成

	// 创建一个消息队列,最大长度为4,最大消息容量为2
	test_mq = rt_mq_create("test_mq", 4, 2, RT_IPC_FLAG_FIFO);
	
	if(test_mq != RT_NULL)
		rt_kprintf("消息队列创建成功!\n");
	
	// 创建一个信号量,初始值为0
	test_sem = rt_sem_create("test_sem", 0, RT_IPC_FLAG_FIFO);
	
	if(test_sem != RT_NULL)
		rt_kprintf("信号量创建成功!\n");
	
	// 创建一个串口线程
	usart_thread =                                 // 线程控制块指针
	rt_thread_create("usart",                      // 线程名字
	                usart_thread_entry,            // 线程入口函数
	                RT_NULL,                       // 入口函数参数
	                512,                           // 线程栈大小
					3,                             // 线程优先级
					20);                           // 线程时间片
	
	// 开启线程调度
	if(usart_thread != RT_NULL)
		rt_thread_startup(usart_thread);
	else
		return -1;
							
	// 创建一个按键线程
	key_thread =                                 // 线程控制块指针
	rt_thread_create("key",                      // 线程名字
	                key_thread_entry,            // 线程入口函数
	                RT_NULL,                     // 入口函数参数
	                512,                         // 线程栈大小
					2,                           // 线程优先级
					20);                         // 线程时间片
	// 开启线程调度
	if(key_thread != RT_NULL)
		rt_thread_startup(key_thread);
	else
		return -1;
}

/******************************************************************************
* @ 函数名  : KEY0_IRQHandler
* @ 功  能  : 按键KEY0中断服务函数
* @ 参  数  : 无
* @ 返回值  : 无
******************************************************************************/
void KEY0_IRQHandler(void)
{
	if(EXTI_GetITStatus(KEY0_INT_EXTI_LINE) != RESET)
	{
		rt_mq_send(test_mq, &send_data1, sizeof(send_data1));
		EXTI_ClearITPendingBit(KEY0_INT_EXTI_LINE);
	}
}	


/******************************************************************************
* @ 函数名  : WK_UP_IRQHandler
* @ 功  能  : 按键WK_UP中断服务函数
* @ 参  数  : 无
* @ 返回值  : 无
******************************************************************************/
void WK_UP_IRQHandler(void)
{
	if(EXTI_GetITStatus(WK_UP_INT_EXTI_LINE) != RESET)
	{
		rt_mq_send(test_mq, &send_data2, sizeof(send_data2));
		EXTI_ClearITPendingBit(WK_UP_INT_EXTI_LINE);
	}
}	


/******************************************************************************
* @ 函数名  : DEBUG_USART_IRQHandler
* @ 功  能  : 串口中断服务函数
* @ 参  数  : 无
* @ 返回值  : 无
******************************************************************************/
void DEBUG_USART_IRQHandler(void)
{
	// 接收中断
	if(USART_GetITStatus(DEBUG_USARTx,USART_IT_RXNE)!=RESET)
	{		
    Usart_Rx_Buf[Usart_Cnt++] = USART_ReceiveData(DEBUG_USARTx);
		//Usart_Rx_Buf[Usart_Cnt] = '\0';
	}	 
	// 空闲中断
	if(USART_GetITStatus(DEBUG_USARTx, USART_IT_IDLE) != RESET)
	{
		// 清除中断
		USART1->SR;
    	USART1->DR;
		
		// 释放信号量
		rt_sem_release(test_sem);
	}
}	

实验现象

本实验分别测试了外部中断和串口中断:

在这里插入图片描述

  嵌入式 最新文章
基于高精度单片机开发红外测温仪方案
89C51单片机与DAC0832
基于51单片机宠物自动投料喂食器控制系统仿
《痞子衡嵌入式半月刊》 第 68 期
多思计组实验实验七 简单模型机实验
CSC7720
启明智显分享| ESP32学习笔记参考--PWM(脉冲
STM32初探
STM32 总结
【STM32】CubeMX例程四---定时器中断(附工
上一篇文章      下一篇文章      查看所有文章
加:2022-06-14 22:50:26  更:2022-06-14 22:51:58 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 0:35:02-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码