F3 freeRTOS示例
1.实现串口任务打印
1.1业务流程
- USART1初始化

- 静态创建任务

注意:创建静态任务必须将Memory Allocation设置为Dynamic/static模式,即使能动态/静态内存

- printf重定向,在usart.c下添加如下代码即可
/* USER CODE BEGIN 1 */
int fputc(int ch, FILE *stream)
{
/* 堵塞判断串口是否发送完成 */
while((USART1->SR & 0X40) == 0);
/* 串口发送完成,将该字符发送 */
USART1->DR = (uint8_t) ch;
return ch;
}
/* USER CODE END 1 */
- 任务内循环打印
void Uart_Task(void const * argument)
{
/* USER CODE BEGIN Uart_Task */
/* Infinite loop */
for(;;)
{
printf("Uart_Task is Runing!\r\n");
osDelay(1000);
}
/* USER CODE END Uart_Task */
}
1.2现象
- LED灯在闪烁
- 串口也在打印
Uart_Task is Runing!
Uart_Task is Runing!
Uart_Task is Runing!
Uart_Task is Runing!
Uart_Task is Runing!
Uart_Task is Runing!
...
2.按键检测任务
2.1功能需求
- 创建按键检测任务
- 当按键按下时候,挂起LED闪烁任务
- 当按键松开时,恢复LED闪烁任务
2.2业务流程
- 按键初始化,将对应按键引脚配置为中断模式并取个别名叫KEY

- 配置按键的中断触发模式,此处需求应该配置为上升/下降沿触发

- 使能中断

- 创建按键检测任务

- 按键中断服务函数实现1,在stm32f1xx_it.c里面找到EXIT4线的中断入口函数,可以看到里面调用了
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_4)函数,进入该函数如下,里面定义了一个回调函数HAL_GPIO_EXTI_Callback(GPIO_Pin),再进入回调函数
void EXTI4_IRQHandler(void)
{
/* USER CODE BEGIN EXTI4_IRQn 0 */
/* USER CODE END EXTI4_IRQn 0 */
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_4);
/* USER CODE BEGIN EXTI4_IRQn 1 */
/* USER CODE END EXTI4_IRQn 1 */
}
void HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin)
{
/* EXTI line interrupt detected */
if (__HAL_GPIO_EXTI_GET_IT(GPIO_Pin) != 0x00u)
{
__HAL_GPIO_EXTI_CLEAR_IT(GPIO_Pin);
HAL_GPIO_EXTI_Callback(GPIO_Pin);
}
}
//该函数为弱函数,当用户定义该函数时候会执行用户写的代码段,否则执行以下默认代码段
__weak void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
/* Prevent unused argument(s) compilation warning */
UNUSED(GPIO_Pin);
/* NOTE: This function Should not be modified, when the callback is needed,
the HAL_GPIO_EXTI_Callback could be implemented in the user file
*/
}
- 按键中断函数实现2
//在gpio.h里面定义枚举类型,保存按键状态
/* USER CODE BEGIN Private defines */
typedef enum
{
KEY_UP,
KEY_DOWN,
KEY_UNKNOW,
}Key_Status;
/* USER CODE END Private defines */
//在gpio.c里面初始化枚举类型,即初始化按键状态为未知状态
/* USER CODE BEGIN 0 */
Key_Status KeyStatus = KEY_UNKNOW;
/* USER CODE END 0 */
//在gpio.c内部实现中断回调函数
/* USER CODE BEGIN 2 */
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
if(GPIO_Pin == KEY_Pin)
{
if(HAL_GPIO_ReadPin(KEY_GPIO_Port, KEY_Pin) == SET)
{
/*延时去抖*/
HAL_Delay(1);
if(HAL_GPIO_ReadPin(KEY_GPIO_Port, KEY_Pin) == SET)
{
KeyStatus = KEY_UP;
}
}
else if(HAL_GPIO_ReadPin(KEY_GPIO_Port, KEY_Pin) == RESET)
{
HAL_Delay(1);
if(HAL_GPIO_ReadPin(KEY_GPIO_Port, KEY_Pin) == RESET)
{
KeyStatus = KEY_DOWN;
}
}
}
}
- 任务内部检测按键状态,根据状态进行挂起/恢复任务
void Key_Task(void const * argument)
{
/* USER CODE BEGIN Key_Task */
/* Infinite loop */
for(;;)
{
if(KeyStatus == KEY_DOWN)
{
/*挂起任务*/
vTaskSuspend(LEDTaskHandle);
KeyStatus = KEY_UNKNOW;
}
else if(KeyStatus == KEY_UP)
{
/*恢复任务*/
vTaskResume(LEDTaskHandle);
KeyStatus = KEY_UNKNOW;
}
osDelay(1);
}
/* USER CODE END Key_Task */
}
3.消息队列
3.1功能需求
- 通过串口输入字符分别控制2个LED灯
3.2业务流程
- 配置LED引脚为输出模式
- 串口中断配置,勾选串口中断选项

- 创建消息队列,其中设置消息队列长度为20,每个单位占1字节,采用动态创建

