STM32 Customer BootLoader 刷新项目 (三) 程序框架搭建
文章目录
- STM32 Customer BootLoader 刷新项目 (三) 程序框架搭建
- 典型工作流程
- 1. 硬件原理图介绍
- 1.1 USART硬件介绍
- 1.2 LED和按键介绍
- 2. STM32 CubeMX工程搭建
- 2.1 创建工程
- 2.2 系统配置
- 2.3 USART串口配置
- 2.4 配置按键GPIO和LED灯
- 2.5 配置CRC
- 2.6 时钟树配置
- 2.5 工程导出设置
- 3. 代码编写
- 4. 工程下载、调试和演示
首先用STM32CubeMX 软件搭建基础工程,来作为二级BootLoader,一级BootLoader是STM32官方自带的startup_stm32f407zgtx.s。我们基于上述最小工程来实现Customer BootLoader的功能。本项目采用的是通过串口实现固件刷新。
下面简单介绍一下二级BootLoder的功能与作用:
二级Customer BootLoader(CBL,Customer BootLoader)是一种在嵌入式系统中常见的软件组件。它主要负责在系统启动时执行初始引导操作,加载和运行应用程序代码。二级Customer BootLoader与一级BootLoader(通常称为Primary BootLoader, PBL)一起工作,提供了更灵活和复杂的引导机制。
典型工作流程
- 系统加电后,一级BootLoader(PBL)启动:
- 负责基本硬件初始化(如设置堆栈指针、初始化RAM等)。
- 加载并执行二级BootLoader(CBL)。
- 二级BootLoader启动:
- 执行更详细的硬件初始化。
- 验证固件的完整性和合法性。
- 根据系统配置和状态,选择合适的固件或操作系统进行引导。
- 加载并启动应用程序或操作系统。
目前本项目的Customer BootLoader具备:
- 获取软件版本;
- 读芯片Chip ID;
- 获取Flash Read Protection等级;
- 擦除指定Flash Sector;
- 更新指定Flash Sector内容;
- 使能读/写保护;
下面开始我们本章内容的工程搭建,其中部分图借用洋桃电子杜老师的STM F4系列的课程内容。
1. 硬件原理图介绍
1.1 USART硬件介绍
本项目采用正点原子探索者v2开发板,选用其中的左下角的USB串口进行和上位机之间的串口通信。
正点原子STM32F4 探索者V2开发板,如下图所示,通过短接PA9-RXD,短接PA10-TXD,即将USART1与CH340芯片连接在一起,串口USART1与上位机可通过USB进行通信。
如下图电路所示,使用一根MicroUSB结构的USB数据线,一端连接计算机的USB口,一端连接开发版左下角的USB_232口上,就可以在计算机上虚拟出一个串口,通过这个虚拟串口可以进行计算机与开发板之间的串口通信。
1.2 LED和按键介绍
从正点原子的原理图中,选择4个按键,2个LED灯作为本次实验的对象,用1个按键来控制是否进入Customer BootLoader。用2个LED灯的亮灭来显示进入到不同模式的情况。
下图所示是1个按键KEY_up和两个LED灯对应的原理图,及相应的配置功能
名称 | 端口 | 引脚功能 | 特性 | 初始电平 |
---|---|---|---|---|
KEY_UP | PA0 | Input mode | Pull-down 下拉 | N/A |
LED1 | PF9 | Output | Pushpull推挽输出, | 初始低电平 |
LED2 | PF10 | Output | Pushpull推挽输出, | 初始低电平 |
2. STM32 CubeMX工程搭建
2.1 创建工程
打开STM32CubdeMX,点击New Project创建新工程
选择 STM32F407 ZGT6
2.2 系统配置
点击左侧System Core,选择RCC,将HSE和LSE都设置为Crystal/Ceramic Resonator(晶体/陶瓷振荡器)
点击SYS,选择Debug功能为JTAG(5 pins),跟板子调试口对应
2.3 USART串口配置
下图是CB的架构图,整个刷新和操作MCU都是通过USART来操作,其中USART1是主要和MCU进行通信、刷新和发送命令的串口,而USART2是Debug 端口,只在调试的时候使用用来输出打印信息,开发阶段完成后,USART2则不再使用。
首先,先配置USART1,选择左侧的Connecttivity选项,点击USART1,如下图所示,点击Mode开始配置
STM32对USART模块提供了下面的这些模式,根据需求选择相应模式,本项目选择的是异步模式Asynchronous。
下面我们来对USART进行配置,首先开发板上的串口对应的USART1串口,Mode配置为异步模式Asynchronous,STMCubeMX会自动分配引脚,目前分配的USART1_RX对于PA10,USART1_TX对于PA9,和我们开发板的引脚正好对应,如果不对应的话,可以根据芯片的data Sheet改成相应的引脚。
下面的参数配置Parameter Settings按照默认配置来,波特率为 115200 bit/s,这里确保主从机是一致的,才能通信成功,数据位 8,无校验位,停止位1,数据方向:Receive and Transmit,采样:16.
点击下方的GPIO Settings,可以看到为USART1自动分配的默认引脚
配置USART2,将其配置为异步通信。
2.4 配置按键GPIO和LED灯
按照1.2节的硬件说明,进行GPIO和LED灯配置,配置内容如下。
2.5 配置CRC
由于在上位机和单片机传输中,需要对发送的数据进行CRC校验,所以需要将CRC功能激活。激活方法如下
2.6 时钟树配置
点击上方的Clock Configuration,开始配置时钟
下面我们来看一下时钟树的结构,如下图所示
现在开始配置开发板相关的时钟频率,首先选择做左边的Input frequency,选择外部8M的晶振,选择HSE,选择PLLCLK,在HCLK处将时钟敲定为168MHz,即STM32F407可支持的最大时钟频率
2.5 工程导出设置
如下图所示,设置工程
代码生成设置
高级设置Advanced Settings
点击右上角,生成代码GENERATE CODE
点击Open Project,本项目是使用STM32CubeIDE作为集成开发环境,做到编译和调试代码的工具
3. 代码编写
我们目前使用的是Hal库进行的工程实现,如下图所示,是串口轮询发送函数HAL_UART_Transmit(),在发送的过程中,会一直在该函数中进行发送,是Polling Mode。
下图是串口接收函数 HAL_UART_Receive(),也是Polling Mode,在接收数据的过程中,CPU无法被抢占,一直需要等到数据被发送完成后才可退出该函数
下面是在main.c中的代码实现:
引用c标准头文件
宏定义,BL_DEBUG_MSG_EN是为调试用的,重定义huart1,设置数据bl_rx_buffer
在main()函数中调用bootloader_uart_read_data()函数进行数据接收和发送
bootloader_uart_read_data()函数中先接收在发送。来判断上位机发送来的命令类型,在执行相应的操作
void bootloader_uart_read_data(void)
{uint8_t rcv_len=0;printmsg_Host("BL_DEBUG_MSG: Receive CMD\n\r");while (1){HAL_GPIO_WritePin(LED2_GPIO_Port, LED2_Pin, GPIO_PIN_RESET);memset(bl_rx_buffer, 0, 200);//here we will read and decode the commands coming from host//first read only one byte from the host , which is the "length" field of the command packetHAL_UART_Receive(C_UART,bl_rx_buffer,1,HAL_MAX_DELAY);rcv_len= bl_rx_buffer[0];HAL_UART_Receive(C_UART,&bl_rx_buffer[1],rcv_len,HAL_MAX_DELAY);switch(bl_rx_buffer[1]){case BL_GET_VER:bootloader_handle_getver_cmd(bl_rx_buffer);break;case BL_GET_HELP:bootloader_handle_gethelp_cmd(bl_rx_buffer);break;case BL_GET_CID:bootloader_handle_getcid_cmd(bl_rx_buffer);break;case BL_GET_RDP_STATUS:bootloader_handle_getrdp_cmd(bl_rx_buffer);break;case BL_GO_TO_ADDR:bootloader_handle_go_cmd(bl_rx_buffer);break;case BL_FLASH_ERASE:bootloader_handle_flash_erase_cmd(bl_rx_buffer);break;case BL_MEM_WRITE:bootloader_handle_mem_write_cmd(bl_rx_buffer);break;case BL_EN_RW_PROTECT:bootloader_handle_en_rw_protect(bl_rx_buffer);break;case BL_MEM_READ:bootloader_handle_mem_read(bl_rx_buffer);break;case BL_READ_SECTOR_P_STATUS:bootloader_handle_read_sector_protection_status(bl_rx_buffer);break;case BL_OTP_READ:bootloader_handle_read_otp(bl_rx_buffer);break;case BL_DIS_R_W_PROTECT:bootloader_handle_dis_rw_protect(bl_rx_buffer);break;default:printmsg("BL_DEBUG_MSG:Invalid command code received from host \n");break;}}}
如果没有按键按下,则跳转到APP程序,进行执行应用层程序。
/*code to jump to user application*Here we are assuming FLASH_SECTOR2_BASE_ADDRESS*is where the user application is stored*/
void bootloader_jump_to_user_app(void)
{//just a function pointer to hold the address of the reset handler of the user app.void (*app_reset_handler)(void);printmsg("BL_DEBUG_MSG:bootloader_jump_to_user_app\n");// 1. configure the MSP by reading the value from the base address of the sector 2uint32_t msp_value = *(volatile uint32_t *)FLASH_SECTOR2_BASE_ADDRESS;printmsg("BL_DEBUG_MSG:MSP value : %#x\n",msp_value);//This function comes from CMSIS.__set_MSP(msp_value);//SCB->VTOR = FLASH_SECTOR1_BASE_ADDRESS;/* 2. Now fetch the reset handler address of the user application* from the location FLASH_SECTOR2_BASE_ADDRESS+4*/uint32_t resethandler_address = *(volatile uint32_t *) (FLASH_SECTOR2_BASE_ADDRESS + 4);app_reset_handler = (void*) resethandler_address;printmsg("BL_DEBUG_MSG: app reset handler addr : %#x\n",app_reset_handler);//3. jump to reset handler of the user applicationapp_reset_handler();}
下面是printmsg()打印数据函数实现。
/* prints formatted string to console over UART */
void printmsg(char *format,...)
{
#ifdef BL_DEBUG_MSG_ENchar str[80];/*Extract the the argument list using VA apis */va_list args;va_start(args, format);vsprintf(str, format,args);HAL_UART_Transmit(D_UART,(uint8_t *)str, strlen(str),HAL_MAX_DELAY);va_end(args);
#endif
}
下面是mainh函数中的完整代码:
int main(void)
{/* USER CODE BEGIN 1 *//* 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();MX_USART2_UART_Init();MX_CRC_Init();/* USER CODE BEGIN 2 *//* USER CODE END 2 *//* Infinite loop *//* USER CODE BEGIN WHILE */while (1){/* Lets check whether button is pressed or not, if not pressed jump to user application */if ( HAL_GPIO_ReadPin(KeyUp_GPIO_Port, KeyUp_Pin) == GPIO_PIN_SET ){printmsg("BL_DEBUG_MSG:Button is pressed .. going to BL mode\n\r");/* Use Usart to Read PC transmit CMD */bootloader_uart_read_data();}else{HAL_GPIO_TogglePin(LED1_GPIO_Port, LED1_Pin);printmsg("BL_DEBUG_MSG:Button is not pressed .. executing user app\n");//jump to user applicationbootloader_jump_to_user_app();}/* USER CODE END WHILE *//* USER CODE BEGIN 3 */}/* USER CODE END 3 */
}
4. 工程下载、调试和演示
将工程编译,之后下载到板子中。
打开设备管理器,查看串口的端口号COM
打开正点原子的串口调试助手,选择刚才设备管理器中串口的COM号,波特率设置为115200,其他默认,打开串口,随便发送一个数据,开发板接收到这个数据,都会原封不动的将该数据打印出来,然后在执行打印Going to BL mode,说明能够接收并发送数据,至此,我们开发Customer BootLoader的第一步,最小工程代码已经搭建完成,后续我们进行上位机与开发板之间的通信协议开发。
一开始程序先跳转至APP层。
复位开发板,下面上电按下KeyUP按键,进入到Customer BootLoader中。
下面打开上位机,连接上位机和开发板连接的串口,根据显示的指令,进行相应的操作。
下面我们展示一下读取版本号的功能,输入1,获取版本号为0x10.
下面我们在试一个刷新命令。下面是录制的一整个执行的流程。
BootLoader串口刷新
如果大家有什么疑问,请随时私信联系我。