0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

用定时器生成PWM波的方法

电子工程师 来源:单片机与嵌入式 作者:单片机与嵌入式 2022-07-29 09:15 次阅读

定时器生成PWM波

PWM全称是Pulse Width Modulation,通过控制高频信号的占空比,眼睛当成低通滤波器,可以控制亮暗。再循环更改PWM的阈值,就弄出了呼吸的效果,相关文章推荐:STM32中PWM的配置与应用详解

这里采用一个比较简单的方法生成PWM波:设置定时器中断然后根据阈值判断置高和置低。

void TIM3_IRQHandler(void)  {TIM_ClearITPendingBit(TIM3,TIM_IT_Update);        if(counter==255)                        counter = 0;        else counter+=1;        if(mode == 0){            if(counter < pwm)                              GPIO_SetBits(GPIOA,GPIO_Pin_0|GPIO_Pin_1);             else                 GPIO_ResetBits(GPIOA,GPIO_Pin_0|GPIO_Pin_1);    }        if(mode == 1)        {            if(counter < pwm)                              GPIO_SetBits(GPIOA,GPIO_Pin_1|GPIO_Pin_2);             else GPIO_ResetBits(GPIOA,GPIO_Pin_1|GPIO_Pin_2);}        if(mode ==2){            if(counter < pwm)                              GPIO_SetBits(GPIOA,GPIO_Pin_2|GPIO_Pin_0);             else                 GPIO_ResetBits(GPIOA,GPIO_Pin_2|GPIO_Pin_0);         }}

程序流程

  • 开启外设时钟(GPIO和TIM)

void RCC_Configuration(void)                {     RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC|RCC_APB2Periph_GPIOA|RCC_APB2Periph_AFIO, ENABLE);                                                            RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4|RCC_APB1Periph_TIM3, ENABLE); }
  • 配置GPIO

  • 配置时钟, 使能中断(计数阈值,预分频,时钟分频,计数模式)

void tim3()                           //配置TIM3为基本定时器模式 ,约10us触发一次,触发频率约100kHz{TIM_TimeBaseInitTypeDefTIM_TimeBaseStructure;//定义格式为TIM_TimeBaseInitTypeDef的结构体的名字为TIM_TimeBaseStructureTIM_TimeBaseStructure. TIM_Period =9;         //配置计数阈值为9,超过时,自动清零,并触发中断TIM_TimeBaseStructure.TIM_Prescaler=71;//时钟预分频值,除以多少TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;  // 时钟分频倍数TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;//计数方式为向上计数TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);      //  初始化tim3TIM_ClearITPendingBit(TIM3,TIM_IT_Update); //清除TIM3溢出中断标志TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE); //  使能TIM3的溢出更新中断TIM_Cmd(TIM3,ENABLE);                     //           使能TIM3}
  • 配置中断优先级

void nvic()                                 //配置中断优先级{    NVIC_InitTypeDefNVIC_InitStructure;////命名一优先级变量 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);    //     将优先级分组方式配置为group1,有2个抢占(打断)优先级,8个响应优先级 NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn; //该中断为TIM4溢出更新中断 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;//打断优先级为1,在该组中为较低的,0优先级最高 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; // 响应优先级0,打断优先级一样时,0最高 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;        //  设置使能NVIC_Init(&NVIC_InitStructure);//初始化 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1); //要用同一个Group NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn; //TIM3 溢出更新中断 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;//    打断优先级为1,与上一个相同,不希望中断相互打断对方 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;     //  响应优先级1,低于上一个,当两个中断同时来时,上一个先执行 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure);}
  • 写中断服务函数

代码实现

为了方便按键检测,除了TIM3配置PWM波之外,TIM4用来检测是否有输入。由于使用开漏输出,这里使用5V电源

#include "stm32f10x.h"#include "math.h"#include"stdio.h"
u8  counter=0; int  pwm=100;int flag=0;int mode =0;int velocity =0;int turning=1;
void RCC_Configuration(void);    //时钟初始化,开启外设时钟void GPIO_Configuration(void);   //IO口初始化,配置其功能void tim3(void);                 //定时器tim4初始化配置void tim4(void);                 //定时器tim4初始化配置void nvic(void);                 //中断优先级等配置void exti(void);                 //外部中断配置void delay_nus(u32);           //72M时钟下,约延时usvoid delay_nms(u32);            //72M时钟下,约延时msvoid breathing(int velocity){        switch(velocity){                case 0:                    if(flag)                            pwm +=1;                            if(pwm>240) flag=0;                    if(flag == 0){                            pwm -=1;                            if(pwm<10) flag=1;                    }break;                case 1:                    if(flag)                            pwm +=2;                            if(pwm>240) flag=0;                    if(flag == 0){                            pwm -=2;                            if(pwm<10) flag=1;                    }break;                case 2:                    if(flag)                            pwm +=3;                            if(pwm>240) flag=0;                    if(flag == 0){                            pwm -=3;                            if(pwm<10) flag=1;                    }                    break;        }}

void assert_failed(uint8_t* file, uint32_t line){    printf("Wrong parameters value: file %s on line %d
", file, line);    while(1);}
void TIM4_IRQHandler(void)   //TIM4的溢出更新中断响应函数 ,读取按键输入值,根据输入控制pwm波占空比{        u8 key_in1=0x01,key_in2=0x01;TIM_ClearITPendingBit(TIM4,TIM_IT_Update);//清空TIM4溢出中断响应函数标志位        key_in1= GPIO_ReadInputDataBit(GPIOC,GPIO_Pin_12);  // 读PC12的状态key_in2=GPIO_ReadInputDataBit(GPIOC,GPIO_Pin_13);//读PC13的状态if(key_in1&&key_in2)turning=1;breathing(velocity);        if(key_in1==0 && turning){                turning =0;        velocity = (velocity + 1) % 3;}//调速度    if(key_in2==0 && turning){                turning =0;        mode = (mode + 1) % 3;    }//调颜色}   

void TIM3_IRQHandler(void)      //    //TIM3的溢出更新中断响应函数,产生pwm波{TIM_ClearITPendingBit(TIM3,TIM_IT_Update);////清空TIM3溢出中断响应函数标志位        if(counter==255)            //counter 从0到255累加循环计数,每进一次中断,counter加一            counter = 0;        else counter+=1;        if(mode == 0){            if(counter < pwm)              //当counter值小于pwm值时,将IO口设为高;当counter值大于等于pwm时,将IO口置低                GPIO_SetBits(GPIOA,GPIO_Pin_0|GPIO_Pin_1); //将PC14 PC15置为高电平            else                         GPIO_ResetBits(GPIOA,GPIO_Pin_0|GPIO_Pin_1);     // 将PC14 PC15置为低电平}        if(mode == 1)        {            if(counter < pwm)              //当counter值小于pwm值时,将IO口设为高;当counter值大于等于pwm时,将IO口置低                GPIO_SetBits(GPIOA,GPIO_Pin_1|GPIO_Pin_2); //将PC14 PC15置为高电平            else GPIO_ResetBits(GPIOA,GPIO_Pin_1|GPIO_Pin_2);//将PC14PC15置为低电平}        if(mode ==2){            if(counter < pwm)              //当counter值小于pwm值时,将IO口设为高;当counter值大于等于pwm时,将IO口置低                GPIO_SetBits(GPIOA,GPIO_Pin_2|GPIO_Pin_0); //将PC14 PC15置为高电平            else                 GPIO_ResetBits(GPIOA,GPIO_Pin_2|GPIO_Pin_0); // 将PC14 PC15置为低电平        }}   

int main(void){    RCC_Configuration();                                                                      GPIO_Configuration();                             tim4();    tim3();nvic();    while(1)    { }}   
void delay_nus(u32 n)       //72M时钟下,约延时us{  u8 i;  while(n--)  {    i=7;    while(i--);  }}

void delay_nms(u32 n)     //72M时钟下,约延时ms{    while(n--)      delay_nus(1000);}

void RCC_Configuration(void)                 //使用任何一个外设时,务必开启其相应的时钟{    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC|RCC_APB2Periph_GPIOA|RCC_APB2Periph_AFIO, ENABLE);    //使能APB2控制外设的时钟,包括GPIOC, 功能复用时钟AFIO等,                                                                                  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4|RCC_APB1Periph_TIM3, ENABLE); //使能APB1控制外设的时钟,定时器tim3、4,其他外设详见手册             }

void GPIO_Configuration(void)            //使用某io口输入输出时,请务必对其初始化配置{    GPIO_InitTypeDef GPIO_InitStructure;   //定义格式为GPIO_InitTypeDef的结构体的名字为GPIO_InitStructure  //typedefstruct{u16GPIO_Pin;GPIOSpeed_TypeDefGPIO_Speed;GPIOMode_TypeDefGPIO_Mode;}GPIO_InitTypeDef;    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;    //配置IO口的工作模式为上拉输入(该io口内部外接电阻到电源)    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //配置IO口最高的输出速率为50M    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12|GPIO_Pin_13;  //配置被选中的管脚,|表示同时被选中GPIO_Init(GPIOC,&GPIO_InitStructure);//初始化GPIOC的相应IO口为上述配置,用于按键检测    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;       //配置IO口工作模式为 推挽输出(有较强的输出能力)    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;      //配置IO口最高的输出速率为50M    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2;  //配置被选的管脚,|表示同时被选中    GPIO_Init(GPIOA, &GPIO_InitStructure);        //初始化GPIOA的相应IO口为上述配置    GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE); //失能STM32 JTAG烧写功能,只能用SWD模式烧写,解放出PA15和PB中部分IO口}

void tim4()                           //配置TIM4为基本定时器模式,约10ms触发一次,触发频率约100Hz{TIM_TimeBaseInitTypeDefTIM_TimeBaseStructure;//定义格式为TIM_TimeBaseInitTypeDef的结构体的名字为TIM_TimeBaseStructure    TIM_TimeBaseStructure. TIM_Period =9999;          // 配置计数阈值为9999,超过时,自动清零,并触发中断TIM_TimeBaseStructure.TIM_Prescaler=71;//时钟预分频值,除以多少    TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; // 时钟分频倍数TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;//计数方式为向上计数    TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure);      //  初始化tim4    TIM_ClearITPendingBit(TIM4,TIM_IT_Update); //清除TIM4溢出中断标志    TIM_ITConfig(TIM4,TIM_IT_Update,ENABLE);   //  使能TIM4的溢出更新中断    TIM_Cmd(TIM4,ENABLE);                //        使能TIM4}

void tim3()                           //配置TIM3为基本定时器模式 ,约10us触发一次,触发频率约100kHz{TIM_TimeBaseInitTypeDefTIM_TimeBaseStructure;//定义格式为TIM_TimeBaseInitTypeDef的结构体的名字为TIM_TimeBaseStructure    TIM_TimeBaseStructure. TIM_Period =9;         //配置计数阈值为9,超过时,自动清零,并触发中断TIM_TimeBaseStructure.TIM_Prescaler=71;//时钟预分频值,除以多少    TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;  // 时钟分频倍数TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;//计数方式为向上计数    TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);      //  初始化tim3    TIM_ClearITPendingBit(TIM3,TIM_IT_Update); //清除TIM3溢出中断标志    TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE); //  使能TIM3的溢出更新中断    TIM_Cmd(TIM3,ENABLE);                     //           使能TIM3}

void nvic()                                 //配置中断优先级{    NVIC_InitTypeDefNVIC_InitStructure;////命名一优先级变量     NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);    //     将优先级分组方式配置为group1,有2个抢占(打断)优先级,8个响应优先级     NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn; //该中断为TIM4溢出更新中断     NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;//打断优先级为1,在该组中为较低的,0优先级最高     NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; // 响应优先级0,打断优先级一样时,0最高     NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;        //  设置使能NVIC_Init(&NVIC_InitStructure);//初始化     NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1); //要用同一个Group     NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn; //TIM3 溢出更新中断     NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;//    打断优先级为1,与上一个相同,不希望中断相互打断对方     NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;     //  响应优先级1,低于上一个,当两个中断同时来时,上一个先执行     NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;NVIC_Init(&NVIC_InitStructure);}

审核编辑:汤梓红


声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • PWM
    PWM
    +关注

    关注

    114

    文章

    5186

    浏览量

    213878
  • STM32
    +关注

    关注

    2270

    文章

    10899

    浏览量

    355886
  • 定时器
    +关注

    关注

    23

    文章

    3247

    浏览量

    114764

原文标题:STM32呼吸灯的PWM原理与代码实现

文章出处:【微信号:单片机与嵌入式,微信公众号:单片机与嵌入式】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    定时器PWM模式的疑点!

    先上图,这是我定时器3的PWM模式1的通道1生成的脉冲但是接二连三的问题就出现了首先我在定时器
    发表于 04-30 15:27

    定时器产生PWM

    定时器产生20ms为周期的PWM,可以实现,但是我的步进有0.08ms,我现在想实现步进0.02ms,怎么办。
    发表于 08-09 15:33

    STM32.定时器.PWM

    定时器的通道与路是什么关系???定时器可以通过不同的通道实现不同的模式输出,那么图片所说的PWM 4路输出是什么意思?如果每一路代表不同的模式,是4种不同的模式、通过不同端口输出吗?还是说相同的模式下也以4路? 仅使用一种通道产
    发表于 07-17 10:01

    一个定时器生成多路PWM波形的原理和方法

    ,实现一个定时器生成多路PWM方法如下。首先来看看一个定时器实现一路
    发表于 03-30 21:03

    如何判断PWM是由定时器还是PWM模块产生的?

    正在看清华版的原理&实践,在看到TM4C的PWM模块时没太看懂,PWM声称模块中也是用了定时器定时器生成
    发表于 09-06 10:55

    定时器输出PWM

    定时器输出PWM什么是PWM脉冲宽度调制(PWM),是英文“Pulse Width Modulation”的缩写,简称脉宽调制,是利用微处
    发表于 01-05 07:05

    定时器输出PWM实验

    定时器输出PWM 实验 一. 实验目的利用定时器控制产生占空比可变的PWM 。二. 实验设备及器件IBM PC 机 一台DP-51PRO
    发表于 09-22 10:49 7420次阅读

    如何通过STM32的定时器输出PWM

      本文将介绍通过STM32的定时器输出PWM,如果对定时器不太熟悉的同学可以看下之前的文章《STM32基础定时器详解》,关于定时器的基础功
    的头像 发表于 02-20 15:08 2.2w次阅读
    如何通过STM32的<b class='flag-5'>定时器</b>输出<b class='flag-5'>PWM</b>?

    51单片机定时器实现PWM

    51单片机是可以实现PWM输出的,原理其实都是一样的。说白了,PWM就是让某一个引脚输出周期性连续高低电平变化的信号。那么如何用51单片机实现周期性的高低电平呢?答案就是
    发表于 11-12 10:36 13次下载
    51单片机<b class='flag-5'>定时器</b>实现<b class='flag-5'>PWM</b><b class='flag-5'>波</b>

    STM8学习笔记---定时器输出7路PWM

    STM8S003F3P6单片机共有三个定时器定时器1、定时器2、定时器4。其中定时器1为16位高级定时器
    发表于 11-26 16:06 9次下载
    STM8学习笔记---<b class='flag-5'>定时器</b>输出7路<b class='flag-5'>PWM</b><b class='flag-5'>波</b>

    STM32CubeMX_定时器中断_PWM

    文章目录前言STM32CubeMX新建工程基本定时器配置生成代码定时器中断PWM配置工程代码前言STM32CubeMX_环境搭建_GPIO_外部中断上节整理的是GPIO和外部中断, 这
    发表于 12-05 13:51 13次下载
    STM32CubeMX_<b class='flag-5'>定时器</b>中断_<b class='flag-5'>PWM</b>

    定时器生成PWM的原理和方法

    PWM全称是Pulse Width Modulation,通过控制高频信号的占空比,眼睛当成低通滤波,可以控制亮暗。再循环更改pwm的阈值,就弄出了呼吸的效果。
    的头像 发表于 06-13 14:46 4720次阅读

    利用通用定时器输出PWM(附示例驱动直流电机)

    上一节讲述了时钟树和基本定时器的配置方法,本节先介绍通用定时器和基本定时器的差异,然后粗略讲述PWM
    发表于 04-03 14:56 0次下载
    利用通用<b class='flag-5'>定时器</b>输出<b class='flag-5'>PWM</b>(附示例驱动直流电机)

    一文详解HPM6000系列PWM定时器模块

    在进行电机类、电源类应用开发时,如何使用PWM定时器模块灵活、高效的实现所需 PWM波形的输出,是众多开发者关注的问题。在上篇文章里,我们介绍了PWM
    的头像 发表于 05-30 14:36 1169次阅读
    一文详解HPM6000系列<b class='flag-5'>PWM</b><b class='flag-5'>定时器</b>模块

    高精度定时器与高级控制定时器 PWM后再恢复的区别

    高精度定时器与高级控制定时器 PWM后再恢复的区别
    的头像 发表于 10-17 16:52 783次阅读
    高精度<b class='flag-5'>定时器</b>与高级控制<b class='flag-5'>定时器</b> <b class='flag-5'>PWM</b> 封<b class='flag-5'>波</b>后再恢复的区别