STM32单片机一个定时器输出不同频率PWM波

发布者:PeacefulOasis最新更新时间:2022-01-14 来源: eefocus关键字:STM32  单片机  定时器输出  PWM波 手机看文章 扫描二维码
随时随地手机看文章

  在使用STM32单片机输出PWM波形的时候,通常可以直接使用定时器提供的PWM模式。可以通过自动重装载寄存器(TIMx_ARR)来设置定时器的输出频率,然后通过捕获/ 比较寄存器 1(TIMx_CCRx)来设置占空比。一个定时器只有一个自动重装载寄存器(TIMx_ARR),但是有4个通道的捕获/ 比较寄存器 1(TIMx_CCR1、TIMx_CCR2、TIMx_CCR3、TIMx_CCR4)。所以使用一个定时器输出PWM波形的时候,频率是统一调整的,4个通道的频率是相同的,但是占空比每个通道可以独立设置。比较寄存器TIMx_CCR1、TIMx_CCR2、TIMx_CCR3、TIMx_CCR4分别设置4个通道的占空比。


  如果使用中央对齐模式计数的话,那么定时器的计数波形如下:

在这里插入图片描述

  三角波就是计数器计数的方式,从0开始逐渐增大到装载值ARR,然后在减小到0。蓝色的波形就是比较寄存器的值CCR,计数器在计数的时候,每次都会将计数值和比较寄存器的值CCR进行比较,当计数器值小于CCR的值时,输出低电平。当计数器的值大于CCR的值时,输出高电平。

在这里插入图片描述

  当需要改变占空比的时候,只需要改变比较寄存器CCR的值就行了。

在这里插入图片描述

  通过改变ARR的值设置输出PWM波形的频率,通过改变CCR的值改变PWM波的占空比。但是用这种方式输出PWM波时,一个定时器的4个通道输出的PWM波频率都是一样的,那么能不能用一个定时器输出4路不同频率的PWM波呢?

  通过观察捕获/ 比较模式寄存器 1(TIMx_CCMR1)中的OC1M位时可以发现,除了PWM模式外还有一个翻转模式,在翻转模式下,当计数器CNT的值等于比较寄存器CCR的值时,输出的电平就会翻转。

在这里插入图片描述

  那么就可以通过这个功能,在输出PWM波的过程中不停的去改变CCR的值,那么输出的电平就会不停的翻转,这样就可以手动改变PWM波的频率了。

在这里插入图片描述

  比如第一次将CCR的值设置为CCR1,然后使能比较中断,当计数器的值CNT等于CCR1时,就会触发中断,然后在中断中将CCR的值修改为CCR2,。接下来当计数器的值等于CCR2时,又会触发中断,在中断中继续设置下一次CCR的值。这样每触发一次中断后,输出的PWM波形电平就会翻转一次。在计数器从0到ARR计数期间,PWM的波形可以翻转好多次。在PWM模式中,每一个计数周期波形只能翻转一次,但是在翻转模式下,一个计数周期可以翻转好多次,这样就可以改变输出PWM波形的频率了,同样每次设置不同的CCR值时,也可以改变占空比。


  下面就通过代码来实现。


#include "timer2.h"

//比较值

u16 CCR1_Val = 32768;

u16 CCR2_Val = 16384;

u16 CCR3_Val = 8192;

u16 CCR4_Val = 4096;


void TIM2_PWM_Init( u16 arr, u16 psc )

