1. 资料准备
因为板子是stm32F407的第二版的,所以开始下的资料是旧版本的,但是旧版本的FreeRTOS工程没有hal库的,都是标准库的,这里是下载stm32F407最新版的资料,进行移植。
资料可以在正点原子官网下载,如下:
http://www.openedv.com/docs/boards/stm32/index.html
一定要下载最新的资料(开始用旧版本的移植不成功)。
准备stm32F407 hal库的 实验8 基本定时器实验和 实验37 内存管理实验两个工程,以及FreeRTOS的源码,以内存管理实验为模版,进行移植。
2. 实验流程
- 添加 FreeRTOS 源码
- 添加FreeRTOSConfig.h配置文件
- 修改SYSTEM文件
- 修改中断相关文件
- 添加应用程序
2.1 添加 FreeRTOS 源码
在Middlewares文件夹中新建一个FreeRTOS文件夹,把FreeRTOS源码中的source中文件都拷贝到新建的FreeRTOS文件夹中,删去其余不是.c的文件。
portable文件夹里面的东西是连接软件层面的FreeRTOS 操作系统和硬件层面的芯片的桥梁,这里只保留portable文件夹中的RVDS、MemMang、Keil三个文件夹(因为stm32F4只用到这三个文件夹)。
2.2 将文件添加到工程
在工程中新建两个分组,Middlewares/FreeRTOS_CORE 和 Middlewares/FreeRTOS_PORT。
将.c文件添加到Middlewares/FreeRTOS_CORE 分组中。
将MemMang文件夹下的heap_4.c添加到Middlewares/FreeRTOS_PORT分组中。
将RVDS文件夹下的ARM_CM4F下的port.c添加到Middlewares/FreeRTOS_PORT分组中。
2.3 添加头文件路径
添加头文件路径FreeRTOS/include和port.c的文件路径。
2.4 添加 FreeRTOSConfig.h文件
在FreeRTOS的例程中找到该文件FreeRTOSConfig.h,放到移植工程即可,这里放到user文件夹中。
2.5 修改SYSTEM文件
- 修改sys.h文件
/**
1. SYS_SUPPORT_OS用于定义系统文件夹是否支持OS
2. 0,不支持OS
3. 1,支持OS
*/
//#define SYS_SUPPORT_OS 0
#define SYS_SUPPORT_OS 1
- 修改usart.c 文件,删掉OSIntEnter()和 OSIntExit()两个函数
void USART_UX_IRQHandler(void)
{
uint32_t timeout = 0;
uint32_t maxDelay = 0x1FFFF;
//#if SYS_SUPPORT_OS /* 使用OS */
// OSIntEnter();
//#endif
HAL_UART_IRQHandler(&g_uart1_handle); /* 调用HAL库中断处理公用函数 */
timeout = 0;
while (HAL_UART_GetState(&g_uart1_handle) != HAL_UART_STATE_READY) /* 等待就绪 */
{
timeout++; /* 超时处理 */
if(timeout > maxDelay)
{
break;
}
}
timeout=0;
/* 一次处理完成之后,重新开启中断并设置RxXferCount为1 */
while (HAL_UART_Receive_IT(&g_uart1_handle, (uint8_t *)g_rx_buffer, RXBUFFERSIZE) != HAL_OK)
{
timeout++; /* 超时处理 */
if (timeout > maxDelay)
{
break;
}
}
//#if SYS_SUPPORT_OS /* 使用OS */
// OSIntExit();
//#endif
}
- 将 usart.c中包含的关于 OS 的头文件删除
///* 如果使用os,则包括下面的头文件即可 */
//#if SYS_SUPPORT_OS
//#include "includes.h" /* os 使用 */
//#endif
- 修改delay.c 文件,删除如下代码
//static uint16_t g_fac_ms = 0;
///*
// * 当delay_us/delay_ms需要支持OS的时候需要三个与OS相关的宏定义和函数来支持
// * 首先是3个宏定义:
// * delay_osrunning :用于表示OS当前是否正在运行,以决定是否可以使用相关函数
// * delay_ostickspersec:用于表示OS设定的时钟节拍,delay_init将根据这个参数来初始化systick
// * delay_osintnesting :用于表示OS中断嵌套级别,因为中断里面不可以调度,delay_ms使用该参数来决定如何运行
// * 然后是3个函数:
// * delay_osschedlock :用于锁定OS任务调度,禁止调度
// * delay_osschedunlock:用于解锁OS任务调度,重新开启调度
// * delay_ostimedly :用于OS延时,可以引起任务调度.
// *
// * 本例程仅作UCOSII和UCOSIII的支持,其他OS,请自行参考移植
// */
//
///* 支持UCOSII */
//#ifdef OS_CRITICAL_METHOD /* OS_CRITICAL_METHOD定义了,说明要支持UCOSII */
//#define delay_osrunning OSRunning /* OS是否运行标记,0,不运行;1,在运行 */
//#define delay_ostickspersec OS_TICKS_PER_SEC /* OS时钟节拍,即每秒调度次数 */
//#define delay_osintnesting OSIntNesting /* 中断嵌套级别,即中断嵌套次数 */
//#endif
///* 支持UCOSIII */
//#ifdef CPU_CFG_CRITICAL_METHOD /* CPU_CFG_CRITICAL_METHOD定义了,说明要支持UCOSIII */
//#define delay_osrunning OSRunning /* OS是否运行标记,0,不运行;1,在运行 */
//#define delay_ostickspersec OSCfg_TickRate_Hz /* OS时钟节拍,即每秒调度次数 */
//#define delay_osintnesting OSIntNestingCtr /* 中断嵌套级别,即中断嵌套次数 */
//#endif
///**
// * @brief us级延时时,关闭任务调度(防止打断us级延迟)
// * @param 无
// * @retval 无
// */
//void delay_osschedlock(void)
//{
//#ifdef CPU_CFG_CRITICAL_METHOD /* 使用UCOSIII */
// OS_ERR err;
// OSSchedLock(&err); /* UCOSIII的方式,禁止调度,防止打断us延时 */
//#else /* 否则UCOSII */
// OSSchedLock(); /* UCOSII的方式,禁止调度,防止打断us延时 */
//#endif
//}
///**
// * @brief us级延时时,恢复任务调度
// * @param 无
// * @retval 无
// */
//void delay_osschedunlock(void)
//{
//#ifdef CPU_CFG_CRITICAL_METHOD /* 使用UCOSIII */
// OS_ERR err;
// OSSchedUnlock(&err); /* UCOSIII的方式,恢复调度 */
//#else /* 否则UCOSII */
// OSSchedUnlock(); /* UCOSII的方式,恢复调度 */
//#endif
//}
///**
// * @brief us级延时时,恢复任务调度
// * @param ticks : 延时的节拍数
// * @retval 无
// */
//void delay_ostimedly(uint32_t ticks)
//{
//#ifdef CPU_CFG_CRITICAL_METHOD
// OS_ERR err;
// OSTimeDly(ticks, OS_OPT_TIME_PERIODIC, &err); /* UCOSIII延时采用周期模式 */
//#else
// OSTimeDly(ticks); /* UCOSII延时 */
//#endif
//}
- 添加 FreeRTOS 的相关代码(在delay.c文件中)
extern void xPortSysTickHandler(void);
- 修改SysTick_Handler()函数
void SysTick_Handler(void)
{
HAL_IncTick();
// if (delay_osrunning == 1) /* OS开始跑了,才执行正常的调度处理 */
// {
// OSIntEnter(); /* 进入中断 */
// OSTimeTick(); /* 调用ucos的时钟服务程序 */
// OSIntExit(); /* 触发任务切换软中断 */
// }
/* OS 开始跑了,才执行正常的调度处理 */
if (xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED)
{
xPortSysTickHandler();
}
}
- 修改delay_init()函数
void delay_init(uint16_t sysclk)
{
reload *= 1000000 / configTICK_RATE_HZ;
// g_fac_ms = 1000 / delay_ostickspersec;
}
/* 删除不用的 g_fac_ms 相关代码 */
- 修改delay_us()函数
void delay_us(uint32_t nus)
{
// delay_osschedlock(); /* 阻止OS调度,防止打断us延时 */
// delay_osschedunlock(); /* 恢复OS调度 */
}
- 修改delay_ms()函数
void delay_ms(uint16_t nms)
{
// if (delay_osrunning && delay_osintnesting == 0) /* 如果OS已经在跑了,并且不是在中断里面(中断里面不能任务调度) */
// {
// if (nms >= g_fac_ms) /* 延时的时间大于OS的最少时间周期 */
// {
// delay_ostimedly(nms / g_fac_ms); /* OS延时 */
// }
// nms %= g_fac_ms; /* OS已经无法提供这么小的延时了,采用普通方式延时 */
// }
// delay_us((uint32_t)(nms * 1000)); /* 普通方式延时 */
uint32_t i;
for (i=0; i<nms; i++)
{
delay_us(1000);
}
}
- 包含头文件
/* 添加公共头文件 ( ucos需要用到) */
//#include "includes.h"
#include "FreeRTOS.h"
#include "task.h"
- 修改中断相关文件,修改stm32f4xx_it.c文件
//void SVC_Handler(void)
//{
//}
//void PendSV_Handler(void)
//{
//}
//void SysTick_Handler(void)
//{
// HAL_IncTick();
//}
- 修改FreeRTOSConfig.h文件。
#define configPRIO_BITS __NVIC_PRIO_BITS
//#define __NVIC_PRIO_BITS 4U
#define __NVIC_PRIO_BITS 4
2.6 移除USMART调试组件
因为没有使用到 USMART 调试组件,所以把这个组件删掉。
注释掉main.c中的有关USMART代码。
//#include "./USMART/usmart.h"
//int main(void){
// usmart_dev.init(84); /* 初始化USMART */
}
2.7 添加定时器驱动
把基本定时器工程中的TIMER文件夹,复制到移植工程中的BSP文件夹中。
将定时器的相关驱动文件添加到工程的 Drivers/BSP 文件分组中。
3. 实验验证
把FreeRTOS例程中的demo复制到移植工程中User中。
将demo的驱动文件添加到工程的User文件分组中。
删减之后的部分代码如下:
int main(void)
{
HAL_Init(); /* 初始化 HAL 库 */
sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
delay_init(72); /* 延时初始化 */
usart_init(115200); /* 串口初始化为 115200 */
led_init(); /* 初始化 LED */
lcd_init(); /* 初始化 LCD */
key_init(); /* 初始化按键 */
sram_init(); /* SRAM 初始化 */
my_mem_init(SRAMIN); /* 初始化内部 SRAM 内存池 */
my_mem_init(SRAMEX); /* 初始化外部 SRAM 内存池 */
freertos_demo(); /* 运行 FreeRTOS 例程 */
}
void freertos_demo(void)
{
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 task1(void *pvParameters)
{
while(1)
{
LED0_TOGGLE(); /* LED0闪烁 */
vTaskDelay(1000); /* 延时1000ticks */
}
}
void task2(void *pvParameters)
{
while(1)
{
LED1_TOGGLE(); /* LED1闪烁 */
vTaskDelay(500); /* 延时500ticks */
}
}
把程序烧到开发板中,实验结果如下,可以看到与预设程序一致,红色LED,1000ms亮灭一次,蓝色LED,500ms亮灭一次(可以在如下视频看到)。
FreeRTOS移植
4. 总结
- 遇到如下报大量语法错误,原因是keil编译器问题,把编译器的版本设置为V5即可。
- 最后把程序烧到开发板中,如果LED灯没有亮,复位几次,观察LED是否亮。