嵌入式STM32学习笔记(6)——通用定时器通道频率更改方法

首先原则上STM32通用定时器的4个通道的频率是一样,是由定时器设定频率决定,各个通道可以设置不同的占空比和开启关闭,这些是相互独立的;

但是利用一些特殊方法可以修改定时器不同通道的频率,方法比较讨巧:就是利用CCR1的自动溢出的中断更改频率,用CCR1的值不断的在增加,设置ARR为65535,而65535的二进制是1111111111111111,超出会溢;如果TIMx_CNT=TIM_CCR1 那么就发生中断 ,在中断里他会将CCR1的值加上你自己设定的CCR1Val值,再赋值到CCR1 上,如此循环周期频率就被修改了;

以下是从硬石论坛摘录的:

/

发现步进电机的控制采用的是一个定时器控制一个步进(在每次中断中进行频率的更改)
这种方式控制4个步进电机,不知道是不是会出现CPU资源占用太高的问题?

例如: 如果每路的脉冲频率为100K,那么中断10US要一次。假设进入中断,处理等1路占用1-2us的化,那么4路如果都是全速
到100Khz,是不是就要接近50~80%的CPU都在做步进的控制了? 这样剩余的性能还足够做其他的事情吗?

以上大家一起讨论一下。

我所了解的步进电机的位置控制在STM32的控制中无外乎以下几种方式
1) 采用定时中断  -- 硬石采用的方法, 这种方法最简单直接,但是中断频率一旦上来后CPU的利用率实在太低了。所以一般都是10KHZ一下
2) 采用定时器级联 (一路发,一路计数) 但是这个又太浪费STM32的资源了。
3) 采用定时器的办法,这个方法就不说了。狠容易丢脉冲。
4) DMA的计数通道计数法,这个方法会占用总线资源,但是好在频率高,发送脉冲时几乎不占用CPU资源。 但是DMA的通道资源非常浪费, ST的通讯口又没有FIFO,都要靠DMA,所以这里如何平衡又是问题。
5) STM32 的TIM1 和TIM8特有的循环计数器,配合单脉冲+256的循环计数器,可以降低频率。但是也只有2个。

这个就是我总结的第2)个方法。这个方法好是好,但是对于STM32来说有限的TIMER,counter瞬间被占用了。
所以
1)的方法,适合单轴,或者几个轴速度低于10K以下(10K就是100us,这样CPU占用率还是可以的)
2)的反方,适合2-3轴高速控制,不占用CPU利用率,但是把STM32的TIMER COUNTER都耗尽了。
3)
4)的方法要提前计算,放入两个缓冲(缓冲大小为伺服周期里最大频率脉冲数)用DMA的乒乓操作。采用DMA的循环计数器来做脉冲的计数,也对CPU的占用不多。如果DMA 需求不大其他应用。那么这个方法可以做到4-6轴左右。且CPU占用率不高,就是总线占用率和DMA用来计数了。
5) 如果2轴的话,我推荐用这个方法,比2节省很多资源,只用掉TIM1和TIM8,且CPU占用率极低。资源也占用不高。

但是不知道是否还有其他方法。 (仅用STM32)

///

下面是实验源码(来源网上),在stm32f103上实验过,供大家参考:

//利用CCR1的自动溢出的中断更改频率,
//用CCR1的值不断的在增加,一定要设置ARR为65535
//65535的二进制是1111111111111111,超出会溢出

vu16 CCR1_Val = 32768;
vu16 CCR2_Val = 16384;
vu16 CCR3_Val = 8192;
vu16 CCR4_Val = 4096;

u16 capture = 0;
u16 capture2 = 0;
extern vu16 CCR1_Val;
extern vu16 CCR2_Val;
extern vu16 CCR3_Val;
extern vu16 CCR4_Val;

