初学FreeRTOS实现多任务程序

2023-05-16

初学FreeRTOS实现多任务程序

  • 1 什么是FreeRTOS
  • 2 STM32下FreeRTOS移植
    • 2.1 准备工作
    • 2.2 移植更改
      • 2.2.1 新建分组
      • 2.2.2 添加相应的头文件路径
      • 2.2.3 修改 SYSTEM 文件
  • 3 多任务程序的实现
    • 3.1 修改main.c
    • 3.2 烧录结果

1 什么是FreeRTOS

  • Free 即免费的,RTOS 全称是 Real Time Operating System,中文就是实时操作系统。注意,RTOS 不是指某一个确定的系统,而是指一类系统。比如 uC/OS,FreeRTOS,RTX,RT-Thread 等这些都是 RTOS 类操作系统。
  • 操作系统允许多个任务同时运行,这个叫做多任务。实际上,一个处理器核心在某一时刻只能运行一个任务。操作系统中任务调度器的责任就是决定在某一时刻究竟运行哪个任务。任务调度在各个任务之间的切换非常快,就给人们造成了同一时刻有多个任务同时运行的错觉。
  • 某些操作系统给每个任务分配同样的运行时间,时间到了就轮到下一个任务,比如Unix 操作系统。 FreeRTOS 操作系统则是由用户给每个任务分配一个任务优先级,任务调度器就可以根据此优先级来决定下一刻应该运行哪个任务。
  • FreeRTOS 是 RTOS 系统的一种,FreeRTOS 十分的小巧,可以在资源有限的微控制器中运行,当然,FreeRTOS 不仅局限于在微控制器中使用。但从文件数量上来看 FreeRTOS 要比uC/OSII 和 uC/OSIII 小的多。
    ([此处来源于博客)

2 STM32下FreeRTOS移植

2.1 准备工作

我们移植FreeRTOS需要一个基础工程,按照路径打开文件夹
在这里插入图片描述
把实验1 跑马灯实验文件复制到自己喜欢的文件夹
在这里插入图片描述
依次打开下面文件夹
在这里插入图片描述
将FreeRTOS源码拷贝到上面例程当中并新建文件夹
在这里插入图片描述

2.2 移植更改

2.2.1 新建分组

打开基础工程,新建分组 FreeRTOS_COREFreeRTOS_PORTABLE,然后向这两个分组中添加文件
在这里插入图片描述
文件路径分别为
第一个分组
在这里插入图片描述
在这里插入图片描述
第二个分组
在这里插入图片描述

2.2.2 添加相应的头文件路径

添加完 FreeRTOS 源码中的 C 文件以后还要添加 FreeRTOS 源码的头文件路径
在这里插入图片描述
接下来还需要一个头文件,否则编译会出错
头文件路径如下,把它放到include文件夹即可
在这里插入图片描述

2.2.3 修改 SYSTEM 文件

2.2.3.1 修改 sys.h 文件
在分组SYSTEM-sys.c-sys.h中修改如下

//改前
#define SYSTEM_SUPPORT_OS 0 //定义系统文件夹是否支持 OS
//改后
#define SYSTEM_SUPPORT_OS 1 //定义系统文件夹是否支持 OS

2.2.3.2 修改 usart.c 文件
在分组SYSTEM-usart.c中修改如下

//改前
#if SYSTEM_SUPPORT_OS
#include "includes.h"//ucos 使用
#endif
//改后
#if SYSTEM_SUPPORT_OS
#include "FreeRTOS.h" //os 使用
#endif

还有一个中断函数改如下

//改后
void USART1_IRQHandler(void) //串口 1 中断服务程序
{
	u8 Res;
	if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
	{
		Res =USART_ReceiveData(USART1); //读取接收到的数据
		if((USART_RX_STA&0x8000)==0) //接收未完成
		{
			if(USART_RX_STA&0x4000) //接收到了 0x0d
			{
				if(Res!=0x0a)USART_RX_STA=0; //接收错误,重新开始
				else USART_RX_STA|=0x8000; //接收完成了
			}
			else //还没收到 0X0D
			{
				if(Res==0x0d)USART_RX_STA|=0x4000;
				else
				{
					USART_RX_BUF[USART_RX_STA&0X3FFF]=Res ;
					USART_RX_STA++;
					if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;
				}
			}
		}
	}
}

2.2.3.3 修改 delay.c 文件
修改如下:

extern void xPortSysTickHandler(void);

//systick中断服务函数,使用ucos时用到
void SysTick_Handler(void)
{	
    if(xTaskGetSchedulerState()!=taskSCHEDULER_NOT_STARTED)//系统已经运行
    {
        xPortSysTickHandler();	
    }
}
oid delay_init()
{
	u32 reload;
	SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK);//选择外部时钟  HCLK
	fac_us=SystemCoreClock/1000000;				//不论是否使用OS,fac_us都需要使用
	reload=SystemCoreClock/1000000;				//每秒钟的计数次数 单位为M  
	reload*=1000000/configTICK_RATE_HZ;			//根据configTICK_RATE_HZ设定溢出时间
												//reload为24位寄存器,最大值:16777216,在72M下,约合0.233s左右	
	fac_ms=1000/configTICK_RATE_HZ;				//代表OS可以延时的最少单位	   

	SysTick->CTRL|=SysTick_CTRL_TICKINT_Msk;   	//开启SYSTICK中断
	SysTick->LOAD=reload; 						//每1/configTICK_RATE_HZ秒中断一次	
	SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk;   	//开启SYSTICK    
}								 
//延时nus
//nus:要延时的us数.	
//nus:0~204522252(最大值即2^32/fac_us@fac_us=168)	    								   
void delay_us(u32 nus)
{		
	u32 ticks;
	u32 told,tnow,tcnt=0;
	u32 reload=SysTick->LOAD;				//LOAD的值	    	 
	ticks=nus*fac_us; 						//需要的节拍数 
	told=SysTick->VAL;        				//刚进入时的计数器值
	while(1)
	{
		tnow=SysTick->VAL;	
		if(tnow!=told)
		{	    
			if(tnow<told)tcnt+=told-tnow;	//这里注意一下SYSTICK是一个递减的计数器就可以了.
			else tcnt+=reload-tnow+told;	    
			told=tnow;
			if(tcnt>=ticks)break;			//时间超过/等于要延迟的时间,则退出.
		}  
	};										    
}  
//延时nms
//nms:要延时的ms数
//nms:0~65535
void delay_ms(u32 nms)
{	
	if(xTaskGetSchedulerState()!=taskSCHEDULER_NOT_STARTED)//系统已经运行
	{		
		if(nms>=fac_ms)						//延时的时间大于OS的最少时间周期 
		{ 
   			vTaskDelay(nms/fac_ms);	 		//FreeRTOS延时
		}
		nms%=fac_ms;						//OS已经无法提供这么小的延时了,采用普通方式延时    
	}
	delay_us((u32)(nms*1000));				//普通方式延时
}

