TimerA输出可调占空比PWM——华大HC32F460

目录

了解PWM

什么是PWM

pwm的频率

pwm的周期:

占空比:

周期

PWM原理

计数模式

PWM向上计数

PWM中心对齐

模式选择

定时器的溢出时间计算

 PWM配置

关于HC32F460Timer_A的PWM

捕获模式

捕获模式的代码(在官方代码/加点修改/加注释)

修改地方


了解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向上计数

  以STM32PWM mode 1为例,有效电平为高时:

  1. TIMx CNT小于TIMx CCR1时,输出高电平。

  2. TIMx CNT大于TIMx CCR1时,输出低电平。

PWM向下计数使用类似的方法。

PWM中心对齐

  以STM32PWM mode 1为例,有效电平为高时:

  1. TIMx CNT小于TIMx CCR1时,输出高电平。

  2. TIMx CNT大于TIMx CCR1时,输出低电平。

  3. 计数器计数到自动重载值ARR,然后向下计数。

  4. 计数器计数到TIMx CCR1,输出高电平。

  5. 计数器计数到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时钟,通常等于系统时钟

如:

  1. tclk为72M
  2. psc为7199
  3. 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 是预分频值

如:

  1. 主频=72M

  2. arr=4999

  3. 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

### 配置HC32F460 MCU上的TIMA以实现PWM输出 为了在HC32F460微控制器上通过TIMA配置并实现PWM输出,需遵循一系列具体的设置步骤来初始化定时器和调整其参数。 #### 初始化硬件资源 确保`timera_pwm.c` 和 `timera_pwm.h` 文件被放置于 `\实验20 TimerA PWM实验\Hardware\Peripheral` 路径下,并将 `timera_pwm.c` 添加至工程中的 Hardware/Peripheral 下[^2]。这一步骤对于集成必要的库文件至关重要,以便后续能够顺利调用与 TIMA 相关的功能函数。 #### 定义PWM模式下的计数方向及周期设定 当利用华大HC32F460TimerA模块生成PWM信号时,可以通过修改寄存器值的方式改变PWM占空比[^1]。具体来说: - **选择工作模式**:使能向上计数模式或上下计数模式取决于应用需求; - **设定自动重装载值**:此数值决定了PWM波形的一个完整周期时间长度; ```c // 设置自动重载值, 即PWM周期 TIMERACHNL_AUTORELOAD_SET(Timerx, PeriodValue); ``` #### 设定比较通道用于控制占空比 要动态更改PWM占空比,则需要操作相应的捕获/比较寄存器(CCR),该寄存器存储了匹配事件发生前的最大计数值,在每次达到这个预设值时会触发一次翻转动作从而形成高低电平变化。 ```c // 更新CCR寄存器值以更新占空比 TIMERACHNL_COMPARE_SET(Timerx, Channel, CompareValue); ``` 其中 `CompareValue` 是指期望得到的目标占空比对应的计数值,而 `Channel` 表明所使用的特定通道编号。 #### 启动定时器并启用中断(如果必要的话) 一旦完成了上述所有配置之后就可以启动定时器开始正常运作了。如果有更复杂的逻辑处理比如实时监控当前状态或是响应外部输入的变化等情况还可以考虑开启相应类型的中断服务程序(ISR)来进行进一步开发。 ```c // 开启指定定时器 TIMERA_ENABLE(Timerx); // (可选) 如果需要ISR支持则注册对应中断向量表项 NVIC_EnableIRQ(TIMERA_IRQn); ``` 以上就是基于HC32F460平台使用TIMA外设创建一个可以灵活调整占空比PWM输出实例的方法概述。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值