15092|28

283

帖子

0

TA的资源

纯净的硅(初级)

楼主
 

STM32 定时器产生PWM彻底应用 [复制链接]

这次学习STM32花了很长时间,一个礼拜多,也有颇多收获,学习过程也有颇多曲折。这次的任务是:用STM32的一个定时器在四个通道上产生四路频率可调占空比可调的PWM波。

看到这个题,我先看STM32的数据手册,把STM32的定时器手册看完就花了一天,但是看了一遍任然不知道所云,就看库函数,略有点理解,就想一哈把这个程序调出来,于是就花了一天多时间仿照网上别人的程序来写,花了一天多写出来调试,结果行不通,做了无用功,于是静下心来想想,还是一步一步的来。

我先用STM32的通用定时器用PWM模式产生四路相同占空比,不同频率的PWM波,配置如下:

RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);//使能TIM2时钟

       TIM_InternalClockConfig(TIM2);//使用内部时钟

 

       TIM_BaseInitStructure.TIM_Prescaler=3; //设置TIM时钟频率除数的预分频值

      TIM_BaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up;//选择计数器模式

      TIM_BaseInitStructure.TIM_Period=1799;//设置下一个更新事件装入活动的自动重装载寄存器周期的值

      TIM_BaseInitStructure.TIM_ClockDivision=0;//设置时钟分割

 

       TIM_TimeBaseInit(TIM2,&TIM_BaseInitStructure);

      

       //通道1

       TIM_OCInitStructure.TIM_OCMode=TIM_OCMode_PWM1;//选择定时器模式

      TIM_OCInitStructure.TIM_OutputState=TIM_OutputState_Enable;//选择输出比较状态

      TIM_OCInitStructure.TIM_OutputNState=TIM_OutputNState_Disable;//选择互补输出比较状态

      TIM_OCInitStructure.TIM_Pulse=CCR1_Val;//设置了待装入捕获比较器的脉冲值

      TIM_OCInitStructure.TIM_OCPolarity=TIM_OCPolarity_High;//设置输出极性

      TIM_OCInitStructure.TIM_OCNPolarity=TIM_OCNPolarity_Low;//设置互补输出极性

      TIM_OCInitStructure.TIM_OCIdleState=TIM_OCIdleState_Set;//选择空闲状态下得非工作状态

      TIM_OCInitStructure.TIM_OCNIdleState=TIM_OCNIdleState_Reset;//选择互补空闲状态下得非工作状态

       TIM_OC1Init(TIM2,&TIM_OCInitStructure);

       TIM_OC1PreloadConfig(TIM2, TIM_OCPreload_Enable);

 

       //通道2  

      TIM_OCInitStructure.TIM_Pulse=CCR2_Val;//设置了待装入捕获比较器的脉冲值

       TIM_OC2Init(TIM2,&TIM_OCInitStructure);

       TIM_OC2PreloadConfig(TIM2,TIM_OCPreload_Enable);

 

       //通道3

       TIM_OCInitStructure.TIM_Pulse=CCR3_Val;//设置了待装入捕获比较器的脉冲值

       TIM_OC3Init(TIM2,&TIM_OCInitStructure);

       TIM_OC3PreloadConfig(TIM2,TIM_OCPreload_Enable);

 

       //通道4

       TIM_OCInitStructure.TIM_Pulse=CCR4_Val;//设置了待装入捕获比较器的脉冲值

       TIM_OC4Init(TIM2,&TIM_OCInitStructure);

       TIM_OC4PreloadConfig(TIM2,TIM_OCPreload_Enable);

 

       TIM_Cmd(TIM2, ENABLE);

       TIM_CtrlPWMOutputs(TIM2,ENABLE);

pwm模式输出的频率和占空比是固定的,不可调,要想输出频率可调,占空比可调,必须得使用比较输出模式。这点资料是在STM32全国巡回研讨会上看到的,如图:

[ 本帖最后由 TopMars 于 2011-10-12 15:59 编辑 ]

STM32 定时器产生PWM 彻底应用.doc

138.5 KB, 下载次数: 789

此帖出自stm32/stm8论坛

最新回复

感谢楼主,看了很受启发  详情 回复 发表于 2014-12-18 21:59
点赞 关注(1)
 

