【电机控制】STM32使用TIM触发ADC,CUBEMX配置(用于电机控制电流采样)

无论是做直流有刷电机的电流环,还是做BLDC方波的电流环,亦或是做FOC的电流环,都需要进行电流采样,需要精确控制采样时间与采样点,因此电流采样是电机控制的重中之重,这一个环节非常重要。电流环一般是在每个PWM周期都进行一次的,所以频率较高,有些人做了平均电流的电流环,频率低些,有些人是用瞬时电流值来做电流环,也就是电流环的周期就等于PWM的周期。电机的PWM周期一般都是15K以上(这是由于15K频率是人耳能听到的最高频率范围,超过15K,人耳就听不到那个高频的声音了,在静音方面也有这方面的原因)。

这里以BLDC方波控制的电流采样为例,详细讲述使用CUBEMX配置结合寄存器如何操作电流采样,为后续的电流环做准备。后续做FOC就是需要SVPWM的电流采样,各位不要着急,后续我会把CSDN博客的文章搬运过来,再稍作修改。


首先,我们要明确电流环的采样点到底是在哪,如果是随机进行的ADC采样,那么电流采样点就会随机出现在相对于PWM的任意时刻,要知道,电流在PWM为高时是上升的,在PWM为低时是下降的,甚至有可能为0,如果这样操作,多组数据平均一下,就是平均电流,但这样不是我们要的电流环,我们要的电流环需要瞬时电流。因此采样点需要固定在PWM发波的高电平的中间,要知道MOS管在打开时是有一个尖端脉冲的,此时采样易受干扰,因此放在中间,示意图如下:

这样可以确定采样出来的电流是真实的电机电流,应该是会比平均电流大些的。因为我这边PWM产生是用TIM1的互补PWM通道,而TIM1又是由TIM3的COM事件来触发的,现在我需要有另外一个定时器来产生一个跟TIM1周期相同,但是PWM的值是TIM1的一半的波形,这里我选择的是TIM15,因此需要保证TIM15的CCR寄存器的值为TIM1的一半,因此,可以直接在TIM1的溢出中断的回调函数中更新TIM15的CCR寄存器的值:

 

接下来就是要保证TIM1与TIM15同步了,因为TIM1是TIM3(霍尔定时器)的从定时器,那么,将TIM15也配置为TIM3的从定时器就可以了,由寄存器参考手册可知,TIM15的触发源是ITR1:

 

然后将CUBEMX的触发源选择为ITR1,并且选择复位模式,选择内部时钟:

 

在配置好触发源之后,我们希望的是使用TIM15的PWM结束的时候,这样讲有些歧义吧,换个说法就是咋TIM15的计数值CNT达到CCR的值的之后,触发ADC开启采集,那也就是说TIM15需要开启主从模式,并且需要发送一个OC1REF信号(因为使用的是1通道):

在这里需要说明一下这个OC1REF信号的产生必须是在PWM模式下的,最开始我也不清楚,连同公司的软件大佬一起搞这个问题搞了两天,依旧是发送不出来这个OC1REF信号,最后还是看到了一篇博客:http://blog.sina.com.cn/s/blog_155ff95b20102wwen.html,
里面最后一段话有提到这个:

 

我之前没有配置PWM模式的CUBEMX是这样的:

 

一个原因是因为我不知道这个知识点,另一个原因是因为确实我的引脚资源不够用了,已经没有引脚给我配置PWM了,后来看了那篇博客之后,将TIM15配置改为:

 

当然理论上,配置成Output Compare No Output模式,然后TIM15的TRGO信号使用OC1信号也是可以的,这个我没有测试。也是参考了那篇博客,我将ADC改为了上升沿触发,而要使用上升沿触发,那么只需要将TIM15改为PWM2模式就可以了:

 

接下来就是ADC那边的配置了,因为我们是要在PWM高电平的中间采集一次,下一次PWM再采集下一次,因此需要将ADC配置为单次采集,至于DMA配置的话,正常模式与循环模式都是可以的,只不过是多一句重新启动的问题,我采用的是DMA循环模式,而触发信号就选择TIM15的处罚信号,上升沿触发,配置如下:

 

 

实际用示波器的电流如图:

 

方框内为发波时的电流波形,我现在把负载调小了些,所以电流小了些。
实际STM32读出来的电流波形如图:(使用了串口打印出来,每个PWM周期采集一次,即66us采集一次)

 

  • 11
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
你可以使用STM32的定时器(TIM)和DMA来触发ADC的转换和数据传输。下面是一个简单的示例代码,演示了如何使用TIM触发ADC和DMA进行采样: ```c #include "stm32f4xx.h" #define ADC_BUFFER_SIZE 100 uint16_t adc_buffer[ADC_BUFFER_SIZE]; void TIM_Configuration(void) { RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_TimeBaseStructure.TIM_Period = 1000; // 定时器周期为1000(在72MHz下,定时器时钟为72MHz/1000 = 72kHz) TIM_TimeBaseStructure.TIM_Prescaler = 71; // 预分频器为71(定时器时钟为72kHz/72 = 1kHz) TIM_TimeBaseStructure.TIM_ClockDivision = 0; TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); TIM_SelectOutputTrigger(TIM2, TIM_TRGOSource_Update); // 选择更新事件作为触发信号 TIM_Cmd(TIM2, ENABLE); } void ADC_DMA_Configuration(void) { RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE); DMA_InitTypeDef DMA_InitStructure; DMA_InitStructure.DMA_Channel = DMA_Channel_0; DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR; DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)&adc_buffer; DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory; DMA_InitStructure.DMA_BufferSize = ADC_BUFFER_SIZE; DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; DMA_InitStructure.DMA_Priority = DMA_Priority_High; DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable; DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull; DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single; DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; DMA_Init(DMA2_Stream0, &DMA_InitStructure); DMA_Cmd(DMA2_Stream0, ENABLE); ADC_CommonInitTypeDef ADC_CommonInitStructure; ADC_CommonInitStructure.ADC_Mode = ADC_Mode_Independent; ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div2; ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_1; ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_5Cycles; ADC_CommonInit(&ADC_CommonInitStructure); ADC_InitTypeDef ADC_InitStructure; ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b; ADC_InitStructure.ADC_ScanConvMode = DISABLE; ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; // 连续转换模式 ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_Rising; ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T2_TRGO; // 使用TIM2的触发信号 ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; ADC_InitStructure.ADC_NbrOfConversion = 1; ADC_Init(ADC1, &ADC_InitStructure); ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_3Cycles); // 配置ADC通道0 ADC_DMARequestAfterLastTransferCmd(ADC1, ENABLE); ADC_DMACmd(ADC1, ENABLE); ADC_Cmd(ADC1, ENABLE); ADC_SoftwareStartConv(ADC1); } int main(void) { TIM_Configuration(); ADC_DMA_Configuration(); while (1) { // 程序主循环 } } ``` 上述代码中,我们使用TIM2定时器的更新事件作为ADC触发信号,并使用DMA2的Stream 0将ADC转换结果传输到adc_buffer数组中。请根据你的需求进行修改和适配。注意,此示例代码基于STM32F4系列微控制器,并使用了相应的库函数。如果你使用的是其他型号的STM32微控制器,请根据其参考手册和库函数进行相应的修改和调整。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值