目录
了解PWM
什么是PWM
脉冲宽度调制(PWM),是英文“Pulse Width Modulation”的缩写,简称脉宽调制,是利用微处理器的数字输出来对模拟电路进行控制的一种非常有效的技术,广泛应用在从测量、通信到功率控制与变换的许多领域中。
pwm的频率
是指1秒钟内信号从高电平到低电平再回到高电平的次数(一个周期);
也就是说一秒钟PWM有多少个周期 单位: Hz 表示方式: 50Hz 100Hz
pwm的周期:
T=1/f 周期=1/频率 50Hz = 20ms 一个周期
如果频率为50Hz ,也就是说一个周期是20ms 那么一秒钟就有 50次PWM周期
占空比:
是一个脉冲周期内,高电平的时间与整个周期时间的比例 单位: % (0%-100%) 表示方式:80%
周期
一个脉冲信号的时间
1s内测周期次数等于频率
脉宽时间: 高电平时间
上图中 脉宽时间占总周期时间的比例,就是占空比 比方说周期的时间是10ms,脉宽时间是8ms 那么低电平时间就是2ms 总的占空比 8/(8+2)= 80%
这就是占空比为80%的脉冲信号
而我们知道PWM就是脉冲宽度调制 通过调节占空比,就可以调节脉冲宽度(脉宽时间) 而频率就是单位时间内脉冲信号的次数,频率越大
以20Hz 占空比为80% 举例 就是1秒钟之内输出了20次脉冲信号 每次的高电平时间为40ms
PWM原理
以单片机为例,我们知道,单片机的IO口输出的是数字信号,IO口只能输出高电平和低电平
假设高电平为5V 低电平则为0V 那么我们要输出不同的模拟电压,就要用到PWM,通过改变IO口输出的方波的占空比从而获得使用数字信号模拟成的模拟电压信号。
上图中,周期为T T1为高电平时间 T2 为低电平时间
假设周期T为 1s 那么频率就是 1Hz 那么高电平时间0.5s ,低电平时间0.5s 总的占空比就是 0.5 /1
占空比=50%
在一定的频率下,通过不同的占空比 即可得到不同的输出模拟电压
pwm就是通过这种原理实现D/A转换的。
总结: PWM就是在合适的信号频率下,通过一个周期里改变占空比的方式来改变输出的有效电压
PWM频率越大,响应越快
计数模式
PWM
具有如下计数模式:
-
向上计数模式
:计数器从0
递增计数到自动重载值ARR
,计数器又回到0
,重新开始计数。 -
向下计数模式
:计数器从自动重载值ARR
递减计数到0
,计数器又回到重载值,重新开始计数。
-
中心对齐模式
:计数器从0
递增计数到自动重载值ARR
,然后计数器从自动重载值ARR
递减计数到0
,重复之前的流程。
PWM向上计数
以STM32
的PWM mode 1
为例,有效电平为高时:
-
当
TIMx CNT
小于TIMx CCR1
时,输出高电平。 -
当
TIMx CNT
大于TIMx CCR1
时,输出低电平。
PWM
向下计数使用类似的方法。
PWM中心对齐
以STM32
的PWM mode 1
为例,有效电平为高时:
-
当
TIMx CNT
小于TIMx CCR1
时,输出高电平。 -
当
TIMx CNT
大于TIMx CCR1
时,输出低电平。 -
计数器计数到自动重载值
ARR
,然后向下计数。 -
计数器计数到
TIMx CCR1
,输出高电平。 -
计数器计数到
0
,然后开始新的周期。
模式选择
- 一般场合使用
向上计数模式
或向下计数模式
。 - 特殊场合使用
中心对齐模式
,例如电机控制。
————————————————
版权声明:本文为CSDN博主「「已注销」」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/fukangwei_lite/article/details/122015800
定时器的溢出时间计算
time
=(ARR+1)*(PSC+1)/Tclk
-
ARR为自动装载值
-
PSC:预分频系数
-
Tclk:定时器的APB时钟,通常等于系统时钟
如:
tclk为72M
psc为7199
arr为4999
time=(4999+1)*(7199+1)/72 000 000 = 0.5s = 500ms
PWM配置
ARR为自动装载值
CCRx 为捕获比较寄存器值
-
预分频系数决定了PWM的时钟速度
-
ARR的大小决定了PWM的周期
-
CRRx决定了输出有效信号的时间
有效信号:
-
高电平
-
低电平
PWM模式:
-
模式1,不管是向上还是向下计数,当计数值小于重装载值是输出有效电平。
-
模式2,不管是向上还是向下计数,当计数值小于重装载值是输出无效电平。
PWM周期计算
Fpwm = 主频 / (arr+1)/(psc+1)(单位:Hz)
arr 是计数值
psc 是预分频值
如:
主频=72M
arr=4999
psc=7199
72,000,000/5000/7200=2Hz
————————————————
版权声明:本文为CSDN博主「东小东博客」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/dongxiaodongvip/article/details/113449370
了解完PWM的基本知识,开始实战
关于HC32F460Timer_A的PWM
捕获模式
一、捕获模式的介绍 捕获模式是在应用中很常用的一种模式,可以进行脉冲计数,侧量脉宽,可以用作小球计数、频率计、超声波测距等应用中。
捕获模式可以捕捉上升沿,也可以捕捉下降沿。需要对寄存器进行具体设置。
捕获模式的实质就是在捕获上升沿或者下降沿的同时进入捕获中断,执行中断服务函数,同时把TAR计数的值赋给 TACCR0或TACCR1、TACCR2(要看具体用的是哪个引脚的捕获),从而捕获到当前TAR(计数器)的值。
捕获模式类似于51单片机的计数器,但是又与计数器工作原理有很大差别,功能也比计数器强大。它既可以脉冲计数也可以计算一个脉冲宽度(也就是高电平的时间或者低电平的时间),也可以计算脉冲周期(一个脉冲整个周期的时间)。
捕获模式的代码(在官方代码/加点修改/加注释)
#include "hc32_ddl.h"
/* LED0 Port/Pin definition */
#define LED0_PORT (PortE)
#define LED0_PIN (Pin06)
#define LED0_ON() (PORT_SetBits(LED0_PORT, LED0_PIN))
#define LED0_OFF() (PORT_ResetBits(LED0_PORT, LED0_PIN))
#define LED0_TOGGLE() (PORT_Toggle(LED0_PORT, LED0_PIN))
/* KEY0 Port/Pin definition */
#define KEY0_PORT (PortD)
#define KEY0_PIN (Pin03)
/* KEY1 Port/Pin definition */
#define KEY1_PORT (PortD)
#define KEY1_PIN (Pin04)
#define KEY1_TRIGGER_EVENT (EVT_PORT_EIRQ4)
/* TIMERA unit and clock definition */
#define TIMERA_UNIT1 (M4_TMRA1)
#define TIMERA_UNIT1_OVERFLOW_INT (INT_TMRA1_OVF)
/* TIMERA channel 1 Port/Pin definition */
#define TIMERA_UNIT1_CH1 (TimeraCh1)
#define TIMERA_UNIT1_CH1_PORT (PortE)
#define TIMERA_UNIT1_CH1_PIN (Pin09)
#define TIMERA_UNIT1_CH1_FUNC (Func_Tima0)
/* TIMERA channel 3 Port/Pin definition */
#define TIMERA_UNIT1_CH3 (TimeraCh3)
#define TIMERA_UNIT1_CH3_PORT (PortE)
#define TIMERA_UNIT1_CH3_PIN (Pin13)
#define TIMERA_UNIT1_CH3_FUNC (Func_Tima0)
static void SystemClk_Init(void)
{
stc_clk_sysclk_cfg_t stcSysClkCfg;
stc_clk_xtal_cfg_t stcXtalCfg;
stc_clk_mpll_cfg_t stcMpllCfg;
MEM_ZERO_STRUCT(stcSysClkCfg);
MEM_ZERO_STRUCT(stcXtalCfg);
MEM_ZERO_STRUCT(stcMpllCfg);
/* Set bus clk div. */
stcSysClkCfg.enHclkDiv = ClkSysclkDiv1; // Max 168MHz
stcSysClkCfg.enExclkDiv = ClkSysclkDiv2; // Max 84MHz
stcSysClkCfg.enPclk0Div = ClkSysclkDiv1; // Max 168MHz
stcSysClkCfg.enPclk1Div = ClkSysclkDiv2; // Max 84MHz
stcSysClkCfg.enPclk2Div = ClkSysclkDiv4; // Max 60MHz
stcSysClkCfg.enPclk3Div = ClkSysclkDiv4; // Max 42MHz
stcSysClkCfg.enPclk4Div = ClkSysclkDiv2; // Max 84MHz
CLK_SysClkConfig(&stcSysClkCfg);
/* Switch system clock source to MPLL. */
/* Use Xtal32 as MPLL source. */
stcXtalCfg.enMode = ClkXtalModeOsc;
stcXtalCfg.enDrv = ClkXtalLowDrv;
stcXtalCfg.enFastStartup = Enable;
CLK_XtalConfig(&stcXtalCfg);
CLK_XtalCmd(Enable);
/* MPLL config. *//*84M*/
stcMpllCfg.pllmDiv = 1u;
stcMpllCfg.plln = 42u;
stcMpllCfg.PllpDiv = 2u;
stcMpllCfg.PllqDiv = 2u;
stcMpllCfg.PllrDiv = 2u;
CLK_SetPllSource(ClkPllSrcXTAL);
CLK_MpllConfig(&stcMpllCfg);
/* flash read wait cycle setting */
EFM_Unlock();
EFM_SetLatency(EFM_LATENCY_5);
EFM_Lock();
/* Enable MPLL. */
CLK_MpllCmd(Enable);
/* Wait MPLL ready. */
while (Set != CLK_GetFlagStatus(ClkFlagMPLLRdy));
/* Switch system clock source to MPLL. */
CLK_SetSysClkSource(CLKSysSrcMPLL);
}
static uint8_t TimerabackCnt = 0u;
static uint8_t EXINT_Flag = 0u;
static void Timera_Callback()
{
TimerabackCnt++;
if(TimerabackCnt >= 100u){
TimerabackCnt = 0u;
LED0_TOGGLE();
TIMERA_ClearFlag(TIMERA_UNIT1,TimeraFlagOverflow);
}
}
static void EXINT_Callback()
{
if(Set == EXINT_IrqFlgGet(ExtiCh03)){
EXINT_Flag = 1u;
EXINT_IrqFlgClr(ExtiCh03);
}
}
static void SW2_Init(void)
{
stc_port_init_t stcPortInit;
stc_exint_config_t stcExtiConfig;
stc_irq_regi_conf_t stcIrqRegiConf;
MEM_ZERO_STRUCT(stcPortInit);
MEM_ZERO_STRUCT(stcExtiConfig);
MEM_ZERO_STRUCT(stcIrqRegiConf);
stcPortInit.enExInt = Enable;
PORT_Init(KEY0_PORT,KEY0_PIN,&stcPortInit);
stcExtiConfig.enExitCh = ExtiCh03;
stcExtiConfig.enFilterEn = Enable;
stcExtiConfig.enFltClk = Pclk3Div8;
stcExtiConfig.enExtiLvl = ExIntFallingEdge;
EXINT_Init(&stcExtiConfig);
stcIrqRegiConf.enIntSrc = INT_SWI_IRQ2;
stcIrqRegiConf.enIRQn = Int005_IRQn;
stcIrqRegiConf.pfnCallback = &EXINT_Callback;
enIrqRegistration(&stcIrqRegiConf);
NVIC_ClearPendingIRQ(stcIrqRegiConf.enIRQn);
NVIC_SetPriority(stcIrqRegiConf.enIRQn, DDL_IRQ_PRIORITY_15);
NVIC_EnableIRQ(stcIrqRegiConf.enIRQn);
}
static void LED_Init(void)
{
stc_port_init_t stcPortInit;
MEM_ZERO_STRUCT(stcPortInit);
LED0_OFF();
stcPortInit.enPinMode = Pin_Mode_Out;
PORT_Init(LED0_PORT,LED0_PIN,&stcPortInit);
}
static void Timera_Init(void)
{
stc_timera_base_init_t stcTimeraInit;
stc_timera_compare_init_t stcTimerCompareInit;
stc_timera_hw_startup_config_t stcTimeraHwConfig;
stc_irq_regi_conf_t stcIrqRegiConf;
stc_port_init_t stcPortInit;
/*配置结构体初始化*/
MEM_ZERO_STRUCT(stcTimeraInit);
MEM_ZERO_STRUCT(stcTimerCompareInit);
MEM_ZERO_STRUCT(stcTimeraHwConfig);
MEM_ZERO_STRUCT(stcIrqRegiConf);
MEM_ZERO_STRUCT(stcPortInit);
/*配置外围时钟*/
PWC_Fcg2PeriphClockCmd(PWC_FCG2_PERIPH_TIMA1,Enable);
PWC_Fcg0PeriphClockCmd(PWC_FCG0_PERIPH_AOS,Enable);
/*配置TIMERA比较引脚*/
PORT_SetFunc(TIMERA_UNIT1_CH1_PORT,TIMERA_UNIT1_CH1_PIN,TIMERA_UNIT1_CH1_FUNC,Disable);
PORT_SetFunc(TIMERA_UNIT1_CH3_PORT,TIMERA_UNIT1_CH3_PIN,TIMERA_UNIT1_CH3_FUNC,Disable);
/*配置timera第一单元基础结构*/
stcTimeraInit.enClkDiv = TimeraPclkDiv128;
stcTimeraInit.enCntMode = TimeraCountModeSawtoothWave; /*锯齿形波形*/
stcTimeraInit.enCntDir = TimeraCountDirUp; /*向上计数*/
stcTimeraInit.enSyncStartupEn = Disable;
stcTimeraInit.u16PeriodVal = 0xCD0u; //100Hz 0xCD0=3280, time=(3280+1)*(127+1)/42M=0.01s=10ms
/*配置timera第一单元比较结构*/
stcTimerCompareInit.u16CompareVal = stcTimeraInit.u16PeriodVal *1u / 10u; /*比较值*//*占空比为90%*/
stcTimerCompareInit.enStartCountOutput = TimeraCountStartOutputLow; /*计数开始时输出为低电平*/
stcTimerCompareInit.enStopCountOutput = TimeraCountStopOutputLow; /*计数结束时输出为低电平*/
stcTimerCompareInit.enCompareMatchOutput = TimeraCompareMatchOutputHigh; /*比较值匹配时,PWMn端口输出高电平*/
stcTimerCompareInit.enPeriodMatchOutput = TimeraPeriodMatchOutputLow; /*周期值匹配时,PWMn端口输出低电平*/
stcTimerCompareInit.enSpecifyOutput = TimeraSpecifyOutputInvalid; //该寄存器可用于实现PWM输出占空比0%或100%,故输出无效
stcTimerCompareInit.enCacheEn = Enable; /*使能状态*/
stcTimerCompareInit.enTriangularTroughTransEn = Enable; /*三角波计数模式计数到谷点时,缓存值传送*/
stcTimerCompareInit.enTriangularCrestTransEn = Disable; /*禁用三角波峰时传输缓存值*/
stcTimerCompareInit.u16CompareCacheVal = stcTimerCompareInit.u16CompareVal; /*比较缓存值*/
/*配置通道1*/
TIMERA_CompareInit(TIMERA_UNIT1,TIMERA_UNIT1_CH1,&stcTimerCompareInit);
TIMERA_CompareCmd(TIMERA_UNIT1,TIMERA_UNIT1_CH1,Disable);
/*配置通道2*/
stcTimerCompareInit.enStartCountOutput = TimeraCountStartOutputHigh; /*计数开始时输出为高电平*/
stcTimerCompareInit.enStopCountOutput = TimeraCountStopOutputHigh; /*计数结束时输出为高电平*/
TIMERA_CompareInit(TIMERA_UNIT1,TIMERA_UNIT1_CH3,&stcTimerCompareInit);
TIMERA_CompareCmd(TIMERA_UNIT1,TIMERA_UNIT1_CH3,Disable);
TIMERA_Cmd(TIMERA_UNIT1, Disable);
/*配置timera第一单元启动*/
stcTimeraHwConfig.enTrigRisingStartupEn = Enable; /*TIMA_<t>_TRIG样本上升沿时硬件启动TIMA_<t>(同步启动有效)*/
stcTimeraHwConfig.enTrigFallingStartupEn = Disable; /*TIMA_<t>_TRIG样本下降沿时硬件启动TIMA_<t>(同步启动有效)*/
stcTimeraHwConfig.enSpecifyEventStartupEn = Disable; /*硬件启动TIMA_<t>当TIMA_HTSSR0寄存器指定事件触发器*/
TIMERA_HwStartupConfig(TIMERA_UNIT1,&stcTimeraHwConfig);
/*使能溢出中断*/
TIMERA_IrqCmd(TIMERA_UNIT1, TimeraIrqOverflow, Enable);
stcIrqRegiConf.enIntSrc = INT_TMRA1_OVF;
stcIrqRegiConf.enIRQn = Int004_IRQn;
stcIrqRegiConf.pfnCallback = &Timera_Callback;
enIrqRegistration(&stcIrqRegiConf);
NVIC_ClearPendingIRQ(stcIrqRegiConf.enIRQn);
NVIC_SetPriority(stcIrqRegiConf.enIRQn, DDL_IRQ_PRIORITY_15);
NVIC_EnableIRQ(stcIrqRegiConf.enIRQn);
/*外部触发计时器启动*/
stcPortInit.enExInt = Enable;
PORT_Init(KEY1_PORT,KEY1_PIN,&stcPortInit);
TIMERA_SetCountTriggerSrc(KEY1_TRIGGER_EVENT);
}
int32_t main()
{
uint16_t u16DataCompare = 0u;
uint16_t u16DataPeriod = 0u;
LED_Init();
SW2_Init();
SystemClk_Init();
Timera_Init();
TIMERA_CompareCmd(TIMERA_UNIT1, TIMERA_UNIT1_CH1, Enable);
TIMERA_CompareCmd(TIMERA_UNIT1, TIMERA_UNIT1_CH3, Enable);
TIMERA_Cmd(TIMERA_UNIT1, Enable);
u16DataCompare = TIMERA_GetCompareValue(TIMERA_UNIT1,TIMERA_UNIT1_CH1);
u16DataPeriod = TIMERA_GetPeriodValue(TIMERA_UNIT1);
while(1){
if( 1u == EXINT_Flag){
EXINT_Flag = 0u;
u16DataCompare += u16DataPeriod / 10u;//每次按一下,减小占空比
if(u16DataCompare > u16DataPeriod){
u16DataCompare = 0u;
}
TIMERA_SetCacheValue(TIMERA_UNIT1,TIMERA_UNIT1_CH1,u16DataCompare);
TIMERA_SetCacheValue(TIMERA_UNIT1,TIMERA_UNIT1_CH3,u16DataCompare);
}
}
}
修改地方
在配置timera第一单元基础结构时,
波形改为锯齿波,在官方代码里,是用三角波
在配置timera第一单元比较结构时,
修改enCompareMatchOutput,改为PWMn端输出高电平,官方代码里是反转电平
修改enPeriodMatchOutput,改为PWMn端输出低电平,官方代码里是保持电平
可能三角形波形情况更复杂点吧,锯齿波挺清晰,该到比较值时,转高电平,该到周期值时,转低电平即可。
具体还想了解更多,不妨看看这篇博文,他写的比较详细http://t.csdn.cn/kshuVhttp://t.csdn.cn/kshuV