回复
举报

283

帖子

0

TA的资源

纯净的硅(初级)

沙发
 
所以,接下来我就写了一个程序通过输出比较模式产生一路PWM波,这个波的频率和占空比都由自己确定,函数配置如下:
        TIM_BaseInitStructure.TIM_Prescaler=3; //设置TIM时钟频率除数的预分频值(18M)
          TIM_BaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up;//选择计数器模式
          TIM_BaseInitStructure.TIM_Period=1800;//设置下一个更新事件装入活动的自动重装载寄存器周期的值
          TIM_BaseInitStructure.TIM_ClockDivision=0;//设置时钟分割
        TIM_TimeBaseInit(TIM2,&TIM_BaseInitStructure);
        //通道1
        TIM_OCInitStructure.TIM_OCMode=TIM_OCMode_Toggle;//选择定时器模式
          TIM_OCInitStructure.TIM_OutputState=TIM_OutputState_Enable;//选择输出比较状态
          TIM_OCInitStructure.TIM_OutputNState=TIM_OutputNState_Disable;//选择互补输出比较状态
          TIM_OCInitStructure.TIM_Pulse=CCR1_Val1;//设置了待装入捕获比较器的脉冲值
        TIM_OCInitStructure.TIM_OCPolarity=TIM_OCPolarity_High;//设置输出极性
          TIM_OCInitStructure.TIM_OCNPolarity=TIM_OCNPolarity_Low;//设置互补输出极性
          TIM_OCInitStructure.TIM_OCIdleState=TIM_OCIdleState_Set;//选择空闲状态下得非工作状态
          TIM_OCInitStructure.TIM_OCNIdleState=TIM_OCNIdleState_Reset;//选择互补空闲状态下得非工作状态
        TIM_OC1Init(TIM2,&TIM_OCInitStructure);
        TIM_OC1PreloadConfig(TIM2, TIM_OCPreload_Disable);
        TIM_ARRPreloadConfig(TIM2,ENABLE);
        TIM_ITConfig(TIM2,TIM_IT_CC1,ENABLE);
        TIM_Cmd(TIM2,ENABLE);
               
}


void TIM2_IRQHandler(void)
{
        TIM_ClearITPendingBit(TIM2,TIM_IT_CC1);
        if(n==1)
        {
                n=0;
                TIM_SetCompare1(TIM2,CCR1_Val2);
        }
        else
        {
                n=1;
                  TIM_SetCompare1(TIM2,CCR1_Val1);
        }                                                 
}
通过改变比较寄存器(CCR1)中的值,改变PWM的占空比,在每次匹配中断中改变CCR1的值。上面程序实现的是产生一路频率为10K占空比为40%的PWM波。
有了上面的思想我就想产生四路不同频率不同占空比的PWM波,经过反复思考光配函数似乎不能实现,在网上去查了的,很多网友也说不能实现,有一个网友给了一个提示:软件模拟。刚开始没明白什么意思,于是还是自己继续配置库函数,在这个过程中一直有两个疑问:
每次中断中,CCR寄存器的值都在循环的增加,CCR的寄存器不可能是无限大吧?就算是无限大,计数器也不是无限大呀,他只能记到65535。初步确定使用匹配中断不行,我有想过同时使用溢出中断和匹配中断,但这样四路PWM波只能是固定的,频率和占空比不能调。大概说一下怎样用溢出中断和匹配中断实现四路固定的PWM波,把计数器寄存器(CNT)的值装最大周期的那个PWM波,当一次计数完成算一下三路小点周期数,在匹配中断中对应的设个变量,CCR就改变几次,溢出中断来了就再次给计数器装初值,同时四个比较寄存器从装初值,这样很麻烦,理论上可以实现,但我考虑到最终不能实现我的要求,就没有去验证。所以产生四路频率可调占空比可调,用一个定时器似乎不能实现,就一直卡到这里,我又在想飞哥说能实现,就肯定能实现,我又在网上找资料,还是没找到,只是有人题四路,软模拟,于是我就思考用软模拟实现,最后在一个师兄的指点下,确实用软件模拟一个中间比较寄存器能实现,思路大概是这样子的,首先让比较寄存器装满,也就是最大值(65535),然后通过改变模拟比较寄存器的值,每次匹配中断只需把模拟比较寄存器的值去比较就行,具体方案看程序。
unsigned char  Cnt[4]; //一个数组,这个数组的每个元素对应一个通道,用来判断装PWM得高电平还是低电平数
unsigned int  T[4];//周期数组
unsigned int  R[4];//模拟的比较寄存器数组,一样的每个通道对应一个数组元素
unsigned int  Rh[4];//模拟的PWM高电平比较寄存器
unsigned int  Rl[4]; //模拟的PWM低电平比较寄存器
unsigned char F[4];//占空比数组
unsigned int CCR1,CCR2,CCR3,CCR4;

