引言
在电机控制、电源管理、数字电源转换等应用中,精准的时序控制和高精度信号采集是核心需求。通过PWM信号触发ADC采样,可实现硬件级同步,减少CPU干预并提高系统效率。本文以通用MCU为例,解析PWM触发ADC采样的实现方法。
一、PWM触发ADC原理
1.1 核心机制
- 硬件联动:利用MCU内部定时器(Timer)的PWM模块与ADC模块直接联动。
- 触发时机:在PWM周期中的特定时刻(如上升沿、下降沿或中心点)生成触发信号,启动ADC转换。
- 优势:消除软件延迟,确保采样时刻与PWM波形严格同步。
1.2 典型应用场景
- 电机控制:在PWM驱动的逆变器中,于特定时刻采集相电流。
- 开关电源:在MOSFET关断后采样输出电压,避免开关噪声。
- 谐振电路:捕捉LC振荡波形的关键点。
二、硬件设计要点
2.1 MCU资源分配
- 定时器选择:需支持PWM输出且具备触发输出(TRGO)功能,如APM32的TIM1/TIM8高级定时器。
- ADC通道配置:选择支持外部触发的ADC模块,并与定时器触发源绑定。
- 引脚映射:确保PWM输出引脚与ADC输入通道无冲突。
2.2 信号链设计
PWM Generation → Timer TRGO → ADC External Trigger → ADC Conversion → DMA/CPU Read
注:推荐使用DMA传输ADC数据以减少CPU负载。
2.3 APM32F035特点


