STM32F103实战:ADC电压采集+LCD显示+串口通信完整教程
2026/9/9 2:06:58 网站建设 项目流程

简介:基于STM32F103ZET6的实践工程,围绕模拟数字转换器采集电压、液晶屏显示和串口通信发送三个核心功能展开,适合希望快速掌握STM32标准外设库驱动与数据通信的嵌入式学习者。压缩包共88个文件,其中C源码与头文件共75个,另含Hex固件、Keil工程配置、汇编启动文件及说明文档,压缩后仅241KB;目录按硬件、系统、用户等功能模块划分,便于直接查看与烧录验证。目前已有12000余人学习浏览,例程可用于多通道模拟采样、液晶显示驱动移植以及串口打印调试等常见场景。整体以可运行例程为基础,保留完整外设初始化与数据处理代码,能清晰展示从模拟电压采集、液晶屏刷新显示到串口上送主机的完整数据链路,适合初学者对照学习,也便于二次开发。 搞嵌入式开发有一个项目我特别推荐新手亲手做一遍:用 STM32F103 的 ADC 采集电压,把数值同时送到 LCD 液晶屏显示,再通过串口发到电脑上。这个题目看起来基础,但一次把单片机开发里三个最常用的外设全串起来了——ADC 模拟采集、LCD 显示、UART 串口通信,每一个在后续实际项目里几乎都会碰到。完成这个项目,你对 STM32F103 的认知基本就从一个"会点灯的芯片"升级到了"能采集、能显示、能上报的小系统"。

对于刚开始接触 STM32F103 的朋友来说,这个项目最大的价值在于它把"模拟世界"和"数字世界"打通了。你把一个电位器的输出电压接到单片机上,ADC 负责把电压变成数值,LCD 负责把数值变成人眼能直接读到的内容,串口则负责把同一份数据交给电脑处理。整个过程相当于做了一个最简化版的数据采集终端,哪怕你以后去做环境监测、电池电压检测、传感器数据读取,核心结构也还是这些东西。

这篇文章我会从硬件准备讲到具体代码,再讲到调试过程中容易踩的坑,全程使用标准外设库(StdPeriph_Lib)来实现。涉及 ADC 采样周期、参考电压、LCD 中文显示、串口乱码这些高频关键词对应的实际问题,我都会结合自己的经验一一拆开来讲。

1. 项目整体设计与硬件准备

1.1 为什么选 STM32F103 来做这个采集显示系统

STM32F103 是我见过最"皮实"的入门级 ARM 单片机之一。它基于 Cortex-M3 内核,主频 72MHz,片内集成 12 位逐次逼近型 ADC、多个 UART、I2C、SPI 等丰富外设,价格也足够便宜。网上资料堆积如山,从最小系统电路图到标准库函数说明一搜一大把,遇到问题非常容易找到参考。最关键的还是它内核稳定,可用的中文参考手册和例程极多,对新手非常友好。

选择 STM32F103 搭配"ADC + LCD + 串口"这套组合,其实对应着实际工程里最典型的一条数据链路:传感器或信号源被 ADC 采集,主控处理后一方面本地显示,另一方面通过串口上报给上位机或云端网关。整条链路练熟了,后面做 Modbus 采集模块、电池管理系统、医用小设备的数据面板,基本都是同一套骨架。

1.2 最小系统搭建与器件清单

做这个项目之前要先有一块能跑起来的 STM32F103 最小系统板。最小系统其实不复杂,核心就四部分:3.3V 电源8MHz 外部晶振(也可以只用内部 HSI,但为了串口波特率准确还是建议外接晶振)、复位电路BOOT 启动配置。现在市面上卖的 STM32F103 核心板基本都是画好的最小系统,直接插上 USB 就能供电和下载程序,省去了自己画板的麻烦。

如果要自己搭最小系统,需要注意给 VDD 和 VDDA 引脚都接上 0.1uF 去耦电容,位置要尽量靠近芯片引脚。VCAP 引脚通常要接一个 2.2uF 钽电容到地,这个很容易被忽略,但它影响内部稳压器工作,接不好芯片可能压根启动不了。复位脚 NRST 用 10k 电阻上拉到 3.3V,再接一个 100nF 电容到地,按键按下拉低复位即可。

