基於STM32F103ZET6實現輸入捕獲

定時器可以使能位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;
						        改變捕極性爲下降沿;
						  }
			  }
}
清除中斷標誌位;
}

希望我的理解可以爲讀者解決問題,也請大家有什麼好的方法或者算法可以在留言下方給出,謝謝,相互學習!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章