STM32 HAL库 +freeRTOS(V9.0.0)+Keil 移植

Keil 版本:Keil MDK uVision5

基础程序:正点原子(hal库实验1 跑马灯实验)

硬件:正点原子Mini开发板

1.打开工程

2.下载FreeRTOS V9.0.0

FreeRTOS Real Time Kernel (RTOS) - Browse /FreeRTOS at SourceForge.net

3.添加 FreeRTOS 源码

1)在基础工程中新建一个名为 FreeRTOS 的文件夹;

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

3)在portable 文件夹中,我们只需要留下 keil MemMang 和 RVDS这三个文件夹,其他的都可以删除掉;

4.向工程分组中添加文件

        打开基础工程,新建分组 FreeRTOS_CORE FreeRTOS_PORTABLE,然后向这两个分组

中添加文件;
       port.c RVDS 文件夹下的 ARM_CM3 中的文件,因为 STM32F103 Cortex-M3 内核的,因此要选择 ARM_CM3 中的 port.c 文件。 heap_4.c MemMang 文件夹中的,前面说了 MemMang 是跟内存管理相关 的,里面有 5 c 文件: heap_1.c heap_2.c heap_3.c heap_4.c heap_5.c 。这 5 c 文件是五种不同的内存管理方法,就像从北京到上海你可以坐火车、坐飞机,如果心情好的话也可以走路,反正有很多种方法,只要能到上海就行。这里也一样的,这 5 个文件都可以用来作为FreeRTOS 的内存管理文件,只是它们的实现原理不同,各有利弊。这里我们选择 heap_4.c。
5.添加相应的头文件路径

       

6.添加FreeRTOSConfig.h 文件在FreeRTOS\include。

     FreeRTOSConfig.h具体内容见下篇文章。FreeRTOSConfig.h 是何方神圣?看名字就知道,他是 FreeRTOS 的配置文件,一般的操作系统都有裁剪、配置功能,而这些裁剪及配置都是通过一个文件来完成的,基本都是通过宏定 义来完成对系统的配置和裁剪的。

 7.修改 SYSTEM 文件 
1) 修改 sys.h 文件 将宏 SYSTEM_SUPPORT_OS 改为 1;
#define SYSTEM_SUPPORT_OS     1            //定义系统文件夹是否支持 OS
2)修改 usart.c 文件
      usart.c 文件修改也很简单, usart.c 文件有两部分要修改,一个是添加 FreeRTOS.h 头文件,
默认是添加的 UCOS 中的 includes.h 头文件,修改以后如下:
 
     
      另外一个就是 USART1 的中断服务函数,在使用 UCOS 的时候进出中断的时候需要添加
OSIntEnter() OSIntExit() ,使用 FreeRTOS 的话就不需要了,所以将这两行代码删除掉。

3)修改delay.c文件;

替换成以下代码:

#include "delay.h"
#include "sys.h"
//      
//如果需要使用OS,则包括下面的头文件即可.
#if SYSTEM_SUPPORT_OS
#include "FreeRTOS.h"         //os 使用 
#include "task.h" 
#endif

static u32 fac_us=0;                            //us延时倍乘数

#if SYSTEM_SUPPORT_OS        
    static u16 fac_ms=0;                        //ms延时倍乘数,在os下,代表每个节拍的ms数
#endif


#if SYSTEM_SUPPORT_OS                         //如果需要支持OS.
extern void xPortSysTickHandler(void); 
//systick中断服务函数,使用ucos时用到
void SysTick_Handler(void)
{    
    if(xTaskGetSchedulerState()!=taskSCHEDULER_NOT_STARTED)//系统已经运行
    {
            xPortSysTickHandler();    
    }
    HAL_IncTick();
}
               
