分享一下FreeRTOS移植STM32F302经验

2019-07-20 12:20发布

本帖最后由 八度空间 于 2017-3-12 11:32 编辑

移植硬件平台:项目板子
编译环境:IAR7.6
库版本:V1.2.2
FreeRTOS版本:V9.0.0

一、下载FreeRTOS源码
      官网直接下载http://www.freertos.org/a00104.html或者原子哥光盘附带的,本贴也上传
1.jpg
二、解压源码
      2.1、新建工程,工程中建FreeRTOS文件夹存放源码
      2.2、打开解压文件“..FreeRTOSv9.0.0”
2.jpg
      2.3、打开“FreeRTOS”文件夹,看到“Source”文件夹,这就是源码了,至于“FreeRTOS-Plus”文件夹就大家自己研究了,像研究水果6plus一样研究
3.jpg
             将此文件夹所有东西拷贝到工程新建的“FreRTOS”文件夹中
4.jpg
      2.4、在源码demo文件夹里“..FreeRTOSv9.0.0FreeRTOSDemoCORTEX_M4F_STM32F407ZG-SK”(STM32F302是Cortex-M4内核的)拷贝“FreeRTOSConfig.h”文件到工程中(放位置随意)
5.jpg
6.jpg
二、打开IAR软件新建工程(可参考http://www.openedv.com/thread-74004-1-1.html这贴进行,至于MDK就大家自行新建了),往文件夹丢文件就是了
7.jpg
             “port.c和portmacro.h”文件路径“..FreeRTOSportableIARARM_CM4F”
             “portasm.s”文件路径(IAR工程必须添加,MDK工程没有的):“..FreeRTOSportableIARARM_CM4F”
             ”heap_x“文件路径:”..FreeRTOSportableMemMang“
三、配置工程,添加头文件路径(可参考http://www.openedv.com/thread-74004-1-1.html这贴进行)
8.jpg
四、配置IAR环境完成
      4.1、编写main函数
9.jpg
             就弄了两个任务在跑,一个是闪灯(板子上只有一个LED),一个是串口每隔1s就输出一个浮点运算(前面说的STM32F302使用的是Cortex-M4内核),有带浮点计算单元的FPU
      4.2、修改文件”FreeRTOSConfig.h“
             打开文件,在开头位置,增加相关的环境预编译条件
12.jpg
             上一个框框就是文件自带的,我增加几个编译环境的宏定义判断,或者去掉也行,下面一个框框就是MDK编译环境的区别了,接下来就是FreeRTOS裁剪宏定义了
      4.3、修改”SYSTEM“相关文件
             4.3.1、修改”sys“文件
                       主要是增加宏定义相关说明,详见工程
             4.3.2、修改”delay“文件
                       增加FreeRTOS支持
[mw_shl_code=applescript,true]/**
  *****************************************************************************
  * @name   : 初始化延时函数
  *
  * @Brief  : 主要Cortex-M3内核对系统时钟计数单元
  *           详细见《Cortex-M3权威指南(中文)》第216页 a)  时钟周期(CYCCNT) 的内容
  *           周立功《CM3计数参考手册》第28、29页、第110、125页
  *
  * @Input  : none
  *
  * @Output : none
  *
  * @Return : none
  *****************************************************************************
**/
void delay_init(void)
{
#if _DELAY_TYPE == 1  //使用其他资源做延时时基
        
        DEM_CTRL |= 1<<24;  //该位必须为1,使能跟踪和调试模块的使用。详细见:周立功《CM3计数参考手册》第115页介绍
                        //在没有使用跟踪时,该位使能对功率使用的控制。它能够由应用程序或调试器使能,供ITM使用
                        //在DWT能够使用之前,调试异常和监控控制寄存器的TRCENA(bit24)位必须置位

        DWT_CTRL |= 1<<0;  //使能DWT_CYCCNT计数器。
                       //如果不使能,则计数器不执行计数操作,因此不会产生PC采样或CYCCNTENA事件。
                       //在正常使用时,CYCCNT计数器应由调试器初始化为0。
        
#else  //使用嘀嗒定时器做延时时基
        
        #if _SYSTEM_SUPPORT_ROTS == 1  //运行了OS
        uint32_t RELOAD = 0;  //挡计数器倒数到0时的重装值,有效位:0 ~ 23
        #endif
        
        #if _SYSTEM_SUPPORT_ROTS == 1  //运行了OS
        #if _RTOS_TYPE == 2  //FreeRTOS
        SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK);
        fac_us = SystemCoreClock / 1000000;
        RELOAD = SystemCoreClock / 1000000;
        #endif
        #else
        /* 根据SysTick定时器的时钟分频来确定重装值 */
        /* 8分频时除以8000‘000,1分频时除以1000’000 */
        SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);  //选择外部时钟 HCLK / 8
        fac_us = SystemCoreClock / 8000000;  //系统时钟的 1/8
        #endif
        
        #if _SYSTEM_SUPPORT_ROTS == 1  //运行了OS
        #if _RTOS_TYPE == 0  //uCOS
        RELOAD = SystemCoreClock / 8000000;        //每秒钟的计数次数,单位Hz
        RELOAD *= 1000000 / OS_TICKS_PER_SEC;        //根据操作系统的心跳时长来计算溢出时间,单位:KHz
                                                //RELOAD为24位计数器,最大值为:16777216
        fac_ms = 1000 / OS_TICKS_PER_SEC;
        #elif _RTOS_TYPE == 1  //RAW-OS
        RELOAD = SystemCoreClock / 8000000;        //每秒钟的计数次数,单位Hz
        RELOAD *= 1000000 / RAW_TICKS_PER_SECOND;  //根据操作系统的心跳时长来计算溢出时间,单位:KHz
                                                   //RELOAD为24位计数器,最大值为:16777216
        fac_ms = 1000 / RAW_TICKS_PER_SECOND;
        #elif _RTOS_TYPE == 2  //FreeRTOS
        RELOAD *= 1000000 / configTICK_RATE_HZ;  //设置溢出时间
                                              //RELOAD为24位计数器,最大值为:16777216
        fac_ms = 1000 / configTICK_RATE_HZ;
        #endif
        
        SysTick->CTRL |= SysTick_CTRL_TICKINT_Msk;        //开启SysTick定时器中断请求
        SysTick->LOAD = RELOAD;        //溢出计数值,每1/TICKINT_CNT秒中断一次
        SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk;      //开始倒数
        
        #else  //没有运行OS
        
        fac_ms = (uint16_t)fac_us*1000;        //ms需要的SysTick时钟数
        
        #endif
        
#endif  //end _DELAY_TYPE
}[/mw_shl_code]
[mw_shl_code=applescript,true]/**
  *****************************************************************************
  * @Name   : 延时n个us
  *
  * @Brief  : none
  *
  * @Input  : nus:要延时的us数
  *
  * @Output : none
  *
  * @Return : none
  *****************************************************************************
**/
void delay_us(uint32_t nus)
{
#if _DELAY_TYPE == 1  //使用其他资源做延时时基
        
        uint32_t savecount,endcnt,CPU_cnt;

        savecount = DWT_CYCCNT;        //保存计数器当前数值
        CPU_cnt = nus*(SystemCoreClock/(1000*1000));        //计算达到所需延时值的CPU时钟数。即多少个系统时钟计数
                                                                                        //得到更精确延时时间,减去前面代码运行的时间即可

        endcnt = savecount + CPU_cnt;        //计算所需延时时间DWT_CYCCNT的计数值,在溢出时返回到0
        
        if(endcnt > savecount)        //所需延时值大于当前计数值
        {
                while(DWT_CYCCNT < endcnt);        //循环等待所需要的延时时间的CPU时钟计数值
        }
        else        //小于当前计数值
        {
                while(DWT_CYCCNT > endcnt);        //等待计数器溢出翻转
                while(DWT_CYCCNT < endcnt);        //等待所需延时时间到达
        }
        
#else  //使用嘀嗒定时器做延时时基
        
        uint32_t temp=0;
        
        #if _SYSTEM_SUPPORT_ROTS == 1  //运行了OS
        
        uint32_t VAL_Prev=0;  //开始计时之前的值
        uint32_t VAL_Now=0;   //当前计时值
        uint32_t VAL_cnt=0;   //计数
        uint32_t Reload=SysTick->LOAD;  //获取到LOAD的值

        temp = nus*fac_us;  //得到延时的节拍数
        
        #if _RTOS_TYPE == 0  //uCOS
        OSSchedLock();  //阻止ucos调度,防止打断us延时
        #elif _RTOS_TYPE == 1  // RAW-OS
        raw_disable_sche();  //阻止RAW-OS调度,防止打断us延时
        #elif _RTOS_TYPE == 2  //FreeRTOS
        #endif
        
        VAL_Prev = SysTick->VAL;  //保存当前的计数值
        
        while(1)
        {
                VAL_Now = SysTick->VAL;  //读取数值
                if(VAL_Now != VAL_Prev)
                {
                        if(VAL_Now < VAL_Prev)  VAL_cnt += VAL_Prev-VAL_Now;  //因为SysTick是一个递减的定时器
                        else                      VAL_cnt += Reload - VAL_Now + VAL_Prev;

                        VAL_Prev = VAL_Now;  //刷新
                        if(VAL_cnt >= temp)  break;  //超过/等于需要的延时值了,则退出循环
                }
        };
        #if _RTOS_TYPE == 0  //uCOS
        OSSchedUnlock();  //开启ucos调度
        #elif _RTOS_TYPE == 1  // RAW-OS
        raw_enable_sche();  //开启RAW-OS调度
        #elif _RTOS_TYPE == 2  //FreeRTOS
        #endif
        
        #else  //没有运行os
        SysTick->LOAD = nus*fac_us; //时间加载                           
        SysTick->VAL = 0x00;        //清空计数器
        SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk;      //开始倒数         
        do
        {
                temp = SysTick->CTRL;
        }while(temp&0x01&&!(temp&(1<<16)));        //等待时间到达   
        SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;        //关闭计数器
        SysTick->VAL = 0x00;        //清空计数器
        
        #endif  //end _SYSTEM_SUPPORT_ROTS
        
#endif  //end _DELAY_TYPE
}

