4、定时器与采样同步:SysTick定时器、通用定时器配置、ADC采样触发与同步
各位同学,咱们接着往下聊。继电保护装置里,采样同步是个绕不开的坎儿。说白了,就是让ADC在正确的时间点,把电网的电压电流波形给“咔嚓”一下拍下来。拍得不准,后面的算法全是白搭。我当年刚入行时,就吃过这个亏,后面会跟你们细说。
4.1 SysTick定时器:系统的“心跳”
SysTick,这玩意儿是ARM内核自带的,24位递减计数器。你想想看,它就像咱们心脏的起搏器,给整个系统提供稳定的节拍。我个人习惯,一上电就把SysTick配成1ms中断一次,作为操作系统的“心跳”,或者裸机程序里的时间基准。
配置起来其实很简单,就几个寄存器的事:
// 配置SysTick为1ms中断,假设系统时钟为72MHz
// SysTick_Config() 这个库函数,我建议你直接用
if (SysTick_Config(SystemCoreClock / 1000)) {
// 配置失败,进入死循环
while (1);
}
嗯,这里要注意一点:SysTick的优先级。我建议你把它设成最低优先级,为啥?因为它就是个“软时钟”,别让它抢了ADC采样中断这种硬实时任务的道。我曾经在一个项目里,把SysTick优先级设高了,结果采样周期抖得像心电图,查了两天才找到原因。
volatile uint32_t g_ulTickCount。这样你就能在任何地方调用 GetTick() 来获取当前时间戳,做延时或者超时判断都很方便。
4.2 通用定时器:灵活的“采样节拍器”
通用定时器,比如STM32的TIM2、TIM3这些,比SysTick灵活得多。它们有独立的时钟源,可以配置成多种模式。在继电保护里,我主要用它来做两件事:
- 产生精确的PWM波:控制MOSFET或者IGBT的导通角。
- 触发ADC采样:这是咱们今天的重点。
配置通用定时器,核心就是设置预分频器(PSC)和自动重装载值(ARR)。公式很简单:
定时频率 = 定时器时钟 / (PSC + 1) / (ARR + 1)
举个例子,假设定时器时钟是72MHz,我想产生一个10kHz的定时中断:
// 配置TIM3,产生10kHz的更新事件
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
TIM_TimeBaseStructure.TIM_Prescaler = 72 - 1; // 72MHz / 72 = 1MHz
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseStructure.TIM_Period = 100 - 1; // 1MHz / 100 = 10kHz
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE);
TIM_Cmd(TIM3, ENABLE);
你看,这样TIM3就会每100微秒产生一次更新中断。但注意,中断服务程序里别干太多活,否则会影响下一个周期的触发。我一般只在中断里置一个标志位,主循环里再处理。
4.3 ADC采样触发:让定时器“喊”ADC干活
继电保护对采样时刻的要求很苛刻。你不能让CPU在中断里手动去启动ADC,那样会有不确定的延迟。正确的做法是:用定时器的触发事件,直接硬件启动ADC转换。
ARM芯片的ADC,一般都支持多种触发源。比如STM32的ADC,可以配置成由TIM的更新事件或输出比较通道来触发。配置起来也不复杂:
// 配置ADC1,由TIM3的TRGO事件触发
ADC_InitTypeDef ADC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
ADC_InitStructure.ADC_ScanConvMode = DISABLE; // 单通道模式
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; // 单次转换
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T3_TRGO; // TIM3触发
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_NbrOfChannel = 1;
ADC_Init(ADC1, &ADC_InitStructure);
// 配置采样通道和采样时间
ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5);
// 使能ADC
ADC_Cmd(ADC1, ENABLE);
// 校准,这一步我建议你别省
ADC_ResetCalibration(ADC1);
while(ADC_GetResetCalibrationStatus(ADC1));
ADC_StartCalibration(ADC1);
while(ADC_GetCalibrationStatus(ADC1));
然后,在TIM3的配置里,把更新事件(UEV)映射到TRGO上:
TIM_SelectOutputTrigger(TIM3, TIM_TRGOSource_Update);
这样,每当TIM3计数器溢出时,就会产生一个TRGO信号,ADC检测到这个信号后,自动开始转换。整个过程不需要CPU干预,延迟是确定的。
4.4 采样同步:多通道、多芯片的“齐步走”
继电保护装置通常要采集三相电压、三相电流,至少6个模拟量。如果这些通道采样不同步,计算出来的功率、相位角全是错的。你想想看,A相采的是t1时刻的值,B相采的是t1+10us时刻的值,那算出来的三相平衡度还有意义吗?
同步的方法,我总结下来主要有三种:
| 方法 | 原理 | 适用场景 | 我的评价 |
|---|---|---|---|
| 单ADC多通道同步采样 | 一个ADC,通过模拟开关切换通道,但只能顺序采样 | 对同步要求不高的场合 | 不推荐用于保护,通道间有延迟 |
| 多ADC同步采样 | 多个ADC同时触发,同时转换 | 高性能保护装置 | 我最常用的方案,效果好 |
| 外部触发同步 | 用同一个定时器触发所有ADC | 多芯片系统 | 适合分布式采样,但要注意信号延迟 |
我个人最推荐第二种——多ADC同步采样。现在的ARM芯片,比如STM32F4系列,内部集成了3个ADC,可以配置成同步注入模式。你只需要把它们的触发源设成同一个定时器,然后同时启动转换即可。
配置多ADC同步,代码会稍微复杂一点:
// 配置ADC1为主机,ADC2为从机,同步采样
ADC_CommonInitTypeDef ADC_CommonInitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 | RCC_APB2Periph_ADC2, ENABLE);
ADC_CommonInitStructure.ADC_Mode = ADC_DualMode_RegSimult; // 双ADC同步规则模式
ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div4;
ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled;
ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_5Cycles;
ADC_CommonInit(&ADC_CommonInitStructure);
// 然后分别配置ADC1和ADC2的触发源,都设为TIM3的TRGO
// ...(省略具体配置,和前面单ADC类似)
这样配置后,ADC1和ADC2会在同一个TIM3触发信号到来时,同时开始转换。转换完成后,你可以通过DMA把数据读走,或者直接在中断里读取。
4.5 实战中的坑与经验
好了,理论讲完了,咱们聊聊实战。我做过好几个继电保护项目,在采样同步这块踩过的坑,可以给你们当反面教材。
第一个坑:采样频率的选择。 继电保护里,采样频率通常是每周波(20ms)采样32点、64点或128点。也就是1.6kHz、3.2kHz或6.4kHz。我建议你选64点,也就是3.2kHz。为啥?32点算谐波不够用,128点对CPU压力太大。64点是个折中,既能算到13次谐波,CPU也扛得住。
第二个坑:ADC的采样时间。 每个通道的采样时间要足够长,让采样电容充分充电。我一般设成55.5个ADC时钟周期。如果采样时间太短,采样值会偏小,尤其是输入阻抗大的信号源。我曾经在一个项目里,用1.5个周期的采样时间,结果测出来的电压值比实际低了5%,查了三天才发现是采样时间不够。
第三个坑:DMA传输。 采样数据最好用DMA传输到内存,别在中断里一个一个读。DMA可以配置成双缓冲模式(ping-pong buffer),一个缓冲区在存数据,另一个缓冲区在算数据,互不干扰。这样采样和计算可以流水线作业,效率高得多。
嗯,最后总结一句:定时器和采样同步,是继电保护装置的“时间基石”。配好了,后面的算法如鱼得水;配不好,整个装置就是个摆设。希望你们在实际项目中,能把这些经验用上,少走弯路。