1:源码获取

程序的源码有多种获取途径,但是主流的途径还是从FreeRTOS的官网进行获取

freeRTOS官网: https://www.freertos.org/zh-cn-cmn-s/a00104.html

得到的源码展示

 打开FreeRTOS的内核

打开FreeRTOS的资源文件

打开portable文件夹:该文件夹是硬件和FreeRTOS连接的桥梁


2:移植FreeRtos源码

FreeRTOS移植步骤

  • 1:添加FreeRTOS源码,将FreeRTOS源码添加到基础工程,头文件等路径

  • 2:添加FreeRTOSConfig.h文件,添加FreeRTOSConfig.h配置文件

  • 3:修改SYSTEM文件夹,主要包含sys,delay,和usart

  • 4:修改中断相关文件, 修改Systick中断,SVC中断,PendSV中断

  • 5:添加应用程序,验证移植是否成功

在 portable 文件夹,我们只需要留下 keil、MemMang 和 RVDS这三个文件夹,其他的都可以删除掉

 

 

MenMang目录下这个是FreeRTOS的内存管理算法一共是有5个我们使用到的主要是第四个也就是heap_4.c

RVDS目录下包含的是各个芯片的内核文件,使用不同的芯片在做开发的时候选择不同的内核文件

打开基础工程,新建分组 FreeRTOS_CORE 和 FreeRTOS_PORTABLE,然后向这两个分组中添加文件

分组 FreeRTOS_CORE 中的文件在什么地方就不说了,打开 FreeRTOS 源码一目了然。重点来说说 FreeRTOS_PORTABLE 分组中的 port.c 和 heap_4.c 是怎么来的,port.c 是 RVDS 文件夹下的 ARM_CM3 中的文件,因为 STM32F103 是 Cortex-M3 内核的,因此要选择 ARM_CM3中的 port.c 文件。heap_4.c 是 MemMang 文件夹中的,前面说了 MemMang 是跟内存管理相关的,里面有 5 个 c 文件:heap_1.c、heap_2.c、heap_3.c、heap_4.c 和 heap_5.c。这 5 个 c 文件是五种不同的内存管理方法,就像从北京到上海你可以坐火车、坐飞机,如果心情好的话也可以走路,反正有很多种方法,只要能到上海就行。这里也一样的,这 5 个文件都可以用来作为FreeRTOS 的内存管理文件,只是它们的实现原理不同,各有利弊。这里我们选择 heap_4.c,

这是FreeRTOS的内存管理算法,选择的是第四个

这里的port.c选择的是基于F1系列单片机的,要根据自己的芯片选择合适的内核进行移植

添加完 FreeRTOS 源码中的 C 文件以后还要添加 FreeRTOS 源码的头文件路径,头文件路

具体的实现效果如下所示

移植后文件夹效果


3:移植过程中遇到的问题

头文件路径添加完成以后编译一下,看看有没有什么错误,结果会发现提示打不开!!!

“FreeRTOSConfig.h”这个文件

这是因为缺少 FreeRTOSConfig.h 文件,这个文件在哪里找呢?你可以自己创建,显然这不是一个明智的做法。我们可以找找 FreeRTOS 的官方移植工程中会不会有这个文件,打开FreeRTOS 针对 STM32F103 的移植工程文件,文件夹是 CORTEX_STM32F103_Keil,打开以后

添加FreeConfig文件可以在官方源码下找到然后复制进入自己例程下的user文件夹下,实际上这个文件夹的路径是可以自己指定的我这里就把它放到User这个文件夹下了

但是这里我们使用的是原子提供的FreeConfig文件不使用官方的文件


4:修改 SYSTEM 文件

SYSTEM 文件夹里面的文件一开始是针对 UCOS 而编写的,所以如果使用 FreeRTOS 的话就需要做相应的修改。本来打算让 SYSTEM 文件夹也支持 FreeRTOS,但是这样的话会导致SYSTEM 里面的文件太过于复杂,这样非常不利于初学者学习,所以这里就专门针对 FreeRTOS修改了 SYSTEM 里面的文件。

修改后的sys.c文件

 

#include "sys.h"