器件清单我建议这样准备:

器件型号/规格用途
主控STM32F103C8T6 核心板系统控制核心
电位器10k 多圈精密电位器提供 0~3.3V 可调电压源
LCD 屏LCD1602 字符液晶(可选 OLED)本地显示电压值
串口模块USB 转 TTL,CH340 芯片连接电脑串口
杜邦线若干公对公/公对母连接各模块
电源5V USB 供电 + AMS1117-3.3给核心板供电

这里我特别提醒一点:输入给 ADC 的电压绝对不能超过 VDDA(即 3.3V),否则轻则读数不准、重则烧掉 ADC 引脚。如果需要采集 5V 或更高电压,务必加分压电阻把电压按比例降到 3.3V 以内。

1.3 LCD 和串口模块怎么接,IO 分配建议

LCD1602 是很常用的字符液晶屏,适合显示电压这种纯文本数据。接线方式有两种,8 位并行模式4 位并行模式。8 位模式一次传一个字节,需要的 GPIO 多;4 位模式分两次传输高四位和低四位,能省 4 个 IO,速度略慢但对静态显示完全够用。我的建议是直接用 4 位模式,把宝贵的 IO 留给后续扩展。

我实际用的接线分配如下,LCD 的 RS 接 PA1、RW 接 PA2、EN 接 PA3,数据口 D4~D7 接 PA4~PA7。这个分配没有硬性限制,方便布线即可。需要注意 RW 脚如果确定只用写入方向,可以直接接地,省掉一个 GPIO。VO 对比度调节引脚接一个 10k 电位器到地,调整出清晰的显示效果。

串口模块的核心是 USB 转 TTL 芯片。CH340 是最常见、驱动安装最方便的一种,兼容性好,在 Windows 下装好驱动后会自动识别出一个 COM 口。接线时把 CH340 模块的 RX 接 STM32F103 的 PA9(USART1_TX),TX 接 PA10(USART1_RX),地线必须共地——这是新手最容易漏掉的一步,模块和开发板各供各的电,串口怎么调都是乱码或无数据。

电位器的三个引脚接线也很简单:两端分别接 3.3V 和 GND,中间抽头接 PA0(ADC1 通道 0)。这样旋转电位器时,PA0 上的电压就从 0V 到 3.3V 线性变化,正好对应 ADC 的量程。

2. ADC 采集核心原理与配置

2.1 12 位逐次逼近型 ADC 的工作逻辑

STM32F103 的 ADC 是12 位逐次逼近型(SAR)转换器,它内部有一个比较器和一个由 DAC 生成的参考电压,通过二分法逐步逼近输入电压。概念上很像"猜数字"游戏:你先猜一个中间值,然后看比目标大还是小,不断折半逼近。12 位分辨率意味着转换结果用 0~4095 的数字表示 0~参考电压范围内的模拟值,所以当参考电压是 3.3V 时,每个最小数字代表 3.3V / 4096 ≈ 0.8mV。

ADC 采样还有个容易混淆的概念——采样周期。STM32F103 的 ADC 采样周期可配置为 1.5、7.5、13.5、28.5、41.5、71.5、239.5 个 ADC 时钟周期。采样周期越长,采样保持电容的充电时间越充裕,对于内阻较大的信号源来说读数更准确;但相应地转换速度会变慢。对于采集电位器这种信号,信号源内阻一般也就几 k 欧姆,用默认的 7.5 或 13.5 周期完全没问题。我实际项目里给 NTC 热敏电阻采样时会用 55.5 或 239.5 周期,因为 NTC 接分压网络后的等效内阻通常更高,采样时间不足会导致读数明显偏小。

ADC 时钟也有讲究。STM32F103 的 ADC 最高时钟是 14MHz,而 APB2 时钟一般是 72MHz,所以 ADC 预分频最低只能选 6 分频(72/6=12MHz),不能选 2 分频(72/2=36MHz 超出上限)。很多人第一次写代码时把 ADC 时钟配到 36MHz,虽然程序能跑,但转换结果的线性度和稳定性都会变差。

2.2 采样周期与参考电压:参数怎么选才靠谱

参考电压直接决定 ADC 读数和真实电压的换算公式。STM32F103 的最小系统板通常把 VREF+ 引脚直接接 VDDA,也就是 3.3V。这时换算公式就是:

实际电压(V)= ADC读数 × 3.3 / 4095

如果追求更高精度,可以用外部基准源给 VREF+ 提供一个精确电压,比如 2.5V 或 3.0V 的高精度基准。但大多数学习和一般测量场景下,直接用 3.3V 就够了,只要确认核心板上的稳压芯片输出的确实是 3.3V 就行。

ADC 有规则通道注入通道两类,普通单通道采集只用规则通道就够。规则通道可以配置为单次转换或连续转换模式。单次转换模式下,每次触发只转换一次,读完结果后需要再次触发;连续转换模式下,ADC 会不断循环采样并更新数据寄存器,对实时监控场景更方便。我建议本项目的初始版本先用单次转换轮询,逻辑简单,调试方便。

2.3 基于标准库的 ADC 初始化配置代码

STM32F103 的 ADC 库函数主要涉及 GPIO 和 ADC 外设两个部分。GPIO 需要把 PA0 配置为模拟输入,注意是模拟输入而不是浮空输入或上拉输入,否则内部电路结构会干扰模拟信号。ADC 配置代码如下:

void ADC_GPIO_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_ADC1, ENABLE); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; GPIO_Init(GPIOA, &GPIO_InitStructure); } void ADC_Config(void) { ADC_InitTypeDef ADC_InitStructure; ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; ADC_InitStructure.ADC_ScanConvMode = DISABLE; ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; ADC_InitStructure.ADC_NbrOfChannel = 1; ADC_Init(ADC1, &ADC_InitStructure); ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_13Cycles5); ADC_Cmd(ADC1, ENABLE); }

读取 ADC 数值的代码很简洁,启动一次转换,等 EOC 标志位置位后直接取数据寄存器:

uint16_t ADC_GetValue(void) { ADC_SoftwareStartConvCmd(ADC1, ENABLE); while (ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET); return ADC_GetConversionValue(ADC1); }

这里我再强调一点:每次读取后都要重新软件触发下一次转换,这是很多人调试时困惑的地方——为什么数据一直不变?因为你读完之后没有重新启动转换,ADC 就停在原地不动了。

3. LCD 显示电压数据

3.1 屏幕选型:LCD1602 还是 OLED

显示部分我用过 LCD1602 也用过 0.96 寸 OLED。两者各有侧重,LCD1602 是字符屏,只能显示 ASCII 字符和一些日文假名、自定义符号,胜在尺寸大、可视角度好、成本低,工业控制面板上经常见到。OLED 能显示中文和图形,对比度高,体积小,但刷新率偏低,而且早期模块如果驱动电压极性接反容易烧屏。

对于本项目的需求——显示电压数字,LCD1602 其实最合适。16 列 2 行的布局足够显示"Voltage: 1.234V"这样的信息,而且学习并行时序还能帮你理解 LCD 的工作原理。如果以后做菜单交互界面,再用 OLED 或 TFT 屏也不迟。

3.2 GPIO 模拟并口时序驱动 LCD1602

LCD1602 的驱动时序本质上是并口写数据的操作。你先把要写入的数据放到 D0~D7 上,然后根据是写命令还是写数据设置 RS,最后给 EN 引脚一个下降沿脉冲,数据就被锁存进屏幕控制器了。虽然 STM32F103 的 FSMC 接口可以硬件驱动 LCD,但对于 1602 这种慢速设备,GPIO 模拟完全足够,代码也更直观。

我写了一个精简的 LCD1602 驱动库,核心就三个函数:写命令、写数据、初始化。下面是使用 4 位模式时的写命令函数:

void LCD1602_WriteCmd(uint8_t cmd) { LCD_RS_LOW(); LCD_RW_LOW(); LCD_D4_SET(cmd & 0x01); LCD_D5_SET(cmd & 0x02); LCD_D6_SET(cmd & 0x04); LCD_D7_SET(cmd & 0x08); LCD_EN_PULSE(); LCD_D4_SET((cmd >> 4) & 0x01); LCD_D5_SET((cmd >> 4) & 0x02); LCD_D6_SET((cmd >> 4) & 0x04); LCD_D7_SET((cmd >> 4) & 0x08); LCD_EN_PULSE(); }