//初始化延迟函数
//当使用ucos的时候,此函数会初始化ucos的时钟节拍
//SYSTICK的时钟固定为AHB时钟
//SYSCLK:系统时钟频率
void delay_init(u8 SYSCLK)
{
    u32 reload;
    HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);//SysTick频率为HCLK
    fac_us=SYSCLK;                        //不论是否使用OS,fac_us都需要使用
    reload=SYSCLK;                        //每秒钟的计数次数 单位为K       
    reload*=1000000/configTICK_RATE_HZ;    //根据delay_ostickspersec设定溢出时间
                                            //reload为24位寄存器,最大值:16777216,在72M下,约合0.233s左右    
    fac_ms=1000/configTICK_RATE_HZ;        //代表OS可以延时的最少单位       
    SysTick->CTRL|=SysTick_CTRL_TICKINT_Msk;//开启SYSTICK中断
    SysTick->LOAD=reload;                     //每1/OS_TICKS_PER_SEC秒中断一次    
    SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk; //开启SYSTICK
}                                    
//延时nus
//nus:要延时的us数.    
//nus:0~190887435(最大值即2^32/fac_us@fac_us=22.5)                                           
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);
}
#else  //不用ucos时
void delay_init(u8 SYSCLK)
{
    HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);//SysTick频率为HCLK
    fac_us=SYSCLK;                        //不论是否使用OS,fac_us都需要使用
}                
//延时nus
//nus为要延时的us数.    
//nus:0~190887435(最大值即2^32/fac_us@fac_us=22.5)     
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数
void delay_ms(u16 nms)
{
    u32 i;
    for(i=0;i<nms;i++) delay_us(1000);
}
#endif
 

解释:

    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;
// 不论是否使用 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 .
//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)
{
// 这里注意一下 SYSTICK 是一个递减的计数器就可以了 .
if(tnow<told)tcnt+=told-tnow;
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() 的时候就会导致任务切换。

8.屏蔽stm32f10x_it.c 中的三个函数。

       port.c、delay.c 和 stm32f10x_it.c 中三个重复定义的函数:SysTick_Handler()、SVC_Handler()PendSV_Handler(),这三个函数分别为滴答定时器中断服 务函数、SVC 中断服务函数和 PendSV 中断服务函数,将 stm32f10x_it.c 中的三个函数屏蔽掉。

9.替换main.c文件。

#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "FreeRTOS.h"
#include "task.h"
#include "FreeRTOSConfig.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         20  
//任务句柄
TaskHandle_t LED0Task_Handler;
//任务函数
void led0_task(void *pvParameters);

//任务优先级
#define LED1_TASK_PRIO        3
//任务堆栈大小    
#define LED1_STK_SIZE         20  
//任务句柄
TaskHandle_t LED1Task_Handler;
//任务函数
void led1_task(void *pvParameters);


int main(void)
{
    HAL_Init();                             //初始化HAL库    
    Stm32_Clock_Init(RCC_PLL_MUL10);       //设置时钟,72M
        delay_init(70);                       //初始化延时函数
        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();          //开启任务调度
                                return(1);
//        while(1){
//            PAout(6)=~PAout(6);
//            delay_ms(500);
//            PAout(7)=~PAout(7);
//            delay_ms(1000);
//        };
    }
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(1000);
    }
}   

//LED1任务函数
void led1_task(void *pvParameters)
{
    while(1)
    {
       LED1=~LED1;
             vTaskDelay(2000);
    }
}

有错误。
10.修改错误。
1)方法1: 找这个定义 configMAX_SYSCALL_INTERRUPT_PRIORITY

再找 configPRIO_BITS
再找 __NVIC_PRIO_BITS
发现定为4U , 去掉U改成4就可以了
一般在 Drivers\CMSIS\Device\ST\STM32FXxx\Include\stm32xxx.h

但是我修改不了这个文件,所以采用方法2)。

2)方法2:在port.c的__asm void xPortPendSVHandler( void )之前加上

#undef configPRIO_BITS
#define configPRIO_BITS         4

11.参考文献

1)《STM32F1 FreeRTOS 开发手册 V1.1》第二章;

2)STM32 HAL库 +freeRTOS+Keil 移植 - 孤燕 - 博客园 (cnblogs.com)

