公司动态
b站铁头山羊freertos入门篇学习1
FreeRTOS英文名为 real_timeOS,TSOS为分时操作系统(windos系统)详细见:FreeRTOS官方网站地址 www.freertos.org/如果我们要在开发版编写以下程序让LED1每次点亮100ms、熄灭100ms同时让LED3每次点亮50ms、熄灭150ms。我们注意到这两个LED的亮灭周期都为200ms,程序相对来说比较好设计文件main.c int main(void) { ....... while (1) { // 1. 同时点亮LED1和LED3并延迟50ms HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, GPIO_PIN_SET); HAL_GPIO_WritePin(LED3_GPIO_Port, LED3_Pin, GPIO_PIN_SET); HAL_Delay(50); // 2. 熄灭LED3并再次延迟50ms HAL_GPIO_WritePin(LED3_GPIO_Port, LED3_Pin, GPIO_PIN_RESET); HAL_Delay(50); // 3. 熄灭LED1并延迟100ms HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, GPIO_PIN_RESET); HAL_Delay(100); } }那如果变复杂一点,比如说周期不一样那么就需要用到状态机的思想。LED1仍然点亮100ms、熄灭100ms但LED3的情况有所变化这次它亮灯30ms、灭灯150ms。首先需要画出状态转换图:接着写代码的思路应该为:需要获取当前时间设置好状态变量。代码见下文件main.c uint8_t led1State 0; // LED1的状态0 - 熄灭1 - 点亮 uint8_t led3State 0; // LED3的状态0 - 熄灭1 - 点亮 uint32_t led1LastToggleTime 0; // LED1上次翻转的时间 uint32_t led3LastToggleTime 0; // LED3上次翻转的时间 // 作用LED1的状态机 void LED1_Proc(void) { uint32_t now HAL_GetTick(); // 获取当前时间 switch(led1State) { case 0: // 当前处于熄灭状态 if(HAL_GetTick() - led1LastToggleTime 100) // 超过100ms后点亮 { HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, GPIO_PIN_SET); led1State 1; // 状态机切换到点亮状态 led1LastToggleTime now; } break; case 1: // 当前处于点亮状态 if(HAL_GetTick() - led1LastToggleTime 100) // 超过100ms后熄灭 { HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, GPIO_PIN_RESET); led1State 0; // 状态机切换到熄灭状态 led1LastToggleTime now; } break; default:break; } } // 作用LED3的状态机 void LED3_Proc(void) { uint32_t now HAL_GetTick(); // 获取当前时间 switch(led3State) { case 0: // 当前处于熄灭状态 if(HAL_GetTick() - led3LastToggleTime 150) // 超过150ms后点亮 { HAL_GPIO_WritePin(LED3_GPIO_Port, LED3_Pin, GPIO_PIN_SET); led3State 1; // 状态机切换到点亮状态 led3LastToggleTime now; } break; case 1: // 当前处于点亮状态 if(HAL_GetTick() - led3LastToggleTime 30) // 超过30ms后熄灭 { HAL_GPIO_WritePin(LED3_GPIO_Port, LED3_Pin, GPIO_PIN_RESET); led3State 0; // 状态机切换到熄灭状态 led3LastToggleTime now; } break; default:break; } } int main(void) { ......... while (1) { LED1_Proc(); // 循环运行LED1的状态机 LED3_Proc(); // 循环运行LED3的状态机 } }我们发现这么简单的任务需要的代码比较复杂需要编写状态机所以为了简化流程我们需要学习FreeRTOS。怎么用freertos创建任务呢下面是简单任务的创建代码// 任务函数的一般写法 // 任务函数使用void类型的返回值但是有一个void*类型的参数 void vTask1(void *pvParameters) { // 编程方式类似于main()方法一般写一个无限循环 for(;;) { // 使用vTaskDelay()而不使用HAL_Delay()进行延迟 } // 坚决不能写return vTaskDelete(NULL); //一旦退出需删除自身 }主函数的写法:int main(void) { //一些底层的初始化代码放这里 ......... //先创建任务 xTaskCreate(vTask1, Task1, .....); //任务1 xTaskCreate(vTask2, Task2, .....); //任务2 //再启动调度器 vTaskStartScheduler(); //无限循环 while (1) //代码不会执行到这里除非因创建空闲任务或者定时器任务失败而退出 { } }接下来我们来了解freertos的总图由于框图我们可以看出核心是调度器不一样的调度算法决定了任务之间如何切换以及什么时间点切换接着看到任务这一栏空闲任务和定时器任务是两个特殊的任务这两个任务必须被创建创建失败则启动rtos失败定时器任务我们后面在看先来了解空闲任务看到以下这段代码文件main.c // 作用LED1的任务函数 void vLED1Task(void *pvParameters) { while(1) { HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, GPIO_PIN_SET); // 点亮 vTaskDelay(pdMS_TO_TICKS(100)); // 延迟100ms HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, GPIO_PIN_RESET); // 熄灭 vTaskDelay(pdMS_TO_TICKS(100)); // 延迟100ms } } int main(void) { xTaskCreate(vLED1Task, LED1, 128, NULL, 1, NULL); // 创建LED1任务 while (1) { } }我们用的是vTaskDelay这个函数任务调用该函数后系统内核将该任务从“就绪列表”移动到“阻塞列表”并立即触发任务调度器这个时候cpu执行空闲任务。而我们熟知的HAL_Delay()它让 CPU 依然处于运行态。它虽然在“空转”但并没有停止工作。接下来看三个中断在调度器的上方也有3个中断Systick中断、PendSV中断和SVC中断。这3个中断大家可能没见过它们比较特殊都是跟内核相关的所以大家见得不多。这里先跟大家说一下它们的地位这3种中断是FreeRTOS调度器赖以生存的底层硬件驱动调度器运行的原动力。其中Systick中断用于产生系统节拍计时用的并驱动调度器的“时间片机制”刚刚讲过跟调度算法有关SVC中断用来启动第一个任务PendSV中断用来执行后续的任务切换2.2内核的文件结构文件的分类在kernal这一栏task和list必须添加其他的根据需要我们需要知道每个文件的中文名portable为移植层移植层为FreeRTOS内核和某款单片机之间的媒介config文件里是各种宏定义介绍几个常见的配置项配置项1configCPU_CLOCK_HZ - 内核的运行频率告诉FreeRTOS你的单片机的内核频率配置项2configTICK_TYPE_WIDTH_IN_BITS - 设置TickType_t类型的位宽配置项3configKERNEL_INTERRUPT_PRIORITY - PendSV和SYSTICK的中断优先级它们分别是SVC中断、PendSV中断和SYSTICK中断。SVC中断默认设置最高优先级剩下的两个中断的优先级由该配置项设置。下面需要进行中断服务程序的配置3.3回顾c语言基础熟悉类型的变量以及变量的作用域和生命周期还有加上static后对全局变量和局部变量的影响SRAM被划分为不同的区域:(SRAM的起始地址是0x2000 0000长度为48k字节)堆:使用malloc()函数时数据会被存储到堆中而需要的空间为:数据的大小信息头(8个字节).当我们调用xTaskCreate()创建一个任务的时候在xTaskCreate()内部会自动调用pvPortMalloc()为新创建的任务在堆里分配一段内存用来保存这个新任务的关键数据。其中pvPortMalloc()是FreeRTOS里用来管理堆内存的编程接口它的功能和标准C语言里的malloc()相似。栈:每次调用时需要保存上下文以便返回时恢复。这里所说的“上下文”包括参数、局部变量和返回地址。在freertos中每个任务都有自己栈因为在freertos中任务是并行的,需要在各个任务之间来回切换。编写代码时的规范:特别注意:xTaskGetTickCount(void);返回值为系统节拍数如果需要的是时间需要在使用pdTICKS_TO_MS()宏将获取到的系统节拍转化为毫秒级的时间。变量的命名规则:类型前缀”和“实际名称”两部分组成举个例子函数的命名规则:当函数前边有static关键字时静态方法前缀应该替换为prvprv private 私有的表示这个方法是私有的作用域在文件内部。