STM32F103C8T6学习——直接存储器访问(DMA)标准库实战3(ADC数据采集+DMA回传)
本文介绍了一个基于STM32F103C8T6微控制器的双通道模拟信号采集系统。系统采用ADC1(光敏和红外传感器输入)、DMA1(双通道数据自动传输)和USART1(串口通信)等外设,构建了高效的数据采集流水线。通过DMA实现ADC数据自动搬运至内存数组,CPU仅需定期格式化数据并通过DMA发送至上位机,显著降低了CPU占用率。重点阐述了ADC连续扫描模式配置、DMA循环传输机制及volatile
1. 项目概述
本项目成功地在STM32F103C8T6微控制器上,使用标准外设库(SPL),构建了一个自动化、高效率的双通道模拟信号数据采集与上报系统。该系统能够在无需CPU持续干预的情况下,连续监测两个模拟输入端口(如电位器电压)这里我们采集的一个是光敏传感器,一个是红外避障传感器,并将采集到的数据格式化后,通过串口实时发送给上位机。
这个项目是嵌入式系统设计中一个典型的“生产者-消费者”模型,完美展示了如何利用DMA解放CPU,实现硬件间的并行工作,是从入门到进阶的标志性实践。
2. 核心技术栈
-
硬件平台: STM32F103C8T6 核心板
-
软件库: STM32 标准外设库 (Standard Peripheral Library, SPL)
-
关键外设:
-
ADC1 (模数转换器): 用于将模拟信号转换为数字值。
-
DMA1 (直接内存访问): 使用两个独立通道,是实现系统自动化的核心。
-
USART1 (通用同步异步收发器): 用于与PC进行串行通信。
-
3. 系统架构与数据流
本项目的精髓在于构建了一条高效的数据流水线 (Data Pipeline),数据在其中自动流动,CPU仅在必要时介入:
-
信号输入: 两个电位器产生的模拟电压信号 (0-3.3V) 分别输入到MCU的
PA0和PA1引脚。 -
ADC自动采样: ADC1被配置为多通道扫描和连续转换模式。启动后,它会像一个永不停歇的“扫描仪”,自动依次对
PA0和PA1进行A/D转换,并将结果暂存至其内部的数据寄存器(ADC1->DR)。 -
DMA无缝搬运 (采集环节):
-
DMA1通道1 被配置为专门服务于ADC1。
-
它持续“监听”
ADC1->DR寄存器。每当ADC完成一次转换,DMA1通道1便被硬件自动触发,将12位的转换结果(以16位HalfWord形式)从ADC1->DR中取出,并存入内存中的volatile uint16_t adc_values[2]数组。 -
此过程工作在循环模式下,当存满
adc_values[1]后,下一次数据会自动存回adc_values[0],实现了对内存缓冲区的循环写入。
-
-
CPU轻量处理:
-
CPU的主循环
while(1)以一个固定的频率醒来一次。 -
它的任务非常简单:读取
volatile adc_values数组中由DMA随时更新的最新数据,并使用sprintf函数将这两个数字格式化成一个人类可读的字符串(例如"ADC CH0=1024, CH1=2048\r\n"),存入uart_tx_buffer。
-
-
DMA异步上报 (上报环节):
-
CPU准备好数据后,启动DMA1通道4。
-
DMA1通道4从
uart_tx_buffer中读取字符串内容,并逐字节地送入USART1->DR寄存器,通过串口发送出去。 -
此过程工作在普通模式下,发送完指定长度的数据后便自动停止。CPU在启动它之后,无需等待,可以立刻进入下一次延时。
-
4. 关键实现细节
-
ADC配置:
ADC_ScanConvMode和ADC_ContinuousConvMode的使能是实现自动连续采样的关键。同时,ADC上电后的校准流程是保证数据精度的必要步骤。 -
ADC的DMA配置: 数据宽度必须设置为
HalfWord(16位) 来匹配ADC数据寄存器。循环模式 (DMA_Mode_Circular) 与ADC的连续转换模式完美配合。 -
共享数据
volatile: 存储ADC结果的adc_values数组必须用volatile关键字修饰,以防止编译器优化,确保CPU每次都能从内存中读取到最新的、由DMA更新的值。 -
UART的DMA发送:重用了项目二中经过千锤百炼的健壮发送逻辑,即在每次启动发送前,先关闭通道,再用
DMA_SetCurrDataCounter设置长度,并用DMA_ClearFlag清除上一次的“传输完成”标志,最后再启动通道。
5. 项目价值与意义
-
解放CPU,提升系统效率: 整个数据采集过程几乎由ADC和DMA硬件在后台自动完成,CPU占用率极低,可以去执行更复杂的任务(如算法、控制、UI刷新等)。
-
硬件并行,实时性强: 数据的采集和上报都在并行发生,系统能够非常及时地响应模拟信号的变化,具有很强的实时性。
-
掌握核心设计模式: 深刻理解了DMA作为片上“数据总线”的核心作用,掌握了外设之间通过DMA联动的设计模式,这是构建复杂嵌入式系统的基础。
-
代码健壮性: 通过处理DMA状态标志位等细节,学习了如何编写稳定、可靠、可重复工作的嵌入式应用程序。
DMA的相关代码就不再给出了,参考之前的项目一和项目二很快就能想到,重点在ADC的采集上,可认真参阅下下面的代码
#include "stm32f10x.h" // Device header
#include <stdio.h>
#include "Delay.h"
#include "Serial.h"
// ADC相关定义
#define ADC_CHANNELS 2
// volatile关键字是必须的!因为它会被DMA在后台修改,而主程序会读取它
// 这可以防止编译器因优化而导致主程序读到的是旧数据
volatile uint16_t adc_values[ADC_CHANNELS];
void AD_Init(void)
{
//配置时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);
RCC_APB2PeriphClockCmd(GPIOA,ENABLE);
//配置ADCCLK
RCC_ADCCLKConfig(RCC_PCLK2_Div6);
//配置GPIO
GPIO_InitTypeDef GPIO_Initstruct;
GPIO_Initstruct.GPIO_Mode=GPIO_Mode_AIN;
GPIO_Initstruct.GPIO_Pin=GPIO_Pin_0 | GPIO_Pin_1;
GPIO_Initstruct.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_Initstruct);
// 定义一个ADC_InitTypeDef类型的结构体变量,用于存储ADC的所有配置参数
ADC_InitTypeDef ADC_InitStructure;
// 设置ADC工作在独立模式。这是单个ADC工作时的标准设置。
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
// 使能扫描模式。当ADC配置为转换多个通道时,此模式必须使能。
ADC_InitStructure.ADC_ScanConvMode = ENABLE;
// 使能连续转换模式。ADC完成一次扫描序列后,会自动从第一个通道开始,无缝地进行下一次扫描。
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
// 禁止外部触发转换。ADC的转换将由软件命令(ADC_SoftwareStartConvCmd)在配置完成后立即启动。
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
// 设置ADC转换结果的数据对齐方式为右对齐。12位的转换结果将存储在16位数据寄存器的低12位。
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
// 指定在规则转换组中要转换的通道数量。这里我们设置为2(通道0和通道1)。
ADC_InitStructure.ADC_NbrOfChannel = 2;
// 根据上面在ADC_InitStructure结构体中设置好的所有参数,正式初始化ADC1外设。
ADC_Init(ADC1, &ADC_InitStructure);
// --- 配置ADC的常规通道扫描序列 ---
// Rank参数指定了通道在扫描序列中的顺序。
// ADC_SampleTime_55Cycles5 设置了通道的采样时间,采样时间越长,精度越高,但速度越慢。
// 配置ADC1的通道0(对应PA0),作为扫描序列的第1个转换通道。
ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5);
// 配置ADC1的通道1(对应PA1),作为扫描序列的第2个转换通道。
ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 2, ADC_SampleTime_55Cycles5);
// 使能ADC1外设。这是ADC开始工作前的总开关。
ADC_Cmd(ADC1, ENABLE);
// --- ADC校准 (关键步骤,用于消除偏移误差,显著提高转换精度) ---
// 1. 复位ADC的校准寄存器。
ADC_ResetCalibration(ADC1);
// 2. 等待复位校准完成。
while(ADC_GetResetCalibrationStatus(ADC1));
// 3. 开始ADC校准。
ADC_StartCalibration(ADC1);
// 4. 等待校准完成。
while(ADC_GetCalibrationStatus(ADC1));
// 通过软件命令启动ADC转换。
// 因为我们配置的是连续转换模式,所以这个命令只需要在初始化时执行一次。
// ADC就会从此开始,永不停歇地进行多通道扫描和数据转换。
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
}
void AD_Scan(void)
{
uint16_t len;
// 1. 格式化ADC采集到的数据
// ADC是12位的,所以最大值是4095
// %04d 格式表示打印4位数,不足的前面补0,使显示更整齐
sprintf((char*)TxBuffer, "ADC CH0=%04d, CH1=%04d\r\n", adc_values[0], adc_values[1]);
// 2. 获取字符串长度
len = strlen((const char*)TxBuffer);
// 3. 使用完善的DMA发送逻辑来启动一次数据上报
DMA_Cmd(DMA1_Channel4, DISABLE); // 先关闭通道,确保可以修改配置
DMA_SetCurrDataCounter(DMA1_Channel4, len); // 设置本次要发送的长度
DMA_ClearFlag(DMA1_FLAG_TC4); // 清除上次的“传输完成”标志
DMA_Cmd(DMA1_Channel4, ENABLE); // 启动发送!
// 4. 延时500ms,控制上报频率为2Hz
Delay_ms(100);
}
ADC数据转运通道的初始化
void ADCDMA_init()
{
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
DMA_InitTypeDef DMA_InitStructure;
// --- 配置DMA1_Channel5 (USART1_RX) ---
DMA_DeInit(DMA1_Channel1); // 复位DMA通道1
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR; // 外设地址:USART1数据寄存器
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)adc_values; // 内存地址:接收缓冲区
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; // 方向:外设(串口)到内存
DMA_InitStructure.DMA_BufferSize = 2; // 缓冲区大小
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; // 外设地址不自增
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; // 内存地址自增
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; // ADC数据是16位
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; // 数据宽度:字节
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; // 模式:循环模式
DMA_InitStructure.DMA_Priority = DMA_Priority_High; // 优先级:高
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; // 非内存到内存模式
DMA_Init(DMA1_Channel1, &DMA_InitStructure);
// 使能ADC1外设发出DMA请求。当一次转换完成时,ADC会向DMA控制器请求数据转运。
ADC_DMACmd(ADC1, ENABLE);
// 使能DMA1_Channel5
DMA_Cmd(DMA1_Channel1, ENABLE);
}


完整代码在本页顶部位置处,有需要的铁子可以自取,细细体会一番
openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。
更多推荐



所有评论(0)