STM32 Customer BootLoader 刷新项目 (一) STM32CubeMX UART串口通信工程搭建

STM32 Customer BootLoader 刷新项目 (一) STM32CubeMX UART串口通信工程搭建

文章目录

  • STM32 Customer BootLoader 刷新项目 (一) STM32CubeMX UART串口通信工程搭建
      • 功能与作用
      • 典型工作流程
    • 1. 硬件原理图介绍
    • 2. STM32 CubeMX工程搭建
      • 2.1 创建工程
      • 2.2 系统配置
      • 2.3 USART串口配置
      • 2.4 时钟树配置
      • 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)一起工作,提供了更灵活和复杂的引导机制。

功能与作用

  1. 硬件初始化: 二级Customer BootLoader通常负责对特定硬件的初始化工作。虽然一级BootLoader已经完成了一些基本的硬件初始化,但二级BootLoader会进行更详细的硬件配置,如设置外设(例如UART、SPI、I2C等)、初始化存储设备(如Flash、EEPROM等)以及配置系统时钟等。
  2. 固件验证与更新: 二级Customer BootLoader常常用于验证固件的完整性和合法性。这可以通过校验和(Checksum)、加密签名等方式来实现。若检测到固件损坏或版本过旧,BootLoader可以从预设的位置(如网络、USB设备或备用存储区)下载并更新固件。
  3. 安全启动: 为了增强系统安全性,二级BootLoader可以实现安全启动机制。它会检查固件的数字签名或哈希值,确保只有经过验证和授权的固件才能被加载和执行,从而防止恶意代码的运行。
  4. 引导多种操作系统或应用程序: 二级Customer BootLoader可以配置为引导不同的操作系统或应用程序。例如,在嵌入式系统中,可能需要根据不同的条件引导进入不同的应用程序模块,BootLoader可以根据预设的规则进行选择和加载。
  5. 配置和诊断功能: 二级BootLoader可以提供一些配置和诊断功能。例如,它可以允许用户通过串口或网络接口进入配置模式,调整系统参数,进行硬件诊断和调试。
  6. 引导时间优化: 由于嵌入式系统通常需要快速启动,二级BootLoader可以优化引导过程,减少启动时间。它可以通过压缩固件、优化初始化代码等手段来实现快速引导。

典型工作流程

  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. 硬件原理图介绍

本项目采用正点原子探索者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

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串口配置

选择左侧的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

2.4 时钟树配置

点击上方的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-20240611075845775

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

image-20240611075754745

bootloader_uart_read_data()函数中先接收在发送。

image-20240611075915162

printmsg()打印数据函数实现。

image-20240611075926137

下面是main.c的完整代码:

/* USER CODE BEGIN Header */
/********************************************************************************* @file           : main.c* @brief          : Main program body******************************************************************************* @attention** Copyright (c) 2024 STMicroelectronics.* All rights reserved.** This software is licensed under terms that can be found in the LICENSE file* in the root directory of this software component.* If no LICENSE file comes with this software, it is provided AS-IS.********************************************************************************/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include <stdarg.h>
#include <string.h>
#include <stdint.h>
#include <stdio.h>
/* USER CODE END Includes *//* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD *//* USER CODE END PTD *//* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
#define BL_DEBUG_MSG_EN
#define BL_RX_LEN  200/* USER CODE END PD *//* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM *//* USER CODE END PM *//* Private variables ---------------------------------------------------------*/
UART_HandleTypeDef huart1;/* USER CODE BEGIN PV */
#define C_UART   &huart1uint8_t bl_rx_buffer[BL_RX_LEN];
/* USER CODE END PV *//* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_USART1_UART_Init(void);
/* USER CODE BEGIN PFP */
void  bootloader_uart_read_data(void);
static void printmsg(char *format,...);/* 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 *//* 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 *//* USER CODE END 2 *//* Infinite loop *//* USER CODE BEGIN WHILE */while (1){bootloader_uart_read_data();/* USER CODE END WHILE *//* USER CODE BEGIN 3 */}/* 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 RCC Oscillators according to the specified parameters* in the RCC_OscInitTypeDef structure.*/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 = 4;RCC_OscInitStruct.PLL.PLLN = 168;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 buses 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();}
}/*** @brief USART1 Initialization Function* @param None* @retval None*/
static void MX_USART1_UART_Init(void)
{/* USER CODE BEGIN USART1_Init 0 *//* USER CODE END USART1_Init 0 *//* USER CODE BEGIN USART1_Init 1 *//* USER CODE END USART1_Init 1 */huart1.Instance = USART1;huart1.Init.BaudRate = 115200;huart1.Init.WordLength = UART_WORDLENGTH_8B;huart1.Init.StopBits = UART_STOPBITS_1;huart1.Init.Parity = UART_PARITY_NONE;huart1.Init.Mode = UART_MODE_TX_RX;huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;huart1.Init.OverSampling = UART_OVERSAMPLING_16;if (HAL_UART_Init(&huart1) != HAL_OK){Error_Handler();}/* USER CODE BEGIN USART1_Init 2 *//* USER CODE END USART1_Init 2 */}/*** @brief GPIO Initialization Function* @param None* @retval None*/
static void MX_GPIO_Init(void)
{
/* USER CODE BEGIN MX_GPIO_Init_1 */
/* USER CODE END MX_GPIO_Init_1 *//* GPIO Ports Clock Enable */__HAL_RCC_GPIOC_CLK_ENABLE();__HAL_RCC_GPIOH_CLK_ENABLE();__HAL_RCC_GPIOA_CLK_ENABLE();__HAL_RCC_GPIOB_CLK_ENABLE();/* USER CODE BEGIN MX_GPIO_Init_2 */
/* USER CODE END MX_GPIO_Init_2 */
}/* USER CODE BEGIN 4 */void  bootloader_uart_read_data(void)
{uint8_t rcv_len = 0;uint8_t rcv_flag = 0;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, (uint8_t *)bl_rx_buffer, 1, HAL_MAX_DELAY);rcv_len= bl_rx_buffer[0];if (rcv_len != 0){HAL_UART_Transmit(C_UART,(uint8_t *)&rcv_len, 1, HAL_MAX_DELAY);rcv_flag = 1;}if (rcv_flag == 1){printmsg("-> Going to BL mode\n\r");}}/* 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(C_UART,(uint8_t *)str, strlen(str),HAL_MAX_DELAY);va_end(args);
#endif}/* USER CODE END 4 *//*** @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 */__disable_irq();while (1){}/* 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,ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) *//* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */

4. 工程下载和调试

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

image-20240608084959855

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

image-20240608084402078

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

image-20240608084720673

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

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

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

相关文章

tokenization(一)概述

文章目录 背景基于词&#xff08;Word-based&#xff09;基于字符&#xff08;Character-based&#xff09;子词词元化&#xff08;Subword tokenization&#xff09; 背景 tokenization是包括大语言模型在内所有自然语言处理的任务的基础步骤&#xff0c;其目标是将文本数据转…

【面试干货】聚集索引和非聚集索引区别?

【面试干货】聚集索引和非聚集索引区别? 1、聚集索引&#xff08;Clustered Index&#xff09;1.1 特点1.2 例子 2、非聚集索引&#xff08;Nonclustered Index&#xff09;2.1 特点2.2 例子 3、根本区别 &#x1f496;The Begin&#x1f496;点点关注&#xff0c;收藏不迷路&…

Sklearn的安装和用法

安装sklearn相对简单&#xff0c;因为它是一个Python库&#xff0c;可以通过Python的包管理器pip来安装。 Windows、macOS和Linux通用步骤&#xff1a; 确保Python已安装&#xff1a; sklearn是基于Python的&#xff0c;所以首先确保你的计算机上安装了Python。推荐使用Pytho…

NLP——电影评论情感分析

python-tensorflow2.0 numpy 1.19.1 tensorflow 2.0.0 导入库 数据加载 数据处理 构建模型 训练 评估 预测 1.基于2层dropout神经网络 2.基于LSTM的网络 #导入需要用到的库 import os import tarfile import urllib. request import tensorflow as tf import numpy a…

5W-35W-150W-300W-500W铝壳功率电阻器

带铝制外壳的电阻器 EAK采用铝型材的导线电阻器将久经考验的导线材料的高脉冲稳定性与优化的导热和高度保护相结合。安装在导热表面上可进一步改善散热并提高稳定性。 连接线有各种长度和材料可供选择。可选配集成温度开关。也可根据客户要求提供定制组件。 该产品有多种版本…

CVE-2023-37474(目录遍历)

靶场简介 Copyparty是一个可移植的文件服务器。在1.8.2版本之前的版本存在一个CTF技巧&#xff0c;该漏洞位于.cpr子文件夹中。路径遍历攻击技术允许攻击者访问位于Web文档根目录之外的文件、目录. 靶场 进入靶场 根据简介访问.cpr目录 使用curl命令访问etc/passwd文件 确定…

kettle_Hbase

kettle_Hbase ☀Hbase学习笔记 读取hdfs文件并将sal大于1000的数据保存到hbase中 前置说明&#xff1a; 1.需要配置HadoopConnect 将集群中的/usr/local/soft/hbase-1.4.6/conf/hbase-site.xml复制至Kettle中的 Kettle\pdi-ce-8.2.0.0-342\data-integration\plugins\pentah…

8.1 基本打印功能