3)FreeRTOS STM32CubeMX port.c(483): error: A1586E: Bad operand types (UnDefOT, Constant) ...-CSDN博客

  • 19
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: STM32是一款由ST(意法半导体)公司开发的高性能32位单片机系列。它集成了丰富的外设,如串口、定时器、PWM、ADC等,同时支持多种通信接口如SPI、I2C和CAN等。STM32 HAL(Hardware Abstraction Layer)是ST公司为STM32系列开发的一套硬件抽象层,它提供了一套统一的编程接口,简化了在不同STM32芯片之间的移植工作。 FreeRTOS是一个流行的实时操作系统(RTOS),它在STM32上得到广泛的应用。它提供了多任务调度、信号量、消息队列等功能,可以帮助开发者实现复杂的任务并行处理。在STM32中使用FreeRTOS,可以充分利用STM32的多核处理能力和丰富的外设资源。 MQTT是一种轻量的消息传输协议,广泛应用于物联网领域。它通过发布和订阅模式实现消息的传输,具有简单、开销小、可靠性高的特点。在STM32中使用MQTT,可以实现与各种设备的通信,如传感器、控制器等。 综上所述,STM32 HAL是ST公司为STM32系列开发的硬件抽象层,可以方便地在不同芯片之间移植FreeRTOS是一个实时操作系统,能够帮助开发者实现并行处理和任务调度。MQTT是一种轻量的消息传输协议,可以用于STM32与其他设备之间的通信。通过结合使用这三种技术,可以开发出高性能、可靠的物联网应用。 ### 回答2: STM32 HAL是ST公司提供的一套基于硬件抽象层的开发库,用于简化嵌入式系统的开发。HAL库提供了一系列功能丰富的函数接口,包括GPIO、UART、SPI、I2C等外设的控制接口,可以方便地对STM32单片机进行配置和控制。 FreeRTOS是一款广泛使用的开源实时操作系统(RTOS),适用于嵌入式系统的开发。FreeRTOS提供了任务管理、调度器、队列、信号量等功能,可以用于多任务的并发执行。它具有轻量、可移植、可靠等特点,广泛应用于各种嵌入式系统中。 MQTT(Message Queuing Telemetry Transport)是一种基于发布-订阅模式的轻量级通信协议,常用于物联网(IoT)应用中的设备间通信。MQTT协议使用简单、开销小,适用于带宽有限的场景。它通过客户端和代理服务器之间的消息传递实现通信,支持可靠传输和压缩技术,可以满足物联网应用对低功耗、低带宽的要求。 结合起来,使用STM32 HAL库FreeRTOS可以实现在STM32单片机上运行MQTT协议。HAL库提供了对待控制的硬件外设的支持,可以与MQTT库进行配合,实现对设备的配置和控制。FreeRTOS提供了任务管理和调度功能,可以用于处理MQTT消息的异步接收和处理,以及与其他任务的并行执行。通过这些组件的结合使用,可以开发出功能强大、稳定可靠的物联网设备。 ### 回答3: STM32 HAL是指STM32微控制器的硬件抽象层(Hardware Abstraction Layer)。它提供了一个统一的接口,以便开发人员能够简化对STM32微控制器的底层硬件操作。通过使用HAL,开发人员可以更方便地编写可移植且易于维护的代码。 FreeRTOS是一个开源的嵌入式实时操作系统(RTOS)。它提供了任务调度、时间管理、内存管理、通信和同步机制等功能,使开发人员能够更方便地编写多任务并发的嵌入式应用程序。在STM32项目中,FreeRTOS通常与STM32 HAL一起使用,以实现高效的任务调度和资源管理。 MQTT是一种基于发布/订阅模式的轻量级消息传输协议。它被广泛应用于物联网等场景中,以实现设备之间的消息通信。MQTT具有低延迟、低能耗和网络带宽占用小等特点,非常适合在资源有限的嵌入式系统中使用。在STM32 HAL和FreeRTOS的基础上,使用MQTT可以实现STM32微控制器与其他设备之间的可靠、高效的通信。 总结来说,STM32 HAL提供了对STM32微控制器硬件的抽象接口,简化了底层编程;FreeRTOS是一个实时操作系统,提供了任务调度和资源管理;而MQTT是一种轻量级的消息传输协议,用于在嵌入式系统中实现设备之间的通信。这三个技术共同使用可以实现高效、可靠的嵌入式应用程序开发。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值