zoukankan      html  css  js  c++  java
  • STM32定时器应用——PWM

    STM32的定时器有三种,高级定时器(TIM1和TIM8),通用定时器(TIM2、TIM3、TIM4、TIM5)和基本定时器(TIM6和TIM7)。

    这三者的区别是:

    • 基本定时器:基本定时器功能比较简单,主要是计时,也可以为DAC提供时钟,直接触发驱动DAC
    • 通用定时器:通用定时器除了基本的定时功能外,还可以测量输入信号的脉冲长度,也就是输入捕获功能,也可以产生输出波形,即输出比较和PWM。                                                
    • 高级定时器:通用定时器有的功能,高级定时器也有,而且高级定时器还可以输出嵌入死区的互补PWM。 

    这几天放假,在家搜到一个NUCLEO-L010RB的开发板,并且在stm社区下载了相关的几份资料,就捣鼓了一下。

    这个板子用到的MCU是STM32L010RBT6,这个板子刚开始我还不知道怎么用,不知道烧录程序时是否需要外接ST-Link,最后捣鼓着捣鼓着发现它本身自带ST-Link的功能,USB端口不仅可以给板子供电,还可以用于烧录程序。

    第一个程序当然是点亮一个LED灯啦,查阅相关文档发现这个板子的PA5引脚上接了LED2,而且该引脚也是TIM2的CH1通道,那么就可以通过TIM2的CH1输出PWM波控制LED的亮度,实现呼吸灯效果。

    一、首先用CubeMX工具生成代码框架:

     这个MCU跟STM32的其他系列有很多的不同,比如定时器方面,这个单片机只有TIM2,TIM21和TIM22。这个程序中用到了TIM2和TIM21这两个定时器,TIM2用于生成PWM调节LED的亮度,TIM21用于定时修改PWM的占空比。

    ①设置时钟源,原理图上HSE上接的是8MHz的晶振,但是实物上实际并没有外接这个晶振,所以HSE没有配置。LSE上接了32.768KHz的晶振,所以LSE上就选择了Crystal/Ceramic Resonator。如图:

     

     ②定时器设置,如图:

     

    • 定时器的时钟源一般都选择内部时钟源。
    • 这次我们用到TIM2的CH1输出PWM波,所以Channel1应该选择PWM Generation CH1。
    • TIM2挂载在APB1上,而APB1的时钟频率为32MHz,所以TIM2的预分频器设置为32000,分频后得到1KHz,也就是1ms计数一次,计数模式选择向上,ARR设置为20。
    • PWM的模式设置为 PWM mode 1,Pulse设置为0。PWM的模式有两种,模式1:向上计数时,CNT<CCRx时输出有效电平,CNT>=CCRx时输出无效电平。向下计数时,CNT>CCRx时输出无效电平,CNT<=CCRx输出有效电平。模式2:向上计数时,CNT<CCRx时输出无效电平,CNT>=CCRx时输出有效电平。向下计数时,CNT>CCRx时输出有效电平,CNT<=CCRx输出无效电平。二者刚好是相反的。(Pulse就是CCRx的值,这里初始化为0,当然也可以设置为小于ARR大于0的其他数值)
    • CH Polarity选择为High,也就是设置有效电平为高电平。

      TIM21的作用只是定时而已,所以设置比较简单,同样时钟源选择内部时钟,预分频32000,向上计数,ARR为100,因为TIM21挂载在APB2上也是32MHz,所以最终的结果是100ms产生一个中断(TIM21的全局中断需要打开)

     打开TIM21的全局中断:

     ③时钟配置:由图可知,外部高速时钟(HSE)的外部晶振和外部低速时钟(LSE)跟整个时钟树的其他部分是断开的,所以我选择了内部高速时钟(HSI)作为系统时钟源,不过精度肯定没有外部晶振准确,因为HSI采用的是RC振荡器,容易受到温度的影响。

    二、代码修改

    ①首先需要在main()函数里面调用两个函数:

    HAL_TIM_Base_Start_IT(&htim21);//开启TIM21的中断
    HAL_TIM_PWM_Start(&htim2,TIM_CHANNEL_1);//开启PWM的输出
    

    ②在TIM21中断的回调函数里增加功能代码:

    1 void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
     2 {
     3     static uint8_t DutyCycle = 5;
     4     static uint8_t ToggleFlag = 0;
     5     if(htim->Instance == TIM21)
     6     {
     7         if(ToggleFlag == 0)
     8         {
     9             DutyCycle += 1;
    10             if(DutyCycle > 19)
    11             {
    12                 DutyCycle = 19;
    13                 ToggleFlag = 1;
    14             }
    15         }
    16         else
    17         {
    18             DutyCycle -= 1;
    19             if(DutyCycle <= 1)
    20             {
    21                 ToggleFlag = 0;
    22             }
    23         }
    24         
    25         TIM2->CCR1 = DutyCycle;
    26     }
    27 }

    TIM21每100ms进入一次中断,就会调用这个回调函数,这个函数里实现的功能就是修改TIM2的CCR1的值,也就是修改PWM的占空比,CCR1每次比上一次的数值增加1(直到19(ARR的值),也就是占空比为1),当CCR1的值达到了19,又开始递减。所以LED的效果就是从暗逐渐变量又从亮逐渐变暗,就好像呼吸一样。关系到呼吸灯的效果有几个参数:

      ①htim2.Init.Prescaler(TIM2的预分频系数),htim2.Init.Period(TIM2 的计数周期,也就是ARR的值),预分频系数越大,得到的频率越小,CNT增加得越慢,可能造成的效果就是看到灯在闪,因为我们看到灯的亮度变化并不是真的可以从本质上改变灯的亮度,而是一个周期内灯亮的时间占了多大的比例,由于一个周期时间很短,短到人眼分辨不出亮灭的变化,就会觉得灯的亮度变化了,所以周期也不应该太长,太长的话,人眼就可以分辨出亮灭变化,就会觉得灯在闪。

      ②CCR1每次的增量太大的话,呼吸灯就没有一个渐变的过程,显得不自然。

      ③htim21.Init.Prescaler 和 htim21.Init.Period这两个参数决定多久更新一次CCR1的数值,也就是决定每一个亮度可以保持多久的时间。

    PS:下面是TIM2和TIM21的初始化代码:

     1 void MX_TIM2_Init(void)
     2 {
     3   TIM_ClockConfigTypeDef sClockSourceConfig = {0};
     4   TIM_MasterConfigTypeDef sMasterConfig = {0};
     5   TIM_OC_InitTypeDef sConfigOC = {0};
     6 
     7   htim2.Instance = TIM2;
     8   htim2.Init.Prescaler = 8000-1;
     9   htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
    10   htim2.Init.Period = 50-1;
    11   htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
    12   htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
    13   if (HAL_TIM_Base_Init(&htim2) != HAL_OK)
    14   {
    15     Error_Handler();
    16   }
    17   sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
    18   if (HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig) != HAL_OK)
    19   {
    20     Error_Handler();
    21   }
    22   if (HAL_TIM_PWM_Init(&htim2) != HAL_OK)
    23   {
    24     Error_Handler();
    25   }
    26   sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
    27   sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
    28   if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK)
    29   {
    30     Error_Handler();
    31   }
    32   sConfigOC.OCMode = TIM_OCMODE_PWM1;
    33   sConfigOC.Pulse = 0;
    34   sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
    35   sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
    36   if (HAL_TIM_PWM_ConfigChannel(&htim2, &sConfigOC, TIM_CHANNEL_1) != HAL_OK)
    37   {
    38     Error_Handler();
    39   }
    40   HAL_TIM_MspPostInit(&htim2);
    41 
    42 }
     1 void MX_TIM21_Init(void)
     2 {
     3   TIM_ClockConfigTypeDef sClockSourceConfig = {0};
     4   TIM_MasterConfigTypeDef sMasterConfig = {0};
     5 
     6   htim21.Instance = TIM21;
     7   htim21.Init.Prescaler = 32000-1;
     8   htim21.Init.CounterMode = TIM_COUNTERMODE_UP;
     9   htim21.Init.Period = 250-1;
    10   htim21.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
    11   htim21.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
    12   if (HAL_TIM_Base_Init(&htim21) != HAL_OK)
    13   {
    14     Error_Handler();
    15   }
    16   sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
    17   if (HAL_TIM_ConfigClockSource(&htim21, &sClockSourceConfig) != HAL_OK)
    18   {
    19     Error_Handler();
    20   }
    21   sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
    22   sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
    23   if (HAL_TIMEx_MasterConfigSynchronization(&htim21, &sMasterConfig) != HAL_OK)
    24   {
    25     Error_Handler();
    26   }
    27 
    28 }
  • 相关阅读:
    kylin_学习_00_资源帖
    Saiku_学习_03_Saiku+Kylin构建多维分析OLAP平台
    Saiku_学习_02_Schema Workbench 开发mdx和模式文件
    Tomcat_总结_02_单机多实例
    Saiku_学习_01_saiku安装与运行
    【HDU】1693 Eat the Trees
    【Ural】1519. Formula 1
    蒟蒻修养之tc蓝名计划
    【UVa】11270 Tiling Dominoes
    【POJ】2406 Power Strings
  • 原文地址:https://www.cnblogs.com/young-dalong/p/14733260.html
Copyright © 2011-2022 走看看