首先感謝我姐送來的耳機(血脈壓制,被逼的),再順便給我姐撈個男朋友(誠心)。
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLicWZwpmLwYTNyEDZ2YTN4IzM3UjZ5cTOmRjZ2YTZyYTYhNGZwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.jpeg)
一.實驗簡介
通過定時器的capture模式中的邊沿計時模式,實作輸入捕獲PWM的頻率和占空比。
二.闆載定時器介紹(capture-邊沿計時)
定時器子產品可以捕捉上升沿,下降沿,在邊沿到來時記錄計數值,用來測定脈沖間的時間間隔。加計時的計時範圍是從0到使用者的預設值,減計時的範圍為從預設值減到0,預設值可以通過TimerLoadSet函數進行設定。但是在此模式中不需要設定,如果在不設定preload的情況下也沒有啟用預分頻器,那麼預設值将預設為是2^16=0xffff,如果啟用了預分頻器,那麼預設值将變為2^24。
對于加計時來說,加到preload後将會重置為0重複之前的工作,減計時也是,減到0後會重置到preload然後重複之前工作。
每當邊沿到來,定時器子產品會産生中斷并記錄目前計時值。可以通過計時值計算頻率,占空比。
注意輸入捕獲隻能在定時器拆分的情況下使用。
三.引腳圖
使用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)
{
}
}