STM32 BootLoader 刷新项目 (三) 程序框架搭建及刷新演示

news2025/1/16 2:55:14

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的功能。本项目采用的是通过串口实现固件刷新。

image-20240622200254124

下面简单介绍一下二级BootLoder的功能与作用:

二级Customer BootLoader(CBL,Customer BootLoader)是一种在嵌入式系统中常见的软件组件。它主要负责在系统启动时执行初始引导操作,加载和运行应用程序代码。二级Customer BootLoader与一级BootLoader(通常称为Primary BootLoader, PBL)一起工作,提供了更灵活和复杂的引导机制。

典型工作流程

  1. 系统加电后,一级BootLoader(PBL)启动
    • 负责基本硬件初始化(如设置堆栈指针、初始化RAM等)。
    • 加载并执行二级BootLoader(CBL)。
  2. 二级BootLoader启动
    • 执行更详细的硬件初始化。
    • 验证固件的完整性和合法性。
    • 根据系统配置和状态,选择合适的固件或操作系统进行引导。
    • 加载并启动应用程序或操作系统。

目前本项目的Customer BootLoader具备:

  1. 获取软件版本;
  2. 读芯片Chip ID;
  3. 获取Flash Read Protection等级;
  4. 擦除指定Flash Sector;
  5. 更新指定Flash Sector内容;
  6. 使能读/写保护;

下面开始我们本章内容的工程搭建,其中部分图借用洋桃电子杜老师的STM F4系列的课程内容。

1. 硬件原理图介绍

1.1 USART硬件介绍

本项目采用正点原子探索者v2开发板,选用其中的左下角的USB串口进行和上位机之间的串口通信。

image-20240608085309250

正点原子STM32F4 探索者V2开发板,如下图所示,通过短接PA9-RXD,短接PA10-TXD,即将USART1与CH340芯片连接在一起,串口USART1与上位机可通过USB进行通信。

image-20240601085106572

如下图电路所示,使用一根MicroUSB结构的USB数据线,一端连接计算机的USB口,一端连接开发版左下角的USB_232口上,就可以在计算机上虚拟出一个串口,通过这个虚拟串口可以进行计算机与开发板之间的串口通信。

image-20240601085723436

image-20240601185940740

1.2 LED和按键介绍

从正点原子的原理图中,选择4个按键,2个LED灯作为本次实验的对象,用1个按键来控制是否进入Customer BootLoader。用2个LED灯的亮灭来显示进入到不同模式的情况。

image-20231214223415136

下图所示是1个按键KEY_up和两个LED灯对应的原理图,及相应的配置功能

名称端口引脚功能特性初始电平
KEY_UPPA0Input modePull-down 下拉N/A
LED1PF9OutputPushpull推挽输出,初始低电平
LED2PF10OutputPushpull推挽输出,初始低电平

2. STM32 CubeMX工程搭建

2.1 创建工程

打开STM32CubdeMX,点击New Project创建新工程

image-20240519214220349

选择 STM32F407 ZGT6

image-20240520070646930

2.2 系统配置

点击左侧System Core,选择RCC,将HSE和LSE都设置为Crystal/Ceramic Resonator(晶体/陶瓷振荡器)

image-20240604074221880

点击SYS,选择Debug功能为JTAG(5 pins),跟板子调试口对应image-20240604075633710

2.3 USART串口配置

下图是CB的架构图,整个刷新和操作MCU都是通过USART来操作,其中USART1是主要和MCU进行通信、刷新和发送命令的串口,而USART2是Debug 端口,只在调试的时候使用用来输出打印信息,开发阶段完成后,USART2则不再使用。image-20240621072252557

首先,先配置USART1,选择左侧的Connecttivity选项,点击USART1,如下图所示,点击Mode开始配置

image-20240604080347571

STM32对USART模块提供了下面的这些模式,根据需求选择相应模式,本项目选择的是异步模式Asynchronous。

image-20240604080404318

下面我们来对USART进行配置,首先开发板上的串口对应的USART1串口,Mode配置为异步模式Asynchronous,STMCubeMX会自动分配引脚,目前分配的USART1_RX对于PA10,USART1_TX对于PA9,和我们开发板的引脚正好对应,如果不对应的话,可以根据芯片的data Sheet改成相应的引脚。

下面的参数配置Parameter Settings按照默认配置来,波特率为 115200 bit/s,这里确保主从机是一致的,才能通信成功,数据位 8,无校验位,停止位1,数据方向:Receive and Transmit,采样:16.

image-20240604080436567

点击下方的GPIO Settings,可以看到为USART1自动分配的默认引脚

image-20240604080454808

配置USART2,将其配置为异步通信。image-20240713100429531

2.4 配置按键GPIO和LED灯

按照1.2节的硬件说明,进行GPIO和LED灯配置,配置内容如下。

image-20240713101449299

2.5 配置CRC

由于在上位机和单片机传输中,需要对发送的数据进行CRC校验,所以需要将CRC功能激活。激活方法如下

image-20240713102335927

2.6 时钟树配置

