PWM—脉宽调制
PWM模块可以在GPIO上产生脉宽调制信号。 该模块实现了一个上行或上下计数器,具有四个PWM通道,驱动分配的gpio。
三个PWM模块可以提供多达12个PWM通道与单独的频率控制组多达4个通道。 此外,内置的解码器和EasyDMA功能可以在没有CPU干预的情况下操纵PWM占空比。 任意占空比序列可从数据RAM中读取,并可链接以实现循环缓冲或重复进入复杂循环。
这里列出了一个PWM模块的主要特点:
- 固定的PWM基频与可编程时钟分频器
- 多达四个PWM通道,各自的极性和占空比值
- 沿PWM通道或中心对齐的脉冲
- Data RAM中定义的多个占空比阵列(序列)
- 通过EasyDMA从内存直接自动和无故障更新占空比值
- 在每个PWM周期上可能改变极性、占空比和基频
- 数据RAM序列可以重复或连接到循环
波计数器
波计数器负责产生占空比取决于比较值,频率取决于COUNTERTOP。
有一个普通的15位计数器与四个比较通道。 因此,所有四个通道将共享相同的周期(PWM频率),但可以有各自的占空比和极性。 极性由从RAM读取的值设置(参见第498页图144:解码器内存访问模式),而MODE寄存器控制计数器是向上计数还是向上和向下计数。 计时器的顶部值由COUNTERTOP寄存器控制。 这个寄存器值与PWM_CLK的所选PRESCALER结合将产生一个给定的PWM周期。 COUNTERTOP值小于比较设置将导致不产生PWM边缘的状态。 考虑到极性设置为FallingEdge,则将OUT[n]设置为高值。
所有的比较寄存器都是内部的,只能通过后面提供的解码器进行配置。
台面可随时安全书写。 它将在START任务之后进行采样。 如果解码器。 LOAD不是波形,它也会在STARTSEQ[n]任务之后采样,以及在序列回放期间从RAM加载新值时采样。 如果解码器。 LOAD=波形,寄存器值将被忽略,并从RAM中获取(参见下面498页的译码器和 EasyDMA)。
[nrf52] low_power_pwm pwm_library pwm_driver 三者区别
/********************************************************************************
* @file bsp_pwm.c
* @author jianqiang.xue
* @version V1.0.0
* @date 2021-08-10
* @brief NULL
********************************************************************************/
/* Includes ------------------------------------------------------------------*/
#include "RTE_Components.h"
#include CMSIS_device_header
#include "nrf_drv_pwm.h"
#include "bsp_gpio.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
static nrf_drv_pwm_t m_pwm0 = NRF_DRV_PWM_INSTANCE(0);
static uint16_t const m_pwm0_top = BS_TIM1_PERIOD;
//static uint16_t const m_pwm0_step = 1;
static nrf_pwm_values_individual_t m_pwm0_seq_values;
static nrf_pwm_sequence_t const m_pwm0_seq =
{
.values.p_individual = &m_pwm0_seq_values,
.length = NRF_PWM_VALUES_LENGTH(m_pwm0_seq_values),
.repeats = 0,
.end_delay = 0
};
#endif
#if BS_TIM2_EN
static nrf_drv_pwm_t m_pwm1 = NRF_DRV_PWM_INSTANCE(1);
static uint16_t const m_pwm1_top = BS_TIM2_PERIOD;
//static uint16_t const m_pwm1_step = 1;
static nrf_pwm_values_individual_t m_pwm1_seq_values;
static nrf_pwm_sequence_t const m_pwm1_seq =
{
.values.p_individual = &m_pwm1_seq_values,
.length = NRF_PWM_VALUES_LENGTH(m_pwm1_seq_values),
.repeats = 0,
.end_delay = 0
};
#endif
#if BS_TIM3_EN
static nrf_drv_pwm_t m_pwm2 = NRF_DRV_PWM_INSTANCE(2);
static uint16_t const m_pwm2_top = BS_TIM3_PERIOD;
//static uint16_t const m_pwm2_step = 1;
static nrf_pwm_values_individual_t m_pwm2_seq_values;
static nrf_pwm_sequence_t const m_pwm2_seq =
{
.values.p_individual = &m_pwm2_seq_values,
.length = NRF_PWM_VALUES_LENGTH(m_pwm2_seq_values),
.repeats = 0,
.end_delay = 0
};
#endif
void bsp_pwm_init(void)
{
if (g_pwm_init)
{
return;
}
#if BS_TIM1_EN
nrf_drv_pwm_config_t const config0 =
{
.output_pins =
{
#if BS_TIM1_CH0_PIN != NULL
BS_TIM1_CH0_PIN | NRF_DRV_PWM_PIN_INVERTED, // channel 0
#else
NRF_DRV_PWM_PIN_NOT_USED, // channel 0
#endif
#if BS_TIM1_CH1_PIN != NULL
BS_TIM1_CH1_PIN | NRF_DRV_PWM_PIN_INVERTED, // channel 1
#else
NRF_DRV_PWM_PIN_NOT_USED, // channel 1
#endif
#if BS_TIM1_CH2_PIN != NULL
BS_TIM1_CH2_PIN | NRF_DRV_PWM_PIN_INVERTED, // channel 2
#else
NRF_DRV_PWM_PIN_NOT_USED, // channel 2
#endif
#if BS_TIM1_CH3_PIN != NULL
BS_TIM1_CH3_PIN | NRF_DRV_PWM_PIN_INVERTED, // channel 3
#else
NRF_DRV_PWM_PIN_NOT_USED, // channel 3
#endif
},
.irq_priority = APP_IRQ_PRIORITY_LOWEST,
.base_clock = (nrf_pwm_clk_t)BS_TIM1_PRESCALER,
.count_mode = NRF_PWM_MODE_UP,
.top_value = m_pwm0_top,
.load_mode = NRF_PWM_LOAD_INDIVIDUAL,
.step_mode = NRF_PWM_STEP_AUTO
};
APP_ERROR_CHECK(nrf_drv_pwm_init(&m_pwm0, &config0, NULL));
m_pwm0_seq_values.channel_0 = 0x8000;
m_pwm0_seq_values.channel_1 = 0x8000;
m_pwm0_seq_values.channel_2 = 0x8000;
m_pwm0_seq_values.channel_3 = 0x8000;
(void)nrf_drv_pwm_simple_playback(&m_pwm0, &m_pwm0_seq, 1, NRF_DRV_PWM_FLAG_LOOP);
g_pwm_init = true;
#endif
#if BS_TIM2_EN
nrf_drv_pwm_config_t const config1 =
{
.output_pins =
{
#if BS_TIM2_CH0_PIN != NULL
BS_TIM2_CH0_PIN | NRF_DRV_PWM_PIN_INVERTED, // channel 0
#else
NRF_DRV_PWM_PIN_NOT_USED, // channel 0
#endif
#if BS_TIM2_CH1_PIN != NULL
BS_TIM2_CH1_PIN | NRF_DRV_PWM_PIN_INVERTED, // channel 1
#else
NRF_DRV_PWM_PIN_NOT_USED, // channel 1
#endif
#if BS_TIM2_CH2_PIN != NULL
BS_TIM2_CH2_PIN | NRF_DRV_PWM_PIN_INVERTED, // channel 2
#else
NRF_DRV_PWM_PIN_NOT_USED, // channel 2
#endif
#if BS_TIM2_CH3_PIN != NULL
BS_TIM2_CH3_PIN | NRF_DRV_PWM_PIN_INVERTED, // channel 3
#else
NRF_DRV_PWM_PIN_NOT_USED, // channel 3
#endif
},
.irq_priority = APP_IRQ_PRIORITY_LOWEST,
.base_clock = (nrf_pwm_clk_t)BS_TIM2_PRESCALER,
.count_mode = NRF_PWM_MODE_UP,
.top_value = m_pwm1_top,
.load_mode = NRF_PWM_LOAD_INDIVIDUAL,
.step_mode = NRF_PWM_STEP_AUTO
};
APP_ERROR_CHECK(nrf_drv_pwm_init(&m_pwm1, &config1, NULL));
m_pwm1_seq_values.channel_0 = 0x8000;
m_pwm1_seq_values.channel_1 = 0x8000;
m_pwm1_seq_values.channel_2 = 0x8000;
m_pwm1_seq_values.channel_3 = 0x8000;
(void)nrf_drv_pwm_simple_playback(&m_pwm1, &m_pwm1_seq, 1, NRF_DRV_PWM_FLAG_LOOP);
g_pwm_init = true;
#endif
#if BS_TIM3_EN
nrf_drv_pwm_config_t const config2 =
{
.output_pins =
{
#if BS_TIM3_CH0_PIN != NULL
BS_TIM3_CH0_PIN | NRF_DRV_PWM_PIN_INVERTED, // channel 0
#else
NRF_DRV_PWM_PIN_NOT_USED, // channel 0
#endif
#if BS_TIM3_CH1_PIN != NULL
BS_TIM3_CH1_PIN | NRF_DRV_PWM_PIN_INVERTED, // channel 1
#else
NRF_DRV_PWM_PIN_NOT_USED, // channel 1
#endif
#if BS_TIM3_CH2_PIN != NULL
BS_TIM3_CH2_PIN | NRF_DRV_PWM_PIN_INVERTED, // channel 2
#else
NRF_DRV_PWM_PIN_NOT_USED, // channel 2
#endif
#if BS_TIM3_CH3_PIN != NULL
BS_TIM3_CH3_PIN | NRF_DRV_PWM_PIN_INVERTED, // channel 3
#else
NRF_DRV_PWM_PIN_NOT_USED, // channel 3
#endif
},
.irq_priority = APP_IRQ_PRIORITY_LOWEST,
.base_clock = (nrf_pwm_clk_t)BS_TIM3_PRESCALER,
.count_mode = NRF_PWM_MODE_UP,
.top_value = m_pwm1_top,
.load_mode = NRF_PWM_LOAD_INDIVIDUAL,
.step_mode = NRF_PWM_STEP_AUTO
};
APP_ERROR_CHECK(nrf_drv_pwm_init(&m_pwm2, &config2, NULL));
m_pwm2_seq_values.channel_0 = 0x8000;
m_pwm2_seq_values.channel_1 = 0x8000;
m_pwm2_seq_values.channel_2 = 0x8000;
m_pwm2_seq_values.channel_3 = 0x8000;
(void)nrf_drv_pwm_simple_playback(&m_pwm2, &m_pwm2_seq, 1, NRF_DRV_PWM_FLAG_LOOP);
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
nrf_drv_pwm_uninit(&m_pwm0);
g_pwm_init = false;
#endif
#if BS_TIM2_EN
nrf_drv_pwm_uninit(&m_pwm1);
g_pwm_init = false;
#endif
#if BS_TIM3_EN
nrf_drv_pwm_uninit(&m_pwm2);
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 BS_TIM1_EN || BS_TIM2_EN
if (!g_pwm_init)
{
return;
}
#endif
if (pwmx == BSP_PWM_0)
{
#if BS_TIM1_EN && BS_PWM0_EN
m_pwm0_seq_values.channel_0 = val;
#endif
}
else if (pwmx == BSP_PWM_1)
{
#if BS_TIM1_EN && BS_PWM1_EN
m_pwm0_seq_values.channel_1 = val;
#endif
}
else if (pwmx == BSP_PWM_2)
{
#if BS_TIM1_EN && BS_PWM2_EN
m_pwm0_seq_values.channel_2 = val;
#endif
}
else if (pwmx == BSP_PWM_3)
{
#if BS_TIM1_EN && BS_PWM3_EN
m_pwm0_seq_values.channel_3 = val;
#endif
}
else if (pwmx == BSP_PWM_4)
{
#if BS_TIM2_EN && BS_PWM4_EN
m_pwm1_seq_values.channel_0 = val;
#endif
}
else if (pwmx == BSP_PWM_5)
{
#if BS_TIM2_EN && BS_PWM5_EN
m_pwm1_seq_values.channel_1 = val;
#endif
}
else if (pwmx == BSP_PWM_6)
{
#if BS_TIM2_EN && BS_PWM6_EN
m_pwm1_seq_values.channel_2 = val;
#endif
}
else if (pwmx == BSP_PWM_7)
{
#if BS_TIM2_EN && BS_PWM7_EN
m_pwm1_seq_values.channel_3 = val;
#endif
}
else if (pwmx == BSP_PWM_8)
{
#if BS_TIM3_EN && BS_PWM8_EN
m_pwm2_seq_values.channel_0 = val;
#endif
}
else if (pwmx == BSP_PWM_9)
{
#if BS_TIM3_EN && BS_PWM9_EN
m_pwm2_seq_values.channel_1 = val;
#endif
}
else if (pwmx == BSP_PWM_10)
{
#if BS_TIM3_EN && BS_PWM10_EN
m_pwm2_seq_values.channel_2 = val;
#endif
}
else if (pwmx == BSP_PWM_11)
{
#if BS_TIM3_EN && BS_PWM11_EN
m_pwm2_seq_values.channel_3 = val;
#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