尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

FreeRTOS中断管理 基于STM32

FreeRTOS中断管理 基于STM32 文章目录一、异常与中断的基本概念二、中断的介绍三、和中断相关的名词解释四、中断管理的运作机制五、 中断延迟的概念六、中断管理的应用场景七、中断管理讲解八、中断管理实验九、中断管理实验现象一、异常与中断的基本概念异常是导致处理器脱离正常运行转向执行特殊代码的任何事件如果不及时进行处理 轻则系统出错重则会导致系统毁灭性瘫痪。所以正确地处理异常避免错误的发生是提 高软件鲁棒性稳定性非常重要的一环对于实时系统更是如此。异常是指任何打断处理器正常执行并且迫使处理器进入一个由有特权的特殊指令执 行的事件。异常通常可以分成两类同步异常和异步异常。由内部事件像处理器指令运 行产生的事件引起的异常称为同步异常例如造成被零除的算术运算引发一个异常又 如在某些处理器体系结构中对于确定的数据尺寸必须从内存的偶数地址进行读和写操作。 从一个奇数内存地址的读或写操作将引起存储器存取一个错误事件并引起一个异常称为 校准异常。异步异常主要是指由于外部异常源产生的异常是一个由外部硬件装置产生的事件引 起的异步异常。同步异常不同于异步异常的地方是事件的来源同步异常事件是由于执行 某些指令而从处理器内部产生的而异步异常事件的来源是外部硬件装置。例如按下设备 某个按钮产生的事件。同步异常与异步异常的区别还在于同步异常触发后系统必须立 刻进行处理而不能够依然执行原有的程序指令步骤而异步异常则可以延缓处理甚至是忽 略例如按键中断异常虽然中断异常触发了但是系统可以忽略它继续运行同样也忽 略了相应的按键事件。中断中断属于异步异常。所谓中断是指中央处理器 CPU 正在处理某件事的时候外 部发生了某一事件请求 CPU 迅速处理CPU暂时中断当前的工作转入处理所发生的事 件处理完后再回到原来被中断的地方继续原来的工作这样的过程称为中断。中断能打断任务的运行无论该任务具有什么样的优先级因此中断一般用于处理比 较紧急的事件而且只做简单处理例如标记该事件在使用 FreeRTOS 系统时一般建 议使用信号量、消息或事件标志组等标志中断的发生将这些内核对象发布给处理任务 处理任务再做具体处理。通过中断机制在外设不需要 CPU 介入时CPU 可以执行其他任务而当外设需要 CPU 时通过产生中断信号使 CPU 立即停止当前任务转而来响应中断请求。这样可以使 CPU 避免把大量时间耗费在等待、查询外设状态的操作上因此将大大提高系统实时性以 及执行效率。此处读者要知道一点FreeRTOS 源码中有许多处临界段的地方临界段虽然保护了关 键代码的执行不被打断但也会影响系统的实时任何使用了操作系统的中断响应都不会 比裸机快。比如某个时候有一个任务在运行中并且该任务部分程序将中断屏蔽掉也 就是进入临界段中这个时候如果有一个紧急的中断事件被触发这个中断就会被挂起 不能得到及时响应必须等到中断开启才可以得到响应如果屏蔽中断时间超过了紧急中断能够容忍的限度危害是可想而知的。所以操作系统的中断在某些时候会有适当的中 断延迟因此调用中断屏蔽函数进入临界段的时候也需快进快出。当然 FreeRTOS 也能 允许一些高优先级的中断不被屏蔽掉能够及时做出响应不过这些中断就不受系统管理 也不允许调用 FreeRTOS 中与中断相关的任何 API 函数接口。FreeRTOS 的中断管理支持● 开/关中断。●恢复中断。●中断使能。●中断屏蔽。●可选择系统管理的中断优先级。二、中断的介绍与中断相关的硬件可以划分为三类外设、中断控制器、CPU 本身。外设当外设需要请求 CPU 时产生一个中断信号该信号连接至中断控制器。中断控制器中断控制器是 CPU 众多外设中的一个它一方面接收其他外设中断信号 的输入另一方面它会发出中断信号给 CPU。可以通过对中断控制器编程实现对中断源 的优先级、触发方式、打开和关闭源等设置操作。在 Cortex-M 系列控制器中常用的中断控 制器是 NVIC内嵌向量中断控制器 Nested Vectored Interrupt Controller。CPUCPU 会响应中断源的请求中断当前正在执行的任务转而执行中断处理程序。 NVIC 最多支持 240个中断每个中断最多 256 个优先级。三、和中断相关的名词解释中断号每个中断请求信号都会有特定的标志使得计算机能够判断是哪个设备提出 的中断请求这个标志就是中断号。中断请求“紧急事件”需向 CPU 提出申请要求 CPU 暂停当前执行的任务转而 处理该“紧急事件”这一申请过程称为中断请求。中断优先级为使系统能够及时响应并处理所有中断系统根据中断时间的重要性和 紧迫程度将中断源分为若干个级别称作中断优先级。中断处理程序当外设产生中断请求后CPU 暂停当前的任务转而响应中断申请 即执行中断处理程序。中断触发中断源发出并送给 CPU 控制信号将中断触发器置“1”表明该中断源 产生了中断要求 CPU 去响应该中断CPU 暂停当前任务执行相应的中断处理程序。 中断触发类型外部中断申请通过一个物理信号发送到 NVIC可以是电平触发或边 沿触发。中断向量中断服务程序的入口地址。中断向量表存储中断向量的存储区中断向量与中断号对应中断向量在中断向量 表中按照中断号顺序存储。临界段代码的临界段也称为临界区一旦这部分代码开始执行则不允许任何中断 打断。为确保临界段代码的执行不被中断在进入临界段之前须关中断而临界段代码执 行完毕后要立即开中断。四、中断管理的运作机制当中断产生时处理机将按如下的顺序执行1. 保存当前处理机状态信息2. 载入异常或中断处理函数到 PC寄存器3. 把控制权转交给处理函数并开始执行4. 当处理函数执行完成时恢复处理器状态信息5. 从异常或中断中返回到前一个程序执行点中断使得 CPU 可以在事件发生时才给予处理而不必让 CPU 连续不断地查询是否有 相应的事件发生。通过两条特殊指令关中断和开中断可以让处理器不响应或响应中断 在关闭中断期间通常处理器会把新产生的中断挂起当中断打开时立刻进行响应所以 会有适当的延时响应中断故用户在进入临界区的时候应快进快出。中断发生的环境有两种情况在任务的上下文中在中断服务函数处理上下文中。● 任务在工作的时候如果此时发生了一个中断无论中断的优先级是多大都会 打断当前任务的执行从而转到对应的中断服务函数中执行其过程具体见图1图 1、1(3)在任务运行的时候发生了中断那么中断会打断任务的运行那么 操作系统将先保存当前任务的上下文环境转而去处理中断服务函数。图1、2(4)当且仅当中断服务函数处理完的时候才恢复任务的上下文环境继 续运行任务。图1中断发生在任务上下文● 在执行中断服务例程的过程中如果有更高优先级别的中断源触发中断由于当 前处于中断处理上下文环境中根据不同的处理器构架可能有不同的处理方式 比如新的中断等待挂起直到当前中断处理离开后再行响应或新的高优先级中断 打断当前中断处理过程而去直接响应这个更高优先级的新中断源。后面这种情 况称之为中断嵌套。在硬实时环境中前一种情况是不允许发生的不能使响 应中断的时间尽量的短。而在软件处理软实时环境上FreeRTOS 允许中断嵌 套即在一个中断服务例程期间处理器可以响应另外一个优先级更高的中断 过程如图 2 所示。当中断 1 的服务函数在处理的时候发生了中断 2由于中断 2 的优先级比 中断 1 更高所以发生了中断嵌套那么操作系统将先保存当前中断服务函数的上下文环 境并且转向处理中断 2当且仅当中断 2 执行完的时候图 (2)才能继续执行中断 1。图2中断嵌套发生五、 中断延迟的概念即使操作系统的响应很快了但对于中断的处理仍然存在着中断延迟响应的问题我 们称之为中断延迟(Interrupt Latency) 。中断延迟是指从硬件中断发生到开始执行中断处理程序第一条指令之间的这段时间。 也就是系统接收到中断信号到操作系统作出响应并完成换到转入中断服务程序的时间。 也可以简单地理解为外部硬件设备发生中断到系统执行中断服务子程序 ISR的第一条指令的时间。中断的处理过程是外界硬件发生了中断后CPU 到中断处理器读取中断向量并且 查找中断向量表找到对应的中断服务子程序ISR的首地址然后跳转到对应的 ISR 去做相应处理。这部分时间我称之为识别中断时间。在允许中断嵌套的实时操作系统中中断也是基于优先级的允许高优先级中断抢断 正在处理的低优先级中断所以如果当前正在处理更高优先级的中断即使此时有低优 先级的中断也系统不会立刻响应而是等到高优先级的中断处理完之后才会响应。而 即使在不支持中断嵌套即中断是没有优先级的中断是不允许被中断的所以如果当 前系统正在处理一个中断而此时另一个中断到来了系统也是不会立即响应的而只是 等处理完当前的中断之后才会处理后来的中断。此部分时间我称其为等待中断打开 时间。在操作系统中很多时候我们会主动进入临界段系统不允许当前状态被中断打断 故而在临界区发生的中断会被挂起直到退出临界段时候打开中断。此部分时间我称其 为关闭中断时间。中断延迟可以定义为从中断开始的时刻到中断服务例程开始执行的时刻之间的时间 段。中断延迟 识别中断时间 [等待中断打开时间] [关闭中断时间]。六、中断管理的应用场景中断在嵌入式处理器中应用非常之多没有中断的系统不是一个好系统因为有中断 才能启动或者停止某件事情从而转去做另一间事情。我们可以举一个日常生活中的例子 来说明假如你正在给朋友写信电话铃响了这时你放下手中的笔去接电话通话完毕 再继续写信。这个例子就表现了中断及其处理的过程电话铃声使你暂时中止当前的工作 而去处理更为急需处理的事情——接电话当把急需处理的事情处理完毕之后再回过头 来继续原来的事情。在这个例子中电话铃声就可以称为“中断请求”而你暂停写信去 接电话就叫作“中断响应”那么接电话的过程就是“中断处理”。由此我们可以看出 在计算机执行程序的过程中由于出现某个特殊情况(或称为“特殊事件”)使得系统暂 时中止现行程序而转去执行处理这一特殊事件的程序处理完毕之后再回到原来程序的 中断点继续向下执行。为什么说吗没有中断的系统不是好系统呢我们可以再举一个例子来说明中断的作用。 假设有一个朋友来拜访你但是由于不知何时到达你只能在门口等待于是什么事情也 干不了但如果在门口装一个门铃你就不必在门口等待而可以在家里去做其他的工作 朋友来了按门铃通知你这时你才中断手中的工作去开门这就避免了不必要的等待。 CPU 也是一样如果时间都浪费在查询的事情上那这个 CPU啥也干不了要他何用。在 嵌入式系统中合理利用中断能更好利用 CPU 的资源。七、中断管理讲解ARM Cortex-M 系列内核的中断是由硬件管理的而 FreeRTOS 是软件它并不接管由 硬件管理的相关中断接管简单来说就是所有的中断都由 RTOS 的软件管理硬件来了 中断时由软件决定是否响应可以挂起中断延迟响应或者不响应只支持简单的开 关中断等所以 FreeRTOS 中的中断使用其实跟裸机差不多的需要我们自己配置中断并且使能中断编写中断服务函数在中断服务函数中使用内核 IPC 通信机制一般建议 使用信号量、消息或事件标志组等标志事件的发生将事件发布给处理任务等退出中断 后再由相关处理任务具体处理中断。用 户 可 以 自 定 义 配 置 系 统 可 管 理 的 最 高 中 断 优 先 级 的 宏 定 义 configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 它 是 用 于 配 置 内 核 中 的 basepri 寄存器的当 basepri 设置为某个值的时候NVIC 不会响应比该优先级低的中断 而优先级比之更高的中断则不受影响。就是说当这个宏定义配置为 5 的时候中断优先级 数值在 0、1、2、3、4 的这些中断是不受 FreeRTOS 屏蔽的也就是说即使在系统进入临 界段的时候这些中断也能被触发而不是等到退出临界段的时候才被触发当然这些中 断服务函数中也不能调用 FreeRTOS 提供的 API 函数接口而中断优先级在 5 到 15 的这些 中断是可以被屏蔽的也能安全调用 FreeRTOS 提供的 API 函数接口。ARM Cortex-M NVIC 支持中断嵌套功能当一个中断触发并且系统进行响应时处理 器硬件会将当前运行的部分上下文寄存器自动压入中断栈中这部分的寄存器包括 PSR R0R1R2R3 以及 R12 寄存器。当系统正在服务一个中断时如果有一个更高优先级 的中断触发那么处理器同样的会打断当前运行的中断服务例程然后把老的中断服务例 程上下文的 PSRR0R1R2R3 和 R12 寄存器自动保存到中断栈中。这些部分上下文 寄存器保存到中断栈的行为完全是硬件行为这一点是与其他 ARM 处理器最大的区别 以往都需要依赖于软件保存上下文。另外在 ARM Cortex-M 系列处理器上所有中断都采用中断向量表的方式进行处理 即当一个中断触发时处理器将直接判定是哪个中断源然后直接跳转到相应的固定位置 进行处理。而在 ARM7、ARM9 中一般是先跳转进入 IRQ 入口然后再由软件进行判断 是哪个中断源触发获得了相对应的中断服务例程入口地址后再进行后续的中断处理。 ARM7、ARM9 的好处在于所有中断它们都有统一的入口地址便于 OS 的统一管理。而 ARM Cortex-M 系列处理器则恰恰相反每个中断服务例程必须排列在一起放在统一的地 址上这个地址必须要设置到 NVIC 的中断向量偏移寄存器中。中断向量表一般由一个 数组定义或在起始代码中给出在 STM32 上默认采用起始代码给出具体见代码。__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_EndFreeRTOS 在 Cortex-M 系列处理器上也遵循与裸机中断一致的方法当用户需要使用 自定义的中断服务例程时只需要定义相同名称的函数覆盖弱化符号即可。所以 FreeRTOS 在 Cortex-M 系列处理器的中断控制其实与裸机没什么差别。八、中断管理实验中断管理实验是在 FreeRTOS 中创建了两个任务分别获取信号量与消息队列并且定 义了两个按键 KEY1 与 KEY2 的触发方式为中断触发其触发的中断服务函数则跟裸机一 样在中断触发的时候通过消息队列将消息传递给任务任务接收到消息就将信息通过串 口调试助手显示出来。而且中断管理实验也实现了一个串口的 DMA 传输空闲中断功能 当串口接收完不定长的数据之后产生一个空闲中断在中断中将信号量传递给任务任务 在收到信号量的时候将串口的数据读取出来并且在串口调试助手中回显。/* FreeRTOS头文件 */ #include FreeRTOS.h #include task.h #include queue.h #include semphr.h /* 开发板硬件bsp头文件 */ #include bsp_led.h #include bsp_usart.h #include bsp_key.h #include bsp_exti.h /* 标准库头文件 */ #include string.h /**************************** 任务句柄 ********************************/ /* * 任务句柄是一个指针用于指向一个任务当任务创建好之后它就具有了一个任务句柄 * 以后我们要想操作这个任务都需要通过这个任务句柄如果是自身的任务操作自己那么 * 这个句柄可以为NULL。 */ static TaskHandle_t AppTaskCreate_Handle NULL;/* 创建任务句柄 */ static TaskHandle_t LED_Task_Handle NULL;/* LED任务句柄 */ static TaskHandle_t KEY_Task_Handle NULL;/* KEY任务句柄 */ /********************************** 内核对象句柄 *********************************/ /* * 信号量消息队列事件标志组软件定时器这些都属于内核的对象要想使用这些内核 * 对象必须先创建创建成功之后会返回一个相应的句柄。实际上就是一个指针后续我 * 们就可以通过这个句柄操作这些内核对象。 * * 内核对象说白了就是一种全局的数据结构通过这些数据结构我们可以实现任务间的通信 * 任务间的事件同步等各种功能。至于这些功能的实现我们是通过调用这些内核对象的函数 * 来完成的 * */ QueueHandle_t Test_Queue NULL; SemaphoreHandle_t BinarySem_Handle NULL; /******************************* 全局变量声明 ************************************/ /* * 当我们在写应用程序的时候可能需要用到一些全局变量。 */ extern char Usart_Rx_Buf[USART_RBUFF_SIZE]; /******************************* 宏定义 ************************************/ /* * 当我们在写应用程序的时候可能需要用到一些宏定义。 */ #define QUEUE_LEN 4 /* 队列的长度最大可包含多少个消息 */ #define QUEUE_SIZE 4 /* 队列中每个消息大小字节 */ /* ************************************************************************* * 函数声明 ************************************************************************* */ static void AppTaskCreate(void);/* 用于创建任务 */ static void LED_Task(void* pvParameters);/* LED_Task任务实现 */ static void KEY_Task(void* pvParameters);/* KEY_Task任务实现 */ static void BSP_Init(void);/* 用于初始化板载相关资源 */ /***************************************************************** * brief 主函数 * param 无 * retval 无 * note 第一步开发板硬件初始化 第二步创建APP应用任务 第三步启动FreeRTOS开始多任务调度 ****************************************************************/ int main(void) { BaseType_t xReturn pdPASS;/* 定义一个创建信息返回值默认为pdPASS */ /* 开发板硬件初始化 */ BSP_Init(); printf(这是一个FreeRTOS中断管理实验\n); printf(按下KEY1 | KEY2触发中断\n); printf(串口发送数据触发中断,任务处理数据!\n); /* 创建AppTaskCreate任务 */ xReturn xTaskCreate((TaskFunction_t )AppTaskCreate, /* 任务入口函数 */ (const char* )AppTaskCreate,/* 任务名字 */ (uint16_t )512, /* 任务栈大小 */ (void* )NULL,/* 任务入口函数参数 */ (UBaseType_t )1, /* 任务的优先级 */ (TaskHandle_t* )AppTaskCreate_Handle);/* 任务控制块指针 */ /* 启动任务调度 */ if(pdPASS xReturn) vTaskStartScheduler(); /* 启动任务开启调度 */ else return -1; // while(1);/* 正常不会执行到这里 */ } /*********************************************************************** * 函数名 AppTaskCreate * 功能说明 为了方便管理所有的任务创建函数都放在这个函数里面 * 参数 无 * 返回值 无 **********************************************************************/ static void AppTaskCreate(void) { BaseType_t xReturn pdPASS;/* 定义一个创建信息返回值默认为pdPASS */ taskENTER_CRITICAL(); //进入临界区 /* 创建Test_Queue */ Test_Queue xQueueCreate((UBaseType_t ) QUEUE_LEN,/* 消息队列的长度 */ (UBaseType_t ) QUEUE_SIZE);/* 消息的大小 */ if(NULL ! Test_Queue) printf(Test_Queue消息队列创建成功!\n); /* 创建 BinarySem */ BinarySem_Handle xSemaphoreCreateBinary(); if(NULL ! BinarySem_Handle) printf(BinarySem_Handle二值信号量创建成功!\n); /* 创建LED_Task任务 */ xReturn xTaskCreate((TaskFunction_t )LED_Task, /* 任务入口函数 */ (const char* )LED_Task,/* 任务名字 */ (uint16_t )512, /* 任务栈大小 */ (void* )NULL, /* 任务入口函数参数 */ (UBaseType_t )2, /* 任务的优先级 */ (TaskHandle_t* )LED_Task_Handle);/* 任务控制块指针 */ if(pdPASS xReturn) printf(创建LED_Task任务成功!\n); /* 创建KEY_Task任务 */ xReturn xTaskCreate((TaskFunction_t )KEY_Task, /* 任务入口函数 */ (const char* )KEY_Task,/* 任务名字 */ (uint16_t )512, /* 任务栈大小 */ (void* )NULL,/* 任务入口函数参数 */ (UBaseType_t )3, /* 任务的优先级 */ (TaskHandle_t* )KEY_Task_Handle);/* 任务控制块指针 */ if(pdPASS xReturn) printf(创建KEY_Task任务成功!\n); vTaskDelete(AppTaskCreate_Handle); //删除AppTaskCreate任务 taskEXIT_CRITICAL(); //退出临界区 } /********************************************************************** * 函数名 LED_Task * 功能说明 LED_Task任务主体 * 参数 * 返回值 无 ********************************************************************/ static void LED_Task(void* parameter) { BaseType_t xReturn pdPASS;/* 定义一个创建信息返回值默认为pdPASS */ uint32_t r_queue; /* 定义一个接收消息的变量 */ while (1) { /* 队列读取接收等待时间为一直等待 */ xReturn xQueueReceive( Test_Queue, /* 消息队列的句柄 */ r_queue, /* 发送的消息内容 */ portMAX_DELAY); /* 等待时间 一直等 */ if(pdPASS xReturn) { printf(触发中断的是 KEY%d !\n,r_queue); } else { printf(数据接收出错\n); } LED1_TOGGLE; } } /********************************************************************** * 函数名 LED_Task * 功能说明 LED_Task任务主体 * 参数 * 返回值 无 ********************************************************************/ static void KEY_Task(void* parameter) { BaseType_t xReturn pdPASS;/* 定义一个创建信息返回值默认为pdPASS */ while (1) { //获取二值信号量 xSemaphore,没获取到则一直等待 xReturn xSemaphoreTake(BinarySem_Handle,/* 二值信号量句柄 */ portMAX_DELAY); /* 等待时间 */ if(pdPASS xReturn) { printf(收到数据:%s\n,Usart_Rx_Buf); memset(Usart_Rx_Buf,0,USART_RBUFF_SIZE);/* 清零 */ LED2_TOGGLE; } } } /*********************************************************************** * 函数名 BSP_Init * 功能说明 板级外设初始化所有板子上的初始化均可放在这个函数里面 * 参数 * 返回值 无 *********************************************************************/ static void BSP_Init(void) { /* * STM32中断优先级分组为4即4bit都用来表示抢占优先级范围为0~15 * 优先级分组只需要分组一次即可以后如果有其他的任务需要用到中断 * 都统一用这个优先级分组千万不要再分组切忌。 */ NVIC_PriorityGroupConfig( NVIC_PriorityGroup_4 ); /* LED 初始化 */ LED_GPIO_Config(); /* DMA初始化 */ USARTx_DMA_Config(); /* 串口初始化 */ USART_Config(); /* 按键初始化 */ Key_GPIO_Config(); /* 按键初始化 */ EXTI_Key_Config(); } /********************************END OF FILE****************************/中断服务程序/* Includes ------------------------------------------------------------------*/ #include stm32f10x_it.h /* FreeRTOS头文件 */ #include FreeRTOS.h #include task.h #include queue.h #include semphr.h /* 开发板硬件bsp头文件 */ #include bsp_led.h #include bsp_usart.h #include bsp_key.h #include bsp_exti.h /** * brief This function handles SysTick Handler. * param None * retval None */ extern void xPortSysTickHandler(void); //systick中断服务函数 void SysTick_Handler(void) { #if (INCLUDE_xTaskGetSchedulerState 1 ) if (xTaskGetSchedulerState() ! taskSCHEDULER_NOT_STARTED) { #endif /* INCLUDE_xTaskGetSchedulerState */ xPortSysTickHandler(); #if (INCLUDE_xTaskGetSchedulerState 1 ) } #endif /* INCLUDE_xTaskGetSchedulerState */ } /* 声明引用外部队列 二值信号量 */ extern QueueHandle_t Test_Queue; extern SemaphoreHandle_t BinarySem_Handle; static uint32_t send_data1 1; static uint32_t send_data2 2; /********************************************************************************* * 函数名 KEY1_IRQHandler * 功能说明 中断服务函数 * 参数 无 * 返回值 无 ********************************************************************************/ void KEY1_IRQHandler(void) { BaseType_t pxHigherPriorityTaskWoken; //确保是否产生了EXTI Line中断 uint32_t ulReturn; /* 进入临界段临界段可以嵌套 */ ulReturn taskENTER_CRITICAL_FROM_ISR(); if(EXTI_GetITStatus(KEY1_INT_EXTI_LINE) ! RESET) { /* 将数据写入发送到队列中等待时间为 0 */ xQueueSendFromISR(Test_Queue, /* 消息队列的句柄 */ send_data1,/* 发送的消息内容 */ pxHigherPriorityTaskWoken); //如果需要的话进行一次任务切换 portYIELD_FROM_ISR(pxHigherPriorityTaskWoken); //清除中断标志位 EXTI_ClearITPendingBit(KEY1_INT_EXTI_LINE); } /* 退出临界段 */ taskEXIT_CRITICAL_FROM_ISR( ulReturn ); } /********************************************************************************* * 函数名 KEY1_IRQHandler * 功能说明 中断服务函数 * 参数 无 * 返回值 无 ********************************************************************************/ void KEY2_IRQHandler(void) { BaseType_t pxHigherPriorityTaskWoken; uint32_t ulReturn; /* 进入临界段临界段可以嵌套 */ ulReturn taskENTER_CRITICAL_FROM_ISR(); //确保是否产生了EXTI Line中断 if(EXTI_GetITStatus(KEY2_INT_EXTI_LINE) ! RESET) { /* 将数据写入发送到队列中等待时间为 0 */ xQueueSendFromISR(Test_Queue, /* 消息队列的句柄 */ send_data2,/* 发送的消息内容 */ pxHigherPriorityTaskWoken); //如果需要的话进行一次任务切换 portYIELD_FROM_ISR(pxHigherPriorityTaskWoken); //清除中断标志位 EXTI_ClearITPendingBit(KEY2_INT_EXTI_LINE); } /* 退出临界段 */ taskEXIT_CRITICAL_FROM_ISR( ulReturn ); } /********************************************************************************* * 函数名 DEBUG_USART_IRQHandler * 功能说明 串口中断服务函数 * 参数 无 * 返回值 无 ********************************************************************************/ void DEBUG_USART_IRQHandler(void) { uint32_t ulReturn; /* 进入临界段临界段可以嵌套 */ ulReturn taskENTER_CRITICAL_FROM_ISR(); if(USART_GetITStatus(DEBUG_USARTx,USART_IT_IDLE)!RESET) { Uart_DMA_Rx_Data(); /* 释放一个信号量表示数据已接收 */ USART_ReceiveData(DEBUG_USARTx); /* 清除标志位 */ } /* 退出临界段 */ taskEXIT_CRITICAL_FROM_ISR( ulReturn ); }九、中断管理实验现象程序编译好用 USB 线连接电脑和开发板的 USB 接口对应丝印为 USB 转串口 用 仿真器把配套程序下载到 STM32 开发板在电脑上打开串口调试助手然后复位开发板就可 以在调试助手中看到串口的打印信息按下开发板的 KEY1 按键触发中断发送消息 1按 下 KEY2 按键发送消息 2我们按下 KEY1 与 KEY2 试试在串口调试助手中可以看到运 行结果然后通过串口调试助手发送一段不定长信息触发中断会在中断服务函数发送信 号量通知任务任务接收到信号量的时候将串口信息打印出来。
返回列表