[APM32F4] 基于APM32F402的多通道 ADC 同步采样系统设计与实现

[复制链接]
 楼主| Peixu 发表于 2025-5-21 01:37 | 显示全部楼层 |阅读模式
本帖最后由 Peixu 于 2025-5-30 11:59 编辑

APM32F402 系列 MCU 的双 ADC 同步采样方案,通过注入通道机制实现三路模拟信号的高精度同步采集。

一、系统设计:
实现 ADC1 和 ADC2 的同步触发,消除通道间采样时间差
利用注入通道的高优先级特性,确保关键信号优先处理
配置定时器触发源,实现精确的采样周期控制
优化中断处理流程,提高系统实时响应能力

二、硬件架构详解
2.1 核心芯片选型
本文选用极海半导体 APM32F402 系列 MCU,该芯片具备以下关键特性:
双 ADC 模块,支持同步采样模式
高达 120MHz 的系统主频,提供强大运算能力
丰富的定时器资源,支持多种触发模式
12 位 ADC 分辨率,采样率可达 1MHz

2.2 引脚分配与信号连接
25090682cbe3183ea4.png


模拟输入部分:
PA2 (ADC1_CH2):连接第 1 路模拟信号源
PA3 (ADC2_CH3):连接第 2 路模拟信号源
PA4 (ADC2_CH4):连接第 3 路模拟信号源

触发控制部分:
TMR1_TRGO:定时器 1 的触发输出,连接至 ADC 触发输入

通信接口部分:
PB10 (USART3_TX):串口发送,用于调试信息输出
PB11 (USART3_RX):串口接收,可扩展为上位机命令接收

三、软件实现详解
3.1 双 ADC 同步采样配置
软件设计的核心是配置双 ADC 的同步工作模式,实现三路信号的同步采集。关键代码如下:
  1. void ADC_Init(void)
  2. {
  3.     // 1. 使能GPIO和ADC时钟
  4.     RCM_EnableAPB2PeriphClock(RCM_APB2_PERIPH_GPIOA);
  5.     RCM_EnableAPB2PeriphClock(RCM_APB2_PERIPH_ADC1 | RCM_APB2_PERIPH_ADC2);

  6.     // 2. 配置模拟输入引脚
  7.     GPIO_Config_T GPIO_ConfigStruct;
  8.     GPIO_ConfigStructInit(&GPIO_ConfigStruct);
  9.     GPIO_ConfigStruct.mode = GPIO_MODE_ANALOG;
  10.     GPIO_ConfigStruct.pin = GPIO_PIN_2 | GPIO_PIN_3 | GPIO_PIN_4;
  11.     GPIO_Config(GPIOA, &GPIO_ConfigStruct);

  12.     // 3. 配置ADC1
  13.     ADC_Config_T ADC_ConfigStruct;
  14.     ADC_Reset(ADC1);
  15.     ADC_ConfigStructInit(&ADC_ConfigStruct);
  16.     ADC_ConfigStruct.mode = ADC_MODE_INJEC_SIMULT;  // 注入同步模式
  17.     ADC_ConfigStruct.scanConvMode = ENABLE;         // 扫描模式
  18.     ADC_ConfigStruct.continuousConvMode = DISABLE;  // 单次转换模式
  19.     ADC_ConfigStruct.externalTrigConv = ADC_EXT_TRIG_CONV_NONE;
  20.     ADC_ConfigStruct.dataAlign = ADC_DATA_ALIGN_RIGHT;
  21.     ADC_ConfigStruct.nbrOfChannel = 3;
  22.     RCM_ConfigADCCLK(RCM_PCLK2_DIV_6);  // ADCCLK = 120MHz/6 = 20MHz
  23.    
  24.     // 4. 配置ADC1注入通道
  25.     ADC_ConfigInjectedSequencerLength(ADC1, 1);
  26.     ADC_ConfigInjectedChannel(ADC1, ADC_CHANNEL_2, 1, ADC_SAMPLETIME_13CYCLES5);
  27.     ADC_ConfigExternalTrigInjectedConv(ADC1, ADC_EXT_TRIG_INJEC_CONV_TMR1_TRGO);
  28.    
  29.     // 5. 配置ADC2注入通道
  30.     ADC_Reset(ADC2);
  31.     ADC_Config(ADC2, &ADC_ConfigStruct);
  32.     ADC_ConfigInjectedSequencerLength(ADC2, 2);
  33.     ADC_ConfigInjectedChannel(ADC2, ADC_CHANNEL_3, 1, ADC_SAMPLETIME_13CYCLES5);
  34.     ADC_ConfigInjectedChannel(ADC2, ADC_CHANNEL_4, 2, ADC_SAMPLETIME_13CYCLES5);
  35.     ADC_ConfigExternalTrigInjectedConv(ADC2, ADC_EXT_TRIG_INJEC_CONV_TMR1_TRGO);

  36.     // 6. 使能中断并启动ADC
  37.     ADC_EnableInterrupt(ADC1, ADC_INT_INJEOC);
  38.     ADC_EnableInterrupt(ADC2, ADC_INT_INJEOC);
  39.     NVIC_EnableIRQRequest(ADC1_2_IRQn, 0, 0);
  40.    
  41.     // 7. ADC校准与启动
  42.     ADC_Enable(ADC1);
  43.     ADC_ResetCalibration(ADC1);
  44.     while (ADC_ReadResetCalibrationStatus(ADC1));
  45.     ADC_StartCalibration(ADC1);
  46.     while (ADC_ReadCalibrationStartFlag(ADC1));
  47.    
  48.     ADC_Enable(ADC2);
  49.     ADC_ResetCalibration(ADC2);
  50.     while (ADC_ReadResetCalibrationStatus(ADC2));
  51.     ADC_StartCalibration(ADC2);
  52.     while (ADC_ReadCalibrationStartFlag(ADC2));
  53.    
  54.     // 8. 启动注入转换
  55.     ADC_EnableSoftwareStartInjectedConv(ADC1);
  56.     ADC_EnableSoftwareStartInjectedConv(ADC2);
  57. }

