RTOS随笔之FreeRTOS

为什么使用RTOS?

1. 先考虑OS有什么特点?

无OS开发情况下,就是一个while循环中执行所有模块功能

  • 顺序执行
    模块依次执行,不能调换顺序,更不能同步实时执行
void main()
{
    BSP_Init();//硬件初始化
    while(1)
    {
      task1();//模块1执行
      .......
      task4();//模块4执行
    }
}
  • 状态调度
    模块根据状态变化执行,可以调换顺序,不能同步实时执行
unsigned int switchflag;
void main()
{
    BSP_Init();//硬件初始化
    while(1)
    {
	    switch(switchflag)
	    {
	     case status1:
	          task1();//模块1执行
	          break;
	     case status2:
	          task2();//模块2执行
	          break;
	      .......
	    }
    }
}

上面的任务都需要执行完才能跳转到其它任务,不能在任务运行期间跳转。也有一种情况是任务自己提前break,但是你能让自己break,你不能让别人break啊,所以我们需要一个东西管理任务切换执行,这个就是OS了。

基于这个最初的需求可以看一下现在的OS实现了什么功能?

  • 支持抢占切换/合作切换/时间片切换任务
  • 支持低功耗模式
  • 支持多种系统架构
  • 支持任务同步机制
  • 支持堆栈溢出检测
  • 工具免费,源码免费,版权免费

2. 再考虑什么情况下使用OS?

1.项目功能单一,程序简单,各模块没有强烈同步执行的需求(实时性需求不强),不需要OS
2.项目功能复杂,模块交织,多个模块有强烈同步执行的需求(实时性需求强),需要OS
根据项目考量,没有明确界限

FreeRTOS调试技巧

  • 重定向printf/scanf函数进行串口打印
/*重定向printf函数*/
int fputc(int data,FILE* file)
{
   USART_SendData(USARTx,(char)data);//发送一个字节
   while(USART_GetFlagStatus(USARTx,USART_FLAG_TXE)==RESET);//等待发送成功
   return data;
}
/*重定向scanf函数*/
int fgetc(FILE* file)
{
   char data;
   while(USART_GetFlagStatus(USARTx,USART_FLAG_RXNE)==RESET);//等待接收成功
   data=USART_ReceiveData(USARTx);//接收一个字节
   return (int)data;
}

  • 调试任务运行状态,任务栈余量,CPU占用率
  1. 实现计数器
volatile uint32_t  OS_DebugTimeTicks=0;//调试心跳
void BSP_Timex()
{
 //实现20KHz频率的定时器
 //开定时器中断
 //OS_DebugTimeTicks在中断中自增
}
  1. FreeRTOSConfig.h文件宏配置
extern uint32_t OS_DebugTimeTicks;
#define configUSE_TRACE_FACILITY	                 1
#define configGENERATE_RUN_TIME_STATS                1
#define configUSE_STATS_FORMATTING_FUNCTIONS         1
#define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS()     (OS_DebugTimeTicks= 0UL)
#define portGET_RUN_TIME_COUNTER_VALUE()             OS_DebugTimeTicks
  1. 调用RTOS调试函数vTaskList(),vTaskGetRunTimeStats()输出任务信息
static void vTaskTaskUserIF(void *pvParameters)
{
	uint8_t ucKeyCode;
	uint8_t DebugInfoBuff[500];

    while(1)
    {
		printf("=================================================\r\n");
		printf("任务名      任务状态 优先级   剩余栈 任务序号\r\n");
		vTaskList((char *)&DebugInfoBuff);
		printf("%s\r\n", DebugInfoBuff);
	
		printf("\r\n任务名       运行计数         使用率\r\n");
		vTaskGetRunTimeStats((char *)&DebugInfoBuff);
		printf("%s\r\n", DebugInfoBuff);	
		
		vTaskDelay(20);
	}
}

任务管理

1. 任务调度流程

在这里插入图片描述

2. 任务栈大小,栈溢出检测

  • 栈大小(最小栈空间)
    在这里插入图片描述
内容
函数每层函数:局部变量+形参+返回地址+状态保存
任务切换部分CPU寄存器 +FPU寄存器
中断部分CPU寄存器 +FPU寄存器
可变参函数,函数指针,递归无法确定栈大小

