【正点原子STM32连载】第二十四章 高级定时器PWM输入模式实验 摘自【正点原子】APM32F407最小系统板使用指南

1)实验平台:正点原子stm32f103战舰开发板V4
2)平台购买地址:https://detail.tmall.com/item.htm?id=609294757420
3)全套实验源码+手册+视频下载地址: http://www.openedv.com/thread-340252-1-1.html#

第二十四章 高级定时器PWM输入模式实验

本章将介绍使用APM32F407的高级定时器检测输入PWM的占空比和周期。通过本章的学习,读者将学习到高级定时器PWM输入模式的使用。
本章分为如下几个小节:
24.1 硬件设计
24.2 程序设计
24.3 下载验证

24.1 硬件设计

24.1.1 例程功能

  1. 利用定时器8的通道1(PC6)捕获定时器14通道1(PA7)输出的占空比为50%,频率为100Hz的PWM,结果通过串口输出
  2. LED0闪烁,指示程序正在运行
    24.1.2 硬件资源
  3. LED
    LED1 - PF9
  4. 定时器14
    通道1 - PA7
  5. 定时器8
    通道1 - PC6
  6. USART1(PA9、PA10连接至板载USB转串口芯片上)
    24.1.3 原理图
    本章实验使用的定时器8为APM32F407的片上资源,因此没有对应的连接原理图。
    24.2 程序设计
    24.2.1 Geehy标准库的TMR驱动
    本章实验将使用TMR8的通道1(PC6引脚)在PWM输入模式下捕获TMR14从通道1(PA7)引脚输出的PWM(有关通用定时器输出PWM的相关内容,请见第十八章“通用定时器PWM输出实验”),将分别捕获输入PWM信号的上升沿和下降沿,以此来计算输入PWM信号的占空比和周期,要是实现以上功能都依赖于高级定时器的PWM输入模式,其具体的配置步骤如下:
    ①:配置TMR8的自动重装载值和预分频器数值等参数
    ②:配置TMR8通道1的PWM输入模式
    ③:配置TMR8的触发输入信号
    ④:配置TMR8的从模式功能为复位模式
    ⑤:使能TMR8的捕获比较通道1和2中断
    ⑥:使能TMR8中断,并配置其相关的中断优先级
    ⑦:使能TMR8
    ⑧:读取TMR8通道1和通道2的捕获比较寄存器
    在Geehy标准库中对应的驱动函数如下:
    ①:配置TMR
    请见第16.2.1小节中配置TMR的相关内容。
    ②:配置PWM输入模式
    该函数用于配置PWM输入模式,其函数原型如下所示:
    void TMR_ConfigPWM(TMR_T* tmr, TMR_ICConfig_T* PWMConfig);
    该函数的形参描述,如下表所示:
    形参 描述
    tmr 指向TMR外设结构体的指针
    例如:TMR1、TMR2等(在apm32f4xx.h文件中有定义)
    PWMConfig 指向PWM输入模式配置结构体的指针
    需自行定义,并根据PWM输入模式配置参数填充结构体中的成员变量
    表24.2.1.1 函数TMR_ConfigPWM()形参描述
    该函数的返回值描述,如下表所示:
    返回值 描述
    无 无
    表24.2.1.2 函数TMR_ConfigPWM()返回值描述
    该函数使用TMR_ICConfig_T类型的结构体变量传入PWM输入模式的配置参数,该结构体的定义请见第19.2.1小节中TMR_ICConfig_T的相关内容。
    该函数的使用示例,如下所示:
#include "apm32f4xx.h"
#include "apm32f4xx_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等(在apm32f4xx.h文件中有定义)
triggerSource 定时器的触发输入信号源
例如:TMR_TRIGGER_SOURCE_ITR0、TMR_TRIGGER_SOURCE_TI1FP1等(在apm32f4xx_tmr.h文件中有定义)
表24.2.1.3 函数TMR_SelectInputTrigger()形参描述
该函数的返回值描述,如下表所示:
返回值 描述
无 无
表24.2.1.4 函数TMR_SelectInputTrigger()返回值描述
该函数的使用示例,如下所示:

