4. ADC同步采样:GD32 ADC注入组与规则组配置,采样窗口与PWM触发同步
做FOC控制,电流采样是第一步,也是最关键的一步。采样不准,后面算得再漂亮也是白搭。GD32的ADC有个很实用的设计——规则组和注入组。说白了,规则组就是常规任务,注入组就是高优先级的中断任务。在电机控制里,我们通常用规则组做三相电流的同步采样,用注入组处理一些紧急的辅助测量。
4.1 规则组与注入组的角色分工
我刚开始用GD32做FOC时,也想过用单通道轮流采样。结果发现电流波形在换相点附近总是有毛刺。后来仔细看了手册,才明白规则组和注入组的区别。
| 特性 | 规则组 | 注入组 |
|---|---|---|
| 优先级 | 低,可被注入组打断 | 高,可打断规则组转换 |
| 触发方式 | PWM定时器触发,适合同步采样 | 软件触发或外部事件,适合紧急测量 |
| 典型用途 | 三相电流Ia、Ib、Ic同步采样 | 母线电压、温度等慢速信号 |
| 数据寄存器 | 共享一个数据寄存器,需DMA搬运 | 每个通道有独立数据寄存器 |
你想想看,规则组就像流水线上的工人,按顺序干活。注入组就像车间主任,随时可以插队。在FOC里,我们最怕的就是电流采样被其他任务干扰,所以把三相电流采样放在规则组,用PWM触发,保证每次都在同一个电角度位置采样。
4.2 PWM触发同步:采样窗口的精确控制
为什么要用PWM触发ADC?因为FOC需要知道电流在PWM周期内的确切时刻。我见过有人用定时器中断去启动ADC,结果中断响应时间抖动,采样点飘来飘去,电流环带宽怎么也上不去。
GD32的定时器可以输出TRGO信号,直接触发ADC转换。这个延迟是硬件级别的,只有几个时钟周期,非常稳定。
核心要点:采样窗口必须避开PWM开关噪声。通常设置在PWM计数器的中间点(即占空比50%附近),此时上下桥臂都处于导通状态,电流采样最准确。
我个人习惯把采样点放在PWM周期的中心对齐位置。这样采样到的电流是周期平均值,而不是峰值或谷值。对于FOC控制来说,平均值更有意义。
4.3 配置实战:规则组+DMA+注入组
下面是我在一个量产项目里用过的配置代码。这个配置实现了:PWM触发规则组采样三相电流,DMA自动搬运结果,注入组用于母线电压监测。
// ADC规则组配置:3通道,PWM触发
void adc_regular_config(void)
{
rcu_periph_clock_enable(RCU_ADC0);
rcu_periph_clock_enable(RCU_DMA0);
// ADC时钟配置:14分频,约6MHz
adc_clock_config(ADC_ADCCK_PCLK2_DIV14);
// 规则组序列:通道0、1、2
adc_regular_channel_config(0, ADC_CHANNEL_0, ADC_SAMPLETIME_55POINT5);
adc_regular_channel_config(1, ADC_CHANNEL_1, ADC_SAMPLETIME_55POINT5);
adc_regular_channel_config(2, ADC_CHANNEL_2, ADC_SAMPLETIME_55POINT5);
// 触发源:TIMER0的TRGO
adc_regular_trigger_config(ADC_REGULAR_TRIGGER_TIMER0_CH0);
// 开启DMA
adc_dma_mode_enable();
dma_config();
}
// 注入组配置:1通道,软件触发
void adc_injected_config(void)
{
// 注入组通道:通道16(内部温度传感器)
adc_injected_channel_config(0, ADC_CHANNEL_16, ADC_SAMPLETIME_239POINT5);
// 注入组触发:软件触发
adc_injected_trigger_config(ADC_INJECTED_TRIGGER_SW);
// 注入组自动转换
adc_injected_auto_sequence_enable();
}
// DMA配置:循环模式,搬运3个数据
void dma_config(void)
{
dma_parameter_struct dma_init;
dma_deinit(DMA0, DMA_CH0);
dma_init.direction = DMA_PERIPHERAL_TO_MEMORY;
dma_init.memory_addr = (uint32_t)&adc_buffer;
dma_init.memory_inc = DMA_MEMORY_INCREASE_ENABLE;
dma_init.memory_width = DMA_MEMORY_WIDTH_16BIT;
dma_init.number = 3;
dma_init.periph_addr = (uint32_t)&ADC_RDATA;
dma_init.periph_inc = DMA_PERIPH_INCREASE_DISABLE;
dma_init.periph_width = DMA_PERIPH_WIDTH_16BIT;
dma_init.priority = DMA_PRIORITY_HIGH;
dma_circulation_enable(DMA0, DMA_CH0);
dma_init(DMA0, DMA_CH0, &dma_init);
dma_channel_enable(DMA0, DMA_CH0);
}
经验之谈:采样时间设置很关键。55.5个ADC时钟周期对于一般电机应用足够了。如果电机转速很高,可以适当减小采样时间,但要注意信号建立时间。我曾经遇到过采样时间设太短,结果高速时电流波形失真,查了两天才发现是采样窗口不够宽。
4.4 采样窗口与PWM同步的时序分析
为什么采样窗口要精确对齐?我们来看一个典型的PWM周期时序图。
从时序图可以看出,采样点必须避开开关切换时刻。GD32的定时器可以配置在计数器的上溢或下溢事件时触发ADC。我建议使用上溢触发,因为此时PWM周期刚刚结束,电流已经稳定。
4.5 避坑指南:我曾经踩过的坑
做这个配置时,有几个地方特别容易出问题。我一个个说。
- DMA地址对齐问题:GD32的DMA要求外设地址按字对齐。ADC数据寄存器地址是0x40012400,这个没问题。但如果你用结构体存放ADC数据,要注意成员变量的对齐。我曾经因为结构体里插了个uint8_t变量,导致DMA搬运错位,电流数据全乱了。
- 注入组优先级冲突:注入组可以打断规则组转换。如果注入组转换时间太长,会严重影响规则组的采样周期。我建议注入组只做慢速测量,比如每100ms测一次温度,不要频繁触发。
- 采样时间与阻抗匹配:ADC的采样电容需要外部信号源充电。如果信号源阻抗太大,采样时间不够,采样值会偏小。我遇到过用分压电阻测母线电压,分压电阻太大导致采样不准。后来加了电压跟随器才解决。
特别注意:GD32的ADC有两次采样转换之间的最小间隔要求。如果PWM频率很高(比如20kHz以上),要确保ADC转换时间能跟上。我一般把ADC时钟设在6-8MHz,采样时间55.5个周期,这样单次转换约9μs,可以支持到100kHz的PWM频率。
4.6 同步采样的性能验证
配置完成后,怎么验证采样是否同步?我通常用两种方法:
- 示波器观察:用DAC输出采样值,和PWM信号对比。如果采样点稳定在PWM周期的固定位置,说明同步成功。
- 软件统计:在中断里记录采样时刻的定时器计数值。如果计数值抖动小于±2个时钟周期,说明硬件触发延迟非常稳定。
我记得有一次调试,发现采样值在低速时正常,高速时出现周期性波动。后来用示波器一看,原来是PWM频率提高后,采样窗口正好落在了开关噪声区。调整了采样触发相位后,问题就解决了。
嗯,ADC同步采样这部分内容就这些。核心就是:规则组+DMA做三相电流同步采样,注入组做辅助测量,采样窗口必须避开开关噪声。把这个基础打牢了,后面的电流环调优才能顺利进行。