求知 文章 文库 Lib 视频 iPerson 课程 认证 咨询 工具 讲座 Modeler   Code  
会员   
要资料
 
追随技术信仰

随时听讲座
每天看新闻
 
 
FreeRTOS源码分析及应用开发
1. FreeRTOS系列 | FreeRTOS简介
2. FreeRTOS系列 | 开发环境
3. FreeRTOS系列 | 任务基础知识
4. FreeRTOS系列 | 任务创建和删除
5. FreeRTOS系列 | 任务挂起和恢复
6. FreeRTOS系列|多任务调度
7. FreeRTOS系列|时间管理
8. FreeRTOS系列|中断管理和临界段
9. FreeRTOS系列|任务堆栈
10. FreeRTOS系列 | 处理器利用率
11. FreeRTOS系列|任务相关API函数
12. FreeRTOS系列 | 列表和列表项
13. FreeRTOS系列|消息队列一
14. FreeRTOS系列|消息队列二
15. FreeRTOS系列 | 二值信号量
16. FreeRTOS系列 | 互斥信号量
17. FreeRTOS系列 | 计数信号量
18. FreeRTOS系列 | 递归互斥信号量
19. FreeRTOS系列 | 事件标志组
20. FreeRTOS系列 | 软件定时器
21. FreeRTOS系列 | 低功耗管理
22. FreeRTOS系列 | 内存管理一
23. FreeRTOS系列 | 内存管理二
 

 
目录
FreeRTOS系列|消息队列二
作者:安迪西嵌入式
219 次浏览
6次  

消息队列应用实例

本文主要介绍如何在任务或中断中向队列发送消息或者从队列中接收消息。

使用STM32CubeMX将FreeRTOS移植到工程中,创建两个任务以及两个消息队列,并开启两个中断

两个任务

  • Keyscan_Task:读取按键的键值,并将键值发送到队列Key_Queue中
  • Keyprocess_Task:按键处理任务,读取队列Key_Queue中的消息,并根据不同的消息值做相应的处理

两个队列

  • Msg_Queue:用于传递串口发送过来的消息
  • Key_Queue:用于传递按键值

两个中断

  • 串口接收中断:接收串口发来的数据,并将接收到的数据发送到队列Msg_Queue中
  • 定时器中断:定时读取队列Msg_Queue中的消息,并控制LED3/LED4的亮灭

1. STM32CubeMX设置

  • RCC设置外接HSE,时钟设置为72M
  • PC0/PC1/PC2/PC3设置为GPIO推挽输出模式、上拉、高速、默认输出电平为高电平
  • PA0设置为GPIO输入模式、下拉模式;PE2/PE3/PE4设置为GPIO输入模式、上拉模式
  • USART1选择为异步通讯方式,波特率设置为115200Bits/s,传输数据长度为8Bit,无奇偶校验,1位停止位;开启串口中断
  • 激活TIM3定时器,时钟源选择为内部时钟,PSC预分频设置为7200-1,向上计数,自动重装载值(ARR)设置为5000-1,激活TIM3定时器中断;根据公式可算出:计数器时钟CK_CNT = 72M/7200 = 10000Hz,计时器中断时间为 ARR/10000 = 500ms。可参考定时器中断介绍
  • 激活FreeRTOS,添加任务,设置任务名称、优先级、堆栈大小、函数名称等参数

  • 添加队列Msg_Queue和Key_Queue,如下图设置

  • 使用FreeRTOS操作系统,一定要将HAL库的Timebase Source从SysTick改为其他定时器,选好定时器后,系统会自动配置TIM
  • 输入工程名,选择路径(不要有中文),选择MDK-ARM V5;勾选Generated periphera initialization as a pair of ‘.c/.h' files per IP ;点击GENERATE CODE,生成工程代码
2. MDK-ARM软件编程
  • 创建按键驱动文件key.c和key.h,参考 按键输入 例程
  • 添加KeyscanTask、KeyprocessTask任务函数代码