三、软件实现步骤(以APM32F035库为例)
3.1 初始化PWM
(1) PWM 通用配置
TMR_TimeBase_T TIM_TimeBaseInitStructure;
TMR_OCConfig_T TIM_OCInitStructure;
TMR_BDTInit_T TIM_BDTRInitStructure;
//使能外设时钟
RCM_EnableAPB2PeriphClock(RCM_APB2_PERIPH_SYSCFG | RCM_APB2_PERIPH_TMR1 );
/* 设置PWM clock 分频为1 分频,选用中心对齐模式22,设置重复计数器为11,*/
TIM_TimeBaseInitStructure.period = u16_Period; //(SYSTEM_FREQUENCY/2/8000) - 1;
TIM_TimeBaseInitStructure.div = 0;
TIM_TimeBaseInitStructure.counterMode = TMR_COUNTER_MODE_CENTERALIGNED2;
TIM_TimeBaseInitStructure.clockDivision = TMR_CKD_DIV1;
TIM_TimeBaseInitStructure.repetitionCounter = 1;
TMR_ConfigTimeBase(TMR1, &TIM_TimeBaseInitStructure);
TMR_EnableAUTOReload(TMR1);
TMR_Enable(TMR1);
TMR1->REPCNT = 1;
/* Main Output Enable */
TMR_EnablePWMOutputs(TMR1);
(2) PWM 输出状态配置
设置PWM 上下管输出状态,并使能配置上下管P WM 输出有效,配置使能刹车,配置刹车输入极性,关闭刹车硬件自动恢复。
/* Automatic Output enable, Break, dead time and lock configuration*/
TIM_BDTRInitStructure.RMOS_State = TMR_RMOS_STATE_ENABLE;//--------
TIM_BDTRInitStructure.IMOS_State = TMR_IMOS_STATE_ENABLE;//--------
TIM_BDTRInitStructure.lockLevel = TMR_LOCK_LEVEL_OFF;//00:锁定关闭,寄存器无写保护;01:锁定级别1,不能写入TIMx_BDTR寄存器的DTG、BKE、BKP、AOE位和TIMx_CR2寄存器的OISx/OISxN位;
TIM_BDTRInitStructure.deadTime = u16_DeadTime;//死区时间
/**
* Brake configuration: enable brake
* Brake input polarity: active in low level
* Auto output enable configuration: Disable MOE bit hardware control
*/
TIM_BDTRInitStructure.breakState = TMR_BREAK_STATE_ENABLE;//TMR_BREAK_STATE_ENABLE TMR_BREAK_STATE_DISABLE;
TIM_BDTRInitStructure.breakPolarity = TMR_BREAK_POLARITY_HIGH;
TIM_BDTRInitStructure.automaticOutput = TMR_AUTOMATIC_OUTPUT_DISABLE;
TMR_ConfigBDT(TMR1, &TIM_BDTRInitStructure);
/*pwm driver set,channel 1,2,3,4set pwm mode*/
TIM_OCInitStructure.OC_Mode = TMR_OC_MODE_PWM2;
TIM_OCInitStructure.OC_OutputState = TMR_OUTPUT_STATE_ENABLE; //TMR_OUTPUT_STATE_DISABLE;
TIM_OCInitStructure.OC_OutputNState = TMR_OUTPUT_NSTATE_ENABLE; //TMR_OUTPUT_NSTATE_DISABLE;//------------
TIM_OCInitStructure.Pulse = u16_Period;
TIM_OCInitStructure.OC_Polarity = TMR_OC_POLARITY_HIGH;
TIM_OCInitStructure.OC_NPolarity = TMR_OC_NPOLARITY_HIGH; //互补输出极性-------
TIM_OCInitStructure.OC_Idlestate = TMR_OCIDLESTATE_RESET; // TMR_OCIDLESTATE_SET; //
TIM_OCInitStructure.OC_NIdlestate = TMR_OCNIDLESTATE_RESET; // TMR_OCNIDLESTATE_SET;//
//set OC1/OC1N
TMR_OC1Config(TMR1, &TIM_OCInitStructure);
//set oc2/oc2N
TIM_OCInitStructure.Pulse = u16_Period;
TMR_OC2Config(TMR1, &TIM_OCInitStructure);
//set OC3/OC3N
TIM_OCInitStructure.Pulse = u16_Period;
TMR_OC3Config(TMR1, &TIM_OCInitStructure);
TIM_OCInitStructure.OC_Mode = TMR_OC_MODE_PWM2; //选择定时器模式:TIM脉冲宽度调制模式2
TIM_OCInitStructure.OC_OutputState = TMR_OUTPUT_STATE_ENABLE; //CH1比较输出使能
TIM_OCInitStructure.OC_OutputNState = TMR_OUTPUT_NSTATE_ENABLE; //CH1N比较输出使能
TIM_OCInitStructure.OC_Polarity = TMR_OC_POLARITY_HIGH; //CH1输出极性:TIM输出比较极性高
TIM_OCInitStructure.OC_NPolarity = TMR_OC_NPOLARITY_HIGH; //CH1N输出极性:TIM输出比较极性高
TIM_OCInitStructure.OC_Idlestate = TMR_OCIDLESTATE_RESET; //CH1输出空闲状态:0
TIM_OCInitStructure.OC_NIdlestate = TMR_OCNIDLESTATE_SET; //CH1N输出空闲状态:0
//set OC4
TIM_OCInitStructure.Pulse = 10;
TMR_OC4Config(TMR1, &TIM_OCInitStructure);//OC4
//enable interrupt
TMR_EnableInterrupt(TMR1, TMR_INT_BRK);
TMR_EnableAUTOReload(TMR1);//TIMx_ARR寄存器被装入缓冲器
TMR_OC1PreloadConfig(TMR1,TMR_OC_PRELOAD_ENABLE);
TMR_OC2PreloadConfig(TMR1,TMR_OC_PRELOAD_ENABLE);//比较值预装载使能
TMR_OC3PreloadConfig(TMR1,TMR_OC_PRELOAD_ENABLE);
TMR_OC4PreloadConfig(TMR1,TMR_OC_PRELOAD_ENABLE);
3.2 配置ADC外部触发
采用DMA 模式,ADC 量化后的数据直接搬运到ADC_ConvertedValue 数组中存储,ADC 触发条件采用TMR1 的CC4 作为触发源,开启ADC 使能及配置ADC 中断优先级及其使能。
ADC_Config_T ADC_InitStructure;
DMA_Config_T DMA_InitStructure;
DMA_InitStructure.peripheralAddress = (uint32_t)&(ADC->DATA);//ADC地址
DMA_InitStructure.memoryAddress = (uint32_t)&ADC_ConvertedValue[0]; //内存地址
DMA_InitStructure.direction = DMA_DIR_PERIPHERAL; //方向(从外设到内存)
DMA_InitStructure.bufferSize = TOTAL_CHANNEL;//TOTAL_CHANNEL; //传输内容的大小---传输次数
DMA_InitStructure.peripheralInc = DMA_PERIPHERAL_INC_DISABLE; //外设地址固定
DMA_InitStructure.memoryInc = DMA_MEMORY_INC_ENABLE;//DMA_MEMORY_INC_ENABLE; //内存地址固定
DMA_InitStructure.peripheralDataSize = DMA_PERIPHERAL_DATASIZE_HALFWORD ; //外设数据单位
DMA_InitStructure.memoryDataSize = DMA_MEMORY_DATASIZE_HALFWORD ; //内存数据单位
DMA_InitStructure.circular = DMA_CIRCULAR_ENABLE ; //DMA模式:循环传输
DMA_InitStructure.priority = DMA_PRIORITY_LEVEL_VERYHIGH ; //优先级:高
DMA_InitStructure.memoryTomemory = DMA_M2M_DISABLE; //禁止内存到内存的传输
DMA_Config(DMA_CHANNEL_1, &DMA_InitStructure); //配置DMA的1通道
DMA_Enable(DMA_CHANNEL_1);
ADC_Reset();
ADC_ClockMode(ADC_CLOCK_MODE_ASYNCLK);//48M/4=12mADC_CLOCK_MODE_SYNCLKDIV4
ADC_ConfigStructInit(&ADC_InitStructure);
ADC_InitStructure.convMode = ADC_CONVERSION_SINGLE;
ADC_InitStructure.scanDir = ADC_SCAN_DIR_UPWARD;
ADC_InitStructure.extTrigConv1 = ADC_EXT_TRIG_CONV_TRG1; // timer1 CC4
ADC_InitStructure.extTrigEdge1 = ADC_EXT_TRIG_EDGE_RISING;
ADC_InitStructure.dataAlign = ADC_DATA_ALIGN_RIGHT;
ADC_InitStructure.resolution = ADC_RESOLUTION_12B;
ADC_Config(&ADC_InitStructure);
ADC_ConfigChannel(ADC_CHANNEL_2 | ADC_CHANNEL_8 | ADC_CHANNEL_9 | ADC_CHANNEL_7| ADC_CHANNEL_5 ,ADC_SAMPLE_TIME_1_5);
ADC->CFG1_B.OVRMAG = 1;
ADC_EnableInterrupt(ADC_INT_CS);
//=========================ADC中断使用=================================================
NVIC_EnableIRQ(ADC_COMP_IRQn);
NVIC_SetPriority(ADC_COMP_IRQn,0);
ADC_DMARequestMode(ADC_DMA_MODE_CIRCULAR);
ADC_EnableDMA();
ADC_Enable();
ADC_StartConversion();//必需要启动一下
3.3 中断处理与数据读取
// ADC转换完成中断回调
void ADC_COMP_IRQHandler(void) {
//清除标志位
ADC->STS = ADC->STS;
// 逻辑功能处理
}
四、调试与优化
4.1 关键验证点
- 触发同步性:用示波器同时捕捉PWM边沿与ADC启动信号(如ADC的
CONVERT
引脚)。
- 时序余量:确保ADC转换时间小于PWM触发间隔。
- 抗干扰设计:在PWM触发后插入微小延迟(如死区时间)再采样,避开开关噪声。
4.2 常见问题
- 触发失效:检查定时器与ADC的时钟源是否使能。
- 数据错位:DMA传输时需配置正确的内存地址和缓冲区大小。
- 抖动问题:优化PCB布局,避免PWM信号对ADC输入造成串扰。
五、扩展应用
- 多通道交替采样:结合扫描模式,在单个PWM周期内采集多个传感器信号。
- 闭环控制集成:将ADC结果实时反馈至PWM占空比调节,实现数字PID控制。
结语
通过PWM硬件触发ADC采样,可显著提升系统实时性与可靠性。实际开发中需结合具体