【FreeRTOS】从0写简易RTOS实现任务切换

1. RTOS引入

单片机性能越来越强,很多Linux程序在单片机上也可以运行了:这需要RTOS。

我们要开发的单片机产品,功能也越来越丰富:这也需要RTOS。

就个人技术发展来说,单片机开发的技术提升方向之一就是RTOS。

RTOS已经无处不在:

  • ESP8266 WIFI模块,出厂自带FreeRTOS,可以在上面做二次开发;
  • 4G模块CAT1,出厂自带FreeRTOS,可以在上面做二次开发;
  • 想实现功能比较丰富的设备时,比如加上MQTT功能,就需要RTOS
  • 比如已经被RT-Thread采用的kawaii-mqtt,默认就不支持裸机
  • 你去看所有的智能设备:小度音箱、小爱闹钟、家居摄像头,都使用RTOS。

2. RTOS必需的几个文件

2.1 start.S

开发板上电运行的第一个文件。必需的文件之一。

  1. 首先需要先设置异常向量表。
    • 开发板复位后,找到第一条执行的指令。
    • 作为实时操作系统,肯定要实现任务切换的功能,而任务切换需要依赖于中断,因此,当中断发生时,需要硬件跳转到中断发生的异常地址处
  2. 初始化内存,必需操作之一
  3. 初始化串口
    • 用于调试
    • 用于观察程序的运行情况
  4. 初始化时钟
    • 为了实现任务切换的功能,采用时间片轮转的方式进行任务切换,假定当一个任务运行1ms后,发生时钟中断,俗称tick中断,确保系统的心跳。
    • 由于需要实现,每1ms进行一次任务切换,因此,任务启动的功能以及任务切换的功能就需要在时钟中断的函数中实现
  5. 跳转至main函数

上述功能作为RTOS的基础,必须要实现的

/* 设置异常向量表 */
__Vectors       DCD     0                  
                DCD     Reset_Handler              ; Reset Handler
                DCD     0                ; NMI Handler
                DCD     HardFault_Handler          ; Hard Fault Handler
                DCD     0          ; MPU Fault Handler
                DCD     0           ; Bus Fault Handler
                DCD     UsageFault_Handler_asm         ; Usage Fault Handler
                DCD     0                          ; Reserved
                DCD     0                          ; Reserved
                DCD     0                          ; Reserved
                DCD     0                          ; Reserved
                DCD     SVC_Handler                ; SVCall Handler
                DCD     0           ; Debug Monitor Handler
                DCD     0                          ; Reserved
                DCD     0             ; PendSV Handler
                DCD     SysTick_Handler_asm            ; SysTick Handler
				AREA    |.text|, CODE, READONLY

/* 上电运行的第一个函数 */
; Reset handler
Reset_Handler   PROC
				EXPORT  Reset_Handler             [WEAK]
                IMPORT  mymain

				IMPORT SystemInit
				IMPORT uart_init
				IMPORT UsageFaultInit
				IMPORT SysTickInit
				IMPORT LedInit
				
				/* 由于会用到C函数,因此必须设置一个栈,在内存的顶部随便找了一个地方 */
				LDR SP, =(0x20000000+0x10000)
				/* 初始化内存,内存中的各个分区 */
				BL SystemInit
				/* 初始化串口 */
				BL uart_init
				/* 初始化中断控制器 */
				BL UsageFaultInit
				
				LDR R0, =0
				LDR R1, =0x11111111
				LDR R2, =0x22222222
				LDR R3, =0x33333333
				LDR R12, =0x44444444
				LDR LR, =0x55555555
				
				DCD 0xffffffff
					
				SVC #1
				
				/* 初始化系统时钟,并设置时钟中断1ms */
				BL SysTickInit
				
				/* 点灯指示 */
				BL LedInit

				/* 跳转到main函数执行,可以看到主函数的名字不一定非要设置为 "main" */
				;BL mymain
				LDR R0, =mymain
				BLX R0

                ENDP

2.2 task.c

在该文件中包含了任务创建、开始任务(开始调度)、获得当前任务栈、任务调度等,是RTOS中关键的主要文件。

2.2.1 创建任务 create_task

所谓的任务环境,指的就是当该任务运行时,寄存器中的值

首先,我们能想到的

  • 新创建出来的任务无非是为了运行一个新的程序,而且这个新程序不需要返回
  • 创建的任务也是一个C函数,因此需要有地方来存放自己的环境,比如局部变量,任务传入参数以及多个寄存器
  • 任务的现场就是运行该任务时的寄存器,因此,当任务切换时,为了下次还能接着运行,需要将切换任务时寄存器的状态保存起来,需要保存到自己的任务栈里面,当下次调度到自己时,直接将栈里的环境弹出,恢复环境就可以接着运行了。
  • 为了可以让任务参与调度,当创建任务时,任务的寄存器还没有使用,因此,我们需要替他伪造一个环境,让其可以参与调度,这样我们的任务就可以跟上其他的任务一起参与调度了。

所以,创建任务的关键就是伪造任务的环境

void create_task(task_function f, void *param, char *stack, int stack_len)
{
   
	int *top = (int *)(stack + stack_len);
	
	/* 伪造现场 */
	top -= 16;
	
    /* 新创建任务的栈的结构如下图所示 */
	/* r4~r11 */
	top[0] = 0; /* r4 */
	top[1] = 0; /* r5 */
	top[2] = 0; /* r6 */
	top[3] = 0; /* r7 */
	top[4] = 0; /* r8 */
	top[5] = 0</
  • 3
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值