【STM32】TIM定時器基本定時功能

第一部分:定時器基本定時的功能;

第二部分:定時器的輸出比較功能;

第三部分:定時器輸入捕獲的功能;

第四部分:定時器的編碼接口。

1?TIM簡介

  1. TIM(Timer)定時器;
  2. 定時器可以對輸入的時鐘進行計數,并在計數值達到設定值時觸發中斷(定時觸發中斷);
  3. 16位計數器、預分頻器、自動重裝寄存器的時基單元,在72MHz計數時鐘下可以實現最大59.65s的定時(72M/65536/65536,再取倒數);
  4. 不僅具備基本的定時中斷功能,而且還包含內外時鐘源選擇輸入捕獲輸出比較編碼器接口主從觸發模式等多種功能;
  5. 根據復雜度和應用場景分為了高級定時器、通用定時器、基本定時器三種類型。

1.1?定時器類型

類型

編號

總線

功能

高級定時器

TIM1TIM8

APB2

擁有通用定時器全部功能,并額外具有重復計數器、死區生成、互補輸出、剎車輸入等功能

通用定時器

TIM2TIM3TIM4TIM5

APB1

擁有基本定時器全部功能,并額外具有內外時鐘源選擇、輸入捕獲、輸出比較、編碼器接口、主從觸發模式等功能

基本定時器

TIM6TIM7

APB1

擁有定時中斷、主模式觸發DAC的功能

STM32F103C8T6定時器資源:TIM1TIM2TIM3TIM4

1.1.1 基本定時器

基本定時器框圖:擁有定時中斷、主模式觸發DAC的功能

下面三部分預分頻器計數器自動重裝載寄存器構成最基本的計數計時電路,因此叫時基單元。

預分頻器之前連接的是基準計數時鐘的輸入,可以認為連接到了輸入端,也就是CK_INT(72MHz)

預分頻器對72MHz的計數時鐘進行分頻,預分頻器寫0就是不分頻,寫1就是二分頻36MHz......,所以預分頻器的值和實際的分頻系數相差1,即實際分頻系數=預分頻器的值 + 1。這個預分頻器是16位的,所以最大值是65535,也就是65536分頻。

然后是計數器,計數器可以對預分頻器后的計數時鐘進行計數,計數時鐘每來一個上升沿,計數器就加1,這個計數器也是16位的,所以里面的值可以從0一直加到65535,再加的話,計數器就會從0開始。所以計數器的值在計時過程中會不斷的自增運行,當自增運行到目標值時,產生中斷,那就完成了定時的任務,所以還需要一個存儲目標值的寄存器,那就是自動重裝載寄存器。

自動重裝載寄存器也是16位的,它存儲的就是我們寫入的計數目標,在運行過程中,計數器值不斷自增,自動重裝載寄存器的值是固定的,當計數值等于自動重裝值時,也就是計時時間到了,此時會產生一個中斷信號,并且清零計數器,計數器自動開始下一次的計數計時。

UI那里向上的折線,代表這里會產生中斷信號,像這種計數值等于重裝值產生的中斷,稱為"更新中斷",這個更新中斷會通往NVIC,再配置好NVIC的定時器通道,那定時器的更新中斷就能夠得到CPU的響應了。

U向下的折線,代表這里會產生一個事件,這里對應的事件稱為"更新事件",更新事件不會觸發中斷,但是可以觸發內部其它電路的工作。

總結:從基準時鐘,到預分頻器,再到計數器,計數器自增,同時不斷與自動重裝寄存器進行比較,它倆值相等時,即計時時間到,這時會產生一個更新中斷和更新事件,CPU響應更新中斷,就完成定時中斷的任務了。

主模式觸發DAC(數字/模擬轉換模塊)功能:它能讓內部的硬件在不受程序的控制下實現自動運行。

用途:在使用DAC的時候,可能會用DAC輸出一段波形,那就需要每隔一段時間來觸發一次DAC,讓它輸出下一個電壓點。如果用正常的思路實現,就是先設置一個定時器產生中斷,每隔一段時間在中斷函數中調用代碼手動觸發一次DAC轉換,然后DAC輸出,這樣也是沒問題的,但是會使主程序處于頻繁被中斷的狀態,會影響主程序的運行和其它中斷的響應,所以定時器設置了一個主模式,使用這個主模式可以把定時器的更新事件映射到觸發輸出(Trigger Out, TRFO)的位置,然后TRGO直接接到DAC的觸發轉換引腳上,這樣定時器的更新就不需要通過中斷來觸發DAC轉換了,僅需要把更新事件通過主模式映射到TRGO,然后TRGO就會直接去觸發DAC了,整個過程不需要軟件的參與,實現了硬件的自動化,這就是主模式的作用。

