当前位置:网站首页>STM32學習記錄:輸入捕獲應用
STM32學習記錄:輸入捕獲應用
2022-07-06 15:23:00 【Bitter tea seeds】
目錄
前言
介紹通用定時器作為輸入捕獲的使用。用 TIM5 的通道 1(PA0)來做輸入捕獲,捕獲 PA0 上高電平的脈寬(用 WK_UP 按鍵輸入高電平),通過串口打印高電平脈寬時間。
一、輸入捕獲的應用
輸入捕獲一般應用在兩個方面,一個方面是脈沖跳變沿時間測量,另一方面是 PWM 輸入測量。
輸入捕獲模式可以用來測量脈沖寬度或者測量頻率。STM32 的定時器,除了 TIM6 和 TIM7, 其他定時器都有輸入捕獲功能。
STM32 的輸入捕獲,就是通過檢測 TIMx_CHx 上的邊沿信號,在邊沿信號發生跳變(比如上昇沿/下降沿)的時候,將當前定時器的值(TIMx_CNT) 存放到對應的通道的捕獲/比較寄存器(TIMx_CCRx)裏面,完成一次捕獲。同時還可以配置捕獲時是否觸發中斷/DMA 。
用到 TIM5_CH1 來捕獲高電平脈寬,也就是要先設置輸入捕獲為上昇沿檢測,記錄發生上昇沿的時候 TIM5_CNT 的值。然後配置捕獲信號為下降沿捕獲,當下降沿到來時,發生捕獲,並記錄此時的 TIM5_CNT 值。這樣,前後兩次 TIM5_CNT 之差,就是高電平的脈寬, 同時 TIM5 的計數頻率我們是知道的,從而可以計算出高電平脈寬的准確時間。
1.1、測量脈寬或者頻率
1.2、測量頻率的步驟方法
當捕獲通道 TIx 上出現上昇沿時,發生第一次捕獲,計數器 CNT 的值會被鎖存到捕獲寄存器 CCR 中,而且還會進入捕獲中斷,在中斷服務程序中記錄一次捕獲(可以用一個標志變量來記錄),並把捕獲寄存器中的值讀取到 value1 中。當出現第二次上昇沿時,發生第二次捕獲,計數器 CNT 的值會再次被鎖存到捕獲寄存器 CCR 中,並再次進入捕獲中斷,在捕獲中斷中,把捕獲寄存器的值讀取到 value3 中,並清除捕獲記錄標志。利用 value3 和 value1 的差值我們就可以算出信號的周期(頻率)。
1.3、測量脈寬的步驟方法
當捕獲通道 TIx 上出現上昇沿時,發生第一次捕獲,計數器 CNT 的值會被鎖存到捕獲寄存器 CCR 中,而且還會進入捕獲中斷,在中斷服務程序中記錄一次捕獲(可以用一個標志變量來記錄),並把捕獲寄存器中的值讀取到 value1 中。然後把捕獲邊沿改變為下降沿捕獲,目的是捕獲後面的下降沿。當下降沿到來的時候,發生第二次捕獲,計數器 CNT 的值會再次被鎖存到捕獲寄存器 CCR 中,並再次進入捕獲中斷,在捕獲中斷中,把捕獲寄存器的值讀取到 value3 中,並清除捕獲記錄標志。然後把捕獲邊沿設置為上昇沿捕獲。 在測量脈寬過程中需要來回的切換捕獲邊沿的極性,如果測量的脈寬時間比較長,定時器就會發生溢出,溢出的時候會產生更新中斷,我們可以在中斷裏面對溢出進行記錄處理。
二、 輸入捕獲工作過程
2.1、CH1為例,輸入捕獲工作過程
2.2、輸入通道
當使用需要被測量的信號從定時器的外部引脚 TIMx_CH1/2/3/4 進入,通常叫 TI1/2/3/4,在後面的捕獲中對於要被測量的信號都以 TIx 為標准叫法。
2.3、輸入濾波和邊沿檢測
2.4、捕獲通道
2.5、預分頻器
1、ICx 的輸出信號會經過一個預分頻器,用於决定發生多少個事件時進行一次捕獲。
2、具體的由寄存器 CCMRx 的比特 ICxPSC 配置,如果希望捕獲信號的每一個邊沿,則不分頻。
三、輸出比較
3.1、輸出比較的作用
四、捕獲寄存器
需要用到的寄存器有:TIMx_ARR、 TIMx_PSC、TIMx_CCMR1、TIMx_CCER、TIMx_DIER、TIMx_CR1、TIMx_CCR1 。
4.1、捕獲/比較模式寄存器 :TIMx_CCMR1
當在輸入捕獲模式下使用的時候,對應第二行描述,從圖中可以看出,TIMx_CCMR1 明顯是針對 2 個通道的配置,低八比特[7:0]用於捕獲/比較通道 1 的控制,而高八 比特[15:8]則用於捕獲/比較通道 2 的控制。
其中 CC1S[1:0],這兩個比特用於 CCR1 的通道配置,這裏我們設置 IC1S[1:0]=01,也就是配 置 IC1 映射在 TI1 上;
輸入捕獲 1 預分頻器 IC1PSC[1:0];因為1 次邊沿就觸發 1 次捕獲,所以選擇 00 。
輸入捕獲 1 濾波器 IC1F[3:0],這個用來設置輸入采樣頻率和數字濾波器長度。其中 是定時器的輸入頻率(TIMxCLK),一般為 72Mhz。而
則是根據 TIMx_CR1 的 CKD[1:0] 的設置來確定的,如果 CKD[1:0]設置為 00,那麼
。N 值就是濾波長度,舉個簡單的例子:假設 IC1F[3:0]=0011,並設置 IC1 映射到通道 1 上,且為上昇沿觸發,那麼在捕獲到上昇沿的時候,再以
的頻率,連續采樣到 8 次通道 1 的電平,如果都是高電平,則說明卻是一個有效的觸發,就會觸發輸入捕獲中斷(如果開啟了的話)。這樣可以濾除那些高電平脈寬低於 8 個采樣周期的脈沖信號,從而達到濾波的效果。
4.2、捕獲/比較使能寄存器:TIMx_CCER
要用到這個寄存器的最低 2 比特,CC1E 和 CC1P 比特。
要使能輸入捕獲,必須設置 CC1E=0,而 CC1P 則根據自己的需要來配置。
4.3、DMA/中斷使能寄存器:TIMx_DIER
需要用到中斷來處理捕獲數據,所以必須開啟通道 1 的捕獲比較中 斷,即 CC1IE 設置為 1。
五、程序設計
5.1、配置步驟
① 初始化定時器和通道對應IO的時鐘。
② 初始化IO口,模式為輸入:GPIO_Init();
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; //PA0 輸入
③初始化定時器ARR,PSC
TIM_TimeBaseInit();
④初始化輸入捕獲通道
TIM_ICInit();
⑤如果要開啟捕獲中斷,
TIM_ITConfig();
NVIC_Init();
⑥使能定時器:TIM_Cmd();
⑦編寫中斷服務函數:TIMx_IRQHandler();
5.2、程序編寫
time.c代碼如下所示
#include "timer.h"
#include "led.h"
#include "usart.h"
/*通用定時器中斷初始化
這裏時鐘選擇為APB1的2倍,而APB1為36M
arr:自動重裝值。
psc:時鐘預分頻數
這裏使用的是定時器3!*/
void TIM3_Int_Init(u16 arr,u16 psc)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //時鐘使能
TIM_TimeBaseStructure.TIM_Period = arr; //設置在下一個更新事件裝入活動的自動重裝載寄存器周期的值 計數到5000為500ms
TIM_TimeBaseStructure.TIM_Prescaler =psc; //設置用來作為TIMx時鐘頻率除數的預分頻值 10Khz的計數頻率
TIM_TimeBaseStructure.TIM_ClockDivision = 0; //設置時鐘分割:TDTS = Tck_tim
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上計數模式
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根據TIM_TimeBaseInitStruct中指定的參數初始化TIMx的時間基數單比特
TIM_ITConfig( //使能或者失能指定的TIM中斷
TIM3, //TIM2
TIM_IT_Update | //TIM 中斷源
TIM_IT_Trigger, //TIM 觸發中斷源
ENABLE //使能
);
NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn; //TIM3中斷
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //先占優先級0級
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //從優先級3級
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
NVIC_Init(&NVIC_InitStructure); //根據NVIC_InitStruct中指定的參數初始化外設NVIC寄存器
TIM_Cmd(TIM3, ENABLE); //使能TIMx外設
}
void TIM3_IRQHandler(void) //TIM3中斷
{
if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET) //檢查指定的TIM中斷發生與否:TIM 中斷源
{
TIM_ClearITPendingBit(TIM3, TIM_IT_Update ); //清除TIMx的中斷待處理比特:TIM 中斷源
LED1=!LED1;
}
}
/*PWM輸出初始化
arr:自動重裝值
psc:時鐘預分頻數*/
void TIM3_PWM_Init(u16 arr,u16 psc)
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE); //使能GPIO外設和AFIO複用功能模塊時鐘使能
GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3, ENABLE); //Timer3部分重映射 TIM3_CH2->PB5 //用於TIM3的CH2輸出的PWM通過該LED顯示
//設置該引脚為複用輸出功能,輸出TIM3 CH2的PWM脈沖波形
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; //TIM_CH2
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //複用推挽輸出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
//GPIO_WriteBit(GPIOA, GPIO_Pin_7,Bit_SET); // PA7上拉
TIM_TimeBaseStructure.TIM_Period = arr; //設置在下一個更新事件裝入活動的自動重裝載寄存器周期的值 80K
TIM_TimeBaseStructure.TIM_Prescaler =psc; //設置用來作為TIMx時鐘頻率除數的預分頻值 不分頻
TIM_TimeBaseStructure.TIM_ClockDivision = 0; //設置時鐘分割:TDTS = Tck_tim
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上計數模式
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根據TIM_TimeBaseInitStruct中指定的參數初始化TIMx的時間基數單比特
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; //選擇定時器模式:TIM脈沖寬度調制模式2
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比較輸出使能
TIM_OCInitStructure.TIM_Pulse = 0; //設置待裝入捕獲比較寄存器的脈沖值
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //輸出極性:TIM輸出比較極性高
TIM_OC2Init(TIM3, &TIM_OCInitStructure); //根據TIM_OCInitStruct中指定的參數初始化外設TIMx
TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable); //使能TIMx在CCR2上的預裝載寄存器
TIM_ARRPreloadConfig(TIM3, ENABLE); //使能TIMx在ARR上的預裝載寄存器
TIM_Cmd(TIM3, ENABLE); //使能TIMx外設
}
//定時器5通道1輸入捕獲配置
TIM_ICInitTypeDef TIM5_ICInitStructure;
void TIM5_Cap_Init(u16 arr,u16 psc)
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5, ENABLE); //使能TIM5時鐘
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //使能GPIOA時鐘
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; //PA0 清除之前設置
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; //PA0 輸入
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_ResetBits(GPIOA,GPIO_Pin_0); //PA0 下拉
//初始化定時器5 TIM5
TIM_TimeBaseStructure.TIM_Period = arr; //設定計數器自動重裝值
TIM_TimeBaseStructure.TIM_Prescaler =psc; //預分頻器
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //設置時鐘分割:TDTS = Tck_tim
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上計數模式
TIM_TimeBaseInit(TIM5, &TIM_TimeBaseStructure); //根據TIM_TimeBaseInitStruct中指定的參數初始化TIMx的時間基數單比特
//初始化TIM5輸入捕獲參數
TIM5_ICInitStructure.TIM_Channel = TIM_Channel_1; //CC1S=01 選擇輸入端 IC1映射到TI1上
TIM5_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising; //上昇沿捕獲
TIM5_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; //映射到TI1上
TIM5_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1; //配置輸入分頻,不分頻
TIM5_ICInitStructure.TIM_ICFilter = 0x00;//IC1F=0000 配置輸入濾波器 不濾波
TIM_ICInit(TIM5, &TIM5_ICInitStructure);
//中斷分組初始化
NVIC_InitStructure.NVIC_IRQChannel = TIM5_IRQn; //TIM3中斷
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; //先占優先級2級
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; //從優先級0級
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
NVIC_Init(&NVIC_InitStructure); //根據NVIC_InitStruct中指定的參數初始化外設NVIC寄存器
TIM_ITConfig(TIM5,TIM_IT_Update|TIM_IT_CC1,ENABLE);//允許更新中斷 ,允許CC1IE捕獲中斷
TIM_Cmd(TIM5,ENABLE ); //使能定時器5
}
u8 TIM5CH1_CAPTURE_STA=0; //輸入捕獲狀態
u16 TIM5CH1_CAPTURE_VAL; //輸入捕獲值
//定時器5中斷服務程序
void TIM5_IRQHandler(void)
{
if((TIM5CH1_CAPTURE_STA&0X80)==0)//還未成功捕獲
{
if (TIM_GetITStatus(TIM5, TIM_IT_Update) != RESET)
{
if(TIM5CH1_CAPTURE_STA&0X40)//已經捕獲到高電平了
{
if((TIM5CH1_CAPTURE_STA&0X3F)==0X3F)//高電平太長了
{
TIM5CH1_CAPTURE_STA|=0X80;//標記成功捕獲了一次
TIM5CH1_CAPTURE_VAL=0XFFFF;
}else TIM5CH1_CAPTURE_STA++;
}
}
if (TIM_GetITStatus(TIM5, TIM_IT_CC1) != RESET)//捕獲1發生捕獲事件
{
if(TIM5CH1_CAPTURE_STA&0X40) //捕獲到一個下降沿
{
TIM5CH1_CAPTURE_STA|=0X80; //標記成功捕獲到一次上昇沿
TIM5CH1_CAPTURE_VAL=TIM_GetCapture1(TIM5);
TIM_OC1PolarityConfig(TIM5,TIM_ICPolarity_Rising); //CC1P=0 設置為上昇沿捕獲
}else //還未開始,第一次捕獲上昇沿
{
TIM5CH1_CAPTURE_STA=0; //清空
TIM5CH1_CAPTURE_VAL=0;
TIM_SetCounter(TIM5,0);
TIM5CH1_CAPTURE_STA|=0X40; //標記捕獲到了上昇沿
TIM_OC1PolarityConfig(TIM5,TIM_ICPolarity_Falling); //CC1P=1 設置為下降沿捕獲
}
}
}
TIM_ClearITPendingBit(TIM5, TIM_IT_CC1|TIM_IT_Update); //清除中斷標志比特
}
timer.h程序如下所示:
#ifndef __TIMER_H
#define __TIMER_H
#include "sys.h"
void TIM3_Int_Init(u16 arr,u16 psc);
void TIM3_PWM_Init(u16 arr,u16 psc);
void TIM5_Cap_Init(u16 arr,u16 psc);
#endif
main.c代碼如下所示:
#include "led.h"
#include "delay.h"
#include "key.h"
#include "sys.h"
#include "usart.h"
#include "timer.h"
extern u8 TIM5CH1_CAPTURE_STA; //輸入捕獲狀態
extern u16 TIM5CH1_CAPTURE_VAL; //輸入捕獲值
int main(void)
{
u32 temp=0;
delay_init(); //延時函數初始化
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //設置NVIC中斷分組2:2比特搶占優先級,2比特響應優先級
uart_init(115200); //串口初始化為115200
LED_Init(); //LED端口初始化
TIM3_PWM_Init(899,0); //不分頻。PWM頻率=72000/(899+1)=80Khz
TIM5_Cap_Init(0XFFFF,72-1); //以1Mhz的頻率計數
while(1)
{
delay_ms(10);
TIM_SetCompare2(TIM3,TIM_GetCapture2(TIM3)+1);
if(TIM_GetCapture2(TIM3)==300)TIM_SetCompare2(TIM3,0);
if(TIM5CH1_CAPTURE_STA&0X80)//成功捕獲到了一次上昇沿
{
temp=TIM5CH1_CAPTURE_STA&0X3F;
temp*=65536;//溢出時間總和
temp+=TIM5CH1_CAPTURE_VAL;//得到總的高電平時間
printf("HIGH:%d us\r\n",temp);//打印總的高點平時間
TIM5CH1_CAPTURE_STA=0;//開啟下一次捕獲
}
}
}
边栏推荐
- CSAPP Shell Lab 实验报告
- Emqtt distribution cluster and node bridge construction
- 基于485总线的评分系统双机实验报告
- In Oracle, start with connect by prior recursive query is used to query multi-level subordinate employees.
- MySQL数据库(三)高级数据查询语句
- ArrayList集合
- Global and Chinese market of DVD recorders 2022-2028: Research Report on technology, participants, trends, market size and share
- Collection collection and map collection
- What if software testing is too busy to study?
- ucore lab 2
猜你喜欢
Daily code 300 lines learning notes day 9
What are the commonly used SQL statements in software testing?
Video scrolling subtitle addition, easy to make with this technique
Example 071 simulates a vending machine, designs a program of the vending machine, runs the program, prompts the user, enters the options to be selected, and prompts the selected content after the use
How to rename multiple folders and add unified new content to folder names
Mysql database (IV) transactions and functions
如何成为一个好的软件测试员?绝大多数人都不知道的秘密
Réponses aux devoirs du csapp 7 8 9
C4D quick start tutorial - Introduction to software interface
Lab 8 file system
随机推荐
Cadence physical library lef file syntax learning [continuous update]
Portapack application development tutorial (XVII) nRF24L01 launch B
MySQL数据库(二)DML数据操作语句和基本的DQL语句
Which version of MySQL does php7 work best with?
软件测试有哪些常用的SQL语句?
Visual analysis of data related to crawling cat's eye essays "sadness flows upstream into a river" | the most moving film of Guo Jingming's five years
软件测试Bug报告怎么写?
自动化测试你必须要弄懂的问题,精品总结
Collection集合与Map集合
软件测试工作太忙没时间学习怎么办?
Stc-b learning board buzzer plays music 2.0
Global and Chinese market of RF shielding room 2022-2028: Research Report on technology, participants, trends, market size and share
The latest query tracks the express logistics and analyzes the method of delivery timeliness
遇到程序员不修改bug时怎么办?我教你
Lab 8 文件系统
The minimum sum of the last four digits of the split digit of leetcode simple problem
51 lines of code, self-made TX to MySQL software!
The most detailed postman interface test tutorial in the whole network. An article meets your needs
Sorting odd and even subscripts respectively for leetcode simple problem
Rearrange spaces between words in leetcode simple questions