输出比较产生PWM

转自:作者:马一飞                                                         QQ:791729359       

看了这位博主的博客学到了很多东西,一开始有些地方不是很明白,现在看懂了,稍微修改了下,原文连接如下

 https://blog.csdn.net/qq_34952376/article/details/81172535

下面开始正文:

        之前我们讲解了通用定时器使用PWM模式产生PWM波,但是到最后我们总结出了一个缺点:PWM模式同一定时器中,不同的通道下,输出的频率固定,由时基单元初始化时设置决定的,占空比可变。也就是说在初始化时频率设置成多少,那么在这个定时器下的各个通道产生个PWM波频率也是相同的。

        那么如果想各个通道产生的PWM频率不同,占空比也不同,那我们就需要借助一个通用定时器的输出比较模式了。

        首先我们先理解一下什么叫输出比较,我给大家简单画一个简图。

我们知道,stm32f1的通用定时器位数的16位的,那么就代表计数值最大可以达到0xFFFF,如果到了0xFFFF再继续往下计数的话就会清0重新向上技术。那么我们可以利用定时器的这个特点,来产生一个PWM波。

        我们把计数值设置成0xFFFF,那么定时器就会一直向上计数,pulse就相当于我们设定的周期值,也就是我们所说的频率。那么,在一个周期内,占空比是多少?就是Duty所决定的,在一个pulse内,小于Duty为高电平,大于Duty为低电平,直到进入下一个pulse。这就是输出比较模式的特点。为什么把它称作为输出比较模式呢?就是因为定时器的计数值一直向上计数的同时,他要不断的跟我们设定的pulse和Duty做比较。

        我们把这个概念理解好了之后,就可以开始编写代码了。为了更好的让大家学习PWM波,上一次用的是TIM3,这次我就用TIM2来产生PWM

同样的方法,首先我们先查看有哪些引脚是复用为TIM2的

我们可以看到PA1,PA2,PA3都可以复用为TIM2,这次我们就用了PA1,PA2做双通道比较输出吧。如果大家想再开启PA3也是相同的方法。

        我们使用输出比较PWM的话是要用到定时器中断的,但是这个中断又不等同于定时中断。既然用到中断的话就一定要吧misc.c这个库函数添加进工程目录里。

1. 首先当然得使能PA口和TIM2时钟

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
2.初始化PA1,PA2设置复用推挽输出模式

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_2;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
3.配置TIM2中断

NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
4. 配置定时器2基础设置,我们把计数值设置到最大,也就是0xffff,并且71分频。

TIM_TimeBaseInitStructure.TIM_Period = 0xffff;
TIM_TimeBaseInitStructure.TIM_Prescaler = 71;
TIM_TimeBaseInitStructure.TIM_ClockDivision = 0x0;
TIM_TimeBaseInitStructure.TIM_CounterMode = 0x0;
TIM_TimeBaseInit( TIM2, &TIM_TimeBaseInitStructure);
5. 通过输入的频率值,算出pulse的值,也就是周期值

//表示在当前定时器配置下要达到要求频率的实际计数值
CH2_Val = 1000000 / ch2_fre;  //频率为捕获比较寄存器的值,要计数到这个值的时间为T=1us*ch2_fre
CH3_Val = 1000000 / ch3_fre;  //则频率就是1/T=1000000/ch2_fre
CH2_Duty = CH2_Val * ch2_duty / 100;  //形参中占空比用×100后的整数表示
CH3_Duty = CH3_Val * ch3_duty / 100;
        因为是71分频,所以pulse的值就等于1000000 除 输入的频率值。

        同时还得通过设定的占空比数,算出Duty的位置。

6. 设定输出模式为触发模式,并且极性为低极性,使能输出,把算出来的周期指赋给Pulse。

TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Toggle;  //定时器通道工作于输出比较模式
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; //初始输出极性,表示低电平
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = CH2_Val;  //捕获比较寄存器的初始值,可设置初始相位
TIM_OC2Init(TIM2,&TIM_OCInitStructure);
        
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Toggle;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = CH3_Val;
TIM_OC3Init(TIM2,&TIM_OCInitStructure);
7. 把定时器计数值清0,并且把通道2,通道3的比较值也设置为0,在接下来只要一启动定时器就直接开始比较了。