1.1.2 通用定時器

復雜很多,中間部分還是時基單元

通用計數器那種模式是向上計數,即自增;通用定時器和高級定時器還支持向下計數模式(向下自減,減到0,再回到重裝值申請中斷)和中央對齊模式(先向上自增到重裝值,申請中斷,再向下自減,減到0,申請中斷)

這部分是內外時鐘源選擇和主從觸發模式的結構了。對于基本定時器而言,定時只能選擇內部時鐘,也就是系統頻率72MHz;到了通用定時器這里,時鐘源不僅可以選擇內部的72MHz時鐘,還可以選擇外部時鐘。第一個外部時鐘來自TIMx_ETR引腳上的外部時鐘,這個ETR(External)引腳的位置,參考引腳定義表。

TIM2_CH1_ETR的意思是TIM2的CH1和ETR都是復用在這個位置,即PA0引腳。

這里就可以在TIM2的ETR引腳,也就是PA0上接一個外部方波時鐘,然后配置一下內部的極性選擇、邊沿檢測和預分頻器電路,再配置一下輸入濾波電路(對輸入波形進行濾波),濾波后的信號,兵分兩路,上面一路ETRF進入觸發控制器,緊跟著就可以選擇作為時基單元的時鐘了,這一路電路稱為"外部時鐘模式2"。

除了外部ETR引腳可以提供時鐘外,下面還有一路可以提供時鐘,就是TRGI(Trigger In),它主要的作用是用作觸發輸入來使用的,這個觸發輸入可以觸發定時器的從模式。暫時可以把TRGI看作外部時鐘的輸入來看,這一路稱為"外部時鐘模式1"。通過這一路的外部時鐘有ETR引腳的信號ITR信號(ITR信號來自其他定時的TRGO處,連接方式見下圖:TIM2的ITR0連到TIM1的TRGO上,TIM2的ITR1連到TIM8的TRGO上,TIM2的ITR2連到TIM1的TRG3上,TIM2的ITR3連到TIM4的TRGO上)可以實現定時器級聯的功能。

通過這一路的外部時鐘有TI1F_ED這里連接的是輸入捕獲單元的CH1引腳,也就是從CH1引腳獲得時鐘,后面加ED,是邊沿的(Edge)意思,也就是通過這一路輸入的時鐘,上升沿和下降沿均有效。

最后還能通過TI1FP1TI2FP2獲得。其中TI1FP1連接到CH1引腳的時鐘,其中TI2FP2連接到CH2引腳的時鐘。

總結:外部時鐘模式1的輸入可以是ETR引腳、其他定時器、CH1引腳的邊沿、CH1引腳和CH2引腳;一般情況外部時鐘通過ETR引腳就可以了。

如果使用外部時鐘,首選ETR引腳外部時鐘模式2的輸入。

定時的編碼器接口,可以讀取正交編碼器的輸出波形。

再看下面的電路,主要是兩部分:

輸出比較電路:總共有4個通道,分別對應CH1-CH4的引腳,可以用于輸出PWM波形,驅動電機

輸入捕獲電路:總共有4個通道,分別對應CH1-CH4的引腳,可以用來測量輸入方波的頻率等

中間寄存器是捕獲/比較寄存器,是輸入捕獲和輸出比較共用的電路。輸入捕獲和輸出比較不能同時使用。

1.1.3?高級定時器

和通用定時器相比多的地方,第一個是申請中斷的地方,增加了重復次數計數器,實現每個幾個計數周期才發生一次更新事件和更新中斷(原來是每個計數周期都會發生更新),相當于對輸出的更新信號又做了一次分頻(59s*65536)

下面的是高級定時器對輸出比較模塊的升級了

DTG(Dead Time Generate)死區生成電路,右邊的輸出引腳由原來的一個變為兩個互補的輸出,可以輸出一對互補的PWM波,這些電路是為了驅動三相無刷電機的(四軸飛行器、電動車的后輪、電鉆等)