//延时nms,不会引起任务调度
//nms:要延时的ms数
void delay_xms(u32 nms)
{
	u32 i;
	for(i=0;i<nms;i++) delay_us(1000);
}

在这里插入图片描述
即可修改完成。

3 多任务程序的实现

3.1 修改main.c

修改代码如下:

#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "FreeRTOS.h"
#include "task.h"

//任务优先级
#define START_TASK_PRIO		1
//任务堆栈大小	
#define START_STK_SIZE 		128  
//任务句柄
TaskHandle_t StartTask_Handler;
//任务函数
void start_task(void *pvParameters);


//任务优先级
#define LED0_TASK_PRIO		2
//任务堆栈大小	
#define LED0_STK_SIZE 		50  
//任务句柄
TaskHandle_t LED0Task_Handler;
//任务函数
void led0_task(void *pvParameters);


//任务优先级
#define USART0_TASK_PRIO		3
//任务堆栈大小	
#define USART0_STK_SIZE 		50  
//任务句柄
TaskHandle_t USART0Task_Handler;
//任务函数
void usart0_task(void *pvParameters);


//任务优先级
#define TEMPER0_TASK_PRIO		4
//任务堆栈大小	
#define TEMPER0_STK_SIZE 		50  
//任务句柄
TaskHandle_t TEMPER0Task_Handler;
//任务函数
void temper0_task(void *pvParameters);
int main(void)
{
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//设置系统中断优先级分组4	 
	delay_init();	    				//延时函数初始化	  
	uart_init(115200);					//初始化串口
	LED_Init();		  					//初始化LED
	 
	//创建开始任务
    xTaskCreate((TaskFunction_t )start_task,            //任务函数
                (const char*    )"start_task",          //任务名称
                (uint16_t       )START_STK_SIZE,        //任务堆栈大小
                (void*          )NULL,                  //传递给任务函数的参数
                (UBaseType_t    )START_TASK_PRIO,       //任务优先级
                (TaskHandle_t*  )&StartTask_Handler);   //任务句柄              
    vTaskStartScheduler();          //开启任务调度
}