//	 
//本程序只供学习使用,未经作者许可,不得用于其它任何用途
//ALIENTEK Mini STM32开发板
//系统中断分组设置化		   
//正点原子@ALIENTEK
//技术论坛:www.openedv.com
//修改日期:2012/9/10
//版本:V1.4
//版权所有,盗版必究。
//Copyright(C) 正点原子 2009-2019
//All rights reserved
//********************************************************************************  
//THUMB指令不支持汇编内联
//采用如下方法实现执行汇编指令WFI  
void WFI_SET(void)
{
	__ASM volatile("wfi");		  
}
//关闭所有中断
void INTX_DISABLE(void)
{		  
	__ASM volatile("cpsid i");
}
//开启所有中断
void INTX_ENABLE(void)
{
	__ASM volatile("cpsie i");		  
}
//设置栈顶地址
//addr:栈顶地址
__asm void MSR_MSP(u32 addr) 
{
    MSR MSP, r0 			//set Main Stack value
    BX r14
}

修改 usart.c 文件usart.c 文件修改也很简单,usart.c 文件有两部分要修改,一个是添加 FreeRTOS.h 头文件, 默认是添加的 UCOS 中的 includes.h 头文件,这里可以直接删除掉:

 另外一个就是 USART1 的中断服务函数,在使用 UCOS 的时候进出中断的时候需要添加OSIntEnter()和 OSIntExit(),使用 FreeRTOS 的话就不需要了,所以将这两行代码删除掉,修改以后如下:

修改后的usart.c文件

#include "sys.h"
#include "usart.h"	

#if SYSTEM_SUPPORT_OS

#include "FreeRTOS.h" //os 使用

#endif
//加入以下代码,支持printf函数,而不需要选择use MicroLIB	  
#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 

/*使用microLib的方法*/
 /* 
int fputc(int ch, FILE *f)
{
	USART_SendData(USART1, (uint8_t) ch);

	while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET) {}	
   
    return ch;
}
int GetKey (void)  { 

    while (!(USART1->SR & USART_FLAG_RXNE));

    return ((int)(USART1->DR & 0x1FF));
}
*/
 
#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;       //接收状态标记	  
  
void uart_init(u32 bound)
{
	//GPIO端口设置
	GPIO_InitTypeDef GPIO_InitStructure;
	USART_InitTypeDef USART_InitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;

	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE);	//使能USART1,GPIOA时钟

	//USART1_TX   GPIOA.9
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;	//复用推挽输出
	GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.9

	//USART1_RX	  GPIOA.10初始化
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//PA10
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
	GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.10  

	//Usart1 NVIC 配置
	NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;//抢占优先级3
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;		//子优先级3
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;			//IRQ通道使能
	NVIC_Init(&NVIC_InitStructure);	//根据指定的参数初始化VIC寄存器

	//USART 初始化设置

	USART_InitStructure.USART_BaudRate = bound;//串口波特率
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式
	USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位
	USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制
	USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;	//收发模式

  USART_Init(USART1, &USART_InitStructure); //初始化串口1
  USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启串口接受中断
  USART_Cmd(USART1, ENABLE);                    //使能串口1 

}

void USART1_IRQHandler(void)                	//串口1中断服务程序
{
	u8 Res;

	
	if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)  //接收中断(接收到的数据必须是0x0d 0x0a结尾)
		{
		Res =USART_ReceiveData(USART1);	//读取接收到的数据
		
		if((USART_RX_STA&0x8000)==0)//接收未完成
			{
			if(USART_RX_STA&0x4000)//接收到了0x0d
				{
				if(Res!=0x0a)USART_RX_STA=0;//接收错误,重新开始
				else USART_RX_STA|=0x8000;	//接收完成了 
				}
			else //还没收到0X0D
				{	
				if(Res==0x0d)USART_RX_STA|=0x4000;
				else
					{
					USART_RX_BUF[USART_RX_STA&0X3FFF]=Res ;
					USART_RX_STA++;
					if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//接收数据错误,重新开始接收	  
					}		 
				}
			}   		 
     } 

} 
#endif	

修改:delay.c 文件修改的就比较大了,因为涉及到 FreeRTOS 的系统时钟,delay.c 文件里面有 4个函数,先来看一下函数 SysTick_Handler(),此函数是滴答定时器的中断服务函数,代码如下:

#include "delay.h"

// 	 
//如果需要使用OS,则包括下面的头文件即可.
#if SYSTEM_SUPPORT_OS
#include "FreeRTOS.h"					//ucos 使用	  
#include "task.h"
#endif


static u8  fac_us=0;							//us延时倍乘数			   
static u16 fac_ms=0;							//ms延时倍乘数,在ucos下,代表每个节拍的ms数
	
	


extern void xPortSysTickHandler(void);

