linux+makefile向STM32移植FreeRTOS
前期准备:
- ubuntu或者虚拟机
- stm32f103c8t6最小系统板
- FreeRTOS官方文件
- stm32f103c8t6的开发环境
- stm32官方固件库
FreeRTOS的准备和修改:
FreeRTOS的源文件从官方网站下载就行。
1、解压到ubuntu里之后进入FreeRTOS/Source/portable发现里面有一大堆文件,阅读readme之后发现这些文件是根据各种不同编译器而准备的不同内核文件,由于我们是在linux上用GCC来编译,所以要保留GCC文件,另外还有MemMang文件(此文件是通用的内存管理文件),protable目录下的其他文件就可以随缘删掉了。
2、进入GCC目录,里面是不同芯片内核的驱动,读者可根据 自己的芯片内核选择相应的驱动,我使用的是stm32f103c8t6,内核是Cortex-M3所以保留ARM_CM3,其他就可以删掉了。
3、进入FreeRTOS/Demo/CORTEX_STM32F103_Primer_GCC目录,拷贝FreeRTOSConfig.h文件到工程目录的user文件里。
建立工程文件:
创建一个文件目录stm32+freertos,将FreeRTOS源文件、stm32官方固件库,创建一个user文件夹用以存放自定义的头文件和.c文件,创建一个Startup目录用以存放启动文件。将STM32F10x_StdPeriph_Lib_V3.5.0/Libraries/CMSIS/CM3/DeviceSupport/ST/STM32F10x/startup/arm目录下的startup_stm32f10x_md.s文件复制进Startup。
编写main.c
在这里我创建两个LED闪烁任务,分别是PA2和PA3独立闪烁,其中关于任务调度和创建的函数可以参考FreeRTOS开发手册。
1、main.c:
#include "stm32f10x.h"
#include "led.h"
#include "FreeRTOS.h"
void task_1(void){
while(1){
LED1_OFF;
vTaskDelay(500);
LED1_ON;
vTaskDelay(500);
}
}
void task_2(void){
while(1){
LED2_ON;
vTaskDelay(500);
LED2_OFF;
vTaskDelay(500);
}
}
int main(void)
{
//NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
LED_GPIO_Config();
xTaskCreate(task_1, "task1", 10, NULL, 1, NULL);//创建任务1的函数,让PA2接口的LED灯闪烁
xTaskCreate(task_2, "task2", 10, NULL, 1, NULL);//创建任务2的函数,让PA3接口的LED灯闪烁
vTaskStartScheduler();//任务调度函数
while(1);
}
2、led.h:
#ifndef __LED_H
#define __LED_H
#include "stm32f10x.h"
/* 定义LED连接的GPIO端口, 用户只需要修改下面的代码即可改变控制的LED引脚 */
#define LED1_GPIO_PORT GPIOA /* GPIO端口 */
#define LED1_GPIO_CLK RCC_APB2Periph_GPIOA /* GPIO端口时钟 */
#define LED1_GPIO_PIN GPIO_Pin_2
#define LED2_GPIO_PORT GPIOA /* GPIO端口 */
#define LED2_GPIO_CLK RCC_APB2Periph_GPIOA /* GPIO端口时钟 */
#define LED2_GPIO_PIN GPIO_Pin_3
/** the macro definition to trigger the led on or off
* 1 - off
*0 - on
*/
#define ON 0
#define OFF 1
/* 使用标准的固件库控制IO*/
#define LED1(a) if (a) \
GPIO_SetBits(LED1_GPIO_PORT,LED1_GPIO_PIN);\
else \
GPIO_ResetBits(LED1_GPIO_PORT,LED1_GPIO_PIN)
#define LED2(a) if (a) \
GPIO_SetBits(LED2_GPIO_PORT,LED2_GPIO_PIN);\
else \
GPIO_ResetBits(LED2_GPIO_PORT,LED2_GPIO_PIN)
/* 直接操作寄存器的方法控制IO */
#define digitalHi(p,i) {p->BSRR=i;} //输出为高电平
#define digitalLo(p,i) {p->BRR=i;} //输出低电平
#define digitalToggle(p,i) {p->ODR ^=i;} //输出反转状态
/* 定义控制IO的宏 */
#define LED1_TOGGLE digitalToggle(LED1_GPIO_PORT,LED1_GPIO_PIN)
#define LED1_OFF digitalHi(LED1_GPIO_PORT,LED1_GPIO_PIN)
#define LED1_ON digitalLo(LED1_GPIO_PORT,LED1_GPIO_PIN)
#define LED2_TOGGLE digitalToggle(LED2_GPIO_PORT,LED2_GPIO_PIN)
#define LED2_OFF digitalHi(LED2_GPIO_PORT,LED2_GPIO_PIN)
#define LED2_ON digitalLo(LED2_GPIO_PORT,LED2_GPIO_PIN)
void LED_GPIO_Config(void);
#endif /* __LED_H */
3、led.c:
#include "bsp_led.h"
/**
* @brief 初始化控制LED的IO
* @param 无
* @retval 无
*/
void LED_GPIO_Config(void)
{
/*定义一个GPIO_InitTypeDef类型的结构体*/
GPIO_InitTypeDef GPIO_InitStructure;
/*开启LED相关的GPIO外设时钟*/
RCC_APB2PeriphClockCmd( LED1_GPIO_CLK | LED2_GPIO_CLK , ENABLE);
/*选择要控制的GPIO引脚*/
GPIO_InitStructure.GPIO_Pin = LED1_GPIO_PIN;
/*设置引脚模式为通用推挽输出*/
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
/*设置引脚速率为50MHz */
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
/*调用库函数,初始化GPIO*/
GPIO_Init(LED1_GPIO_PORT, &GPIO_InitStructure);
/*选择要控制的GPIO引脚*/
GPIO_InitStructure.GPIO_Pin = LED2_GPIO_PIN;
/*调用库函数,初始化GPIO*/
GPIO_Init(LED2_GPIO_PORT, &GPIO_InitStructure);
/* 关闭所有led灯 */
GPIO_SetBits(LED1_GPIO_PORT, LED1_GPIO_PIN);
/* 关闭所有led灯 */
GPIO_SetBits(LED2_GPIO_PORT, LED2_GPIO_PIN);
}
/*********************************************END OF FILE**********************/
Makefile的编写:
推荐这篇文章学习Makefile的语法:Makefile学习教程: 跟我一起写 Makefile
下面是一篇比较易懂的例子, 也可以作为此次工程的Makefile。
# GCC路径
GCC_PATH = /usr/bin
# 目标名字
TARGET = STM32F103C8T6
# 输出文件夹
BUILD_DIR = build
# C源 用到的库什么的都写在这
C_SOURCES = \
User/main.c \
User/led.c \
STM32F10x_StdPeriph_Lib_V3.5.0/Libraries/STM32F10x_StdPeriph_Driver/src/stm32f10x_gpio.c \
STM32F10x_StdPeriph_Lib_V3.5.0/Libraries/STM32F10x_StdPeriph_Driver/src/stm32f10x_rcc.c \
STM32F10x_StdPeriph_Lib_V3.5.0/Libraries/CMSIS/CM3/CoreSupport/core_cm3.c \
STM32F10x_StdPeriph_Lib_V3.5.0/Libraries/CMSIS/CM3/DeviceSupport/ST/STM32F10x/system_stm32f10x.c \
FreeRTOS/Source/tasks.c \
FreeRTOS/Source/list.c \
FreeRTOS/Source/queue.c \
FreeRTOS/Source/timers.c \
FreeRTOS/Source/portable/GCC/ARM_CM3/port.c \
FreeRTOS/Source/portable/MemMang/heap_4.c \
##Libraries/src/stm32f10x_usart.c \
##User/bsp_usart.c \
##Libraries/src/misc.c \
##User/stm32f10x_it.c
##FreeRTOS/Source/portable/IAR/ARM_CM3/portasm.s \
######################################
# ASM sources
ASM_SOURCES = \
Startup/startup_stm32f10x_md.s
######################################
# building variables
######################################
# debug build?
DEBUG = 1
# optimization
OPT = -Og
#######################################
# binaries
#######################################
PREFIX = arm-none-eabi-
CC = $(GCC_PATH)/$(PREFIX)gcc
AS = $(GCC_PATH)/$(PREFIX)gcc -x assembler-with-cpp
CP = $(GCC_PATH)/$(PREFIX)objcopy
SZ = $(GCC_PATH)/$(PREFIX)size
HEX = $(CP) -O ihex
BIN = $(CP) -O binary -S
#######################################
# CFLAGS
#######################################
# cpu
CPU = -mcpu=cortex-m3
# fpu
# NONE for Cortex-M0/M0+/M3
# float-abi
# mcu
MCU = $(CPU) -mthumb $(FPU) $(FLOAT-ABI)
# macros for gcc
# AS defines
AS_DEFS =
# C defines
C_DEFS = \
-DUSE_STDPERIPH_DRIVER \
-DSTM32F10X_MD
# AS includes
AS_INCLUDES = \
-IFreeRTOS\Source
# C includes
C_INCLUDES = \
-ISTM32F10x_StdPeriph_Lib_V3.5.0/Libraries/CMSIS/CM3/CoreSupport \
-ISTM32F10x_StdPeriph_Lib_V3.5.0/Libraries/CMSIS/CM3/DeviceSupport/ST/STM32F10x \
-ISTM32F10x_StdPeriph_Lib_V3.5.0/Libraries/inc \
-ISTM32F10x_StdPeriph_Lib_V3.5.0/Libraries/src \
-IStartup \
-IUser \
-IFreeRTOS/Source/include \
-IFreeRTOS/Source/portable/GCC/ARM_CM3 \
-IFreeRTOS/Source
# compile gcc flags
ASFLAGS = $(MCU) $(AS_DEFS) $(AS_INCLUDES) $(OPT) -Wall -fdata-sections -ffunction-sections
CFLAGS = $(MCU) $(C_DEFS) $(C_INCLUDES) $(OPT) -Wall -fdata-sections -ffunction-sections
ifeq ($(DEBUG), 1)
CFLAGS += -g -gdwarf-2
endif
# Generate dependency information
CFLAGS += -MMD -MP -MF"$(@:%.o=%.d)"
#######################################
# LDFLAGS
#######################################
# link script
LDSCRIPT = STM32F103C8Tx_FLASH.ld
# libraries
LIBS = -lc -lm -lnosys
LIBDIR =
LDFLAGS = $(MCU) -specs=nano.specs -T$(LDSCRIPT) $(LIBDIR) $(LIBS) -Wl,-Map=$(BUILD_DIR)/$(TARGET).map,--cref -Wl,--gc-sections
# default action: build all
all: $(BUILD_DIR)/$(TARGET).elf $(BUILD_DIR)/$(TARGET).hex $(BUILD_DIR)/$(TARGET).bin
#######################################
# build the application
#######################################
# list of objects
OBJECTS = $(addprefix $(BUILD_DIR)/,$(notdir $(C_SOURCES:.c=.o)))
vpath %.c $(sort $(dir $(C_SOURCES)))
# list of ASM program objects
OBJECTS += $(addprefix $(BUILD_DIR)/,$(notdir $(ASM_SOURCES:.s=.o)))
vpath %.s $(sort $(dir $(ASM_SOURCES)))
$(BUILD_DIR)/%.o: %.c Makefile | $(BUILD_DIR)
$(CC) -c $(CFLAGS) -Wa,-a,-ad,-alms=$(BUILD_DIR)/$(notdir $(<:.c=.lst)) $< -o $@
$(BUILD_DIR)/%.o: %.s Makefile | $(BUILD_DIR)
$(AS) -c $(CFLAGS) $< -o $@
$(BUILD_DIR)/$(TARGET).elf: $(OBJECTS) Makefile
$(CC) $(OBJECTS) $(LDFLAGS) -o $@
$(SZ) $@
$(BUILD_DIR)/%.hex: $(BUILD_DIR)/%.elf | $(BUILD_DIR)
$(HEX) $< $@
$(BUILD_DIR)/%.bin: $(BUILD_DIR)/%.elf | $(BUILD_DIR)
$(BIN) $< $@
$(BUILD_DIR):
mkdir $@
#######################################
# clean up
#######################################
clean:
-rm -fR $(BUILD_DIR)
#######################################
# dependencies
#######################################
-include $(wildcard $(BUILD_DIR)/*.d)
# *** EOF ***
在工程目录下创建一个build.sh文件:
make clean
make all
st-flash write build/STM32F103C8T6.bin 0x08000000
在工程目录下创建一个STM32F103C8Tx_FLASH.ld文件:
/* Entry Point */
ENTRY(Reset_Handler)
/* Highest address of the user mode stack */
_estack = 0x20005000; /* end of RAM */
/* Generate a link error if heap and stack don't fit into RAM */
_Min_Heap_Size = 0x200; /* required amount of heap */
_Min_Stack_Size = 0x400; /* required amount of stack */
/* Specify the memory areas */
MEMORY
{
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 20K//将可执行代码、宏定义的变量和可读写变量放入RAM//
//RAM的起始地址和大小。后期查看elf文件的布局时参考。
FLASH (rx) : ORIGIN = 0x8000000, LENGTH = 64K//宏定义的变量和可读写变量放入flash//
//flash的起始地址和大小。后期查看elf文件的布局时参考。
}
/* Define output sections */
SECTIONS
{
/* The startup code goes first into FLASH */
.isr_vector :
{
. = ALIGN(4);
KEEP(*(.isr_vector)) /* Startup code */
. = ALIGN(4);
} >FLASH
/* The program code and other data goes into FLASH */
.text :
{
. = ALIGN(4);
*(.text) /* .text sections (code) */
*(.text*) /* .text* sections (code) */
*(.glue_7) /* glue arm to thumb code */
*(.glue_7t) /* glue thumb to arm code */
*(.eh_frame)
KEEP (*(.init))
KEEP (*(.fini))
. = ALIGN(4);
_etext = .; /* define a global symbols at end of code */
} >FLASH
/* Constant data goes into FLASH */
.rodata :
{
. = ALIGN(4);
*(.rodata) /* .rodata sections (constants, strings, etc.) */
*(.rodata*) /* .rodata* sections (constants, strings, etc.) */
. = ALIGN(4);
} >FLASH
.ARM.extab : { *(.ARM.extab* .gnu.linkonce.armextab.*) } >FLASH
.ARM : {
__exidx_start = .;
*(.ARM.exidx*)
__exidx_end = .;
} >FLASH
.preinit_array :
{
PROVIDE_HIDDEN (__preinit_array_start = .);
KEEP (*(.preinit_array*))
PROVIDE_HIDDEN (__preinit_array_end = .);
} >FLASH
.init_array :
{
PROVIDE_HIDDEN (__init_array_start = .);
KEEP (*(SORT(.init_array.*)))
KEEP (*(.init_array*))
PROVIDE_HIDDEN (__init_array_end = .);
} >FLASH
.fini_array :
{
PROVIDE_HIDDEN (__fini_array_start = .);
KEEP (*(SORT(.fini_array.*)))
KEEP (*(.fini_array*))
PROVIDE_HIDDEN (__fini_array_end = .);
} >FLASH
/* used by the startup to initialize data */
_sidata = LOADADDR(.data);
/* Initialized data sections goes into RAM, load LMA copy after code */
.data :
{
. = ALIGN(4);
_sdata = .; /* create a global symbol at data start */
*(.data) /* .data sections */
*(.data*) /* .data* sections */
. = ALIGN(4);
_edata = .; /* define a global symbol at data end */
} >RAM AT> FLASH
/* Uninitialized data section */
. = ALIGN(4);
.bss :
{
/* This is used by the startup in order to initialize the .bss secion */
_sbss = .; /* define a global symbol at bss start */
__bss_start__ = _sbss;
*(.bss)
*(.bss*)
*(COMMON)
. = ALIGN(4);
_ebss = .; /* define a global symbol at bss end */
__bss_end__ = _ebss;
} >RAM
/* User_heap_stack section, used to check that there is enough RAM left */
._user_heap_stack :
{
. = ALIGN(8);
PROVIDE ( end = . );
PROVIDE ( _end = . );
. = . + _Min_Heap_Size;
. = . + _Min_Stack_Size;
. = ALIGN(8);
} >RAM
/* Remove information from the standard libraries */
/DISCARD/ :
{
libc.a ( * )
libm.a ( * )
libgcc.a ( * )
}
.ARM.attributes 0 : { *(.ARM.attributes) }
}
编译和链接:
进入你所创建的工程目录:
cd stm32-freertos
直接运行build.sh文件:
./build.sh