FreeRTOS移植STM32F103开发板HAL库版本
<svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
<path stroke-linecap="round" d="M5,0 0,2.5 5,5z" id="raphael-marker-block" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0);"></path>
</svg>
<h2><a id="FreeRTOSSTM32F103HAL_1"></a>FreeRTOS移植STM32F103开发板HAL库版本:</h2>
1:下载FreeRTOS源码
第一步首先去FreeRTOS官网:FreeRTOS - Market leading RTOS (Real Time Operating System) for embedded systems with Internet of Things extensions
下载最新源码。下载下来之后,源码结构如下:
其中我们要用到的移植文件为主要是FreeRTOS 这个文件夹里面的内容。Demo 文件夹里面就是 FreeRTOS 的相关例程,License这个文件夹里面就是相关的许可信息,要用 FreeRTOS 做产品的得仔细看看,尤其是要出口的产品。Source文件夹中存放的就是FreeRTOS的源码了。打开如下图所示:
归根结底就是个纯软件的东西,它是怎么和硬件联系在一起的呢?软件到硬件中间必须有一个桥梁,portable 文件夹里面的东西就是 FreeRTOS系统和具体的硬件之间的连接桥梁!不同的编译环境,不同的 MCU,其桥梁应该是不同的,打开 portable 文件夹,如下图所示:
红框标注的文件夹是我们Keil平台所要用到的。MenMang文件夹是关于内存管理的。RVDS是用MDK环境编译所需要的文件。
打开后如上图所示,由于我们要移植的F103板子是CM3内核的。所以只需要复制这个文件夹包含的内容即可。也就是下图所包含的内容:
2:以正点原子HAL库跑马灯为基础工程移植:
在基础工程里面新建FreeRTOS文件夹,并将Source文件夹中这些文件拷贝过来。具体内容如下图所示:
然后要把portable中的文件夹给删除了。只留下我们需要的三个文件夹。具体内容如上图所示。
打开keil工程,新建下图中的两个文件夹:
建立好文件夹后,开始往对应文件夹添加对应文件,添加后的结果如下图所示:
接着要添加对应包含头文件的路径,要添加的具体路径如下图:
以上步骤完成后,对工程进行编译,发生会提示错误。因为缺少FreeRTOSConfig.h这个文件。这个就是FreeRTOS 的配置文件,一般的操作系统都有裁剪、配置功能,而这些裁剪及配置都是通过一个文件来完成的,基本都是通过宏定义来完成对系统的配置和裁剪的,
我们复制完这个文件后,发现编译后依旧报错:
定位一步一步错误到这里:
这里在外面用VSCODE打开并将4U更改为4编译就不会报这个错误了,但是再次进行编译又会产生新的错误。具体错误如下图:错误说的是函数重复定义。具体错误如下图:
解决方法就是将stm32f1xx_it.c的这俩个函数给注释掉,具体如下图:
注释完了后,再次进行编译可以已经不再报错了。
2.1修改SYSTEM文件夹中的三个.c文件
首先修改Usart.c中的内容:
#include "sys.h" #include "usart.h" #if 1 #pragma import(__use_no_semihosting) //标准库需要的支持函数 struct __FILE { int handle;
};
FILE __stdout;
//定义_sys_exit()以避免使用半主机模式
void _sys_exit(int x)
{
x = x;
}
//重定义fputc函数
int fputc(int ch, FILE *f)
{
while((USART1->SR&0X40)==0);//循环发送,直到发送完毕
USART1->DR = (u8) ch;
return ch;
}
#endif#if EN_USART1_RX //如果使能了接收
//串口1中断服务程序
//注意,读取USARTx->SR能避免莫名其妙的错误
u8 USART_RX_BUF[USART_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节.
//接收状态
//bit15, 接收完成标志
//bit14, 接收到0x0d
//bit13~0, 接收到的有效字节数目
u16 USART_RX_STA=0; //接收状态标记u8 aRxBuffer[RXBUFFERSIZE];//HAL库使用的串口接收缓冲
UART_HandleTypeDef UART1_Handler; //UART句柄//初始化IO 串口1
//bound:波特率
void uart_init(u32 bound)
{
//UART 初始化设置
UART1_Handler.Instance=USART1; //USART1
UART1_Handler.Init.BaudRate=bound; //波特率
UART1_Handler.Init.WordLength=UART_WORDLENGTH_8B; //字长为8位数据格式
UART1_Handler.Init.StopBits=UART_STOPBITS_1; //一个停止位
UART1_Handler.Init.Parity=UART_PARITY_NONE; //无奇偶校验位
UART1_Handler.Init.HwFlowCtl=UART_HWCONTROL_NONE; //无硬件流控
UART1_Handler.Init.Mode=UART_MODE_TX_RX; //收发模式
HAL_UART_Init(&UART1_Handler); //HAL_UART_Init()会使能UART1<span class="token function">HAL_UART_Receive_IT</span><span class="token punctuation">(</span><span class="token operator">&</span>UART1_Handler<span class="token punctuation">,</span> <span class="token punctuation">(</span>u8 <span class="token operator">*</span><span class="token punctuation">)</span>aRxBuffer<span class="token punctuation">,</span> RXBUFFERSIZE<span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token comment">//该函数会开启接收中断:标志位UART_IT_RXNE,并且设置接收缓冲以及接收缓冲接收最大数据量</span>
}
//UART底层初始化,时钟使能,引脚配置,中断配置
//此函数会被HAL_UART_Init()调用
//huart:串口句柄void HAL_UART_MspInit(UART_HandleTypeDef *huart)
{
//GPIO端口设置
GPIO_InitTypeDef GPIO_Initure;<span class="token keyword">if</span><span class="token punctuation">(</span>huart<span class="token operator">-></span>Instance<span class="token operator">==</span>USART1<span class="token punctuation">)</span><span class="token comment">//如果是串口1,进行串口1 MSP初始化</span> <span class="token punctuation">{<!-- --></span> <span class="token function">__HAL_RCC_GPIOA_CLK_ENABLE</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">//使能GPIOA时钟</span> <span class="token function">__HAL_RCC_USART1_CLK_ENABLE</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">//使能USART1时钟</span> <span class="token function">__HAL_RCC_AFIO_CLK_ENABLE</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> GPIO_Initure<span class="token punctuation">.</span>Pin<span class="token operator">=</span>GPIO_PIN_9<span class="token punctuation">;</span> <span class="token comment">//PA9</span> GPIO_Initure<span class="token punctuation">.</span>Mode<span class="token operator">=</span>GPIO_MODE_AF_PP<span class="token punctuation">;</span> <span class="token comment">//复用推挽输出</span> GPIO_Initure<span class="token punctuation">.</span>Pull<span class="token operator">=</span>GPIO_PULLUP<span class="token punctuation">;</span> <span class="token comment">//上拉</span> GPIO_Initure<span class="token punctuation">.</span>Speed<span class="token operator">=</span>GPIO_SPEED_FREQ_HIGH<span class="token punctuation">;</span><span class="token comment">//高速</span> <span class="token function">HAL_GPIO_Init</span><span class="token punctuation">(</span>GPIOA<span class="token punctuation">,</span><span class="token operator">&</span>GPIO_Initure<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">//初始化PA9</span> GPIO_Initure<span class="token punctuation">.</span>Pin<span class="token operator">=</span>GPIO_PIN_10<span class="token punctuation">;</span> <span class="token comment">//PA10</span> GPIO_Initure<span class="token punctuation">.</span>Mode<span class="token operator">=</span>GPIO_MODE_AF_INPUT<span class="token punctuation">;</span> <span class="token comment">//模式要设置为复用输入模式! </span> <span class="token function">HAL_GPIO_Init</span><span class="token punctuation">(</span>GPIOA<span class="token punctuation">,</span><span class="token operator">&</span>GPIO_Initure<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">//初始化PA10</span>
#if EN_USART1_RX
HAL_NVIC_EnableIRQ(USART1_IRQn); //使能USART1中断通道
HAL_NVIC_SetPriority(USART1_IRQn,3,3); //抢占优先级3,子优先级3
#endif
}
}void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if(huart->InstanceUSART1)//如果是串口1
{
if((USART_RX_STA&0x8000)0)//接收未完成
{
if(USART_RX_STA&0x4000)//接收到了0x0d
{
if(aRxBuffer[0]!=0x0a)USART_RX_STA=0;//接收错误,重新开始
else USART_RX_STA|=0x8000; //接收完成了
}
else //还没收到0X0D
{
if(aRxBuffer[0]==0x0d)USART_RX_STA|=0x4000;
else
{
USART_RX_BUF[USART_RX_STA&0X3FFF]=aRxBuffer[0] ;
USART_RX_STA++;
if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//接收数据错误,重新开始接收
}
}
}<span class="token punctuation">}</span>
}
//串口1中断服务程序
void USART1_IRQHandler(void)
{
u32 timeout=0;<span class="token function">HAL_UART_IRQHandler</span><span class="token punctuation">(</span><span class="token operator">&</span>UART1_Handler<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">//调用HAL库中断处理公用函数</span> timeout<span class="token operator">=</span><span class="token number">0</span><span class="token punctuation">;</span> <span class="token keyword">while</span> <span class="token punctuation">(</span><span class="token function">HAL_UART_GetState</span><span class="token punctuation">(</span><span class="token operator">&</span>UART1_Handler<span class="token punctuation">)</span> <span class="token operator">!=</span> HAL_UART_STATE_READY<span class="token punctuation">)</span><span class="token comment">//等待就绪</span> <span class="token punctuation">{<!-- --></span> timeout<span class="token operator">++</span><span class="token punctuation">;</span><span class="token comment">超时处理</span> <span class="token keyword">if</span><span class="token punctuation">(</span>timeout<span class="token operator">></span>HAL_MAX_DELAY<span class="token punctuation">)</span> <span class="token keyword">break</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> timeout<span class="token operator">=</span><span class="token number">0</span><span class="token punctuation">;</span> <span class="token keyword">while</span><span class="token punctuation">(</span><span class="token function">HAL_UART_Receive_IT</span><span class="token punctuation">(</span><span class="token operator">&</span>UART1_Handler<span class="token punctuation">,</span> <span class="token punctuation">(</span>u8 <span class="token operator">*</span><span class="token punctuation">)</span>aRxBuffer<span class="token punctuation">,</span> RXBUFFERSIZE<span class="token punctuation">)</span> <span class="token operator">!=</span> HAL_OK<span class="token punctuation">)</span><span class="token comment">//一次处理完成之后,重新开启中断并设置RxXferCount为1</span> <span class="token punctuation">{<!-- --></span> timeout<span class="token operator">++</span><span class="token punctuation">;</span> <span class="token comment">//超时处理</span> <span class="token keyword">if</span><span class="token punctuation">(</span>timeout<span class="token operator">></span>HAL_MAX_DELAY<span class="token punctuation">)</span> <span class="token keyword">break</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
}
#endif
修改delay.c的文件
#include "delay.h" #include "sys.h" #include "FreeRTOS.h" #include "task.h" static u32 fac_us=0; //us延时倍乘数 static u16 fac_ms=0; //ms延时倍乘数,在os下,代表每个节拍的ms数 extern void xPortSysTickHandler(void);
//systick中断服务函数,使用ucos时用到
void SysTick_Handler(void)
{
HAL_IncTick();
if(xTaskGetSchedulerState()!=taskSCHEDULER_NOT_STARTED)//系统已经运行
{
xPortSysTickHandler();
}
}//初始化延迟函数
//当使用ucos的时候,此函数会初始化ucos的时钟节拍
//SYSTICK的时钟固定为AHB时钟
//SYSCLK:系统时钟频率
void delay_init(u8 SYSCLK)
{
#if SYSTEM_SUPPORT_OS //如果需要支持OS.
u32 reload;
#endif
SysTick->CTRL = 0;
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; //根据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
#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; //时间超过/等于要延迟的时间,则退出.
}
};
}
void delay_ms(u16 nms)
{
u32 i;
for(i=0;i<nms;i++) delay_us(1000);
}
#endif
修改之后继续编辑:
主函数添加下列代码:
int main(void)
{
HAL_Init();
Stm32_Clock_Init(RCC_PLL_MUL9);
delay_init(72);
LED_Init();
while(1)
{ //创建开始任务
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(500);
LED1=1;
vTaskDelay(500);
}
}
本文来自博客园,作者:Bathwind_W,转载请注明原文链接:https://www.cnblogs.com/bathwind/p/18107940