//开始任务任务函数
void start_task(void *pvParameters)
{
    taskENTER_CRITICAL();           //进入临界区
    //创建LED0任务
    xTaskCreate((TaskFunction_t )led0_task,     	
                (const char*    )"led0_task",   	
                (uint16_t       )LED0_STK_SIZE, 
                (void*          )NULL,				
                (UBaseType_t    )LED0_TASK_PRIO,	
                (TaskHandle_t*  )&LED0Task_Handler);   
    //创建usart0任务
    xTaskCreate((TaskFunction_t )usart0_task,     
                (const char*    )"usart0_task",   
                (uint16_t       )USART0_STK_SIZE, 
                (void*          )NULL,
                (UBaseType_t    )USART0_TASK_PRIO,
                (TaskHandle_t*  )&USART0Task_Handler);
		//创建temper0任务
    xTaskCreate((TaskFunction_t )temper0_task,     
                (const char*    )"temper_task",   
                (uint16_t       )TEMPER0_STK_SIZE, 
                (void*          )NULL,
                (UBaseType_t    )TEMPER0_TASK_PRIO,
                (TaskHandle_t*  )&TEMPER0Task_Handler); 								
    vTaskDelete(StartTask_Handler); //删除开始任务
    taskEXIT_CRITICAL();            //退出临界区
}

//LED0任务函数 
void led0_task(void *pvParameters)
{
    while(1)
    {
        LED0=~LED0;
        vTaskDelay(500);
    }
}   

//usart0任务函数
void usart0_task(void *pvParameters)
{
	
			while(1)
			{	
				printf("helloworld!");
				printf("\r\n");
				vTaskDelay(2000);
			}
}

//temper0任务函数
void temper0_task(void *pvParameters)
{
	
			while(1)
			{	
				printf("check\r\n");
        		vTaskDelay(5000);
			}
}

3.2 烧录结果

在这里插入图片描述
多任务实现成功
在烧录的过程中遇到了一点问题,使用stlink进行烧录时,keil直接闪退,对stlink debugger设置时也会出现闪退情况。但是换为使用Jlink的时候,同时也设置成jlink cortex就可以正常烧录。
总结: 通过本次动手移植的过程,了解了多任务系统可以让stm32实现更多想要的功能,多任务的优点是显而易见的。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

