PWM 模式
脉冲宽度调制模式可以产生一个由 TIM1_ARR 寄存器确定频率、由 TIM1_CCRx 寄存器确定占空比的信号。
在 TIM1_CCMRx 寄存器中的 OCxM 位写入’110’(PWM 模式 1)或’111’(PWM 模式 2),能够独立地设置每个 OCx 输出通道产生一路 PWM。 必须通过设置 TIM1_CCMRx 寄存器的 OCxPE 位使能相应的预装载寄存器,最后还要设置 TIM1_CR1 寄存器的 ARPE 位,(在向上计数或中心对称模式中)使能自动重装载的预装载寄存器。
仅当发生一个更新事件的时候, 预装载寄存器才能被传送到影子寄存器, 因此在计数器开始计数之前,必须通过设置 TIM1_EGR 寄存器中的 UG 位来初始化所有的寄存器。
OCx 的极性可以通过软件在 TIM1_CCER 寄存器中的 CCxP 位设置,它可以设置为高电平有效或低电平有效。OCx 的输出使能通过(TIM1_CCER 和 TIM1_BDTR 寄存器中)CCxE、CCxNE、MOE、OSSI 和 OSSR 位的组合控制。详见 TIM1_CCER 寄存器的描述。
在 PWM 模式(模式 1 或模式 2)下,TIM1_CNT 和 TIM1_CCRx 始终在进行比较,(依据计数器的计数方向)以确定是否符合 TIM1_CCRx≤TIM1_CNT 或TIM1_CNT≤TIM1_CCRx。 根据TIM1_CR1寄存器中CMS位的状态, 定时器能够产生边沿对齐的PWM信号或中央对齐的PWM信号。
PWM 中央对齐模式
当 TIM1_CR1 寄存器中的 CMS 位不为’00’时为中央对齐模式(所有其他的配置对 OCxREF/OCx 信号都有相同的作用)。根据不同的 CMS 位设置,比较标志可以在计数器向上计数时被置 1、在计数器向下计数时被置 1、 或在计数器向上和向下计数时被置 1。 TIM1_CR1 寄存器中的计数方向位(DIR)由硬件更新,不要用软件修改它。
下图给出了一些中央对齐的 PWM 波形的例子
⚫ TIM1_ARR=8
⚫ PWM 模式 1
⚫ TIM1_CR1 寄存器的 CMS=01,在中央对齐模式 1 下,当计数器向下计数时设置比较标志。
使用中央对齐模式的提示:
⚫ 进入中央对齐模式时, 使用当前的向上/向下计数配置。 这就意味着计数器向上还是向下计数取决于 TIM1_CR1 寄存器中 DIR 位的当前值。此外,软件不能同时修改 DIR 和 CMS 位。
⚫ 不推荐当运行在中央对齐模式时改写计数器,因为这会产生不可预知的结果。特别地:
- 如果写入计数器的值大于自动重加载的值(TIM1_CNT>TIM1_ARR),则方向不会被更新。
例如,如果计数器正在向上计数,它就会继续向上计数。 - 如果将 0 或者 TIM1_ARR 的值写入计数器,方向被更新,但不产生更新事件 UEV。
⚫ 使用中央对齐模式最保险的方法,就是在启动计数器之前产生一个软件更新(设置 TIM1_EGR位中的 UG 位),并且不要在计数进行过程中修改计数器的值。
/********************************************************************************
* @file bsp_pwm.c
* @author jianqiang.xue
* @version V1.0.0
* @date 2021-04-18
* @brief NULL
********************************************************************************/
/* Includes ------------------------------------------------------------------*/
#include "RTE_Components.h"
#include CMSIS_device_header
#include "bsp_gpio.h"
#include "bsp_tim.h"
#include "bsp_pwm.h"
/* Private Includes ----------------------------------------------------------*/
#include "business_gpio.h"
#include "business_function.h"
/* Private Variables ---------------------------------------------------------*/
static bool g_pwm_init = false;
#if BS_TIM1_EN
extern TIM_HandleTypeDef tim1_handle_t;
extern TIM_OC_InitTypeDef tim1_oc_init_handle_t;
#endif
/* Public function prototypes -----------------------------------------------*/
/**
* @brief PWN功能初始化,使用定时器和ppi来模拟PWM功能
* @note NULL
* @retval None
*/
void bsp_pwm_init(void)
{
if (g_pwm_init)
{
return;
}
#if BS_TIM1_EN
bsp_tim_pwm_init();
g_pwm_init = true;
#endif
}
/**
* @brief PWN功能关闭
* @note NULL
* @retval None
*/
void bsp_pwm_deinit(void)
{
if (!g_pwm_init)
{
return;
}
#if BS_TIM1_EN
bsp_tim_pwm_deinit();
g_pwm_init = false;
#endif
}
/**
* @brief 设置PWM占空比
* @note NULL
* @param pwmx: PWM组号
* @param val: 0-100 PWM值
* @retval None
*/
void bsp_pwm_set_pulse(bsp_pwm_t pwmx, uint16_t val)
{
if (!g_pwm_init)
{
return;
}
if (pwmx == BSP_PWM_0)
{
#if BS_TIM1_EN && BS_PWM0_EN
HAL_TIM_PWM_Stop(&tim1_handle_t, TIM_CHANNEL_1);
tim1_oc_init_handle_t.Pulse = val;
HAL_TIM_PWM_ConfigChannel(&tim1_handle_t, &tim1_oc_init_handle_t, TIM_CHANNEL_1);
HAL_TIM_PWM_Start(&tim1_handle_t, TIM_CHANNEL_1);
#endif
}
else if (pwmx == BSP_PWM_1)
{
#if BS_TIM1_EN && BS_PWM1_EN
HAL_TIM_PWM_Stop(&tim1_handle_t, TIM_CHANNEL_2);
tim1_oc_init_handle_t.Pulse = val;
HAL_TIM_PWM_ConfigChannel(&tim1_handle_t, &tim1_oc_init_handle_t, TIM_CHANNEL_2);
HAL_TIM_PWM_Start(&tim1_handle_t, TIM_CHANNEL_2);
#endif
}
else if (pwmx == BSP_PWM_2)
{
#if BS_TIM1_EN && BS_PWM2_EN
HAL_TIM_PWM_Stop(&tim1_handle_t, TIM_CHANNEL_3);
tim1_oc_init_handle_t.Pulse = val;
HAL_TIM_PWM_ConfigChannel(&tim1_handle_t, &tim1_oc_init_handle_t, TIM_CHANNEL_3);
HAL_TIM_PWM_Start(&tim1_handle_t, TIM_CHANNEL_3);
#endif
}
else if (pwmx == BSP_PWM_3)
{
#if BS_TIM1_EN && BS_PWM3_EN
HAL_TIM_PWM_Stop(&tim1_handle_t, TIM_CHANNEL_4);
tim1_oc_init_handle_t.Pulse = val;
HAL_TIM_PWM_ConfigChannel(&tim1_handle_t, &tim1_oc_init_handle_t, TIM_CHANNEL_4);
HAL_TIM_PWM_Start(&tim1_handle_t, TIM_CHANNEL_4);
#endif
}
}
/********************************************************************************
* @file bsp_pwm.h
* @author jianqiang.xue
* @version V1.0.0
* @date 2021-04-18
* @brief NULL
********************************************************************************/
#ifndef __BSP_PWM_H
#define __BSP_PWM_H
/* Includes ------------------------------------------------------------------*/
#include <stdint.h>
/* Public enum ---------------------------------------------------------------*/
typedef enum
{
BSP_PWM_0 = 0,
BSP_PWM_1,
BSP_PWM_2,
BSP_PWM_3,
BSP_PWM_4,
BSP_PWM_5,
BSP_PWM_6,
BSP_PWM_7,
BSP_PWM_8,
BSP_PWM_9,
BSP_PWM_10,
BSP_PWM_11
} bsp_pwm_t;
/* Public Function Prototypes ------------------------------------------------*/
void bsp_pwm_init(void);
void bsp_pwm_deinit(void);
void bsp_pwm_set_pulse(bsp_pwm_t pwmx, uint16_t val);
#endif