#include "apm32f4xx.h"
#include "apm32f4xx_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等(在apm32f4xx.h文件中有定义)
slaveMode 定时器的从模式功能
例如:TMR_SLAVE_MODE_RESET、TMR_SLAVE_MODE_GATED等(在apm32f4xx_tmr.h文件中有定义)
表24.2.1.5 函数TMR_SelectSlaveMode()形参描述
该函数的返回值描述,如下表所示:
返回值 描述
无 无
表24.2.1.6 函数TMR_SelectSlaveMode()返回值描述
该函数的使用示例,如下所示:

#include "apm32f4xx.h"
#include "apm32f4xx_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通道的捕获比较寄存器,其函数原型如下所示:

uint32_t TMR_ReadCaputer1(TMR_T* tmr);
uint32_t TMR_ReadCaputer2(TMR_T* tmr);
uint32_t TMR_ReadCaputer3(TMR_T* tmr);
uint32_t TMR_ReadCaputer4(TMR_T* tmr);

该函数的形参描述,如下表所示:
形参 描述
tmr 指向TMR外设结构体的指针
例如:TMR1、TMR2等(在apm32f4xx.h文件中有定义)
表24.2.1.7 函数TMR_ReadCaptuern()形参描述
该函数的返回值描述,如下表所示:
返回值 描述
uint32_t类型数据 TMR通道的捕获比较寄存器值
表24.2.1.8 函数TMR_ReadCaptuern()返回值描述
该函数的使用示例,如下所示:

#include "apm32f4xx.h"
#include "apm32f4xx_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相关的宏定义,如下所示:

#define ATMR_TMRX_PWMIN					TMR8
#define ATMR_TMRX_PWMIN_CC_IRQn			TMR8_CC_IRQn
#define ATMR_TMRX_PWMIN_CC_IRQHandler	TMR8_CC_IRQHandler
#define ATMR_TMRX_PWMIN_CHY				TMR_CHANNEL_1
#define ATMR_TMRX_PWMIN_CLK_ENABLE()						\
    do {														\
    		RCM_EnableAPB2PeriphClock(RCM_APB2_PERIPH_TMR8);	\
    } while (0)

#define ATMR_TMRX_PWMIN_CHY_GPIO_PORT			GPIOC
#define ATMR_TMRX_PWMIN_CHY_GPIO_PIN			GPIO_PIN_6
#define ATMR_TMRX_PWMIN_CHY_GPIO_PIN_SOURCE	GPIO_PIN_SOURCE_6
#define ATMR_TMRX_PWMIN_CHY_GPIO_AF			GPIO_AF_TMR8
#define ATMR_TMRX_PWMIN_CHY_GPIO_CLK_ENABLE()				\
    do {														\
    		RCM_EnableAHB1PeriphClock(RCM_AHB1_PERIPH_GPIOC);	\
    } while (0)
高级定时器驱动中TMR8的初始化函数,如下所示:
/**
 * @brief	初始化高级定时器PWM输入模式
 * @note	当APB1PSC!=1时,定时器的时钟频率为APB2时钟的2倍
 * 			因此定时器的时钟频率为168MHz
 * 			定时器溢出时间计算方法:Tout = ((arr + 1) * (psc + 1)) / TMRxCLK
 * 			TMRxCLK=定时器时钟频率,单位MHz
 * @param	psc: 预分频器数值
 * @retval	无
 */