最后一部分就是剎車輸入功能,為了給電機驅動提供安全保障的。如果外部引腳TIMx_BKIN(break in)產生剎車信號或者內部時鐘失效,產生了故障,那么控制電路就會自動切斷電機的輸出,防止意外的發生。

1.2?定時中斷基本結構

本小結的兩任務-定時中斷內部時鐘源選擇

左邊是為時基單元提供時鐘的部分,可以選擇RCC內部時鐘,也可以選擇ETR引腳提供的外部時鐘模式2。

第一個案例定時中斷使用的是RCC內部時鐘

第二個定時器外部時鐘使用的是外部時鐘模式2這一路。

中斷輸出控制就是一個中斷輸出的允許位。

1.2.1?預分頻器時序

預分頻器可以將計數器的時鐘頻率按1到65536之間的任意值分頻。它是基于一個(在TIMx_PSC
寄存器中的)16位寄存器控制的16位計數器。這個控制寄存器帶有緩沖器,它能夠在工作時被改
變。新的預分頻器參數在下一次更新事件到來時被采用。

第一行CK_PSC,預分頻器的輸入時鐘,選內部時鐘的話,一般是72MHz

第二行CNT_EN,計數器使能,高電平計數器正常運行,低電平計數器停止運行。

第三行CK_CNT,計數器時鐘,它既是預分頻器的時鐘輸出,也是計數器的時鐘輸入;開始時計數器未使能,計數器時鐘不運行;然后使能后,前半段,預分頻器系數為1,計數器的時鐘等于預分頻器前的時鐘;后半段,預分頻器系數變為2,計數器的時鐘也就變為預分頻器前時鐘的一半了。

第四行計數器寄存器,在計數器時鐘的驅動下,計數器寄存器也跟著時鐘的上升沿不斷自增。在中間位置FC之后,計數值變為0,可以推斷出ARR(自動重裝值)就是FC,當計數值和重裝值相等,并且下一個時鐘來臨時,計數值才清零,同時第五行產生一個更新事件(UEV)。這就是一個計數周期的工作流程。

下面還有三行時序。

這里描述的是預分頻器的一種緩沖機制,也就是預分頻器實際是有兩個,一個是供我們讀寫使用的,它并不直接決定分頻系數;另外還有一個緩沖寄存器(影子寄存器),它才是真正起作用的。為了防止在一個周期中間修改了分頻系數,這時這個變化不會立即生效,而是等到本次計數周期結束時產生了跟新事件,預分頻器的值才會被傳遞到緩沖寄存器里面,這時才會生效。

最后預分頻器內部實際也是靠計數來分頻的。當預分頻值是0時,計數器就一直為0,直接輸出原頻率;當預分頻值為1時,計數器就一直0、1、0、1、0、1......這樣計數,在回到0的時候輸出一個脈沖。這樣輸出頻率就是輸入頻率的二分頻。預分頻器的值和實際的分頻系數之間有一個數的偏移。

計數器時鐘:CK_CNT

預分頻器的輸入時鐘:CK_PSC

計數器計數頻率:CK_CNT = CK_PSC / (PSC + 1)

PSC就是0分頻、1分頻、2分頻......

1.2.2?計數器時序

第一行CK_INT是內部時鐘72MHz;

第二行是時鐘使能,高電平正常運行,低電平停止運行。

第三行是計數器時鐘,因為分頻系數位2,所以這個分頻是上面的1/2;然后第四行計數器寄存器的值在上升沿自增,增到0036時,發生溢出,此時再來一個上升沿,計數器清零,計數器溢出(第五行),產生一個更新事件脈沖(第六行),另外還會更新中斷標志(UIF),這個標志位只要置1了,就會去申請中斷,然后中斷響應后,需要在中斷程序中手動清零。

計數器溢出(中斷)頻率:CK_CNT_OV = CK_CNT / (ARR + 1)

????????????????????????????????????????????????????= CK_PSC / (PSC + 1) / (ARR + 1)

ARR自動重裝值

計數器計數頻率:CK_CNT = CK_PSC / (PSC + 1)

寄存器圖中有陰影的都有影子寄存器(緩沖寄存器)

1.2.3?計數器無預裝時序

計數器時序圖,當ARPE=0時的更新事件(TIMx_ARR沒有預裝入),即沒有影子寄存器(緩沖寄存器)

