使用状态机编程,编程过程比较繁琐,因此使用FreeRTOS。
只需要把几个独立的部分拆分成独立的任务,单独编写代码,按照mian方法进行编写即可。
例如,2个LED小灯,闪烁周期不同,LED1亮1s,灭1s循环,LED2亮0.3s,灭1.5s循环。有使用FreeRTOS和状态机2种方法。
状态机方法编写2个不同闪灯周期LED
定义变量
123456 | /* USER CODE BEGIN PV */uint8_t LED1State = 0;//状态,0-熄灭,1-点亮uint8_t LED3State = 0;uint32_t led1ChangeTime = 0;//记录上次变换状态的时间uint32_t led3ChangeTime = 0;/* USER CODE END PV */ |
LED的状态机程序
12345678910111213141516171819202122232425262728293031323334353637383940 | /* Private user code ---------------------------------------------------------*//* USER CODE BEGIN 0 */void LED1_StateMachine(void){ uint32_t now = HAL_GetTick();//记录当前时间,到now //如果上次切换亮灭时间大于1000ms,且当前状态是灭灯状态,那么点亮灯,记录状态,更新时间 if((LED1State == 0) && (now-led1ChangeTime >= 1000)) { HAL_GPIO_WritePin(LED1_GPIO_Port,LED1_Pin,GPIO_PIN_SET); LED1State = 1; led1ChangeTime = now; } //如果上次切换亮灭时间大于1000ms,且当前状态是亮灯状态,那么熄灯,记录状态,更新时间 if((LED1State == 1) && (now-led1ChangeTime >= 1000)) { HAL_GPIO_WritePin(LED1_GPIO_Port,LED1_Pin,GPIO_PIN_RESET); LED1State = 0; led1ChangeTime = now; } }void LED3_StateMachine(void){ uint32_t now = HAL_GetTick();//记录当前时间,到now //如果上次切换亮灭时间大于1000ms,且当前状态是灭灯状态,那么点亮灯,记录状态,更新时间 if((LED3State == 0) && (now-led3ChangeTime >= 1500)) { HAL_GPIO_WritePin(LED3_GPIO_Port,LED3_Pin,GPIO_PIN_SET); LED3State = 1; led3ChangeTime = now; } //如果上次切换亮灭时间大于1000ms,且当前状态是亮灯状态,那么熄灯,记录状态,更新时间 if((LED3State == 1) && (now-led3ChangeTime >= 300)) { HAL_GPIO_WritePin(LED3_GPIO_Port,LED3_Pin,GPIO_PIN_RESET); LED3State = 0; led3ChangeTime = now; }}/* USER CODE END 0 */ |
在while(1)中调用这2个函数
12345678 | /* USER CODE BEGIN WHILE */while (1){ LED1_StateMachine(); LED3_StateMachine(); /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */} |
FreeRTOS编写2个不同闪灯周期LED
先编写2个LED闪灯的程序。
1234567891011121314151617181920212223242526 | /* Private user code ---------------------------------------------------------*//* USER CODE BEGIN 0 */void vLED1Task(void *pvParameters){ while(1) { HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, GPIO_PIN_SET); vTaskDelay(pdMS_TO_TICKS(1000)); HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, GPIO_PIN_RESET); vTaskDelay(pdMS_TO_TICKS(1000)); } vTaskDelete(NULL);} void vLED3Task(void *pvParameters){ while(1) { HAL_GPIO_WritePin(LED3_GPIO_Port, LED3_Pin, GPIO_PIN_SET); vTaskDelay(pdMS_TO_TICKS(300)); HAL_GPIO_WritePin(LED3_GPIO_Port, LED3_Pin, GPIO_PIN_RESET); vTaskDelay(pdMS_TO_TICKS(1500)); } vTaskDelete(NULL);}/* USER CODE END 0 */ |
vTaskDelay(pdMS_TO_TICKS(1000));的作用是延时1000ms
在主程序中调用
12345 | /* USER CODE BEGIN 2 */xTaskCreate(vLED1Task, "LED1", 128, NULL, 1, NULL);xTaskCreate(vLED3Task, "LED3", 128, NULL, 1, NULL);vTaskStartScheduler();//启用调度器/* USER CODE END 2 */ |
xTaskCreate的作用就是创建任务,相当于是把任务代码交给调度器。
FreeRTOS程序的一般写法
123456789101112 | // 任务函数的一般写法// 任务函数使用void类型的返回值,但是有一个void*类型的参数void vTask1(void *pvParameters){ // 编程方式类似于main()方法,一般写一个无限循环 while(1) { // 使用vTaskDelay()而不使用HAL_Delay()进行延迟 } // 坚决不能写return! vTaskDelete(NULL); //一旦退出需删除自身} |
1234567891011121314151617 | int main(void){ //一些底层的初始化代码放这里 ......... //先创建任务 xTaskCreate(vTask1, "Task1", .....); //任务1 xTaskCreate(vTask2, "Task2", .....); //任务2 //再启动调度器 vTaskStartScheduler(); //无限循环 while (1) //代码不会执行到这里,除非因创建空闲任务或者定时器任务失败而退出 { }} |
在FreeRTOS中,延时函数不使用HAL_Delay,比如延时300msvTaskDelay(pdMS_TO_TICKS(300));