void atmr_tmrx_pwmin_chy_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_CLK_ENABLE();			/* 使能高级定时器时钟 */
    ATMR_TMRX_PWMIN_CHY_GPIO_CLK_ENABLE();	/* 使能PWM输入引脚端口时钟 */
    
    /* 配置PWM输入引脚 */
    gpio_init_struct.pin	= ATMR_TMRX_PWMIN_CHY_GPIO_PIN;	/* PWM输入引脚 */
    gpio_init_struct.mode	= GPIO_MODE_AF;					/* 复用功能模式 */
    gpio_init_struct.speed	= GPIO_SPEED_100MHz;			/* 高速 */
    gpio_init_struct.otype	= GPIO_OTYPE_PP;				/* 推挽输出 */
    gpio_init_struct.pupd	= GPIO_PUPD_DOWN;				/* 下拉 */
    GPIO_Config(ATMR_TMRX_PWMIN_CHY_GPIO_PORT, &gpio_init_struct);
    GPIO_ConfigPinAF(ATMR_TMRX_PWMIN_CHY_GPIO_PORT,			/* 配置引脚复用功能 */
    ATMR_TMRX_PWMIN_CHY_GPIO_PIN_SOURCE,
    ATMR_TMRX_PWMIN_CHY_GPIO_AF);
    
    /* 配置高级定时器 */
    tmr_init_struct.countMode			= TMR_COUNTER_MODE_UP;	/* 向上计数 */
    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输入模式 */
    tmr_ic_init_struct.channel		= ATMR_TMRX_PWMIN_CHY;
    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(ATMR_TMRX_PWMIN, &tmr_ic_init_struct);
    
    /* 配置从模式 */
    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;
    		
    		/* 清除CC1中断标志 */
    		TMR_ClearIntFlag(ATMR_TMRX_PWMIN, TMR_INT_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;
    		
    		/* 清除CC2中断标志 */
    		TMR_ClearIntFlag(ATMR_TMRX_PWMIN, TMR_INT_CC2);
    }
}

从上面的代码中可以看出,在捕获比较通道1中断中获取通道1的捕获比较寄存器值就是TMR8在输入PWM信号的两个上升沿之间的计数值,通过该值可以计算出输入PWM信号的周期;在捕获比较通道2中断中获取通道2的捕获比较寄存器值就是TMR8在输入PWM信号的上升沿和下降沿之间的计数值,通过该值可以计算出输入PWM信号的高电平占空比。
24.2.3 实验应用代码
本章实验的应用代码,如下所示:

int main(void)
{
    uint8_t t = 0;
    
    NVIC_ConfigPriorityGroup(NVIC_PRIORITY_GROUP_3);	/* 设置中断优先级分组为组3 */
    sys_apm32_clock_init(336, 8, 2, 7);					/* 配置系统时钟 */
    delay_init(168);										/* 初始化延时功能 */
    usart_init(115200);									/* 初始化串口 */
    led_init();											/* 初始化LED */
    /* 初始化通用定时器PWM输出,频率为100Hz */
    gtmr_tmrx_pwm_chy_init(100 - 1, 8400 - 1);
    /* 初始化高级定时器PWM输入模式,计数频率为1MHz */
    atmr_tmrx_pwmin_chy_init(168 - 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 Hz\r\n", 1000000 / g_tmrxchy_pwmin_cval);
    			printf("\r\n");
    		}
    		
    		t++;
    		if (t >= 20)
    		{
    			t = 0;
    			LED0_TOGGLE();
    		}
    		
    		delay_ms(10);
    }
}

从上面的代码中可以看出,会配置通用定时器输出一个频率为100Hz、占空比为50%的PWM,该PWM信号用于作为本实验中高级定时器的PWM输入信号。随后会初始化高级定时器TMR8,TMR8的预分频计数器值配置为(168-1),并且TMR8的时钟频率为168MHz,因此TMR8的计数频率为1MHz,即1个计数值对应1微秒,方便后面的时间计算。
初始化完成后,就等待高级定时器中断函数中的捕获成功标志为真,捕获成功后,便将捕获到的PWM输入信号的周期和占空比输出值串口调试助手。
24.3 下载验证
在完成编译和烧录操作后,将TMR14通道1输出的PWM信号接入TMR8的PWM输入引脚,即将PA7引脚与PC6引脚进行短接,随后便可在串口调试助手上看到,捕获到PWM输入信号的高电平时间为5000微秒、PWM周期为10000微秒,即PWM的占空比为50%,并且也能看到PWM输入信号的频率为100Hz。

你可能感兴趣的:(stm32,嵌入式硬件)