文章目录
-
- 一、项目概述
- 二、开发环境搭建
- 三、项目工程文件结构
- 四、Mermaid程序流程图
- 五、逐文件编写代码
-
- [1. 创建文件:adc.h](#1. 创建文件:adc.h)
- [2. 创建文件:adc.c](#2. 创建文件:adc.c)
- [3. 创建文件:usart.h](#3. 创建文件:usart.h)
- [4. 创建文件:usart.c](#4. 创建文件:usart.c)
- [5. 创建文件:main.c](#5. 创建文件:main.c)
- 六、工程配置与编译下载
-
- [1. 工程核心配置](#1. 工程核心配置)
- [2. 编译工程](#2. 编译工程)
- [3. 程序下载](#3. 程序下载)
- 七、实物调试与结果验证
-
- [1. 调试步骤](#1. 调试步骤)
- [2. 预期结果](#2. 预期结果)
- [3. 常见问题解决](#3. 常见问题解决)
- 八、项目扩展与工业应用
一、项目概述
本项目基于STM32F103C8T6 核心板,实现工业仪表中最常用的多路ADC模拟数据采集功能,可采集0~3.3V范围内的模拟电压信号(如温度传感器、压力传感器、电压检测信号等),通过串口将采集到的原始ADC值和转换后的实际电压值实时上传到电脑,零基础也能一步步复刻实现,最终可直接应用于工业仪表、数据采集终端等实际场景。
核心功能
- 配置STM32F103 ADC1为多通道扫描模式,采集PA0、PA1、PA2三路模拟信号
- ADC采用软件触发、单次转换模式,保证采集精度
- 将ADC原始12位数据转换为实际电压值(保留2位小数)
- 通过USART1串口(波特率9600)向上位机实时发送采集数据
- 工业级稳定采集,无数据丢失、无通道干扰
硬件准备
- STM32F103C8T6核心板(最小系统板)
- USB转TTL模块(CH340/CP2102)
- 杜邦线若干
- 可调电位器(3.3V)3个(模拟传感器模拟信号)
- 电脑(安装串口调试助手)
硬件接线
| STM32引脚 | 外设/模块 | 功能说明 |
|---|---|---|
| PA0 | 电位器输出端 | ADC通道0采集 |
| PA1 | 电位器输出端 | ADC通道1采集 |
| PA2 | 电位器输出端 | ADC通道2采集 |
| PA9 (TX) | USB转TTL RX | 串口发送 |
| PA10 (RX) | USB转TTL TX | 串口接收 |
| 3.3V | 电位器VCC | 供电 |
| GND | 电位器GND、USB转TTL GND | 共地(核心) |
二、开发环境搭建
本项目使用STM32标准库开发(最适合零基础入门,资料最全),环境准备步骤:
- 安装MDK-ARM(Keil5),并添加STM32F1芯片支持包
- 下载STM32F10x标准库固件库(V3.5.0版本)
- 新建标准库工程模板(包含启动文件、核心库文件)
零基础小白直接使用现成的STM32F103标准库工程模板即可,无需手动配置复杂的工程选项。
三、项目工程文件结构
我们创建5个核心文件,分工明确,零基础也能清晰理解:
main.c:主函数,程序入口,循环采集+数据发送adc.c:ADC多路采集驱动代码adc.h:ADC驱动头文件usart.c:串口驱动代码usart.h:串口驱动头文件
四、Mermaid程序流程图
系统上电
初始化系统时钟72MHz
初始化USART1串口 9600波特率
初始化ADC1 多通道扫描模式
循环采集三路ADC数据
读取ADC原始值 CH0/CH1/CH2
转换为实际电压值
串口发送数据到上位机
延时100ms 刷新采集
五、逐文件编写代码
1. 创建文件:adc.h
作用:声明ADC初始化函数、ADC读取函数,定义ADC通道引脚
c
#ifndef __ADC_H
#define __ADC_H
#include "stm32f10x.h"
// 定义ADC采集通道对应的GPIO口
#define ADC1_CH0_GPIO_PORT GPIOA
#define ADC1_CH0_GPIO_PIN GPIO_Pin_0
#define ADC1_CH0_GPIO_CLK RCC_APB2Periph_GPIOA
#define ADC1_CH1_GPIO_PORT GPIOA
#define ADC1_CH1_GPIO_PIN GPIO_Pin_1
#define ADC1_CH1_GPIO_CLK RCC_APB2Periph_GPIOA
#define ADC1_CH2_GPIO_PORT GPIOA
#define ADC1_CH2_GPIO_PIN GPIO_Pin_2
#define ADC1_CH2_GPIO_CLK RCC_APB2Periph_GPIOA
// 函数声明
void ADCx_Init(void); // ADC初始化
u16 Get_ADC_Val(u8 ch); // 读取单通道ADC值
u16 Get_ADC_Average(u8 ch,u8 times); // 读取ADC平均值(滤波)
#endif
2. 创建文件:adc.c
作用:实现ADC GPIO初始化、ADC模式配置、数据读取核心代码
c
#include "adc.h"
#include "delay.h"
// ADC初始化函数 配置PA0 PA1 PA2为模拟输入
void ADCx_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
ADC_InitTypeDef ADC_InitStructure;
// 1. 开启时钟:GPIOA + ADC1
RCC_APB2PeriphClockCmd(ADC1_CH0_GPIO_CLK | RCC_APB2Periph_ADC1, ENABLE);
// 2. 配置ADC预分频器:ADC时钟最大14MHz,72M/6=12MHz
RCC_ADCCLKConfig(RCC_PCLK2_Div6);
// 3. 配置GPIO为模拟输入模式
GPIO_InitStructure.GPIO_Pin = ADC1_CH0_GPIO_PIN | ADC1_CH1_GPIO_PIN | ADC1_CH2_GPIO_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; // 模拟输入
GPIO_Init(GPIOA, &GPIO_InitStructure);
// 4. ADC基础配置
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);
// 5. 使能ADC1
ADC_Cmd(ADC1, ENABLE);
// 6. ADC校准(必须执行)
ADC_ResetCalibration(ADC1); // 复位校准
while(ADC_GetResetCalibrationStatus(ADC1)); // 等待复位完成
ADC_StartCalibration(ADC1); // 开始校准
while(ADC_GetCalibrationStatus(ADC1)); // 等待校准完成
}
// 读取指定通道的ADC值
// ch:通道编号 0~17
u16 Get_ADC_Val(u8 ch)
{
// 设置指定通道的转换顺序 排名1 采样时间239.5周期
ADC_RegularChannelConfig(ADC1, ch, 1, ADC_SampleTime_239Cycles5);
ADC_SoftwareStartConvCmd(ADC1, ENABLE); // 软件启动转换
while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC)); // 等待转换完成
return ADC_GetConversionValue(ADC1); // 返回转换结果
}
// 多次采集取平均值 抗干扰
u16 Get_ADC_Average(u8 ch,u8 times)
{
u32 temp_val=0;
u8 t;
for(t=0;t<times;t++)
{
temp_val+=Get_ADC_Val(ch);
delay_ms(5);
}
return temp_val/times;
}
3. 创建文件:usart.h
作用:声明串口初始化函数、串口发送函数
c
#ifndef __USART_H
#define __USART_H
#include "stm32f10x.h"
// 函数声明
void USART1_Init(void); // 串口1初始化
void USART1_Send_Byte(u8 dat); // 发送单个字节
void USART1_Send_String(char *str); // 发送字符串
void USART1_Send_Num(u32 num); // 发送数字
#endif
4. 创建文件:usart.c
作用:实现串口初始化、数据发送功能,用于和电脑通信
c
#include "usart.h"
#include "delay.h"
// 串口1初始化 波特率9600
void USART1_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
// 1. 开启时钟:GPIOA + USART1
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_USART1, ENABLE);
// 2. 配置TX引脚 PA9 推挽复用输出
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);
// 3. 配置RX引脚 PA10 浮空输入
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// 4. 串口基础配置
USART_InitStructure.USART_BaudRate = 9600; // 波特率9600
USART_InitStructure.USART_WordLength = USART_WordLength_8b; // 8位数据位
USART_InitStructure.USART_StopBits = USART_StopBits_1; // 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);
// 5. 使能串口1
USART_Cmd(USART1, ENABLE);
}
// 串口发送单个字节
void USART1_Send_Byte(u8 dat)
{
USART_SendData(USART1, dat);
while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
}
// 串口发送字符串
void USART1_Send_String(char *str)
{
while(*str)
{
USART1_Send_Byte(*str);
str++;
}
}
// 串口发送数字(辅助调试)
void USART1_Send_Num(u32 num)
{
if(num/10000) USART1_Send_Byte(num/10000+'0');
if(num/1000) USART1_Send_Byte(num/1000%10+'0');
if(num/100) USART1_Send_Byte(num/100%10+'0');
if(num/10) USART1_Send_Byte(num/10%10+'0');
USART1_Send_Byte(num%10+'0');
}
5. 创建文件:main.c
作用:程序主入口,循环调用ADC采集,转换电压并通过串口发送
c
#include "stm32f10x.h"
#include "delay.h"
#include "adc.h"
#include "usart.h"
// 主函数
int main(void)
{
u16 adc_val0,adc_val1,adc_val2; // 原始ADC值
float vol_val0,vol_val1,vol_val2;// 实际电压值
// 初始化模块
delay_init(); // 延时初始化
USART1_Init(); // 串口初始化
ADCx_Init(); // ADC初始化
// 开机提示
USART1_Send_String("STM32F103 工业仪表多路ADC采集\r\n");
USART1_Send_String("采集通道:PA0 PA1 PA2\r\n");
USART1_Send_String("------------------------------------\r\n");
while(1)
{
// 采集三路ADC 10次平均滤波
adc_val0 = Get_ADC_Average(ADC_Channel_0,10);
adc_val1 = Get_ADC_Average(ADC_Channel_1,10);
adc_val2 = Get_ADC_Average(ADC_Channel_2,10);
// 转换为实际电压 公式:电压 = (ADC值 / 4095) * 3.3
vol_val0 = (adc_val0 * 3.3f) / 4095;
vol_val1 = (adc_val1 * 3.3f) / 4095;
vol_val2 = (adc_val2 * 3.3f) / 4095;
// 串口发送原始ADC值
USART1_Send_String("CH0_ADC:");
USART1_Send_Num(adc_val0);
USART1_Send_String(" ");
USART1_Send_String("CH1_ADC:");
USART1_Send_Num(adc_val1);
USART1_Send_String(" ");
USART1_Send_String("CH2_ADC:");
USART1_Send_Num(adc_val2);
USART1_Send_String("\r\n");
// 串口发送实际电压值 保留2位小数
USART1_Send_String("CH0_Vol:");
USART1_Send_Num((u16)vol_val0);
USART1_Send_Byte('.');
USART1_Send_Num((u16)((vol_val0-(u16)vol_val0)*100));
USART1_Send_String("V ");
USART1_Send_String("CH1_Vol:");
USART1_Send_Num((u16)vol_val1);
USART1_Send_Byte('.');
USART1_Send_Num((u16)((vol_val1-(u16)vol_val1)*100));
USART1_Send_String("V ");
USART1_Send_String("CH2_Vol:");
USART1_Send_Num((u16)vol_val2);
USART1_Send_Byte('.');
USART1_Send_Num((u16)((vol_val2-(u16)vol_val2)*100));
USART1_Send_String("V\r\n");
USART1_Send_String("------------------------------------\r\n");
delay_ms(100); // 100ms采集一次
}
}
六、工程配置与编译下载
1. 工程核心配置
- 打开Keil5工程,点击魔法棒图标
Target选项卡:外部晶振填写8.0(STM32F103默认晶振)Output选项卡:勾选Create HEX File(生成下载文件)Debug选项卡:选择你的下载器(ST-Link/J-Link)
2. 编译工程
- 点击Keil左上角编译按钮
- 等待编译完成,提示
0 Error(s), 0 Warning(s)即为成功 - 自动生成
.hex文件,用于下载到STM32
3. 程序下载
- 用下载器连接STM32核心板(SWD模式:SWDIO、SWCLK、3.3V、GND)
- 打开下载软件(ST-Link Utility),加载HEX文件
- 点击下载,等待提示
Download Complete
七、实物调试与结果验证
1. 调试步骤
- 按照硬件接线表完成所有接线(GND必须共地,否则采集数据异常)
- 打开电脑串口调试助手,配置参数:波特率9600、8N1、无校验
- 给STM32和USB转TTL模块上电
- 旋转电位器,观察串口调试助手的数据变化
2. 预期结果
- 串口开机打印提示信息
- 实时显示三路ADC原始值(04095)和实际电压值(0.003.30V)
- 旋转电位器,电压值平滑变化,无跳变、无乱码
3. 常见问题解决
- 串口乱码:检查波特率是否为9600,晶振配置是否为8MHz
- ADC采集数据固定不变:检查GPIO接线、ADC通道配置是否正确
- 电压值不准:执行ADC校准代码,保证共地,使用10次平均滤波
八、项目扩展与工业应用
本项目是工业仪表数据采集的基础核心,可直接扩展以下功能:
- 增加更多ADC通道(STM32F103最多18个ADC通道)
- 接入真实传感器(DS18B20温度传感器、气压传感器、霍尔传感器)
- 添加OLED/LCD屏幕,本地实时显示采集数据
- 结合ESP8266模块,将数据上传到物联网平台(云平台)
- 添加报警功能:电压超出阈值时蜂鸣器报警