計數器正在自增計數,突然更改自動加載寄存器,就是自動重裝寄存器,由FF變成36,所以上面計到36就直接更新,開始下一輪計數。

1.2.4?計數器有預裝時序

計數器時序圖,當ARPE=1時的更新事件(預裝入了TIMx_ARR),即有影子寄存器(緩沖寄存器)

在計數的中途,把計數目標由F5改成36,下面有個影子寄存器,這個影子寄存器才是真正起作用的,它還是F5,等計數到F5時,產生更新事件;同時要更改的36才被傳到影子寄存器,在下一個計數周期更改的36才有效。引入影子寄存器實際為了同步,就是讓值的變化和更新事件同步發生,防止在運行中途更改造成錯誤。圖中改成36,而F1大于36,如果沒有影子寄存器,計數就會一直加,加到FF,再從0開始加到36,才能產生更新,這就造成一些小問題。

1.2.5?RCC時鐘樹

這個時鐘樹就是STM32中用來產生和配置時鐘,并且把配置好的時鐘發送到各個外設的系統。時鐘是所有外設運行的基礎,所以時鐘也是最先需要配置的東西。主函數之前的SystemInit中配置時鐘。

在時鐘產生電路有四個振蕩源,分別是:

(1)內部的8MHz高速RC振蕩器(提供系統時鐘);

(2)外部的4-16MHz高速石英晶體振蕩器,也就是晶振,一般接8MHz(提供系統時鐘);

(3)外部的32.768KHz低速晶振,一般給RTC提供時鐘;

(4)最后是內部的40KHz低速RC振蕩器,可以給看門狗提供時鐘。

AHB、APB1、APB2的時鐘都是來自前兩個高速晶振。外部的石英晶振要比內部的RC振蕩器更加穩定,所以一般使用外部晶振。

SystemInit配置的流程是:首先會啟動內部時鐘,選擇內部8MHz為系統時鐘,暫時以內部8MHz的時鐘運行;然后再啟動外部時鐘,進入PLL鎖相環進行倍頻,8MHz的9倍是72MHz,等到鎖相環輸出穩定后,選擇鎖相環輸出為系統時鐘,這樣就把系統時鐘由8MHz切換成72MHz。

鎖相環:

為什么是72MHz:

如果外部晶振出問題了,程序時鐘大概慢10倍(1s到10s),會以內部的8MHz運行(不是9倍嗎)

CSS(Clock Security System)時鐘安全系統:負責切換時鐘,可以檢測外部時鐘的運行狀態,一旦外部時鐘失效,它就會自動把外部時鐘切換回內部時鐘,保證系統時鐘的允許,防止程序卡死造成事故。

看右邊的時鐘分配電路。系統時鐘72MHz進入AHB總線,AHB總線有個預分頻器,在SystemInit中配置的分配系數是1,那AHB的時鐘就是72MHz;然后進入APB1總線,這里的配置的分配系數是2,所以APB1總線的時鐘是36MHz。

之前一直說無論高級定時器、通用定時器、基本定時器時鐘都是72MHz,可以看下面的分支,APB1的預分頻系數如果是1,則頻率不變,否則頻率*2,即36 * 2 = 72MHz。

APB2的分頻系數是1,所以APB2的時鐘和AHB的時鐘一樣,都是72MHz。

外部時鐘使能對應代碼

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);

手冊

2?TIM定時器之定時器中斷

2.1?接線圖

2.2?模塊化

按照這個圖進行初始化

步驟:

(1)RCC開啟時鐘;

(2)選擇時基單元的時鐘源(內部時鐘);

(3)配置時基單元(預分頻器、自動重裝器、計數模式等);

(4)配置輸出中斷控制,允許更新中斷輸出到NVIC;

(5)配置NVIC,在NVIC中打開定時器中斷通道,并分配一個優先級;

(6)啟動計數器。

定時器的庫函數

