STM32实战:基于STM32F103的工业仪表数据采集(多路ADC)

文章目录

一、项目概述

本项目基于STM32F103C8T6 核心板,实现工业仪表中最常用的多路ADC模拟数据采集功能,可采集0~3.3V范围内的模拟电压信号(如温度传感器、压力传感器、电压检测信号等),通过串口将采集到的原始ADC值和转换后的实际电压值实时上传到电脑,零基础也能一步步复刻实现,最终可直接应用于工业仪表、数据采集终端等实际场景。

核心功能

  1. 配置STM32F103 ADC1为多通道扫描模式,采集PA0、PA1、PA2三路模拟信号
  2. ADC采用软件触发、单次转换模式,保证采集精度
  3. 将ADC原始12位数据转换为实际电压值(保留2位小数)
  4. 通过USART1串口(波特率9600)向上位机实时发送采集数据
  5. 工业级稳定采集,无数据丢失、无通道干扰

硬件准备

  1. STM32F103C8T6核心板(最小系统板)
  2. USB转TTL模块(CH340/CP2102)
  3. 杜邦线若干
  4. 可调电位器(3.3V)3个(模拟传感器模拟信号)
  5. 电脑(安装串口调试助手)

硬件接线

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标准库开发(最适合零基础入门,资料最全),环境准备步骤:

  1. 安装MDK-ARM(Keil5),并添加STM32F1芯片支持包
  2. 下载STM32F10x标准库固件库(V3.5.0版本)
  3. 新建标准库工程模板(包含启动文件、核心库文件)

零基础小白直接使用现成的STM32F103标准库工程模板即可,无需手动配置复杂的工程选项。

三、项目工程文件结构

我们创建5个核心文件,分工明确,零基础也能清晰理解:

  1. main.c:主函数,程序入口,循环采集+数据发送
  2. adc.c:ADC多路采集驱动代码
  3. adc.h:ADC驱动头文件
  4. usart.c:串口驱动代码
  5. 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. 工程核心配置

  1. 打开Keil5工程,点击魔法棒图标
  2. Target选项卡:外部晶振填写8.0(STM32F103默认晶振)
  3. Output选项卡:勾选Create HEX File(生成下载文件)
  4. Debug选项卡:选择你的下载器(ST-Link/J-Link)

2. 编译工程

  1. 点击Keil左上角编译按钮
  2. 等待编译完成,提示0 Error(s), 0 Warning(s)即为成功
  3. 自动生成.hex文件,用于下载到STM32

3. 程序下载

  1. 用下载器连接STM32核心板(SWD模式:SWDIO、SWCLK、3.3V、GND)
  2. 打开下载软件(ST-Link Utility),加载HEX文件
  3. 点击下载,等待提示Download Complete

七、实物调试与结果验证

1. 调试步骤

  1. 按照硬件接线表完成所有接线(GND必须共地,否则采集数据异常)
  2. 打开电脑串口调试助手,配置参数:波特率9600、8N1、无校验
  3. 给STM32和USB转TTL模块上电
  4. 旋转电位器,观察串口调试助手的数据变化

2. 预期结果

  1. 串口开机打印提示信息
  2. 实时显示三路ADC原始值(04095)和实际电压值(0.003.30V)
  3. 旋转电位器,电压值平滑变化,无跳变、无乱码

3. 常见问题解决

  1. 串口乱码:检查波特率是否为9600,晶振配置是否为8MHz
  2. ADC采集数据固定不变:检查GPIO接线、ADC通道配置是否正确
  3. 电压值不准:执行ADC校准代码,保证共地,使用10次平均滤波

八、项目扩展与工业应用

本项目是工业仪表数据采集的基础核心,可直接扩展以下功能:

  1. 增加更多ADC通道(STM32F103最多18个ADC通道)
  2. 接入真实传感器(DS18B20温度传感器、气压传感器、霍尔传感器)
  3. 添加OLED/LCD屏幕,本地实时显示采集数据
  4. 结合ESP8266模块,将数据上传到物联网平台(云平台)
  5. 添加报警功能:电压超出阈值时蜂鸣器报警
相关推荐
BT-BOX1 小时前
Stm32CubeMX+Proteus仿真--STM32外部中断
stm32·单片机·proteus
Wallystech-Linda2 小时前
DR9574 vs DR9574S — Choosing the Right IPQ9574 WiFi 7 Platform for Your Network
嵌入式硬件
森利威尔电子-2 小时前
森利威尔SL8700 降压型大功率 LED 恒流驱动器:5A/95%效率,支持 PWM/模拟调光
单片机·嵌入式硬件·集成电路·芯片·电源芯片
三佛科技-187366133973 小时前
GP8892SEH贴片SOP7省外围5V2A隔离型原边反馈芯片直接替代MT3723
单片机·嵌入式硬件
Quinn273 小时前
正点原子 STM32MP257 修复异核 FreeRTOS 例程 osDelay() 函数比 HAL_Delay() 延时快的问题
stm32·单片机·嵌入式硬件
周周记笔记3 小时前
【元器件专题】三极管性能
单片机·嵌入式硬件
不会武功的火柴4 小时前
ModelSim入门实战(三): 批处理一键仿真与波形调试
嵌入式硬件·fpga·仿真·modelsim·ic验证·rtl
23124_806 小时前
【无标题】
单片机·嵌入式硬件
ytttr8736 小时前
STM32 读写 SD 卡源码(SPI 模式 + FATFS 文件系统)
stm32·单片机·嵌入式硬件