定時器可以使能位PWM模式,當然也可以使能爲PWM波的輸入捕獲!按鍵可以輸入脈衝,正好對應的是定時器TIM5的通道1,輸入捕獲模式可以用來測量脈衝寬度或者測量頻率。
一句話概括
**通過檢測定時器某個通道上的邊沿信號,在邊沿信號發生跳變(比如上升沿/下降沿)的時候,將當前定時器的值(TIMx_CNT)存放到對應的捕獲/比較寄存器(TIMx_CCRx)裏面,完成一次捕獲。**同時還可以配置捕獲時是否觸發中斷/DMA 等。
目的:
用 TIM5 的通道 1(PA0)來做輸入捕獲,捕獲 PA0 上高電平的脈寬(用 WK_UP 按鍵輸入高電平),通過串口打印高電平脈寬時間
算法流程
我們用到 TIM5_CH1 來捕獲高電平脈寬,也就是要先設置輸入捕獲爲上升沿檢測,記錄發生上升沿的時候 TIM5_CNT 的值。然後配置捕獲信號爲下降沿捕獲,當下降沿到來時,發生捕獲,並記錄此時的 TIM5_CNT 值。這樣,前後兩次 TIM5_CNT 之差,就是高電平的脈寬,同時 TIM5 的計數週期已知,從而可以計算出高電平脈寬的準確持續時間。
捕獲/比較模式寄存器 1:TIMx_CCMR1
TIMx_CCMR1寄存器可以配置通道1和2,低八位[7:0]用於捕獲/比較通道 1 的控制。
0-1:位控制輸入輸出的方向及輸入腳的選擇,我們配置值爲01,爲輸入模式,IC1映射到TI1上,由下圖可知,當然還有一個選擇就是IC2映射到TI1上。平時就默認IC1映射到TI1上。
我們用到的是IC1
2-3:輸入捕獲預分頻器
值00:不分頻,每次邊沿都觸發捕獲
值01:每兩個邊沿出現觸發捕獲
值10:每四個邊沿出現觸發捕獲
值11:每八個邊沿出現觸發捕獲
我們用到的是:00
4-7:輸入捕獲濾波器
0000:無慮波,每次採樣觸發一次都會產生一個輸出的跳變
0001:每兩次採樣觸發一次都會產生一個輸出的跳變
等等;
我們用到的是:0000
捕獲/比較使能寄存器:TIMx_CCER
我們要用到這個寄存器的最低 2 位,CC1E 和 CC1P 位。
所以,要使能輸入捕獲,必須設置 CC1E=0,而 CC1P 則根據自己的需要來配置。
初始化寄存器流程:
(1):使能GPIOA,定時器5時鐘
(2):初始化定時器,得出溢出時間,GPIO及中斷,
(3):初始化定時器爲輸入捕獲模式,映射通道1到IC1
(4):開啓更新中斷和捕獲中斷,編寫中斷服務函數
如果捕獲的是高電平信號的脈寬,那第一次捕獲是上升沿,第二次捕獲時下降沿,必須在捕獲上升沿之後,設置捕獲邊沿爲下降沿,同時,如果脈寬比較長,那麼定時器就會溢出,對溢出必須做處理。這兩件事,我們都在中斷裏面做,所以必須開啓捕獲中斷和更新中斷。
(5):開啓定時器
輸入捕獲頭文件timepwm.h
#ifndef TIMEPWM_H
#define TIMEPWM_H
#include "sys.h"
void timepwm_init(u16 arr,u16 prer);
#endif
輸入捕獲源文件timepwm.c
#include "timepwm.h"
#include "sys.h"
#include "delay.h"
#include "led.h"
#include "usart.h"
void timepwm_init(u16 arr,u16 prer)
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_InitStructure;
TIM_ICInitTypeDef TIM_ICInitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5, ENABLE);
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPD;
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
GPIO_ResetBits(GPIOA,GPIO_Pin_0);
TIM_InitStructure.TIM_ClockDivision=TIM_CKD_DIV1;
TIM_InitStructure.TIM_CounterMode=TIM_CounterMode_Up;
TIM_InitStructure.TIM_Period=arr;
TIM_InitStructure.TIM_Prescaler=prer;
TIM_TimeBaseInit(TIM5, &TIM_InitStructure);
TIM_ICInitStructure.TIM_Channel=TIM_Channel_1;
TIM_ICInitStructure.TIM_ICFilter=0x00;
TIM_ICInitStructure.TIM_ICPolarity=TIM_ICPolarity_Rising;
TIM_ICInitStructure.TIM_ICPrescaler= TIM_ICPSC_DIV1;
TIM_ICInitStructure.TIM_ICSelection= TIM_ICSelection_DirectTI;
TIM_ICInit(TIM5, &TIM_ICInitStructure);
NVIC_InitStructure.NVIC_IRQChannel= TIM5_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2;
NVIC_InitStructure.NVIC_IRQChannelSubPriority=0;
NVIC_Init(&NVIC_InitStructure);
TIM_ITConfig( TIM5,TIM_IT_Update|TIM_IT_CC1,ENABLE);
TIM_Cmd(TIM5,ENABLE );
}
unsigned char status;
short int value;
void TIM5_IRQHandler(void)
{
if((status&0x80)==0)
{
if(TIM_GetITStatus(TIM5, TIM_IT_Update) != RESET)
{
if(status&0x40)
{
if((status&0x3f)==0x3f)
{
status|=0x80;
value=0xffff;
}
else
status++;
}
}
if(TIM_GetITStatus(TIM5, TIM_IT_CC1) != 0)
{
if(status&0x40)
{
status|=0x80;
value=TIM_GetCapture1(TIM5);
TIM_OC1PolarityConfig(TIM5,TIM_ICPolarity_Rising);
}
else
{
status=0;
value=0;
TIM_SetCounter(TIM5,0);
status|=0x40;
TIM_OC1PolarityConfig(TIM5,TIM_ICPolarity_Falling);
}
}
}
TIM_ClearITPendingBit(TIM5, TIM_IT_CC1|TIM_IT_Update);
}
主函數main.c
#include "delay.h"
#include "sys.h"
#include "usart.h"
#include "timer.h"
#include "stdio.h"
#include "timepwm.h"
extern unsigned char status;
extern short int value;
**#if 1//添加printf輸出到串口
#pragma import(__use_no_semihosting)
struct __FILE
{
int handle;
};
FILE __stdout;
_sys_exit(int x)
{
x = x;
}
int fputc(int ch, FILE *f)
{
while(USART_GetFlagStatus(USART1,USART_FLAG_TC)==RESET);
USART_SendData(USART1,(uint8_t)ch);
return ch;
}
#endif**
int main(void)
{
u32 temp;
delay_init();
LED_Init();
usart_init(115200);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
timepwm_init(0xFFFF,71);
while(1)
{
delay_ms(10);
if(status&0x80)
{
temp=(status&0x3f);
temp*=65536;
temp+=value;
printf("temp:%d",temp);
status=0;
}
}
}
這段引入 printf 函數支持的代碼在 usart.h 頭文件的最上方,這段代碼加入之後便可以通過printf 函數向串口發送我們需要的內容,方便開發過程中查看代碼執行情況以及一些變量值。這段代碼不需要修改,引入到 usart.h 即可。
計算
總時間計算:
首先我們設置定時器的頻率爲72MHz/prer:72M/72=1MHz,週期爲1/f=1us,定時器的位數爲16位,最大溢出時計數2的16次方爲65535,次數爲65536,溢出時間爲65536us,根據當前計數值,加上溢出次數的總和,可算出總時間值;
中斷函數思路
定義一個八位的狀態變量,定義一個十六位的定時器變量存儲定時器值
更新中斷和捕獲中斷都是定時器中斷,我們要設置優先級及中斷分組,一旦進入中斷函數,有可能是捕獲中斷也可能是更新中斷,所以進入中斷函數先判斷中斷類型。
void TIM5_IRQHandler(void)//**程序文字化,進入中斷函數**
{
if(脈衝接收完成)
{
if(如果是更新中斷)
{
if(bit6爲1)//也就是說接收到了上升沿
{
if(溢出次數達到上限0x3f)
{
bit7標誌位賦值爲1;
計數值賦值爲最大0xffff;
}
else
次數加1;
}
}
if(如果是捕獲中斷)
{
if(判斷bit6爲1)//這次來的應該是下降沿
{
記下當前定時器值;
bit7賦值爲1,表示捕獲結束;
改變爲下次上升沿捕獲;
}
else//這次來的是上升沿,第一次捕獲
{
定時器置零,方便後面時間計算;
十六位變量值清零;
bit6賦值爲1;
改變捕極性爲下降沿;
}
}
}
清除中斷標誌位;
}
希望我的理解可以爲讀者解決問題,也請大家有什麼好的方法或者算法可以在留言下方給出,謝謝,相互學習!