定时器PWM输出

目录

介绍

PWM占空比

框图

输出比较

通道 1 输出比较功能为例

PWM 输出模式

PWM 边沿对齐模式

hal库代码

标准库代码


介绍

       脉冲宽度调制(PWM),是英文“Pulse Width Modulation”的缩写,简称脉宽调制,是利用微
处理器的数字输出来对模拟电路进行控制的的技术。

        高级控制定时器 (TIM1 和 TIM8) 和通用定时器在基本定时器的基础上引入了外部引脚,可以实现输入捕获和输出比较功能。高级控制定时器比通用定时器增加了可编程死区互补输出、重复计
数器、带刹车 (断路) 功能,这些功能都是针对工业电机控制方面。主要常用的输入捕获和输出比较功能。

        如果把灯亮看作100%,灯灭看作0%,要实现50%的亮度,可以在某个单位时间里亮灯50%时间、灭灯50%时间,只要这个单位时间够小,由于人眼具有视觉暂留效应,就会感觉整个灯是一直亮着,且亮度只要原来的一半。PWM实质就是GPIO不断翻转输出高、低电平,这个效果可以写代码控制GPIO产生,但这样就会占用CPU, CPU就不方便做其它事情。此时可以利用定时器,设置好翻转时间,让其自动控制GPIO翻转,无需CPU再参与。

PWM占空比

        首先定时器从0开始计数,在0~t1时间段, TIMx_CNT<TIMx_CCR1,输出低电平;在t1~t2时间段, TIMx_CNT>TIMx_CCR1,输出高电平; t2时, TIMx_CNT=TIMx_ARR计数器溢出,重新从0开始,如此循环

        在一个周期内,高电平占整个信号周期的百分比,称之为占空比

框图

框图

输出比较

        输出比较就是通过定时器的外部引脚对外输出控制信号,有冻结、将通道 X(x=1,2,3,4)设置为匹配时输出有效电平、将通道 X 设置为匹配时输出无效电平、翻转、强制变为无效电平、强制变为有效电平、 PWM1 和 PWM2 这八种模式,具体使用哪种模式由寄存器 CCMRx 的位 OCxM[2:0]配置。其中 PWM 模式是输出比较中使用的最多。

通道 1 输出比较功能为例

        灰色阴影部分是输入捕获功能部分,右边没有阴影部分就是输出比较功能部分。首先程序员写 CCR1 寄存器,即写入比较值。这个比较值需要转移到对应的捕获/比较影子寄存器后才会真正生效。什么条件下才能转移?图阴影中可以看到 compare_transfer 旁边的与门,需要满足三个条件: CCR1 不在写入操作期间、 CC1S[1:0] = 0 配置为输出、 OC1PE 位置0(或者 OC1PE 位置 1,并且需要发生更新事件,这个更新事件可以软件产生或者硬件产生)。
        当 CCR1 寄存器的值转移到其影子寄存器后,新的值就会和计数器的值进行比较,它们的
比较结果将会通过输出控制部分影响定时器的输出。

PWM 输出模式

        PWM 输出就是对外输出脉宽(即占空比)可调的方波信号,信号频率由自动重装寄存器 ARR 的值决定,占空比由比较寄存器 CCR 的值决定

PWM 边沿对齐模式

        在递增计数模式下,计数器从 0 计数到自动重载值(TIMx_ARR 寄存器的内容),然后重新从 0开始计数并生成计数器上溢事件

        在边沿对齐模式下,计数器 CNT 只工作在一种模式,递增或者递减模式。这里我们以 CNT 工作在递增模式为例,在中, ARR=8, CCR=4, CNT 从 0 开始计数,当 CNT<CCR 的值时, OCxREF为有效的高电平,于此同时,比较中断寄存器 CCxIF 置位。当 CCR<=CNT<=ARR 时, OCxREF为无效的低电平。然后 CNT 又从 0 开始计数并生成计数器上溢事件,以此循环往复。
 

hal库代码

uint8_t led = 0;
TIM_HandleTypeDef g_pwm;
/*计数器CNT从0到CCR, 输出低电平,LED灯亮
 *计数器CNT从CCR到ARR,输出高电平, LED灯灭
 *CCR值越小, 占空比越大,灯越暗, CCR值越大,占空比越小,灯越亮, CCR值与亮度成正比
 *定时器时钟频率为72MHz / 360 = 200kHz。
 *计算得到每次计数的时间为1 / 200kHz = 5us。
 *计算定时器的溢出时间可以使用下面的公式:
 *Tout = ((ARR + 1) / TIM_CLK)= ((2000) / 200000) = 0.01秒 = 10毫秒
 */
