使用STM32CubeMX创建以太网在线升级(IAP)工程的完整指南
本教程将详细介绍如何使用STM32CubeMX从零开始创建一个完整的以太网在线升级(IAP)工程,涵盖Bootloader和应用程序的配置与开发。
一、环境准备与工程规划
1.1 软硬件准备
- 软件:
- STM32CubeMX v6.7+
- Keil MDK/IAR/STM32CubeIDE
- TCP/UDP测试工具(如NetAssist)
- 硬件:
- STM32F439开发板(支持RMII以太网)
- 以太网PHY芯片(如LAN8720A)
- ST-Link调试器
1.2 存储空间规划
| 区域 | 起始地址 | 大小 | 说明 |
|---|---|---|---|
| Bootloader | 0x08000000 | 64KB | IAP核心 |
| Application | 0x08010000 | 384KB | 主程序区 |
| Backup | 0x08070000 | 48KB | 固件缓存区 |
| Parameters | 0x0807F000 | 4KB | 升级标志存储 |
二、Bootloader工程配置
2.1 创建工程
- 打开STM32CubeMX,选择STM32F439芯片
- 项目命名:
F439_IAP_Bootloader - 设置工程路径
- 选择IDE:MDK-ARM V5
2.2 时钟配置
2.3 外设配置
以太网ETH:
- 模式:RMII接口
- PHY地址:0(根据硬件调整)
- PHY接口:LAN8720
- 高级设置:
- 接收缓冲数量:6
- 发送缓冲数量:4
串口USART1:
- 波特率:115200
- 数据位:8
- 停止位:1
- NVIC设置:启用全局中断
Flash配置:
- 启用Flash读写操作
- 添加用户自定义段:
// Bootloader配置段
__attribute__((section(".boot_config"))) const uint32_t boot_config = 0xAA55A55A;
2.4 中间件配置
LWIP:
- 协议栈:LWIP v2.1.2
- DHCP:禁用(使用静态IP)
- IP地址:192.168.1.100
- 子网掩码:255.255.255.0
- 添加应用协议:
- TFTP服务器
- SNTP客户端(可选)
2.5 生成代码设置
- 项目设置:
- 堆栈大小:0x1000
- Heap大小:0x2000
- 代码生成:
- 勾选"生成外设初始化代码"
- 勾选"为每个外设生成单独文件"
- 高级设置:
- 启用"备份中断向量表"
- IRQ处理函数:默认
三、应用程序工程配置
3.1 创建工程
- 新建CubeMX工程
- 项目命名:
F439_IAP_App - 使用与Bootloader相同的芯片设置
3.2 关键配置差异
中断向量表偏移:
- 在System Core > NVIC中设置:
// 应用程序向量表偏移
#define VECT_TAB_OFFSET0x10000
Flash分区:
- 修改链接脚本:
MEMORY
{
RAM (xrw): ORIGIN = 0x20000000, LENGTH = 192K
FLASH (rx): ORIGIN = 0x8010000, LENGTH = 384K
}
3.3 添加升级触发机制
在应用程序中实现升级请求:
void RequestFirmwareUpdate(void)
{
// 设置升级标志
HAL_FLASH_Unlock();
uint32_t flag = UPDATE_REQUEST_FLAG;
HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, PARAM_FLAG_ADDR, flag);
HAL_FLASH_Lock();
// 软复位跳转到Bootloader
NVIC_SystemReset();
}
四、关键代码实现
4.1 Bootloader主逻辑
/* 跳转函数 */
void JumpToApplication(void)
{
typedef void (*ApplicationEntry)(void);
ApplicationEntry AppStart = (ApplicationEntry)(*(__IO uint32_t*)(APPLICATION_ADDR + 4));
// 禁用所有中断
__disable_irq();
// 设置新堆栈指针
uint32_t StackPointer = *(__IO uint32_t*)APPLICATION_ADDR;
__set_MSP(StackPointer);
// 设置向量表偏移
SCB->VTOR = APPLICATION_ADDR;
// 跳转到应用程序
AppStart();
}
/* 主流程 */
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
if(CheckUpdateRequest()) {
MX_ETH_Init();
MX_LWIP_Init();
StartTFTP_Server();
DownloadFirmware();
if(VerifyFirmware()) {
FlashSwitchApplication();
}
}
if(VerifyApplication()) {
JumpToApplication();
} else {
// 恢复备份固件
}
}
4.2 TFTP服务器实现
void StartTFTP_Server(void)
{
struct udp_pcb *tftp_pcb = udp_new();
udp_bind(tftp_pcb, IP_ADDR_ANY, TFTP_PORT);
udp_recv(tftp_pcb, tftp_recv_callback, NULL);
}
void tftp_recv_callback(void *arg, struct udp_pcb *pcb,
struct pbuf *p, const ip_addr_t *addr, u16_t port)
{
uint16_t opcode = ntohs(*(uint16_t*)p->payload);
switch(opcode) {
case TFTP_RRQ: // 读请求
udp_sendto(pcb, p, addr, port);
break;
case TFTP_WRQ: // 写请求
StartFirmwareDownload(pcb, addr, port);
break;
case TFTP_DATA: // 数据包
ProcessDataPacket(p->payload, p->len);
SendACK(pcb, addr, port);
break;
}
pbuf_free(p);
}
4.3 Flash操作封装
HAL_StatusTypeDef FlashEraseSector(uint32_t sector)
{
FLASH_EraseInitTypeDef EraseInit;
uint32_t SectorError = 0;
EraseInit.TypeErase = FLASH_TYPEERASE_SECTORS;
EraseInit.Sector = sector;
EraseInit.NbSectors = 1;
EraseInit.VoltageRange = FLASH_VOLTAGE_RANGE_3;
HAL_FLASH_Unlock();
HAL_StatusTypeDef status = HAL_FLASHEx_Erase(&EraseInit, &SectorError);
HAL_FLASH_Lock();
return status;
}
HAL_StatusTypeDef FlashWrite(uint32_t addr, uint8_t *data, uint32_t len)
{
HAL_StatusTypeDef status = HAL_OK;
HAL_FLASH_Unlock();
for(uint32_t i=0; i<len; i+=4) {
uint32_t word = *(uint32_t*)(data + i);
status = HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, addr+i, word);
if(status != HAL_OK) break;
// 实时验证
if(*(__IO uint32_t*)(addr+i) != word) {
status = HAL_ERROR;
break;
}
}
HAL_FLASH_Lock();
return status;
}
五、调试与测试流程
5.1 烧录步骤
- 先烧录Bootloader到0x08000000
- 烧录应用程序到0x08010000
- 使用串口监控输出
- 连接网线到开发板
5.2 TFTP升级测试
# 使用命令行工具测试
tftp -i 192.168.1.100 PUT firmware.bin
# 使用图形工具步骤:
1. 打开TFTP客户端工具
2. 设置服务器IP:192.168.1.100
3. 选择要上传的固件文件
4. 点击"发送"
5.3 调试技巧
- LED指示:
- 长亮:Bootloader运行
- 慢闪:网络连接中
- 快闪:数据传输中
- 双闪:固件验证成功
- 串口日志:
printf("[BOOT] Firmware download started\n");
printf("[ETH] Link Up: 100Mbps\n");
printf("[FLASH] Sector %d erased successfully\n", sector);
六、常见问题解决
6.1 网络连接失败
排查步骤:
- 检查PHY芯片地址配置
// 在stm32f4xx_hal_conf.h中确认
#define PHY_ADDRESS 0x00
- 验证RMII时钟:
- 使用示波器检测REF_CLK(50MHz)
- 检查网络变压器中心抽头电容
- 测试PHY寄存器读写:
uint32_t phy_id1, phy_id2;
HAL_ETH_ReadPHYRegister(&heth, PHY_ADDRESS, PHY_ID1, &phy_id1);
HAL_ETH_ReadPHYRegister(&heth, PHY_ADDRESS, PHY_ID2, &phy_id2);
6.2 跳转失败问题
解决方案:
- 确认向量表偏移:
// 应用程序中必须设置
SCB->VTOR = FLASH_BASE | 0x10000;
- 检查栈指针有效性:
uint32_t sp = *(__IO uint32_t*)APPLICATION_ADDR;
if(sp < SRAM_BASE || sp > (SRAM_BASE + SRAM_SIZE)) {
// 无效栈指针
}
- 在跳转前禁用所有中断:
__disable_irq();
6.3 固件校验失败
优化方案:
- 添加双校验机制:
bool VerifyFirmware(uint32_t addr, uint32_t size)
{
// CRC32校验
uint32_t crc = CalculateCRC(addr, size);
if(crc != expected_crc) return false;
// SHA-256摘要验证
uint8_t hash[32];
SHA256_Calculate(addr, size, hash);
return (memcmp(hash, expected_hash, 32) == 0);
}
- 实现分段校验:
for(int i=0; i<num_chunks; i++) {
uint32_t chunk_crc = CalculateChunkCRC(addr + i*chunk_size, chunk_size);
if(chunk_crc != stored_chunk_crc[i]) {
// 请求重传该分块
RequestRetransmit(i);
}
}
七、工程优化建议
7.1 安全性增强
- 添加TLS加密传输:
// 在lwipopts.h中启用
#define LWIP_ALTCP 1
#define LWIP_ALTCP_TLS 1
- 实现固件签名验证:
bool VerifySignature(uint8_t *firmware, uint32_t size, uint8_t *signature)
{
uint8_t digest[32];
SHA256(firmware, size, digest);
return ECDSA_Verify(pub_key, digest, signature);
}
7.2 性能提升
- 启用ETH DMA双缓冲:
heth.Init.RxDesc = &DMARxDscrTab[0];
heth.Init.TxDesc = &DMATxDscrTab[0];
- 优化LWIP内存池:
// 在lwipopts.h中调整
#define PBUF_POOL_SIZE 16
#define MEM_SIZE 16000
#define TCP_WND 8192
#define TCP_MSS 1460
7.3 量产注意事项
- 锁定Bootloader区域:
// 在Option Bytes中设置写保护
HAL_FLASH_OB_Unlock();
FLASH_OB_WRPConfig(OB_WRP_SECTOR_0, ENABLE);
HAL_FLASH_OB_Launch();
HAL_FLASH_OB_Lock();
- 添加版本管理:
// Bootloader头信息
__attribute__((section(".boot_header")))
const BootHeader_t header = {
.magic = 0xAA55A55A,
.version = 0x0102,
.crc = CALCULATED_CRC,
.entry_point = APPLICATION_ADDR
};
八、总结与进阶
通过STM32CubeMX创建在线升级工程的关键步骤:
- 合理分区:明确Bootloader/App/Backup区域
- 正确配置:ETH外设、LWIP协议栈、时钟树
- 双工程协调:设置向量表偏移和链接脚本
- 实现核心功能:网络传输+Flash操作+安全跳转
- 完善机制:校验、回滚、错误处理
进阶方向:
- 添加无线升级支持(4G/WiFi)
- 实现A/B双备份无缝切换
- 开发远程诊断接口
- 集成安全启动(TrustZone)
- 添加压缩算法减少传输量
特别提醒:完整工程请从GitHub获取:
https://github.com/STMicroelectronics/STM32CubeF4/tree/master/Projects
掌握在线升级技术可大幅提升产品竞争力,本指南提供了从零到量产的完整路径,助您构建工业级可靠的OTA升级系统。
2191

被折叠的 条评论
为什么被折叠?