void SysTick_Handler(void)
{	
  if(xTaskGetSchedulerState()!=taskSCHEDULER_NOT_STARTED)//系统已经运行
   {
   xPortSysTickHandler();
   }
}


			   

void delay_init()
{
		u32 reload;
		SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK);//选择外部时钟 HCLK		
		fac_us=SystemCoreClock/1000000; //不论是否使用 OS,fac_us 都需要使用
		reload=SystemCoreClock/1000000; //每秒钟的计数次数 单位为 M
		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/configTICK_RATE_HZ 秒中断
		//一次
		SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk; //开启 SYSTICK
}	


#if SYSTEM_SUPPORT_OS  							//如果需要支持OS.

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)
		{
		//这里注意一下 SYSTICK 是一个递减的计数器就可以了.
		if(tnow<told)tcnt+=told-tnow;
		else tcnt+=reload-tnow+told;
		told=tnow;
		if(tcnt>=ticks)break; //时间超过/等于要延迟的时间,则退出.
		}
		};
}

void delay_ms(u16 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);
}

#endif 

修改完成后出现编译错误

解决问题的步骤就是将task文件添加到FreeRTOS文件之后

又发现一个错误

*** Using Compiler 'V5.06 update 5 (build 528)', folder: 'F:\Keil_v5\ARM\ARMCC\Bin'
Build target 'MALLOC'
compiling key.c...
compiling usmart_config.c...
compiling delay.c...
compiling main.c...
compiling lcd.c...
linking...
..\OBJ\MALLOC.axf: Error: L6218E: Undefined symbol xTaskGetSchedulerState (referred from delay.o).
Not enough information to list image symbols.
Not enough information to list load addresses in the image map.
Finished: 2 information, 0 warning and 1 error messages.
"..\OBJ\MALLOC.axf" - 1 Error(s), 0 Warning(s).
Target not created.
Build Time Elapsed:  00:00:01

解决问题的方法FreeRTOS文件中修改这个参数

参考博客:https://blog.csdn.net/weixin_46672094/article/details/124721375

然后又出现这个错误

把这个东西改为ST-Link试试应该是可以的:然后又出现这个错误

解决这个问题方法:出现这个问题的原因是原先移植文件的芯片和我们需要移植的芯片使用的是不同的型号,这里需要点击魔术棒按钮然后修改为我们所使用的芯片内核文件

 

STM32F10X_MD,USE_STDPERIPH_DRIVER

具体的操作步骤是

找一个startup_stm32f10x_md.s文件放入CORE文件夹中然后将这个文件ADD进入keil中

然后移除原来文件夹中的hd.s文件

参考博客:Error: Flash Download failed - “Cortex-M3“_error: flash download failed - "cortex-m3-CSDN博客文章浏览阅读4.9w次,点赞155次,收藏525次。问题描述:KEIL5使用ST-LINKV2烧录程序进入STM32F103c8t6时,程序编译没问题,但出现“Error: Flash Download failed - “Cortex-M3””错误,如下:原因分析:以正点原子STM32F1开发板中库函数的跑马灯程序为例,它的程序适用于STM32F103ZET6芯片,为大容量芯片,而与STM32F103C8T6芯片并不完全兼容。所以要想正常烧录,得需要将程序工程配置成STM32F103C8T6的环境。解决方案:1、点击魔术棒,点击"Devi_error: flash download failed - "cortex-m3https://blog.csdn.net/qq_44619221/article/details/124051529

解决以上问题之后又来了一堆的问题

这个问题出现的原因主要是移前的芯片存储容量高于当前的芯片,当前的芯片无法存储如此多的数据所以我们需要修改一个参数就是FreeRTOSConfig.h