3.2 定时器触发配置
定时器 TMR1 配置为中心对齐模式,生成周期性触发信号:
  1. void TMR_Init(void)
  2. {
  3.     // 1. 使能定时器和GPIO时钟
  4.     RCM_EnableAPB2PeriphClock(RCM_APB2_PERIPH_TMR1);
  5.     RCM_EnableAPB2PeriphClock(RCM_APB2_PERIPH_GPIOA | RCM_APB2_PERIPH_GPIOB);

  6.     // 2. 配置PWM输出引脚(可用于电机控制等应用)
  7.     GPIO_Config_T gpioConfig;
  8.     gpioConfig.speed = GPIO_SPEED_50MHz;
  9.     gpioConfig.mode = GPIO_MODE_AF_PP;
  10.     gpioConfig.pin = GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10;  // PWM输出引脚
  11.     GPIO_Config(GPIOA, &gpioConfig);

  12.     // 3. 配置定时器基本参数
  13.     TMR_BaseConfig_T tmrBaseConfig;
  14.     tmrBaseConfig.countMode = TMR_COUNTER_MODE_CENTERALIGNED1;  // 中心对齐模式
  15.     tmrBaseConfig.clockDivision = TMR_CLOCK_DIV_1;
  16.     tmrBaseConfig.period = 999;  // 周期值
  17.     tmrBaseConfig.division = 11999;  // 预分频值
  18.     tmrBaseConfig.repetitionCounter = 1;  // 重复计数器
  19.     TMR_ConfigTimeBase(TMR1, &tmrBaseConfig);

  20.     // 4. 配置PWM输出模式(示例)
  21.     TMR_OCConfig_T tmrOCConfig;
  22.     tmrOCConfig.mode = TMR_OC_MODE_PWM2;
  23.     tmrOCConfig.outputState = TMR_OC_STATE_ENABLE;
  24.     tmrOCConfig.outputNState = TMR_OC_NSTATE_ENABLE;
  25.     tmrOCConfig.pulse = 500;  // 占空比50%
  26.     TMR_ConfigOC1(TMR1, &tmrOCConfig);
  27.     TMR_ConfigOC2(TMR1, &tmrOCConfig);
  28.     TMR_ConfigOC3(TMR1, &tmrOCConfig);

  29.     // 5. 配置触发输出(关键步骤)
  30.     TMR_SelectOutputTrigger(TMR1, TMR_TRGO_SOURCE_UPDATE);  // 使用更新事件作为触发源

  31.     // 6. 配置死区时间(用于互补PWM输出)
  32.     TMR_BDTConfig_T TIM_BDTRStruct;
  33.     TIM_BDTRStruct.deadTime = 0x1D;  // 约2μs死区时间
  34.     TIM_BDTRStruct.BRKState = TMR_BRK_STATE_DISABLE;
  35.     TMR_ConfigBDT(TMR1, &TIM_BDTRStruct);

  36.     // 7. 使能定时器和PWM输出
  37.     TMR_EnablePWMOutputs(TMR1);
  38.     TMR_Enable(TMR1);
  39. }

