PWM

互补输出简介

互补输出只能在高级控制定时器(TIM1和TIM8)上使用,可以输出两路互补信号,包括主输出OCx和互补输出OCxN。基于比较输出一节的内容,OCx和OCxN都可以输出一定频率和占空比的PWM波形,且他们的极性是相反的,如图1所示,OCxREF是参考信号,OCx输出信号与参考信号相同,只是它的上升沿相对于参考信号的上升沿有一个延迟,OCxN输出信号与参考信号相反,只是它的上升沿相对于参考信号的下降沿有一个延迟,这个延迟时间,我们是可以通过死区寄存器配置的,本节中我们可以设置为0。

1.png

图1 带死区插入的互补输出

2.png

图2捕获比较通道的输出阶段

如图3所示,因为互补输出多用于PWM控制电机的项目中,所以紧急情况下的刹车控制是必不可少的,OSSR的含义是运行模式下的关闭状态选择,OSSI的含义是空闲模式下的关闭状态选择,MOE的含义是主输出使能,一般的应用模式为,当短路输入为有效状态,MOE又硬件异步清零,OSSI设置为0,禁止OC\OCN输出,OSI1\OSI1N设置为0,OC1\OC1N输出为0,达到紧急刹车的目的。

3.png

图3 控制位和输出状态

配置步骤

互补输出实际跟比较输出章节一样使用的是定时器的功能,所以相关的函数设置同样在库函数文件CKS32f4xx_tim.h和CKS32f4xx_tim.c文件中。

1)开启TIM1和GPIO时钟,配置PA7、PA8选择复用功能GPIO_AF_TIM1输出。

要使用TIM1,我们必须先开启TIM1的时钟,这点相信大家看了这么多代码,应该明白了。这里我们还要配置PA7、PA8为复用(GPIO_AF_TIM1)输出,才可以实现TIM1_CH1的互补PWM经过PA7、PA8输出。库函数使能TIM1时钟的方法是:

RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1,ENABLE); //>>TIM1 时钟使能

这在前面章节已经提到过。当然,这里我们还要使能GPIOA的时钟。然后我们要配置PA7引脚映射至GPIO_AF_TIM1,复用为定时器1,调用的函数为:

GPIO_PinAFConfig(GPIOA,GPIO_PinSource7,GPIO_AF_TIM1); //>>GPIOA7 复用为定时器1

配置PA8引脚映射至GPIO_AF_TIM1,复用为定时器1,调用的函数为:

GPIO_PinAFConfig(GPIOA,GPIO_PinSource8,GPIO_AF_TIM1); //>>GPIOA8 复用为定时器1

这个方法跟我们串口实验讲解一样,调用的同一个函数,最后设置PA7为复用功能输出这里我们只列出GPIO初始化为复用功能的一行代码:

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; //>>复用功能

这里还需要说明一下,对于定时器通道的引脚关系,大家可以查看CKS32F4对应的数据手册,比如我们PWM实验,我们使用的是定时器1的通道1,对应的引脚PA7可以从数据手册表中查看:

3.png

2)初始化TIM1,设置TIM1的ARR和PSC等参数。

在开启了TIM1的时钟之后,我们要设置ARR和PSC两个寄存器的值来控制输出PWM的周期。这在库函数是通过TIM_TimeBaseInit函数实现的,在上一节定时器中断章节我们已经有讲解,这里就不详细讲解,调用的格式为:

TIM_TimeBaseStructure.TIM_Period = arr; //设置自动重装载值

TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置预分频值
TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //向上计数模式
TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure); //根据指定的参数初始化 TIMx

3)设置TIM1_CH1的PWM模式,使能TIM1的CH1输出。

设置TIM1_CH1为PWM模式(默认是冻结的)通过配置TIM1_CCMR1的相关位来控制TIM1_CH1的模式。在库函数中,PWM通道设置是通过函数TIM_OC1Init()~TIM_OC4Init()来设置的,不同的通道的设置函数不一样,这里我们使用的是通道1,所以使用的函数是TIM_OC1Init()。

Void TIM_OC1Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);

这种初始化格式大家学到这里应该也熟悉了,所以我们直接来看看结构体TIM_OCInitTypeDef的定义:

typedef struct
{
    uint16_t TIM_OCMode;
    uint16_t TIM_OutputState;
    uint16_t TIM_OutputNState; */
    uint16_t TIM_Pulse;
    uint16_t TIM_OCPolarity;
    uint16_t TIM_OCNPolarity;
    uint16_t TIM_OCIdleState;
    uint16_t TIM_OCNIdleState;
} 
TIM_OCInitTypeDef;

这里我们讲解一下与我们要求相关的几个成员变量:

参数TIM_OCMode设置模式是PWM还是输出比较,这里我们是PWM模式。

参数TIM_OutputState\OutputNState用来设置比较输出使能,也就是使能PWM输出到端口。参数TIM_OCPolarity\OCNPolarity用来设置极性是高还是低。参数TIM_OCIdleState和TIM_OCNIdleState用来设置空闲时的输出状态。

要实现我们上面提到的场景,方法是:

TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //>>选择模式 PWM
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //>>比较输出使能
TIM_OCInitStructure.TIM_OutputNState = TIM_OutputState_Enable; //>>比较输出使能
TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCPolarity_High; //>>输出极性高
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; //>>输出极性低
TIM_OCInitStructure. TIM_OCIdleState = TIM_OCNIdleState_Reset; //>>当MOE=0重置输出空闲状态
TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCNIdleState_Reset; //>>当MOE=0重置输出空闲状态
TIM_OCInitStruct.TIM_Pulse=100;//待装入捕获比较寄存器的脉冲值
TIM_OC1Init(TIM1, &TIM_OCInitStructure); //>>根据指定的参数初始化外设
TIM1 OC1 TIM_OC1PreloadConfig(TIM1,TIM_OCPreload_Enable);  //使能TIM1在CCR上的预装载寄存器

4)使能 TIM1。

在完成以上设置了之后,我们不开启刹车功能,并使能TIM1,使能TIM1的方法前面已经讲解过:

TIM_BDTRStructure.TIM_AutomaticOutput=TIM_AutomaticOutput_Enable;//自动输出功能使能
TIM_BDTRStructure.TIM_Break=TIM_Break_Disable;//失能刹车输入
TIM_BDTRStructure.TIM_BreakPolarity=TIM_BreakPolarity_High; //刹车输入管脚极性高
TIM_BDTRStructure.TIM_DeadTime=0; //输出打开和关闭状态之间的延时
TIM_BDTRStructure.TIM_LOCKLevel=TIM_LOCKLevel_OFF;// 锁电平参数: 不锁任何位
TIM_BDTRStructure.TIM_OSSIState=TIM_OSSIState_Disable; //设置在运行模式下非工作状态选项
TIM_BDTRStructure.TIM_OSSRState=TIM_OSSRState_Disable; //设置在运行模式下非工作状态选项
TIM_BDTRConfig(TIM1,&TIM_BDTRStructure);
TIM_ARRPreloadConfig(TIM1,ENABLE); //使能TIM1在ARR上的预装载寄存器
TIM_CtrlPWMOutputs(TIM1,ENABLE);   //PWM使能主输出MOE=1
TIM_Cmd(TIM1,ENABLE);   //打开TIM1

5)修改TIM1_CCR1来控制占空比。

最后,在经过以上设置之后,PWM其实已经开始输出了,只是其占空比和频率都是固定的,而我们通过修改TIM1_CCR1则可以控制CH1的输出占空比。在库函数中,修改TIM1_CCR1占空比的函数是:

void TIM_SetCompare1(TIM_TypeDef* TIMx, uint16_t Compare2);

理所当然,对于其他通道,分别有一个函数名字,函数格式为:

TIM_SetComparex(x=1,2,3,4)

通过以上5个步骤,我们就可以控制TIM1的CH1输出互补PWM波了。

3代码示例

添加PWM配置文件pwm.c和pwm.h。

pwm.c源文件代码如下:

//>>TIM1 PWM 部分初始化
//>>PWM 输出初始化
//>>arr:自动重装值 psc:时钟预分频数
void TIM1_PWM_Init(u32 arr,u32 psc)
{
    PIO_InitTypeDef GPIO_InitStructure;
    TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
    TIM_OCInitTypeDef TIM_OCInitStructure;
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1,ENABLE);//TIM1 时钟使能
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); //使能 PORTA 时钟
    GPIO_PinAFConfig(GPIOA,GPIO_PinSource7,GPIO_AF_TIM1); //PA7 复用为 TIM1
    GPIO_PinAFConfig(GPIOA,GPIO_PinSource8,GPIO_AF_TIM1); //PA8 复用为 TIM1
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7 | GPIO_Pin_8; //GPIOF9
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; //复用功能
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; //速度 100MHz
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽复用输出
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉
    GPIO_Init(GPIOA,&GPIO_InitStructure); //初始化 PA7\PA8
    TIM_TimeBaseStructure.TIM_Prescaler=psc; //定时器分频
    TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up; //向上计数模式
    TIM_TimeBaseStructure.TIM_Period=arr; //自动重装载值
    TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;
    TIM_TimeBaseInit(TIM1,&TIM_TimeBaseStructure);//初始化定时器 1
    //初始化  TIM1 Channel1 互补PWM 模式
    TIM_OCInitTypeDef TIM_OCInitStructure;
    TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //>>选择模式 PWM
    TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //>>比较输出使能
    TIM_OCInitStructure.TIM_OutputNState = TIM_OutputState_Enable; //>>比较输出使能
    TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCPolarity_High; //>>输出极性低
    TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; //>>输出极性低
    TIM_OCInitStructure. TIM_OCIdleState = TIM_OCNIdleState_Reset; //>>当MOE=0重置输出空闲状态
    TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCNIdleState_Reset; //>>当MOE=0重置输出空闲状态
    TIM_OC1Init(TIM1, &TIM_OCInitStructure); //>>根据指定的参数初始化外设TIM1 OC1 
    TIM_OC1PreloadConfig(TIM1,TIM_OCPreload_Enable);  //使能TIM1在CCR上的预装载寄存器
    TIM_BDTRStructure.TIM_AutomaticOutput=TIM_AutomaticOutput_Enable;//自动输出功能使能  
    TIM_BDTRStructure.TIM_Break=TIM_Break_Disable;//失能刹车输入
    TIM_BDTRStructure.TIM_BreakPolarity=TIM_BreakPolarity_High; //刹车输入管脚极性高
    TIM_BDTRStructure.TIM_DeadTime=0; //输出打开和关闭状态之间的延时
    TIM_BDTRStructure.TIM_LOCKLevel=TIM_LOCKLevel_OFF;// 锁电平参数: 不锁任何位
    TIM_BDTRStructure.TIM_OSSIState=TIM_OSSIState_Disable; //设置在运行模式下非工作状态选项
    TIM_BDTRStructure.TIM_OSSRState=TIM_OSSRState_Disable; //设置在运行模式下非工作状态选项
    TIM_BDTRConfig(TIM1,&TIM_BDTRStructure);
    TIM_ARRPreloadConfig(TIM1,ENABLE); //使能TIM1在ARR上的预装载寄存器
    TIM_CtrlPWMOutputs(TIM1,ENABLE);   //PWM使能主输出MOE=1
    TIM_Cmd(TIM1,ENABLE);              //打开TIM1
    TIM_SetCompare1(TIM1,300); //>>设置pwm的占空比为300/500 = 60%
}

此部分代码包含了上面介绍的PWM输出设置的前5个步骤。这里我们关于TIM1的设置就不再说了。接下来,我们看看主程序里面的main函数如下:

int main(void)
{ 
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//>>设置系统中断优先级分组 2
    delay_init(168); //>>初始化延时函数
    TIM1_PWM_Init(500-1,84-1); //>>定时器时钟为 84M,分频系数为 84,所以计数频率
    //>> 84M/84=1Mhz,重装载值500,所以PWM频率为1M/500=2Khz.
    while(1) {}
}

这里,我们先设置好了NVIC终端优先级,然后初始化延时函数和timer,在timer的初始化参数中我们把PWM的频率设置成2K,将占空比设置成60%,完成PWM输出设置。

来源:中科芯MCU

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

围观 8

01、前言

有客户反馈,使用STM32F4的TIM2结合DMA,产生的PWM波形不符合预期,但是相同的配置使用在TIM3上,得到的PWM波形就是符合预期的。其代码和配置都是从F1移植过来的,在F1上使用TIM2是没有问题的,对于F4的TIM2发生的问题,客户一直没有找到根本原因。

02、实验

根据客户的反馈,我们进行了实验。

硬件:STM32F401RE-NUCLEO