TIM_SetCounter(TIM2,0x0);  //从0开始计数,对初相位有要求时必须设置
TIM_SetCompare2(TIM2,0x0);  //初相位为0
TIM_SetCompare3(TIM2,0x0);
8.启动定时器,并使能通道2、通道3比较中断。

TIM_Cmd( TIM2, ENABLE);
TIM_ITConfig(TIM2, TIM_IT_CC2 | TIM_IT_CC3, ENABLE);


这个时候我们就把输入比较初始化完成了。

那么我们就要开始编写中断处理函数,手动的让定时器按照我们的要求,去产生指定占空比的PWM波形。

我们先定义两个标志位 channel_2_flag,channel_3_flag。用来确定我们当前比较的状态。

channel_2_flag=1,表示接下来为输出高电平,反之接下来输出低电平

然后就开始编写中断服务函数了,

u16 capture;
if(TIM_GetITStatus(TIM2, TIM_IT_CC2) == 1)
{
    TIM_ClearITPendingBit(TIM2, TIM_IT_CC2);  //这里现在代表的是输出比较中断,不是输入捕获
    capture = TIM_GetCapture2(TIM2);
    if(channel_2_flag)
    {
        TIM_SetCompare2(TIM2,capture + CH2_Duty);  //接下来为输出高电平时间
    }else                                        //输出极性会自动翻转
    {
        TIM_SetCompare2(TIM2,capture + CH2_Val - CH2_Duty);
    }
    channel_2_flag ^= 1;
}
我们要写定义一个16位的变量,用处是

来获取我们通道当前的比较值。在每次进入比较中断时,我们都把通道当前的比较值获取到capture里,当我们channel_2_flag为真的时候,代表将进入Duty以下的位置,channel_2_flag为假则代表将进入Duty以上的位置。当我们每次得到一个中断,引脚都会自动的把电平翻转,我们再中断处理函数中只需要设定好比较值就ok了。

TIM_SetCompare2(TIM2,capture + CH2_Duty);
代表在当前的比较值,加上一个高电平时间作为通道2的比较值。而

TIM_SetCompare2(TIM2,capture + CH2_Val - CH2_Duty);
        则是在当前的比较值加上一个低电平时间作为通道2的比较值。那么当我们初始化完成之后,比较通道的值就会不断的与计数值进行比较,假如计数值到达我们比较值,那么就会产生一个中断请求,并且引脚的电平会翻转。

同理,通道3的设置也是相同的。

if(TIM_GetITStatus(TIM2, TIM_IT_CC3) == 1)
{
    TIM_ClearITPendingBit(TIM2, TIM_IT_CC3);
    capture = TIM_GetCapture3(TIM2);
    if(channel_3_flag)
    {
        TIM_SetCompare3(TIM2,capture + CH3_Duty);
    }else
    {
        TIM_SetCompare3(TIM2,capture + CH3_Val - CH3_Duty);
    }
    channel_3_flag ^= 1;
}


那么我们写好之后就可以烧录这个程序到开发板上看看效果。

TIM2_PWM_OUTPUT(1000,40,5000,80);
我们把通道2的频率设置为1000HZ,占空比为40%,通道3的频率设置为5000HZ,占空比为80%。

我们用逻辑分析仪看看PA1,PA2引脚上产生的波形。

先看PA1的波形

PA2的波形

我们能够看到,完全达到我们的要求,且产生的波形频率和占空比,非常准确无误差。

我们能够发现,比较通道的优点是:同一定时器下,不同通道能够产生频率不同,占空比不同,甚至相位也不同的方波。

至于相位不同怎么设置,也很简单,这个任务就交给你们思考啦。
--------------------- 
作者:eavane 
来源:CSDN 
原文:https://blog.csdn.net/qq_34952376/article/details/81172535 
版权声明:本文为博主原创文章,转载请附上博文链接!

猜你喜欢

转载自blog.csdn.net/dbfy666/article/details/88751619
今日推荐