侧边栏壁纸
博主昵称
Warren

有钱人终成眷属,没钱人亲眼目睹

  • 累计撰写 37 篇文章
  • 累计收到 1 条评论

第二章 FreeRTOS移植

warren
2025-02-20 / 0 评论 / 2 阅读 / 正在检测是否收录...

2.1 准备工作

首先准备一个简单的基础工作;选用基础例程中的跑马灯来作为基础工程
FreeRTOS的源码使用V9.0.0版本

2.2 FreeRTOS移植

2.2.1向工程中添加相应的文件

 在基础工程中新建一个FreeRTOS的文件夹。将FreeRTOSv9.0.0\FreeRTOS\Source下的所有文件复制到该文件夹下面。完成后如下图所示:
m7dc4ypy.png
m7dc6jo6.png
 再打开portable文件夹。只保留keil、MemMang、RVDS三个文件夹。将其他的内容均删除掉。完成后portable文件夹如下图所示
m7dc9btz.png
接下来打开工程,新建两个分组。分别为FreeRTOS_CORE和FreeRTOS_PORTABLE。向FreeRTOS_CORE分组中添加FreeRTOS里面的所有文件。向FreeRTOS_PORTABLE分组中添加portable/MemMang/heap_4.c和portable/RVDS/ARM_CM3/port.c
m7dch8pa.png
m7dchmvk.png
然后在工程中添加FreeRTOS源码的头文件路径
m7dckgnq.png
到目前为止,我们还缺少一个FreeRTOSConfig.h的文件。这个文件有3中解决办法,1、自己创建,2、在FreeRTOS的官方移植工程中拷贝,3、从正点原子教程的Demo中拷贝。我选择第三种。
到此工程可以正常编译。工程所需要的文件均已添加。

2.2.2 修改SYSTEM文件

  1. 在sys.h文件中将将宏 SYSTEM_SUPPORT_OS 改为 1
  2. usart.c文件中添加FreeRTOS.h头文件。另在删除掉串口1的中断函数中的OSIntEnter()和 OSIntExit()
  3. delay.c文件直接重写。delay.h中增加对void delay_xms(u32 nms);的声明

    #include "delay.h"
    #include "sys.h"
    //////////////////////////////////////////////////////////////////////////////////      
    //如果需要使用OS,则包括下面的头文件即可.
    #if SYSTEM_SUPPORT_OS
    #include "FreeRTOS.h"                    //FreeRTOS使用          
    #include "task.h" 
    #endif
    
    static u8  fac_us=0;                            //us延时倍乘数               
    static u16 fac_ms=0;                            //ms延时倍乘数,在ucos下,代表每个节拍的ms数
     
    extern void xPortSysTickHandler(void);
    
    //systick中断服务函数,使用ucos时用到
    void SysTick_Handler(void)
    {    
     if(xTaskGetSchedulerState()!=taskSCHEDULER_NOT_STARTED)//系统已经运行
     {
         xPortSysTickHandler();    
     }
    }
                
    //初始化延迟函数
    //SYSTICK的时钟固定为AHB时钟,基础例程里面SYSTICK时钟频率为AHB/8
    //这里为了兼容FreeRTOS,所以将SYSTICK的时钟频率改为AHB的频率!
    //SYSCLK:系统时钟频率
    void 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);
    }

    最后可以看到三个报错信息,提示SysTick_Handler()、 SVC_Handler()和 PendSV_Handler()在项目中存在多出定义。
     SysTick_Handler()滴答定时器中断在delay.c中实现。需要屏蔽掉stm32f10x_it.c中的实现。这个中断函数主要是为FreeRTOS系统提供系统节拍
    SVC_Handler()是一种特殊得到软件中断,SVC中断是由软件指令触发的,通常用于系统调用。他的中断函数由FreeRTOS系统接管。在port.c中已实现。需要在stm32f10x_it.c中屏蔽掉重复的实现函数。SVC中断是操作系统与用户程序之间交互的桥梁。通过SVC中断,用户程序可以请求操作系统执行各种服务,如文件操作、进程管理、内存分配等。
    PendSV_Handler(),主要职责是在任务切换时保存当前任务的上下文,并恢复下一个要运行的任务的上下文。具体来说,PendSV_Handler()可能会执行以下操作:1.保存当前任务的上下文;2.更新任务状态;3.恢复下一个任务的上下文;4.返回。他的中断函数由FreeRTOS系统接管。在port.c中已实现。需要在stm32f10x_it.c中屏蔽掉重复的实现函数。

    2.3 移植验证实验

    2.3.1 实验程序设计

    在实验工程中启动4个任务。分别为start_task()、 led0_task ()、 led1_task ();

  4. start_task():用来创建其他三个任务
  5. led0_task ():控制 LED0 的闪烁,提示系统正在运行
  6. led1_task ():控制 LED1 的闪烁。

    #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 LED1_TASK_PRIO        3                        //任务优先级
    #define LED1_STK_SIZE            50                    //任务堆栈大小
    TaskHandle_t LED1Task_Handler;                //任务句柄
    void led1_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 //任务句柄
     );
         
     //创建LED1任务
     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();                                //退出临界区
    }
    
    //LED0任务函数
    void led0_task(void *pvParameters)
    {
     while(1){
         LED0 = ~LED0;
         vTaskDelay(500);
     }
    }
    
    
    //LED1任务函数
    void led1_task(void *pvParameters)
    {
     while(1){
         LED1 = 0;
         vTaskDelay(200);
         LED1 = 1;
         vTaskDelay(800);
     }
    }

    2.3.2 实验程序运行结果分析

    LED0 和 LED1 开始闪烁。LED0 均匀闪烁,LED1 亮的时间短,灭的时间长。

1

评论 (0)

取消