简介:面向高校《嵌入式系统》课程学习者的一份实验报告文档,基于苏州大学金葫芦平台与AHL-STM32L431开发套件编写,聚焦六个验证性实验,适合课程实验对照、报告撰写参考与期末复习。内容覆盖GPIO输入输出与开发环境使用、串口通信及中断组帧、PWM波形配置、Flash读写擦除、SPI总线通信以及独立看门狗复位机制,每个实验均记录实验目的、软硬件准备、操作过程、关键截图与实验总结,并包含修改程序参数后观察现象、分析结果的完整思路。资源包仅含1个docx文件,大小约10.04MB,图文排版集中,便于按实验章节检索和摘录。目前已有400人学习下载。读者可据此梳理嵌入式开发从环境搭建、编译下载到外设构件调用的完整流程,理解硬件与软件交互方式,也可作为实验排错和知识点查漏补缺的参照,需注意其仅含验证性实验内容。
1. 六个验证性实验的知识地图:一条从点灯到看门狗的主线
嵌入式系统实验这门课,六个验证性实验摆在一起像六件互不相干的事:点灯、串口、PWM、Flash、SPI、看门狗。真按顺序做完一遍会发现它们其实是一条链子——先让一个 .hex 文件真的落进板子的 Flash 并跑起来(实验一),再让板子学会和 PC 说话(实验二),然后用定时器把时间变成波形(实验三),用 Flash 把数据留下来(实验四),用 SPI 把字节送给外部器件(实验五),最后用独立看门狗给整套系统留一条退路(实验六)。这条链子的每一环本质相同:CPU 通过一组寄存器去指挥一个外设,GPIO 是最简单的一种,看门狗是最容易被忽视的一种。这份笔记只覆盖验证性部分,设计性和进阶实验不在其中,适合刚做完实验要写报告、或者考前想把六个实验从头捋一遍的人。
2. 开发环境与 GPIO:第一个工程从导入到跑通
实验一看着最没技术含量,实际是整个课程的地基。后面五个实验用的是同一套流程:导入工程、编译、串口更新、观察现象,只是换了个工程名和现象。所以第一节把这条流水线拆开讲清楚,后面就能直接套用;这一节没打通,后面五个实验会反复卡在同一个地方。
2.1 导入、编译、串口更新:三段式流程里的四个前提
一套完整的下载链路是这样的:IDE 把 C 源码编译成 .hex,通过 Type-C 口走串口把 hex 送进板子上的引导程序,引导程序把它写进 Flash,复位后 CPU 从 Flash 取指执行。这条链路里没有独立的调试探针参与,所以断点、单步、变量观察窗统统没有,程序跑成什么样只能看灯和串口打印。想通这一点,“为什么我的代码没反应”这类问题就有了一半的排查方向。
流程本身只有三步,但每一步都有前提:
| 步骤 | 操作 | 前提与关键点 | 失败表现 |
|---|---|---|---|
| 导入 | 文件菜单导入工程 | 工程目录不要有中文、空格、超长嵌套 | 导入后文件树空、编译报找不到头文件 |
| 编译 | 编译工程 | 模板工程原样复制后重命名,别改模板本身 | 编译通过但产物时间戳还是旧的 |
| 连接 | 串口更新里点连接 | Type-C 线接底板而不是只接供电口 | 扫描不到目标,或提示的串口号不对 |
| 下载 | 选择 Debug 目录下的 .hex 后更新 | 一次只接一块板子,串口不被其他工具占用 | 提示更新失败、更新后现象不变 |
配套 IDE 是 4.55 版,连接成功后会回显“串口号 + 引导程序版本号”,这两个信息建议每次都记在实验记录里。串口号能定位你连的是哪块板子,版本号能确认通信链路真的通了——很多人失败在这里却以为是代码问题,其实是串口被串口调试工具提前占用了,必须先关掉占用再连接。
工程目录的整理方式值得单独说一句。按“分门别类,各有归处”的原则,每个实验一个顶层文件夹,其下再分工程、文档、截图;模板工程复制一份出来重命名,永远保留一份干净模板。血泪经验是:直接在模板上改,改到第三个实验时已经想不起原始版本长什么样,想回退都没得回退。
# 建议的目录结构(每个实验一套,模板单独留一份) embedded-lab/ ├── 00-template/ # 干净模板,只读不改 ├── lab01-gpio/ │ ├── project/ # 复制自 00-template 并重命名 │ ├── shots/ # 现象截图 │ └── notes.md # 参数记录:串口号、占空比、超时值 ├── lab02-uart/ └── ...这个结构解决的是复习时的检索问题:考前翻笔记,一眼能看到每个实验改了哪个参数、现象是什么,而不是在十几个同名工程里靠时间戳猜。
2.2 GPIO 的输出与输入:写 1 不一定亮
GPIO 是通用输入输出口的缩写,听起来简单,但它是理解“构件”这个概念最好的入口。一个引脚要工作,至少要定四件事:方向(输入还是输出)、输出类型(推挽还是开漏)、上下拉(浮空、上拉、下拉)、速度。这四件事在寄存器层面分别对应模式寄存器、输出类型寄存器、上下拉寄存器和速度寄存器,在构件层面则被封装成一两个初始化函数加几个读写函数。
| 寄存器 | 管什么 | 常踩的坑 |
|---|---|---|
| 模式寄存器 | 输入/输出/复用/模拟 | 配成复用功能才能输出 PWM、SPI 波形 |
| 输出类型寄存器 | 推挽 / 开漏 | 开漏不加上拉,输出高电平时是悬空的 |
| 上下拉寄存器 | 内部上拉、下拉 | 按键输入不配上拉,读数随机跳 |
| 输出数据寄存器 | 输出电平 | 写 1 亮还是写 0 亮,取决于 LED 接法 |
| 置位复位寄存器 | 原子置位/复位 | 多任务里改单一引脚用它,别读改写整个端口 |
| 输入数据寄存器 | 读引脚实际电平 | 读的是引脚,不是输出寄存器,两者可能不一致 |
“写 1 灯不亮”是实验一最常见的翻车点,原因通常有两个:一是 LED 是共阳极接法,引脚输出低电平才导通,代码里逻辑反了;二是引脚压根没配成输出模式,写进去的值停在输出寄存器里,引脚没动。解决办法很朴素——先确认电路接法,再看模式寄存器的值,最后才怀疑代码逻辑。
输入侧同理。按键接在引脚上,如果配成浮空输入,手没碰的时候引脚电平是玄学,读数随机跳;配上拉或下拉才有确定的默认状态。机械按键还有抖动,按下瞬间电平会在高低之间弹跳几毫秒,软件上要么延时再读一次,要么连续采样几次一致才认。实验里通常用最简单的延时消抖,但复习时要清楚这不是唯一方案。
/* 按键读取的典型写法:上拉输入 + 简单消抖,按键按下为低电平 */ int key_pressed(void) { if (gpio_read(KEY_PIN) == 0) { /* 上拉输入,按下拉低 */ delay_ms(10); /* 跳过机械抖动窗口 */ if (gpio_read(KEY_PIN) == 0) /* 二次确认,防误触发 */ return 1; } return 0; }这段代码的关键参数是那 10 毫秒:太短跳不过抖动,太长手感变钝。实际工程里更常见的是状态机消抖或者定时器扫描,实验阶段用延时足够,但要能说出它的代价——延时期间 CPU 什么都干不了。
2.3 printf 调试与 C/汇编互调:没有调试器时的黑匣子
没有断点可用的时候,printf 就是唯一的黑匣子。但 printf 默认输出到标准输出,在单片机上这个“标准输出”不存在,所以要把它重定向到串口:重写底层单字符输出函数,让每个字符走串口发送寄存器。没做重定向就调用 printf,程序可能直接卡死在里面等一个永远不会来的输出设备,现象是“灯不闪了、也没打印”,很容易误判成死机。
重定向之后,printf 的用法和 PC 上一样,但要注意两点:一是在中断服务程序里别打印,串口发送是慢操作,会把中断拖到几十微秒甚至更久;二是打印本身会改变时序,某些对时间敏感的现象(比如 PWM 呼吸的流畅度)加了打印就看不出来了,调试验证完记得删掉或条件编译掉。
实验一里还有一段汇编样例,目的是让 C 和汇编互相调用。规则不复杂,但每一条都容易错:
/* 汇编侧:实现一个两数相加的函数,供 C 调用 */ EXPORT asm_add /* 导出符号,C 里才能 extern 声明 */ AREA |.text|, CODE, READONLY asm_add ADD r0, r0, r1 /* 第 1、2 个参数在 r0、r1,结果放回 r0 */ BX lr /* 返回,lr 里是调用点地址 */ END要点有三个:参数按顺序放在 r0 到 r3,超过四个走栈;返回值放 r0;返回用 BX lr 而不是普通跳转。C 侧只要写extern int asm_add(int, int);就能像调用普通函数一样用它。真正容易忽略的是栈对齐——调用约定要求栈指针保持 8 字节对齐,汇编函数里如果自己压栈又没管对齐,浮点运算或者库函数调用就可能出问题。实验阶段这个坑不明显,但复习时要记住它存在。
3. 串口通信与中断:从单字节回显到一帧数据
实验二开始接触“异步事件”这个概念。前面点灯是主动轮询,程序想什么时候读就什么时候读;中断反过来,外设有事了才叫 CPU 一声。这一个字的差别,决定了后面所有外设的编程模型。
3.1 中断三要素:使能、标志、清标志
任何一个中断要真正跑起来,必须同时满足三件事:外设自己的中断使能位打开、中断控制器里对应的通道使能、全局中断打开。三个漏一个,现象都是“中断进不去”,而且从代码上很难一眼看出来。排查顺序建议从外设使能位开始,逐级往上查,因为最外层通常是默认开着的。
中断进来之后,第一件事是清标志。串口接收中断的标志通常在读数据寄存器时自动清,但发送完成之类的标志需要手动写清除;如果忘了清,退出中断的瞬间又会重新进一次,现象是程序卡在中断里出不来,主循环完全停摆——这种“看起来死机”的情况,九成以上是标志没清或者清错了位。
/* 串口接收中断服务程序:读一个字节、回一个字节,并做最简单的处理 */ void uart_rx_isr(void) { uint8_t ch = uart_read_data(); /* 读数据寄存器,同时清接收标志 */ if (ch == 0x0D) { /* 收到回车,认为一帧结束 */ frame_ready = 1; /* 只置标志,处理留给主循环 */ return; } if (rx_len < RX_BUF_SIZE) /* 防溢出,缓冲区满则丢弃 */ rx_buf[rx_len++] = ch; }这段代码最值得抄走的不是语法,而是“中断里只做搬运、不做处理”的分工:中断负责把字节放进缓冲区、置一个标志,主循环看到标志再去解析。这样中断停留时间控制在几微秒,不会影响其他中断的响应。实验里那个“收到一字节减 3 再发回”的小改动,本质就是在这个框架里把回发值改一下,现象是输入 7 收到 4——能对上,说明收发链路和你的处理逻辑是通的。
3.2 组帧:单字节回显和一帧数据的差距
单字节收发只能验证链路通不通,实际通信一定是一帧一帧的。所谓组帧,就是双方约定一套格式,让接收方知道“从哪开始、有多长、对不对”。最常见的结构是帧头、长度、负载、校验四段:
| 字段 | 长度 | 作用 | 设计要点 |
|---|---|---|---|
| 帧头 | 1~2 字节 | 标识一帧开始 | 选一个负载里不常出现的值,如 0xAA 0x55 |
| 长度 | 1 字节 | 负载字节数 | 必须校验上限,防止伪造长度导致缓冲区溢出 |
| 负载 | N 字节 | 真正的数据 | 定长还是变长,决定了接收端要不要状态机 |
| 校验 | 1 字节 | 累加和或异或 | 能发现单字节错误,别指望它纠错 |
接收端解析最好写成状态机:等帧头、收长度、收负载、验校验,任何一步对不上就回到等帧头。这样即使中间丢了字节,也能自动重新同步,不会一直错位。另一个必备机制是超时——收到半个帧之后对方不发了,状态机要能在一段时间后自己复位,否则会永远卡在“收负载”状态。
提示:帧头选值要避开负载的高频值。如果负载里全是 0x00 和 0xFF,帧头却选了 0x00,接收端会频繁误判成新帧开始,现象是数据莫名其妙被截断。
3.3 上位机侧:串口工具和自写上位机的差别
实验里先用 IDE 自带的串口工具验证,这一步只验证板子会不会说话,参数必须和板子完全一致:波特率、数据位、停止位、校验位、流控。这五项里任何一项不同,收到的就是乱码或者什么都收不到。另外串口工具通常有“十六进制显示”和“ASCII 显示”两种模式,发数字的时候要看清楚——发“7”这个字符送出去的是 0x37,发数值 7 送出去的是 0x07,减 3 之后的结果完全不是一回事,这是实验二最常见的错。
自写上位机(课程里用 C# 的串口类)要解决的是两个新问题。第一个是线程:串口接收事件在后台线程触发,直接在里面更新界面会抛跨线程异常,必须切回界面线程。第二个是粘包:串口是字节流,一帧数据可能分两次到达,也可能两帧挤在一次事件里,所以上位机侧同样要有一个和板子对称的组帧状态机,不能假设“一次接收事件等于一帧”。
// 上位机侧:接收事件里只做入缓冲,解析统一放到独立方法,避免跨线程问题 private void OnDataReceived(object sender, SerialDataReceivedEventArgs e) { int n = serialPort.BytesToRead; byte[] buf = new byte[n]; serialPort.Read(buf, 0, n); // 先全部读出来,别在事件里解析 lock (rxLock) { rxBuffer.AddRange(buf); } BeginInvoke(new Action(ParseFrames)); // 切回界面线程再处理 }这段的关键点在于“读”和“解析”分离:事件里只管把字节搬进缓冲区,解析交给界面线程,两边用锁保护共享缓冲区。这是串口上位机最容易出问题的地方,也是答辩时最容易被追问的地方。
4. 构件化外设四连:PWM、Flash、SPI 与看门狗
实验三到实验六共享同一个套路:找一个构件、看它的初始化参数、改一个参数、观察现象变化。所谓“构件”,可以理解成把某个外设的寄存器操作打包成一组函数,初始化传结构体,使用调接口。复习的时候不要只记住函数名,要能说出每个参数落到哪个寄存器、改大了会怎样。
4.1 PWM:频率、占空比与定时器的三个参数
PWM 是用数字引脚模拟“中间电压”的手段:引脚高速开关,开关的快慢决定频率,高电平占一个周期的比例决定占空比。灯看起来的亮度、电机看起来的转速,实际感受的是占空比,而不是频率——但频率太低会看到闪烁,太高又会让开关损耗变大,所以常见的 LED 调光落在几百赫兹到几千赫兹。
定时器产生 PWM 靠三个参数配合:
| 参数 | 含义 | 影响 |
|---|---|---|
| 预分频值 | 把定时器时钟先除一次 | 决定计数快慢,间接影响频率和分辨率 |
| 自动重装值 | 计数器数到多少归零 | 与预分频一起决定周期,即频率 |
| 比较值 | 计数值小于它时输出有效电平 | 直接决定占空比 |
三者的关系可以写成两条式子,背下来比记结论有用:频率 = 定时器时钟 ÷ ((预分频 + 1) × (重装值 + 1)),占空比 = 比较值 ÷ (重装值 + 1)。
/* 以 80MHz 定时器时钟为例,目标 1kHz、占空比可调 */ #define TIM_CLK 80000000UL #define PWM_FREQ 1000UL /* 1kHz,人眼看不到闪烁 */ #define PRESCALER 79UL /* 80 分频,计数时钟 1MHz */ #define PERIOD (TIM_CLK / ((PRESCALER + 1) * PWM_FREQ)) /* 重装值 = 999 */ pwm_init(PWM_CH1, PRESCALER, PERIOD - 1); pwm_set_duty(PWM_CH1, PERIOD * 30 / 100); /* 30% 占空比 */参数怎么改,取决于你要什么:想调亮度,只动比较值;想改频率,动预分频或重装值,但同时会改变分辨率——重装值越小,能调的占空比档位越少,1000 档和 100 档的顺滑程度完全不是一个体验。实验里改一个参数看现象,改的通常就是比较值,这也是最安全的一个。
两个易忽略的硬件前提:PWM 输出引脚必须配成复用功能模式,配成普通输出是出不来波形的;占空比设成 0% 或 100% 时,有的定时器配置会输出恒定电平而不是“几乎没有”的波形,边界值要单独验证,别拿它当线性变化的证据。
4.2 Flash:擦除边界与写入粒度
Flash 和内存最大的区别是:写之前必须先擦,而且擦除的最小单位远大于写入的最小单位。以常见的 2KB 页为例,擦除一次至少是一页,写入则可以按字或双字进行。这意味着改一个字节,实际流程是“把整页读到内存、改掉那个字节、擦掉整页、把整页写回去”。
| 操作 | 粒度 | 说明 |
|---|---|---|
| 擦除 | 页(如 2KB) | 擦完后整页变成全 1,即 0xFF |
| 写入 | 字 / 双字 | 只能把 1 改成 0,不能把 0 改回 1 |
| 读取 | 字节 | 和普通内存读一样,但等待周期要配置 |
三条必须记住的规则:不擦就写,写进去的数据是原值与新值的按位与,结果不可预期;跨页写,超出当前页的部分会写到下一页的起始地址,把别的数据覆盖掉;擦写期间 CPU 取指会暂停,如果代码和数据在同一块 Flash 上,擦写时间过长会导致中断响应延迟,所以擦写操作要避开高频中断。
还有个寿命问题:Flash 的擦写次数有限,通常标称一万次量级。实验里随便折腾没关系,但如果设计成“每次采集都存一次”,几小时就能把一个扇区写废。正常做法是攒够一批再写,或者把写入位置在扇区之间轮转,不要盯着同一个地址反复擦写。
4.3 SPI:四种模式与一条没有应答的总线
SPI 是同步串行总线,四根线:时钟、主出从入、主入从出、片选。和串口最大的不同是它有独立的时钟线,所以速度快得多,常见几兆到几十兆;另一个不同是它没有应答机制,主设备发出去的字节,从设备收没收到、对不对,协议层面完全不知道。
最容易卡住的是时序模式。时钟极性和时钟相位两个位组合出四种模式:
| 模式 | 时钟极性 | 时钟相位 | 采样时刻 |
|---|---|---|---|
| 0 | 空闲低 | 第一个边沿采样 | 上升沿采样 |
| 1 | 空闲低 | 第二个边沿采样 | 下降沿采样 |
| 2 | 空闲高 | 第一个边沿采样 | 下降沿采样 |
| 3 | 空闲高 | 第二个边沿采样 | 上升沿采样 |
主从双方的模式必须一致,不一致的现象是收到的数据整体错位一位,看起来像“数据都对,就是乘以 2 或者除以 2”。这是 SPI 调试里最经典的坑,血泪经验是:现象像移位错误,先怀疑模式,别怀疑接线。片选也要注意,多数器件要求片选在整个字节传输期间保持有效,中途拉高会把一次传输截断成两半。
调试顺序建议固定下来:先降速到几百千赫兹确认能通,再逐步往上提;先用回环(把主出和主入短接)验证主设备配置正确,再接真实器件。SPI 没有应答,一旦不通你无法区分是“发错了”还是“器件没理你”,回环是唯一的隔离手段。
4.4 独立看门狗:超时公式与喂狗位置
看门狗是一个独立计数的定时器,程序必须在它数满之前“喂”一次,让它重新计数;如果程序跑飞、卡死、进了死循环,喂狗动作停了,计数器溢出就触发复位,把系统救回来。独立看门狗之所以“独立”,是因为它由内部低速时钟驱动,主时钟挂了它照样工作。
超时时间由预分频和重装值共同决定,以 32kHz 左右的低速时钟为例,计数时钟先被 4 固定分频,再经过可编程预分频:
/* 独立看门狗超时估算:t = (4 * 2^PR * (RLR + 1)) / f_LSI */ #define LSI_FREQ 32000UL /* 内部低速时钟,实际有 ±% 偏差 */ #define IWDG_PR 3 /* 预分频系数选择位,PR=3 即 32 分频 */ #define IWDG_RLR 1249 /* 重装值,喂狗时重新装载 */ /* 超时 ≈ (4 * 2^3 * (1249 + 1)) / 32000 ≈ 1.25 秒 */三个要点:低速时钟精度差,标称 32kHz 实际可能在 26~34kHz 之间浮动,所以算出来的 1.25 秒只是估算,要留足余量;喂狗必须放在主循环里,如果放在定时器中断里,主循环卡死而中断照跑,看门狗就形同虚设;调试时 CPU 被暂停,看门狗仍在计数,很容易刚连上就复位,需要在调试配置里选择暂停时冻结看门狗。
复位之后要能区分“上电复位”和“看门狗复位”,芯片通常有复位状态寄存器记录复位来源,读一下就知道这次重启是正常上电还是被狗咬的。这一点在实验报告里写清楚,比只写“程序会重启”有价值得多。
5. 易漏点排查:六个实验里最容易翻车的五处细节
这些点单独看都不难,难在它们发生时表现得很像别的问题。把它们列在一起,是为了在现象出现时能快速对号入座。
5.1 易漏点一:路径里的中文、空格和超长嵌套
为什么漏:工程能导入、能打开,看起来一切正常,直到编译或下载时才报错,报错信息又往往指向“找不到某个文件”,把注意力引向代码。
怎么补:工程根目录用纯英文加下划线,层级控制在三层以内,从模板复制的时候顺手检查一遍路径。已经踩了的,把整个文件夹挪到盘符根目录下再试,能通就说明是路径问题。
5.2 易漏点二:改了代码没重编,或下载了旧的 hex
为什么漏:编译和下载是两个按钮,中间还夹着“选择文件”这一步。Debug 目录里堆着好几个同名 hex 时,选错一个太正常了,现象是“代码明明改了,板子行为没变”。
怎么补:形成固定动作——改代码、编译、看编译输出确认无错、进 Debug 目录看 hex 的修改时间是不是刚刚、再下载。时间戳是最便宜的后悔药,比反复检查代码快得多。
5.3 易漏点三:中断里做耗时操作
为什么漏:代码逻辑是对的,功能也能实现,只是系统变慢或者偶发卡顿,很难联想到是中断待太久。串口打印、延时、Flash 擦写,写在中断里都能“正常工作”,代价藏在时序里。
怎么补:中断里只做三件事——读数据、存缓冲、置标志,其余全部挪到主循环。判断标准很简单:中断函数里出现循环、延时、打印中的任何一个,就该重构。
5.4 易漏点四:现象和参数没有一一对应地记录
为什么漏:做实验时改动是连续的,改完占空比看灯亮一点,再改预分频看灯闪,最后写报告时已经说不清哪个参数对应哪个现象,只能写“观察到了预期现象”。
怎么补:每改一个参数就记一行:参数名、原值、新值、现象。截图也要把参数界面一起截进去,只截现象不截参数的图,事后没有任何证明力。
5.5 易漏点五:Flash 没擦就写、或者写入跨页
为什么漏:写入函数返回成功,读回来也“有数据”,但数据是错的——因为写进去的是原值和新值的按位与结果,读出来不是 0xFF 就是混合值,看起来像数据损坏而不是操作顺序错误。
怎么补:把“先擦后写”做成固定流程,擦除后读一遍确认全 0xFF 再写;写入前算一下起始地址加长度有没有跨过页边界,跨了就拆分或者换起始地址。
6. 复习清单:考前把六个实验过一遍的顺序与自查表
复习这六个实验,最有效的顺序不是按实验编号,而是按“输入—处理—输出”三问过一遍。任何一个实验,先问它的输入是什么(按键、串口字节、定时器溢出、外部器件返回的数据),再问处理逻辑在哪(主循环还是中断,状态机有几个状态),最后问输出是什么(引脚电平、串口字节、PWM 波形、Flash 里的数据)。三问答不上来,就说明这个实验只是照着步骤点完了,没真正吃透。
第二个动作是把六个实验的参数横向对比一遍。同样是“改一个参数看现象”,PWM 改的是比较值,串口改的是波特率或者回发值,看门狗改的是重装值,Flash 改的是写入地址,改的东西不同,但“参数—寄存器—现象”这条链条是一样的。能把这五条链子各自说清楚,基本就稳了。
| 实验 | 核心构件 | 一句话考点 | 最常见错误 |
|---|---|---|---|
| 一 环境与 GPIO | 通用输入输出 | 方向、上下拉、输出类型四件事配齐 | 写 1 不亮——接法反了或没配输出模式 |
| 二 串口与中断 | 串口 + 中断控制器 | 中断三要素齐全,标志要清 | 中断里打印,或发的是字符不是数值 |
| 三 PWM | 定时器 | 频率与占空比的两条公式 | 引脚没配复用功能,没波形 |
| 四 Flash | 片上 Flash | 先擦后写,别跨页 | 没擦就写,读回数据不对 |
| 五 SPI | 同步串行总线 | 主从模式必须一致 | 模式不匹配,数据整体移位 |
| 六 独立看门狗 | 独立看门狗 | 超时公式与喂狗位置 | 喂狗放在中断里,主循环卡死也不复位 |
第三个动作是准备三个“如果”。如果串口收不到数据,你从哪一步开始查?如果 PWM 没波形,先看引脚模式还是先看参数?如果板子反复重启,怎么确认是看门狗而不是供电问题?这三个问题答得出来,说明排查路径是通的,而不是只会复现成功路径。
最后一个习惯:每个实验留一份最小可复现记录,包含工程路径、关键参数值、现象截图、改过的代码行。做完实验六再回头补实验一的记录,八成会漏细节。
我自己的教训是实验二那次,改完回发逻辑直接下载,结果现象死活不对,折腾了半小时才发现下载的是上一版 hex。从那以后我每次下载前都强制走一遍“看编译输出、看 hex 时间戳、再点更新”的三步,再没在这上面浪费过时间。希望帮到你。
本文还有配套的精品资源,点击获取