高级计时器TIM1:单片机PWM输出实现呼吸灯

PWM概述

  • PWM,即脉冲宽度调制Pulse Width Modulation,也就是通过调节一系列脉冲的宽度来控制模拟信号电平的数字编码方法

  • 在PWM中,占空比是关键参数,它指的是在一个周期内,信号处于高电平的时间占整个周期的百分比。例如,一个方波的占空比是50%

    • 想要测量PWM信号的占空比,就需要用到输入捕获功能与复位触发模式,这在上一节中已有介绍;
    • 想要控制输出信号的占空比,就要用到输出比较功能与中间/边沿对齐模式

输出比较功能

输出比较的定义

  • 借助输出比较功能,可以方便地通过四个独立的输出/比较通道输出4路周期可控、占空比可调的PWM信号

  • TIM1的输出功能是一个专门的机制,无需像输入捕获那样需要CPU时刻处理

  • 之所以在输出后还有带有比较二字,是因为输出波形由靠当前计数值(储存在TIM1_CNTR中)和捕获/比较寄存器TIM_CCRx中设定的计数值比较得到,当两个数产生比较关系后,特定引脚就会按配置输出电平信号

    而这个比较关系具体如何影响输出的电平,要看PWM输出被配置成了什么对齐模式

PWM输出的对齐模式

  • 边沿对齐PWM输出

    在边沿对齐方式下,计数器只有向上和向下计数模式,以向上计数为例,看PWM输出的边沿对其工作方式:

    image

    可见TIM1_CNTR中的当前计数值与两个数据进行比较,以决定当前的输出

    TIM1_ARR决定了周期,TIM1_CCRx决定了高电平脉宽,通过调整这两个寄存器的数值,就可以对PWM信号的频率与占空比进行调节

  • 中间对齐PWM输出

    把边沿对齐方式中计数器的工作方式改为向上向下,那就是中间对齐方式了,启动此对齐方式需要通过配置控制寄存器TIM1_CR1的CMS位(边沿对齐模式是默认值,无需单独配置)

    工作图如下:

    image

    当ARR和CCR寄存器配置相同时,边沿对齐方式产生的PWM频率是中间对齐的2倍,但占空比相同

输出比较功能配置流程

输出比较配置简述

  1. 设置自动重装载数值

    配置TIM1_ARR自动重装载寄存器的ARR[15:0],分TIM1_ARRH与TIM1_ARRL两个寄存器保存

    根据上述的对齐模式原理,这个数的大小关系到PWM信号的周期

  2. 设置捕获比较数值

    配置TIM1_CCRx的CCRi[15:0]位,同样分两个寄存器

    这个数的大小关系到PWM信号的占空比,因此可以先决定信号占空比再进行配置

  3. 设置PWM输出模式及其对齐方式

    配置TIM1_CCMRx的OCiM[2:0]位设置输出模式

    配置TIM1_CR1的CMS[1:0]位设置对齐方式

  4. 配置信号输出极性并使能输出

    配置TIM1_CCERx的CCiP和CCiE位,和输入捕获功能一样:CCiP控制信号极性,CCiE使能相关通道

  5. 设置空闲状态输出电平

    配置TIM1_OISR寄存器的OISi和OISiN位,所谓空闲状态就是禁止了PWM输出时产生的状态

  6. 使能输出

    除了使能计数器外,输出比较功能还有一个输出使能的总开关刹车信号,刹车寄存器TIM1_BKR的MOE位置1,才能输出信号(置0时就会禁止PWM输出,也就是空闲状态),因此需要配置TIM1_CR1_CEN位和TIM1_BKR_MOE位