void TIM_Configuration(void)
{  
     //1.设置TIM2 pwm波对应引脚
    GPIO_InitTypeDef GPIO_InitStructure;
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    /* GPIOA Configuration:TIM2 Channel1, 2, 3 and 4 in Output */
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
  
    //2.中断设置
     NVIC_InitTypeDef NVIC_InitStructure;
    /* Configure one bit for preemption priority */
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
    NVIC_InitStructure.NVIC_IRQChannel=TIM2_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority=1;
    NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
    NVIC_Init(&NVIC_InitStructure);
     
    //3.设置通用定时器,分频及初始化
    TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
    TIM_OCInitTypeDef TIM_OCInitStructure;
    /* TIM2 clock enable */
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
    /* ---------------------------------------------------------------
    TIM2 Configuration: Output Compare Toggle Mode:
    TIM2CLK = 36 MHz, Prescaler = 0x2, TIM2 counter clock = 12 MHz
    CC1 update rate = TIM2 counter clock / CCR1_Val = 366.2 Hz
    CC2 update rate = TIM2 counter clock / CCR2_Val = 732.4 Hz
    CC3 update rate = TIM2 counter clock / CCR3_Val = 1464.8 Hz
    CC4 update rate = TIM2 counter clock / CCR4_Val = 2929.6 Hz
    --------------------------------------------------------------- */
    /* Time base configuration */
    TIM_TimeBaseStructure.TIM_Period = 65535; //65535的二进制是1111111111111111,会溢出
    //无中断操作时候 183.1       
    TIM_TimeBaseStructure.TIM_Prescaler = 2;      
    TIM_TimeBaseStructure.TIM_ClockDivision = 0;   
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
    TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
   
    //4.初始化TIM Channel 通道及PWM模
    /* Channel 1 Configuration in PWM mode */
    TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Toggle;      //PWM模式2
    TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //正向通道有效
    TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Disable;//反向通道无效
    TIM_OCInitStructure.TIM_Pulse = CCR1_Val;         //占空时间
    TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;    //输出极性
    TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCNPolarity_High;     //互补端的极性
    TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Set;
    TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCIdleState_Reset;

    TIM_OC1Init(TIM2,&TIM_OCInitStructure);        //通道1
    TIM_OC1PreloadConfig(TIM2, TIM_OCPreload_Disable);

    TIM_OCInitStructure.TIM_Pulse = CCR2_Val;         //占空时间
    TIM_OC2Init(TIM2,&TIM_OCInitStructure);        //通道2
    TIM_OC2PreloadConfig(TIM2, TIM_OCPreload_Disable);

    TIM_OCInitStructure.TIM_Pulse = CCR3_Val;         //占空时间
    TIM_OC3Init(TIM2,&TIM_OCInitStructure);        //通道3
    TIM_OC3PreloadConfig(TIM2, TIM_OCPreload_Disable);

    TIM_OCInitStructure.TIM_Pulse = CCR4_Val;         //占空时间
    TIM_OC4Init(TIM2,&TIM_OCInitStructure);        //通道4
    TIM_OC4PreloadConfig(TIM2, TIM_OCPreload_Disable);

    /* TIM2 counter enable */
    TIM_Cmd(TIM2,ENABLE);

    /* TIM2 Main Output Enable */
    //TIM_CtrlPWMOutputs(TIM2,ENABLE);

    /* TIM IT enable */
    TIM_ITConfig(TIM2, TIM_IT_CC1 | TIM_IT_CC2 | TIM_IT_CC3 | TIM_IT_CC4, ENABLE);
}

void TIM2_IRQHandler(void)
{
    /* TIM2_CH1 toggling with frequency = 366.2Hz */
    if (TIM_GetITStatus(TIM2, TIM_IT_CC1) != RESET)
    {
	TIM_ClearITPendingBit(TIM2, TIM_IT_CC1 );
//	capture =TIM_GetCapture1(TIM2);
//	TIM_SetCompare1(TIM2, capture + CCR1_Val );
	
	capture2 =TIM_GetCapture1(TIM2);//测试
//	TIM_SetCompare1(TIM2, capture2 + CCR1_Val );
	TIM_SetCompare1(TIM2, capture2+30000);//
	
//	OCToggle的运行方式基本是这样的:
//       如果TIMx_CNT=TIM_CCR1 那么就发生上面的中断 
//	在中断里 他会将CCR1的值加上你自己设定的CCR1Val值 
//	再赋值到CCR1 上 每发生一次中断 就会将电平翻转(toggle)
	
    }

    /* TIM2_CH2 toggling with frequency = 732.4Hz */
    if (TIM_GetITStatus(TIM2, TIM_IT_CC2) != RESET)
    {
	TIM_ClearITPendingBit(TIM2, TIM_IT_CC2);
	capture= TIM_GetCapture2(TIM2);
	TIM_SetCompare2(TIM2, capture + CCR2_Val);
    }

    /* TIM2_CH3 toggling with frequency = 1464.8Hz */
    if (TIM_GetITStatus(TIM2, TIM_IT_CC3) != RESET)
    {
	TIM_ClearITPendingBit(TIM2, TIM_IT_CC3);
	capture = TIM_GetCapture3(TIM2);
	TIM_SetCompare3(TIM2, capture + CCR3_Val);
    }

    /* TIM2_CH4 toggling with frequency = 2929.6 Hz */
    if (TIM_GetITStatus(TIM2, TIM_IT_CC4) != RESET)
    {
	TIM_ClearITPendingBit(TIM2, TIM_IT_CC4);
	capture = TIM_GetCapture4(TIM2);
	TIM_SetCompare4(TIM2, capture + CCR4_Val);
    }
}

 

  • 0
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值