/**
  *****************************************************************************
  * @Name   : 延时n个ms
  *
  * @Brief  : 1、延时ms级定义,延时范围:1 ~ 65535ms。延时最大值可变,不爆机uint32_t/1000范围即可
  *           2、SysTick->LOAD为24位寄存器,所以,最大延时为:
  *              nms <= 0xffffff*8*1000/SYSCLK
  *              SYSCLK单位为Hz,nms单位为ms
  *              注意nms的范围 0 ~ 1864(72M情况下)
  *
  * @Input  : nms:要延时的ms数
  *
  * @Output : none
  *
  * @Return : none
  *****************************************************************************
**/
void delay_ms(uint16_t nms)
{
#if _DELAY_TYPE == 1  //使用其他资源做延时时基
        
        delay_us((uint32_t)(nms*1000));  //采用普通的延时
        
#else  //使用嘀嗒定时器做延时时基
        
        
        #if _SYSTEM_SUPPORT_ROTS == 1  //运行了OS
        #if _RTOS_TYPE == 0  //uCOS
        if (OSRunning == OS_TRUE && OSLockNesting == 0)  //ucos已经在跑了
        #elif _RTOS_TYPE == 1  // RAW-OS
        if (raw_os_active == RAW_OS_RUNNING && raw_int_nesting == 0)
        #elif _RTOS_TYPE == 2  //FreeRTOS
        if (xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED)  //系统已经在运行
        #endif
        {
                if(nms > fac_ms)  //延时大于ucos基数
                {
                        #if _RTOS_TYPE == 0  //uCOS
                        OSTimeDly(nms/fac_ms);  //采用ucos延时
                        #elif _RTOS_TYPE == 1  // RAW-OS
                        raw_sleep(nms / fac_ms);
                        #elif _RTOS_TYPE == 2  //FreeRTOS
                        vTaskDelay(nms/fac_ms);  //FreeRTOS延时
                        #endif
                }
                nms %= fac_ms;  //ucos无法提供小于节拍的延时了
        }
        if (nms == 0)  return;  //没意义了,直接退出,不加这句,在运行RAW-OS时会死机
        delay_us((uint32_t)(nms*1000));  //采用普通的延时
        
        #else  //没有运行os
        
        uint32_t temp;
                           
        SysTick->LOAD = (uint32_t)nms*fac_ms;        //时间加载(SysTick->LOAD为24bit)
        SysTick->VAL = 0x00;           //清空计数器
        SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk;      //开始倒数  
        do
        {
                temp = SysTick->CTRL;
        }while(temp&0x01&&!(temp&(1<<16)));        //等待时间到达   
        SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;        //关闭计数器
        SysTick->VAL = 0x00;        //清空计数器
        
        #endif  //end _SYSTEM_SUPPORT_ROTS
        
#endif  //end _DELAY_TYPE
}[/mw_shl_code]
             4.3.3、修改”usart“文件
                       主要修改串口中断接收函数兼容FreeRTOS