本文仅供学习交流&#xff0c;严禁用于商业用途&#xff0c;如本文涉及侵权请及时联系本人将于及时删除 在使用“MFC应用”项目模板生成应用程序的过程中&#xff0c;如果在“高级功能”窗口中不取消对打印和打印预览的设置&#xff0c;那么应用程序就已经具备了简单的打印和打…

MySQL—多表查询—练习(2)

一、引言 接着上篇博客《 MySQL多表查询——练习&#xff08;1&#xff09;》继续完成剩下的案例需求。 二、案例 &#xff08;0&#xff09;三张表&#xff08;员工表、部门表、薪资等级表&#xff09; 员工表&#xff1a;emp 部门表&#xff1a;dept 薪资等级表&#xff1a;…

使用 PlatformIO 将文件上传到 ESP32-S3 的 SPIFFS 文件系统

PlatformIO环境 将文件上传到 ESP32-S3 的 SPIFFS 文件系统 介绍&#xff1a; PlatformIO 是一个流行的开发平台&#xff0c;用于编写、构建和上传嵌入式项目。ESP32-S3 是 Espressif 推出的一款功能强大的嵌入式开发板&#xff0c;具有丰富的外设和通信接口。本文将介绍如何…

前端 JS 经典:动态执行 JS

前言&#xff1a;怎么将字符串当代码执行。有 4 中方式实现 eval、setTimeout、创建 script 标签、new Function 1. eval 特点&#xff1a;同步执行&#xff0c;当前作用域 var name "yq"; function exec(string) {var name "yqcoder";eval(string); …

认识Spring中的BeanFactoryPostProcessor

先看下AI的介绍 在Spring 5.3.x中&#xff0c;BeanFactoryPostProcessor是一个重要的接口&#xff0c;用于在Spring IoC容器实例化任何bean之前&#xff0c;读取bean的定义&#xff08;配置元数据&#xff09;&#xff0c;并可能对其进行修改。以下是关于BeanFactoryPostProce…

【学习笔记】finalshell上传文件夹、上传文件失败或速度为0

出现标题所述的情况&#xff0c;大概率是finalshell上传文件的过程中的权限不够。 可参照&#xff1a;Finalshell上传文件失败或者进度总为百分之零解决方法 如果不成功&#xff0c;建议关闭客户端重试。 同时建议在设置finalshell的ssh连接时根据不同用户设置多个连接&#xf…

OJ刷题——2086.AI=?、2087.剪花布条、KPM算法

2086.AI&#xff1f; 题目描述 Problem - 2086 运行代码 #include <iostream> #include <cstdio> using namespace std; const int N 3005; int main() {int n;double Ao, An;double num[N];while (cin>>n) {cin >> Ao>>An;for (int i 1; i…

kubernetes(k8s)集群部署(2)

目录 k8s集群类型 k8s集群规划&#xff1a; 1.基础环境准备&#xff1a; &#xff08;1&#xff09;保证可以连接外网 &#xff08;2&#xff09;关闭禁用防火墙和selinux &#xff08;3&#xff09;同步阿里云服务器时间&#xff08;达到集群之间时间同步&#xff09; &…

html+CSS+js部分基础运用20

根据下方页面效果如图1所示&#xff0c;编写程序&#xff0c;代码放入图片下方表格内 图1.效果图 <!DOCTYPE html> <html lang"en"> <head> <meta charset"UTF-8"> <meta http-equiv"X-UA-Compatible" conte…

LabVIEW调用DLL时需注意的问题

在LabVIEW中调用DLL&#xff08;动态链接库&#xff09;是实现与外部代码集成的一种强大方式&#xff0c;但也存在一些常见的陷阱和复杂性。本文将从参数传递、数据类型匹配、内存管理、线程安全、调试和错误处理等多个角度详细介绍LabVIEW调用DLL时需要注意的问题&#xff0c;…

邻接矩阵深度优先遍历

深度优先遍历&#xff0c;就是一条路&#xff0c;走到底&#xff0c;然后再走下一个岔路。 下面代码就主要使用递归来进行&#xff0c;当然也可以借助栈来实现。 private void traverse(char v, boolean[] visited) {int index _getIndexOfV(v);//获取v顶点在vertexS字符数组…

Prisma数据库ORM框架学习

初始化项目 中文网站 点击快速开始,点击创建sql项目,后面一步一步往后走 这个博主也挺全的,推荐下 可以看这个页面初始化项目跟我下面是一样的,这里用得是ts,我下面是js,不需要额外的配置了 1.vscode打开一个空文件夹 2.npm init -y 初始化package.json 3.安装相关依赖 …

常用的通信协议

最近在做项目&#xff0c;用到了一些通信协议&#xff0c;这里详细整理一下相关的通信协议&#xff0c;方便以后查阅。 常用的通信协议 单工 半双工 全双工单工通信&#xff08;Simplex Communication&#xff09;半双工(Half-duplex Communication)全双工&#xff08;Full-dup…