点击上方的Clock Configuration,开始配置时钟

image-20240611074559483

下面我们来看一下时钟树的结构,如下图所示

image-20240604081134525

现在开始配置开发板相关的时钟频率,首先选择做左边的Input frequency,选择外部8M的晶振,选择HSE,选择PLLCLK,在HCLK处将时钟敲定为168MHz,即STM32F407可支持的最大时钟频率

image-20240604081430121

2.5 工程导出设置

如下图所示,设置工程

image-20240604213948820

代码生成设置

image-20240604214023852

高级设置Advanced Settings

image-20240604214510752

点击右上角,生成代码GENERATE CODE

image-20240611075023223

点击Open Project,本项目是使用STM32CubeIDE作为集成开发环境,做到编译和调试代码的工具

image-20240604214643976

3. 代码编写

我们目前使用的是Hal库进行的工程实现,如下图所示,是串口轮询发送函数HAL_UART_Transmit(),在发送的过程中,会一直在该函数中进行发送,是Polling Mode。

image-20240608084049717

下图是串口接收函数 HAL_UART_Receive(),也是Polling Mode,在接收数据的过程中,CPU无法被抢占,一直需要等到数据被发送完成后才可退出该函数

image-20240608084138029

下面是在main.c中的代码实现:

引用c标准头文件

image-20240611080012875

宏定义,BL_DEBUG_MSG_EN是为调试用的,重定义huart1,设置数据bl_rx_buffer

image-20240713103012848

在main()函数中调用bootloader_uart_read_data()函数进行数据接收和发送

image-20240713102835072

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 packet
    	HAL_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 2
    uint32_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 application
    app_reset_handler();

}

下面是printmsg()打印数据函数实现。

/* prints formatted string to console over UART */
void printmsg(char *format,...)
{
#ifdef BL_DEBUG_MSG_EN
	char 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 application
		  bootloader_jump_to_user_app();
	  }

    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

4. 工程下载、调试和演示

将工程编译,之后下载到板子中。

image-20240608084959855

打开设备管理器,查看串口的端口号COM

image-20240608084402078

打开正点原子的串口调试助手,选择刚才设备管理器中串口的COM号,波特率设置为115200,其他默认,打开串口,随便发送一个数据,开发板接收到这个数据,都会原封不动的将该数据打印出来,然后在执行打印Going to BL mode,说明能够接收并发送数据,至此,我们开发Customer BootLoader的第一步,最小工程代码已经搭建完成,后续我们进行上位机与开发板之间的通信协议开发。

image-20240608084720673

一开始程序先跳转至APP层。

image-20240713103936091

复位开发板,下面上电按下KeyUP按键,进入到Customer BootLoader中。

image-20240713104148618

下面打开上位机,连接上位机和开发板连接的串口,根据显示的指令,进行相应的操作。

image-20240713104433991

下面我们展示一下读取版本号的功能,输入1,获取版本号为0x10.

image-20240713104633571

下面我们在试一个刷新命令。下面是录制的一整个执行的流程。

BootLoader串口刷新

如果大家有什么疑问,请随时私信联系我。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1931850.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SSE(Server Sent Event)实战(2)- Spring MVC 实现

一、服务端实现 使用 RestController 注解创建一个控制器类(Controller) 创建一个方法来创建一个客户端连接,它返回一个 SseEmitter,处理 GET 请求并产生(produces)文本/事件流 (text/event-stream) 创建…

QT小细节

QT小细节 1 QTextToSpeech1.1 cmake1.2 qmake QT6 6.7.2 1 QTextToSpeech 从下图可以看到,分别使用qmake或者cmake编译情况下的,QTextToSpeech的使用方法 QTextToSpeech官方链接,也可以直接在QT Creator的帮助中搜索 1.1 cmake 将上图中的…

无人机之机型区别与应用领域

一、多旋翼无人机 特点:多旋翼无人机依靠产生升力以平衡飞行器的重力,通过改变每个旋翼的转速来控制飞行姿态,能够悬停和垂直起降。他们具备体积小、重量轻、噪音小、隐蔽性好的特点,操作灵活且易于维护。 应用:多旋…

django踩坑(四):终端输入脚本可正常执行,而加入crontab中无任何输出