这里用的是宏定义控制引脚电平,先把低四位放上去,EN 拉高再拉低完成一次锁存,然后再发高四位。写完数据之后注意留出足够的延时,我一般会加 2~5ms 的延时,因为 LCD1602 内部控制器处理数据需要时间,操作太频繁会丢数据。

初始化时序也容易踩坑:上电后要等 40ms 以上,然后连续写三次 0x30(8 位模式命令),再切换到 4 位模式。很多人的 LCD1602 不显示,就是因为初始化时电平和延时时序不对。

3.3 屏幕显示效果的细节:中文、亮度与极化问题

LCD1602 本身无法直接显示中文,因为它内置的字库就是 ASCII 字符集。如果非要在 1602 上显示中文,只能通过自定义字符发生器 CGRAM 来实现,最多存 8 个 5x8 点阵的自定义图案,而且一个汉字通常要拆成两个 5x8 的字符拼接,比较麻烦。对"Voltage:"这种英文标识加数字的方式,直接用 ASCII 就足够了。

LCD 极化这个词是我在实际调试中才深有体会的。LCD 屏的显示原理依赖液晶分子的偏转,对环境温度和驱动电压都很敏感。LCD1602 的 VO 对比度引脚如果电压不对,屏幕要么全黑要么看不到任何字迹。我调试时遇到过屏幕"完全没显示但程序正常"的现象,最后发现就是 VO 引脚悬空了。接上一个 10k 电位器,一端接地一端接 VO,调节到合适对比度,问题立刻解决。

另外,LCD1602 的背光 LED 一般串一个 220 欧姆电阻后接 5V 或 3.3V,不串电阻直接接电源的话背光电流过大很容易烧坏背光 LED。这一点在 OLED 屏上也类似,注意模块额定电压是 3.3V 还是 5V,别直接怼上去。

等到液晶屏能正常显示之后,显示电压值就很简单了。先把 ADC 整数转换为浮点电压值,再用sprintf格式化到字符串,然后定位到屏幕第二行写入:

char buf[16]; float voltage = (float)adc_value * 3.3f / 4095.0f; sprintf(buf, "Voltage: %.3fV", voltage); LCD1602_SetCursor(0, 1); LCD1602_WriteString(buf);

这里建议把电压格式化成3 位小数,因为 12 位 ADC 配合 3.3V 量程,理论分辨率就是 0.8mV 左右,显示 3 位小数刚好匹配,显示太多位只是假精度。

4. 串口发送与上位机数据查看

4.1 UART 参数设置与 printf 重定向

STM32F103 的 USART1 挂在 APB2 总线上,配置时先设置波特率、数据位、停止位和校验位。最常用的配置是115200-8-N-1,即波特率 115200,8 个数据位,无校验,1 个停止位。波特率计算由硬件自动完成,底层有一个 16 倍过采样的分频器,库函数封装得比较干净:

void USART1_Config(uint32_t baud) { GPIO_InitTypeDef GPIO_InitStructure; USART_InitTypeDef USART_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_USART1, ENABLE); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOA, &GPIO_InitStructure); USART_InitStructure.USART_BaudRate = baud; USART_InitStructure.USART_WordLength = USART_WordLength_8b; USART_InitStructure.USART_StopBits = USART_StopBits_1; USART_InitStructure.USART_Parity = USART_Parity_No; USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; USART_InitStructure.USART_Mode = USART_Mode_RX | USART_Mode_TX; USART_Init(USART1, &USART_InitStructure); USART_Cmd(USART1, ENABLE); }

串口发送数据最方便的方式是重定向 printf。在 MDK/Keil 里勾选 Use MicroLIB 后,重写fputc函数,把单个字符直接送到 USART1 发送寄存器,之后就能直接用printf输出格式化信息了:

int fputc(int ch, FILE *f) { while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET); USART_SendData(USART1, (uint8_t)ch); return ch; }

这样主程序里写printf("ADC: %d, Voltage: %.3fV\r\n", adc_value, voltage);就能把数据直接发到串口调试助手的窗口里。我记得第一次重定向成功时还挺激动,因为从此以后打印调试信息就变得很方便,不用再手写一个个字符串发送函数了。

4.2 CH340 模块与串口调试助手的配合

