使用STM32CubeMX创建以太网在线升级(IAP)工程的完整指南

开源AI·十一月创作之星挑战赛 10w+人浏览 556人参与

使用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 存储空间规划

区域起始地址大小说明
Bootloader0x0800000064KBIAP核心
Application0x08010000384KB主程序区
Backup0x0807000048KB固件缓存区
Parameters0x0807F0004KB升级标志存储

二、Bootloader工程配置

2.1 创建工程

  1. 打开STM32CubeMX,选择STM32F439芯片
  2. 项目命名:F439_IAP_Bootloader
  3. 设置工程路径
  4. 选择IDE:MDK-ARM V5

2.2 时钟配置

8MHz晶振
分频/8
倍频/336
分频/2
168MHz
25MHz

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 生成代码设置

  1. 项目设置
  • 堆栈大小:0x1000
  • Heap大小:0x2000
  1. 代码生成
  • 勾选"生成外设初始化代码"
  • 勾选"为每个外设生成单独文件"
  1. 高级设置
  • 启用"备份中断向量表"
  • IRQ处理函数:默认

三、应用程序工程配置

3.1 创建工程

  1. 新建CubeMX工程
  2. 项目命名:F439_IAP_App
  3. 使用与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 烧录步骤

  1. 先烧录Bootloader到0x08000000
  2. 烧录应用程序到0x08010000
  3. 使用串口监控输出
  4. 连接网线到开发板

5.2 TFTP升级测试

# 使用命令行工具测试
tftp -i 192.168.1.100 PUT firmware.bin

# 使用图形工具步骤:
1. 打开TFTP客户端工具
2. 设置服务器IP:192.168.1.100
3. 选择要上传的固件文件
4. 点击"发送"

5.3 调试技巧

  1. LED指示
  • 长亮:Bootloader运行
  • 慢闪:网络连接中
  • 快闪:数据传输中
  • 双闪:固件验证成功
  1. 串口日志
printf("[BOOT] Firmware download started\n");
printf("[ETH] Link Up: 100Mbps\n");
printf("[FLASH] Sector %d erased successfully\n", sector);

六、常见问题解决

6.1 网络连接失败

排查步骤

  1. 检查PHY芯片地址配置
// 在stm32f4xx_hal_conf.h中确认
#define PHY_ADDRESS 0x00
  1. 验证RMII时钟:
  • 使用示波器检测REF_CLK(50MHz)
  1. 检查网络变压器中心抽头电容
  2. 测试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 跳转失败问题

解决方案

  1. 确认向量表偏移:
// 应用程序中必须设置
SCB->VTOR = FLASH_BASE | 0x10000;
  1. 检查栈指针有效性:
uint32_t sp = *(__IO uint32_t*)APPLICATION_ADDR;
if(sp < SRAM_BASE || sp > (SRAM_BASE + SRAM_SIZE)) {
// 无效栈指针
}
  1. 在跳转前禁用所有中断:
__disable_irq();

6.3 固件校验失败

优化方案

  1. 添加双校验机制:
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);
}
  1. 实现分段校验:
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 安全性增强

  1. 添加TLS加密传输:
// 在lwipopts.h中启用
#define LWIP_ALTCP 1
#define LWIP_ALTCP_TLS 1
  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 性能提升

  1. 启用ETH DMA双缓冲:
heth.Init.RxDesc = &DMARxDscrTab[0];
heth.Init.TxDesc = &DMATxDscrTab[0];
  1. 优化LWIP内存池:
// 在lwipopts.h中调整
#define PBUF_POOL_SIZE 16
#define MEM_SIZE 16000
#define TCP_WND 8192
#define TCP_MSS 1460

7.3 量产注意事项

  1. 锁定Bootloader区域:
// 在Option Bytes中设置写保护
HAL_FLASH_OB_Unlock();
FLASH_OB_WRPConfig(OB_WRP_SECTOR_0, ENABLE);
HAL_FLASH_OB_Launch();
HAL_FLASH_OB_Lock();
  1. 添加版本管理:
// Bootloader头信息
__attribute__((section(".boot_header")))
const BootHeader_t header = {
.magic = 0xAA55A55A,
.version = 0x0102,
.crc = CALCULATED_CRC,
.entry_point = APPLICATION_ADDR
};

八、总结与进阶

通过STM32CubeMX创建在线升级工程的关键步骤:

  1. 合理分区:明确Bootloader/App/Backup区域
  2. 正确配置:ETH外设、LWIP协议栈、时钟树
  3. 双工程协调:设置向量表偏移和链接脚本
  4. 实现核心功能:网络传输+Flash操作+安全跳转
  5. 完善机制:校验、回滚、错误处理

进阶方向

  1. 添加无线升级支持(4G/WiFi)
  2. 实现A/B双备份无缝切换
  3. 开发远程诊断接口
  4. 集成安全启动(TrustZone)
  5. 添加压缩算法减少传输量

特别提醒:完整工程请从GitHub获取:
https://github.com/STMicroelectronics/STM32CubeF4/tree/master/Projects

掌握在线升级技术可大幅提升产品竞争力,本指南提供了从零到量产的完整路径,助您构建工业级可靠的OTA升级系统。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值