*** Using Compiler 'V5.06 update 5 (build 528)', folder: 'F:\Keil_v5\ARM\ARMCC\Bin'
Build target 'MALLOC'
assembling startup_stm32f10x_md.s...
linking...
..\OBJ\MALLOC.axf: Error: L6406E: No space in execution regions with .ANY selector matching malloc.o(.bss).
..\OBJ\MALLOC.axf: Error: L6406E: No space in execution regions with .ANY selector matching heap_4.o(.bss).
..\OBJ\MALLOC.axf: Error: L6406E: No space in execution regions with .ANY selector matching startup_stm32f10x_md.o(STACK).
..\OBJ\MALLOC.axf: Error: L6406E: No space in execution regions with .ANY selector matching startup_stm32f10x_md.o(HEAP).
..\OBJ\MALLOC.axf: Error: L6406E: No space in execution regions with .ANY selector matching usmart_config.o(.data).
..\OBJ\MALLOC.axf: Error: L6406E: No space in execution regions with .ANY selector matching usart.o(.bss).
..\OBJ\MALLOC.axf: Error: L6406E: No space in execution regions with .ANY selector matching tasks.o(.bss).
..\OBJ\MALLOC.axf: Error: L6406E: No space in execution regions with .ANY selector matching libspace.o(.bss).
..\OBJ\MALLOC.axf: Error: L6406E: No space in execution regions with .ANY selector matching tasks.o(.data).
..\OBJ\MALLOC.axf: Error: L6406E: No space in execution regions with .ANY selector matching malloc.o(.data).
..\OBJ\MALLOC.axf: Error: L6406E: No space in execution regions with .ANY selector matching usmart.o(.data).
..\OBJ\MALLOC.axf: Error: L6406E: No space in execution regions with .ANY selector matching heap_4.o(.data).
..\OBJ\MALLOC.axf: Error: L6406E: No space in execution regions with .ANY selector matching system_stm32f10x.o(.data).
..\OBJ\MALLOC.axf: Error: L6406E: No space in execution regions with .ANY selector matching stm32f10x_rcc.o(.data).
..\OBJ\MALLOC.axf: Error: L6406E: No space in execution regions with .ANY selector matching lcd.o(.bss).
..\OBJ\MALLOC.axf: Error: L6406E: No space in execution regions with .ANY selector matching main.o(.data).
..\OBJ\MALLOC.axf: Error: L6406E: No space in execution regions with .ANY selector matching usart.o(.data).
..\OBJ\MALLOC.axf: Error: L6406E: No space in execution regions with .ANY selector matching lcd.o(.data).
..\OBJ\MALLOC.axf: Error: L6406E: No space in execution regions with .ANY selector matching delay.o(.data).
..\OBJ\MALLOC.axf: Error: L6406E: No space in execution regions with .ANY selector matching port.o(.data).
..\OBJ\MALLOC.axf: Error: L6407E: Sections of aggregate size 0xf850 bytes could not fit into .ANY selector(s).
Not enough information to list image symbols.
Not enough information to list load addresses in the image map.
Finished: 2 information, 0 warning and 21 error messages.
"..\OBJ\MALLOC.axf" - 21 Error(s), 0 Warning(s).
Target not created.
Build Time Elapsed:  00:00:01

 把这个 种的内存容量改为10*1024

#define configTOTAL_HEAP_SIZE					((size_t)(10*1024))     //系统所有总的堆大小

参考来自这个博客 

https://www.cnblogs.com/hexia7935/p/16449377.htmlicon-default.png?t=N7T8https://www.cnblogs.com/hexia7935/p/16449377.html

这是另外一个解决办法的尝试

怀疑有可能某些段的size溢出了。

因为使用的ARM芯片内置flash为64KB,RAM = 8KB,因此不太可能是flash溢出了,很可能是RAM溢出了。

所以尝试了两种解决方法:

第一种是将keil option里面的编译优化选项,从level 0 调高到level 2,看看经过优化还会不会有溢出的问题。

实验的结果是没有溢出了,所以怀疑某些段溢出这个定论是对的,下一步就是要区分是RAM还是Flash溢出了

5:编写测试程序验证移植效果

main.c测试程序

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "freeRTOS.h"
#include "task.h"

TaskHandle_t myTaskHandler;

void myTask(void * arg){
    
	  while(1){
		  GPIO_ResetBits(GPIOA, GPIO_Pin_1);  
      vTaskDelay(500);
			GPIO_SetBits(GPIOA, GPIO_Pin_1); 
			vTaskDelay(500);
			
		}
}

int main(void)
{
		/*开启时钟*/
		RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);		//开启GPIOA的时钟
		
		/*GPIO初始化*/
		GPIO_InitTypeDef GPIO_InitStructure;
		GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
		GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
		GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
		GPIO_Init(GPIOA, &GPIO_InitStructure);						//将PA1和PA2引脚初始化为推挽输出
		
		/*设置GPIO初始化后的默认电平*/
		GPIO_ResetBits(GPIOA, GPIO_Pin_1);				//设置PA1和PA2引脚为低电平
	  xTaskCreate(myTask,"myTask",512,NULL,2,&myTaskHandler);
	  vTaskStartScheduler();
	  while(1){
		
		}
}


编译程序后得到的结果 -------------> 编译通过

 使用ST-Link下载到开发版中实现程序效果

 

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