丽萨单片机固定升级,主工程下建两个副工程,分别是 bootloader 和 app。固件升级需要 2 进制的文件,而瑞萨的 CS+ IDE 编译生成的是 mot,之后使用 J-flash 软件转为 hex 文件,升级时需要把后面大量的 FF 删去!!!
后面大量的 0xFF:可以不删,但删掉更好!!!
不删也能正常升级,但强烈建议删掉。原因和具体操作如下。
一、什么是 "后面大量的 FF"
你的 App 实际代码可能只到 0x8000 就结束了,但 Flash 末尾是 0x3FFFF(256KB)。如果转换时用了 -fill 0xFF 或 J-Flash 默认填充,生成的文件会包含:
0x2000 ~ 0x8000 → 实际固件数据(有效)
0x8001 ~ 0x3FFFF → 全是 0xFF(无效,Flash 擦除后本来就是这个值)
↑
这部分就是"后面大量的FF"
一个 256KB 的 Flash,实际 App 可能只有 30KB,剩下 200 多 KB 全是 0xFF,白白占用升级时间。
二、不删会怎样?
不删也能正常工作,因为:
- Flash 擦除后默认值就是
0xFF - Bootloader 把收到的
0xFF再写一遍,结果还是0xFF,不影响 - 升级后 App 运行正常
但有 3 个缺点:
| 缺点 | 说明 |
|---|---|
| 升级慢 | 200KB 的 0xFF 通过串口传输,115200 波特率要约 20 秒,实际有效数据可能 3 秒就传完 |
| 易出错 | 传输数据量越大,串口误码概率越高,CRC 校验失败要重传 |
| 占空间 | 升级包文件大,存储和分发不方便 |
三、删掉的正确做法
关键原则:只保留 "有实际数据" 的区域,不填充到 Flash 末尾
方法 1:srec_cat(推荐,最精确)
不要加 -fill 参数 ,只用 -crop 裁剪到实际数据范围:
# 先查看 app.mot 中实际数据的最高地址
srec_info app.mot -Motorola
输出类似:
Format: Motorola S-Record
Data: 0x002000 - 0x0087FF ← 实际数据只到 0x87FF
然后只裁剪到实际结束地址 + 1:
# 只保留 0x2000~0x87FF 的实际数据,不填充后面的FF
srec_cat app.mot -Motorola ^
-crop 0x2000 0x8800 ^
-o app.bin -Binary
0x8800= 实际结束地址0x87FF+ 1。这样生成的 bin 文件只有约 26KB,而不是 248KB。
自动获取结束地址的脚本(Windows)
@echo off
REM 自动获取 mot 中最高地址,裁剪生成最小 bin
for /f "tokens=3" %%a in ('srec_info app.mot -Motorola ^| findstr /C:"Data:"') do set END=%%a
echo 实际数据结束地址: %END%
REM 结束地址+1
set /a END_PLUS_1=%END% + 1
srec_cat app.mot -Motorola -crop 0x2000 %END_PLUS_1% -o app.bin -Binary
echo 生成 app.bin,大小:
srec_info app.bin -Binary
方法 2:J-Flash 中手动删除
- J-Flash 打开
app.mot - 查看内存视图,找到实际数据的最高地址(后面全是 0xFF 的起点)
- 菜单 Edit → Delete range
- 输入:
Start = 实际结束地址+1,End = 0x3FFFF - 删完后,File → Save as → 选择 .bin 格式
方法 3:J-Flash 保存时只保存有效区域
J-Flash 保存时,有些版本可以选择 "Save only occupied areas"(只保存有数据的区域),这样自动忽略后面的 0xFF。
四、删了之后 Bootloader 要怎么改
删掉后面的 0xFF 后,bin 文件大小 = 实际固件大小,不是固定的 Flash 大小。Bootloader 需要:
1. 上位机先发固件长度
上位机 → Bootloader:
字节0~3: 固件大小(4字节,小端或大端,约定好)
字节4~N: 固件数据(bin 文件内容)
最后2字节: CRC16 校验
2. Bootloader 按实际长度处理
#define APP_START_ADDR 0x2000
void do_firmware_update(void)
{
/* 1. 接收固件长度 */
uint32_t fw_size = uart_recv_u32();
/* 2. 按实际长度擦除 App 区(从 0x2000 开始) */
flash_erase(APP_START_ADDR, fw_size);
/* 3. 接收 fw_size 字节数据,依次写入 Flash */
uint32_t addr = APP_START_ADDR;
uint32_t received = 0;
while (received < fw_size)
{
uint16_t len = uart_recv_frame(rx_buf, 256);
flash_write(addr, rx_buf, len);
addr += len;
received += len;
}
/* 4. CRC 校验 */
uint16_t crc_recv = uart_recv_u16();
uint16_t crc_calc = crc16(APP_START_ADDR, fw_size);
if (crc_recv != crc_calc) {
/* 校验失败,不标记新固件,保持旧固件(回滚) */
return;
}
/* 5. 标记新固件有效 */
set_fw_valid_flag();
}
关键点 :擦除和写入都按实际固件长度,不是按整个 Flash 大小。
五、中间空隙的 0xFF 要不要删?
App 的段之间可能有空隙(比如 .text 结束到 .data 开始之间有几个字节的空隙),这些空隙也是 0xFF。
这些中间的 0xFF 不能删,因为:
- bin 文件是连续地址流
- 中间空隙的 0xFF 占据了正确的地址偏移
- 如果删掉,后面的数据地址就错位了
只有 "最后面" 连续的 0xFF 可以删,因为它们不影响前面数据的地址偏移。
六、总结对比
| 处理方式 | 文件大小 | 升级速度 | 复杂度 | 推荐度 |
|---|---|---|---|---|
| 不删,填充到 Flash 末尾 | 大(如 248KB) | 慢 | 低 | ❌ 不推荐 |
| 删掉末尾 0xFF,只保留有效数据 | 小(如 26KB) | 快 | 中 | ✅ 推荐 |
| 用压缩算法压缩后传输 | 更小 | 最快 | 高 | ⭐ 高级方案 |
七、操作清单
- 用
srec_info app.mot -Motorola查看实际数据结束地址 - 用
srec_cat -crop 0x2000 结束地址+1生成 bin (不加-fill) - 上位机先发 4 字节固件长度,再发 bin 数据,最后发 CRC16
- Bootloader 按实际长度擦除、写入、校验
- 不要删中间空隙的 0xFF,只删末尾连续的 0xFF