硬件上,STM32F103 的 USART1 输出的是 TTL 电平(0~3.3V),电脑串口是 RS232 电平(正负电压范围),两者不能直接连。利用 CH340 模块做 USB 转 TTL 转换是最省事的方法:单片机侧是 3.3V TTL 电平,USB 侧插进电脑识别成 COM 口。安装好 CH340 驱动后,打开设备管理器确认端口号。

串口调试助手的选择比较灵活,网上有很多免费工具,基本的操作逻辑都一样:选择串口号、设置波特率 115200、数据位 8、停止位 1、无校验,然后打开串口,就能看到接收区不断滚动的电压数据。比如:

ADC: 2048, Voltage: 1.650V ADC: 2054, Voltage: 1.655V ADC: 2060, Voltage: 1.660V

如果你装了虚拟串口工具,甚至可以把数据转发给 Python 脚本或 LabVIEW 处理,这块后续可玩的空间很大。

4.3 发送间隔与数据格式设计

电压数据发送频率要结合场景考虑。如果只做人眼观察,每秒发 5~10 次就够了,发太快窗口刷屏严重。如果后续要画电压曲线、做数据分析,每秒发 50~100 次也来得及。最简单的方式是在循环里加一个延时控制发送间隔,更规范的情况下会用定时器置一个标志位,主循环检测到标志置位后再发送。我最初版本就是while(1){ 采集 -> 显示 -> printf -> Delay(100ms); },足够学习使用。

数据格式上建议一定带上单位并用\r\n结尾。带单位方便对读数时人眼判断,\r\n则是为了让串口助手自动换行显示。如果你后续想用串口直接控制其他设备,格式就按对方的协议来,但先把这个最简单的文本格式跑通了,再去扩展也不晚。

5. 数据滤波与 ADC 校准

5.1 采样值为什么一直跳?真实测量中的波动

ADC 读数在屏幕上跳动是新手最容易困惑的现象之一。明明电压稳定,为什么最后两三位一直变?其实是正常的。第一,参考电压本身有噪声,板载稳压芯片输出不可能绝对稳定;第二,信号源有内阻和噪声,杜邦线本身就像一根小天线,周围环境电磁干扰会被 ADC 敏感地捕捉;第三,软件处理频率高,每一次采样都是独立的快照,抖动自然就显示出来了。

解决跳动的思路有两类:一是硬件上加强滤波,比如在 ADC 引脚对地并联一个 100nF 电容形成低通滤波,能滤掉高频噪声;二是软件上做数字滤波,多次采样取平均或取中值。对于 50Hz 工频干扰,还可以考虑采样间隔取 20ms 的整数倍,利用同步采样削弱干扰。

5.2 经典的中值 + 均值滤波代码实现

我在实际项目里最常用的组合是"先去掉最大最小值,再取平均"。原理是:多次采样中如果出现个别毛刺,最大值和最小值可能是异常值,先剔除可以防止它们把平均结果拉偏。实现起来也不复杂:

uint16_t ADC_Filter(void) { uint32_t sum = 0; uint16_t i, max = 0, min = 4096; for (i = 0; i < 8; i++) { uint16_t val = ADC_GetValue(); if (val > max) max = val; if (val < min) min = val; sum += val; } return (uint16_t)((sum - max - min) / 6); }

这个函数采样 8 次,剔除 1 个最大值和 1 个最小值,剩下 6 个数取平均。实测下来电压数据跳动从原来的 ±10 个 LSB 降到 ±2 个 LSB 左右,效果非常明显。滤波的代价是每次读值的时间变长,但在这个场景下完全可接受。

5.3 外部三点校准:把读数做得更准

即使做了滤波,读数仍然可能和万用表测量值有偏差。原因是多方面的:参考电压不是严格 3.3V,实际可能是 3.28V 或 3.32V;ADC 本身有失调误差和增益误差。外部校准是提升精度的硬核手段。所谓三点校准,就是用三个已知标准电压源(比如 0V、1.65V、3.3V,用精密可调电源产生),分别记录 ADC 读数,然后建立实际电压与 ADC 读数之间的直线关系。

校准公式本质是一个线性映射:

实际电压 = k × ADC读数 + b

