TM4C123系列(六)————timer定时器(capture边沿计时实现输入捕获PWM频率和占空比)

首先感谢我姐送来的耳机(血脉压制,被逼的),再顺便给我姐捞个男朋友(诚心)。

一.实验简介

通过定时器的capture模式中的边沿计时模式,实现输入捕获PWM的频率和占空比。

二.板载定时器介绍(capture-边沿计时)

定时器模块可以捕捉上升沿,下降沿,在边沿到来时记录计数值,用来测定脉冲间的时间间隔。加计时的计时范围是从0到用户的预设值,减计时的范围为从预设值减到0,预设值可以通过TimerLoadSet函数进行设置。但是在此模式中不需要设置,如果在不设置preload的情况下也没有启用预分频器,那么预设值将默认为是2^16=0xffff,如果启用了预分频器,那么预设值将变为2^24。

对于加计时来说,加到preload后将会重置为0重复之前的工作,减计时也是,减到0后会重置到preload然后重复之前工作。

每当边沿到来,定时器模块会产生中断并记录当前计时值。可以通过计时值计算频率,占空比。

注意输入捕获只能在定时器拆分的情况下使用。

三.引脚图

 使用PF2作为PWM输出引脚,使用PB6作为输入捕获的引脚。

四.所需函数

有关串口GPIOPWM还有timer中已经用过的函数不在提起,有问题的可以去看我之前的博客

1.GPIOPinTypeTimer(uint32_t ui32Port, uint8_t ui8Pins)

参数:ui32Port为GPIO基地址,ui8Pins为外设引脚

作用:分配定时器信号

2.TimerControlEvent(uint32_t ui32Base, uint32_t ui32Timer,uint32_t ui32Event)

参数:ui32Base为定时器基地址,ui32Timer为定时器中的模块,ui32Event为捕捉边沿

作用:设置输入捕获的捕捉边沿

3.TimerValueGet(uint32_t ui32Base, uint32_t ui32Timer)

参数:ui32Base为定时器基地址,ui32Timer为定时器中的模块

作用:获取特定引脚的计时值。

4.TimerIntEnable(uint32_t ui32Base, uint32_t ui32IntFlags)

参数:ui32Base为定时器基地址,ui32IntFlags为中断模式

//! - \b TIMER_TIMB_DMA - Timer B uDMA complete
//! - \b TIMER_TIMA_DMA - Timer A uDMA complete
//! - \b TIMER_CAPB_EVENT  - Capture B event interrupt
//! - \b TIMER_CAPB_MATCH  - Capture B match interrupt
//! - \b TIMER_TIMB_TIMEOUT  - Timer B timeout interrupt
//! - \b TIMER_RTC_MATCH  - RTC interrupt mask
//! - \b TIMER_CAPA_EVENT  - Capture A event interrupt
//! - \b TIMER_CAPA_MATCH  - Capture A match interrupt
//! - \b TIMER_TIMA_TIMEOUT  - Timer A timeout interrupt

 如果是普通定时器模式就设置为TIMOUT,如果是capture的边沿计数就是match,如果是边沿计时就是EVENT

作用:使能中断模式

五.代码及讲解

usart.c
#include "usart.h"
#include "uart.h"
#include "gpio.h"
#include "sysctl.h"
#include "pin_map.h"
#include "hw_memmap.h"
#include "uartstdio.h"
//配置串口,有问题的去看我之前博客
void USART_Config(void)
{
    SysCtlPeripheralEnable( SYSCTL_PERIPH_GPIOA);
	SysCtlPeripheralEnable( SYSCTL_PERIPH_UART0);
	GPIOPinConfigure( GPIO_PA0_U0RX);
	GPIOPinConfigure( GPIO_PA1_U0TX);
	GPIOPinTypeUART( GPIO_PORTA_BASE,  GPIO_PIN_0);
	GPIOPinTypeUART( GPIO_PORTA_BASE,  GPIO_PIN_1);
	UARTClockSourceSet( UART0_BASE,  UART_CLOCK_PIOSC);
	UARTStdioConfig( 0,  115200,
                             16000000);
}
usart.h
#ifndef __USART_H
#define __USART_H
void USART_Config(void);
#endif
pwm_out.c
#include "gpio.h"
#include "pin_map.h"
#include "pwm_out.h"
#include "sysctl.h"
#include "pwm.h"
#include "hw_memmap.h"
//配置PWM输出,有问题的看我之前博客
void PWM_OUT_Config(void)
{
    SysCtlPWMClockSet( SYSCTL_PWMDIV_4);
    SysCtlPeripheralEnable( SYSCTL_PERIPH_GPIOF);
	SysCtlPeripheralEnable( SYSCTL_PERIPH_PWM1);
	GPIOPinTypePWM( GPIO_PORTF_BASE,  GPIO_PIN_2);
	GPIOPinConfigure( GPIO_PF2_M1PWM6);
	PWMGenConfigure( PWM1_BASE,  PWM_GEN_3,
                             PWM_GEN_MODE_DOWN|PWM_GEN_MODE_NO_SYNC);
	PWMGenPeriodSet( PWM1_BASE,  PWM_GEN_3,
                             2000);
	PWMPulseWidthSet( PWM1_BASE,  PWM_OUT_6,
                              PWMGenPeriodGet(PWM1_BASE, PWM_GEN_3)*0.5 - 1);
	PWMOutputState( PWM1_BASE,  PWM_OUT_6_BIT,
                            true);
	PWMGenEnable( PWM1_BASE,  PWM_GEN_3);
}
pwm_out.h
#ifndef __PWM_OUT_H
#define __PWM_OUT_H
void PWM_OUT_Config(void);
#endif
pwm_in.c
#include "pwm_in.h"
#include "gpio.h"
#include "sysctl.h"
#include "hw_memmap.h"
#include "pin_map.h"
#include "hw_ints.h"
#include "uartstdio.h"
#include "timer.h"
_Bool flag=0;
uint32_t zhouqi=0;
uint32_t freq=0;
_Bool uart_flag=0;
_Bool led_flag=0;
 uint32_t capture_1=0,capture_2=0,capture_3=0;
 uint32_t timer_flag=0;
 uint32_t duty=0;
 uint32_t up_count=0,down_count=0;
