天天看點

TM4C123系列(六)————timer定時器(capture邊沿計時實作輸入捕獲PWM頻率和占空比)

首先感謝我姐送來的耳機(血脈壓制,被逼的),再順便給我姐撈個男朋友(誠心)。

TM4C123系列(六)————timer定時器(capture邊沿計時實作輸入捕獲PWM頻率和占空比)
TM4C123系列(六)————timer定時器(capture邊沿計時實作輸入捕獲PWM頻率和占空比)
TM4C123系列(六)————timer定時器(capture邊沿計時實作輸入捕獲PWM頻率和占空比)

一.實驗簡介

通過定時器的capture模式中的邊沿計時模式,實作輸入捕獲PWM的頻率和占空比。

二.闆載定時器介紹(capture-邊沿計時)

定時器子產品可以捕捉上升沿,下降沿,在邊沿到來時記錄計數值,用來測定脈沖間的時間間隔。加計時的計時範圍是從0到使用者的預設值,減計時的範圍為從預設值減到0,預設值可以通過TimerLoadSet函數進行設定。但是在此模式中不需要設定,如果在不設定preload的情況下也沒有啟用預分頻器,那麼預設值将預設為是2^16=0xffff,如果啟用了預分頻器,那麼預設值将變為2^24。

對于加計時來說,加到preload後将會重置為0重複之前的工作,減計時也是,減到0後會重置到preload然後重複之前工作。

每當邊沿到來,定時器子產品會産生中斷并記錄目前計時值。可以通過計時值計算頻率,占空比。

注意輸入捕獲隻能在定時器拆分的情況下使用。

三.引腳圖

TM4C123系列(六)————timer定時器(capture邊沿計時實作輸入捕獲PWM頻率和占空比)

 使用PF2作為PWM輸出引腳,使用PB6作為輸入捕獲的引腳。

四.所需函數

有關序列槽,GPIO,PWM還有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)
	{

	}
}