在STM32CubeMX中,将TIM2和TIM3所有参数均做相同的配置,其中配置DMA两端均为halfword长度。

1.png

2.png

生成代码,并定义两个数组如下图所示:

3.png

在主函数中开启Timer。

4.png

我们可以发现,实验结果如客户反馈的,TIM2输出的PWM是不正确的,TIM3输出的PWM是正确的。

5.png

03、分析

我们的实验中,TIM2和TIM3的配置是完全一样的,即使传输相同的数据,得到的PWM波形也是不同的。为此我们比较了TIM2和TIM3的硬件属性,可以很容易查看出,TIM2的计数器是32bit的,而TIM3的计数器是16bit的。

我想我们已经知道答案了,TIM2的计数器是32bit的,但是我们配置的DMA是halfword长度,这在AHB总线上解析数据时产生了非预期的结果。在调试界面我们也能看到,当问题发生时,TIM2的CCR1竟然比ARR的值要大,或者出现异常值,所以出现异常波形。

6.png

根本原因在于,对于大部分STM32系列,主设备基于AHB外设进行寻址是不支持byte/half-word传输的,总线会强制将数据转化为32bit传送到总线上,这就是为什么我们看到CCR1的高半字和低半字的值是相同的原因。

当我们将TIM2的DMA外设端修改为word长度,并将内存数组定义为32bit,再次实验,可以发现PWM的波形就是正常的了:

7.png

8.png

9.png

04、小结

因为F103上没有32bit计数器的Timer,所以客户在F103上并没有出现类似的问题。在使用DMA访问经过AHB转APB的桥接外设时,我们要注意DMA对外设的访问宽度配置问题。

来源:STM32单片机

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

围观 75

概 述

在进行电机类、电源类应用开发时,如何使用PWM定时器模块灵活、高效的实现所需 PWM波形的输出,是众多开发者关注的问题。在上篇文章里,我们介绍了PWM定时器模块内的一些概念,以及如何生成普通PWM的过程。本篇内容将继续介绍互补PWM、同步 PWM、错相 PWM以及PWM如何使用ACMP封波等内容。

互补PWM

关于相同的代码部分,此处不再重复讲解,请参考《上篇》的普通PWM的讲解。

1.jpg

2.jpg

此处设置了 3 个比较器:cmp_config[0]与 cmp_config[1]用来生成中心对称 PWM,cmp_config[2]作为 PWM 影子寄存器的更新事件源,当 CNT 等于 cmp_config[2]时,影子寄存器写入寄存器内生效。

3.jpg

互补 PWM 对的配置,配置左死区宽度为 8000 个 half_clock,右死区宽度为 16000 个 half_clock。

4.jpg

设置 cmp_config[2]作为 PWM 影子寄存器的更新事件源,启动计数器。

5.jpg

通过修改 CMP0 与 CMP1 的值,每 100ms 更新一次互补 PWM 占空比。运行结果如下:

6.jpg

同步PWM

本节实验设计:使用 PWM0 的比较器比较事件去做 PWM1、PWM2、PWM3 的同步事件。故输出波形上看,PWM1、PWM2、PWM3 应完全同步,PWM0 与其则不同步。代码如下:

7.jpg

使能 4 个 PWM 定时器的 SYNCI 信号。

8.jpg

设置 STA 与 RLD。

9.jpg

cmp_config[0]与 cmp_config[1]用来做中心对称 PWM 所需的比较器。

10.jpg

cmp_config[2]用来做使 PWM 影子寄存器生效的比较器。

11.jpg

cmp_config[3]设置在 PWM0 CH8 上,用来产生比较事件,同步 PWM1、PWM2、PWM3。

12.jpg

互补 PWM 的死区设置与输出设置。

13.jpg

设置 PWM0 CH0 CH1 互补输出,同时设置 PWM1 CH8 使用 CMP3 产生比较事件。

14.jpg

设置 PWM1、PWM2、PWM3 互补输出。

15.jpg

启动计数器。

以下代码是对互联管理器的配置。配置 PWM0 CH8 的下降沿输出到TRGM0_OUTX0 上,同时 TRGM0_OUTX0 作为 TRGM1、TRGM2、TRGM3的输入,路由到 PWM1、PWM2、PWM3 的 SYNCI 信号上。

16.jpg

波形如下:

17.jpg

可见 PWM1、PWM2、PWM3 是完全同步的,PWM0 则与其有 20ns 的不同步。

错相 PWM

本节实验设计:使用 SYNT 实现 PWM0、PWM1、PWM2、PWM3 错相90°。代码如下:

18.jpg

19.jpg

20.jpg

21.jpg

22.jpg

以上代码请参考上文 “同步PWM” 章节的讲解,主要目的是生成 4 对互补PWM。

23.jpg

配置 TRGM,将 SYNT CH0 的比较事件路由到 PWM0 的 SYNCI 信号上,将 SYNT CH1 的比较事件路由到 PWM1 的 SYNCI 信号上,将 SYNT CH2 的21 / 24先楫半导体比较事件路由到 PWM2 的 SYNCI 信号上,将 SYNT CH3 的比较事件路由到PWM3 的 SYNCI 信号上。

24.jpg

配置 SYNT,分别设置 SYNT CH0 比较器比较值为 0,SYNT CH1 比较器比较值为 reload/4,SYNT CH2 比较器比较值为 reload/2,SYNT CH4 比较器比较值为 reload*3/4。

波形如下:

25.jpg

PWM+ACMP 封波

本节实验设计:使用两个片上 ACMP 对目标模拟电压进行监控,当电压超过 1.65V 时停止 PWM 输出。其基本思路为,将 ACMP 的输出信号通过互联管理器 TRGM 路由到 PWM 的内部 Fault 信号上,当 Fault 信号有效时 PWM波停止输出(故障保护功能)。

代码如下:

26.jpg

初始化 PWM 引脚,初始化 DAC 时钟与引脚(使用 DAC 输出模拟电压到ACMP 上模拟过压)。

27.jpg

输出互补 PWM 波。

28.jpg

设置 Fault 信号高电平有效;使能 FaultI0 与 FaultI1 有效(PWM 共 4 内2 外 fault 信号,哪些信号生效可选)。

29.jpg

设置 DAC 输出为直接模式,12bit DAC 输出范围 0~4095,4030 约为3.247V。该部分代码请直接参考 DAC 例程。

30.jpg

配置 ACMP,使能 ACMP2 与 ACMP3。

31.jpg

配置互联管理器,将 ACMP2 与 ACMP3 的输出信号路由到 PWM 的FaultI0 与 FaultI1 上去。

32.jpg

acmp_config 函数源码如下:

33.jpg

代码中将 ACMP 的正极输入选择为 IO 引脚,负极输入选择为 ACMP 内部专用 DAC,参考电压设置为 0x80,即 1.65V。

当 IO 引脚电压 3.247V 时(来自于外设 DAC 输出),ACMP 正极电压超过负极电压 1.65V,ACMP 输出有效,为高电平;经过互联管理器路由到PWM 的 Fault 信号上;由于 PWM 模块内配置了 Fault 信号为高电平有效,因此此时 Fault 信号有效,PWM 波形停止输出。

当 IO 引脚电压 0V 时(将引脚与 GND 短接),ACMP 正极电压未超过负极电压 1.65V,ACMP 输出无效,PWM 波形正常输出。

小 结

本文首先介绍了 PWM 定时器内各模块的基本概念与功能,而后对 PWM的使用由浅入深依次以代码实例进行讲解。可以看到,PWM 的使用只要配置好 STA、RLD、CMPx、影子寄存器等即可实现输出;如果有同步需求或与其它外设协同使用需求,则主要通过互联管理器 TRGM 的配置进行实现。

比较经典的例程还包括使用 PWM 触发 ADC 采样,先楫半导体 SDK 中已经有完整的实例与代码,各位开发者可以到官网下载研究,并欢迎大家多多交流。

来源:先楫半导体HPMicro

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

围观 30

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

一.   TIMER分类:

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

1.png

其中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的引脚复用功能映像:

2.jpg

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

3.png

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

4.png

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

 5.png

根据以上重映像表,我们使用定时器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)。

围观 183

概述

本次测试用的是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)。

围观 128

引言

在高精度定时器中,可以使用外部事件来对 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)。

围观 112

做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)。

围观 280

用定时器生成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)。

围观 156

本文分享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)。

围观 30

大多数微控制器至少有一个脉冲宽度调制 (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

围观 78

页面

订阅 RSS - PWM