目录
1 中断管理
在RTOS中,需要应对各类事件。这些事件很多时候是通过硬件中断产生,怎么处理中断呢? 假设当前系统正在运行Task1时,用户按下了按键,触发了按键中断。这个中断的处理流程如下:
- CPU跳到固定地址去执行代码,这个固定地址通常被称为中断向量,这个跳转时硬件实现的
- 执行代码做什么?
- 保存现场:Task1被打断,需要先保存Task1的运行环境,比如各类寄存器的值
- 分辨中断、调用处理函数(这个函数就被称为ISR,interrupt service routine)
- 恢复现场:继续运行Task1,或者运行其他优先级更高的任务
如果这个硬件中断的处理,就是非常耗费时间呢?对于这类中断的处理就要分为2部分:
- ISR:尽快做些清理、记录工作,然后触发某个任务
- 任务:更复杂的事情放在任务中处理 ,所以:需要ISR和任务之间进行通信
前面讲解过的队列、信号量、互斥量、事件组、任务通知等等方法,都可用于中断与任务间的通讯,下面的例程使用信号量实现了中断和任务间的通讯。
2 示例程序
2.1 例程功能
开启串口DMA中断,串口接收到数据后,触发中断,在中断回调函数中释放二值信号量,打印任务获得二值信号量并通过串口打印字符串“接收到数据了”。
2.2 步骤
打开串口中断并开启DMA通道接收数据
创建一个二值信号量
创建一个动态任务,任务的传入参数为所创建的二值信号量的句柄
在主函数中打开串口DMA空闲中断,在中断回调函数中释放二值信号量
int main(void)
{
//...
//在DMA模式下接收一定数量的数据,直到接收到预期数量的数据或发生空闲事件。
HAL_UARTEx_ReceiveToIdle_DMA(&huart1,INVT_Databuff,10);
//...
}
//...
/* USER CODE BEGIN 4 */
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
{
if(huart->Instance == USART1)
{
osSemaphoreRelease(myBinarySem01Handle);
}
}
/* USER CODE END 4 */
因为二值信号量创建后初始值为1,所以我们要在FREERTOS初始化程序中先将二值信号量给获得了
void MX_FREERTOS_Init(void) {
//...
/* USER CODE BEGIN RTOS_SEMAPHORES */
/* add semaphores, ... */
osSemaphoreWait(myBinarySem01Handle,0); //初始的时候就将二值信号给获得了
/* USER CODE END RTOS_SEMAPHORES */
//...
}
等待获取信号量,获取信号量成功后就打印字符串“接受到数据了”,并开启下一次串口空闲中断接收
/* USER CODE END Header_StartTask_myTask01 */
void StartTask_myTask01(void const * argument)
{
/* USER CODE BEGIN StartTask_myTask01 */
/* Infinite loop */
osSemaphoreId semaphore=(osSemaphoreId) argument;
for(;;)
{
if(semaphore!=NULL)
{
if(osSemaphoreWait(semaphore,osWaitForever)==osOK)
{
printf("接受到数据了 \n");
HAL_UARTEx_ReceiveToIdle_DMA(&huart1,INVT_Databuff,10);//再次打开接受中断
}
}
osDelay(1);
}
/* USER CODE END StartTask_myTask01 */
}
2.3 实验结果
串口调试助手发送任意字符串,单片机串口接受到数据后发送“接收到数据了”