使用crontab执行python脚本时,有时会遇到脚本无法执行的问题。这是因为crontab在执行任务时使用的环境变量与我们在终端中使用的环境变量不同。具体来说,crontab使用的环境变量是非交互式(non-interactive)环境变量,而终端则使用交互式(inter…

补充.IDEA的使用

首先我们要了解在idea中Java工程由项目(project)、模块(module)包(package)、类(class)组成。 他们之间的关系是project包含module包含package包含class。 所以我们要按照先建一个pr…

启智畅想火车类集装箱号码识别技术,软硬件解决方案

集装箱号码识别需求: 实时检测车皮号、火车底盘号码、集装箱号码,根据火车类型分为以下三种情况: 1、纯车皮,只检测车皮号; 2、火车拉货箱(半车皮),检测车皮号集装箱号码&#xff1b…

巧用通义灵码助力护网面试

前言 前几年护网还算是一个比较敏感的话题,但是随着近段时间的常态化开始,护网行动也是逐渐走进了大众的视野,成为了社会各界共同关注的安全盛事。本篇也是受通义灵码备战求职季活动的启发,结合近期要开始的护网行动&#xff0c…

监控系统怎样做?

监控类型自底向上分为资源监控、服务监控和业务监控。希望打造公司级的监控系统最好的时机是系统规划时,如果把监控设计往后放,将会面临一个巨大的难题:推行和现有不兼容的规范。 三种监控类型 资源监控 这个相对简单,随着k8s的兴…

Python 如何使用列表推导式(list comprehensions)?

列表推导式(List Comprehensions)是 Python 中一种简洁且强大的创建列表的方式。通过使用列表推导式,可以用一行代码来生成列表,而不是通过多行代码的循环或其他方法。 一、列表推导式的基本语法 列表推导式的基本语法如下&…

QT开发笔记:信号和槽

乱码问题: 出现乱码问题原因只有一个:就是编码方式不匹配!!! 中文常见汉字4K,算上各种生僻字差不多六万字 仍然使用一个大表格,给每个汉字,分配一个整数即可。 字符集~~表示汉字的字符集&#…

基于若依的ruoyi-nbcio流程管理系统修正自定义业务表单的回写bug

更多ruoyi-nbcio功能请看演示系统 gitee源代码地址 前后端代码: https://gitee.com/nbacheng/ruoyi-nbcio 演示地址:RuoYi-Nbcio后台管理系统 http://218.75.87.38:9666/ 更多nbcio-boot功能请看演示系统 gitee源代码地址 后端代码: h…

差异分析的结果各种热图,火山图,箱式图可视化作图教程

1. 基因表达的热图绘制 1.1 根据所有差异基因绘制基因表达的聚类热图 视频教程: https://www.bilibili.com/video/BV13m421g7wv/ 1.2 绘制top差异基因表达的聚类热图 视频教程: https://www.bilibili.com/video/BV1jZ42147KP/ 1.3 绘制感兴趣基因的聚类热图 视频教程: http…

一招教你选出独立站爆品

独立站选品其实是让很多卖家感到头大的问题,明明选了一个在其他平台是爆款的品,放到独立站上就是卖不起量,有自己想卖的产品却找不到好的供应商。对于这些问题,主要还是因为在选品时照搬电商平台的选品思路,没有认清自…

2024年中级消防设施操作员(考前冲刺)证考试题库及中级消防设施操作员(考前冲刺)试题解析

题库来源:安全生产模拟考试一点通公众号小程序 2024年中级消防设施操作员(考前冲刺)证考试题库及中级消防设施操作员(考前冲刺)试题解析是安全生产模拟考试一点通结合(安监局)特种作业人员操作…

5个超牛的Java开源OA项目(强烈推荐)

1. O2OA ——开源地址:https://gitee.com/o2oa/O2OA 概述: O2OA 是一款真正全代码(包含服务器、安卓以及IOS客户端)开源的企业应用定制化开发平台,适用于企业OA、协同办公类信息化系统的建设和开发。技术:…

HarmonyOS 开发者联盟高级认证最新题库

本篇文章包含 Next 版本更新后高级认证题库中95%的题目。 答案正确率 50-60%,答案仅做参考。 请在考试前重点看一遍题目,勿要盲目抄答案。 欢迎在评论留言正确答案和未整理的题目。 1、下面关于方舟字节码格式PREF_IMM16_v8_v8描述正确的是 16位前缀操作…

【wyTest自动化测试】快照校验模式的创新实践

本文将以遥遥领先的【华为商城】web端自动化为例,进行测试脚本开发的讲解。仅用于教学指导使用,如有侵权,请联系我删除。 一、测试脚本结构 按照教程惯例,开篇先展示一下测试脚本的标准结构模版: case_xx特性_xx001.py class Cas…

挖矿宝藏之硬盘分区

目录 一、硬盘分区的相关知识 二、主分区、活动分区、扩展分区、逻辑盘和盘符 三、硬盘分区原因 1.减少硬盘空间的浪费 2.便于文件的分类管理 3.有利于病毒的防治 四、硬盘分区的原则 1.方便性 2.实用性 3.安全性 五、利用Diskpart进行分区 1.命令行工具Diskpart …

信创学习笔记(四),信创之数据库DB思维导图

创作不易 只因热爱!! 热衷分享,一起成长! “你的鼓励就是我努力付出的动力” 一. 信创学习回顾 1.信创内容 信创内容思维导图 2.信创之CPU芯片架构 信创之CPU芯片架构思维导图 3.信创之操作系统OS 信创之操作系统OS思维导图 二. 信创之国产数据库DB思维导图 …

# Redis 入门到精通(六)-- redis 事务

Redis 入门到精通(六)-- redis 事务 一、redis 事务–redis事务简介 1、Redis 执行指令过程中,多条连续执行的指令被干扰,打断,插队,就会造成结果偏差。 2、什么是 redis 事务? redis 事务&…