void Init(void)
{
此帖出自stm32/stm8论坛
 
 

回复

283

帖子

0

TA的资源

纯净的硅(初级)

板凳
 
unsigned char i = 0;       
        for(i = 0; i < 4; i++)
        {
                Cnt= 0;
                T  = 0;
                R  = 0;
                Rh = 0;
                Rl = 0;
                F  = 0;
        }       
        //t的范围为(0~65536)       
        T[0] = 450;           //F=40K
        T[1] = 600;           //F=30K
        T[2] = 900;           //F=20K
        T[3] = 1800;   //F=10K
        //F(占空比)的范围为(0~100)
        F[0] = 40;
        F[1] = 30;
        F[2] = 20;
        F[3] = 10;       
        for(i = 0; i < 4; i++)
        {
                Rh = (T * F) / 100;
                Rl = T - Rh;
        }       
        R[0] = Rl[0];
        R[1] = Rl[1];
        R[2] = Rl[2];
        R[3] = Rl[3];
       
        CCR1 = R[0];
        CCR2 = R[1];
        CCR3 = R[2];
        CCR4 = R[3];
}
对应的数组初始化

void RCC_Configuration(void)
{
        RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_AFIO,ENABLE);
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC|RCC_APB2Periph_GPIOD,ENABLE);
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_AFIO,ENABLE);
}
时钟配置
void GPIO_Configuration(void)
{
        GPIO_InitTypeDef GPIO_InitStructure;
   //Key1 PA0 Key3 PA8
        GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0|GPIO_Pin_8;
          GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
          GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU;
        GPIO_Init(GPIOA,&GPIO_InitStructure);
        //Key2 PC13
        GPIO_InitStructure.GPIO_Pin=GPIO_Pin_13;
          GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
          GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU;
        GPIO_Init(GPIOC,&GPIO_InitStructure);
        //Key PD3
        GPIO_InitStructure.GPIO_Pin=GPIO_Pin_3;
          GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
          GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU;
        GPIO_Init(GPIOD,&GPIO_InitStructure);
        //TIM3 CH1 CH2
        GPIO_InitStructure.GPIO_Pin=GPIO_Pin_6|GPIO_Pin_7;
          GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
          GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;
        GPIO_Init(GPIOA,&GPIO_InitStructure);
        //TIM3 CH3 CH4
        GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0|GPIO_Pin_1;
          GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
          GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;
        GPIO_Init(GPIOB,&GPIO_InitStructure);
}
管脚配置
void NVIC_Configuration(void)
{
        NVIC_InitTypeDef NVIC_InitStructure;
        #ifdef VECT_TAB_RAM
        NVIC_SetVectorTable(NVIC_VectTab_RAM,0x0);
        #else
        NVIC_SetVectorTable(NVIC_VectTab_FLASH,0x0);
        #endif
        NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);

        NVIC_InitStructure.NVIC_IRQChannel=TIM3_IRQChannel;
          NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1;
          NVIC_InitStructure.NVIC_IRQChannelSubPriority=1;
          NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;

        NVIC_Init(&NVIC_InitStructure);       
}
中断配置
void TIM_Configuration(void)
{
        TIM_TimeBaseInitTypeDef TIM_BaseInitStructure;
        TIM_OCInitTypeDef TIM_OCInitStructure;

        TIM_InternalClockConfig(TIM3);

        TIM_BaseInitStructure.TIM_Prescaler=3;//4分频,18M
          TIM_BaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up;
          TIM_BaseInitStructure.TIM_Period=65535;
          TIM_BaseInitStructure.TIM_ClockDivision=0;
          TIM_BaseInitStructure.TIM_RepetitionCounter=0;
        TIM_TimeBaseInit(TIM3,&TIM_BaseInitStructure);

        TIM_OCInitStructure.TIM_OCMode=TIM_OCMode_Toggle;
          TIM_OCInitStructure.TIM_OutputState=TIM_OutputState_Enable;
          TIM_OCInitStructure.TIM_Pulse=CCR1;
          TIM_OCInitStructure.TIM_OCPolarity=TIM_OCPolarity_Low;
        TIM_OC1Init(TIM3,&TIM_OCInitStructure);
        TIM_OC1PreloadConfig(TIM3,TIM_OCPreload_Disable);
        TIM_ClearITPendingBit(TIM3,TIM_IT_CC1);

        TIM_OCInitStructure.TIM_OCMode=TIM_OCMode_Toggle;
        TIM_OCInitStructure.TIM_Pulse=CCR2;
        TIM_OC2Init(TIM3,&TIM_OCInitStructure);
        TIM_OC2PreloadConfig(TIM3,TIM_OCPreload_Disable);
        TIM_ClearITPendingBit(TIM3,TIM_IT_CC2);

           TIM_OCInitStructure.TIM_OCMode=TIM_OCMode_Toggle;
        TIM_OCInitStructure.TIM_Pulse=CCR3;
        TIM_OC3Init(TIM3,&TIM_OCInitStructure);
        TIM_OC3PreloadConfig(TIM3,TIM_OCPreload_Disable);
        TIM_ClearITPendingBit(TIM3,TIM_IT_CC3);

        TIM_OCInitStructure.TIM_OCMode=TIM_OCMode_Toggle;
        TIM_OCInitStructure.TIM_Pulse=CCR4;
        TIM_OC4Init(TIM3,&TIM_OCInitStructure);
        TIM_OC4PreloadConfig(TIM3,TIM_OCPreload_Disable);
        TIM_ClearITPendingBit(TIM3,TIM_IT_CC4);

        TIM_Cmd(TIM3,ENABLE);
        TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE);
        TIM_ITConfig(TIM3,TIM_IT_CC1|TIM_IT_CC2|TIM_IT_CC3|TIM_IT_CC4,ENABLE);
}