{


    TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;

    NVIC_InitTypeDef NVIC_InitStructure;

    TIM_OCInitTypeDef TIM_OCInitStructure;

    GPIO_InitTypeDef GPIO_InitStructure;

    

//使能时钟

    RCC_APB1PeriphClockCmd( RCC_APB1Periph_TIM2, ENABLE );

    RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA, ENABLE );


    //设置IO口

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3;

    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;

    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

    GPIO_Init( GPIOA, &GPIO_InitStructure );


    //设置中断优先级

    NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;

    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x01;

    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x01;

    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;

    NVIC_Init( &NVIC_InitStructure );


    //设置定时器基本参数

    TIM_TimeBaseInitStructure.TIM_Period = arr;

    TIM_TimeBaseInitStructure.TIM_Prescaler = psc;

    TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0; //只有高级定时器需要设置,其他定时器可以不设置。

    TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;

    TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;

    TIM_TimeBaseInit( TIM2, &TIM_TimeBaseInitStructure );


    //设置工作模式

    TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Toggle; //设置定时器工作在翻转模式

    TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;

    TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;


    //设置4个通道 比较寄存器值

    TIM_OCInitStructure.TIM_Pulse = CCR1_Val;

    TIM_OC1Init( TIM2, &TIM_OCInitStructure ); //TIM2_OC1   PA0


    TIM_OCInitStructure.TIM_Pulse = CCR2_Val;

    TIM_OC2Init( TIM2, &TIM_OCInitStructure ); //TIM2_OC2   PA1


    TIM_OCInitStructure.TIM_Pulse = CCR3_Val;

    TIM_OC3Init( TIM2, &TIM_OCInitStructure ); //TIM2_OC1   PA2


    TIM_OCInitStructure.TIM_Pulse = CCR4_Val;

    TIM_OC4Init( TIM2, &TIM_OCInitStructure ); //TIM2_OC1   PA3


    //禁止自动预装载

    TIM_OC1PreloadConfig( TIM2, TIM_OCPreload_Disable );

    TIM_OC2PreloadConfig( TIM2, TIM_OCPreload_Disable );

    TIM_OC3PreloadConfig( TIM2, TIM_OCPreload_Disable );

    TIM_OC4PreloadConfig( TIM2, TIM_OCPreload_Disable );


    //使能定时器

    TIM_Cmd( TIM2, ENABLE );

    TIM_ITConfig( TIM2, TIM_IT_CC1 | TIM_IT_CC2 | TIM_IT_CC3 | TIM_IT_CC4, ENABLE );

}


  首先初始化定时器,初始化的两个参数arr和psc分别设置定时器的自动重装载值和分频系数。这里将arr的自动装载值设置为最大0xFFFF,也就是65535,相当于计数器从0开始计数,直到65535才结束。将分频系数psc设置为0,也就是不分频,定时器按照72MHz的频率工作。


  将定时器的工作模式设置为翻转模式,然后依次给4个通道设置比较值。这里要将自动重装载功能关闭,否则就不能手动的更改比较值了。最后使能定时器和4个通道的比较中断。


  接下来就是最重要的环节了,在中断中改变每个通道的频率和占空比。


//占空比 0 --- 100

u16 CCR1_dc = 20;

u16 CCR2_dc = 40;

u16 CCR3_dc = 60;

u16 CCR4_dc = 80;


u32 capture = 0;

u8 flag1 = 0, flag2 = 0, flag3 = 0, flag4 = 0;

u16 setcap = 0;


void TIM2_IRQHandler( void )