- 编写串口中断回调函数,由于HAL库针对串口的效率比较低,此处采用直接操作寄存器的方式来获取串口数据
void USART1_IRQHandler(void)
{
/* USER CODE BEGIN USART1_IRQn 0 */
uint8_t u8Data;//存储一字节临时变量
/*判断接收标志位*/
if(__HAL_UART_GET_FLAG(&huart1, UART_FLAG_RXNE) == SET)
{
u8Data = huart1.Instance->DR;//读取数据寄存器
xQueueSendFromISR(CmdQueueHandle, &u8Data, NULL);//在中断中将数据入队
}
/* USER CODE END USART1_IRQn 0 */
HAL_UART_IRQHandler(&huart1);
/* USER CODE BEGIN USART1_IRQn 1 */
/* USER CODE END USART1_IRQn 1 */
}
- 在函数中使能串口中断
void HAL_UART_MspInit(UART_HandleTypeDef* uartHandle)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
if(uartHandle->Instance==USART1)
{
/* USER CODE BEGIN USART1_MspInit 0 */
/* USER CODE END USART1_MspInit 0 */
/* USART1 clock enable */
__HAL_RCC_USART1_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
/**USART1 GPIO Configuration
PA9 ------> USART1_TX
PA10 ------> USART1_RX
*/
GPIO_InitStruct.Pin = GPIO_PIN_9;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_10;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/* USART1 interrupt Init */
HAL_NVIC_SetPriority(USART1_IRQn, 5, 0);
HAL_NVIC_EnableIRQ(USART1_IRQn);
/* USER CODE BEGIN USART1_MspInit 1 */
__HAL_UART_ENABLE_IT(uartHandle, UART_IT_RXNE);//使能接收中断
/* USER CODE END USART1_MspInit 1 */
}
}
- 使用面向对象中的封装特性来编写LED控制代码
- 先定义要控制的灯的个数
#define LED_NUM 2
- 将灯对应引脚存在数组中
/*保存指令的数组*/
uint8_t u8CmdBuff[20];
/*定义一个指针数组,保存字符串常量指针,此处存放开指令*/
uint8_t *OpenString[LED_NUM] = {
"openled0",
"openled1",
};
/*定义一个指针数组,保存字符串常量指针,此处存放关指令*/
uint8_t *CloseString[LED_NUM] = {
"closeled0",
"closeled1",
};
/*定义一个GPIO_TypeDef类型的指针数组,保存GPIO分组信息*/
GPIO_TypeDef *LedPort[LED_NUM] = {
LED0_GPIO_Port,
LED1_GPIO_Port,
};
/*定义一个数组,保存具体引脚号*/
uint16_t LedPin[LED_NUM] = {
LED0_Pin,
LED1_Pin,
};
- 串口任务,此处采用面向对象方法,不论加入多少灯,只需要在之前的数组中添加相应元素即可,而不是一味地加switch
void vParseString(uint8_t *buff)
{
uint8_t i;
/*解析开灯指令*/
for(i = 0; i < LED_NUM; i++)
{
if(strcmp((char const *)buff, (char const *)OpenString[i]) == 0)
{
HAL_GPIO_WritePin(LedPort[i], LedPin[i], GPIO_PIN_RESET);
printf("cmd is %s\n", OpenString[i]);
return;
}
}
/*解析关灯指令*/
for(i = 0; i < LED_NUM; i++)
{
if(strcmp((char const *)buff, (char const *)CloseString[i]) == 0)
{
HAL_GPIO_WritePin(LedPort[i], LedPin[i], GPIO_PIN_SET);
printf("cmd is %s\n", CloseString[i]);
return;
}
}
}
/* USER CODE END Header_Uart_Task */
void Uart_Task(void const * argument)
{
/* USER CODE BEGIN Uart_Task */
uint8_t u8Index;
/* Infinite loop */
for(;;)
{
/*每次读取消息的时候将索引值初始化为0*/
u8Index = 0;
/*一直等待接收消息,第一个消息应该放在消息缓冲区的第一个元素上*/
/*传入portMAX_DELAY表示函数在该处阻塞,直到有数据*/
if(xQueueReceive(CmdQueueHandle, &u8CmdBuff[u8Index++], portMAX_DELAY) == pdPASS)
{
/*一直接收数据,直到消息队列为空*/
while(xQueueReceive(CmdQueueHandle, &u8CmdBuff[u8Index++], 50))
{
}
/*加入结束标志,保证数据完整性*/
u8CmdBuff[u8Index] = '\0';
/*字符解析函数*/
vParseString(u8CmdBuff);
/*清空缓冲区*/
memset(u8CmdBuff, 0, 20);
}
}
/* USER CODE END Uart_Task */
}
4.软件定时器
4.1功能需求
- 使用软件定时器功能完成闹钟设计
- 当闹钟时间到达的时候,可根据执行动作,触发LED亮灭
4.2实现方案
- 用户通过串口终端设置闹钟参数
- 内部通过RTC实时时钟来计时
- 通过GPIO来提示
4.3格式定义
- 设置实时时钟
参数头:年-月-日,时:分:秒
realtime:2020-9-13,12:00:00
- 设置闹钟参数
参数头:时:分:秒,是否重复,操作LED动作
alarmtime:12:05:00,0,0
4.4业务流程
-
实时时钟,RTC功能开发
-
命令参数配置,串口解析功能开发
-
软件定时器功能
-
多任务消息同步

浙公网安备 33010602011771号