15 增强型脉宽调制发生器(PWM)
增强型脉宽调制发生器具有两大功能:生成脉宽调制信号(PWM),捕捉外部脉冲输入(PCAP)。 增强型脉宽调制发生器具备 3 个通道,每个通道都可以单独配置为 PWM 或者 PCAP 模式。 每个通道拥有独立的 FIFO。FIFO 里的每个存储单元为 2 个 20bit 数据。 FIFO 深度为 4,即最多存储 4 个单元,共 \(8 \times 20bit\) 数据。 这里的 20bit 位宽是因为本硬件模块内部 PWM 使用的各计数器都是 20 比特。 可根据 FIFO 内的数据量触发中断或者 DMA 传输。
说明:TIMER 也支持生成脉宽调制信号,但是可配置的参数较简单,不支持死区等。
PWM 特性:
- 最多支持 3 个 PWM 通道,每一个通道包含 A、B 两个输出
- 每个通道参数独立
- 支持死区
- 支持通过 DMA 更新 PWM 配置
PCAP 特性:
- 支持 3 个 PCAP 通道,每一个通道包含两个输入
- 支持捕捉上升沿、下降沿
- 支持通过 DMA 读取数据
15.1 PWM 工作模式
PWM 使用的时钟频率可配置,请参考 SYSCTRL。
每个 PWM 通道支持以下多种工作模式:
typedef enum
{
..._UP_WITHOUT_DIED_ZONE = ...,
..._UP_WITH_DIED_ZONE = ...,
..._UPDOWN_WITHOUT_DIED_ZONE = ...,
..._UPDOWN_WITH_DIED_ZONE = ...,
..._SINGLE_WITHOUT_DIED_ZONE = ...,
..._DMA = ...,
} PWM_WorkMode_t;
15.1.1 最简单的模式:UP_WITHOUT_DIED_ZONE
此模式需要配置两个门限:计数器回零门限 PERA_TH、高门限 HIGH_TH,HIGH_TH 必须小于 HIGH_TH。以伪代码描述 A、B 输出如下:
= 0;
cnt ()
on_clock_rising_edge{
= cnt < PERA_TH ? cnt + 1 : 0;
cnt = HIGH_TH <= cnt;
A = !A;
B }
15.1.2 UP_WITH_DIED_ZONE
与 UP_WITHOUT_DIED_ZONE 相比,此模式需要一个新的死区门限 DZONE_TH,DZONE_TH 必须小于 HIGH_TH。以伪代码描述 A、B 输出如下:
= 0;
cnt ()
on_clock_rising_edge{
= cnt < PERA_TH ? cnt + 1 : 0;
cnt = HIGH_TH + DZONE_TH <= cnt;
A = DZONE_TH <= cnt < HIGH_TH);
B }
15.1.3 UPDOWN_WITHOUT_DIED_ZONE
此模式需要的门限参数与 UP_WITHOUT_DIED_ZONE 相同。以伪代码描述 A、B 输出如下:
= 0;
cnt ()
on_clock_rising_edge{
= cnt < 2 * PERA_TH ? cnt + 1 : 0;
cnt = PERA_TH - HIGH_TH <= cnt <= PERA_TH + HIGH_TH;
A = !A;
B }
15.1.4 UPDOWN_WITH_DIED_ZONE
与 UP_WITHOUT_DIED_ZONE 相比,此模式需要一个新的死区门限 DZONE_TH。 以伪代码描述 A、B 输出如下:
= 0;
cnt ()
on_clock_rising_edge{
= cnt < 2 * PERA_TH ? cnt + 1 : 0;
cnt = PERA_TH - HIGH_TH + DZONE_TH <= cnt <= PERA_TH + HIGH_TH;
A = (cnt < PERA_TH - HIGH_TH) || (cnt > PERA_TH + HIGH_TH + DZONE_TH);
B }
15.2 PCAP
PCAP 每个通道包含两路输入。PCAP 内部有一个单独的 32 比特计数器3, 当检测到输入信号变化(包含上升沿和下降沿)时,PCAP 将计数器的值及边沿变化信息作为一个存储单元压入 FIFO:
struct data0
{
uint32_t cnt_high:12;
uint32_t p_cap_0_p:1; // A 路出现上升沿
uint32_t p_cap_0_n:1; // A 路出现下降沿
uint32_t p_cap_1_p:1; // B 路出现上升沿
uint32_t p_cap_1_n:1; // B 路出现下降沿
uint32_t tag:4;
uint32_t padding:12;
};
struct data1
{
uint32_t cnt_low:20;
uint32_t padding:12;
};
通过复位整个模块可以清零 PCAP 计数器。
15.3 PWM 使用说明
15.3.1 启动与停止
共有两个开关与 PWM 的启动和停止有关:使能(Enable)、停机控制(HaltCtrl)。只有当 Enable
为 1,
HaltCtrl
为 0 时,PWM 才真正开始工作。
相关的 API 为:
// 使能 PWM 通道
void PWM_Enable(
const uint8_t channel_index, // 通道号
const uint8_t enable // 使能或禁用
);
// PWM 通道停机控制
void PWM_HaltCtrlEnable(
const uint8_t channel_index, // 通道号
const uint8_t enable // 停机(1) 或运转(0)
);
15.3.2 配置工作模式
void PWM_SetMode(
const uint8_t channel_index, // 通道号
const PWM_WorkMode_t mode // 模式
);
15.3.3 配置门限
// 配置 PERA_TH
void PWM_SetPeraThreshold(
const uint8_t channel_index,
const uint32_t threshold);
// 配置 DZONE_TH
void PWM_SetDiedZoneThreshold(
const uint8_t channel_index,
const uint32_t threshold);
// 配置 HIGH_TH
void PWM_SetHighThreshold(
const uint8_t channel_index,
const uint8_t multi_duty_index, // 对于 ING916XX,此参数无效
const uint32_t threshold);
各门限值最大支持 0xFFFFF,共 20 个比特。
15.3.4 输出控制
// 掩膜控制
void PWM_SetMask(
const uint8_t channel_index, // 通道号
const uint8_t mask_a, // A 路掩膜
const uint8_t mask_b // B 路掩膜
);
// 配置停机输出值
void PWM_HaltCtrlCfg(
const uint8_t channel_index, // 通道号
const uint8_t out_a, // A 路停机输出值
const uint8_t out_b // B 路停机输出值
);
// 反相
void PWM_SetInvertOutput(
const uint8_t channel_index, // 通道号
const uint8_t inv_a, // A 路是否反相
const uint8_t inv_b // B 路是否反相
);
15.3.5 综合示例
下面的例子将 channel_index
通道配置成输出频率为 frequency
、占空比为 (on_duty)%
的方波,
涉及 3 个关键参数:
生成这种最简单的 PWM 信号需要的模式为 UP_WITHOUT_DIED_ZONE;
PERA_TH 控制输出信号的频率,设置为
PWM_CLOCK_FREQ / frequency
;HIGH_TH 控制信号的占空比,设置为
PERA_TH * (100 - on_duty) %
void PWM_SetupSimple(
const uint8_t channel_index,
const uint32_t frequency,
const uint16_t on_duty)
{
uint32_t pera = PWM_CLOCK_FREQ / frequency;
uint32_t high = pera > 1000 ?
/ 100 * (100 - on_duty)
pera : pera * (100 - on_duty) / 100;
(channel_index, 1);
PWM_HaltCtrlEnable(channel_index, 0);
PWM_Enable(channel_index, pera);
PWM_SetPeraThreshold(channel_index, 0, high);
PWM_SetHighThreshold(channel_index, PWM_WORK_MODE_UP_WITHOUT_DIED_ZONE);
PWM_SetMode(channel_index, 0, 0);
PWM_SetMask(channel_index, 1);
PWM_Enable(channel_index, 0);
PWM_HaltCtrlEnable}
15.3.6 使用 DMA 实时更新配置
使用 DMA 能够实时更新配置(相当于工作在 UP_WITHOUT_DIED_ZONE,但是每个循环使用不同的参数):
每当 PWM 计数器计完一圈回零时,自动使用来自 DMA 的数据更新配置。
这些数据以 2 个 uint32_t
为一组,依次表示 HIGH_TH 和 PERA_TH。
void PWM_DmaEnable(
const uint8_t channel_index, // 通道号
uint8_t trig_cfg, // DMA 请求触发门限
uint8_t enable // 使能
);
当 PWM 内部 FIFO 数据少于 trig_cfg
,PWM 请求 DMA 传输数据。PWM FIFO 深度为 8
,可以存储 8 个 32 比特数据(只有低 20 比特有效,其余比特忽略),相当于 4 组 PWM 配置,
所以 trig_cfg
的取值范围为 \(0..7\)。
15.4 PCAP 使用说明
15.4.1 配置 PCAP 模式
要启用 PCAP 模式,需要 5 个步骤:
关闭整个模块的时钟(参考 SYSCTRL)
使用
PCAP_Enable
使能 PCAP 模式void PCAP_Enable( const uint8_t channel_index // 通道号 );
使用
PCAP_EnableEvents
选择要检测的事件void PCAP_EnableEvents( const uint8_t channel_index, uint8_t events_on_0, uint8_t events_on_1);
events
为下面两个事件的组合:enum PCAP_PULSE_EVENT { = 0x1, PCAP_PULSE_RISING_EDGE = 0x2, PCAP_PULSE_FALLING_EDGE };
比如在通道 1 的 A 路输入上同时检测、上报上升沿和下降沿:
(1, PCAP_EnableEvents PCAP_PULSE_RISING_EDGE| PCAP_PULSE_FALLING_EDGE, ...);
打开整个模块的时钟(参考 SYSCTRL)
配置 DMA 传输
当 PCAP 通道 FIFO 内存储的数据多于或等于
trig_cfg
时,请求 DMA 传输数据。trig_cfg
的取值范围为 \(0..7\)。void PCAP_DmaEnable( const uint8_t channel_index, // 通道号 uint8_t trig_cfg, // DMA 请求触发门限 uint8_t enable // 使能 );
使能计数器
void PCAP_CounterEnable( uint8_t enable // 使能(1)/禁用(0) );
所有 6 路输入共有此计数器。↩︎