{

    if( TIM_GetITStatus( TIM2, TIM_IT_CC1 ) != RESET )

    {

        TIM_ClearITPendingBit( TIM2, TIM_IT_CC1 );

        capture = TIM_GetCapture1( TIM2 );

        //设置占空比

        if( flag1 == 0 )

        {

            flag1 = 1;

            setcap = capture + ( u32 )CCR1_Val * CCR1_dc / 100;

        }

        else

        {

            flag1 = 0;

            setcap = capture + ( u32 )CCR1_Val  * ( 100 - CCR1_dc ) / 100;

        }

        TIM_SetCompare1( TIM2, setcap  );

    }


    if( TIM_GetITStatus( TIM2, TIM_IT_CC2 ) != RESET )

    {

        TIM_ClearITPendingBit( TIM2, TIM_IT_CC2 );

        capture = TIM_GetCapture2( TIM2 );

        if( flag2 == 0 )

        {

            flag2 = 1;

            setcap = capture + CCR2_Val * CCR2_dc / 100;

        }

        else

        {

            flag2 = 0;

            setcap = capture + CCR2_Val  * ( 100 - CCR2_dc ) / 100;

        }

        TIM_SetCompare2( TIM2, setcap );

    }



    if( TIM_GetITStatus( TIM2, TIM_IT_CC3 ) != RESET )

    {

        TIM_ClearITPendingBit( TIM2, TIM_IT_CC3 );

        capture = TIM_GetCapture3( TIM2 );


        if( flag3 == 0 )

        {

            flag3 = 1;

            setcap = capture + CCR3_Val * CCR3_dc / 100;

        }

        else

        {

            flag3 = 0;

            setcap = capture + CCR3_Val  * ( 100 - CCR3_dc ) / 100;

        }

        TIM_SetCompare3( TIM2, setcap );

    }


    if( TIM_GetITStatus( TIM2, TIM_IT_CC4 ) != RESET )

    {

        TIM_ClearITPendingBit( TIM2, TIM_IT_CC4 );

        capture = TIM_GetCapture4( TIM2 );


        if( flag4 == 0 )

        {

            flag4 = 1;

            setcap = capture + CCR4_Val * CCR4_dc / 100;

        }

        else

        {

            flag4 = 0;

            setcap = capture + CCR4_Val  * ( 100 - CCR4_dc ) / 100;

        }

        TIM_SetCompare4( TIM2, setcap );

    }


}


  进入中断后,首先读取当前比较寄存器的值,因为比较寄存器的值是会一直累加的,直到比较寄存器的值等于ARR的时,才会清零。所以每次中断的时候,需要将现在的比较值读出来,然后加上一个值,作为下一次的比较值。读出当前的比较值存放在capture中,接下来需要在加一个值,作为下一次的比较值。因为要改变占空比,所以增加的值有两种情况,分别是高电平持续时间值和低电平持续时间值。为了区分高低电平,这里使用的一个标志位来判断,标志位的值只有0和1两种情况,将0作为高电平的时间,将1作为低电平的时间。CCRx_Val 中存放整个周期的计数值,当标志位为0时,CCRx_Val 乘以高电平的百分比,计算出高电平的累加时间。当标志位为1时,CCRx_Val 乘以低电平的百分比,计算出低电平的累加时间。CCRx_dc 中存放占空比的值,范围是0到100,表示占空比从0%到100%。 当需要改变周期时,就修改每个通道的 CCRx_Val 值,当需要改变占空比时,就修改每个通道的 CCRx_dc 的值。


int main( void )

{

    u16 i = 0;

    delay_init();

    NVIC_PriorityGroupConfig( NVIC_PriorityGroup_2 );

    TIM2_PWM_Init( 65535, 0 );

    while( 1 )

    {

    }

}


  接下来在主函数中初始化定时器,定时器2的4个通道输出波形如下:

请添加图片描述

  通过波形可以看到,定时器2的四个通道频率和占空比都是不一样的。说明通过定时器中的比较模式,是可以实现同一个定时器输出不同频率PWM波的。

关键字:STM32  单片机  定时器输出  PWM波 引用地址:STM32单片机一个定时器输出不同频率PWM波

上一篇:在Mac OS X中搭建STM32开发环境(2)
下一篇:STM32F103串口1 printf函数的实现

推荐阅读最新更新时间:2024-11-12 13:22

