第二章 FreeRTOS 移植

目录

2.1 准备工作

2.1.1 基础工程

2.1.2 Free RTOS 源码

2.2 Free RTOS 移植

2.2.1 向工程中添加相应文件

1、添加FreeRTOS 源码

 2、向工程分组中添加文件

3、添加相应的头文件路径

 2.2.2 修改 SYSTEM 文件

1、修改 sys.h 文件

2、修改 usart.c 文件

3、修改 delay.c 文件

2.3 移植实验

1、实验设计

2、程序

3、 实验程序运行结果分析


2.1 准备工作

2.1.1 基础工程

以跑马灯为例

2.1.2 Free RTOS 源码

2.2 Free RTOS 移植

2.2.1 向工程中添加相应文件

1、添加FreeRTOS 源码

在工程中新建一个名为 Free RTOS 的文件夹。

创建 FreeRTOS 文件夹以后就可以将 FreeRTOS 的源码添加到这个文件夹中。

在 1.3.2 小节中详细的讲解过 portable 文件夹,我们只需要留下 keil、 MemMang 和 RVDS这三个文件夹, 其他的都可以删除掉, 完成以后如图。

 2、向工程分组中添加文件

打开基础工程,新建分组 FreeRTOS_CORE 和 FreeRTOS_PORTABLE,然后向这两个分组
中添加文件,如图:

        port.c 是 RVDS 文件夹下的 ARM_CM3 中的文件, 因为 STM32F103 是 Cortex-M3 内的, 因此要选择 ARM_CM3中的 port.c 文件。 heap_4.c 是 MemMang 文件夹中的,前面说了MemMang 是跟内存管理相关的, 这里我们选择 heap_4.c,至于原因, 后面会有一章节专门来讲解 FreeRTOS 的内存管理。

3、添加相应的头文件路径

添加完 FreeRTOS 源码中的 C 文件以后还要添加 FreeRTOS 源码的头文件路径,头文件路径如图:

头文件路径添加完成以后编译一下,看看有没有什么错误, 结果会发现提示打不开“FreeRTOSConfig.h”这个文件, 如图:

因为缺少 FreeRTOSConfig.h 文件,打开FreeRTOS 针对 STM32F103 的移植工程文件,文件夹是 CORTEX_STM32F103_Keil,打开以后如图:
FreeRTOSConfig.h 是 FreeRTOS 的配置文件,一般的操作系统都有裁剪、 配置功能, 而这些裁剪及配置都是通过一个文件来完成的, 基本都是通过宏定义来完成对系统的配置和裁剪的, 关于 FreeRTOS 的配置文件 FreeRTOSConfig.h 后面也会有一章节来详细的讲解。

如果出先了这个错误

 将stm32f10x_it.c 中的 SVC_Handler(void) 和 PendSV_Handler(void) 屏蔽掉即可:

到这里我们再编译一次, 没有错误! 如图:

 2.2.2 修改 SYSTEM 文件

1、修改 sys.h 文件

        在 sys.h 文件里面用宏 SYSTEM_SUPPORT_OS 来定义是否使用 OS,我们使用了 FreeRTOS, 所以应该将宏 SYSTEM_SUPPORT_OS 改为 1。

//0,不支持ucos
//1,支持ucos
#define SYSTEM_SUPPORT_OS		1		//定义系统文件夹是否支持UCOS

2、修改 usart.c 文件

        usart.c 文件有两部分要修改,一个是添加 FreeRTOS.h 头文件,默认是添加的 UCOS 中的 includes.h 头文件,修改以后如下:

//如果使用ucos,则包括下面的头文件即可.
#if SYSTEM_SUPPORT_OS
#include "FreeRTOS.h"					//ucos 使用	  
#endif

        另外一个就是 USART1 的中断服务函数, 在使用 UCOS 的时候进出中断的时候需要添加OSIntEnter()和 OSIntExit(), 使用 FreeRTOS 的话就不需要了,所以将这两行代码删除掉,修改以后如下:

void USART1_IRQHandler(void)                	//串口1中断服务程序
{
	u8 Res;

	if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)  //接收中断(接收到的数据必须是0x0d 0x0a结尾)
		{
		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;//接收数据错误,重新开始接收	  
					}		 
				}
			}   		 
     } 

} 

3、修改 delay.c 文件

        delay.c 文件修改的就比较大了,因为涉及到 FreeRTOS 的系统时钟, delay.c 文件里面有 4个函数,先来看一下函数 SysTick_Handler(),此函数是滴答定时器的中断服务函数, 代码如下:

extern void xPortSysTickHandler(void);
//systick 中断服务函数,使用 OS 时用到
void SysTick_Handler(void)
{
	if(xTaskGetSchedulerState()!=taskSCHEDULER_NOT_STARTED)//系统已经运行
	{
		xPortSysTickHandler();
	}
}

        FreeRTOS 的心跳就是由滴答定时器产生的,根据 FreeRTOS 的系统时钟节拍设置好滴答定时器的周期,这样就会周期触发滴答定时器中断了。在滴答定时器中断服务函数中调用FreeRTOS 的 API 函数 xPortSysTickHandler()。

        delay_init()是用来初始化滴答定时器和延时函数,代码如下:

//初始化延迟函数
//SYSTICK 的时钟固定为 AHB 时钟,基础例程里面 SYSTICK 时钟频率为 AHB/8
//这里为了兼容 FreeRTOS,所以将 SYSTICK 的时钟频率改为 AHB 的频率!
//SYSCLK:系统时钟频率
void delay_init()
{

	u32 reload;

	SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK);	//选择外部时钟  HCLK
	fac_us=SystemCoreClock/1000000;				//为系统时钟的1/8  

	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
}							    

        前面我们说了 FreeRTOS 的系统时钟是由滴答定时器提供的,那么肯定要根据 FreeRTOS 的系统时钟节拍来初始化滴答定时器了, delay_init()就是来完成这个功能的。 FreeRTOS 的系统时钟节拍由宏 configTICK_RATE_HZ 来设置,这个值我们可以自由设置,但是一旦设置好以后我们就要根据这个值来初始化滴答定时器,其实就是设置滴答定时器的中断周期。 在基础例程中滴答定时器的时钟频率设置的是 AHB 的 1/8,这里为了兼容 FreeRTOS 将滴答定时器的时钟频率改为了 AHB,也就是 72MHz!这一点一定要注意!

        接下来的三个函数都是延时的,代码如下:

//延时nus
//nus为要延时的us数.		    								   
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);
}

        delay_us()是 us 级延时函数, delay_ms 和 delay_xms()都是 ms 级的延时函数, delay_us()和delay_xms()不会导致任务切换。 delay_ms()其实就是对 FreeRTOS 中的延时函数 vTaskDelay()的简单封装,所以在使用 delay_ms()的时候就会导致任务切换。

        至此, SYSTEM文件夹就修改完成了。

2.3 移植实验

1、实验设计

        本实验设计四个任务: start_task()、 led0_task ()、 led1_task ()和 float_task(),这四个任务的任务功能如下:
        start_task():用来创建其他三个任务。
        led0_task ():控制 LED0 的闪烁,提示系统正在运行。
        led1_task ():控制 LED1 的闪烁。
        float_task():简单的浮点测试任务,用于测试 STM32F4 的 FPU 是否工作正常。

2、程序

  • 任务设置
#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "FreeRTOS.h"
#include "task.h"


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

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

#define LED1_TASK_PRIO	3 				//任务优先级
#define LED1_STK_SIZE	128					//任务堆栈大小
TaskHandle_t LED1Task_Handler;		//任务句柄
void led1_task(void *parameters); //任务函数
  • main 函数
int main(void)
{	
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
	delay_init();	    //延时函数初始化	  
	LED_Init();		  	//初始化与LED连接的硬件接口
	delay_ms(100);
	
	//创建开始任务
	xTaskCreate((TaskFunction_t	)start_task,						//任务函数
							(const char*		)"start_task",					//任务名称
							(uint16_t 			)START_STK_SIZE,				//任务堆栈大小
							(void *					)NULL,									//传递给任务的参数
							(UBaseType_t 		)STAET_TASK_PRIO,				//任务优先级
							(TaskHandle_t *	)&StartTask_Handler			//任务句柄
								);
	vTaskStartScheduler();			
}
  • 任务函数
void start_task(void* parameters)
{
	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
							);

	//创建 ELD1 任务
	xTaskCreate((TaskFunction_t )led1_task,
							(const char *		)"led1_task",
							(uint16_t 			)LED1_STK_SIZE,
							(void *					)NULL,
							(UBaseType_t 		)LED1_TASK_PRIO,
							(TaskHandle_t *	)&LED1Task_Handler
							);
	vTaskDelete(StartTask_Handler);
	taskEXIT_CRITICAL();
}


void led0_task(void *parameters)
{
	while(1)
	{
		LED0=~LED0;
		vTaskDelay(500);
	}
}


void led1_task(void *parameters)
{
	while(1)
	{
		LED1=~LED1;
		vTaskDelay(200);
		LED1=~LED1;
		vTaskDelay(800);
	}
}

3、 实验程序运行结果分析

        编译并下载代码到 STM32F103 开发板中,下载进去以后会看到 LED0 和 LED1 开始闪烁,LED0 均匀闪烁,那是因为我们在 LED0 的任务代码中设置好的 LED0 亮 500ms,灭 500ms。LED1 亮的时间短,灭的时间长,这是因为在 LED1 的任务代码中设置好的亮 200ms,灭800ms。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值