/*工作参数*/
void tim_pwm_init(uint32_t prescaler, uint32_t period)
{
    TIM_OC_InitTypeDef pwm_tim;
    TIM_ClockConfigTypeDef tim_clk_conf;
    
    /*72MHz 经过 360 分频后,定时器时钟为 200KHz,即计数器每间隔 5us 计数一次,从 0 计数到 ARR,经历 10ms*/
    g_pwm.Instance = TIM2;
    g_pwm.Init.Prescaler = prescaler;/*分频系数  360-1*/
    g_pwm.Init.Period = period;/*自动重装载值   ARR=2000-1*/
    g_pwm.Init.CounterMode = TIM_COUNTERMODE_UP;/*向上计数*/
    
    g_pwm.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;/*定时器时钟不从 HCLK 分频*/
    g_pwm.Init.AutoReloadPreload = TIM_AUTOMATICOUTPUT_DISABLE;/*不自动重新装载*/
    HAL_TIM_PWM_Init(&g_pwm);/*初始化pwm*/
    
    tim_clk_conf.ClockSource = TIM_CLOCKSOURCE_INTERNAL;/*选用内部时钟作为定时器时钟源*/
    HAL_TIM_ConfigClockSource(&g_pwm ,&tim_clk_conf);
    /*pwm配置*/
    pwm_tim.OCMode = TIM_OCMODE_PWM1;/*模式1*/
    pwm_tim.Pulse = led; /*比较值.占空比一半,后面代码再修改*/
    pwm_tim.OCPolarity = TIM_OCPOLARITY_LOW;/*极性,LED灯对应低电平有效*/
    pwm_tim.OCFastMode = TIM_OCFAST_DISABLE;/*输出比较快速使能禁止(仅在 PWM1 和 PWM2 可设置*/
    HAL_TIM_PWM_ConfigChannel(&g_pwm, &pwm_tim, TIM_CHANNEL_2);/*通道2*/
    HAL_TIM_PWM_Start(&g_pwm, TIM_CHANNEL_2);
    /*启动中断*/
    HAL_TIM_PWM_Start_IT(&g_pwm ,TIM_CHANNEL_2 );
}
/*msp初始化*/
void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef *htim)
{
    if(htim->Instance == TIM2)
    {
        GPIO_InitTypeDef g_led;
        __HAL_RCC_GPIOA_CLK_ENABLE();/*使能gpio时钟*/
        __HAL_RCC_TIM2_CLK_ENABLE();/*使能tim2时钟*/
        
        g_led.Pin = GPIO_PIN_1;
        g_led.Mode = GPIO_MODE_AF_PP;/*复用推挽*/
        g_led.Pull = GPIO_PULLUP;
        g_led.Speed = GPIO_SPEED_FREQ_HIGH;
        
        /*初始化灯*/
        HAL_GPIO_Init(GPIOA,&g_led);
        
        HAL_NVIC_SetPriority(TIM2_IRQn,0,0);/*配置定时器中断优先级*/
        HAL_NVIC_EnableIRQ(TIM2_IRQn);/*使能 TIM2 中断*/
    }
}

/*中断函数*/
void TIM2_IRQHandler(void)
{
    HAL_TIM_IRQHandler(&g_pwm);
}

void HAL_TIM_PWM_PulseFinishedCallback(TIM_HandleTypeDef *htim)
{
    if(htim->Instance == TIM2)
    {
        /*修改对应通道的比较寄存器 CCR 的值(占空比)*/
        __HAL_TIM_SET_COMPARE(&g_pwm , TIM_CHANNEL_2 , led*2000/255);
    }
}
/*led呼吸灯*/
void pwm_led(void)
{
    uint8_t flag = 0;
    
    /*波形0到255递增,255到0递减*/
    if(!flag)
    {
        led=led+1;/*递增*/
        if(led == 255)
            flag =1;
    }
    else{
        led=led-1;/*递减*/
        if(led == 0)
            flag =0;
    }
    HAL_Delay(4);
}

标准库代码

void pwm_init(void)
{
    GPIO_InitTypeDef GPIO_InitStruct;
    /*开启时钟*/
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
    
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;/*复用推挽*/
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_1;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    
    GPIO_Init(GPIOA,&GPIO_InitStruct);
    
    /*开启时钟*/
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
    /*使用内部时钟*/
    TIM_InternalClockConfig(TIM2);
    /*初始化定时器*/
     /*PWM频率: frq = CK_PSC/(PSC + 1)/(ARR+1)= 72MHZ/ = 70Mhz/(720-1+1)/(100-1+1) = 1000
     *PWM占空比:duty = CCR/(ARR+1) = 50/(100-1+1) = 50%
     *PWM分辨率:reso = 1/(ARR + 1) = 1/(100-1+1)  = 1%
     */
    
    TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
    TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1;
    TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;/*向上计数*/
    TIM_TimeBaseInitStruct.TIM_Period = 100-1;/*arr自动重装值*/
    TIM_TimeBaseInitStruct.TIM_Prescaler = 720-1;/*psc预分频值*/
    TIM_TimeBaseInitStruct.TIM_RepetitionCounter = 0;/*重复计数器,高级定时器才有,这里给0*/
    TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStruct);
   
    TIM_OCInitTypeDef TIM_OCInitStruct;
    TIM_OCStructInit(&TIM_OCInitStruct);/*初始化TIM_OCInitStruct*/
    TIM_OCInitStruct.TIM_OCMode = TIM_OCMode_PWM1;              /*输出比较模式*/
    TIM_OCInitStruct.TIM_OCNPolarity = TIM_OCPolarity_High;     /*输出比较的极性*/
    TIM_OCInitStruct.TIM_OutputState = TIM_OutputState_Enable;/*使能输出*/
    TIM_OCInitStruct.TIM_Pulse = 0;/*ccr捕获比较寄存器值,后面设置*/
    TIM_OC2Init(TIM2, &TIM_OCInitStruct);/*使用通道*/
    
    /*开启Tim*/
    TIM_Cmd(TIM2,ENABLE);
}

void  pwm_set_ccr(void)
{
    uint8_t i;
    for(i=0;i<=100;i++)/*从0到100呼吸变亮*/
    {
        TIM_SetCompare2(TIM2,i);/*设置定时器通道的捕获比较寄存器(CCR1)的值*/
        Delay_ms(10);
    }
    
    for(i=0;i<=100;i++)/*从0到100呼吸变暗*/
    {
        TIM_SetCompare2(TIM2,100-i);/*设置ccr的值*/
        Delay_ms(10);
    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值