3.3 中断处理机制
ADC 转换完成后触发中断,在中断服务函数中读取采样值并进行处理:
  1. void ADC1_2_IRQHandler(void)
  2. {
  3.     // 1. 标记中断进入(用于调试)
  4.     GPIO_SetBit(GPIOC, GPIO_PIN_13);

  5.     // 2. 处理ADC1注入通道转换完成事件
  6.     if (ADC_ReadIntFlag(ADC1, ADC_INT_INJEOC) == SET)
  7.     {
  8.         uint16_t adc1_value = ADC_ReadInjectedConversionValue(ADC1, ADC_INJEC_CHANNEL_1);
  9.         float voltage = (float)adc1_value / 4095 * 3.3;  // 转换为电压值
  10.         printf("ADC1 (PA2) Data: %d, Voltage: %.3f V\r\n", adc1_value, voltage);
  11.         ADC_ClearIntFlag(ADC1, ADC_INT_INJEOC);  // 清除中断标志
  12.     }

  13.     // 3. 处理ADC2注入通道转换完成事件
  14.     if (ADC_ReadIntFlag(ADC2, ADC_INT_INJEOC) == SET)
  15.     {
  16.         uint16_t adc2_ch3 = ADC_ReadInjectedConversionValue(ADC2, ADC_INJEC_CHANNEL_1);
  17.         uint16_t adc2_ch4 = ADC_ReadInjectedConversionValue(ADC2, ADC_INJEC_CHANNEL_2);
  18.         printf("ADC2 (PA3) Data: %d, Voltage: %.3f V\r\n", adc2_ch3, adc2_ch3*3.3/4095);
  19.         printf("ADC2 (PA4) Data: %d, Voltage: %.3f V\r\n", adc2_ch4, adc2_ch4*3.3/4095);
  20.         ADC_ClearIntFlag(ADC2, ADC_INT_INJEOC);  // 清除中断标志
  21.     }

  22.     // 4. 标记中断处理完成
  23.     GPIO_ResetBit(GPIOC, GPIO_PIN_13);
  24. }

四、测试结果
定时器 TMR1 的计数频率:120MHz / (11999+1) = 10kHz
实际采样频率:10kHz × 2 = 20kHz
每个 ADC 通道的采样时间:1/20kHz = 50μs
55d8f9077efdea9c7d476931a4735a9a
b9179480a3f93ca2ebc5cbb37430654b #申请原创#
@21小跑堂  
您需要登录后才可以回帖 登录 | 注册

本版积分规则

32

主题

58

帖子

0

粉丝
快速回复 在线客服 返回列表 返回顶部

32

主题

58

帖子

0

粉丝
快速回复 在线客服 返回列表 返回顶部