STM32CubeMX HAL库+STM32F407+uC/OS-III移植详细过程
前言
参考资料:
- 官网资料:https://micrium.atlassian.net/wiki/spaces/osiiidoc/overview
- 正点原子
STM32F4 UCOS开发手册_v3.0.pdf
- [野火]uCOS-III内核实现与应用开发实战指南https://doc.embedfire.com/rtos/ucos/zh/latest/application/porting_to_stm32.html
1. 获取uC/OS-III源码
获取源码可以通过官网下载:
https://weston-embedded.com/micrium-examples
同时可以在我的博客中下载(版本为3.04):
https://download.csdn.net/download/fhdghfuiahfsdifbs/18909773
源码目录如下:
2. 建立工程
使用STM32CubeMX自动生成,选择STM32F407VE芯片
配置时钟为168MHz
配置RCC、SYS
配置两个GPIO(LED)以及USART1,用于测试uC/OS-III移植效果
设置并生成工程代码
生成后的代码框架如下:
3. 裸机代码测试
移植uC/OS-III之前先确保裸机代码能够正常运行
在main函数的循环中添加如下代码,其目的是使两个LED灯闪烁
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_6|GPIO_PIN_7, GPIO_PIN_RESET);
HAL_Delay(500);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_6|GPIO_PIN_7, GPIO_PIN_SET);
HAL_Delay(500);
编译下载之后,能够看到LED闪烁,说明裸机程序运行没有问题,接下来开始移植uC/OS-III
4. 移植uC/OS-III
4.1 新建相关文件夹
源码目录如下:
首先在工程目录下新建一个文件夹,命名为UCOSIII,然后将uC-CPU、uC-LIB、uCOS-III三个文件夹复制到UCOSIII文件夹下,复制后的工程文件如下:
然后在UCOSIII文件夹下新建两个文件夹,分别为uCOS-CONFIG、uCOS-BSP,如下所示:
4.2 向uCOS-CONFIG中添加文件
将UCOSIII 3.04\Micrium\Software\EvalBoards\ST\STM32F429II-SK\uCOS-III
下的8个文件复制到uCOS-CONFIG中,如图所示:
4.3 向uCOS-BSP中添加文件
将UCOSIII 3.04\Micrium\Software\EvalBoards\ST\STM32F429II-SK\BSP
下的bsp.c和bsp.h复制到uCOS-BSP中,如图所示:
4.4 向工程中添加分组
4.5 向各个分组添加文件
注意:uCOS_LIB组当中,还需要添加\UCOSIII\uC-LIB\Ports\ARM-Cortex-M4\RealView
目录下的lib_mem_a.asm文件
向各个分组添加文件后,还需要添加相应的头文件路径,如图所示:
4.6 修改文件
4.6.1 修改bsp.h和bsp.c
将bsp.c文件修改为如下内容:
#ifndef __BSP_H__
#define __BSP_H__
#include "stm32f4xx_hal.h"
void BSP_Init(void);
#endif
将bsp.c文件修改为如下内容:
#include "includes.h"
#define BSP_REG_DEM_CR (*(CPU_REG32 *)0xE000EDFC) //DEMCR寄存器
#define BSP_REG_DWT_CR (*(CPU_REG32 *)0xE0001000) //DWT控制寄存器
#define BSP_REG_DWT_CYCCNT (*(CPU_REG32 *)0xE0001004) //DWT时钟计数寄存器
#define BSP_REG_DBGMCU_CR (*(CPU_REG32 *)0xE0042004)
//DEMCR寄存器的第24位,如果要使用DWT ETM ITM和TPIU的话DEMCR寄存器的第24位置1
#define BSP_BIT_DEM_CR_TRCENA DEF_BIT_24
//DWTCR寄存器的第0位,当为1的时候使能CYCCNT计数器,使用CYCCNT之前应当先初始化
#define BSP_BIT_DWT_CR_CYCCNTENA DEF_BIT_00
/*
*********************************************************************************************************
* BSP_CPU_ClkFreq()
* Description : Read CPU registers to determine the CPU clock frequency of the chip.
* Argument(s) : none.
* Return(s) : The CPU clock frequency, in Hz.
* Caller(s) : Application.
* Note(s) : none.
*********************************************************************************************************
*/
CPU_INT32U BSP_CPU_ClkFreq (void)
{
return HAL_RCC_GetHCLKFreq();//返回HCLK时钟频率
}
/*
*********************************************************************************************************
* BSP_Tick_Init()
* Description : BSP_Tick_Init.
* Argument(s) : none.
* Return(s) : none.
* Note(s) : none.
*********************************************************************************************************
*/
void BSP_Tick_Init(void)
{
CPU_INT32U cpu_clk_freq;
CPU_INT32U cnts;
cpu_clk_freq = BSP_CPU_ClkFreq();
#if(OS_VERSION>=3000u)
cnts = cpu_clk_freq/(CPU_INT32U)OSCfg_TickRate_Hz;
#else
cnts = cpu_clk_freq/(CPU_INT32U)OS_TICKS_PER_SEC;
#endif
OS_CPU_SysTickInit(cnts);
}
void BSP_Init(void)
{
BSP_Tick_Init();//此函数会初始化OS系统时钟,如果移植了正点原子的delay文件,则与主函数中的delay_init(168)只需要调用一个即可
}
#if (CPU_CFG_TS_TMR_EN == DEF_ENABLED)
void CPU_TS_TmrInit (void)
{
CPU_INT32U cpu_clk_freq_hz;
BSP_REG_DEM_CR |= (CPU_INT32U)BSP_BIT_DEM_CR_TRCENA; //使用DWT /* Enable Cortex-M4's DWT CYCCNT reg.*/
BSP_REG_DWT_CYCCNT = (CPU_INT32U)0u; //初始化CYCCNT寄存器
BSP_REG_DWT_CR |= (CPU_INT32U)BSP_BIT_DWT_CR_CYCCNTENA; //开启CYCCNT
cpu_clk_freq_hz = BSP_CPU_ClkFreq();
CPU_TS_TmrFreqSet(cpu_clk_freq_hz);
}
#endif
#if (CPU_CFG_TS_TMR_EN == DEF_ENABLED)
CPU_TS_TMR CPU_TS_TmrRd (void)
{
return ((CPU_TS_TMR)BSP_REG_DWT_CYCCNT);
}
#endif
#if (CPU_CFG_TS_32_EN == DEF_ENABLED)
CPU_INT64U CPU_TS32_to_uSec (CPU_TS32 ts_cnts)
{
CPU_INT64U ts_us;
CPU_INT64U fclk_freq;
fclk_freq = BSP_CPU_ClkFreq();
ts_us = ts_cnts / (fclk_freq / DEF_TIME_NBR_uS_PER_SEC);
return (ts_us);
}
#endif
#if (CPU_CFG_TS_64_EN == DEF_ENABLED)
CPU_INT64U CPU_TS64_to_uSec (CPU_TS64 ts_cnts)
{
CPU_INT64U ts_us;
CPU_INT64U fclk_freq;
fclk_freq = BSP_CPU_ClkFreq();
ts_us = ts_cnts / (fclk_freq / DEF_TIME_NBR_uS_PER_SEC);
return (ts_us);
}
#endif
4.6.2 修改startup_stm32f407xx.s
将
DCD PendSV_Handler ; PendSV Handler
DCD SysTick_Handler ; SysTick Handler
修改为
DCD OS_CPU_PendSVHandler ; PendSV Handler
DCD OS_CPU_SysTickHandler ; SysTick Handler
将
PendSV_Handler PROC
EXPORT PendSV_Handler [WEAK]
B .
ENDP
SysTick_Handler PROC
EXPORT SysTick_Handler [WEAK]
B .
ENDP
修改为:
OS_CPU_PendSVHandler PROC
EXPORT PendSV_Handler [WEAK]
B .
ENDP
OS_CPU_SysTickHandler PROC
EXPORT SysTick_Handler [WEAK]
B .
ENDP
修改后如图所示:
4.6.3 支持浮点运算
在startup_stm32f407xx.s的174行添加如下汇编代码:
IF {FPU} != "SoftVFP"
; Enable Floating Point Support at reset for FPU
LDR.W R0, =0xE000ED88 ; Load address of CPACR register
LDR R1, [R0] ; Read value at CPACR
ORR R1, R1, #(0xF <<20); Set bits 20-23 to enable CP10 and CP11 coprocessors
; Write back the modified CPACR value
STR R1, [R0] ; Wait for store to complete
DSB
; Disable automatic FP register content
; Disable lazy context switch
LDR.W R0, =0xE000EF34 ; Load address to FPCCR register
LDR R1, [R0]
AND R1, R1, #(0x3FFFFFFF) ; Clear the LSPEN and ASPEN bits
STR R1, [R0]
ISB ; Reset pipeline now the FPU is enabled
ENDIF
添加完成后如图所示:
同时需要支持浮点运算,默认是支持的
4.6.4 串口重定向
修改usart.h文件
#include <stdio.h>
#include <string.h>
添加后如图所示:
修改usart.c文件,添加如下代码:
#ifdef __GNUC__
/* With GCC/RAISONANCE, small printf (option LD Linker->Libraries->Small printf
set to 'Yes') calls __io_putchar() */
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif /* __GNUC__ */
/**
* @brief Retargets the C library printf function to the USART.
* @param None
* @retval None
*/
PUTCHAR_PROTOTYPE
{
/* Place your implementation of fputc here */
/* e.g. write a character to the EVAL_COM1 and Loop until the end of transmission */
HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xFFFF);
return ch;
}
添加后如图所示:
4.6.5 修改main.c
创建四个任务:
- 创建一个start_task任务,该任务用来系统初始化以及创建其他任务
- 创建一个led0_task任务,用来控制LED0的亮灭
- 创建一个led1_task任务,用来控制LED1的亮灭
- 创建一个float_task任务,用来测试浮点计算
任务 | 任务优先级 | 任务堆栈 | 备注 |
---|---|---|---|
start_task | 3 | 512 | |
led0_task | 4 | 128 | |
led1_task | 5 | 128 | |
float_task | 6 | 128 | 任务堆栈8字节对齐 |
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : main.c
* @brief : Main program body
******************************************************************************
* @attention
*
* <h2><center>© Copyright (c) 2021 STMicroelectronics.
* All rights reserved.</center></h2>
*
* This software component is licensed by ST under BSD 3-Clause license,
* the "License"; You may not use this file except in compliance with the
* License. You may obtain a copy of the License at:
* opensource.org/licenses/BSD-3-Clause
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "usart.h"
#include "gpio.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include <includes.h>
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* UCOSIII中以下优先级用户程序不能使用:
* 将这些优先级分配给了UCOSIII的5个系统内部任务
* 优先级0:中断服务服务管理任务 OS_IntQTask()
* 优先级1:时钟节拍任务 OS_TickTask()
* 优先级2:定时任务 OS_TmrTask()
* 优先级OS_CFG_PRIO_MAX-2:统计任务 OS_StatTask()
* 优先级OS_CFG_PRIO_MAX-1:空闲任务 OS_IdleTask()
*/
/* 任务优先级 */
#define START_TASK_PRIO 3
#define LED0_TASK_PRIO 4
#define LED1_TASK_PRIO 5
#define FLOAT_TASK_PRIO 6
/* 任务堆栈大小 */
#define START_STK_SIZE 512
#define LED0_STK_SIZE 128
#define LED1_STK_SIZE 128
#define FLOAT_STK_SIZE 128
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
/* 任务控制块 */
OS_TCB StartTaskTCB;
OS_TCB Led0TaskTCB;
OS_TCB Led1TaskTCB;
OS_TCB FloatTaskTCB;
/* 任务栈 */
CPU_STK START_TASK_STK[START_STK_SIZE];
CPU_STK LED0_TASK_STK[LED0_STK_SIZE];
CPU_STK LED1_TASK_STK[LED1_STK_SIZE];
__align(8) CPU_STK FLOAT_TASK_STK[FLOAT_STK_SIZE];
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
/* 任务函数定义 */
void start_task(void *p_arg);
void led0_task(void *p_arg);
void led1_task(void *p_arg);
void float_task(void *p_arg);
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
OS_ERR err;
CPU_SR_ALLOC();
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_USART1_UART_Init();
/* USER CODE BEGIN 2 */
//delay_init(168); //时钟初始化 如果移植了正点原子的delay文件且SYSTEM_SUPPORT_OS被定义,此函数会初始化OS系统时钟,与start_task任务中的BSP_Init()只需要调用一个即可
OSInit(&err); //初始化UCOSIII
OS_CRITICAL_ENTER();//进入临界区
//创建开始任务
OSTaskCreate((OS_TCB * )&StartTaskTCB, //任务控制块
(CPU_CHAR* )"start task", //任务名字
(OS_TASK_PTR)start_task, //任务函数
(void * )0, //传递给任务函数的参数
(OS_PRIO )START_TASK_PRIO, //任务优先级
(CPU_STK * )&START_TASK_STK[0], //任务堆栈基地址
(CPU_STK_SIZE)START_STK_SIZE/10, //任务堆栈深度限位
(CPU_STK_SIZE)START_STK_SIZE, //任务堆栈大小
(OS_MSG_QTY)0, //任务内部消息队列能够接收的最大消息数目,为0时禁止接收消息
(OS_TICK )0, //当使能时间片轮转时的时间片长度,为0时为默认长度,
(void * )0, //用户补充的存储区
(OS_OPT )OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR, //任务选项
(OS_ERR * )&err); //存放该函数错误时的返回值
OS_CRITICAL_EXIT(); //退出临界区
OSStart(&err); //启动多任务系统,控制权交给uC/OS-III
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
// HAL_GPIO_WritePin(GPIOA, GPIO_PIN_6|GPIO_PIN_7, GPIO_PIN_RESET);
// HAL_Delay(500);
// HAL_GPIO_WritePin(GPIOA, GPIO_PIN_6|GPIO_PIN_7, GPIO_PIN_SET);
// HAL_Delay(500);
}
/* USER CODE END 3 */
}
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** Configure the main internal regulator output voltage
*/
__HAL_RCC_PWR_CLK_ENABLE();
__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
/** Initializes the CPU, AHB and APB busses clocks
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLM = 8;
RCC_OscInitStruct.PLL.PLLN = 336;
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
RCC_OscInitStruct.PLL.PLLQ = 4;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB busses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5) != HAL_OK)
{
Error_Handler();
}
}
/* USER CODE BEGIN 4 */
//开始任务函数
void start_task(void *p_arg)
{
OS_ERR err;
CPU_SR_ALLOC();
p_arg = p_arg;
/* YangJie add 2021.05.20*/
BSP_Init(); /* Initialize BSP functions */
//CPU_Init();
//Mem_Init(); /* Initialize Memory Management Module */
#if OS_CFG_STAT_TASK_EN > 0u
OSStatTaskCPUUsageInit(&err); //统计任务
#endif
#ifdef CPU_CFG_INT_DIS_MEAS_EN //如果使能了测量中断关闭时间
CPU_IntDisMeasMaxCurReset();
#endif
#if OS_CFG_SCHED_ROUND_ROBIN_EN //当使用时间片轮转的时候
//使能时间片轮转调度功能,时间片长度为1个系统时钟节拍,既1*5=5ms
OSSchedRoundRobinCfg(DEF_ENABLED,1,&err);
#endif
OS_CRITICAL_ENTER(); //进入临界区
/* 创建LED0任务 */
OSTaskCreate((OS_TCB * )&Led0TaskTCB,
(CPU_CHAR * )"led0 task",
(OS_TASK_PTR )led0_task,
(void * )0,
(OS_PRIO )LED0_TASK_PRIO,
(CPU_STK * )&LED0_TASK_STK[0],
(CPU_STK_SIZE)LED0_STK_SIZE/10,
(CPU_STK_SIZE)LED0_STK_SIZE,
(OS_MSG_QTY )0,
(OS_TICK )0,
(void * )0,
(OS_OPT )OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR,
(OS_ERR * )&err);
/* 创建LED1任务 */
OSTaskCreate((OS_TCB * )&Led1TaskTCB,
(CPU_CHAR * )"led1 task",
(OS_TASK_PTR )led1_task,
(void * )0,
(OS_PRIO )LED1_TASK_PRIO,
(CPU_STK * )&LED1_TASK_STK[0],
(CPU_STK_SIZE)LED1_STK_SIZE/10,
(CPU_STK_SIZE)LED1_STK_SIZE,
(OS_MSG_QTY )0,
(OS_TICK )0,
(void * )0,
(OS_OPT )OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR,
(OS_ERR * )&err);
/* 创建浮点测试任务 */
OSTaskCreate((OS_TCB * )&FloatTaskTCB,
(CPU_CHAR * )"float test task",
(OS_TASK_PTR )float_task,
(void * )0,
(OS_PRIO )FLOAT_TASK_PRIO,
(CPU_STK * )&FLOAT_TASK_STK[0],
(CPU_STK_SIZE)FLOAT_STK_SIZE/10,
(CPU_STK_SIZE)FLOAT_STK_SIZE,
(OS_MSG_QTY )0,
(OS_TICK )0,
(void * )0,
(OS_OPT )OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR,
(OS_ERR * )&err);
OS_TaskSuspend((OS_TCB*)&StartTaskTCB,&err); //挂起开始任务
OS_CRITICAL_EXIT(); //进入临界区
}
/* led0任务函数 */
void led0_task(void *p_arg)
{
OS_ERR err;
p_arg = p_arg;
while(1)
{
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_6, GPIO_PIN_RESET);
OSTimeDly(500, OS_OPT_TIME_DLY, &err);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_6, GPIO_PIN_SET);
OSTimeDly(500, OS_OPT_TIME_DLY, &err);
}
}
/* led1任务函数 */
void led1_task(void *p_arg)
{
OS_ERR err;
p_arg = p_arg;
while(1)
{
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_7, GPIO_PIN_RESET);
OSTimeDly(500, OS_OPT_TIME_DLY, &err);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_7, GPIO_PIN_SET);
OSTimeDly(500, OS_OPT_TIME_DLY, &err);
}
}
/* 浮点测试任务 */
void float_task(void *p_arg)
{
OS_ERR err;
p_arg = p_arg;
CPU_SR_ALLOC();
static float float_num=0.01;
while(1)
{
float_num+=0.01f;
OS_CRITICAL_ENTER(); //进入临界区
printf("float_num的值为: %.4f\r\n",float_num);
OS_CRITICAL_EXIT(); //退出临界区
OSTimeDly(500, OS_OPT_TIME_DLY, &err);
}
}
/* USER CODE END 4 */
/**
* @brief Period elapsed callback in non blocking mode
* @note This function is called when TIM6 interrupt took place, inside
* HAL_TIM_IRQHandler(). It makes a direct call to HAL_IncTick() to increment
* a global variable "uwTick" used as application time base.
* @param htim : TIM handle
* @retval None
*/
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
/* USER CODE BEGIN Callback 0 */
/* USER CODE END Callback 0 */
if (htim->Instance == TIM6) {
HAL_IncTick();
}
/* USER CODE BEGIN Callback 1 */
/* USER CODE END Callback 1 */
}
/**
* @brief This function is executed in case of error occurrence.
* @retval None
*/
void Error_Handler(void)
{
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
/* USER CODE END Error_Handler_Debug */
}
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t *file, uint32_t line)
{
/* USER CODE BEGIN 6 */
/* User can add his own implementation to report the file name and line number,
tex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
5. 编译下载
快捷键F7编译,F8下载程序到开发板
6. 实验结果
编译下载后,可以看到开发板两个LED灯闪烁,串口打印浮点型数据
在浮点计算处设置断点,全速运行,此时查看汇编窗口,如图所示:
可以看到有VLDR、VADD.F32等FPU指令,这说明使用STM32F407内部的FPU进行浮点运算成功
总结:
该移植过程主要修改的文件包括如下:
bsp.c和bsp.h
startup_stm32f407xx.s
main.c
usart.c和usart.h