当前位置: 首页 > news >正文

FreeRTOS实时操作系统(二)系统文件代码学习

文章目录

  • 前言
  • 系统配置
  • 任务创建
  • 任务创建删除实践


前言

接着学习正点原子的FreeRTOS教程,涉及到一些详细的系统内文件代码


系统配置

可以通过各种的宏定义来实现我们自己的RTOS配置(在FreeRTOSconfig.h)

  1. “INCLUDE”:配置API函数
  2. ”config“:完成功能配置和裁剪
  3. 其他配置项LPendSV宏定义,SVC宏定义
    在这里插入图片描述

任务创建

分为静态和动态创建

动态任务创建:任务的任务控制块以及任务的栈空间所需的内存,均由自动从堆中分配

静态创建任务:任务的任务控制块以及任务的栈空间所需的内存,需用户分配提供


任务创建:
在这里插入图片描述
返回值:
pdPASS:创建成功
errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY:创建失败(内存分配失败)

如何成功创建:

  1. 将宏configSUPPORT_DYNAMIC_ALLOCATION 配置为 1
  2. 定义函数入口参数
  3. 编写任务函数

任务控制块:
在这里插入图片描述
每个任务都有属于自己的任务控制块,类似身份证。


静态创建任务:
在这里插入图片描述
返回值:
NULL:用户没有提供相应的内存,任务创建失败
其他值:任务句柄,任务创建成功

如何成功创建:

  1. 需将宏configSUPPORT_STATIC_ALLOCATION 配置为 1
  2. 定义空闲任务&定时器任务的任务堆栈及TCB
  3. 实现两个接口函数:vApplicationGetIdleTaskMemory( ) ,vApplicationGetTimerTaskMemory ( )
  4. 定义函数入口参数
  5. 编写任务函数

任务删除:
void vTaskDelete(TaskHandle_t xTaskToDelete);
参数是待删除任务的任务句柄,用于删除已被创建的任务,被删除的任务将从就绪态任务列表、阻塞态任务列表、挂起态任务列表和事件列表中移除

要点:

  1. 当传入的参数为NULL,则代表删除任务自身(当前正在运行的任务)
  2. 空闲任务会负责释放被删除任务中由系统分配的内存,但是由用户在任务删除前申请的内存, 则需要由用户在任务被删除前提前释放,否则将导致内存泄露

如何删除:

  1. 使用删除任务函数,需将宏INCLUDE_vTaskDelete 配置为 1
  2. 入口参数输入需要删除的任务句柄(NULL代表删除本身)

任务创建删除实践

1.任务创建删除:(正点原子的代码)

 xTaskCreate((TaskFunction_t         )   task1,(char *                 )   "task1",(configSTACK_DEPTH_TYPE )   TASK1_STACK_SIZE,(void *                 )   NULL,(UBaseType_t            )   TASK1_PRIO,(TaskHandle_t *         )   &task1_handler );

利用该函数创建任务,从上到下分别是指向任务函数的指针,任务名,堆栈大小,任务指针参数,优先级,任务句柄。但是我感觉不需要强制类型转换,有时写错也不容易发现

一般系统初始化的操作是先创建一个任务,这个任务完成系统所有任务的创建,创建完成后把自己删除掉的结构,这样代码比较简洁,结构清晰。

/* START_TASK 任务 配置* 包括: 任务句柄 任务优先级 堆栈大小 创建任务*/
#define START_TASK_PRIO         1
#define START_TASK_STACK_SIZE   128
TaskHandle_t    start_task_handler;
void start_task( void * pvParameters );/* TASK1 任务 配置* 包括: 任务句柄 任务优先级 堆栈大小 创建任务*/
#define TASK1_PRIO         2
#define TASK1_STACK_SIZE   128
TaskHandle_t    task1_handler;
void task1( void * pvParameters );/* TASK2 任务 配置* 包括: 任务句柄 任务优先级 堆栈大小 创建任务*/
#define TASK2_PRIO         3
#define TASK2_STACK_SIZE   128
TaskHandle_t    task2_handler;
void task2( void * pvParameters );/* TASK3 任务 配置* 包括: 任务句柄 任务优先级 堆栈大小 创建任务*/
#define TASK3_PRIO         4
#define TASK3_STACK_SIZE   128
TaskHandle_t    task3_handler;
void task3( void * pvParameters );
void freertos_demo(void)  //先创建一个任务{    xTaskCreate((TaskFunction_t         )   start_task,(char *                 )   "start_task",(configSTACK_DEPTH_TYPE )   START_TASK_STACK_SIZE,(void *                 )   NULL,(UBaseType_t            )   START_TASK_PRIO,(TaskHandle_t *         )   &start_task_handler );vTaskStartScheduler();  //开始调度,否则不会执行start_task任务
}void start_task( void * pvParameters )  //这个任务完成所有任务的创建
{taskENTER_CRITICAL();               /* 进入临界区 */xTaskCreate((TaskFunction_t         )   task1,(char *                 )   "task1",(configSTACK_DEPTH_TYPE )   TASK1_STACK_SIZE,(void *                 )   NULL,(UBaseType_t            )   TASK1_PRIO,(TaskHandle_t *         )   &task1_handler );xTaskCreate((TaskFunction_t         )   task2,(char *                 )   "task2",(configSTACK_DEPTH_TYPE )   TASK2_STACK_SIZE,(void *                 )   NULL,(UBaseType_t            )   TASK2_PRIO,(TaskHandle_t *         )   &task2_handler );xTaskCreate((TaskFunction_t         )   task3,(char *                 )   "task3",(configSTACK_DEPTH_TYPE )   TASK3_STACK_SIZE,(void *                 )   NULL,(UBaseType_t            )   TASK3_PRIO,(TaskHandle_t *         )   &task3_handler );........................vTaskDelete(NULL);taskEXIT_CRITICAL();                /* 退出临界区 */
}/* 任务一,实现LED0每500ms翻转一次 */
void task1( void * pvParameters )
{while(1){printf("task1正在运行!!!\r\n");LED0_TOGGLE();vTaskDelay(500);}
}
.................
.................
................

这里vTaskStartScheduler()函数执行后,系统就会开始执行任务,但是很明显,真正的任务都没开始创建,且因为系统按照从上往下执行,所以会先创建的任务先执行,不按照优先级走,等所有任务创建完成后,系统才开始真正按照优先级开始执行任务。

我觉得FreeRTOS的执行过程就是一个一个任务的执行,他是通过执行完一个任务后,再执行下一个,遇到优先级比它高的任务,会被打断,这也就是为什么创建task1后,系统就停下了start_task工作,执行task1的代码了,执行完task1后,又回去初始化task2。感觉 vTaskDelay()函数是系统调度的一个灵活工具。让系统从循环执行任务(还是很像裸机),开始进行灵活分配cpu了

这里用到了临界区,FreeRTOS临界区是指那些必须完整运行,不能被打断的代码段,比如有的外设的初始化需要严格的时序,初始化过程中不能被打断。进入临界区代码的时候需要关闭中断,当处理完临界区代码以后再打开中断。起到一个代码保护的作用。

感觉这个FreeRTOS和之间裸机开发时,看到很多人的程序利用uwTick的数值来执行程序很像,但当然FreeRTOS强大的多,如下:

__weak void HAL_IncTick(void) //利用滴答定时器(一般1ms累加一次)
{uwTick += uwTickFreq;
}void deme()
{if(uwTick-led_tick>1000) //实现每隔1000个计数执行一次代码{led_tick=uwTick;..................}}

2.HAL库创建任务:
利用STM32CubeMX生成的FreeRTOS来实现,与正点原子(手动移植)有些区别

我这里简单写了一个点灯任务进行测试,可以正常运行

TaskHandle_t  task1_handler;void vTaskCode( void * pvParameters ){while(1){HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_4); vTaskDelay(500);}}void vOtherFunction( void ){xTaskCreate( vTaskCode, "tak1", 128, NULL, 1, &task1_handler );//vTaskStartScheduler();}int main(void)
{/* MCU Configuration--------------------------------------------------------*//* Reset of all peripherals, Initializes the Flash interface and the Systick. */HAL_Init();/* Configure the system clock */SystemClock_Config();/* Initialize all configured peripherals */MX_GPIO_Init();/* Init scheduler */osKernelInitialize();  /* Call init function for freertos objects (in freertos.c) */MX_FREERTOS_Init();  vOtherFunction();   //放在操作系统内核启动前/* Start scheduler */  osKernelStart();while (1){}
}

有些不懂的地方,首先我自己不需要启动任务调度了,当然执行这个函数vTaskStartScheduler()也没啥问题,但是这个任务创建放的位置需要在osKernelStart()前面,正点原子的如下,不是特别理解,为啥放在操作系统内核启动,开始任务调度后就不能运行了。

查了查资料,可能是任务调度开启之后,就正式进入FreeRTOS系统接管领域,之后程序只会跑在中断和任务函数中,也就是说如果放在后面,根本就不会执行,一个任务都没创建。而正点原子手动开启任务调度前已经创建了一个任务,所以他才成功了。

int main(void)
{HAL_Init();                                 /* 初始化HAL库 */sys_stm32_clock_init(360, 25, 2, 8);        /* 设置时钟,180Mhz */delay_init(180);                            /* 延时初始化 */usart_init(115200);                         /* 串口初始化为115200 */led_init();                                 /* 初始化LED */key_init();                                 /* 初始化按键 */sdram_init();                               /* SRAM初始化 */lcd_init();                                 /* 初始化LCD */my_mem_init(SRAMIN);                        /* 初始化内部内存池 */my_mem_init(SRAMEX);                        /* 初始化外部内存池 */my_mem_init(SRAMCCM);                       /* 初始化CCM内存池 */    freertos_demo();  //任务创建
}

终于算开始入门了,之后继续学习。

http://www.lryc.cn/news/93617.html

相关文章:

  • 分布式驱动电动汽车定速巡航控制
  • 如何启动和关闭分布式集群
  • WLAN基本概述及简单组网配置
  • 响应式Web设计单元测试
  • linux计划任务管理
  • 研一,有点迷茫。
  • 【新版】系统架构设计师 - 软件工程
  • html实现好看的个人介绍,个人主页模板3(附源码)
  • 某大厂工作3年,被劣驱良了。。。
  • 爱奇艺大数据加速:从Hive到Spark SQL
  • c++构造函数的多个细节拷问
  • Redis入门 - Lua脚本
  • Creating Serial Numbers (C#)
  • pycharm使用之torch_geometric安装
  • spring-mvc 工作流程
  • 物联网Lora模块从入门到精通(六)OLED显示屏
  • 平面坐标变换(单应性变换/Homography变换)
  • 大数据项目常识
  • Linux系统:常用服务端口
  • 前端和后端分别是什么?
  • Spring基础知识(一)
  • 易语言使用node编译的js文件
  • 计算机网络笔记:动态主机配置协议(DHCP)
  • 新买的电脑怎么用U盘重装系统?新买的电脑用U盘重装系统教程
  • 图像边缘检测原理
  • 爬虫利器 Beautiful Soup 之遍历文档
  • 12、Nginx高级之高级模块(secure_link/secure_link_md5)
  • 【python】数据可视化,使用pandas.merge()对dataframe和geopandas类型数据进行数据对齐
  • 大模型入门(三)—— 大模型的训练方法
  • 人机交互学习-4 交互设计过程