你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

【经验分享】STM32单片机一个定时器输出不同频率PWM波

[复制链接]
STMCU小助手 发布时间:2022-3-18 22:13
  在使用STM32单片机输出PWM波形的时候,通常可以直接使用定时器提供的PWM模式。可以通过自动重装载寄存器(TIMx_ARR)来设置定时器的输出频率,然后通过捕获/ 比较寄存器 1(TIMx_CCRx)来设置占空比。一个定时器只有一个自动重装载寄存器(TIMx_ARR),但是有4个通道的捕获/ 比较寄存器 1(TIMx_CCR1、TIMx_CCR2、TIMx_CCR3、TIMx_CCR4)。所以使用一个定时器输出PWM波形的时候,频率是统一调整的,4个通道的频率是相同的,但是占空比每个通道可以独立设置。比较寄存器TIMx_CCR1、TIMx_CCR2、TIMx_CCR3、TIMx_CCR4分别设置4个通道的占空比。

  如果使用中央对齐模式计数的话,那么定时器的计数波形如下:
`(OQAIT)Z0`{LP[(Y]~$K9P.png

  三角波就是计数器计数的方式,从0开始逐渐增大到装载值ARR,然后在减小到0。蓝色的波形就是比较寄存器的值CCR,计数器在计数的时候,每次都会将计数值和比较寄存器的值CCR进行比较,当计数器值小于CCR的值时,输出低电平。当计数器的值大于CCR的值时,输出高电平。

K$Q`{652@TWPP)9KKT@LD`P.png

  当需要改变占空比的时候,只需要改变比较寄存器CCR的值就行了。

KM13}J___0`@7A@AJ@XG5WB.png

  通过改变ARR的值设置输出PWM波形的频率,通过改变CCR的值改变PWM波的占空比。但是用这种方式输出PWM波时,一个定时器的4个通道输出的PWM波频率都是一样的,那么能不能用一个定时器输出4路不同频率的PWM波呢?
  通过观察捕获/ 比较模式寄存器 1(TIMx_CCMR1)中的OC1M位时可以发现,除了PWM模式外还有一个翻转模式,在翻转模式下,当计数器CNT的值等于比较寄存器CCR的值时,输出的电平就会翻转。

QKXSGN@UT28(A4NM`B{G60F.png


  那么就可以通过这个功能,在输出PWM波的过程中不停的去改变CCR的值,那么输出的电平就会不停的翻转,这样就可以手动改变PWM波的频率了。

[O%Q`[U9HY[NBV]D8(I5SWE.png

  比如第一次将CCR的值设置为CCR1,然后使能比较中断,当计数器的值CNT等于CCR1时,就会触发中断,然后在中断中将CCR的值修改为CCR2,。接下来当计数器的值等于CCR2时,又会触发中断,在中断中继续设置下一次CCR的值。这样每触发一次中断后,输出的PWM波形电平就会翻转一次。在计数器从0到ARR计数期间,PWM的波形可以翻转好多次。在PWM模式中,每一个计数周期波形只能翻转一次,但是在翻转模式下,一个计数周期可以翻转好多次,这样就可以改变输出PWM波形的频率了,同样每次设置不同的CCR值时,也可以改变占空比。

  下面就通过代码来实现。

  1. #include "timer2.h"
  2. //比较值
  3. u16 CCR1_Val = 32768;
  4. u16 CCR2_Val = 16384;
  5. u16 CCR3_Val = 8192;
  6. u16 CCR4_Val = 4096;

  7. void TIM2_PWM_Init( u16 arr, u16 psc )
  8. {

  9.     TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
  10.     NVIC_InitTypeDef NVIC_InitStructure;
  11.     TIM_OCInitTypeDef TIM_OCInitStructure;
  12.     GPIO_InitTypeDef GPIO_InitStructure;

  13.         //使能时钟
  14.     RCC_APB1PeriphClockCmd( RCC_APB1Periph_TIM2, ENABLE );
  15.     RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA, ENABLE );

  16.     //设置IO口
  17.     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3;
  18.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
  19.     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  20.     GPIO_Init( GPIOA, &GPIO_InitStructure );

  21.     //设置中断优先级
  22.     NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
  23.     NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x01;
  24.     NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x01;
  25.     NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  26.     NVIC_Init( &NVIC_InitStructure );

  27.     //设置定时器基本参数
  28.     TIM_TimeBaseInitStructure.TIM_Period = arr;
  29.     TIM_TimeBaseInitStructure.TIM_Prescaler = psc;
  30.     TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;                                //只有高级定时器需要设置,其他定时器可以不设置。
  31.     TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
  32.     TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
  33.     TIM_TimeBaseInit( TIM2, &TIM_TimeBaseInitStructure );

  34.     //设置工作模式
  35.     TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Toggle;                                        //设置定时器工作在翻转模式
  36.     TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
  37.     TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;

  38.     //设置4个通道 比较寄存器值
  39.     TIM_OCInitStructure.TIM_Pulse = CCR1_Val;
  40.     TIM_OC1Init( TIM2, &TIM_OCInitStructure );                                                        //TIM2_OC1   PA0

  41.     TIM_OCInitStructure.TIM_Pulse = CCR2_Val;
  42.     TIM_OC2Init( TIM2, &TIM_OCInitStructure );                                                        //TIM2_OC2   PA1

  43.     TIM_OCInitStructure.TIM_Pulse = CCR3_Val;
  44.     TIM_OC3Init( TIM2, &TIM_OCInitStructure );                                                        //TIM2_OC1   PA2

  45.     TIM_OCInitStructure.TIM_Pulse = CCR4_Val;
  46.     TIM_OC4Init( TIM2, &TIM_OCInitStructure );                                                        //TIM2_OC1   PA3

  47.     //禁止自动预装载
  48.     TIM_OC1PreloadConfig( TIM2, TIM_OCPreload_Disable );
  49.     TIM_OC2PreloadConfig( TIM2, TIM_OCPreload_Disable );
  50.     TIM_OC3PreloadConfig( TIM2, TIM_OCPreload_Disable );
  51.     TIM_OC4PreloadConfig( TIM2, TIM_OCPreload_Disable );

  52.     //使能定时器
  53.     TIM_Cmd( TIM2, ENABLE );
  54.     TIM_ITConfig( TIM2, TIM_IT_CC1 | TIM_IT_CC2 | TIM_IT_CC3 | TIM_IT_CC4, ENABLE );
  55. }
复制代码

  首先初始化定时器,初始化的两个参数arr和psc分别设置定时器的自动重装载值和分频系数。这里将arr的自动装载值设置为最大0xFFFF,也就是65535,相当于计数器从0开始计数,直到65535才结束。将分频系数psc设置为0,也就是不分频,定时器按照72MHz的频率工作。

  将定时器的工作模式设置为翻转模式,然后依次给4个通道设置比较值。这里要将自动重装载功能关闭,否则就不能手动的更改比较值了。最后使能定时器和4个通道的比较中断。

  接下来就是最重要的环节了,在中断中改变每个通道的频率和占空比。

  1. //占空比 0 --- 100
  2. u16 CCR1_dc = 20;
  3. u16 CCR2_dc = 40;
  4. u16 CCR3_dc = 60;
  5. u16 CCR4_dc = 80;

  6. u32 capture = 0;
  7. u8 flag1 = 0, flag2 = 0, flag3 = 0, flag4 = 0;
  8. u16 setcap = 0;

  9. void TIM2_IRQHandler( void )
  10. {
  11.     if( TIM_GetITStatus( TIM2, TIM_IT_CC1 ) != RESET )
  12.     {
  13.         TIM_ClearITPendingBit( TIM2, TIM_IT_CC1 );
  14.         capture = TIM_GetCapture1( TIM2 );
  15.         //设置占空比
  16.         if( flag1 == 0 )
  17.         {
  18.             flag1 = 1;
  19.             setcap = capture + ( u32 )CCR1_Val * CCR1_dc / 100;
  20.         }
  21.         else
  22.         {
  23.             flag1 = 0;
  24.             setcap = capture + ( u32 )CCR1_Val  * ( 100 - CCR1_dc ) / 100;
  25.         }
  26.         TIM_SetCompare1( TIM2, setcap  );
  27.     }

  28.     if( TIM_GetITStatus( TIM2, TIM_IT_CC2 ) != RESET )
  29.     {
  30.         TIM_ClearITPendingBit( TIM2, TIM_IT_CC2 );
  31.         capture = TIM_GetCapture2( TIM2 );
  32.         if( flag2 == 0 )
  33.         {
  34.             flag2 = 1;
  35.             setcap = capture + CCR2_Val * CCR2_dc / 100;
  36.         }
  37.         else
  38.         {
  39.             flag2 = 0;
  40.             setcap = capture + CCR2_Val  * ( 100 - CCR2_dc ) / 100;
  41.         }
  42.         TIM_SetCompare2( TIM2, setcap );
  43.     }


  44.     if( TIM_GetITStatus( TIM2, TIM_IT_CC3 ) != RESET )
  45.     {
  46.         TIM_ClearITPendingBit( TIM2, TIM_IT_CC3 );
  47.         capture = TIM_GetCapture3( TIM2 );

  48.         if( flag3 == 0 )
  49.         {
  50.             flag3 = 1;
  51.             setcap = capture + CCR3_Val * CCR3_dc / 100;
  52.         }
  53.         else
  54.         {
  55.             flag3 = 0;
  56.             setcap = capture + CCR3_Val  * ( 100 - CCR3_dc ) / 100;
  57.         }
  58.         TIM_SetCompare3( TIM2, setcap );
  59.     }

  60.     if( TIM_GetITStatus( TIM2, TIM_IT_CC4 ) != RESET )
  61.     {
  62.         TIM_ClearITPendingBit( TIM2, TIM_IT_CC4 );
  63.         capture = TIM_GetCapture4( TIM2 );

  64.         if( flag4 == 0 )
  65.         {
  66.             flag4 = 1;
  67.             setcap = capture + CCR4_Val * CCR4_dc / 100;
  68.         }
  69.         else
  70.         {
  71.             flag4 = 0;
  72.             setcap = capture + CCR4_Val  * ( 100 - CCR4_dc ) / 100;
  73.         }
  74.         TIM_SetCompare4( TIM2, setcap );
  75.     }

  76. }
复制代码

  进入中断后,首先读取当前比较寄存器的值,因为比较寄存器的值是会一直累加的,直到比较寄存器的值等于ARR的时,才会清零。所以每次中断的时候,需要将现在的比较值读出来,然后加上一个值,作为下一次的比较值。读出当前的比较值存放在capture中,接下来需要在加一个值,作为下一次的比较值。因为要改变占空比,所以增加的值有两种情况,分别是高电平持续时间值和低电平持续时间值。为了区分高低电平,这里使用的一个标志位来判断,标志位的值只有0和1两种情况,将0作为高电平的时间,将1作为低电平的时间。CCRx_Val 中存放整个周期的计数值,当标志位为0时,CCRx_Val 乘以高电平的百分比,计算出高电平的累加时间。当标志位为1时,CCRx_Val 乘以低电平的百分比,计算出低电平的累加时间。CCRx_dc 中存放占空比的值,范围是0到100,表示占空比从0%到100%。 当需要改变周期时,就修改每个通道的 CCRx_Val 值,当需要改变占空比时,就修改每个通道的 CCRx_dc 的值。

  1. int main( void )
  2. {
  3.     u16 i = 0;
  4.     delay_init();
  5.     NVIC_PriorityGroupConfig( NVIC_PriorityGroup_2 );
  6.     TIM2_PWM_Init( 65535, 0 );
  7.     while( 1 )
  8.     {
  9.     }
  10. }
复制代码

  接下来在主函数中初始化定时器,定时器2的4个通道输出波形如下:

38$@}E}78QD8CQX(RTY%7[L.png

  通过波形可以看到,定时器2的四个通道频率和占空比都是不一样的。说明通过定时器中的比较模式,是可以实现同一个定时器输出不同频率PWM波的。



收藏 评论0 发布时间:2022-3-18 22:13

举报

0个回答

所属标签

相似分享

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版