STM32 SVCall
一个特殊的中断:SVCall 简述:一种由程序进行触发的中断,默认开启 起源:SVC(系统服务调用,亦简称系统调用)多用于在操作系统之上的软件开发中。SVC 用于产生系统函数的调用请求。例如,操作系统不让用户程序直接访问硬件,而是通过提供一些系统服务函数,用户程序使用 SVC 发出对系统服务函数的呼叫请求,以这种方法调用它们来间接访问硬件。因此,当用户程序想要控制特定的硬件时,它就会产生一个 SVC 异常,然后操作系统提供的 SVC 异常服务例程得到执行,它再调用相关的操作系统函数,后者完成用户程序请求的服务。 用途:可以通过设置,使得一段代码能够被某些中断打断,而不能被另外一些中断打断,比如可用于确保模拟IIC的时序
[单片机]
采用MSP430单片机与DTMF技术设计医院呼叫对讲系统设计
随着科学技术的迅速发展,尤其是电子信息技术的不断更新,医疗卫生事业也逐步深化改革,实现现代化医疗成为医院未来发展的趋势,医院呼叫对讲系统是提高医院的护理水平的重要设备之一,其基本功能就是通过一种简便的途径使护理对象与医护人员达成沟通。16位的MSP430单片机的集成度很高,片内资源丰富,主要的突出特点是低功耗,可以实现长时间的稳定工作,应用到该系统后,性能得到很好的优化,可以提高医护人员的工作效率和降低他们的劳动强度,病人也能得到及时的护理和医疗,可以显着提高医院的服务水平和医疗质量。 1 DTMF呼叫对讲系统介绍 1.1 DTMF技术的介绍 DTMF(Dual Tone Multi Frequency ),即双音
[单片机]
采用MSP430<font color='red'>单片机</font>与DTMF技术设计医院呼叫对讲系统设计
STM32系统时钟和分频的一点理解
系统时钟和分频 首先来手册里的一段话。 三种不同的时钟源可被用来驱动系统时钟 (SYSCLK) HSI振荡器时钟 HSE振荡器时钟 PLL时钟 一般用的是PLL时钟,后面有证据。 我们可以通过库函数获取各时钟值 void RCC_GetClocksFreq(RCC_ClocksTypeDef* RCC_Clocks) 在我的系统里,把时钟值打印信息如下: SYSCLK:0x44aa200 //72000000, 72MHz HCLK:0x44aa200 //72000000, 72MHz PCLK1:0x2255100 //36000000, 36MHz PCLK2:0x44aa200
[单片机]
对<font color='red'>STM32</font>系统时钟和分频的一点理解
编写STM32项目遇到的报错
1.Undefined symbol SystemInit(未定义符号SystemInit) 新建项目并编译后遇到以下的错误: ..OutputLED.axf: Error: L6218E: Undefined symbol SystemInit (referred from startup_stm32f429_439xx.o). 错误分析:从括号里面的内容可以知道:这个错误在 startup_stm32f0xx.o 这个文件里面被提及到,所以我们只需要在工程里面找到对应的.s或者.c文件即可(此错误对应的文件为startup_stm32f429_439xx.s)。 这个文件是STM32的启动文件,在此文件中可以找
[单片机]
编写<font color='red'>STM32</font>项目遇到的报错
基于单片机设计的分布式电池管理系统
1 前言 随着高科技及其产业的迅速发展,大存储容量的电池组能源系统已经越来越被人们所重视,在很多领域中都得到广泛地应用,如在汽车产业发展的新方向、新热点——电动汽车及混合动力车的研究及产业化中,将作为车载能源的主要供给者。 蓄电池组是由一定数量的单体电池串联组成的,它可以进行百次至千次的充放电;在使用中必须注意其各个单体电池的各种特性、电池温度、电池的剩余电量及总电流等参数,因为这些参数直接影响电池的使用寿命,必须做到优化运行和有效监控,防止电池出现过充、过放及温度过高等问题,从而延长电池的使用寿命和降低成本,特别是提高电池的可靠性。可以把给电池组配套的电子、控制及数字技术称为数字“电池电子技术”。同样在汽车的电子、数
[单片机]
基于<font color='red'>单片机</font>设计的分布式电池管理系统
stm32 - keil中启动文件的选择和固件库宏定义
● 启动文件的选择 1. Low-density devices (STM32F10nx4 = 16k, STM32F10nx6 = 32k) - startup_stm32f10x_ld_vl.s: 小容量超值型。STM32F100xx系列,闪存16k~32k字节。 - startup_stm32f10x_ld.s: 小容量型。STM32F101xx, STM32F102xx, STM32F103xx系列,闪存16k~32k字节。 2. Medium-density devices (STM32F10nx8 = 64k, STM32F10nxB = 125k) - startup_stm32f10x_md_vl.s: 中
[单片机]
<font color='red'>stm32</font> - keil中启动文件的选择和固件库宏定义
STM32之Systick(系统时钟&滴答定时器)
Systick(系统)定时器 Systick定时器是一个简单定时器,就是系统滴答定时器,可以用来做延时或者系统的心跳时钟 Systick有两个可选的时钟源,一个是外部时钟STCLK,等于HCLK/8,一个是内部时钟HCLK //使用外部时钟 SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);//使用外部时钟,时钟源为HCLK的8分频 //例如外部晶振为8MHz,倍频到72MHz,那么HCLK为72MHz,Systick时钟为9MHz //也就是说,Systick的计数器VAL每减1,时间就过了1/9us 假若选择HCLK,且HCLK频率为72MHz的话,系统时钟周
[单片机]
STM32微控制器应用于Pebble智能手表
意法半导体(STMicroelectronics,简称ST)的STM32微控制器被Pebble智能手表所采用,用于控制这款创新的兼容iPhone手机和安卓智能手机的穿戴式产品。 Pebble智能手表通过蓝牙无缝连接到iPhone和安卓智能手机,当有来电、电子邮件和短信时,可通过静音振动通知用户。Pebble智能手表内置实时性能和能效俱佳的STM32 F2微控制器,使手表在功能性和电池使用寿命之间实现完美平衡,为用户提供全定制功能,配备精美的表盘下载和实用的联网应用软件。 Pebble创始人、首席执行官Eric Migicovsky表示:“Pebble穿戴式产品的用户想要一个这样的手表,它能够无缝连接其它设备,连续运行几
[单片机]