输出比较相关寄存器

  • 自动重装载寄存器TIM1_ARR和捕获/比较寄存器TIM1_CCRx在前文已经介绍,不再赘述

  • 捕获/比较模式寄存器TIM1_CCMR(输出模式)

    输出比较的“比较”情况决定了输出,具体如何决定输出由捕获比较模式寄存器TIM1_CCMRx中的OCiM[2:0]决定

    CCMR寄存器已经介绍了多次,但都是在输入功能下,前文提到:在输入与输出不同模式下除了CC1S外其他位功能不同,所以之前配置为输入模式时是找不到OCiM位的

    不同通道需要配置的寄存器不同,CH1/2/3/4分别对应TIM1_CCMR1/2/3/4

    image

  • 控制寄存器TIM1_CR1

    主要用到其CMS位来控制对齐方式;中央对齐模式分为三种:

    • CMS[1:0]=00 边沿对齐模式,计数器根据方向位DIR决定向上或向下计数
    • CMS[1:0]=01 中央对齐模式1,计数器交替向上和向下计数,配置为输出的通道(即TIM1_CCMRx_CCiS配置为00的通道)的输出比较中断标志位只在计数器向下时被置1
    • CMS[1:0]=10 中央对齐模式2,同模式1,但是输出比较中断标志位只在计数器向上时被置1
    • CMS[1:0]=11 中央对齐模式3,同模式1,但是输出比较中断标志位在计数器向上向下时都被置1
  • 捕获/比较使能寄存器TIM1_CCER

    与之前配置输入捕获功能类似,注意不同通道需要配置的位不同:

    • CCxP位控制信号极性

    • CCxE位(也是一个输入和输出模式下功能不同的位)用于使能相关通道及其互补通道,比如TIM1_CH1需要TIM1_CCER1的CC1E位为1来使能OC1信号输出

  • 输出空闲状态寄存器TIM1_OISR

    顾名思义,配置空闲状态输出的寄存器,注意不同通道使用的配置位不同

    image

  • 刹车寄存器TIM1_BKR

    PWM输出的总开关,当“刹车”时,不输出PWM信号,进入空闲状态,输出电平由TIM1_OISR决定

    而在刹车寄存器TIM1_BKR的MOE位置1,才会输出设定的PWM信号信号