/******************KeyscanTask*******************/
//扫描按键键值,并将键值入队到Key_Queue队列中
void KeyscanTask(void const * argument){
  uint8_t key;
  BaseType_t err;
  
  for(;;){
	key = KEY_Scan(0);
	if((Key_QueueHandle!=0)&&(key)){
	  err = xQueueSend(Key_QueueHandle,&key,10);
	  if(err == errQUEUE_FULL)
		printf("Key_Queue is Full, data send failed!\r\n");
	  else
		printf("Send data to Key_Queue successed!\r\n");
	}
    osDelay(10);
  }
}
/******************KeyprocessTask*******************/
//按键处理函数,从Key_Queue队列中读出键值,控制LED1/LED2的亮灭
void KeyprocessTask(void const * argument){
  uint8_t key_value;

  for(;;){
    if(Key_QueueHandle != 0){
	  if(xQueueReceive(Key_QueueHandle,&key_value,portMAX_DELAY)){
		switch(key_value)
		{
		  case KEY_UP_PRES:
			HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_0);
			printf("LED0 Togglen!\r\n");
			break;
		  case KEY_DOWN_PRES:
			HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_1);
			printf("LED1 Togglen!\r\n");
			break;
		}
	  }
	}
	osDelay(10);
  }
}
  • 添加串口中断回调函数:将串口输入的字符进行入队操作
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart){
  RxBuff[Rx_Count++]=RxByte;	
  if(RxByte==0x0A){
	xQueueSendFromISR(Msg_QueueHandle,RxBuff,NULL);	//进行入队操作	
	printf("Send CMD to Msg_Queue FromISR succesed!\r\n");
	Rx_Count=0;
  }
  if(Rx_Count > 8){
	printf("Wrong CMD, Please Check...!\r\n");
	memset(RxBuff,0,sizeof(RxBuff));
	Rx_Count=0;
  }	
  while(HAL_UART_Receive_IT(&huart1,&RxByte,1)==HAL_OK);
}
  • 添加定时器中断回调函数:每500ms检查一下Msg_Queue是否有数据,如果有数据从Msg_Queue中出队,并对字符串进行比较从而控制LED3/LED4的亮灭
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim){
  if (htim->Instance == TIM1) {
    HAL_IncTick();
  }
  if(htim == &htim3){
	uint8_t RxCMD[8];
	if(Msg_QueueHandle != 0){	
	  if(xQueueReceiveFromISR(Msg_QueueHandle,RxCMD,NULL)){					
		printf("Read CMD Msg_Queue from ISR is:");
		for(int i =0;i<8;i++)
		  printf("%c",RxCMD[i]);
		printf("\n");
	  }
			
	if(strncmp((char *)RxCMD,"LED3on",6) == 0)
	      HAL_GPIO_WritePin(GPIOC,GPIO_PIN_2,GPIO_PIN_RESET);		
	else if(strncmp((char *)RxCMD,"LED3off",6) == 0)
	      HAL_GPIO_WritePin(GPIOC,GPIO_PIN_2,GPIO_PIN_SET);	
	else if(strncmp((char *)RxCMD,"LED4on",6) == 0)
	      HAL_GPIO_WritePin(GPIOC,GPIO_PIN_3,GPIO_PIN_RESET);	
	else if(strncmp((char *)RxCMD,"LED4off",6) == 0)
	      HAL_GPIO_WritePin(GPIOC,GPIO_PIN_3,GPIO_PIN_SET);	
    }
  }
}

  • 在main.c中添加开启串口接收中断和定时器中断代码
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim){
  if (htim->Instance == TIM1) {
    HAL_IncTick();
  }
  if(htim == &htim3){
	uint8_t RxCMD[8];
	if(Msg_QueueHandle != 0){	
	  if(xQueueReceiveFromISR(Msg_QueueHandle,RxCMD,NULL)){					
		printf("Read CMD Msg_Queue from ISR is:");
		for(int i =0;i<8;i++)
		  printf("%c",RxCMD[i]);
		printf("\n");
	  }
			
	if(strncmp((char *)RxCMD,"LED3on",6) == 0)
	      HAL_GPIO_WritePin(GPIOC,GPIO_PIN_2,GPIO_PIN_RESET);		
	else if(strncmp((char *)RxCMD,"LED3off",6) == 0)
	      HAL_GPIO_WritePin(GPIOC,GPIO_PIN_2,GPIO_PIN_SET);	
	else if(strncmp((char *)RxCMD,"LED4on",6) == 0)
	      HAL_GPIO_WritePin(GPIOC,GPIO_PIN_3,GPIO_PIN_RESET);	
	else if(strncmp((char *)RxCMD,"LED4off",6) == 0)
	      HAL_GPIO_WritePin(GPIOC,GPIO_PIN_3,GPIO_PIN_SET);	
    }
  }
}

3. 下载验证

编译无误下载到开发板后,打开串口调试助手,串口输出串口中断和定时器中断开启成功信息;按下K_UP和K_DOWN按键,可以依次对LED1/LED2的亮灭状态进行翻转;串口中输入字符串“LED3on、LED3off、LED4on、LED4off”可以控制LED3/LED4的亮灭并打印出相应的调试信息

 

 

 

 

 


您可以捐助,支持我们的公益事业。

1元 10元 50元





认证码: 验证码,看不清楚?请点击刷新验证码 必填



219 次浏览
6次