目录
1.第一步准备好基础工程
准备好基础工程,并且编译通过,这里使用正点原子STM32F411提供的“F411_标准例程-寄存器版本“中的”实验4 串口通信实验“为基础。
2.软件仿真相关设置
通过菜单project->Options for target,打开工程设置
修改外部时钟频率为8M:
设置软件仿真,初始化文件,以及STM32F411RC对应的调试DLL
说明:
1.选择Use Simulator可以直接使用软件仿真,不需要硬件单板。
2.设置Initialization File是为.\DebugConfig\debug.ini,是为了避免出现
*** error 65: access violation at 0x40023800 : no 'read' permission错误。
对应文件内容为:
map 0x40000000, 0x40007FFF read write // APB1
map 0x40010000, 0x400157FF read write // APB2
map 0x40020000, 0x4007FFFF read write // AHB1
map 0x50000000, 0x50060BFF read write // AHB2
map 0x60000000, 0x60000FFF read write // AHB3
map 0xE0000000, 0xE00FFFFF read write // CORTEX-M4 internal peripherals
3.设置调试dll和参数,调试dll为DARMSTM.DLL,Parameter根据需要仿真的硬件单板来填。
3.软件仿真环境,printf重定向
3.1修改部分仿真环境下死循环问题
- 在仿真环境下,设置时钟为HSE后,等待HSE READY一直等不到,具体原因不晓得。
修改的办法就是增加一个宏隔离,在#ifndef SIM情况下,不用死等。
3.2修改printf仿真环境下无法输出问题
主要参考教材:(4条消息) MDK下仿真实现printf功能_mytt2013的博客-优快云博客_mdk中printf
基于原工程,只需要修改一个函数:
//重定义fputc函数
int fputc(int ch, FILE *f)
{
#ifdef SIM //仿真环境下printf重定向
return ITM_SendChar(ch);
#else
while((USART1->SR&0X40)==0);//循环发送,直到发送完毕
USART1->DR = (u8) ch;
return ch;
#endif
}
另外还有一个教程,是使用微库的版本,也可以参考:printf系列教程01_UART打印输出配置,基于STM32(Keil、IAR)_strongerHuang的博客-优快云博客
4.移植FreeRtos源码
主要过程参考:(4条消息) FreeRTOS在STM32F4上移植_Zach_z的博客-优快云博客
4.1 拷贝文件
1.在测试工程中建立两个文件夹,分别为Freertos_core和Freertos_port,
2.拷贝freertosv9.0.0\FreeRTOS\Source公共源码到工程目录Freertos_core下:
拷贝source目录下所有文件,除了portable文件到工程目录的Freertos_core目录下:
3.拷贝FreeRtos架构相关源码到工程目录Freertos_port下:
拷贝:FreeRTOS\Source\portable\MemMang\heap_4.c
拷贝:\freertosv9.0.0\FreeRTOS\Source\portable\RVDS\ARM_CM4F 下所有文件
另外,还需要一个FreeRTOSConfig.h,由于我们单板是STM32F411,所以选择FreeRTOS\Demo\CORTEX_M4F_STM32F407ZG-SK工程下的文件,将该文件拷贝到freertos_stm32f411\Freertos_port\include目录,拷贝后的文件结构:
5.增加freertos源码到keil工程中
5.1增加.c文件到工程:
打开keil工程,点击Manager Project item,增加
增加Group,并增加文件到Group后的状态:
点击OK后,可以看到工程中,已经增加了FreeRtos相关源码:
5.2增加头文件到工程:
点击Project->Options for "Target 1"
在C/C++选项卡中,增加相关头文件:
增加Freertos头文件后如图:
6.修改编译问题
1.修改SystemCoreClock条件编译,
Freertos_port\include\FreeRTOSConfig.h中
#ifdef __ICCARM__
#include <stdint.h>
extern uint32_t SystemCoreClock;
#endif
修改为
#if defined(__ICCARM__) || defined(__CC_ARM) ||defined(__GUNC__)
#include <stdint.h>
extern uint32_t SystemCoreClock;
#endif
2.重定义函数处理
PendSV_Handler,SVC_Handler ,SysTick_Handler三个函数有重复定义:
注释掉SYSTEM\delay\delay.c中的PendSV_Handler和SVC_Handler定义,
注释掉include\FreeRTOSConfig.h中的#define xPortSysTickHandler SysTick_Handler
3.关闭钩子函数
复制过来的FreeRTOSConfig.h文件中默认开启了一些钩子函数,都是以Hook结尾,但并未定义,在FreeRTOSConfig.h中把configUSE_IDLE_HOOK、configUSE_TICK_HOOK、configCHECK_FOR_STACK_OVERFLOW和configUSE_MALLOC_FAILED_HOOK的宏定义改为0
至此,应该能编译通过了。
7.修改SYSTEM文件和systick中断处理函数
因为原子的SYSTEM文件夹是针对UCOS编写的,所以要进行对应的修改
7.1 修改sys.h文件
把宏定义SYSTEM_SUPPORT_OS改为1即可,要支持OS,UCOS也一样的
//0,不支持os
//1,支持os
#define SYSTEM_SUPPORT_OS 1 //定义系统文件夹是否支持OS
调试过程中发现直接定义SYSTEM_SUPPORT_OS宏,更好一些,避免有些文件没有包含sys.h,导致一些异常难以发现。所以这里可以直接把这行去掉,然后在宏里面定义。
7.2 修改usart.c文件
打开SYSTEM文件夹下usart.c文件,添加FreeRTOS.h头文件
#if SYSTEM_SUPPORT_OS
#include "FreeRTOS.h" //FreeRtos
#endif
USART1的中断服务函数在使用UCOS时进出中断添加OSIntEnter()与OSIntExit(),使用FreeRTOS则不需要,故注释掉
//串口1中断服务程序
void USART1_IRQHandler(void)
{
u32 timeout=0;
u32 maxDelay=0x1FFFF;
//#if SYSTEM_SUPPORT_OS //使用OS
// OSIntEnter();
//#endif
HAL_UART_IRQHandler(&UART1_Handler); //调用HAL库中断处理公用函数
timeout=0;
while (HAL_UART_GetState(&UART1_Handler) != HAL_UART_STATE_READY)//等待就绪
{
timeout++;////超时处理
if(timeout>maxDelay) break;
}
timeout=0;
while(HAL_UART_Receive_IT(&UART1_Handler, (u8 *)aRxBuffer, RXBUFFERSIZE) != HAL_OK)//一次处理完成之后,重新开启中断并设置RxXferCount为1
{
timeout++; //超时处理
if(timeout>maxDelay) break;
}
//#if SYSTEM_SUPPORT_OS //使用OS
// OSIntExit();
//#endif
}
7.3 修改delay.c文件
使用以下代码:
#include "delay.h"
#include "sys.h"
//////////////////////////////////////////////////////////////////////////////////
//如果需要使用OS,则包括下面的头文件即可.
#if SYSTEM_SUPPORT_OS
#include "FreeRTOS.h" //freertos 使用
#include "task.h"
#endif
//////////////////////////////////////////////////////////////////////////////////
static u32 fac_us=0; //us延时倍乘数
#if SYSTEM_SUPPORT_OS
static u16 fac_ms=0; //ms延时倍乘数,在os下,代表每个节拍的ms数
#endif
//初始化延迟函数
//当使用ucos的时候,此函数会初始化ucos的时钟节拍
//SYSTICK的时钟固定为AHB时钟
//SYSCLK:系统时钟频率
void delay_init(u8 SYSCLK)
{
#if SYSTEM_SUPPORT_OS //如果需要支持OS.
u32 reload;
#endif
HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);//SysTick频率为HCLK
fac_us=SYSCLK; //不论是否使用OS,fac_us都需要使用
#if SYSTEM_SUPPORT_OS //如果需要支持OS.
reload=SYSCLK; //每秒钟的计数次数 单位为K
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/OS_TICKS_PER_SEC秒中断一次
SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk; //开启SYSTICK
#else
#endif
}
#if SYSTEM_SUPPORT_OS //如果需要支持OS.
//延时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时
//延时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(u32 nms)
{
u32 i;
for(i=0;i<nms;i++) delay_us(1000);
}
#endif
7.3 修改SysTick_Handler中断处理函数
使用以下代码:
#ifdef SYSTEM_SUPPORT_OS
#include "FreeRTOS.h" //freertos 使用
#include "task.h"
extern void xPortSysTickHandler(void);
//systick中断服务函数,使用OS时用到
void SysTick_Handler(void)
{
if(xTaskGetSchedulerState()!=taskSCHEDULER_NOT_STARTED)//系统已经运行
{
xPortSysTickHandler();
}
HAL_IncTick();
}
#else
void SysTick_Handler(void)
{
HAL_IncTick();
}
#endif
7.修改main文件,创建freertos任务
#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "beep.h"
#include "key.h"
/************************************************
ALIENTEK NANO板STM32F4开发板实验4
串口通信实验-HAL库版本
技术支持:www.openedv.com
淘宝店铺:http://eboard.taobao.com
关注微信公众平台微信号:"正点原子",免费获取STM32资料。
广州市星翼电子科技有限公司
作者:正点原子 @ALIENTEK
************************************************/
//说明,在工程宏定义中增加SYSTEM_SUPPORT_OS定义,则编译出来版本就是freertos创建任务的版本。否则就是普通的裸机版本。
#ifdef SYSTEM_SUPPORT_OS
#include "FreeRTOS.h"
#include "task.h"
#include "timers.h"
#include "semphr.h"
//500ms LED1灯状态翻转一次
static void led1Task( void * pvParameters )
{
u8 len;
while(1)
{
if(USART_RX_STA&0x8000)
{
len=USART_RX_STA&0x3fff;//得到此次接收到的数据长度
printf("\r\n您发送的消息为:\r\n");
HAL_UART_Transmit(&UART1_Handler,(uint8_t*)USART_RX_BUF,len,1000); //发送接收到的数据
while(__HAL_UART_GET_FLAG(&UART1_Handler,UART_FLAG_TC)!=SET); //等待发送结束
printf("\r\n\r\n");//插入换行
USART_RX_STA=0;
}
vTaskDelay(1);
}
}
//1000ms LED2灯状态翻转一次
static void led2Task( void * pvParameters )
{
u32 times=0;
while(1)
{
if(times%5000==0)
{
printf("\r\nALIENTEK NANO STM32开发板 Freertos串口实验\r\n");
printf("正点原子@ALIENTEK\r\n\r\n\r\n");
}
times++;
if(times%200==0)printf("请输入数据,以回车键结束\r\n");
if(times%30==0)LED0=!LED0;//闪烁LED,提示系统正在运行.
vTaskDelay(10);
}
}
void startTasks(void)
{
xTaskCreate(led1Task,"LED1",256,NULL,100,(TaskHandle_t *)NULL);
xTaskCreate(led2Task,"LED2",256,NULL,101,(TaskHandle_t *)NULL);
/* 启动任务调度器. */
vTaskStartScheduler();
}
#else
void startTasks(void)
{
u8 len;
u32 times=0;
while(1)
{
if(USART_RX_STA&0x8000)
{
len=USART_RX_STA&0x3fff;//得到此次接收到的数据长度
printf("\r\n您发送的消息为:\r\n");
HAL_UART_Transmit(&UART1_Handler,(uint8_t*)USART_RX_BUF,len,1000); //发送接收到的数据
while(__HAL_UART_GET_FLAG(&UART1_Handler,UART_FLAG_TC)!=SET); //等待发送结束
printf("\r\n\r\n");//插入换行
USART_RX_STA=0;
}else
{
if(times%5000==0)
{
printf("\r\nALIENTEK NANO STM32开发板 裸机串口实验\r\n");
printf("正点原子@ALIENTEK\r\n\r\n\r\n");
}
times++;
if(times%200==0)printf("请输入数据,以回车键结束\r\n");
if(times%30==0)LED0=!LED0;//闪烁LED,提示系统正在运行.
delay_ms(10);
}
}
}
#endif
int main(void)
{
HAL_Init(); //初始化HAL库
Stm32_Clock_Init(96,4,2,4); //设置时钟,96Mhz
delay_init(96); //初始化延时函数
LED_Init(); //初始化LED
uart_init(115200); //初始化串口115200
startTasks();
}
其他参考文章:
2、STM32F407移植FreeRTOS步骤_nandycooh的博客-优快云博客_stm32f407移植freertos