3、脚本核心API:设备连接API、内存读写API、寄存器操作API、状态查询API
说实话,API 这块是烧录脚本的「硬通货」。你写再多花哨的逻辑,最后落地都得靠这几个接口去跟芯片打交道。我刚开始带团队的时候,发现很多新人把 API 当黑盒用,出了问题就抓瞎。今天咱们就把这四类核心 API 掰开揉碎,讲清楚它们到底怎么用、坑在哪。
核心观点:烧录脚本的本质,就是通过 API 把「人的意图」翻译成「芯片能理解的时序和电平」。API 设计的好坏,直接决定了脚本的稳定性和可维护性。
3.1 设备连接API:先连上,再说别的
设备连接是所有操作的前提。连不上,后面全是空谈。常见的连接 API 就那么几个,但不同场景下用法差别很大。
| API 名称 | 功能描述 | 典型参数 | 返回值 |
|---|---|---|---|
connect(port, baudrate) |
建立与烧录器的物理连接 | 端口号、波特率 | 连接句柄 / 错误码 |
disconnect(handle) |
断开连接,释放资源 | 连接句柄 | 成功 / 失败 |
is_connected(handle) |
检查连接是否存活 | 连接句柄 | 布尔值 |
set_timeout(handle, ms) |
设置通信超时时间 | 句柄、毫秒数 | 无 |
我个人习惯在脚本开头先做一次「连接自检」——调用 is_connected 确认句柄有效,再往下走。别小看这一步,我曾经在生产线上遇到过 USB 线接触不良,脚本跑了一半才报错,整批板子都得重烧。
小技巧:连接超时别设太短。有些老烧录器上电初始化要 2-3 秒,设 1 秒超时大概率连不上。我一般给 5 秒,稳得很。
3.2 内存读写API:烧录的核心战场
内存读写是烧录脚本里调用最频繁的 API。你想想看,烧录的本质不就是把数据写到芯片的 Flash 或 RAM 里吗?
常见的接口长这样:
// 读内存
int read_memory(handle, address, buffer, length);
// 写内存
int write_memory(handle, address, buffer, length);
// 擦除扇区
int erase_sector(handle, address);
// 校验数据
int verify_memory(handle, address, expected_buffer, length);
这里有个坑,我踩过好几次——地址对齐。很多芯片要求写入地址必须是 4 字节或 8 字节对齐,否则 API 直接返回错误。你写脚本的时候,最好加个对齐检查:
if (address % 4 != 0) {
log_error("地址未对齐,请检查");
return -1;
}
另外,verify_memory 这个 API 很多人不爱用,觉得浪费时间。但我建议你每次烧完都调一下。为什么?因为烧录过程中可能受干扰,数据写进去但读出来是错的。我遇到过最离谱的一次,是产线旁边的电焊机一开,烧录数据就全乱套了。加了校验之后,问题立刻现形。
注意:大块数据写入时,一定要分片。一次写 1MB 和分 1024 次每次写 1KB,稳定性天差地别。分片还能让你在中间插入进度回调,用户体验好得多。
3.3 寄存器操作API:芯片的「控制面板」
寄存器操作,说白了就是直接读写芯片内部的控制位。比如解锁保护、设置时钟、切换启动模式,都得靠它。
典型 API 如下:
| API 名称 | 作用 | 示例 |
|---|---|---|
read_reg(handle, reg_addr) |
读取指定寄存器的值 | val = read_reg(handle, 0xE000_ED00) |
write_reg(handle, reg_addr, value) |
写入指定寄存器 | write_reg(handle, 0x4002_3800, 0x5A) |
modify_reg(handle, reg_addr, mask, value) |
按位修改寄存器(读-改-写) | modify_reg(handle, 0x4002_3804, 0x03, 0x02) |
这里我要重点说说 modify_reg。很多新手喜欢先 read_reg,改完再 write_reg,但这两步之间如果被中断打断,寄存器值可能已经变了。用 modify_reg 是原子操作,安全得多。
我曾经在一个量产项目上,就因为没用 modify_reg,导致偶尔出现芯片锁死。排查了三天,最后发现是中断服务程序改了同一个寄存器。换成 modify_reg 之后,问题再没出现过。
避坑指南:寄存器地址一定要从芯片参考手册里抄,别信网上的二手资料。我见过有人把地址写反了,烧了上百片才发现——全是废片。
3.4 状态查询API:脚本的「眼睛」
没有状态查询,你的脚本就是个瞎子。烧录器到底在忙还是空闲?芯片有没有报错?这些信息全靠状态 API 来拿。
常用的状态查询接口:
get_status(handle)—— 获取烧录器当前状态(空闲/忙/错误)get_error_code(handle)—— 获取上次操作的错误码get_progress(handle)—— 获取当前烧录进度(0-100%)wait_until_idle(handle, timeout)—— 阻塞等待烧录器空闲
我个人习惯在每次写操作之后,都调一次 get_status 确认状态。别等最后才查,那时候已经晚了。比如这样:
write_memory(handle, addr, data, len);
status = get_status(handle);
if (status != IDLE) {
error = get_error_code(handle);
log_error("写入失败,错误码:%d", error);
return -1;
}
嗯,这里要注意:wait_until_idle 虽然方便,但别设无限超时。万一烧录器卡死了,你的脚本也会跟着卡死。我一般设 30 秒超时,超时了就主动复位烧录器。
3.5 知识体系总览
下面这张图,把四类 API 的关系和调用流程串起来了。你写脚本的时候,照着这个结构来,基本不会乱。
3.6 实战:把这些API串起来
光讲理论没意思,咱们看一个完整的烧录流程片段。假设你要烧录一个 STM32 芯片:
// 1. 连接
handle = connect("COM3", 115200);
if (handle == NULL) {
log_error("连接失败");
return;
}
// 2. 解锁寄存器(写保护)
write_reg(handle, 0x40022004, 0x45670123);
write_reg(handle, 0x40022004, 0xCDEF89AB);
// 3. 擦除
erase_sector(handle, 0x08000000);
// 4. 写入(分片,每片1KB)
for (int i = 0; i < total_size; i += 1024) {
write_memory(handle, 0x08000000 + i, &data[i], 1024);
status = get_status(handle);
if (status != IDLE) {
log_error("写入失败,地址:0x%08X", 0x08000000 + i);
break;
}
}
// 5. 校验
if (verify_memory(handle, 0x08000000, data, total_size) == 0) {
log_info("烧录成功!");
} else {
log_error("校验失败");
}
// 6. 断开
disconnect(handle);
你看,整个流程就是按照刚才那张图的顺序来的。连接 → 寄存器配置 → 擦写 → 校验 → 断开。每一步都调了状态查询,确保不出幺蛾子。
最后提醒一句:API 的返回值一定要检查。别偷懒,别假设它一定成功。我见过太多脚本因为没检查返回值,烧了一堆坏片才发现问题。你省的那几行代码,最后都会变成加班时间还回来。