FreeRTOS移植STM32F103开发板HAL库版本

FreeRTOS移植STM32F103开发板HAL库版本:

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
	
	HAL_UART_Receive_IT(&UART1_Handler, (u8 *)aRxBuffer, RXBUFFERSIZE);//该函数会开启接收中断:标志位UART_IT_RXNE,并且设置接收缓冲以及接收缓冲接收最大数据量
  
}

//UART底层初始化,时钟使能,引脚配置,中断配置
//此函数会被HAL_UART_Init()调用
//huart:串口句柄

void HAL_UART_MspInit(UART_HandleTypeDef *huart)
{
    //GPIO端口设置
	GPIO_InitTypeDef GPIO_Initure;
	
	if(huart->Instance==USART1)//如果是串口1,进行串口1 MSP初始化
	{
		__HAL_RCC_GPIOA_CLK_ENABLE();			//使能GPIOA时钟
		__HAL_RCC_USART1_CLK_ENABLE();			//使能USART1时钟
		__HAL_RCC_AFIO_CLK_ENABLE();
	
		GPIO_Initure.Pin=GPIO_PIN_9;			//PA9
		GPIO_Initure.Mode=GPIO_MODE_AF_PP;		//复用推挽输出
		GPIO_Initure.Pull=GPIO_PULLUP;			//上拉
		GPIO_Initure.Speed=GPIO_SPEED_FREQ_HIGH;//高速
		HAL_GPIO_Init(GPIOA,&GPIO_Initure);	   	//初始化PA9

		GPIO_Initure.Pin=GPIO_PIN_10;			//PA10
		GPIO_Initure.Mode=GPIO_MODE_AF_INPUT;	//模式要设置为复用输入模式!	
		HAL_GPIO_Init(GPIOA,&GPIO_Initure);	   	//初始化PA10
		
#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->Instance==USART1)//如果是串口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;//接收数据错误,重新开始接收	  
				}		 
			}
		}

	}
}
 
//串口1中断服务程序
void USART1_IRQHandler(void)                	
{ 
	u32 timeout=0;

	HAL_UART_IRQHandler(&UART1_Handler);	//调用HAL库中断处理公用函数
	
	timeout=0;
    while (HAL_UART_GetState(&UART1_Handler) != HAL_UART_STATE_READY)//等待就绪
	{
	 timeout++;超时处理
     if(timeout>HAL_MAX_DELAY) break;		
	
	}
     
	timeout=0;
	while(HAL_UART_Receive_IT(&UART1_Handler, (u8 *)aRxBuffer, RXBUFFERSIZE) != HAL_OK)//一次处理完成之后,重新开启中断并设置RxXferCount为1
	{
	 timeout++; //超时处理
	 if(timeout>HAL_MAX_DELAY) break;	
	}

} 
#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);
 }
}
  • 21
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值