在电机控制领域,STM32G4系列微控制器因其高性能和丰富的外设资源成为热门选择。FOC(磁场定向控制)算法作为现代电机控制的主流方案,其实现过程中ADC采样时序的精确性直接决定了控制效果。传统软件触发ADC采样方式存在两大痛点:一是采样时刻与PWM波形不同步导致的相位误差,二是CPU频繁介入带来的计算延迟。
这个项目要解决的核心问题,就是如何利用STM32G4的硬件特性实现PWM信号与ADC采样的精确同步。具体来说,当PWM波形达到特定时刻(如上桥臂开通瞬间)时,由硬件自动触发ADC采样,无需CPU干预。这种同步机制能确保采样时刻与PWM周期的严格对应,为FOC算法提供更准确的电流反馈数据。
STM32G4的TIM1高级定时器是实现该功能的核心。其关键特性包括:
在FOC应用中,我们通常配置TIM1为中央对齐模式(Center-aligned mode),这样可以在PWM周期的中间点(即计数器归零时刻)自然产生一个触发信号。通过配置TIMx_CR2寄存器的MMS位,可以选择将以下事件作为TRGO输出源:
STM32G4的ADC支持多种触发源,包括:
要实现硬件同步采样,需要将TIM1的TRGO信号路由到ADC的硬件触发输入。这通过配置ADCx_CFGR寄存器的EXTEN和EXTSEL位实现。特别需要注意的是,G4系列新增了ADC硬件预加载功能(ADVREGEN),可以进一步降低触发延迟。
c复制// TIM1基础PWM配置
TIM_HandleTypeDef htim1;
htim1.Instance = TIM1;
htim1.Init.Prescaler = 0;
htim1.Init.CounterMode = TIM_COUNTERMODE_CENTERALIGNED1;
htim1.Init.Period = PWM_PERIOD - 1; // 根据开关频率计算
htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim1.Init.RepetitionCounter = 0;
htim1.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
HAL_TIM_PWM_Init(&htim1);
// 配置TRGO输出
TIM_MasterConfigTypeDef sMasterConfig = {0};
sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE; // 选择更新事件作为触发源
sMasterConfig.MasterOutputTrigger2 = TIM_TRGO2_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
HAL_TIMEx_MasterConfigSynchronization(&htim1, &sMasterConfig);
// 配置PWM通道
TIM_OC_InitTypeDef sConfigOC = {0};
sConfigOC.OCMode = TIM_OCMODE_PWM1;
sConfigOC.Pulse = DUTY_CYCLE; // 占空比设置
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
sConfigOC.OCNPolarity = TIM_OCNPOLARITY_HIGH;
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
sConfigOC.OCIdleState = TIM_OCIDLESTATE_RESET;
sConfigOC.OCNIdleState = TIM_OCNIDLESTATE_RESET;
HAL_TIM_PWM_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_1);
c复制// ADC初始化
ADC_HandleTypeDef hadc1;
hadc1.Instance = ADC1;
hadc1.Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIV1;
hadc1.Init.Resolution = ADC_RESOLUTION_12B;
hadc1.Init.ScanConvMode = ADC_SCAN_ENABLE;
hadc1.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
hadc1.Init.LowPowerAutoWait = DISABLE;
hadc1.Init.ContinuousConvMode = DISABLE; // 必须禁用连续转换模式
hadc1.Init.NbrOfConversion = 3; // 三相电流采样
hadc1.Init.DiscontinuousConvMode = DISABLE;
hadc1.Init.ExternalTrigConv = ADC_EXTERNALTRIG_T1_TRGO; // 关键配置
hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_RISING;
hadc1.Init.DMAContinuousRequests = ENABLE;
hadc1.Init.Overrun = ADC_OVR_DATA_OVERWRITTEN;
hadc1.Init.OversamplingMode = DISABLE;
HAL_ADC_Init(&hadc1);
// 配置ADC通道
ADC_ChannelConfTypeDef sConfig = {0};
sConfig.Channel = ADC_CHANNEL_1;
sConfig.Rank = ADC_REGULAR_RANK_1;
sConfig.SamplingTime = ADC_SAMPLETIME_12CYCLES_5;
sConfig.SingleDiff = ADC_SINGLE_ENDED;
sConfig.OffsetNumber = ADC_OFFSET_NONE;
sConfig.Offset = 0;
HAL_ADC_ConfigChannel(&hadc1, &sConfig);
对于三相电流采样,建议配置DMA实现自动数据传输:
c复制// DMA配置示例
DMA_HandleTypeDef hdma_adc1;
hdma_adc1.Instance = DMA1_Channel1;
hdma_adc1.Init.Direction = DMA_PERIPH_TO_MEMORY;
hdma_adc1.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_adc1.Init.MemInc = DMA_MINC_ENABLE;
hdma_adc1.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
hdma_adc1.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
hdma_adc1.Init.Mode = DMA_CIRCULAR; // 循环模式
hdma_adc1.Init.Priority = DMA_PRIORITY_HIGH;
HAL_DMA_Init(&hdma_adc1);
__HAL_LINKDMA(&hadc1, DMA_Handle, hdma_adc1);
在FOC控制中,电流采样时刻对控制精度影响极大。理想采样点应满足:
具体时间点可通过以下公式计算:
code复制t_sample = t_deadtime + t_delay + t_settling
其中:
ADC采样时间需满足:
code复制t_conv = t_sample + t_hold + (12.5 + n) * t_adcclk
建议:
为确保采样精度,推荐:
code复制f_adc ≥ 5 * f_pwm * N_samples
例如:
使用示波器同时观察:
在ADC中断中翻转GPIO:
c复制void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc) {
HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_0);
}
| 现象 | 可能原因 | 解决方案 |
|---|---|---|
| ADC无触发 | 触发源配置错误 | 检查TIMx_CR2和ADCx_CFGR寄存器 |
| 采样时刻偏移 | 死区时间未补偿 | 调整TIMx_BDTR寄存器 |
| 数据跳动大 | 采样时间不足 | 增加ADC_SAMPLETIME |
| 相位电流失真 | 采样点不在稳定区 | 修改PWM触发时刻 |
c复制hadc1.Init.OversamplingMode = ENABLE;
hadc1.Init.Oversampling.Ratio = ADC_OVERSAMPLING_RATIO_16;
hadc1.Init.Oversampling.RightBitShift = ADC_RIGHTBITSHIFT_4;
c复制TIM_BreakDeadTimeConfigTypeDef sBreakDeadTimeConfig = {0};
sBreakDeadTimeConfig.BreakState = TIM_BREAK_ENABLE;
sBreakDeadTimeConfig.BreakPolarity = TIM_BREAKPOLARITY_LOW;
sBreakDeadTimeConfig.BreakFilter = 0;
sBreakDeadTimeConfig.BreakAFMode = TIM_BREAK_AFMODE_INPUT;
sBreakDeadTimeConfig.Break2State = TIM_BREAK2_DISABLE;
sBreakDeadTimeConfig.AutomaticOutput = TIM_AUTOMATICOUTPUT_ENABLE;
HAL_TIMEx_ConfigBreakDeadTime(&htim1, &sBreakDeadTimeConfig);
在BLDC电机控制实测中,硬件触发同步采样相比软件触发方案展现出明显优势:
特别是在高速电机控制场景(如>20krpm),硬件同步采样几乎成为必需方案。一个实测数据对比如下:
| 指标 | 软件触发 | 硬件触发 |
|---|---|---|
| 采样抖动 | ~500ns | <50ns |
| 相位一致性 | ±3° | ±0.3° |
| CPU负载 | 25% | 8% |
| 转矩波动 | 8% | 3% |
这种实现方式虽然增加了初始配置复杂度,但带来的系统稳定性提升非常值得。我在多个无人机电调项目中验证,硬件触发ADC方案能有效解决高速PWM下的采样同步问题。