1)实验平台:正点原子APM32E103最小系统板
2)平台购买地址:https://detail.tmall.com/item.htm?id=609294757420
3)全套实验源码+手册+视频下载地址: http://www.openedv.com/docs/boards/xiaoxitongban
本章将介绍使用APM32E103的高级定时器检测输入PWM的占空比和周期。通过本章的学习,读者将学习到高级定时器PWM输入模式的使用。
本章分为如下几个小节:
24.1 硬件设计
24.2 程序设计
24.3 下载验证
24.1 硬件设计
24.1.1 例程功能
表24.2.1.1 函数TMR_ConfigPWM()形参描述
该函数的返回值描述,如下表所示:
返回值 描述
无 无
表24.2.1.2 函数TMR_ConfigPWM()返回值描述
该函数使用TMR_ICConfig_T类型的结构体变量传入PWM输入模式的配置参数,该结构体的定义请见第19.2.1小节中TMR_ICConfig_T的相关内容。
该函数的使用示例,如下所示:
#include "apm32e10x.h"
#include "apm32e10x_tmr.h"
void example_fun(void)
{
TMR_ICConfig_T tmr_ic_init_struct;
/* 配置TMR8通道1的PWM输入模式 */
tmr_ic_init_struct.channel = TMR_CHANNEL_1;
tmr_ic_init_struct.polarity = TMR_IC_POLARITY_RISING;
tmr_ic_init_struct.selection = TMR_IC_SELECTION_DIRECT_TI;
tmr_ic_init_struct.prescaler = TMR_IC_PSC_1;
tmr_ic_init_struct.filter = 0;
TMR_ConfigPWM(TMR8, &tmr_ic_init_struct);
}
③:配置触发输入信号
该函数用于配置定时器的触发输入信号,其函数原型如下所示:
void TMR_SelectInputTrigger(TMR_T* tmr, TMR_TRIGGER_SOURCE_T triggerSource);
该函数的形参描述,如下表所示:
形参 描述
tmr 指向TMR外设结构体的指针
例如:TMR1、TMR2等(在apm32e10x.h文件中有定义)
triggerSource 定时器的触发输入信号源
例如:TMR_TRIGGER_SOURCE_ITR0、TMR_TRIGGER_SOURCE_TI1FP1等(在apm32e10x_tmr.h文件中有定义)
表24.2.1.3 函数TMR_SelectInputTrigger()形参描述
该函数的返回值描述,如下表所示:
返回值 描述
无 无
表24.2.1.4 函数TMR_SelectInputTrigger()返回值描述
该函数的使用示例,如下所示:
#include "apm32e10x.h"
#include "apm32e10x_tmr.h"
void example_fun(void)
{
/* 配置TMR8的触发输入信号源为TI1FP1 */
TMR_SelectInputTrigger(TMR8, TMR_TRIGGER_SOURCE_TI1FP1);
}
④:配置从模式功能
该函数用于配置定时器的从模式功能,其函数原型如下所示:
void TMR_SelectSlaveMode(TMR_T* tmr, TMR_SLAVE_MODE_T slaveMode);
该函数的形参描述,如下表所示:
形参 描述
tmr 指向TMR外设结构体的指针
例如:TMR1、TMR2等(在apm32e10x.h文件中有定义)
slaveMode 定时器的从模式功能
例如:TMR_SLAVE_MODE_RESET、TMR_SLAVE_MODE_GATED等(在apm32e10x_tmr.h文件中有定义)
表24.2.1.5 函数TMR_SelectSlaveMode()形参描述
该函数的返回值描述,如下表所示:
返回值 描述
无 无
表24.2.1.6 函数TMR_SelectSlaveMode()返回值描述
该函数的使用示例,如下所示:
#include "apm32e10x.h"
#include "apm32e10x_tmr.h"
void example_fun(void)
{
/* 配置TMR8的从模式功能为复位模式 */
TMR_SelectSlaveMode(TMR8, TMR_SLAVE_MODE_RESET);
}
⑤:使能TMR指定中断
请见第16.2.1小节中使能TMR指定中断的相关内容。
⑥:配置TMR中断
请见第12.2.3小节中配置中断的相关内容。
⑦:使能TMR
请见第16.2.1小节中使能TMR的相关内容。
⑧:读取TMR通道的捕获比较寄存器
该函数用于读取TMR通道的捕获比较寄存器,其函数原型如下所示:
uint16_t TMR_ReadCaputer1(TMR_T* tmr);
uint16_t TMR_ReadCaputer2(TMR_T* tmr);
uint16_t TMR_ReadCaputer3(TMR_T* tmr);
uint16_t TMR_ReadCaputer4(TMR_T* tmr);
该函数的形参描述,如下表所示:
形参 描述
tmr 指向TMR外设结构体的指针
例如:TMR1、TMR2等(在apm32e10x.h文件中有定义)
表24.2.1.7 函数TMR_ReadCaptuern()形参描述
该函数的返回值描述,如下表所示:
返回值 描述
uint32_t类型数据 TMR通道的捕获比较寄存器值
表24.2.1.8 函数TMR_ReadCaptuern()返回值描述
该函数的使用示例,如下所示:
#include "apm32e10x.h"
#include "apm32e10x_tmr.h"
void example_fun(void)
{
uint32_t tmr8_cap_1;
tmr8_cap_1 = TMR_ReadCaputer1(TMR8);
/* Do something. */
}
24.2.2 高级定时器驱动
本章实验的高级定时器驱动主要负责向应用层提供高级定时器的初始化函数,并实现高级定时器的中断服务函数。本章实验中,高级定时器驱动的驱动代码包括atmr.c和atmr.h两个文件。
高级定时器驱动中,对TMR、GPIO相关的宏定义,如下所示:
/* 高级定时器PWM输出引脚定义 */
#define ATMR_TMRX_PWMIN_CHY_GPIO_PORT GPIOC
#define ATMR_TMRX_PWMIN_CHY_GPIO_PIN GPIO_PIN_6
#define ATMR_TMRX_PWMIN_CHY_GPIO_CLK_ENABLE() do{ RCM_EnableAPB2PeriphClock(RCM_APB2_PERIPH_GPIOC); }while(0)
/* 高级定时器定义 */
#define ATMR_TMRX_PWMIN TMR8
#define ATMR_TMRX_PWMIN_IRQn TMR8_UP_IRQn
#define ATMR_TMRX_PWMIN_IRQHandler TMR8_UP_IRQHandler
#define ATMR_TMRX_PWMIN_CHY TMR_CHANNEL_1
#define ATMR_TMRX_PWMIN_CHY_CLK_ENABLE() do{ RCM_EnableAPB2PeriphClock(RCM_APB2_PERIPH_TMR8); }while(0)
#define ATMR_TMRX_PWMIN_CC_IRQn TMR8_CC_IRQn
#define ATMR_TMRX_PWMIN_CC_IRQHandler TMR8_CC_IRQHandler
高级定时器驱动中TMR8的初始化函数,如下所示:
/**
* @brief 初始化高级定时器通道Y与PWM输入模式
* @note
* 高级定时器的时钟来自APB2, 而PCLK2 = 120Mhz, 我们设置PPRE2不分频, 因此
* 高级定时器时钟 = 120Mhz
* 定时器溢出时间计算方法: Tout = ((arr + 1) * (psc + 1)) / Ft us.
* Ft=定时器工作频率,单位:Mhz
*
* 本函数初始化的时候: 使用psc=0, arr固定为65535. 得到采样时钟频率为
72Mhz,精度约13.8ns
*
* @param 无
* @retval 无
*/
void atmr_tmrx_pwmin_init(uint16_t psc)
{
GPIO_Config_T gpio_init_struct;
TMR_BaseConfig_T tmr_init_struct;
TMR_ICConfig_T tmr_ic_init_struct;
ATMR_TMRX_PWMIN_CHY_CLK_ENABLE(); /* 使能高级定时器时钟 */
/* 使能PWM输出引脚时钟 */
ATMR_TMRX_PWMIN_CHY_GPIO_CLK_ENABLE();
/* 配置PWM输入引脚 */
gpio_init_struct.pin = ATMR_TMRX_PWMIN_CHY_GPIO_PIN; /* 通道Y的GPIO口 */
gpio_init_struct.mode = GPIO_MODE_AF_PP; /* 复用功能模式 */
gpio_init_struct.speed = GPIO_SPEED_50MHz; /* 高速 */
GPIO_Config( ATMR_TMRX_PWMIN_CHY_GPIO_PORT, /* 配置PWM输入引脚 */
&gpio_init_struct);
/* 配置高级定时器 */
tmr_init_struct.countMode = TMR_COUNTER_MODE_UP; /* 递增计数模式 */
/* 使能高级定时器AUTORLD进行缓冲 */
tmr_init_struct.clockDivision = TMR_CLOCK_DIV_1;
tmr_init_struct.period = 0XFFFF; /* 自动重装载值 */
tmr_init_struct.division = psc; /* 定时器分频 */
tmr_init_struct.repetitionCounter = 0; /* 重复计数数值 */
TMR_ConfigTimeBase(ATMR_TMRX_PWMIN, &tmr_init_struct);/* 初始化PWM */
/* 配置PWM输入捕获 */
tmr_ic_init_struct.channel = TMR_CHANNEL_1; /* 高级定时器通道1 */
tmr_ic_init_struct.polarity = TMR_IC_POLARITY_FALLING;/* 下降沿捕获 */
/* 输入捕获功能选择 */
tmr_ic_init_struct.selection = TMR_IC_SELECTION_DIRECT_TI;
tmr_ic_init_struct.prescaler = TMR_IC_PSC_1; /* 输入捕获预分频器 */
tmr_ic_init_struct.filter = 0; /* 配置输入滤波器 */
TMR_ConfigPWM(ATMR_TMRX_PWMIN, &tmr_ic_init_struct); /* 配置PWM输入捕获 */
/* 配置从模式 */
/* 输入信号为TI1FP1 */
TMR_SelectInputTrigger(ATMR_TMRX_PWMIN, TMR_TRIGGER_SOURCE_TI1FP1);
/* 复位模式 */
TMR_SelectSlaveMode(ATMR_TMRX_PWMIN, TMR_SLAVE_MODE_RESET);
/* 使能通用定时器及其相关中断 */
NVIC_EnableIRQRequest(ATMR_TMRX_PWMIN_CC_IRQn, 1, 0); /* 使能输入捕获中断 */
TMR_EnableInterrupt(ATMR_TMRX_PWMIN, TMR_INT_CC1); /* 使能CC1中断 */
TMR_EnableInterrupt(ATMR_TMRX_PWMIN, TMR_INT_CC2); /* 使能CC2中断 */
TMR_Enable(ATMR_TMRX_PWMIN); /* 使能通用定时器 */
}
从上面的代码中可以看出,在配置PWM输入模式时,配置了IC1捕获上升沿,IC2捕获下降沿,并同时开启了捕获比较1和2的中断,这么一来就可以在中断回调函数中获取通道1和通道2的捕获比较寄存器值来计算输入PWM信号的周期和占空比了。
高级定时器驱动中,TMR8的中断回调函数,如下所示:
/**
* @brief 高级定时器中断服务函数
* @param 无
* @retval 无
*/
void ATMR_TMRX_PWMIN_CC_IRQHandler(void)
{ /* 判断CC1中断标志 */
if (TMR_ReadIntFlag(ATMR_TMRX_PWMIN, TMR_INT_CC1) == SET)
{
/* 捕获到上升沿 */
/* 获取TMRx_CC1,用于计算输入PWM的周期 */
g_tmrxchy_pwmin_cval = TMR_ReadCaputer1(ATMR_TMRX_PWMIN) + 1;
g_tmrxchy_pwmin_sta = 1; /* 标记捕获完成 */
TMR_ConfigCounter(ATMR_TMRX_PWMIN, 0); /* 清零高级定时器计数值 */
TMR_ClearIntFlag(ATMR_TMRX_PWMIN, TMR_INT_CC1); /* 清除CC1中断标志 */
}
/* 判断CC2中断标志 */
if (TMR_ReadIntFlag(ATMR_TMRX_PWMIN, TMR_INT_CC2) == SET)
{
/* 捕获到下降沿 */
/* 获取TMRx_CC2,用于计算输入PWM的占空比 */
g_tmrxchy_pwmin_hval = TMR_ReadCaputer2(ATMR_TMRX_PWMIN) + 1;
TMR_ClearIntFlag(ATMR_TMRX_PWMIN, TMR_INT_CC2); /* 清除CC2中断标志 */
}
}
从上面的代码中可以看出,在捕获比较通道1中断中获取通道1的捕获比较寄存器值就是TMR8在输入PWM信号的两个上升沿之间的计数值,通过该值可以计算出输入PWM信号的周期;在捕获比较通道2中断中获取通道2的捕获比较寄存器值就是TMR8在输入PWM信号的上升沿和下降沿之间的计数值,通过该值可以计算出输入PWM信号的高电平占空比。
extern uint16_t g_tmrxchy_pwmin_sta; /* PWM输入状态 */
extern uint32_t g_tmrxchy_pwmin_hval; /* PWM的高电平脉宽 */
extern uint32_t g_tmrxchy_pwmin_cval; /* PWM的周期宽度 */
int main(void)
{
uint8_t t = 0;
NVIC_ConfigPriorityGroup(NVIC_PRIORITY_GROUP_4);/* 设置中断优先级分组为组4 */
sys_apm32_clock_init(15); /* 配置系统时钟 */
delay_init(120); /* 初始化延时功能 */
usart_init(115200); /* 初始化串口 */
led_init(); /* 初始化LED */
gtmr_tmrx_pwm_chy_init(500 - 1, 120 - 1); /* 初始化通用定时器通道与PWM */
/* 初始化高级定时器通道Y与PWM输入模式 */
atmr_tmrx_pwmin_init(120 - 1);
while (1)
{
if (g_tmrxchy_pwmin_sta == 1) /* 判断成功捕获标志 */
{
g_tmrxchy_pwmin_sta = 0; /* 清除成功捕获标志 */
printf("高电平时间:%d us\r\n", g_tmrxchy_pwmin_hval);
printf("PWM周期:%d us\r\n", g_tmrxchy_pwmin_cval);
printf("PWM频率:%d us\r\n", 1000000 / g_tmrxchy_pwmin_cval);
printf("\r\n");
}
t++;
if (t >= 20)
{
t = 0;
LED0_TOGGLE();
}
delay_ms(10);
}
}
从上面的代码中可以看出,会配置通用定时器输出一个频率为10KHz、占空比为50%的PWM,该PWM信号用于作为本实验中高级定时器的PWM输入信号。随后会初始化高级定时器TMR8,TMR8的预分频计数器值配置为(120 – 1),并且TMR8的时钟频率为120MHz,因此TMR8的计数频率为1MHz,即1个计数值对应1微秒,方便后面的时间计算。
初始化完成后,就等待高级定时器中断函数中的捕获成功标志为真,捕获成功后,便将捕获到的PWM输入信号的周期和占空比输出值串口调试助手。
24.3 下载验证
在完成编译和烧录操作后,将TMR3通道1输出的PWM信号接入TMR8的PWM输入引脚,即将PA6引脚与PC6引脚进行短接,随后便可在串口调试助手上看到,捕获到PWM输入信号的高电平时间约为5000微秒、PWM周期约为10000微秒,即PWM的占空比为50%,并且也能看到PWM输入信号的频率为10KHz。