标准栈空间:最小栈大小的1.5~2倍大小
函数栈大小及嵌套深度可以参考工程编译文件.htm文件
RTOS调试技巧测试栈空间,适当调整

  • 栈溢出检测
    栈生长地址由高到低,如果栈指针下移的地址越过了任务栈的基地址则栈溢出。
    软件预设检测
    在栈生长方向的末端开辟一段空间专门存放固定值,定时判断固定值有没有被修改,被修改说明栈溢出了。存在栈溢出超过判断区间直接宕机的可能。
    MPU内存保护
    通过MPU对栈生长方向的末端区间进行保护,如果出现栈溢出触发MPU异常,在异常中分析栈溢出情况。
    FreeRTOS栈溢出检测

方法1:任务切换时检测栈指针是否越界,栈溢出触发溢出回调函数vApplicationStackOverflowHook()

#define configCHECK_FOR_STACK_OVERFLOW 1
void vApplicationStackOverflowHook(  TaskHandle_t xTask,signed char *pcTaskName);//溢出回调函数

方法2: 任务栈初始化为固定值,任务切换时检测栈尾16个字节是否都是初始化设置的固定值。

#define configCHECK_FOR_STACK_OVERFLOW 2
void vApplicationStackOverflowHook(  TaskHandle_t xTask,signed char *pcTaskName);//溢出回调函数

任务切换时进行栈溢出检测无法确保任务运行期间栈不溢出。
任务切换时进行栈溢出检测无问题只能说明栈尾的数据是固定值,但是不能说明是初始时设置的固定值还是运行时保存的固定值,存在第2种情况的可能性。

3. 中断管理

M3/M4内核MCU中断优先级取寄存器8位中的高4位表示,共有16种组合。

几点原则:

  • RTOS采用中断分组4,也就是0~15级全抢占式优先级
  • 高抢占级中断可以抢占低抢占级中断
  • 同抢占级中断不能互相抢占,先到先行
  • Reset,NMI不可屏蔽中断,Hard Fault高于普通中断,优先级固定

RTOS中断配置:

Systick中断:产生心跳,触发PendSV中断
PendSV中断:任务切换
SVC中断:启动任务,只执行一次

#define configPRIO_BITS      4//优先级占用bits
#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY   15//RTOS用的Systick和PendSV中断优先级
#define configKERNEL_INTERRUPT_PRIORITY 		( configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )//Systick和PendSV中断配置的优先级左移4位写进优先级配置寄存器
#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY    2//可以使用RTOS函数的最高优先级,2~15优先级中断中可用RTOS接口
#define configMAX_SYSCALL_INTERRUPT_PRIORITY 	( configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )//可以使用RTOS函数的最高优先级左移4位写进优先级配置寄存器

RTOS管理的中断会出现中断延迟的可能,对于实时性要求强的中断不能被RTOS管理,否则会出现中断无法及时响应的情况。
FreeRTOS使用basepri寄存器,uCOS使用primask寄存器。

寄存器功能
primask这是个只有 1 个 bit 的寄存器。 在它被置 1 后,就关掉所有可屏蔽的异常,只剩下NMI 和硬 fault 可以响应。它的缺省值是 0,表示没有关中断
faultmask这是个只有 1 个 bit 的寄存器。当它置 1 时,只有 NMI 才能响应,所有其它的异常,甚至是硬 fault,也通通闭嘴。它的缺省值也是 0,表示没有关异常
basepri这个寄存器最多有 9 位(由表达优先级的位数决定)。它定义了被屏蔽优先级的阈值。当它被设成某个值后,所有优先级号大于等于此值的中断都被关(优先级号越大,优先级越低)。但若被设成 0,则不关闭任何中断, 0 也是缺省值

4. 内存管理

FreeRTOS提供5种内存管理接口
在这里插入图片描述

FreeRTOS API接口

1. 任务创建

函数描述
xTaskCreate( )动态创建一个任务,从RTOS的堆上分配栈空间
xTaskCreateStatic( )静态创建一个任务,用户手动分配栈空间
xTaskCreateRestricted( )任务控制块从RTOS堆上分配,用户手动分配栈空间 (和FreeRTOS-MPU一起使用)
xTaskCreateRestrictedStatic( )用户手动分配任务控制块和栈空间 (和FreeRTOS-MPU一起使用)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值