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 的关系和调用流程串起来了。你写脚本的时候,照着这个结构来,基本不会乱。

烧录脚本核心API体系 烧录脚本核心API体系 烧录脚本入口 设备连接API 内存读写API 寄存器操作API 状态查询API connect / disconnect is_connected / set_timeout read_memory / write_memory erase_sector / verify_memory read_reg / write_reg modify_reg(原子操作) get_status / get_error_code get_progress / wait_until_idle 调用顺序建议 连接 → 寄存器配置(解锁/时钟) → 内存擦写 → 校验 → 状态确认 → 断开 个人经验 每次写操作后查状态,每次寄存器修改用modify_reg, 大块数据分片写入,超时设置不低于5秒。

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 的返回值一定要检查。别偷懒,别假设它一定成功。我见过太多脚本因为没检查返回值,烧了一堆坏片才发现问题。你省的那几行代码,最后都会变成加班时间还回来。