其中 k 和 b 可以通过最小二乘法拟合得到。三个点取平均能减少单点误差的影响,比简单做"读数×3.3/4095"更接近真实值。实际做一次就能感受到差别:用万用表量出来 1.234V,校准前显示 1.187V,校准后显示 1.231V,精度提升非常明显。

校准得到的 k 和 b 可以写死在程序里,也可以存储到 Flash 中掉电保存,具体取决于你的产品是否允许通过串口命令在线修改校准参数。对于这个学习项目,先把固定系数写到代码里就足够了。

6. 常见问题与排查技巧实录

6.1 高频问题速查表

我在调试这个项目以及帮人排查问题时,遇到的高频故障基本集中在下面这几类:

现象可能原因解决办法
串口助手无任何数据串口模块接线接反或未共地交换 RX/TX,确保 GND 连接开发板 GND
串口收到乱码波特率不匹配确认代码和串口助手都设置成 115200
LCD 白屏或全黑对比度 VO 电压不对或初始化时序不对接 10k 电位器调节 VO,检查初始化延时
LCD 只有第一行显示第二行起始地址写错第二行起始地址是 0x40,需加上LCD1602_SetCursor处理
ADC 读数异常跳变采样周期太短或滤波不够增大采样周期,软件加中值和均值滤波
电压显示总是 3.30V 不变电位器接线错误或采样引脚下拉/接死检查电位器抽头是否接 PA0,确认没有和其他短路

其中串口乱码最常见的原因就是波特率不一致,其次是模块没共地。你查这些问题时可以先怀疑最常见、最便宜的原因,不要一上来就怀疑单片机坏了。

6.2 一次典型故障的排查思路

我印象很深的一次调试:LCD1602 能显示,串口却一直收不到数据。检查代码没问题,后来用万用表量 PA9 引脚电平,发现一直是高电平,说明 TX 没有拉低发送起始位。再往模块查——CH340 模块的 TX 和 STM32 的 PA9 之间杜邦线断了,接触不良。换了一根线立刻正常。这类接触性故障真的占了调试问题的一半以上,所以排查时先从物理连接入手,再怀疑软件逻辑。

还有一次是电压值总是偏小,万用表量出 1.8V,程序读出来只有 1.2V。最后发现是采样周期设成了 1.5 个周期,采样保持电容充电不足。把采样周期改为 13.5 后再测,数值就对了。这就是采样周期影响采样精度的最直观案例。

6.3 扩展建议:DMA、多通道与 FreeModbus

如果这个基础版本跑通了,我建议你继续往下面几个方向扩展:

多通道采样 + DMA。ADC 四个通道同时采集时,用 DMA 自动把转换结果搬到内存数组,CPU 不用频繁进入中断,效率和实时性都会更好。这也是实际工程中非常常见的做法。

添加 Modbus RTU 从站功能。串口通信的意义不只是"发数据到电脑看",还能配合 FreeModbus 协议栈做成工业现场的标准通讯从站。STM32F103 通过 RS232/RS485 接口基于 FreeModbus 移植,就能和组态软件、触摸屏、PLC 直接交换数据。这个玩法我强烈推荐,它能把一个简单的电压采集板变成真正的工业设备。

用 Python 做上位机接收。C 端发出来的串口数据,用 Python 的 pyserial 读取后可以实时画曲线,看到 ADC 数值随电位器旋转的波形变化,理解数据链路会有质的提升。

最后再说几句

我用 STM32F103 做这个 ADC 采集显示串口发送项目已经很多次,之前带新人时也经常让他们从这套组合入手。现在回头看,这个项目的意义不在于代码本身有多复杂,而在于它把单片机的三大基础外设串联成了一个完整的闭环系统。整条链路——从模拟信号到数字量,从数字量到屏幕文字,再从屏幕到串口报文——正是很多真实产品的基本骨架。

如果你在调试中遇到问题,我个人的建议是:不要急着猛调代码,先用万用表量引脚电平,再检查物理连接,最后再审视逻辑。这个排查顺序能帮你避开绝大多数新手会走的弯路。最后分享一个小技巧:你在 LCD 显示的电压值旁边加上一个不断闪烁的"*"字符,可以直观判断主循环到底有没有在正常运行——这比任何 printf 调试信息都更直观。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询