// 恢復缺省配置
void TIM_DeInit(TIM_TypeDef* TIMx);
// 時基單元初始化
void TIM_TimeBaseInit(TIM_TypeDef* TIMx, TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct);
// 給結構體變量賦默認值
void TIM_TimeBaseStructInit(TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct);
// 使能計數器    
void TIM_Cmd(TIM_TypeDef* TIMx, FunctionalState NewState);
// 使能中斷輸出信號
void TIM_ITConfig(TIM_TypeDef* TIMx, uint16_t TIM_IT, FunctionalState NewState);// 選擇內部時鐘
void TIM_InternalClockConfig(TIM_TypeDef* TIMx);
// 選擇ITRx其他定時器的時鐘                
void TIM_ITRxExternalClockConfig(TIM_TypeDef* TIMx, uint16_t TIM_InputTriggerSource); 
// 選擇TIx捕獲通道的時鐘  
void TIM_TIxExternalClockConfig(TIM_TypeDef* TIMx, uint16_t TIM_TIxExternalCLKSource,uint16_t TIM_ICPolarity, uint16_t ICFilter); 
// 外部時鐘模式1
void TIM_ETRClockMode1Config(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, uint16_t             TIM_ExtTRGPolarity, uint16_t ExtTRGFilter);
// ETR外部時鐘2                       
void TIM_ETRClockMode2Config(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, uint16_t TIM_ExtTRGPolarity, uint16_t ExtTRGFilter);
// 配置參數
void TIM_ETRConfig(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, uint16_t     TIM_ExtTRGPolarity, uint16_t ExtTRGFilter);                                        // 單獨寫預分頻值
void TIM_PrescalerConfig(TIM_TypeDef* TIMx, uint16_t Prescaler, uint16_t TIM_PSCReloadMode);
// 改變計數器計數模式
void TIM_CounterModeConfig(TIM_TypeDef* TIMx, uint16_t TIM_CounterMode);          
// 自動重裝器預裝功能配置
void TIM_ARRPreloadConfig(TIM_TypeDef* TIMx, FunctionalState NewState);
// 計數器寫值      
void TIM_SetCounter(TIM_TypeDef* TIMx, uint16_t Counter);
// 給自動重裝器寫值                  
void TIM_SetAutoreload(TIM_TypeDef* TIMx, uint16_t Autoreload);                // 獲取計數器的值
uint16_t TIM_GetCounter(TIM_TypeDef* TIMx);
// 獲取預分頻器的值                                 
uint16_t TIM_GetPrescaler(TIM_TypeDef* TIMx);                                
// 在主程序中獲得/清除標志位
FlagStatus TIM_GetFlagStatus(TIM_TypeDef* TIMx, uint16_t TIM_FLAG);			 
void TIM_ClearFlag(TIM_TypeDef* TIMx, uint16_t TIM_FLAG);
// 在中斷函數中獲得/清除標志位
ITStatus TIM_GetITStatus(TIM_TypeDef* TIMx, uint16_t TIM_IT);
void TIM_ClearITPendingBit(TIM_TypeDef* TIMx, uint16_t TIM_IT);

Timer.c

#include "stm32f10x.h"                  // Device header// 定時器初始化
void Timer_Init(void)
{// 第一步RCC開啟時鐘RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);// 第二步選擇時基單元的時鐘源(內部時鐘)TIM_InternalClockConfig(TIM2);// 第三步配置時基單元(預分頻器、自動重裝器、計數模式等)TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;		// 時鐘分頻,影響不大TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;	// 計數模式,向上計數TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;			// 重復計數器的值// 關鍵參數,如果想定時1S
//	CK_CNT_OV = CK_CNT / (ARR + 1)
//			  = CK_PSC / (PSC + 1) / (ARR + 1)
//			 1 = 72000000 / (PSC + 1) / (ARR + 1)
//		     1 = 72000000 / 7200 / 10000TIM_TimeBaseInitStructure.TIM_Period = 10000 - 1;				// ARR自動重裝器的值 兩個合起來計數1秒TIM_TimeBaseInitStructure.TIM_Prescaler = 7200 - 1;				// PSC預分頻器的值,7200分頻,得到10k計數TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);TIM_ClearFlag(TIM2, TIM_FLAG_Update);                           // 防止復位從1開始// 第四步配置輸出中斷控制,允許更新中斷輸出到NVICTIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);// 第五步配置NVIC,在NVIC中打開定時器中斷通道,并分配一個優先級NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);        			// 分組NVIC_InitTypeDef NVIC_InitStruct;NVIC_InitStruct.NVIC_IRQChannel = TIM2_IRQn;           			// 定時器2的通道NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 2;			// 搶占優先級NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;					// 響應優先級NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;					// 使能NVIC_Init(&NVIC_InitStruct);// 第六步啟動計數器TIM_Cmd(TIM2, ENABLE);
}// 中斷函數
//void TIM2_IRQHandler(void)
//{
//	// 檢測中斷標志位,確保是設置的中斷源觸發的這個函數
//	if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)
//	{
//	
//		// 清除中斷標志位
//		TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
//	}
//}

