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">&amp;</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">-&gt;</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">&amp;</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">&amp;</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">&amp;</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">&amp;</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">&gt;</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">&amp;</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">&gt;</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);
 }
}
posted @ 2024-03-27 21:04  Bathwind_W  阅读(93)  评论(0编辑  收藏  举报  来源