stm32/debug/adc-oled-debug.md
STM32F103 ADC+OLED 调试复盘:从黑屏到出数的两座大山
STM32F103 ADC+OLED 调试复盘:从黑屏到出数的两座大山
2026-09-08 实测验证:PA5 光敏电阻 ADC 采集 → OLED(SH1106)第一行居中显示 zhangshaohau + 实时 ADC 值,遮挡光敏电阻数值随之变化。一晚上连踩两个「程序没跑起来」的大坑,完整记录排查路径、寄存器级根因与一套可复用的调试方法论。
#一、元信息
| 字段 | 值 |
|---|---|
| 芯片/平台 | STM32F103C8T6(HAL 库,ADC 用寄存器级操作,ST-Link 烧录) |
| 接口类型 | ADC(PA5=ADC1_IN5,单次转换、软件触发、12 位右对齐)+ OLED(SH1106,软件 I2C:SCL=PB6,SDA=PB7) |
| 验证状态 | ✅ 已验证(2026-09-08) |
| 验证环境 | 洋桃1号开发板 / Keil MDK(ARMCC)/ ST-Link / 2026-09-08 |
| 驱动方式 | 寄存器级 ADC(adc.c),软件 I2C + 帧缓冲 + 页寻址 OLED(oled.c),printf 重定向至 USART1 调试打印 |
#二、接线表
| 信号 | MCU 引脚 | 模块引脚 | 备注 |
|---|---|---|---|
| 光敏电阻 ADC | PA5(ADC1_IN5) | 分压输出 | 模拟输入,CNF=01/MODE=00 |
| OLED SCL | PB6 | OLED SCL | 软件 I2C 时钟线 |
| OLED SDA | PB7 | OLED SDA | 软件 I2C 数据线 |
| OLED 电源 | 3.3V / GND | OLED VCC / GND | 必须共地 |
| 调试串口 | PA9(TX)/ PA10(RX) | USB-TTL | USART1,115200,8N1 |
#三、踩坑清单
- printf 半主机卡死(OLED 黑屏):main.c 缺少
struct __FILE { int handle; };和FILE __stdout;两个 ARMCC 声明时,printf 走半主机_sys_open永久等待 → main() 从未执行 → OLED 初始化从未运行 → 黑屏。 - ADC 软件触发必须 EXTTRIG=1 + EXTSEL=111:EXTTRIG=0 时写 SWSTART 无效,EOC 永不置位,读值恒为 0xFFFF(65535)。
- SWD 突然连不上目标:先重新插拔 ST-Link(USB 端)/ 冷启动目标板,再怀疑程序占用 PA13/PA14;期间可用 FlyMcu 串口烧录绕行。
- OLED 显示 5535:是 65535(0xFFFF)被显示函数截断,ADC 修复返回真实值后自动正常,无需处理。
- 诊断打印要限制次数:用
static uint8_t diag_cnt只打印 3 次,避免一直刷屏没法复制。
#四、问题一:OLED 一直黑屏
#4.1 现象
OLED 全黑,串口无任何输出。反复断电重启、调初始化顺序、加上电延时均无效。
#4.2 排查过程:先硬件后软件
- 烧录官方示例 hex → OLED 显示正常、ADC 有值 → 硬件(OLED 模块、I2C 接线、供电)没问题,问题在软件;
- 软件方面优先怀疑「程序是否真的跑起来了 / 初始化是否成功」;
- 用调试器定位:OpenOCD 连接 ST-Link 后 halt 程序,读 PC 寄存器:
txt[stm32f1x.cpu] halted due to debug-request, current mode: Thread
pc: 0x080001e4 ← reset halt 后(复位向量,正常)
pc: 0x080005c8 ← resume 跑 1.5s 后 halt(异常!)
对照 Keil 生成的 Demo00.map 查找 0x080005c8:
txt_sys_open 0x080005b9 Thumb Code 20 sys_io.o(.text)
PC 停在 _sys_open(C 库函数),说明程序卡在 C 库底层调用。
#4.3 根因:printf 半主机(semihosting)卡死
- main.c 里虽然写了
fputc(发往 USART1),但缺少 ARMCC 标准库重定向必需的两个声明:
txtstruct __FILE { int handle; };
FILE __stdout;
- 缺少它们时,printf 不会调用
fputc,而是走默认半主机模式,进入 C 库的_sys_open等待调试器响应; - 脱机运行时没有调试器响应,
_sys_open永久等待 → main() 从未执行 → OLED 初始化从未运行 → 黑屏。
#4.4 解决
在 main.c 的 USER CODE PV 区补上声明,printf 走 fputc → USART1:
txt/* ARMCC 标准库 printf 重定向必需:使 printf 走 fputc 而非半主机(_sys_open) */
struct __FILE { int handle; };
FILE __stdout;
这也解释了「最初版能亮、加了打印的版本全黑」——最初版没有 printf,没触发半主机调用。
#五、问题二:ADC 值恒为 65535
#5.1 现象
OLED 显示数字 5535(65535 的截断显示),串口打印 ADC=65535(=0xFFFF,即超时返回值)。用手遮挡光敏电阻,数值不变。
#5.2 排查过程:读寄存器逐项对照
在 main 循环里用串口打印 ADC 状态寄存器(限制只打 3 次避免刷屏):
txtADC=65535 SR=0x0 CR2=0x1 SQR3=0x5 SMPR2=0x38000 CRL=0x44448888
| 寄存器 | 值 | 解读 |
|---|---|---|
| SQR3 | 0x5 | 规则序列通道 = 5(ADC1_IN5)✅ |
| SMPR2 | 0x38000 | SMP5=111=239.5 采样周期 ✅ |
| CRL(PA5) | 0x4 | CNF=01 模拟输入、MODE=00 输入 ✅ |
| CR2 | 0x1 | ADON=1 已上电、CAL=0 校准完成 ✅ |
| SR | 0x0 | EOC 从未置位,转换从未完成 ❌ |
配置全部正确,但转换不完成 → 怀疑触发方式。查官方示例 adc.c,关键在:
txtADC_ExternalTrigConv_None = 0x000E0000 // EXTSEL = 7(无外部触发源)
...
ADC_SoftwareStartConvCmd(ADC1, ENABLE); // 实际置 EXTTRIG|SWSTART
#5.3 根因:STM32F1 的软件触发规则
- F1 规定:EXTTRIG=0 时写 SWSTART 无效,转换永远不会启动;
- 软件触发必须:
EXTTRIG=1+EXTSEL=111(无外部触发),然后写SWSTART; - 我的代码只写了
SWSTART(EXTTRIG=0),所以 EOC 永不置位。
#5.4 解决
adc.c 两处修改:
txt/* 初始化:EXTSEL=111 无外部触发(软件触发必需,同官方 None) */
ADC1->CR2 |= ADC_CR2_EXTSEL;
/* 启动转换:EXTTRIG=1 + SWSTART(同官方 ADC_SoftwareStartConvCmd) */
ADC1->CR2 |= ADC_CR2_EXTTRIG | ADC_CR2_SWSTART;
#六、调试方法论总结
定位问题按这个顺序,能最快收敛:
- 先排除硬件:烧录官方/已知正常的 hex,若功能正常 → 硬件没问题,锁定软件;
- 软件问题优先怀疑「初始化/启动是否成功」:程序跑起来了吗?→ 串口打印「进入 main」、初始化前后打点;或用调试器 halt 看 PC 停在哪(本案例 PC 停在
_sys_open一锤定音); - 外设不工作读寄存器:把 SR / CR / 配置寄存器打出来,与参考手册、官方示例逐位对照;
- 一次只改一个变量:改一个点 → 编译烧录 → 验证 → 再改下一个;
- 保留诊断通道:UART 打印是最低成本的调试手段,但注意 printf 重定向配置要正确。
本次两个 bug 都属于「初始化/启动层面」(printf 半主机、ADC 软件触发),都是程序其实没执行到预期逻辑,而不是逻辑写错——这正说明「先确认跑起来没」的价值。
#七、最终验证
- OLED 点亮,第一行居中显示
zhangshaohau; - ADC 值实时刷新,遮挡光敏电阻数值变小,光照增强数值变大;
- 串口输出
ADC=xxxx,与 OLED 一致。
#八、参考库/链接
- STM32F103 参考手册(RM0008):ADC 章节 EXTSEL / EXTTRIG / SWSTART 说明
- 洋桃1号官方例程:
D:\学习资料\【用户资料】洋桃1号开发板(2)\B5-教学视频示例程序\1-2-28-光敏电阻ADC读取程序\ - OLED(SH1106)驱动档案:/kb/stm32/display/oled-sh1106
- ARMCC printf 重定向 / 半主机(semihosting)说明:Keil 帮助文档 uVision Help → C Library
#九、更新日志
📝共 1 条记录
| 日期 | 类型 | 更新内容 |
|---|---|---|
| 2026-09-08 | 新增 | 首次建档(由博客文章迁移至知识库) |