void PWM_IN_IRQHandler(void);
void TIMER_WID_IRQHandler(void);
//配置输入捕获引脚
void PWM_IN_Config(void)
{
    //使能定时器与GPIO
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB);
	SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER0);
    //使能引脚复用
	GPIOPinConfigure(GPIO_PB6_T0CCP0);
    //分配引脚信号
	GPIOPinTypeTimer(GPIO_PORTB_BASE, GPIO_PIN_6);
    //设置引脚方向,注意虽然这是输入捕获,但是这是由外界的PWM信号控制,属于硬件控制,要设置为GPIO_DIR_MODE_HW
	GPIODirModeSet( GPIO_PORTB_BASE,  GPIO_PIN_6,
                            GPIO_DIR_MODE_HW);
    //设置为推挽上拉输入
	GPIOPadConfigSet( GPIO_PORTB_BASE,  GPIO_PIN_6,
                              GPIO_STRENGTH_2MA,  GPIO_PIN_TYPE_STD_WPU);
    //因为是输入捕获,只有在拆分模式下可以用输入捕获,所以将TIMER_CFG_SPLIT_PAIR与A周期计数进行或运算
	TimerConfigure( TIMER0_BASE,  TIMER_CFG_SPLIT_PAIR|TIMER_CFG_A_CAP_TIME_UP);
	//设置为上升沿触发
	TimerControlEvent( TIMER0_BASE,  TIMER_A,
                               TIMER_EVENT_POS_EDGE);
    //注册中断函数
	TimerIntRegister( TIMER0_BASE,  TIMER_A,
                             PWM_IN_IRQHandler);
    //设置中断优先级
	IntPrioritySet( INT_TIMER0A,
                            0);
    //使能定时器中断的计时中断
	TimerIntEnable( TIMER0_BASE,  TIMER_CAPA_EVENT);
    //使能中断
	IntEnable( INT_TIMER0A);
	IntMasterEnable();
	TimerEnable( TIMER0_BASE,  TIMER_A);
    /*设置装载值,在边沿计时模式下可以省略,会自己填入默认值。
    如果设置了预分频值,那么默认装载值就是2^24,如果没有预分频值,那么默认装载值就是2^16。
    相当于STM32中使用了oc——toggle模式,默认预装载值填写65535*/
    //TimerLoadSet(TIMER0_BASE, TIMER_A, Capture_LoadSet);
}
//输入捕获中断
void PWM_IN_IRQHandler(void)
{
    //读取中断状态
    uint32_t status=TimerIntStatus( TIMER0_BASE,  true);
    //清除中断标志位
	TimerIntClear( TIMER0_BASE,  status);
    //第一次进中断是由于检测到了上升沿,然后将计时值读取,并将边沿检测变为下降沿
	if(timer_flag==0)
	{
		TimerControlEvent( TIMER0_BASE,  TIMER_A,
                               TIMER_EVENT_NEG_EDGE);
		capture_1=TimerValueGet( TIMER0_BASE,  TIMER_A);
		timer_flag=1;
	}
    //第二次进中断是因为检测到了下降沿,然后将计时值读取,这时就已经获得了高电平数,
    //可以计算出占空比,并将边沿检测变为上升沿
	else if(timer_flag==1)
	{
		TimerControlEvent( TIMER0_BASE,  TIMER_A,
                               TIMER_EVENT_POS_EDGE);
		capture_2=TimerValueGet( TIMER0_BASE,  TIMER_A);
		timer_flag=2;
	}
    //第三次进中断时因为检测到了上升沿,至此,已经检测到了两个上升沿,也就可以得到周期值
	else if(timer_flag==2)
	{
	    timer_flag=0;
		capture_3=TimerValueGet( TIMER0_BASE,  TIMER_A);
        /* ____   ___ capture_1相当于检测到第一个上升沿记的数,
           |  |   |   capture_2相当于检测到第一个下降沿记的数
          _|  |___|   所以capture_2与capture_1之间即为高电平数
        capture_3相当于检测到第二个上升沿记的数,所以capture_3与capture_1之间为周期数*/
        /*  /|   /| 现在要求占空比和频率,因为设置的是定时器A周期性加计数,
           / |  / | 定时器A记的数先到最大然后再从0开始计数,现在通过求高电平和低电平时间来计算
          /  | /  | 如果capture_1与capture_2都在第一个计数周期的上升阶段,那1与2的差就是高电平
         /   |/   | 如果1与2分别落在两个周期的上升阶段,那高电平就要通过0xffff-capture_1+capture_2获得。
        如果capture_2与capture_3都在第一个计数周期的上升阶段,那2与3的差就是低电平,
        如果分别落在两个周期上升阶段,低电平就要通过0xffff-capture_2+capture_3来获得*/
		if(capture_2>capture_1)
		{
		    up_count=capture_2-capture_1;
		}
		else
		{
		    up_count=0xffff-capture_1+capture_2;
		}
		if(capture_3>capture_2)
		{
		    down_count=capture_3-capture_2;
		}
		else 
		{
		    down_count=0xffff-capture_2+capture_3;
		}
        //频率用主频除周期即可得到
		freq=SysCtlClockGet()/(up_count+down_count);
        //占空比为高电平占周期的比值即可得到
		duty=up_count*100/(up_count+down_count);
	}
}

