2. 多通道轮询采集与数据打包

任务定义

本案例要求使用MCU的ADC模块完成多通道模拟信号的轮询采集,并将采集结果按固定帧格式打包,通过串口输出。以STM32F103系列为例,实现PA0、PA1、PA2三个通道的ADC1扫描转换,每次轮询周期依次采集三路电压,将12位转换结果按“帧头+通道号+数据高字节+数据低字节”的格式封装为4字节帧,通过USART1以115200-8-N-1发送。核心目标是验证多通道轮询逻辑的正确性以及数据打包的字节序准确性。

硬件连接与初始化

将三路0~3.3V模拟信号分别接入PA0、PA1、PA2引脚。使用STM32标准外设库完成GPIO、ADC1和USART1的初始化。ADC1配置为扫描模式、连续转换关闭,每次软件触发后依次转换三个规则通道。USART1发送引脚为PA9(TX),波特率115200。

// ADC多通道轮询采集与数据打包 - 核心代码
#include "stm32f10x.h"
#include <stdio.h>

#define ADC_CHANNEL_COUNT  3
#define FRAME_HEADER       0xAA

// 三个通道对应的GPIO引脚配置
void GPIO_Config(void) {
    GPIO_InitTypeDef GPIO_InitStructure;
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_ADC1 | RCC_APB2Periph_USART1, ENABLE);
    
    // PA0~PA2 模拟输入
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    
    // USART1 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);
}

void ADC_Config(void) {
    ADC_InitTypeDef ADC_InitStructure;
    ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
    ADC_InitStructure.ADC_ScanConvMode = ENABLE;      // 扫描模式
    ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; // 单次转换
    ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; // 软件触发
    ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; // 右对齐
    ADC_InitStructure.ADC_NbrOfChannel = ADC_CHANNEL_COUNT; // 3个通道
    ADC_Init(ADC1, &ADC_InitStructure);
    
    // 设置规则通道转换顺序:PA0→通道0,PA1→通道1,PA2→通道2
    ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5);
    ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 2, ADC_SampleTime_55Cycles5);
    ADC_RegularChannelConfig(ADC1, ADC_Channel_2, 3, ADC_SampleTime_55Cycles5);
    
    ADC_Cmd(ADC1, ENABLE);
    // 校准
    ADC_ResetCalibration(ADC1);
    while (ADC_GetResetCalibrationStatus(ADC1));
    ADC_StartCalibration(ADC1);
    while (ADC_GetCalibrationStatus(ADC1));
}

void USART_Config(void) {
    USART_InitTypeDef USART_InitStructure;
    USART_InitStructure.USART_BaudRate = 115200;
    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_Tx;
    USART_Init(USART1, &USART_InitStructure);
    USART_Cmd(USART1, ENABLE);
}

// 发送单字节
void USART_SendByte(uint8_t byte) {
    while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
    USART_SendData(USART1, byte);
}

// 发送4字节数据帧: [帧头][通道号][数据高8位][数据低8位]
void Send_Frame(uint8_t channel, uint16_t adc_value) {
    USART_SendByte(FRAME_HEADER);
    USART_SendByte(channel);
    USART_SendByte((uint8_t)(adc_value >> 8));   // 高字节
    USART_SendByte((uint8_t)(adc_value & 0xFF)); // 低字节
}

// 轮询采集并打包发送
void ADC_Polling_And_Send(void) {
    uint16_t adc_values[ADC_CHANNEL_COUNT];
    uint8_t i;
    
    // 软件触发一次转换,依次完成3个通道
    ADC_SoftwareStartConvCmd(ADC1, ENABLE);
    
    // 等待转换完成(EOC标志在最后一个通道转换完成后置位)
    while (ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET);
    
    // 读取转换结果(按顺序读取ADC_DR会自动获取对应通道数据)
    for (i = 0; i < ADC_CHANNEL_COUNT; i++) {
        adc_values[i] = ADC_GetConversionValue(ADC1);
    }
    
    // 打包发送三帧数据
    for (i = 0; i < ADC_CHANNEL_COUNT; i++) {
        Send_Frame(i, adc_values[i]);
    }
}

int main(void) {
    GPIO_Config();
    ADC_Config();
    USART_Config();
    
    while (1) {
        ADC_Polling_And_Send();
        // 简单延时控制发送速率(约100ms周期)
        for (volatile uint32_t delay = 0; delay < 720000; delay++);
    }
}

数据帧格式说明

每帧固定4字节,帧结构如下表所示。接收端可通过帧头0xAA识别帧起始,通过通道号区分数据来源,将高字节左移8位与低字节按位或即可还原12位ADC原始值(0~4095)。

偏移  内容        说明
0     0xAA       帧头,固定值
1     通道号      0x00~0x02,对应PA0~PA2
2     数据高字节  ADC结果[11:8]
3     数据低字节  ADC结果[7:0]

验证方法与判定标准

验证工具:USB转TTL模块连接PA9(TX)与PC串口,使用串口调试助手或Python脚本接收数据。

测试步骤:

  1. 将PA0接GND(0V),PA1接3.3V,PA2接1.65V(可用电阻分压);
  2. 编译烧录程序,打开串口助手,波特率115200;
  3. 观察数据流,应周期性收到三组帧,每组帧头均为0xAA;
  4. 解析帧内容:通道0数据应接近0x0000,通道1数据应接近0x0FFF,通道2数据应接近0x0800(允许±10 LSB误差)。

判定标准:连续接收100帧以上,所有帧头均为0xAA,通道号按0→1→2循环,且各通道电压换算值与实测电压误差小于10mV(对应约12 LSB),即判定通过。

预期输出示例

当PA0=0V、PA1=3.3V、PA2=1.65V时,串口应周期性输出如下字节序列(十六进制):

AA 00 00 00   AA 01 0F FF   AA 02 08 00   AA 00 00 00   AA 01 0F FF   AA 02 08 00 ...

该序列表示通道0采集值为0,通道1采集值为4095,通道2采集值为2048,与输入电压严格对应。

ADC多通道轮询采集与数据打包时序 周期1 周期2 周期3 时间→ 软件触发 CH0转换 CH1转换 CH2转换 EOC置位 读取3次DR 帧0 (CH0) 帧1 (CH1) 帧2 (CH2) 帧格式展开 0xAA CH 高字节 低字节 每周期发送3帧 帧间隔≈0.35ms@115200 PA0: 0V PA1: 3.3V PA2: 1.65V