【Let'sdo第四期-液体流量检测仪DIY】过程帖04 使用FreeRTOS
将裸机代码完善成FreeRTOS框架下的代码
void StartDefaultTask(void *argument) { /* USER CODE BEGIN StartDefaultTask */ /* Infinite loop */ for(;;) { HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5); // LED vTaskDelay(500); } /* USER CODE END StartDefaultTask */ }
手动创建三个任务,分别是按键任务,继电器任务,OLED任务
TaskHandle_t xTaskOLED_Handle; // OLED显示任务句柄 TaskHandle_t xTaskKey_Handle; // 按键任务句柄 TaskHandle_t xTaskRelay_Handle; // 继电器任务句柄 BaseType_t xRelay_Handle = xTaskCreate(Relay_Task, "Relay_Task", 128, NULL, (UBaseType_t)osPriorityNormal, &xTaskRelay_Handle); BaseType_t xKey_Handle = xTaskCreate(KEY_Task, "KEY_Task", 128, NULL, (UBaseType_t)osPriorityNormal + 1, &xTaskKey_Handle); BaseType_t xOLED_Handle = xTaskCreate(OLED_Show_Func, "OLED_Task", 256, NULL, (UBaseType_t)osPriorityNormal + 2, &xTaskOLED_Handle);
创建一个软件定时器,10ms扫描一次按键,刷新按键任务的键值
xTimerKey = xTimerCreate( "KeyScanTimer", // 定时器名称 (10), // 定时器周期,10 ms pdTRUE, // 自动重装 (void *)0, // 定时器 ID,这里没有特殊用途 (TimerCallbackFunction_t)vScanKeyCallback // 定时器到期时调用的函数 );
#include "TaskFunction.h" TimerHandle_t xTimerKey; uint32_t flow_cnt = 0; uint8_t OLED_View_Flag = 0; //OLED界面切换FLAG float Moment_Flow = 0.0f; // 瞬间流量 float Cumulative_Flow = 0.0f; // 累计流量 uint16_t TargetFlow = 0; //目标水量 - 100ML uint32_t Target_CNT = 0; void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) { if (GPIO_Pin == GPIO_PIN_2) { flow_cnt ++; } } // 软件定时器-扫描按键-回调函数 void vScanKeyCallback(TimerHandle_t xTimer) { // 定时 10ms获取一次按键键值 // static unsigned char led; key_serv_double(); // 测试软件定时器是否正常工作 // if (++led >= 100) // 1S // { // HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5); // LED // led = 0; // } } void OLED_Show_Func(void *params) { OLED_Init(); OLED_Clear(); OLED_ShowChinese(8*6, 0, "得捷"); OLED_Printf(0, 16, OLED_8X16, " Waiting... "); OLED_Update(); vTaskDelay(1800); OLED_Clear(); for(;;) { char tempStr[20]; if (OLED_View_Flag == 0) { OLED_ShowChinese(8 * 2, 16 * 0, "瞬间流量"); OLED_ShowChar(8 * 10, 16 * 0 + 8, ':', OLED_6X8); // 显示: // sprintf(tempStr, "%d %d ", flow_cnt, TargetFlow); sprintf(tempStr, " %.1f mL/s %d ", Moment_Flow, TargetFlow); OLED_Printf(8 * 4, 16 * 1 + 5, OLED_6X8, tempStr); OLED_ShowChinese(8 * 2, 16 * 2, "累计流量"); OLED_ShowChar(8 * 10, 16 * 2 + 8, ':', OLED_6X8); // 显示: Cumulative_Flow = (float)flow_cnt / CNT_Flow_1ML; sprintf(tempStr, " %.2f mL", Cumulative_Flow); OLED_Printf(8 * 4, 16 * 3 + 5, OLED_6X8, tempStr); } else { OLED_ShowChinese(8*6, 16*1, "得捷"); sprintf(tempStr, "Target:%d ", TargetFlow); OLED_Printf(8 * 4, 16 * 2 + 5, OLED_6X8, tempStr); } OLED_Update(); vTaskDelay(100); } } void KEY_Task(void *argument) { xTimerStart(xTimerKey, 0); KEY_GPIO_Init(); // 按键引脚的初始化 for(;;) { if (Key[1].short_flag == 1) { TargetFlow += 100; //目标值+=100 Key[1].short_flag = 0; } if (Key[1].long_flag == 1) { TargetFlow += 10; // Key[1].long_flag = 0; } if (Key[1].double_flag == 1) { OLED_Clear(); OLED_View_Flag++; // 界面的切换 if (OLED_View_Flag >= 2) // 2个界面 OLED_View_Flag = 0; Key[1].double_flag = 0; } vTaskDelay(100); } } void Relay_Task(void *argument) { static uint32_t Last_CNT; //上次计数 static uint32_t error_Flow; //前后两次的流量差 HAL_GPIO_WritePin(GPIOC, GPIO_PIN_10, GPIO_PIN_RESET); // 默认 继电器 off // HAL_GPIO_WritePin(GPIOC, GPIO_PIN_10, GPIO_PIN_SET); // 继电器 on for(;;) { Target_CNT = TargetFlow*CNT_Flow_1ML; if (flow_cnt <= TargetFlow*CNT_Flow_1ML) // 当前小于目标,未到,开继电器 { HAL_GPIO_WritePin(GPIOC, GPIO_PIN_10, GPIO_PIN_SET); // 继电器 on } else if (flow_cnt >= TargetFlow*CNT_Flow_1ML) //如果当前计数值 >= 目标计数值,关闭继电器 { HAL_GPIO_WritePin(GPIOC, GPIO_PIN_10, GPIO_PIN_RESET); // 继电器 off } error_Flow = flow_cnt - Last_CNT; //前后两次的流量差 20ms Moment_Flow = (float)(error_Flow * (1000/20)) / CNT_Flow_1ML; //计算瞬间流量 // error_Flow * (1000/20) === 20ms的流量*50, 等于1S的流量 Last_CNT = flow_cnt; vTaskDelay(20); // 不能修改这个20ms,需要和计算公式同步修改 } }头文件
#ifndef __TASKFUNCTION_H #define __TASKFUNCTION_H #include "stm32f1xx_hal.h" #include "OLED.h" #include "myoldkey.h" #include "FreeRTOS.h" #include "task.h" #include "timers.h" #define CNT_Flow_1ML 355.357f extern TimerHandle_t xTimerKey; void vScanKeyCallback(TimerHandle_t xTimer); // 软件定时器-扫描按键-回调函数 void OLED_Show_Func(void *params); // OLED显示任务 void KEY_Task(void *params); // 按键任务 void Relay_Task(void *argument); // 继电器任务 #endif
代码:https://gitee.com/Yancl0416/diy-liquid-flow-detector