void TIM3_IRQHandler(void)
{
        if(TIM_GetITStatus(TIM3,TIM_IT_CC1)!=RESET)
        {
                TIM_ClearITPendingBit(TIM3,TIM_IT_CC1);
                Cnt[0]=(~Cnt[0])&0x01;
                if(Cnt[0]==0x01)       
                        R[0]+=Rl[0];
                else
                        R[0] += Rh[0];
                if(R[0]>65535)
                        R[0]=R[0]-65535;
                CCR1=R[0];
                TIM_SetCompare1(TIM3,CCR1);
        }
        if(TIM_GetITStatus(TIM3,TIM_IT_CC2)!=RESET)
        {
                TIM_ClearITPendingBit(TIM3,TIM_IT_CC2);
                Cnt[1]=(~Cnt[1])&0x01;
                if(Cnt[1]==0x01)       
                        R[1]+=Rl[1];
                else
                        R[1] += Rh[1];
                if(R[1]>65535)
                        R[1]=R[1]-65535;
                CCR2=R[1];
                TIM_SetCompare2(TIM3,CCR2);
        }       
        if(TIM_GetITStatus(TIM3,TIM_IT_CC3)!=RESET)
        {
                TIM_ClearITPendingBit(TIM3,TIM_IT_CC3);
                Cnt[2]=(~Cnt[2])&0x01;
                if(Cnt[2]==0x01)       
                        R[2]+=Rl[2];
                else
此帖出自stm32/stm8论坛
 
 

回复

283

帖子

0

TA的资源

纯净的硅(初级)

4
 
R[2] += Rh[2];
                if(R[2]>65535)
                        R[2]=R[2]-65535;
                CCR3=R[2];
                TIM_SetCompare3(TIM3,CCR3);
        }       
        if(TIM_GetITStatus(TIM3,TIM_IT_CC4)!=RESET)
        {
                TIM_ClearITPendingBit(TIM3,TIM_IT_CC4);

                Cnt[3] = (~Cnt[3])&0x01;
               
                if(Cnt[3]==0x01)       
                        R[3]+=Rl[3];
                else
                        R[3] += Rh[3];
                if(R[3]>65535)
                        R[3]=R[3]-65535;
                CCR4=R[3];
                TIM_SetCompare4(TIM3,CCR4);                               
        }
}
中断函数
其余就是按键扫描函数,通过改变周期数组中的值和占空比寄存器中的值就能改变PWM波的频率和占空比,当然按键可以设置为4个(一个按键对应一个通道),如果IO够用也可以设置8个,没两个按键对应一个通道分别改变频率和占空比。
此帖出自stm32/stm8论坛
 
 
 