void Timer_Wid_Config(void)
{
    //使能32/64bit定时器
    SysCtlPeripheralEnable(SYSCTL_PERIPH_WTIMER0);
    //使能复用
	TimerConfigure(WTIMER0_BASE,TIMER_CFG_PERIODIC_UP);
    //设置预装载值,使1s进一次中断
	TimerLoadSet64( WTIMER0_BASE,  SysCtlClockGet()/1000-1);
	//使能定时器A超时中断
	TimerIntEnable( WTIMER0_BASE,  TIMER_TIMA_TIMEOUT);
    //注册中断函数
	TimerIntRegister( WTIMER0_BASE,  TIMER_A,
                             TIMER_WID_IRQHandler);
    //设置优先级
	IntPrioritySet( INT_WTIMER0A,  1);
    //设置中断
	IntEnable( INT_WTIMER0A);
	IntMasterEnable();
	TimerEnable( WTIMER0_BASE,  TIMER_A);
}
void TIMER_WID_IRQHandler(void)
{
    static uint32_t time_count=0;
	
    uint32_t status=TimerIntStatus( WTIMER0_BASE,  true);
	TimerIntClear( WTIMER0_BASE,  status);
	
	time_count++;
    //1s打印一次,但是不知道为什么用UARTprintf打印一次就不打印了,我还在改进,但是输入捕获那块是没问题的
	if(time_count==1000)
	{
	    time_count=0;
		UARTprintf("duty:%d", duty);
        UARTprintf("freq:%d", freq);
	}
}
pwm_in.h
#ifndef __PWM_IN_H
#define __PWM_IN_H
#include <stdint.h>
extern _Bool uart_flag;
extern uint32_t zhouqi;
extern uint32_t freq;
extern _Bool led_flag;
void PWM_IN_Config(void);
void PWM_IN_IRQHandler(void);
void Timer_Wid_Config(void);
void TIMER_WID_IRQHandler(void);
#endif
main.c
#include "tm4c123gh6pm.h"
#include <stdint.h>
#include <stdbool.h>
#include "inc/hw_sysctl.h"
#include "inc/hw_gpio.h"
#include "inc/hw_memmap.h"
#include "driverlib/gpio.h"
#include "driverlib/sysctl.h"
#include "pwm_in.h"
#include "pwm_out.h"
#include "usart.h"
#include "uartstdio.h"
void main(void)
{
	SysCtlClockSet( SYSCTL_SYSDIV_5|SYSCTL_USE_PLL|SYSCTL_XTAL_16MHZ|SYSCTL_OSC_MAIN);
	PWM_OUT_Config();
	PWM_IN_Config();
    USART_Config();
	Timer_Wid_Config();
	while(1)
	{

	}
}

  • 12
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 8
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值