2.3?主函數

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "Timer.h"// 中斷函數
void TIM2_IRQHandler(void);uint16_t num;int main()
{OLED_Init();								// 初始化OLEDTimer_Init();								// 初始化定時器OLED_ShowString(1, 1, "Num:");   			// 顯示字符串while (1){OLED_ShowNum(1, 5, num, 5);   	    			// 顯示計數OLED_ShowNum(2, 5, TIM_GetCounter(TIM2), 5);    // 顯示CNT計數器,最大值9999}
}// 中斷函數
void TIM2_IRQHandler(void)
{// 檢測中斷標志位,確保是設置的中斷源觸發的這個函數if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET){// 中斷處理num++;// 清除中斷標志位TIM_ClearITPendingBit(TIM2, TIM_IT_Update);}
}

現象:定時1s在計數

重點在這里

	// 第三步配置時基單元(預分頻器、自動重裝器、計數模式等)TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;		// 時鐘分頻,影響不大TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;	// 計數模式,向上計數TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;			// 重復計數器的值// 關鍵參數,如果想定時1S
//	CK_CNT_OV = CK_CNT / (ARR + 1)
//			  = CK_PSC / (PSC + 1) / (ARR + 1)
//			 1 = 72000000 / (PSC + 1) / (ARR + 1)
//		     1 = 72000000 / 7200 / 10000TIM_TimeBaseInitStructure.TIM_Period = 10000 - 1;				// ARR自動重裝器的值 兩個合起來計數1秒TIM_TimeBaseInitStructure.TIM_Prescaler = 7200 - 1;				// PSC預分頻器的值,7200分頻,得到10k計數TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);TIM_ClearFlag(TIM2, TIM_FLAG_Update);      

3?TIM定時器之定時器外部時鐘

3.1?接線圖

3.2?模塊化

任務依然是定時中斷,但是時鐘部分,不使用內部時鐘了,

Timer

#include "stm32f10x.h"                  // Device header// 定時器初始化
void Timer_Init(void)
{// 第一步RCC開啟時鐘RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;      // 上拉輸入GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);// 第二步選擇時基單元的時鐘源(外部時鐘)// 不分頻,不反向(高電平/上升沿有效),外部觸發濾波器:不用TIM_ETRClockMode2Config(TIM2, TIM_ExtTRGPSC_OFF, TIM_ExtTRGPolarity_NonInverted, 0x00);// 第三步配置時基單元(預分頻器、自動重裝器、計數模式等)TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;		// 時鐘分頻,影響不大TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;	// 計數模式,向上計數TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;			// 重復計數器的值// 關鍵參數,如果想定時1S
//	CK_CNT_OV = CK_CNT / (ARR + 1)
//			  = CK_PSC / (PSC + 1) / (ARR + 1)TIM_TimeBaseInitStructure.TIM_Period = 10 - 1;					// ARR自動重裝器的值,0-9TIM_TimeBaseInitStructure.TIM_Prescaler = 1 - 1;				// PSC預分頻器的值,不分頻TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);TIM_ClearFlag(TIM2, TIM_FLAG_Update);                           // 防止復位從1開始// 第四步配置輸出中斷控制,允許更新中斷輸出到NVICTIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);// 第五步配置NVIC,在NVIC中打開定時器中斷通道,并分配一個優先級NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);        			// 分組NVIC_InitTypeDef NVIC_InitStruct;NVIC_InitStruct.NVIC_IRQChannel = TIM2_IRQn;           			// 定時器2的通道NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 2;			// 搶占優先級NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;					// 響應優先級NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;					// 使能NVIC_Init(&NVIC_InitStruct);// 第六步啟動計數器TIM_Cmd(TIM2, ENABLE);
}// 獲取計數器的值
uint16_t Timer_GetCounter(void)
{return TIM_GetCounter(TIM2);
}// 中斷函數
//void TIM2_IRQHandler(void)
//{
//	// 檢測中斷標志位,確保是設置的中斷源觸發的這個函數
//	if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)
//	{
//	
//		// 清除中斷標志位
//		TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
//	}
//}

3.3?主函數

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "Timer.h"// 中斷函數
void TIM2_IRQHandler(void);uint16_t num;int main()
{OLED_Init();								// 初始化OLEDTimer_Init();								// 初始化定時器OLED_ShowString(1, 1, "Num:");   			// 顯示字符串OLED_ShowString(2, 1, "Cnt:");   			// 顯示字符串while (1){OLED_ShowNum(1, 5, num, 5);   	    			// 顯示計數OLED_ShowNum(2, 5, TIM_GetCounter(TIM2), 5);    // 顯示CNT計數器,最大值9999}
}// 中斷函數
void TIM2_IRQHandler(void)
{// 檢測中斷標志位,確保是設置的中斷源觸發的這個函數if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET){// 中斷處理num++;// 清除中斷標志位TIM_ClearITPendingBit(TIM2, TIM_IT_Update);}
}

現象:擋一下對射式紅外傳感器,計數器加1,加到9時,觸發定時中斷,定時中斷加1。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/210770.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/210770.shtml
英文地址,請注明出處:http://en.pswp.cn/news/210770.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

在OpenCV基于深度學習的超分辨率模型實踐

1. 引言 OpenCV是一個開源的計算機視覺庫,擁有大量優秀的算法。基于最新的合并,OpenCV包含一個易于使用的接口,主要用于實現基于深度學習方法的超分辨率(SR)。該接口包含預先訓練的模型,這些模型可以非常容…

redis中使用事務保護數據完整性

事務是指一個執行過程,要么全部執行成功,要么失敗什么都不改變。不會存在一部分成功一部分失敗的情況,也就是事務的ACID四大特性(原子性、一致性、隔離性、持久性)。但是redis中的事務并不是嚴格意義上的事務&#xff…

使用flutter_native_splash替換啟動圖片,iOS端替換不成功

使用flutter_native_splash替換啟動圖片,iOS端替換不成功 1、刪除App重啟手機;2、重新創建一個新的LaunchScreen.storyboard,比如命名為NewLaunchScreen.storyboard,在General里面設置Launch Screen File為這個新的NewLaunchScree…

藍橋杯 day01 奇怪的數列

題目描述 奇怪的數列 從 X 星截獲一份電碼,是一些數字,如下: 13 1113 3113 132113 1113122113 ?? YY 博士經徹夜研究,發現了規律: 第一行的數字隨便是什么,以后每一行都是對上一行"讀出來…

智能優化算法應用:基于蝗蟲算法3D無線傳感器網絡(WSN)覆蓋優化 - 附代碼

智能優化算法應用:基于蝗蟲算法3D無線傳感器網絡(WSN)覆蓋優化 - 附代碼 文章目錄 智能優化算法應用:基于蝗蟲算法3D無線傳感器網絡(WSN)覆蓋優化 - 附代碼1.無線傳感網絡節點模型2.覆蓋數學模型及分析3.蝗蟲算法4.實驗參數設定5.算法結果6.參考文獻7.MA…

【數據挖掘】國科大蘇桂平老師數據庫新技術課程作業 —— 第二次作業

1 設 F { A B → C , B → D , C D → E , C E → G H , G → A } F\{AB\rightarrow C,B\rightarrow D, CD\rightarrow E, CE\rightarrow GH, G\rightarrow A \} F{AB→C,B→D,CD→E,CE→GH,G→A},用推理的方法證明 F ∣ A B → G F\;|AB\rightarrow G F∣AB→…

持續集成交付CICD:使用Maven命令上傳Nexus制品

目錄 一、實驗 1.使用Maven命令上傳Nexus制品(第一種方式) 2.使用Maven命令上傳Nexus制品(第二種方式) 一、實驗 1.使用Maven命令上傳Nexus制品(第一種方式) (1)指定一個 hoste…

說說React jsx轉換成真實DOM的過程?

在React中,JSX(JavaScript XML)是一種語法糖,用于描述用戶界面的結構和組件關系。當你編寫React組件并包含JS JSX解析:React中的JSX代碼首先會被解析成JavaScript對象。這個過程通常是通過Babel等工具進行的&#xff0…

Flutter視頻播放器在iOS端和Android端都能實現全屏播放

Flutter開發過程中,對于視頻播放的三方組件有很多,在Android端適配都挺好,但是在適配iPhone手機的時候,如果設置了UIInterfaceOrientationLandscapeLeft和UIInterfaceOrientationLandscapeRight都為false的情況下,無法…

pytorch 筆記:dist 和 cdist

1 dist 1.1 基本使用方法 torch.dist(input, other, p2) 計算兩個Tensor之間的p-范數 1.2 主要參數 input輸入張量other另一個輸入張量p范數 input 和 other的形狀需要是可廣播的 1.3 舉例 import torchxtorch.randn(4) x #tensor([ 1.2698, -0.1209, 0.0462, -1.3271…

基于PaddleOCR銀行卡識別實現(四)之uni-app離線插件

目的 在前三篇文章中完成了銀行卡識別整個模型訓練等工作,通過了解PaddleOCR的端側部署,我們也可以將銀行卡號檢測模型和識別模型移植到手機中,做成一款uni-app手機端離線銀行卡號識別的應用。 準備工作 為了不占用過多篇幅,這…

Nginx的性能優化、安全以及防盜鏈配置

目錄 一、nginx的日志分割 二、nginx性能優化之啟用epoll模型 三、nginx性能優化之設置worker進程數并與cpu進行綁核 四、nginx性能優化之調整worker的最大打開文件數和最大處理連接請求數量 五、nginx性能優化之啟用gzip壓縮,提高傳輸,減少帶寬 六…

字節iconpark基于vue使用

1.安裝 npm i icon-park/vue 2.導入 說明:導入并在main.js使用。 import { install } from icon-park/vue/es/all; import icon-park/vue/styles/index.css; Vue.use(install) 3.打開官網 ByteDance IconPark 4.復制 說明:點擊官方圖標庫&#xff0c…

Java-JDBC操作MySQL

Java-JDBC操作MySQL 文章目錄 Java-JDBC操作MySQL一、Java-JDBC-MySQL的關系二、創建連接三、登錄MySQL四、操作數據庫1、返回型操作2、無返回型操作 練習題目及完整代碼 一、Java-JDBC-MySQL的關系 #mermaid-svg-B7qjXrosQaCOwRos {font-family:"trebuchet ms",verd…

國產Type-C PD芯片—接口快充取電芯片

常用USB PDTYPE-C受電端,即設備端協議IC芯片(PD Sink,也叫PD誘騙芯片),誘導取電芯片。 產品介紹 LDR6328: ◇ 采用 SOP-8 封裝 ◇ 兼容 USB PD 3.0 規范,支持 USB PD 2.0 ◇ 兼容 QC 3.0 規范&#x…

TailwindCSS 支持文本文字超長溢出截斷、文字文本省略號

前言 文本文字超長截斷并自動補充省略號,這是前端日常開發工作中常用的樣式設置能力,文字超長截斷主要分為單行超長截斷和多行超長截斷。本文通過介紹基本CSS樣式、tailwindcss 類設置兩種基礎方式來實現文字超長截斷。 TailwindCSS 設置 單行文字超長…

WPF仿網易云搭建筆記(2):組件化開發

文章目錄 前言專欄和Gitee倉庫依賴屬性實戰:縮小,全屏,關閉按鈕依賴屬性操作封裝主窗口傳遞this本身給TitleView標題控件主要代碼MainWindow.xmalMainWindow.cs依賴屬性方法封裝TitleView.csTitleViewModelTitleViewModel實現效果 前言 這次…

基于以太坊的智能合約開發Solidity(函數繼承篇)

參考教程:【實戰篇】1、函數重載_嗶哩嗶哩_bilibili 1、函數重載: pragma solidity ^0.5.17;contract overLoadTest {//不帶參數function test() public{}//帶一個參數function test(address account) public{}//參數類型不同,雖然uint160可…

發送、接收消息,界面不及時刷新

發送、接收消息后 UI 沒展示,不及時刷新,大概率 是 SDK 的 UI 刷新功能被干擾,參考下面排查: 檢查 initWithAppkey 和 connectWithToken 使用的是否是 IMKit 核心類 RCIM 的方法,如果不是,請換成 RCIM 的。…

【刷題】位運算

2 n 2^n 2n 1<<n判斷某一位是否為1 s&1<<k將上面兩個組合&#xff0c;可以得到判斷一個集合中哪些內容包含&#xff0c;遍歷所有情況。 100140. 關閉分部的可行集合數目 一個公司在全國有 n 個分部&#xff0c;它們之間有的有道路連接。一開始&#xff0c;…