推荐帖子

深度剖析“怡成”5D-1型血糖仪~~
前不久我拆解了特价购入的怡成5D-1型血糖仪:https://bbs.eeworld.com.cn/thread-467912-1-1.html,大家看的尽兴么?昨天@exiao又放上来了此款血糖仪的程序源码:https://bbs.eeworld.com.cn/thread-469469-1-1.html,哈哈,配成套啦~不过,有了拆解的板子,有了原理图,有了源码,还不够,咱打算再趁热打铁对怡成5D-1型血糖仪来次深度的剖析,争取大家都学会怎么去做一个简易的血糖仪?血糖微创检测的
okhxyyo DIY/开源硬件专区
基于电流型脉宽调制器的单端反激式稳压电源设计
电源装置是电力电子技术应用的一个重要领域,其中高频开关式直流稳压电源由于具有效率高、体积小、重量轻等突出优点,而得到了广泛应用。本文详细介绍一种电流控制型开关电源,以低成本的UC3842PWM控制芯片为核心构成的多路输出的开关电源设计电路。本设计采用高性能的专用芯片UC3842,应用原边检测技术去除副边反馈环,从而降低了成本。1单端反激式变换器的特点单端反激式变换器又称电感储能式变换器,工作原理如图1所示,当开关管S1被PWM脉冲激励而导通时,次级整流二极管D1截
破茧佼龙 电源技术
ZigBee不同版本详解
ZigBee的基础是IEEE802.15.4,这是IEEE无线个人区域网(PersonalAreaNetwork,PAN)工作组的一项标准,被称作IEEE802.15.4(ZigBee)技术标准。ZigBee不仅只是802.15.4的名字。IEEE仅处理低级MAC层和物理层协议,因此ZigBee联盟对其网络层协议和API进行了标准化。ZigBee联盟还开发了安全层,以保证这种便携设备不会意外泄漏其标识,而且这种利用网络的远距离传输不
吸铁石上 RF/无线
《深度强化学习实战》读 深度Q网络
本篇来学习深度Q网络,下面做学习笔记理解。为引出深度Q神经网路,开篇举例格子游戏(Gridworld)。给出以下下概念定义。状态:是智能体收到的用于决定采取什么动作的信息。策略:是智能体在收到一个状态时所遵循的对策奖励:是智能体在采取动作后得到的反馈,会产生一个新状态。把从起始状态s1开始遵循某个策略得到的奖励的加权和称为状态-价值,用价值函数如下表示---该函数接收一个初始状态并返回预期的总奖励。其中,系数w1、w
dirty 嵌入式系统
升压电路原理
哪位高手能帮忙讲解一下,不甚感激file:///C:/Documents%20and%20Settings/new/桌面升压电路原理0000000000原理图见你"求教高人帮忙讲解以下电路"的帖子.请参考在xlzhang123友的博客上有一个完全一样的电路,对此作了些简单的讨论,原文Paste如下,请指教:理解倍压电路的关键在"记忆"元件及其工作过程倍压电路的理解中有一个很重要的地方就是必须有对应的"记忆-储能"元件.这里的关键之一在C111和DN102(右半部分)这是一个
joden123 模拟电子
一个按键程序
P1.0~P1.3接行P1.4~P1.7接列#includereg52.h#defineucharunsignedchar#defineuintunsignedintucharnum,temp,num1;uchari=0,j=0;ucharcodetable={0x02,0X9F,0x25,0x0D,0x99,0x49,0x40,0x1F,0x00,0x08};//延时函数voiddelay(uinti){uintj;for(;
zoling 嵌入式系统
小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

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