电机控制基础 - 基于PWM触发ADC采样的设计与实现
## 引言在电机控制、电源管理、数字电源转换等应用中,**精准的时序控制**和**高精度信号采集**是核心需求。通过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特点
!(data/attachment/forum/202504/12/210701s5y5e3py5mv2vmzh.png "image.png")
!(data/attachment/forum/202504/12/210805bvgaogoo5dfoqo3d.png "image.png")
---
## 三、软件实现步骤(以APM32F035库为例)
### 3.1 初始化PWM
#### (1) PWM 通用配置
```c
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 中断优先级及其使能。
```c
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; //内存地址
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 中断处理与数据读取
```c
// ADC转换完成中断回调
void ADC_COMP_IRQHandler(void) {
//清除标志位
ADC->STS =ADC->STS;
// 逻辑功能处理
}
```
---
## 四、调试与优化
### 4.1 关键验证点
1. **触发同步性**:用示波器同时捕捉PWM边沿与ADC启动信号(如ADC的 `CONVERT`引脚)。
2. **时序余量**:确保ADC转换时间小于PWM触发间隔。
3. **抗干扰设计**:在PWM触发后插入微小延迟(如死区时间)再采样,避开开关噪声。
### 4.2 常见问题
- **触发失效**:检查定时器与ADC的时钟源是否使能。
- **数据错位**:DMA传输时需配置正确的内存地址和缓冲区大小。
- **抖动问题**:优化PCB布局,避免PWM信号对ADC输入造成串扰。
---
## 五、扩展应用
- **多通道交替采样**:结合扫描模式,在单个PWM周期内采集多个传感器信号。
- **闭环控制集成**:将ADC结果实时反馈至PWM占空比调节,实现数字PID控制。
---
## 结语
通过PWM硬件触发ADC采样,可显著提升系统实时性与可靠性。实际开发中需结合具体
页:
[1]