一、裁剪官方例程
我们先从FreeRTOS官网下载v202212.01版本的源码https://github.com/FreeRTOS/FreeRTOS/releases/download/202212.01/FreeRTOSv202212.01.ziphttps://github.com/FreeRTOS/FreeRTOS/releases/download/202212.01/FreeRTOSv202212.01.zip
下载完成后,解压其中的FreeRTOS文件夹,进入FreeRTOS目录下,删除Test和License文件夹
然后进入FreeRTOS\Demo目录下,删除除了Common和CORTEX_STM32F103_Keil的所有文件夹
进入CORTEX_STM32F103_Keil目录,删除一些不需要的文件夹和文件,如:ParTest、serial、
LCD_Message.h、spi_flash.c、timertest.c
进入Keil工程后,退出重新进入一下,使Keil工程文件的后缀变为.uvprojx
移除Demo Files组下除main.c以外的文件,移除System组下的lcd.c
进入FreeRTOS\Demo\Common\drivers\ST\STM32F10xFWLib\src目录下,把cortexm3_macro_rvds.s文件复制到FreeRTOS\Demo\CORTEX_STM32F103_Keil目录下,然后删除Common目录
进入Keil工程,移除System组下的cortexm3_macro_rvds.s文件,将其重新添加一下,然后修改main.c文件,创建三个任务,分别打印1、2、3
#include <stdio.h>
#define _USART
#include "stm32f10x_lib.h" // Device header
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "stm32f10x_it.h"
static void prvSetupHardware( void );
void Serial_Init(void);
void Task1(void *param)
{
while(1)
{
printf("1");
}
}
void Task2(void *param)
{
while(1)
{
printf("2");
}
}
void Task3(void *param)
{
while(1)
{
printf("3");
}
}
StaticTask_t ppxIdleTaskTCB;
StackType_t ppxIdleTaskStack[100];
void vApplicationGetIdleTaskMemory( StaticTask_t ** ppxIdleTaskTCBBuffer,
StackType_t ** ppxIdleTaskStackBuffer,
uint32_t * pulIdleTaskStackSize )
{
*ppxIdleTaskTCBBuffer = &ppxIdleTaskTCB;
*ppxIdleTaskStackBuffer = ppxIdleTaskStack;
*pulIdleTaskStackSize = 100;
}
int main( void )
{
#ifdef DEBUG
debug();
#endif
prvSetupHardware();
xTaskCreate(Task1,"Task1",100,NULL,1,NULL);
xTaskCreate(Task2,"Task2",100,NULL,1,NULL);
xTaskCreate(Task3,"Task3",100,NULL,1,NULL);
vTaskStartScheduler();
return 0;
}
static void prvSetupHardware( void )
{
/* Start with the clocks in their expected state. */
RCC_DeInit();
/* Enable HSE (high speed external clock). */
RCC_HSEConfig( RCC_HSE_ON );
/* Wait till HSE is ready. */
while( RCC_GetFlagStatus( RCC_FLAG_HSERDY ) == RESET )
{
}
/* 2 wait states required on the flash. */
*( ( unsigned long * ) 0x40022000 ) = 0x02;
/* HCLK = SYSCLK */
RCC_HCLKConfig( RCC_SYSCLK_Div1 );
/* PCLK2 = HCLK */
RCC_PCLK2Config( RCC_HCLK_Div1 );
/* PCLK1 = HCLK/2 */
RCC_PCLK1Config( RCC_HCLK_Div2 );
/* PLLCLK = 8MHz * 9 = 72 MHz. */
RCC_PLLConfig( RCC_PLLSource_HSE_Div1, RCC_PLLMul_9 );
/* Enable PLL. */
RCC_PLLCmd( ENABLE );
/* Wait till PLL is ready. */
while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET)
{
}
/* Select PLL as system clock source. */
RCC_SYSCLKConfig( RCC_SYSCLKSource_PLLCLK );
/* Wait till PLL is used as system clock source. */
while( RCC_GetSYSCLKSource() != 0x08 )
{
}
/* Enable GPIOA, GPIOB, GPIOC, GPIOD, GPIOE and AFIO clocks */
RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB |RCC_APB2Periph_GPIOC
| RCC_APB2Periph_GPIOD | RCC_APB2Periph_GPIOE | RCC_APB2Periph_AFIO, ENABLE );
/* SPI2 Periph clock enable */
RCC_APB1PeriphClockCmd( RCC_APB1Periph_SPI2, ENABLE );
/* Set the Vector Table base address at 0x08000000 */
NVIC_SetVectorTable( NVIC_VectTab_FLASH, 0x0 );
NVIC_PriorityGroupConfig( NVIC_PriorityGroup_4 );
/* Configure HCLK clock as SysTick clock source. */
SysTick_CLKSourceConfig( SysTick_CLKSource_HCLK );
Serial_Init();
}
void Serial_Init(void)
{
unsigned long ulWantedBaud = 9600;
USART_InitTypeDef USART_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
/* If the queue/semaphore was created correctly then setup the serial port
hardware. */
/* Enable USART1 clock */
RCC_APB2PeriphClockCmd( RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE );
/* Configure USART1 Rx (PA10) as input floating */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init( GPIOA, &GPIO_InitStructure );
/* Configure USART1 Tx (PA9) as alternate function push-pull */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init( GPIOA, &GPIO_InitStructure );
USART_InitStructure.USART_BaudRate = ulWantedBaud;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
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_InitStructure.USART_Clock = USART_Clock_Disable;
USART_InitStructure.USART_CPOL = USART_CPOL_Low;
USART_InitStructure.USART_CPHA = USART_CPHA_2Edge;
USART_InitStructure.USART_LastBit = USART_LastBit_Disable;
USART_Init( USART1, &USART_InitStructure );
USART_Cmd( USART1, ENABLE );
}
int fputc( int ch, FILE *f )
{
USART_TypeDef* USARTx = USART1;
while((USARTx->SR & (1<<7)) == 0);
USARTx->DR = ch;
return ch;
}
#ifdef DEBUG
/* Keep the linker happy. */
void assert_failed( unsigned char* pcFile, unsigned long ulLine )
{
for( ;; )
{
}
}
#endif
点击编译,发现如下两个报错
进入STM32F10x.s文件中,将第39行和第40行的代码删除,将第96行中的vTimer2IntHandler改为0,将第105行中的vUARTInterruptHandler改为0
进入vTaskStartScheduler函数中,在第1959行处,右键跳转到configSUPPORT_STATIC_ALLOCATION的定义处,把这个宏的值从0改为1
二、测试裁剪后的工程
点击编译
点击魔术棒→Debug→选择软件仿真
进入Debug模式,运行程序,观察串行窗口的数据,没有问题
三、更换库函数
FreeRTOS官方例程中的库函数版本有点旧了,所以我们更换库函数版本
复制v3.5.0版标准库中的core_cm3.c、core_cm3.h、stm32f10x.h、system_stm32f10x.c、system_stm32f10x.h、stm32f10x_conf.h、stm32f10x_it.c、stm32f10x_it.h到FreeRTOS工程中,将其添加到对应的组中,删除FreeRTOS\Demo\CORTEX_STM32F103_Keil目录下的stm32f10x_conf.h,不要忘记添加头文件路径
库函数下载链接如下:
删除原来的库函数文件,移除System组中原来的库函数文件,复制新版的标准库函数文件到FreeRTOS工程中,将其添加到System组中
点击魔术棒→C/C++→在Define中定义USE_STDPERIPH_DRIVER这个宏
稍微修改一下main.c文件中的内容
#include <stdio.h>
#include "stm32f10x.h" // Device header
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
static void prvSetupHardware( void );
void Serial_Init(void);
void Task1(void *param)
{
while(1)
{
printf("1");
}
}
void Task2(void *param)
{
while(1)
{
printf("2");
}
}
void Task3(void *param)
{
while(1)
{
printf("3");
}
}
StaticTask_t ppxIdleTaskTCB;
StackType_t ppxIdleTaskStack[100];
void vApplicationGetIdleTaskMemory( StaticTask_t ** ppxIdleTaskTCBBuffer,
StackType_t ** ppxIdleTaskStackBuffer,
uint32_t * pulIdleTaskStackSize )
{
*ppxIdleTaskTCBBuffer = &ppxIdleTaskTCB;
*ppxIdleTaskStackBuffer = ppxIdleTaskStack;
*pulIdleTaskStackSize = 100;
}
int main( void )
{
#ifdef DEBUG
debug();
#endif
prvSetupHardware();
xTaskCreate(Task1,"Task1",100,NULL,1,NULL);
xTaskCreate(Task2,"Task2",100,NULL,1,NULL);
xTaskCreate(Task3,"Task3",100,NULL,1,NULL);
vTaskStartScheduler();
return 0;
}
static void prvSetupHardware( void )
{
/* Start with the clocks in their expected state. */
RCC_DeInit();
/* Enable HSE (high speed external clock). */
RCC_HSEConfig( RCC_HSE_ON );
/* Wait till HSE is ready. */
while( RCC_GetFlagStatus( RCC_FLAG_HSERDY ) == RESET )
{
}
/* 2 wait states required on the flash. */
*( ( unsigned long * ) 0x40022000 ) = 0x02;
/* HCLK = SYSCLK */
RCC_HCLKConfig( RCC_SYSCLK_Div1 );
/* PCLK2 = HCLK */
RCC_PCLK2Config( RCC_HCLK_Div1 );
/* PCLK1 = HCLK/2 */
RCC_PCLK1Config( RCC_HCLK_Div2 );
/* PLLCLK = 8MHz * 9 = 72 MHz. */
RCC_PLLConfig( RCC_PLLSource_HSE_Div1, RCC_PLLMul_9 );
/* Enable PLL. */
RCC_PLLCmd( ENABLE );
/* Wait till PLL is ready. */
while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET)
{
}
/* Select PLL as system clock source. */
RCC_SYSCLKConfig( RCC_SYSCLKSource_PLLCLK );
/* Wait till PLL is used as system clock source. */
while( RCC_GetSYSCLKSource() != 0x08 )
{
}
/* Enable GPIOA, GPIOB, GPIOC, GPIOD, GPIOE and AFIO clocks */
RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB |RCC_APB2Periph_GPIOC
| RCC_APB2Periph_GPIOD | RCC_APB2Periph_GPIOE | RCC_APB2Periph_AFIO, ENABLE );
/* SPI2 Periph clock enable */
RCC_APB1PeriphClockCmd( RCC_APB1Periph_SPI2, ENABLE );
/* Set the Vector Table base address at 0x08000000 */
NVIC_SetVectorTable( NVIC_VectTab_FLASH, 0x0 );
NVIC_PriorityGroupConfig( NVIC_PriorityGroup_4 );
/* Configure HCLK clock as SysTick clock source. */
SysTick_CLKSourceConfig( SysTick_CLKSource_HCLK );
Serial_Init();
}
void Serial_Init(void)
{
unsigned long ulWantedBaud = 9600;
USART_InitTypeDef USART_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
/* If the queue/semaphore was created correctly then setup the serial port
hardware. */
/* Enable USART1 clock */
RCC_APB2PeriphClockCmd( RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE );
/* Configure USART1 Rx (PA10) as input floating */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init( GPIOA, &GPIO_InitStructure );
/* Configure USART1 Tx (PA9) as alternate function push-pull */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init( GPIOA, &GPIO_InitStructure );
USART_InitStructure.USART_BaudRate = ulWantedBaud;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
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 );
USART_Cmd( USART1, ENABLE );
}
int fputc( int ch, FILE *f )
{
USART_TypeDef* USARTx = USART1;
while((USARTx->SR & (1<<7)) == 0);
USARTx->DR = ch;
return ch;
}
#ifdef DEBUG
/* Keep the linker happy. */
void assert_failed( unsigned char* pcFile, unsigned long ulLine )
{
for( ;; )
{
}
}
#endif
四、测试更换库函数后的工程
编译工程
进入Debug模式,进行软件仿真,观察串行窗口中的数据
通过编译信息,我们发现代码的体积有些大,为了减小代码体积,点击魔术棒→C/C++,把优化开到最大(Level 3(-O3)),勾选One ELF Section per Function 和 C99 Mode
再次编译,这次代码体积减小了不少
官方例程中默认使用STM32F103VB,在把程序下载到开发板之前,最好把芯片配置改为对应的
编译后将程序下载到开发板中,现象如下:
最后顺带提醒一下,如果使用下载器下载程序,并且使用的不是ULINK,那么在下载时可能会出现如下错误
在Debug中修改配置还不行,还需要在Utilities中修改配置
FreeRTOS工程模板下载链接: