PWM

概述

本次测试用的是RA生态工作室提供的R7FA2E1A72DFL demo板,控制定时器输出互补的带死区PWM,并通过中断触发一对IO口的电平切换。

问题

为什么使用GPT输出带死区的互补PWM,进入比较匹配中断后会有一小段延时才开始执行callback。

分析

触发中断后没有马上执行逻辑操作,在底层耗时太长。

操作

使用e2 studio配置工程分析现象

1、GPT模块的设置

时钟框图如下,GPT0是32位定时器,GPT4、GPT5、GPT6、GPT7、GPT8和GPT9是16位定时器。

“图1
图1 GPT框图

2、配置工程时需要注意对应通道是0,4,5,6,7,8,9,没有1,2,3。

配置过程:

timer7,P302->GTIOC7A,P301->GTIOC7B,20kHz,duty=50%,打开Capture A Interrupt(compare match A),输出互补同步三角波pwm,死区设1us。

“图2
图2 GPT配置

3、配置IO口在callback进行翻转

“图3
图3 在timer7_callback触发IO翻转

现象如下:

A:死区时间AB设定为1us,1,2通道为GTIOC7A(P302)和GTIOC7B (P301)

B:触发比较匹配中断后,翻转3(P103)通道和4(P104)通道的电平

从触发中断到完成3,4通道电平翻转的时间AC为3.3us。

“图4
图4 定时器中断触发IO翻转时间

分析——使用GPT输出带死区的互补PWM,进入比较匹配中断后会有一小段时间的延时才开始执行电平翻转:

a:通过操作寄存器完成翻转的时间为350ns,因此排除操作IO口占用时间的因素

b:在callback中完成电平翻转需要3.3us

4、为了缩短时间,把IO翻转改到gpt_capture_a_isr中执行,不在r_gpt_call_callback中执行IO翻转,

“图5
图5 gpt_capture_a_isr

“图6
图6 触发中断到完成IO翻转时间为AC:1.22us

结论

因为执行r_gpt_call_callback前后还需要执行一段代码,执行底层耗时过长,因此把callback改为NULL,然后在ISR底层中执行R_PORT1->PCNTR3_b.POSR,是目前测试时间最短的操作了。

来源:瑞萨MCU小百科
免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。

围观 57

引言

在高精度定时器中,可以使用外部事件来对 PWM 输出进行封锁,并可自动恢复;在高级控制定时器中,可以使用 Break 或是 Clr_input 来对 PWM 输出进行封锁, 然后也可以自动恢复,其中 Break 必须结合 AOE 置位来实现自动恢复。

虽然都可以实现封波后的自动恢复,但是在二者之间还是存在一些区别。

PWM 封波-自动恢复原理分析

在高精度定时器中,通过配置 PWM 的 Set 与 Reset 事件,当 Set/Reset 事件发生时,PWM 输出做出对应的响应。要实现封波-自动恢复,只需配置合适的 Reset 事件,Reset 发

生时,触发 PWM 输出 inactive 电平,当 Reset 事件消失后,PWM 将在下个 Set 事件点重新输出。

在高级控制定时器中,PWM 的工作方式有 PWM_Mode1 与 PWM_Mode2,基于定时器比较器的值与当前 Counter 的值来结合 PWM 模式来确定输出电平的状态。要实现封波-自动恢复,通过 Break(with AOE)或是 Clr_input 功能,当 Break 信号或是 Clr_input 信号电平有效时,PWM 输出会被封锁,此时端口电平跟当时的 PWM 配置有关;当 Break 信号或是 Clr_input 信号电平无效时,PWM 将在下个 UEV(更新事件)或是新的 PWM 的周期恢复输出。

下面通过实际的测试与波形来详细说明。

高精度定时器的 “封波-自动恢复”

使用的是外部事件 1(EEV1)关联外部过流故障信号,利用 EEV1 来封锁 PWM。配置如下(测试中使用的配置是高电平触发事件):

“高精度定时器与高级控制定时器

使用以上配置时,只要故障信号持续高电平的时间和 PWM 的 Set 事件不重叠,那么定时器在下个 PWM 周期能正常输出 PWM,如下图所示。

“高精度定时器与高级控制定时器

但是如果 Fault 信号高电平的持续时间覆盖了 PWM 的 set 事件,那么 PWM 将一直输出低电平,直到 Fault 信号高电平不再覆盖 PWM 的 Set 件,如下图所示。

“高精度定时器与高级控制定时器

另外,高精度定时器中的还可以工作的沿模式,例如配置 EEV1 工作在上升沿触发模式,如下图所示。

“高精度定时器与高级控制定时器

高级控制定时器的“封波-自动恢复”

高级控制定时器中,break 为电平有效模式(测试中使用的配置是高电平触发 Break)。当出现 Break 信号出现高电平时,PWM 封波,如果 Break 信号高电平未跨周期,那么在下个周期 PWM 能正常输出,如下图所示。

“高精度定时器与高级控制定时器

但是如果 Break 信号高电平跨周期,即使高电平持续时间没有到达比较事件点,下个周期 PWM 依旧继续“封波”,直到 Break 信号恢复低电平的下个周期 PWM 才会正常输出,如下图所示。

“高精度定时器与高级控制定时器

小结

综上以上的分析与实验结果:

• 高精度定时器封波基于事件触发,封波后只要故障消失,则在下个触发来临时就可以立即生效,恢复 PWM 输出。

• 高级控制定时器封波后,即使故障消失后,也要等到下个 UEV 事件或是新的 PWM周期才能恢复 PWM 输出。

来源:STM32单片机
免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。

围观 66

做STM32智能小车的实验中会用到定时器PWM输出,来改变直流电机的转速。分享本文了解如何通过PWM实现对电机速度的控制。

PWM控制电机速度的基本原理

PWM(Pulse Width Modulation),也就是脉冲宽度调制。

PWM中有一个比较重要的概念,占空比:是一个脉冲周期内有效电平在整个周期所占的比例。

为了实现IO口上电压的持续性变化,可以调节PWM的占空比。这也能够使外设的功率进行持续性变化,最终控制直流电机转速的快慢。如何调节PWM波形的输出就是重点。相关推荐:STM32中PWM的配置与应用详解。

“STM32通过PWM控制电机速度"

上图中的ARR是我们给定时器的一个预装载值,CCRx的上下变化是产生PWM波的关键。我们假设ARR大于CCRx的部分输出为高电平(即t1-t2、t3-t4、t5-t6),ARR小于CCRx的部分输出为低电平(即0-t1、t2-t3、t4-t5),则改变CCRx的值就能改变输出PWM的占空比。因此,想要控制PWM的输出波形,重要的就是如何设置ARR与CCRx这两个寄存器的值了。

STM32定时器中断

为了便于理解接下来关于PWM应用的内容,先插一段定时器中断的知识。

产生定时中断是定时器的用法之一,与定时器用来进行PWM输出和输入捕获相比,定时器中断更容易理解、掌握。

原理简介

使用通用定时器进行中断的原理,其实和开发板Systick定时器进行中断延时很相似(Stm32入门——Systick定时器),即:用psc(预分频系数)设置好定时器时钟后,arr(预装载值)在每个时钟周期内减1,当arr减为0时触发中断然后进入中断处理程序进行中断处理。以下代码为例:

void TIM3_Int_Init(u16 arr,u16 psc)
{
  RCC->APB1ENR|=1<<1;  //TIM3时钟使能    
   TIM3->ARR=arr;    //设定计数器自动重装值 
  TIM3->PSC=psc;    //预分频器设置
  TIM3->DIER|=1<<0;   //允许更新中断        
  TIM3->CR1|=0x01;    //使能定时器3
    MY_NVIC_Init(1,3,TIM3_IRQn,2);//抢占1,子优先级3,组2                   
}
RCC->APB1ENR|=1<<1

解释一下上面这行代码,由于定时器3(TIM3)是挂在APB1上的外设,所以要打开APB1,这里的预分频器值psc是来设置TIM3的时钟频率的,如果系统时钟(SYSTICK)频率为72MHz、psc为7199,则TIM3的时钟频率就为:

72MHz/(7199+1)Hz = 10KHz    //这里的“+1”是手册中规定的。

10KHz是什 么意思呢?就是一秒钟会产生10K个周期,那么一个周期的时间长度就是1/10KHz,如果你想将定时器中断的时间间隔设置为0.5秒,那么你将arr设置为5000即可,因为arr每减1就需要一个周期的时间,减5000次就经过了5000*(1/10KHz)=0.5秒。

TIM3->DIER|=1<<0

再解释下上面这一行,设置允许更新中断,即arr减到0以后可以触发更新中断,还有其他类型的中断。

MY_NVIC_Init(1,3,TIM3_IRQn,2);//抢占1,子优先级3,组2

看上面这行代码,中断优先级有抢占优先级和响应(即子优先级)优先级两种,抢占优先级即:若程序1正在使用CPU,这时如果程序2要求使用CPU,并且程序2的抢占优先级高,则CPU被程序2抢占;若两者抢占优先级相同,则就算程序2的响应优先级高于程序1,CPU也不能被抢占;若程序1正在使用CPU,程序2和程序3的抢占优先级等于或低于程序1,且程序2的响应优先级高于程序三,则待CPU空出后,程序2先运行,程序3最后运行。TIM3_IRQn是指定将要运行的中断处理程序号。“组2”是设置中断优先级分组的,这是因为寄存器提供了四位来设置优先级,组2代表的是前两位给抢占优先级,后两位给响应优先级。

PWM模式、有效电平

前面介绍完中断,再说一下PWM工作原理。相关文章:浅析PWM控制电机转速的原理。

假设上图中ARR大于CCRx时输出为高电平,ARR小于CCRx时输出为低电平,但在实际运用中可能并非如此,有可能是相反的情况——ARR大于CCRx时输出为低电平,ARR小于CCRx时输出为高电平,至于到底是哪种情况,还要看PWM是哪种模式、有效电平又设置的是何种极性了。

  • 模式1:ARR小于CCRx时输出为“有效”电平,ARR大于CCRx时输出为“无效”电平。

  • 模式2:ARR小于CCRx时输出为“无效”电平,ARR大于CCRx时输出为“有效”电平。

这里说的是“有效”和“无效”,而不是“高”和“低”,也就是说有效电平可高可低,并非一定就是高电平。PWM模式、效电平极性,需要程序员自己配置相关的寄存器来实现。通过下面的代码来讲解。

TIM1_PWM_Init(899,0);//不分频。PWM频率=72000/(899+1)=80Khz

上一小节讲过关于定时器参数的设置。使用定时器1的通道1来输出一路PWM波,这里的899设置的就是ARR的值,至于那个0是用来设置TIM1的频率的,不分频就代表TIM1的时钟频率和系统时钟相同,这里假设为72MHz。

void TIM1_PWM_Init(u16 arr,u16 psc)
{                
//此部分需手动修改IO口设置
  RCC->APB2ENR|=1<<11;   //TIM1时钟使能    
  GPIOA->CRH&=0XFFFFFFF0;  //PA8清除之前的设置
  GPIOA->CRH|=0X0000000B;  //复用功能输出 

  TIM1->ARR=arr;      //设定计数器自动重装值 
  TIM1->PSC=psc;      //预分频器设置

  TIM1->CCMR1|=7<<4;    //CH1 PWM2模式     
  TIM1->CCMR1|=1<<3;     //CH1预装载使能   
   TIM1->CCER|=0<<1;     //OC1 输出使能     
//TIM1->CCER|=1<<1;

  TIM1->BDTR|=1<<15;     //MOE 主输出使能     

  TIM1->CR1=0x0080;     //ARPE使能 
  TIM1->CR1|=0x01;      //使能定时器1                       
}

下文具体分析上面的代码。

前面4-6行是用来配置GPIO口的。

TIM1->ARR=arr; //设定计数器自动重装值
TIM1->PSC=psc; //预分频器设置

这两行就是我上门提到的设置定时器的频率和重装载值。

TIM1->CCMR1|=7<<4; //CH1 PWM2模式
TIM1->CCMR1|=1<<3; //CH1预装载使能
TIM1->CCER|=0<<1; //OC1 输出使能

这三行是用来设置PWM输出模式和设置通道的,通道是什么呢?简单地讲就是输出PWM波的GPIO口,代码一开始不是设置了PA8这个GPIO口嘛,这个PA8就是通道1。使用通道的话要先进行输入输出方向、通道使能的设置。

TIM1->CCER|=1<<1;

这行代码是用来设置“有效电平”极性的,根据手册,当TIM1->CCER[1]这位置1时,有效电平为低电平,置0时有效电平为高电平,而默认情况下置0。

TIM1->BDTR|=1<<15; //MOE 主输出使能

这行代码只要对高级定时器进行设置,普通定时器无需设置。

TIM1->CR1=0x0080; //ARPE使能

这行代码是用来使能ARPE,ARPE是什么呢,就是当它被置1时,你自己设置的CCRx会立即生效,如果它被置为0,那么你自己设置的CCRx值不会立即生效(可能之前ARPE已经有值了),而是当之前设置的CCRx生效后才会使用你最新设置的CCRx值。

上面的代码里没有对CCRx进行设置,这是因为CCRx常常是一个变化的值,你可以在主函数中用一个for循环+if判断语句对它进行++或–的操作,从而达到连续改变CCRx值得目的,例如:

for(i=0;i<300;i++){
  TIM1->CCR1=i;
if(i==300){
    i=0;
  }
}

PWM波的周期是由定时器时钟频率和预装载值两者决定的,预装载值就是ARR。

预装载值PSC设置为899,那么,当定时器的当前值val从0增加到899时,一共经过了900个时钟周期,这900个时钟周期会产生一个PWM波形,也就是说900个定时器时钟周期才相当于一个PWM周期,那么PWM的频率就为72MHz/900=80KHz,周期为1/80KHz。

来源:STM32嵌入式开发
免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。

围观 161

用定时器生成PWM波

PWM全称是Pulse Width Modulation,通过控制高频信号的占空比,眼睛当成低通滤波器,可以控制亮暗。再循环更改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_TimeBaseInitTypeDef  TIM_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_InitTypeDef NVIC_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时钟下,约延时us
void delay_nms(u32);            //72M时钟下,约延时ms
void 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\r\n", 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);     // 将PC14 PC15置为低电平
        }  
        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  
                                          //typedef struct { u16 GPIO_Pin; GPIOSpeed_TypeDef GPIO_Speed; GPIOMode_TypeDef GPIO_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_TimeBaseInitTypeDef  TIM_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_TimeBaseInitTypeDef  TIM_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_InitTypeDef NVIC_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);
}

来源:STM32嵌入式开发
免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。

围观 144

本文分享STM32之PWM波形输出配置总结。

一. TIMER分类:

STM32中一共有11个定时器,其中TIM6、TIM7是基本定时器;TIM2、TIM3、TIM4、TIM5是通用定时器;TIM1和TIM8是高级定时器,以及2个看门狗定时器和1个系统嘀嗒定时器。其中系统嘀嗒定时器是前文中所描述的SysTick。

“SMT32的PWM波形输出配置的大神总结”

其中TIM1和TIM8是能够产生3对PWM互补输出,常用于三相电机的驱动,时钟由APB2的输出产生。TIM2-TIM5是普通定时器,TIM6和TIM7是基本定时器,其时钟由APB1输出产生。

二、PWM波形产生的原理:

通用定时器可以利用GPIO引脚进行脉冲输出,在配置为比较输出、PWM输出功能时,捕获/比较寄存器TIMx_CCR被用作比较功能,下面把它简称为比较寄存器。

这里直接举例说明定时器的PWM输出工作过程:若配置脉冲计数器TIMx_CNT为向上计数,而重载寄存器TIMx_ARR被配置为N,即TIMx_CNT的当前计数值数值X在TIMxCLK时钟源的驱动下不断累加,当TIMx_CNT的数值X大于N时,会重置TIMx_CNT数值为0重新计数。

而在TIMxCNT计数的同时,TIMxCNT的计数值X会与比较寄存器TIMx_CCR预先存储了的数值A进行比较,当脉冲计数器TIMx_CNT的数值X小于比较寄存器TIMx_CCR的值A时,输出高电平(或低电平),相反地,当脉冲计数器的数值X大于或等于比较寄存器的值A时,输出低电平(或高电平)。

如此循环,得到的输出脉冲周期就为重载寄存器TIMx_ARR存储的数值(N+1)乘以触发脉冲的时钟周期,其脉冲宽度则为比较寄存器TIMx_CCR的值A乘以触发脉冲的时钟周期,即输出PWM的占空比为 A/(N+1) 。

三、STM32产生PWM的配置方法:

1、配置GPIO口:

配置IO口的时候无非就是开启时钟,然后选择引脚、模式、速率,最后就是用结构体初始化。不过在32上,不是每一个IO引脚都可以直接使用于PWM输出,因为在硬件上已经规定了用某些引脚来连接PWM的输出口。下面是定时器的引脚重映像,其实就是引脚的复用功能选择:

a.定时器1的引脚复用功能映像:

“SMT32的PWM波形输出配置的大神总结”

b.定时器2的引脚复用功能映像:

“SMT32的PWM波形输出配置的大神总结”

c.定时器3的引脚复用功能映像:

“SMT32的PWM波形输出配置的大神总结”

d.定时器4的引脚复用功能映像:

“SMT32的PWM波形输出配置的大神总结”

根据以上重映像表,我们使用定时器3的通道2作为PWM的输出引脚,所以需要对PB5引脚进行配置,对IO口操作代码:

GPIO_InitTypeDef GPIO_InitStructure;//定义结构体
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB  | RCC_APB2Periph_AFIO, ENABLE);//使能GPIO外设和AFIO复用功能模块时钟
GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3, ENABLE); //选择Timer3部分重映像    
//选择定时器3的通道2作为PWM的输出引脚TIM3_CH2->PB5    GPIOB.5
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; //TIM_CH2
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  //复用推挽功能
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化引脚

2、初始化定时器: 

TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;//定义初始化结构体
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //使能定时器3时钟
//初始化TIM3
TIM_TimeBaseStructure.TIM_Period = arr; //自动重装载寄存器的值
TIM_TimeBaseStructure.TIM_Prescaler =psc; //TIMX预分频的值
TIM_TimeBaseStructure.TIM_ClockDivision = 0; //时钟分割
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //向上计数
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根据以上功能对定时器进行初始化

3、设置TIM3_CH2的PWM模式,使能TIM3的CH2输出:

TIM_OCInitTypeDef  TIM_OCInitStructure;//定义结构体
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2;//选择定时器模式,TIM脉冲宽度调制模式2
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;//比较输出使能
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;//输出比较极性低
TIM_OC2Init(TIM3, &TIM_OCInitStructure);//根据结构体信息进行初始化
TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable);  //使能定时器TIM2在CCR2上的预装载值

4、使能定时器3:

TIM_Cmd(TIM3, ENABLE);  //使能定时器TIM3
经过以上的操作,定时器3的第二通道已经可以正常工作并输出PWM波了,只是其占空比和频率都是固定的,我们可以通过改变TIM3_CCR2,则可以控制它的占空比。修改占空比的函数为:TIM_SetCompare2(TIM3,n);  n不同,占空比不同。

5、修改pwm波形的占空比:

编写一个函数:void TIM3_PWM_Init(u16 arr,u16 psc);将以上所有的代码都加进来这个函数中,只要在main函数中调用该函数进行初始化,然后使用TIM_SetCompare2()函数修改PWM的占空比就可以在PB5脚得到需要的PWM波形了。关于频率以及占空比的计算方法有以下例子:

int main(void)
{
  TIM3_PWM_Init(9999,143);//频率为:72*10^6/(9999+1)/(143+1)=50Hz
  TIM_SetCompare2(TIM3,4999);//得到占空比为50%的pwm波形
  while(1);
}

本文转载自:硬件攻城狮
免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:
cathy@eetrend.com)。

围观 28

大多数微控制器至少有一个脉冲宽度调制 (PWM) 外设,以方波形式生成多个波形。这些 PWM 输出可用于驱动同步负载,例如机械系统中的步进电机和电源转换器的功率 MOSFET。对于这些负载,要使目标负载正常工作,PWM 波形必须精确同步,这一点非常重要。

如果 PWM 外设未经过仔细编程,它可能偶尔会在波形之间产生相位延迟,从而导致在波形边沿未正确对齐时失去同步。这些相位延迟将会降低负载的驱动效率,从而浪费功率并可能产生过多的热量。对于常见的 PWM 外设,可以启用或禁用某个 PWM,但同时会导致其他 PWM 输出发生相位延迟。

这对于小规格电池供电型物联网 (IoT) 应用而言尤其是个问题。在此类应用中,单个具有 16 或 32 路输出的 PWM 外设被用于控制多个外部负载。这类物联网应用中的相位延迟可能浪费电池电量。而且,由于未检测到相位延迟,物联网端点的网络诊断可能会遗漏这些延迟。

本文将讨论微控制器 PWM 外设的一些应用,以及在这类应用中,哪些情况下使 PWM 波形保持同步非常重要。然后介绍 Maxim Integrated 的一款微控制器,其中具有一个专为防止这类应用中丢失波形同步的脉冲串外设,最后讨论如何配置此外设以确保目标负载得到高效的驱动。

微控制器 PWM 外设及其目标负载

大多数通用微控制器至少有一个 PWM 外设,用于生成规则的重复方波。PWM 驱动可用于许多负载——从简单负载到更复杂的机械驱动系统。

发光二极管 (LED) 是可通过 PWM 信号高效驱动的简单负载示例之一,尤其是在需要对彩色 LED 进行调光的应用中。与通过改变正向直流电流来为 LED 调光相比,PWM 调光可以更精确地保持光线质量,而不会明显改变颜色。一个 PWM 外设可以轻松驱动一个或多个 LED。如果将这些 LED 用作操作员的视觉指示灯,则两个或多个 LED 之间的相位差不太明显。但如果将这些 LED 用于更复杂的应用,例如多个 LED 以光调制的形式将数据传输到受光器,则 LED 同步可能是非常重要的设计考虑因素。

微控制器 PWM 的另一种简单负载是通过电机驱动器 IC 驱动的直流电机。尽管通过改变直流电机两端的电压可以轻松改变直流电机的速度,但 PWM 控制可以更精确地控制电机旋转。如果将速度传感器用于闭环控制系统,则可以更精确地保持电机速度。如果使用两个或更多个直流电机并且它们必须一起运行,则可能有必要对 PWM 波形进行同步,以便在电机之间保持精确的速度控制。

驱动双极步进电机

当驱动双极步进电机时,设计情况变得更加复杂。双极步进电机由两个可逆的电流绕组驱动(图 1)。每个绕组需要两个 PWM,因此需要四个 PWM。

“图
图 1:双极步进电机由两个电流绕组(表示为红色和绿色线圈)驱动旋转,这两个绕组可承载每个方向上的电流。通过控制绕组中电流的相位和持续时间,可以轻松控制电机的速度和位置。(图片来源:Digi-Key)

如图 1 所示,红色和绿色线圈表示的两个电流绕组必须按正确的顺序驱动,才能使电机正常工作。在每个波形变化时,图 2 所示的序列驱动双极步进电机一整步。

“图
图 2:双极步进电机上的两个线圈必须根据上图进行分别驱动,才能使电机在每次波形变化时运动一整步。首先在一个方向上驱动每个线圈中的电流;接下来线圈空闲;然后以相反方向驱动电流。(图片来源:Digi-Key)

电机的每一步都从每个波形转换开始。如图 2 所示,绕组两端的电压极性以及因此流过每个绕组的电流,在每一步都会发生变化。任何 PWM 信号中的相位延迟都可能导致电机打滑,从而造成扭矩损失,尤其在低速运转时。

当微控制器具有仅使用四个输出的 PWM 外设时,可以轻松控制步进电机,只需适度留意维持同步。但如果使用同一 PWM 外设来控制多个负载,则情况会变得更加复杂。例如,一个 16 输出 PWM 可能将四个 PWM 输出分配给步进电机,而将其他 PWM 输出分配给其他负载,例如直流电机或 LED。使用适当的寄存器配置 PWM 输出的频率和占空比后,将在每个 PWM 的启用/禁用寄存器中设置一个位。在 Arm® 微控制器中,固件可通过使用位绑定来设置相应的位。但是,位绑定会对目标寄存器执行读取/修改/写入 (RMW) 操作。如果有其他 PWM 输出编程为在 RMW 操作期间开始或结束,则可能导致无法预测的结果,在某些情况下,甚至可能按与固件控制相反的方式启用或禁用 PWM。

Maxim Integrated 利用以 120 兆赫兹 (MHz) 频率运行的 MAX32650 Arm Cortex®-M4F 微控制器解决了这一问题。它具有广泛的外设,包括三个标准 SPI 接口、一个四通道 SPI、三个 UART、两个 I2C 端口、一个带物理层 (PHY) 的 USB 2.0 高速接口、六个 32 位定时器,以及一个 AES-256 加密单元(图 3)。

“图
图 3:Maxim Integrated 的 MAX32650 基于 120 MHz Arm Cortex-M4F,具有面向高性能物联网边缘计算应用的全系列外设和存储器选项。(图片来源:Maxim Integrated)

MAX32650 具有 3 MB 的闪存和 1 MB 的 SRAM,面向需要边缘计算的复杂物联网 (IoT) 端点。MAX32650 还具有一个 16 输出脉冲串外设,可以生成复杂的 PWM 信号。它可以生成具有可配置频率和 50% 占空比的方波,以及基于长度可达 32 位的可编程位模式的脉冲串。

防止相位延迟

脉冲串发生器可以使用 32 位 PTG_ENABLE 寄存器单独启用或禁用 16 个 PWM 输出中的任何一个。向任意位位置写入 1 将启用该脉冲串,使其按配置运行。写入 0 将停止脉冲串时钟和逻辑,将输出冻结在当前逻辑状态。该寄存器与大多数微控制器中的启用/禁用寄存器具有相同的 RMW 限制,因此不建议使用位绑定。

为了保持波形之间的相位同步,MAX32650 的脉冲串外设支持一种独特的功能,当使用 32 位寄存器 PTG_SAFE_EN 时称为“安全启用”,而当使用 32 位寄存器 PTG_SAFE_DIS 时则称为“安全禁用”。其中每个寄存器的高 16 位均未使用,建议这些未使用的位置始终写入零。

为了安全地启用任何输出,固件会将 1 写入 PTG_SAFE_EN 中的相应位位置。这还会立即设置这些输出在 PTG_ENABLE 中的位位置,从而启动 PWM 输出。向 PTG_SAFE_EN 中的任何位位置写入 0 对任何脉冲串输出都没有影响。

为了安全地禁用任何输出,固件会将 1 写入 PTG_SAFE_DIS 中的相应位位置。这还会立即清除这些输出在 PTG_ENABLE 中的位位置,从而停止 PWM 输出。向 PTG_SAFE_DIS 中的任何位位置写入 0 对任何脉冲串输出都没有影响。

写入这些寄存器不会执行 RMW。安全启用/禁用功能允许立即启动或停止一个或多个脉冲串,同时保证任何其他脉冲串都不会受到影响。PTG_SAFE_EN 和 PTG_SAFE_DIS 寄存器不支持位绑定。

再次参考图 1 中的双极步进电机,脉冲串输出 0 和 1 可用于 A 和 B 对应的绿色电流绕组,脉冲串输出 2 和 3 则可以用于 C 和 D 对应的红色电流绕组。由于图 2 中的波形包含死点,因此适合使用脉冲串功能来编程一种模式,并能配置为在没有固件干预的情况下重复任意次数。

设置后,可通过将 0000000Fh 写入 PTG_SAFE_EN 来启动电机。这会同时启动脉冲串输出 0 到 3,在不影响任何其他正在运行的脉冲串输出的情况下启动电机。通过将 0000000Fh 写入 PTG_SAFE_DIS,可停止电机。这两项操作都不会影响任何其他正在运行的脉冲串。

如果需要启用或禁用其他 12 个脉冲串输出中的任意一个,也可以使用这两个寄存器安全地控制它们。只要不将 1 写入这些寄存器的低四位位置,步进电机的操作就不会受到影响。这与使用具有 RMW 的标准启用寄存器完全不同,使用 RMW 时,输出可能会卡顿,从而引起相移,这可能对扭矩产生不利影响。安全启用/禁用功能类似于一种原子操作,因此可确保步进电机高效运行,不会浪费功率,并始终保持最大扭矩。

微控制器输出引脚没有足够的能力驱动步进电机,因此需要电机驱动器或 H 桥。Allegro MicroSystems 的 A3909GLYTR-T 是双 H 桥驱动器,可驱动需要 4 至 18 伏电压以及每个电流绕组高达 1 安培 (A) 电流的电机(图 4)。

“图
图 4:Allegro MicroSystems 的 A3909 是双 H 桥驱动器,可为步进电机线圈提供高达 1 A 的拉出和灌入电流。(图片来源:Allegro MicroSystems)

A3909 具有热关断保护、过流保护和短路保护功能。每个输入 (INx) 驱动相应的输出 (OUTx)。
MAX32650 PWM 可以将脉冲串输出 0 和 1 连接到输入 IN1 和 IN2(绿色),以通过 OUT1 和 OUT2 驱动绿色线圈,以及将脉冲串输出 2 和 3 连接到 IN3 和 IN4(红色),以通过 OUT3 和 OUT4 驱动红色线圈。这使 A3909 能够直接驱动步进电机。

A3909 还支持有用的高阻抗功能。如果 H 桥的两个输入均为逻辑 0 的时间超过一毫秒 (ms),则两个输出都将置于高阻抗状态。这适用于允许电机惯性滑行的情况,或任何要求输出为高阻抗的步进电机步阶。再次参考图 2,任何处于空闲状态的波形部分都将因置于高阻抗状态而获益。这可防止电流线圈在电机由另一个电流线圈步进时干扰电机的运行,因此会提高效率。

如果所有四个输入(两对)均保持低电平的时间超过 1 ms,那么很显然,两个输出对都将进入如上所述的高阻抗状态。规格书中称此为休眠模式,因为同时还有一些内部电路也会处于低功耗状态。

总结

常见的微控制器外设往往包括用于驱动外部负载(例如电机和功率 MOSFET)的 PWM 功能。但由于某些情况下在 PWM 启用寄存器上执行位操作可能会导致不可预测的结果,因此微控制器供应商正在使用新的 PWM 外设来解决此问题,这些外设提供的功能可以安全地启用和禁用单个 PWM 输出,而不会干扰其他 PWM 输出,从而防止偶尔出现相位延迟和失去同步。

来源:Digi-Key
作者:Bill Giovino

围观 26

一、配置定时器为PWM产生


二、配置时钟树


三、定时器配置


四、配置完生成程序后,主程序里还要进行启动PWM就可以了

  MX_TIM3_Init();
  HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_2);//启动。置1  CCER的输出使能位bit4
  HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_1);//CCER的bit0

然后可以改变占空比了。
**特别注意:**占空比不能大于自动重载寄存器ARR的值,不然输出的都是高电平。这个问题整了一天。

五、程序分析


/* TIM3 init function */
static void MX_TIM3_Init(void)
{

  TIM_MasterConfigTypeDef sMasterConfig;
  TIM_OC_InitTypeDef sConfigOC;
//------------------实际是调用TIM_Base_SetConfig配置定时器,上一篇分析过--
  htim3.Instance = TIM3;
  htim3.Init.Prescaler = 71;
  htim3.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim3.Init.Period = 99;
  htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  htim3.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
  if (HAL_TIM_PWM_Init(&htim3) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }
//---------------------这个是默认的,不管-------------------------------
  sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  if (HAL_TIMEx_MasterConfigSynchronization(&htim3, &sMasterConfig) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }
//----------------配置输出比较部分-- 上图5部分-------------------------------
  sConfigOC.OCMode = TIM_OCMODE_PWM1;//输出模式PWM1
  sConfigOC.Pulse = 80;//占空比
  sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;//高电平极性
  sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
  if (HAL_TIM_PWM_ConfigChannel(&htim3, &sConfigOC, TIM_CHANNEL_1) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }
//----------------------------------------------------------------------------------
  sConfigOC.Pulse = 50;
  if (HAL_TIM_PWM_ConfigChannel(&htim3, &sConfigOC, TIM_CHANNEL_2) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }
  HAL_TIM_MspPostInit(&htim3);//初始化引脚IO
}

初始化PWM通道HAL_TIM_PWM_ConfigChannel------>调用

case TIM_CHANNEL_1:
{
  assert_param(IS_TIM_CC1_INSTANCE(htim->Instance));
  /* Configure the Channel 1 in PWM mode */
  TIM_OC1_SetConfig(htim->Instance, sConfig);

  /* Set the Preload enable bit for channel1 */
  htim->Instance->CCMR1 |= TIM_CCMR1_OC1PE;//比较模式寄存器CCMR1的bit3位OC1PE置1,写入到CCR1的值在事件更新时,才会传到影子寄存器。清0则写入CCR1的值会马上起作用
  
  /* Configure the Output Fast mode */
  htim->Instance->CCMR1 &= ~TIM_CCMR1_OC1FE;//配置CCMR1的bit2位,输出比较快速使能
  htim->Instance->CCMR1 |= sConfig->OCFastMode;
}
break;
//--------------------------------------------------------------------------------

 void TIM_OC1_SetConfig(TIM_TypeDef *TIMx, TIM_OC_InitTypeDef *OC_Config)
   {
  uint32_t tmpccmrx = 0U;
  uint32_t tmpccer = 0U;
  uint32_t tmpcr2 = 0U;

   /* Disable the Channel 1: Reset the CC1E Bit */
  TIMx->CCER &= ~TIM_CCER_CC1E;//关通道 CCER  bit0清0

  /* 读出三个寄存器的值 */
  tmpccer = TIMx->CCER;
  tmpcr2 =  TIMx->CR2;
  tmpccmrx = TIMx->CCMR1;

  /* Reset the Output Compare Mode Bits */
  tmpccmrx &= ~TIM_CCMR1_OC1M;//复位CCMR1的bit6:4
  tmpccmrx &= ~TIM_CCMR1_CC1S;//复制CCMR1的bit1:0
  /* Select the Output Compare Mode */
  tmpccmrx |= OC_Config->OCMode;//配置OC1M为 110  PWM1模式

  /* Reset the Output Polarity level */
  tmpccer &= ~TIM_CCER_CC1P;//配置输出极性 CCER的 CC1P位
  tmpccer |= OC_Config->OCPolarity;

  if(IS_TIM_CCXN_INSTANCE(TIMx, TIM_CHANNEL_1))//如果是定时器1的123通道,还要设置互补输出
  {
    assert_param(IS_TIM_OCN_POLARITY(OC_Config->OCNPolarity));
    /* Reset the Output N Polarity level */
    tmpccer &= ~TIM_CCER_CC1NP;
    /* Set the Output N Polarity */
    tmpccer |= OC_Config->OCNPolarity;
    /* Reset the Output N State */
    tmpccer &= ~TIM_CCER_CC1NE;
  }

  if(IS_TIM_BREAK_INSTANCE(TIMx))//如果是定时器1,还要设置CR2的bit8  bit9 死区控制
  {
    /* Check parameters */
    assert_param(IS_TIM_OCNIDLE_STATE(OC_Config->OCNIdleState));
    assert_param(IS_TIM_OCIDLE_STATE(OC_Config->OCIdleState));

    /* Reset the Output Compare and Output Compare N IDLE State */
    tmpcr2 &= ~TIM_CR2_OIS1;
    tmpcr2 &= ~TIM_CR2_OIS1N;
    /* Set the Output Idle state */
    tmpcr2 |= OC_Config->OCIdleState;
    /* Set the Output N Idle state */
    tmpcr2 |= OC_Config->OCNIdleState;
  }
  /* 把设置好的值写入这4个寄存器 */
  TIMx->CR2 = tmpcr2;
  TIMx->CCMR1 = tmpccmrx;
  TIMx->CCR1 = OC_Config->Pulse;//占空比
  TIMx->CCER = tmpccer;
}

流程图如下:


需要改变占空比的,直接调用
__HAL_TIM_SET_COMPARE(&htim3,TIM_CHANNEL_1,dutycycle);
改变寄存器CCR1,改变的值是立即生效还是下一个周期生效取决于CCMR1的OC1PE位

 while (1)
  {
		while(dutycycle<100)
		{
			dutycycle+=10;
			__HAL_TIM_SET_COMPARE(&htim3,TIM_CHANNEL_1,dutycycle);
			HAL_Delay(1);
		}
		while(dutycycle)
		{
			dutycycle-=10;
			__HAL_TIM_SET_COMPARE(&htim3,TIM_CHANNEL_1,dutycycle);
			HAL_Delay(1);
		}
}

版权声明:本文为CSDN博主 - D.luffy 的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:
https://blog.csdn.net/liangbin414/article/details/88707340

围观 362

1、概念

脉宽调制(PWM)是利用微处理器的数字输出来对模拟电路进行控制的一种非常有效的技术,广泛应用在从测量、通信到功率控制与变换的许多领域中。

2、应用实例

PWM是一种对模拟信号电平进行数字编码的方法。通过高分辨率计数器的使用,方波的占空比被调制用来对一个具体模拟信号的电平进行编码。PWM信号仍然是数字的,因为在给定的任何时刻,满幅值的直流供电要么完全有(ON),要么完全无(OFF)。电压或电流源是以一种通(ON)或断(OFF)的重复脉冲序列被加到模拟负载上去的。通的时候即是直流供电被加到负载上的时候,断的时候即是供电被断开的时候。只要带宽足够,任何模拟值都可以使用PWM进行编码。

图1

图1显示了三种不同的PWM信号。图1a是一个占空比为10%的PWM输出,即在信号周期中,10%的时间通,其余90%的时间断。图1b和图1c显示的分别是占空比为50%和90%的PWM输出。这三种PWM输出编码的分别是强度为满度值的10%、50%和90%的三种不同模拟信号值。例如,假设供电电源为9V,占空比为10%,则对应的是一个幅度为0.9V的模拟信号。

图2

图2是一个可以使用PWM进行驱动的简单电路。图中使用9V电池来给一个白炽灯泡供电。如果将连接电池和灯泡的开关闭合50ms,灯泡在这段时间中将得到9V供电。如果在下一个50ms中将开关断开,灯泡得到的供电将为0V。如果在1秒钟内将此过程重复10次,灯泡将会点亮并象连接到了一个4.5V电池(9V的50%)上一样。这种情况下,占空比为50%,调制频率为10Hz。

3、特点

PWM的一个优点是从处理器到被控系统信号都是数字形式的,无需进行数模转换,让信号保持为数字形式可将噪声影响降到最小。噪声只有在强到足以将逻辑1改变为逻辑0或将逻辑0改变为逻辑1时,也才能对数字信号产生影响。

对噪声抵抗能力的增强是PWM相对于模拟控制的另外一个优点,而且这也是在某些时候将PWM用于通信的主要原因。从模拟信号转向PWM可以极大地延长通信距离。在接收端,通过适当的RC或LC网络可以滤除调制高频方波并将信号还原为模拟形式。

总之,PWM既经济、节约空间、抗噪性能强,是一种值得广大工程师在许多设计应用中使用的有效技术。

版权声明:本文为CSDN博主「凡旭国」的原创文章,
遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:
https://blog.csdn.net/sinat_20265495/article/details/72416780

围观 55

页面

订阅 RSS - PWM