[mw_shl_code=applescript,true]/**
  *****************************************************************************
  * @Name   : 串口接收中断服务程序
  *
  * @Brief  : none
  *
  * @Input  : none
  *
  * @Output : none
  *
  * @Return : none
  *****************************************************************************
**/
void USART1_IRQHandler(void)
{
        #if USART_USER_DMA_EN
        uint16_t usart_rx_len = 0;
        #else
        uint8_t res;
        #endif

#if _SYSTEM_SUPPORT_ROTS == 1
        #if _RTOS_TYPE == 0  //uCOS
        OSIntEnter();
        #elif _RTOS_TYPE == 1  //RAW-OS
        raw_enter_interrupt();
        #elif _RTOS_TYPE == 2  //FreeRTOS
        #endif
#endif
        #if USART_USER_DMA_EN
        if (USART_GetITStatus(USART1, USART_IT_IDLE) != RESET)
        {
                /* ==================== STM32F0xx Series chips ==================== */
                #if __STM32xx == 0x00  //STM32F0xx
                DMA_Cmd(DMA1_Channel3, DISABLE);  //先关闭,预防干扰
                usart_rx_len = USART_REC_LEN - DMA_GetCurrDataCounter(DMA1_Channel3);
                DMA1_Channel3->CNDTR = (uint16_t)USART_REC_LEN;
                /* ==================== STM32F4xx Series chips ==================== */
                #elif __STM32xx == 0x04  //STM32F4xx
                DMA_Cmd(DMA2_Stream5, DISABLE);  //先关闭,预防干扰
                DMA_ClearFlag(DMA2_Stream5, DMA_FLAG_TCIF5);
                usart_rx_len = USART_REC_LEN - DMA_GetCurrDataCounter(DMA2_Stream5);
                DMA2_Stream5->NDTR = (uint16_t)USART_REC_LEN;
                #else  //STM32F10x or STM32F3xx
                DMA_Cmd(DMA1_Channel5, DISABLE);  //先关闭,预防干扰
                usart_rx_len = USART_REC_LEN - DMA_GetCurrDataCounter(DMA1_Channel5);
                DMA1_Channel5->CNDTR = (uint16_t)USART_REC_LEN;
                #endif
                //
                //判断是否接受到回车符
                //
                if (USART_RX_BUF[usart_rx_len-2] == 0x0D)  //CR (carriage return)
                {
                        if (USART_RX_BUF[usart_rx_len-1] == 0x0A)  //LF (NL line feed, new line)
                        {
                                usart_rx_len -= 2;  // 不计算在内
                                USART_RX_STA = (usart_rx_len & 0x3FFF);  //得到接收数据长度
                                USART_RX_STA |= 0x8000;  //标志接收完成标志
                        }
                        else
                        {
                                USART_RX_STA = 0;
                        }
                }
                /* ==================== STM32F0xx Series chips ==================== */
                #if __STM32xx == 0x00  //STM32F0xx
                DMA_Cmd(DMA1_Channel3, ENABLE);
                /* ==================== STM32F4xx Series chips ==================== */
                #elif __STM32xx == 0x04  //STM32F4xx
                DMA_Cmd(DMA2_Stream5, ENABLE);
                #else  //STM32F10x or STM32F3xx
                DMA_Cmd(DMA1_Channel5, ENABLE);
                #endif
                (void)USART_ReceiveData(USART1);
        }
        USART_ClearITPendingBit(USART1, USART_IT_IDLE);
        #else
        if (USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)  //接收到数据
        {
                res = USART_ReceiveData(USART1);  //读取接收到的数据
               
                if ((USART_RX_STA & 0x8000) == 0)  //接收未完成
                {
                        if ((USART_RX_STA & 0x4000) != 0)  //接收到了0x0d
                        {
                                /***********************************************
                                  修改内容如下
                    当用户数据当中有0x0d的时候修正的错误的判断
                                ***********************************************/
                                
                                if(res != 0x0a)
                                {
                                        USART_RX_BUF[USART_RX_STA & 0x3fff] = 0x0d;        //补上丢失的0x0d数据
                                        USART_RX_STA++;
                                        USART_RX_BUF[USART_RX_STA & 0x3fff] = res;        //继续接收数据
                                        USART_RX_STA++;
                                        USART_RX_STA &= 0xbfff;                                                //清除0x0d标志
                                }
                                
                                /***********************************************
                                      修改完成
                                ***********************************************/
                                
                                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;//接收数据错误,重新开始接收
                                        }
                                }                 
                        }
                }        //end 接收未完成                    
        }        //end 接收到数据
        #endif
#if _SYSTEM_SUPPORT_ROTS == 1
        #if _RTOS_TYPE == 0  //uCOS
        OSIntExit();
        #elif _RTOS_TYPE == 1  // RAW-OS
        raw_finish_int();
        #elif _RTOS_TYPE == 2  //FreeRTOS
        #endif
#endif
}[/mw_shl_code]
五、至此,也差不多了,点击编译改错,最后得到这么一个框框提示
10.jpg
      大家是否也觉得不可思议,头文件路径已经添加了,为啥报错???有木有有木有(在这里我想了很久,不明白IAR为啥报错)
11.jpg
      打开”FreeRTOSConfig.h“文件查看报错在哪里,仔细看报错文件路径
13.jpg
      后面提示的是文件”portasm.s”路径报错,于是打开此文件,是任务切换什么的,汇编文件,再回头看看IAR配置,有这么一项
14.jpg
      汇编文件管理,看到一个Preprocessor选项,二话不说,增加路径
15.jpg
      再次编译
16.jpg
      没错误没警告
六、下载到板子,看到LED在闪烁,接上串口,每隔一段时间输出一个小数,结果一致
友情提示: 此问题已得到解决,问题已经关闭,关闭后问题禁止继续编辑,回答。