初学FreeRTOS实现多任务程序 的相关文章

  • ENV环境配置及其下载网络组件包问题

    准备工作 Env 工具包含了 RT Thread 源代码开发编译环境和软件包管理系统 从 RT Thread 官网下载 Env 工具 在电脑上装好 git xff0c 软件包管理功能需要 git 的支持 git 的下载地址为https gi
  • 芯片flash保护(解锁)

    报错结果 Error while accessing a target resource The resource is perhaps notavailable 就是无法下载程序 解决办法 1 下载ST LINK Utility 链接 x
  • 2.RTT-点灯大师的修炼

    1 创建工程模板 相信大家通过学习上一篇文章 1 RTT 环境搭建 现在能熟练的创建一个标准模板了 xff0c 如果不会就跳回去学习一下吧 链接 xff1a 1 RTT 环境搭建 嵌入式路上的流浪的博客 CSDN博客 建立好的工程模板编译并
  • 4.RTT-UART-中断接收及轮询发送

    本期博客开始分享RTT的UART xff0c 利用战舰V3的uart2来输入输出一些字符串 UART xff08 Universal Asynchronous Receiver Transmitter xff09 通用异步收发传输器 xff
  • 12.RTT-IIC设备-AHT10温湿度传感器

    本系列博客更新结束啦 xff01 完结啦 xff01 xff01 xff01 撒花 xff01 xff01 xff01 关于RTT的设备和驱动专题更新完毕啦 xff0c 本期是最后一期 一段学习旅途的结束意味着下一段学习冒险的开始 虽然本系
  • RTduino+sht31温湿度传感器

    本次博客使用的是STM32F103C8T6 xff0c 因为该BSP已经对接好RTduino了可以直接上手使用 一 RTduino简介 RTduino是RT Thread实时操作系统的Arduino生态兼容层 xff0c 为RT Threa
  • 一文揭秘字节跳动、华为、京东的薪资职级

    声明 xff1a 本文所有数字均不是官方数据 xff0c 为网络资料收集整理 字节跳动 01 全球员工总数 字节的员工数量目前超过5万人 图片来源 xff1a 字节范 02 岗位职级 字节跳动的职级研发序列一共10级 xff1a 字节跳动创
  • ESP_C3在ubuntu下运行RT-Thread

    1 clone源代码RT Thread git clone git 64 github com RT Thread rt thread git 2 开始搭建ESP IDF环境 进入源码到bsp文件夹下找到ESP32 C3 xff0c 开始配
  • uniapp中使用弹出层

    uniapp中使用弹出层 因为业务的需要 xff0c 需要弹出一个复选框 xff0c 使用uniapp中自带的框架 使用 xff1a 第一步 xff1a 下载下示例项目 xff0c 找到主要的文件夹 第二步 xff1a 将该文件夹放到组件的
  • 手写一个uniapp的步骤条组件

    span class token operator lt span template span class token operator gt span span class token operator lt span view span
  • uniapp中的分页

    数据量过多就会使用分页 第一种 xff1a API span class token comment 和data同级 span span class token function variable function onReachBotto
  • uniapp中生成随机的二维码并进行保存

    需求 xff1a 需要根据用户id的不同生成不同的二维码 xff0c 并进行本地保存 第一步 xff1a 下载插件 这里对于二维码的生成 xff0c 使用的是第三方插件weapp qrcode min js xff0c 主要用到的文件是 d
  • Pc端的基本Echarts

    Pc端的基本Echarts 双环传态图组件 span class token operator lt span template span class token operator gt span span class token oper
  • el-table表格的sortable排序的使用以及出现小数、%排序错乱

    前端实现排序 xff1a 只需要在表头上加上一个sortable属性即可 span class token tag span class token tag span class token punctuation lt span el t
  • 浏览器的回退和导航栏的选中转态不同步,路由在新窗口打开

    问题1 xff1a 浏览器的回退和导航栏的选中状态不能同步的问题 问题 xff1a 用户后退时候 xff0c 左边导航栏显示的还是上一个页面的导航 xff0c 但是路由和页面已经变了 span class token operator lt
  • 左右联动-左侧点击相应的位置,右侧随之滚动

    第一步 xff1a npm下载 npm install better scroll save 第二步 xff1a 局部注册 xff08 当前组件 xff09 span class token keyword import span BScr
  • 杂技-各种css小技巧

    渐变字体 background image webkit linear gradient bottom 379ED7 0FE2EE webkit background clip text webkit text fill color tra
  • 深入理解Kotlin无参构造函数

    Unsafe 创建实例 在java中 创建一个对象 其实主要就是3种方法 通过new 关键字来创建 这种是最常见的 通过反射构造方法来创建对象 这种也不少见 很多框架中都有使用 Unsafe类来创建实例 xff0c 这种情况非常少见 这里先
  • VScode 快捷键

    一 VScode 快捷键 这里主要记录是 VScode开发工具常用的快捷键 xff0c 以便提高工作效率 xff0c 以及今后方便查阅 xff01 xff01 xff01 SHIFT 43 ALT 43 I 在选定的每行末尾插入光标CTRL
  • 结构体与共用体 链表 编译预处理

    结构图和共同体 单科成绩分析统计程序 题目要求 xff1a 1 输入学生个数 成绩 2 计算平均分 最高分 最低分 3 计算各个分数段人数并计算平均分 4 排序从高到低 刚开始计算各分段人数时是这样写的 排序是这样写的 发现case2 ca

随机推荐