代码实现

  • CH1通道输出PWM信号

    代码运行结果是PC1引脚输出设定的PWM信号

    //为了方便,可以使用F_PWM_SET这一个变量储存设定值,用以下方法设置
    TIM1_ARRH = F_PWM_SET/256;
    TIM1_ARRL = F_PWM_SET%256;
    //设置CCRx值时,可以通过决定的信号占空比来配置,创建一个变量a来计算占空比
    float a;
    a = Duty_CH1 * F_SET_CH1;//计算占空比,Duty_CH1为周期,F_SET_CH1为想设定的占空比
    TIM1_CCR1H = ((u16)(a))/256;
    TIM1_CCR1L = ((u16)(a))%256;
    
    //选择PWM模式,配置为何值取决于自己的选择,下面为例子
    TIM1_CCMR1 = 0x60; //OCiM[2:0]=110,PWM模式1
    TIM1_CR1 &= 0x8F; /*配置为向上计数边沿对齐
    0x8F = 1000 1111,使用按位与的方式,只配置需要的对齐方式,具体对应了:
    ARPE = X; 自动预装载允许位保持原状态
    CMS[1:0]=00; 选择边沿对齐模式
    DIR=0; 计数器向上
    OPM=X; URS=X; UDIS=X; CEN=X; 单脉冲、更新请求源、禁止更新、计数器使能都保持原状态*/
    
    //TIM1_CR1 |= 0x60;//0x60=0110 0000
    //同理,另一个例子是配置为中间对齐模式3,只需要配置CMS为11即可
    
    //配置输出极性
    TIM1_CCER1 &= 0xFD;//配置CC1P=0,OC1高电平有效,反之低电平
    TIM1_CCER1 |= 0x01;//配置CC1E=1,使能OC1输出
    //注意如果需要配置多个通道PWM输出,需要配置各自的TIM1_CCERx的CCiP和CCiE
    
    //设置空闲输出电平
    TIM1_OISR |= 0x01;//空闲状态为高电平
    
    //开启计数器,并开启输出总开关
    TIM1_CR1 |= 0x01;
    TIM1_BKR = 0x80;
    
  • PWM呼吸灯

    将PC2/3/4引脚连接到RGB灯,即CH2/3/4通道的输出分别控制了红色、绿色、蓝色的灯光;通过调节通过调节信号占空比来改变灯的亮度,实现呼吸灯效果

    首先写一个控制所有通道PWM输出的函数

    void PWM_CH1_output(unsigned long pwmset_ch1,float duty_ch1)
    {
      float a;
      a = duty_ch1 * pwmset_ch1;//计算占空比系数
      TIM1_CCR1H = ((u16)(a))/256;
      TIM1_CCR1L = ((u16)(a))%256;
      TIM1_CCMR1 = 0x60;//PWM模式1
      TIM1_CCER1 &= 0xFD;//OC1高电平有效
      TIM1_CCER1 |= 0x01;//使能OC1
      TIM1_OISR |= 0x01; //空闲状态高电平
    }//CH1作为示例,之后的CH2/3/4除了CCMR和CCER配置按通道不同有所差别其余一致
    void PWM_CH2_output(unsigned long pwmset_ch2,float duty_ch2)
    {
      float b;
      b = duty_ch2 * pwmset_ch2;//计算占空比系数
      TIM1_CCR2H = ((u16)(b))/256;
      TIM1_CCR2L = ((u16)(b))%256;
      TIM1_CCMR2 = 0x60;//PWM模式1
      TIM1_CCER1 &= 0xDF;//CC2P=0,OC2高电平有效
      TIM1_CCER1 |= 0x10;//CC2E=1,使能OC2
      TIM1_OISR |= 0x04; //空闲状态OC2高电平
    }
    void PWM_CH3_output(unsigned long pwmset_ch3,float duty_ch3)
    {
      float c;
      c = duty_ch3 * pwmset_ch3;//计算占空比系数
      TIM1_CCR3H = ((u16)(c))/256;
      TIM1_CCR3L = ((u16)(c))%256;
      TIM1_CCMR3 = 0x60;//PWM模式1
      TIM1_CCER2 &= 0x3D;//CC3P=0,OC3高电平有效
      TIM1_CCER2 |= 0x01;//CC3E=1,使能OC3
      TIM1_OISR |= 0x10; //空闲状态OC3高电平
    }
    void PWM_CH4_output(unsigned long pwmset_ch4,float duty_ch4)
    {
      float d;
      d = duty_ch4 * pwmset_ch4;//计算占空比系数
      TIM1_CCR4H = ((u16)(d))/256;
      TIM1_CCR4L = ((u16)(d))%256;
      TIM1_CCMR4 = 0x60;//PWM模式1
      TIM1_CCER2 &= 0x1F;//CC4P=0,OC4高电平有效
      TIM1_CCER2 |= 0x10;//CC4E=1,使能OC4
      TIM1_OISR |= 0x40; //空闲状态OC4高电平
    }
    void PWM_output(unsigned long pwmset,float duty_ch1,float duty_ch2,float duty_ch3,float duty_ch4)
    {
      TIM1_ARRH = pwmset/256;
      TIM1_ARRL = pwmset%256;
      TIM1_CR1 &= 0x8F;//向上计数边沿对齐
      PWM_CH1_output(pwmset,duty_ch1);
      PWM_CH2_output(pwmset,duty_ch2);
      PWM_CH3_output(pwmset,duty_ch3);
      PWM_CH4_output(pwmset,duty_ch4);
      TIM1_CR1 |= 0x01;
      TIM1_BKR = 0x80;
    }//通过这一个函数控制所有通道的PWM输出
    
  • 在写好了这个函数后,在主函数中就可以通过调用它来实现呼吸灯效果了

    
    #define STEP 0.01 //步长
    int main()
    {
      CLK_SWR = 0xE1;
      CLK_CKDIVR = 0x00;//16Mhz不分频
      Delay(10);//延时等待稳定
      float step = STEP;
      float duty_cycle=0.0;
      
      while(1)
      {
            PWM_output(16000,0,duty_cycle,duty_cycle,duty_cycle);
    
            // 更新占空比
            duty_cycle += step;
    
            // 到达最大或最小占空比时,改变步长方向
            if (duty_cycle >= 0.99) {
                step = -STEP;
            } else if (duty_cycle <= 0.01) {
                step = STEP;
            }
            Delay(80);
      }
    }//这只是一种方法,也可以不使用调节占空比的函数,而是直接调节TIM1_CCR的数值
    

posted on 2024-11-29 11:11  无术师  阅读(94)  评论(0编辑  收藏  举报