目录
一、如何测量PWM的占空比
在之前,我们使用通用定时器的输入捕获功能实现了测量PWM的周期,其原理可以概括为:定时器输入捕获通道中的边沿检测器检测到上升沿信号后开始计数,直到再次检测到上升沿后将CNT中的值拷贝至CCRx中,我们使用了中断的方式,在上升沿时触发中断,在中断服务函数中做了以下操作:
- 将CNT置零从新开始下一次检测
- 读取计数器内的数值,乘以定时器的计数周期,就可以计算出输入信号的周期和频率信息。
进一步,如果要得到PWM的占空比信息,就需要记录上升沿信息后的第一个下降沿的时间t1和第一个上升沿的时间t2,其中的t1就代表了高电平的持续时间,t2代表了PWM的周期,占空比就是t1/t2。
在STM32中的普通定时器中,为我们提供了实现这一功能的硬件基础:
1.通用定时器的时基部分的触发控制器可以接入多种触发信号,其中就包括了来自输入捕获通道的TI1FP1和TI2FP2。
2.定时器可以工作在从模式,通过外部信号(例如TI1FP1和TI2FP2)实现对定时器的行为控制。
3.定时器的输入捕获通道1和2的输入信号可以进行交叉传输。(3和4两个通道也可以交叉,但是不能作为触发控制器的触发信号)
我们可以根据通用定时器以上的特性,来实现测量PWM的占空比。
二、定时器的触发信号

1.触发输入信号(TRGI)
定时器的触发信号可以是从外引脚部过来的 ,也可能是自己输入通道过来的信号到触发控制器。
这些信号的主要功能是控制本定时器的一些动作,例如复位、使能计数器向上向下计数。
当定时器需要通过触发输入信号来控制自己的时候,本定时器就处于主从模式中的从模式。
2.触发输出信号(TRGO)
本定时器将经过处理的自己的信号传给其他的定时器或DAC/ADC、可以用于和其他定时器做级联,触发其他定时器的一些动作,或者触发其他的一些外设的工作。这个时候,本定时器就是工作在主从模式中的主模式。
三、触发输入信号的选择
触发控制信号的选择需要通过从模式控制寄存器SMCR中的对应控制位实现
从模式控制寄存器:SMCR

SMCR->TS[2:0] 触发选择,用于选择需要的触发信号源

这些输入信号可以分为4类:
第1类:
TS = 000-011 共4个,都是内部定时器触发,内部定时器触发的连接表如下:

第2类:
TS = 111 共1个
来源于外部触发脚ETR,经过极性选择、边沿检测盒预分频器,输入滤波器,称为TRGI信号。
第3类:
TS = 100 共1个
来自TI1(通道1)的边沿检测器(TI1F_ED),上升沿和下降沿都会产生触发信号
第4类:
TS = 101 和 110 共2个
来自于自身的通道1和通道2,经过滤波和边沿检测器,得到TI1FP1和TI2FP2信号,他们是上升沿或下降沿,只能选一个。 检测PWM占空比使用这种方式。
四、定时器从模式
当定时器的一些动作需要通过外部信号驱动时,定时器就需要工作在从模式下,STM32中的定时器设置了多种从模式,以应对不同的使用场景,从模式的选择需要通过 从模式控制寄存器SMCR中的从模式选择位进行选择。
从模式选择位SMCR->SMS[2:0]