回复

167

帖子

0

TA的资源

一粒金砂(中级)

5
 
    楼主  辛苦了
此帖出自stm32/stm8论坛
 
 
 

回复

81

帖子

0

TA的资源

一粒金砂(初级)

6
 
楼主太厉害了
此帖出自stm32/stm8论坛
 
 
 

回复

315

帖子

0

TA的资源

纯净的硅(高级)

7
 
注释很给力,致敬!
此帖出自stm32/stm8论坛
 
 
 

回复

4005

帖子

0

TA的资源

版主

8
 

回复 楼主 TopMars 的帖子

混入太多的软件控制在频率比较高时pwm信号会受影响
此帖出自stm32/stm8论坛
 
 
 

回复

54

帖子

0

TA的资源

一粒金砂(初级)

9
 
每下必回!!!實在很高興!可以看到這麼好的知識!
此帖出自stm32/stm8论坛
 
 
 

回复

3

帖子

0

TA的资源

一粒金砂(初级)

10
 
顶:carnation:
此帖出自stm32/stm8论坛
 
 
 

回复

5

帖子

0

TA的资源

一粒金砂(中级)

11
 
我真正学这个,不过要不到4路,只要一路改变就好了。 用ADC采集的信号来时时控制改变输出PWM的那种。我还在云里雾里啊
此帖出自stm32/stm8论坛
 
 
 

回复

1

帖子

0

TA的资源

一粒金砂(初级)

12
 

回复 楼主 TopMars 的帖子

怎么也要顶顶
此帖出自stm32/stm8论坛
 
 
 

回复

9

帖子

0

TA的资源

一粒金砂(中级)

13
 
感谢楼主分享,一直在学习,还有那个捕捉,也弄不清楚,吐过楼主能帮忙,就跪谢了。
此帖出自stm32/stm8论坛
 
 
 

回复

11

帖子

0

TA的资源

一粒金砂(初级)

14
 

值得下,谢谢分享

此帖出自stm32/stm8论坛
 
 
 

回复

8

帖子

0

TA的资源

一粒金砂(初级)

15
 
楼主给力!顶起来
此帖出自stm32/stm8论坛
 
 
 

回复

1

帖子

0

TA的资源

一粒金砂(中级)

16
 

此方法有一定限制

根据楼主所说,由于比较值只能在0-65535之间,故 TH与TL计算时,如果要用10Hz的脉冲, 那比较值不是超过65535?如果用一个定时器来实现4路频率可调,占空比先不说,频率范围不用大了,0-20KHz,在低频率时肯定有一段盲区无法输出的,楼主想过吗?
此帖出自stm32/stm8论坛
 
 
 

回复

7

帖子

0

TA的资源

一粒金砂(初级)

17
 
PWM 是一種協議嗎?
此帖出自stm32/stm8论坛
 
 
 

回复

27

帖子

0

TA的资源

一粒金砂(中级)

18
 
非常感谢哈
此帖出自stm32/stm8论坛
 
个人签名jackhan
 
 

回复

7

帖子

0

TA的资源

一粒金砂(初级)

19
 
楼主厉害,膜拜!
此帖出自stm32/stm8论坛
 
 
 

回复

37

帖子

0

TA的资源

一粒金砂(中级)

20
 
谢谢楼主~~~~~~~~~~~
此帖出自stm32/stm8论坛
 
 
 

回复
您需要登录后才可以回帖 登录 | 注册

随便看看
查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
关闭
站长推荐上一条 1/10 下一条

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表