注意:仅有111模式采用了外部时钟驱动计数器。
通过上面的从模式选择信息可以发现:复位模式可以用于检测PWM的周期和占空比,且较为常用。
五、PWM输入模式
PWM输入模式是输入捕获模式的一个特例,操作与输入捕获模式类似,以通道1输入为例:
经过输入滤波器和边沿检测器得到2路信号TI1FP1和TI1FP2。
TI1FP1和TI1FP2是经过滤波、极性选择后得到的信号:
TI1FP1的极性选择为CC1P = 0;检测上升沿;
TI2FP2的极性选择为CC2P = 1;检测下降沿;
TI1FP1作为触发输入信号传给触发控制器,触发控制器工作在复位模式。
六、应用:测量PWM的占空比
根据上文,我们已经了解到:测量PWM的占空比,输入检测通道需要同时检测上升沿和下降沿,因此需要使用两个通道,一个通道用于检测上升沿,另一个通道检测下降沿。
触发控制器需要工作在从模式的复位模式。
当收到上升沿检测通道传来的上升沿信号时,控制计数器归零,同时硬件自动将上升沿通道计数器的值拷贝到上升沿通道的捕获/比较寄存器中(CCRx);
当收到下降沿检测通道传来的下降沿信号时,硬件自动将下降沿通道计数器的值拷贝到下降沿通道的捕获/比较寄存器中(CCRx)。
七、相关代码
实验时用来检测PWM占空比的定时器为TIM4,将TIM4的初始化、占空比测试的相关代码如下:
文件名:tim.c
cpp
#include "tim4.h"
// 初始化
void TIM4_Init(void)
{
// 1.开启时钟
RCC->APB1ENR |= RCC_APB1ENR_TIM4EN;
RCC->APB2ENR |= RCC_APB2ENR_IOPBEN;
// 2.配置GPIOB6引脚的工作模式,检测PWM信号,引脚需要工作在浮空输入模式。CNF-01;MODE-00;
GPIOB->CRL &= ~GPIO_CRL_CNF6_1;
GPIOB->CRL |= GPIO_CRL_CNF6_0;
GPIOB->CRL &= ~GPIO_CRL_MODE6;
// 3.配置定时器4的基本参数
// 3.1.时钟的输入频率定为1MHz,用这个频率作为PWM信号的采样频率
TIM4->PSC = 71;
// 3.2.自动重装载寄存器设置为最大,尽量避免溢出
TIM4->ARR = 65535;
// 3.3.开启预加载功能
TIM4->CR1 |= TIM_CR1_ARPE;
// 4.配置定时器4通道1和通道2的相关参数
// 4.1.配置定时器为复位从模式-100
TIM4->SMCR |= TIM_SMCR_SMS_2;
TIM4->SMCR &= ~TIM_SMCR_SMS_1;
TIM4->SMCR &= ~TIM_SMCR_SMS_0;
// 4.2.从模式的触发选择-滤波后的定时器1作为触发信号-101
TIM4->SMCR |= TIM_SMCR_TS_2;
TIM4->SMCR &= ~TIM_SMCR_TS_1;
TIM4->SMCR |= TIM_SMCR_TS_0;
// 4.3.配置通道1为输入模式,TI1与IC的映射为直连,01
TIM4->CCMR1 &= ~TIM_CCMR1_CC1S_1;
TIM4->CCMR1 |= TIM_CCMR1_CC1S_0;
// 4.4.配置通道2为输入模式,TI1与IC2进行交叉连接,10
TIM4->CCMR1 |= TIM_CCMR1_CC2S_1;
TIM4->CCMR1 &= ~TIM_CCMR1_CC2S_0;
// 4.5.配置通道接入信号TI
TIM4->CR2 &= ~TIM_CR2_TI1S;
// 4.6.配置信号滤波器,不滤波,0000
TIM4->CCMR1 &= ~TIM_CCMR1_IC1F;
// 4.7.配置边沿检测器,通道1,上升沿检测,通道2下降沿检测
TIM4->CCER &= ~TIM_CCER_CC1P;
TIM4->CCER |= TIM_CCER_CC2P;
// 4.8.预分频配置,不进行预分频
TIM4->CCMR1 &= ~TIM_CCMR1_IC1F;
TIM4->CCMR1 &= ~TIM_CCMR1_IC2F;
// 5.开启通道1和通道2
TIM4->CCER |= TIM_CCER_CC1E;
TIM4->CCER |= TIM_CCER_CC2E;
}
// 开启定时器,开始检测PWM波形
void TIM4_Start(void)
{
TIM4->CR1 |= TIM_CR1_CEN;
}
// 关闭定时器,停止检测PWM波形
void TIM4_Stop(void)
{
TIM4->CR1 &= ~TIM_CR1_CEN;
}
// 检测PWM的周期信息,返回周期数据,单位ms
double TIM4_PWM_Cycle(void)
{
return TIM4->CCR1 / 1000.0F;
}
// 检测PWM的占空比,返回占空比的比值,返回百分数据
double TIM4_PWM_Duty(void)
{
if(TIM4->CCR2 == 0)
{
printf("TIM4->CCR2 == 0, Division by zero is illegal !!\n");
}
return (TIM4->CCR2 / ((TIM4->CCR1) * 1.0)) * 100;
}
文件名:tim.h
cpp
#ifndef __TIM4_H
#define __TIM4_H
#include "stm32f10x.h"
#include <stdio.h>
// 函数声明
// 初始化
void TIM4_Init(void);
// 开启定时器,开始检测PWM波形
void TIM4_Start(void);
// 关闭定时器,停止检测PWM波形
void TIM4_Stop(void);
// 检测PWM的周期信息,返回周期数据,单位ms
double TIM4_PWM_Cycle(void);
// 检测PWM的占空比,返回占空比的比值,返回百分数据
double TIM4_PWM_Duty(void);
#endif