一、時(shí)鐘系統(tǒng)??
? ? ? ? 時(shí)鐘系統(tǒng)是CPU的脈搏, 由于STM32 本身非常復(fù)雜,外設(shè)非常的多,并不是所有外設(shè)都需要系統(tǒng)時(shí)鐘這么高的頻率,比如看門狗以及 RTC 只需要幾十 k 的時(shí)鐘即可,因此STM32F4 的時(shí)鐘系統(tǒng)比較復(fù)雜,不像簡(jiǎn)單的 51 單片機(jī)一個(gè)系統(tǒng)時(shí)鐘就可以解決一切。
? ? ? ? 如下圖所示,STM32F4有5個(gè)最重要的時(shí)鐘源,分別為HSI(高速內(nèi)部時(shí)鐘)、HSE(高速外部時(shí)鐘)、LSI(低速內(nèi)部時(shí)鐘)、LSE(低速外部時(shí)鐘)、PLL(鎖相環(huán)倍頻輸出)。在這 5 個(gè)中 HSI,HSE 以及 PLL 是高速時(shí)鐘,LSI 和 LSE 是低速時(shí)鐘。從來源可分為外部時(shí)鐘源和內(nèi)部時(shí)鐘源,外部時(shí)鐘源就是從外部通過接晶振的方式獲取時(shí)鐘源,其中 HSE 和LSE 是外部時(shí)鐘源,其他的是內(nèi)部時(shí)鐘源。
LSI時(shí)鐘:
????????LSI 是低速內(nèi)部時(shí)鐘,RC 振蕩器,頻率為 32kHz 左右。供獨(dú)立看門狗和自動(dòng)喚醒單元使用;
LSE時(shí)鐘:?
????????LSE 是低速外部時(shí)鐘,接頻率為 32.768kHz 的石英晶體。主要是 RTC 的時(shí)鐘源;
HSE時(shí)鐘:?
????????HSE 是高速外部時(shí)鐘,可接石英/陶瓷諧振器,或者接外部時(shí)鐘源,頻率范圍為4MHz~26MHz。HSE 也可以直接做為系統(tǒng)時(shí)鐘或者 PLL 輸入;
HSI 時(shí)鐘:?
????????HSI 是高速內(nèi)部時(shí)鐘,RC 振蕩器產(chǎn)生,頻率為 16MHz??梢灾苯幼鳛橄到y(tǒng)時(shí)鐘或者用作 PLL輸入。?
?PLL配置:
? ? ? ? STM32F4具有兩個(gè) PLL:
- 主 PLL (PLL) 由 HSE 或 HSI 振蕩器提供時(shí)鐘信號(hào),并具有兩個(gè)不同的輸出時(shí)鐘:
- ?第一個(gè)輸出用于生成高速系統(tǒng)時(shí)鐘(最高達(dá) 168 MHz)
- 第二個(gè)輸出用于生成 USB OTG FS 的時(shí)鐘 (48 MHz)、隨機(jī)數(shù)發(fā)生器的時(shí)鐘 (48 MHz) 和 SDIO 時(shí)鐘 (? 48 MHz)。
- 專用 PLL (PLLI2S) 用于生成精確時(shí)鐘,從而在 I2S 接口實(shí)現(xiàn)高品質(zhì)音頻性能。
? ? ? ? ?如上圖所示,主 PLL 時(shí)鐘的時(shí)鐘源要先經(jīng)過一個(gè)分頻系數(shù)為 M 的分頻器,然后經(jīng)過
倍頻系數(shù)為 N 的倍頻器出來之后的時(shí)候還需要經(jīng)過一個(gè)分頻系數(shù)為 P(第一個(gè)輸出 PLLP)或者 Q(第二個(gè)輸出 PLLQ)的分頻器分頻之后,最后才生成最終的主 PLL 時(shí)鐘。假如我們的外部晶振選擇 8MHz。同時(shí)我們?cè)O(shè)置相應(yīng)的分頻器 M=8,倍頻器倍頻系數(shù) N=336,分頻器分頻系數(shù) P=2,那么主 PLL 生成的第一個(gè)輸出高速時(shí)鐘 PLLP 為:PLL=8MHz * N/ (M*P)=8MHz* 336 /(8*2) = 168MHz?
? ? ? ? 根據(jù)上文分析,系統(tǒng)時(shí)鐘SYSCLK可來源于三個(gè)時(shí)鐘源:HSI時(shí)鐘,HSE時(shí)鐘及PLL時(shí)鐘。而PLL是由HSI或HSE產(chǎn)生。
? ? ? ? 由上文圖片中可以看出來,任何一個(gè)外設(shè)資源在使用之前都必須先使能相應(yīng)的時(shí)鐘。
? ? ? ? 系統(tǒng)時(shí)鐘的配置在main()函數(shù)之前,也就是說,在運(yùn)行main()函數(shù)之前,必須保證系統(tǒng)時(shí)鐘已經(jīng)配置好了。但是,我們?cè)趯?shí)際的使用過程中,好像從來沒有主動(dòng)配置過時(shí)鐘,這是因?yàn)橄到y(tǒng)已經(jīng)配置好了,在啟動(dòng)項(xiàng)配置中,我們可以看到在執(zhí)行main()函數(shù)之前,先執(zhí)行了系統(tǒng)函數(shù)SystemInit(void),該函數(shù)主要對(duì)系統(tǒng)時(shí)鐘進(jìn)行了配置。
二、中斷
? ? ? ? 通常,把CPU內(nèi)部產(chǎn)生的緊急事件叫做異常,如非法指令(除零)、地址訪問越界等,把來自CPU外部的片上外設(shè)產(chǎn)生的緊急事件叫做中斷,如GPIO引腳電平變化,定時(shí)器溢出等。異常和中斷的效果基本一致,都是暫停當(dāng)前任務(wù),優(yōu)先執(zhí)行緊急事件。因此,一般將中斷和異常統(tǒng)稱為中斷。
????????CM4 內(nèi)核支持 256 個(gè)中斷,其中包含了 16 個(gè)內(nèi)核中斷和 240 個(gè)外部中斷。????????????????STM32F40xx/STM32F41xx 總共有 92 個(gè)中斷,這92 個(gè)中斷里面,包括 10 個(gè)內(nèi)核中斷和 82 個(gè)可屏蔽中斷,具有 16 級(jí)可編程的中斷優(yōu)先級(jí),而我們常用的就是這 82 個(gè)可屏蔽中斷。
????????這么多中斷,應(yīng)該如何進(jìn)行管理呢?我們通過中斷控制器NVIC進(jìn)行管理。
2.1 NVIC(嵌套向量中斷控制器)
????????NVIC 英文全稱是 Nested Vectored Interrupt Controller,它通過配置分組對(duì)中斷進(jìn)行管理。如下圖組0~4所示,同時(shí),它對(duì)每個(gè)中斷設(shè)置一個(gè)搶占優(yōu)先級(jí)和一個(gè)響應(yīng)優(yōu)先級(jí)。
????????分組配置是在寄存器SCB->AIRCR中配置;該寄存器的10~8為設(shè)置了中斷的組,每個(gè)組確定之后,該中斷的寄存器IP(7~4位)的分配情況也就確定了,如下圖所示,如果中斷設(shè)置為組別3,那么寄存器IP的分配情況就是IP配置中有3位用來設(shè)置搶占優(yōu)先級(jí),1位用來配置響應(yīng)優(yōu)先級(jí)。
搶占優(yōu)先級(jí)和響應(yīng)優(yōu)先級(jí):
- 高優(yōu)先級(jí)的搶占優(yōu)先級(jí)是可以打斷正在進(jìn)行的低搶占優(yōu)先級(jí)中斷的
- 搶占優(yōu)先級(jí)相同的中斷,高響應(yīng)優(yōu)先級(jí)不可以打斷低響應(yīng)優(yōu)先級(jí)的中斷
- 搶占優(yōu)先級(jí)相同的中斷,當(dāng)兩個(gè)中斷同時(shí)發(fā)生的時(shí)候,哪個(gè)響應(yīng)優(yōu)先級(jí)高,哪個(gè)先執(zhí)行;
- 如果兩個(gè)中斷的搶占優(yōu)先級(jí)和響應(yīng)優(yōu)先級(jí)都是一樣的,那么看哪個(gè)中斷先發(fā)生就先執(zhí)行。
????????例:假定設(shè)置中斷優(yōu)先級(jí)分組為2,然后設(shè)置:中斷3(RTC中斷)的搶占優(yōu)先級(jí)為2,響應(yīng)優(yōu)先級(jí)為1。中斷6(外部中斷0)的搶占優(yōu)先級(jí)為3,響應(yīng)優(yōu)先級(jí)為0,中斷7(外部中斷1)的搶占優(yōu)先級(jí)為2,響應(yīng)優(yōu)先級(jí)為0.
????????分析,首先判斷搶占優(yōu)先級(jí),哪個(gè)搶占優(yōu)先級(jí)高,那么其中斷優(yōu)先級(jí)就高,可以看但中斷3和中斷7搶占優(yōu)先級(jí)都是2,那么中斷3和中斷7之間不存在相互打斷。而中斷7的響應(yīng)優(yōu)先級(jí)是0,中斷3的響應(yīng)優(yōu)先級(jí)是1,中斷7的響應(yīng)優(yōu)先級(jí)高于中斷3,因此,當(dāng)中斷7和中斷3同時(shí)發(fā)生中斷時(shí),中斷7優(yōu)先響應(yīng)。
? ? ? ? 那么,這三個(gè)中斷的優(yōu)先級(jí)順序?yàn)椋?strong>中斷7>中斷3>中斷6
特別說明:
? ? ? ? 一般情況下,系統(tǒng)代碼執(zhí)行過程中,只設(shè)置一次中斷優(yōu)先級(jí)分組,比如設(shè)置號(hào)分組2之后,一般不會(huì)再改變分組了,否則,隨意改變分組會(huì)導(dǎo)致中斷管理混亂,程序出現(xiàn)意想不到的執(zhí)行結(jié)果。
? ? ? ? 這是因?yàn)?,不同的分組,搶占優(yōu)先級(jí)和響應(yīng)優(yōu)先級(jí)位數(shù)是不同的,若貿(mào)然改變,其代表的數(shù)值會(huì)被打亂。
程序中,可以調(diào)用下面的函數(shù)設(shè)置中斷優(yōu)先級(jí)分組:
void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup) { /* Check the parameters */ assert_param(IS_NVIC_PRIORITY_GROUP(NVIC_PriorityGroup)); /* Set the PRIGROUP[10:8] bits according to NVIC_PriorityGroup value */ SCB->AIRCR = AIRCR_VECTKEY_MASK | NVIC_PriorityGroup; }
? ? ? ? 設(shè)置好中斷分組之后,如何設(shè)置單個(gè)中斷的搶占優(yōu)先級(jí)和響應(yīng)優(yōu)先級(jí)呢,我們通過結(jié)構(gòu)體NVIC_Type來進(jìn)行設(shè)置,如下所示:
typedef struct
{
__IO uint32_t ISER[8]; /*!< 中斷使能寄存器組 */
uint32_t RESERVED0[24];
__IO uint32_t ICER[8]; /*!< 中斷失能寄存器組 */
uint32_t RSERVED1[24];
__IO uint32_t ISPR[8]; /*!< 中斷掛起寄存器組 */
uint32_t RESERVED2[24];
__IO uint32_t ICPR[8]; /*!< 中斷解掛寄存器組 */
uint32_t RESERVED3[24];
__IO uint32_t IABR[8]; /*!< 中斷激活標(biāo)志位寄存器組 */
uint32_t RESERVED4[56];
__IO uint8_t IP[240]; /*!< 中斷優(yōu)先級(jí)控制的寄存器組 */
uint32_t RESERVED5[644];
__O uint32_t STIR; /*!< Software Trigger Interrupt Register */
} NVIC_Type;
通過上述結(jié)構(gòu)體,可以完成對(duì)中斷優(yōu)先級(jí)的設(shè)置:
? ? ? ? 中斷優(yōu)先級(jí)控制的寄存器組是:IP[240]
? ? ? ? 這240個(gè)8位寄存器,每個(gè)中斷使用其中一個(gè)寄存器來確定優(yōu)先級(jí)。根據(jù)中斷分組的不同,每個(gè)IP寄存器的高4位用來設(shè)置搶占優(yōu)先級(jí)和響應(yīng)優(yōu)先級(jí)。低四位沒有用到。
? ? ? ? 再庫函數(shù)中,我們使用如下函數(shù)來初始化中斷
void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct)?
? ? ? ? 該函數(shù)的入口參數(shù)NVIC_InitTypeDef是一個(gè)結(jié)構(gòu)體:
typedef struct
{
uint8_t NVIC_IRQChannel;
uint8_t NVIC_IRQChannelPreemptionPriority;
uint8_t NVIC_IRQChannelSubPriority;
FunctionalState NVIC_IRQChannelCmd;
} NVIC_InitTypeDef;
NVIC_InitTypeDef結(jié)構(gòu)體屬性如下:
????????NVIC_IRQChannel:定義初始化的是哪個(gè)中斷,這個(gè)我們可以在 stm32f4xx.h 中定義的枚舉類?型 IRQn 的成員變量中可以找到每個(gè)中斷對(duì)應(yīng)的名字。例如串口 1 對(duì)應(yīng)USART1_IRQn。?
????????NVIC_IRQChannelPreemptionPriority:定義這個(gè)中斷的搶占優(yōu)先級(jí)別。?
????????NVIC_IRQChannelSubPriority:定義這個(gè)中斷的響應(yīng)優(yōu)先級(jí)別。?
????????NVIC_IRQChannelCmd:該中斷通道是否使能。?
?中斷優(yōu)先級(jí)設(shè)置步驟:
? ? ? ? 1. 系統(tǒng)運(yùn)行后先設(shè)置中斷優(yōu)先級(jí)分組,調(diào)用函數(shù):NVIC_PriorityGroupConfig();再整個(gè)系統(tǒng)運(yùn)行的過程中,只設(shè)置一次中斷分組;
? ? ? ? 2. 針對(duì)每一個(gè)中斷,設(shè)置對(duì)應(yīng)的搶占優(yōu)先級(jí)和響應(yīng)優(yōu)先級(jí);調(diào)用函數(shù)NVIC_Init();?
?2.2 外部中斷
? ? ? ? STM32F4 的每個(gè) IO 都可以作為外部中斷的中斷輸入口,這點(diǎn)也是 STM32F4 的強(qiáng)大之處。STM32F407 的中斷控制器支持 22 個(gè)外部中斷/事件請(qǐng)求。每個(gè)中斷設(shè)有狀態(tài)位,每個(gè)中斷/事件都有獨(dú)立的觸發(fā)和屏蔽設(shè)置。STM32F407的 22 個(gè)外部中斷為:
- EXTI 線 0~15:對(duì)應(yīng)外部 IO 口的輸入中斷。?
- EXTI 線 16:連接到 PVD 輸出。?
- EXTI 線 17:連接到 RTC 鬧鐘事件。?
- EXTI 線 18:連接到 USB OTG FS 喚醒事件。?
- EXTI 線 19:連接到以太網(wǎng)喚醒事件。?
- EXTI 線 20:連接到 USB OTG HS(在 FS 中配置)喚醒事件。?
- EXTI 線 21:連接到 RTC 入侵和時(shí)間戳事件。?
- EXTI 線 22:連接到 RTC 喚醒事件。?
? ? ? ? 從上文可以看出,STM32F4供IO使用的中斷線只有16個(gè)(每個(gè)中斷線才能產(chǎn)生一個(gè)中斷請(qǐng)求),但是?STM32F4 的 IO 口卻遠(yuǎn)遠(yuǎn)不止 16 個(gè),那么 STM32F4 是怎么把 16 個(gè)中斷線和 IO 口一一對(duì)應(yīng)起來的呢?
? ? ? ? ?如下圖所示,STM32是這樣設(shè)計(jì)的:GPIO 的管腳GPIOx.0~GPIOx.15(x=A,B,C,D,E,F(xiàn),G,H,I)分別對(duì)應(yīng)中斷線 0~15。這樣每個(gè)中斷線對(duì)應(yīng)了最多 9 個(gè) IO 口,以線 0 為例:它對(duì)應(yīng)了GPIOA.0、GPIOB.0、GPIOC.0、GPIOD.0、GPIOE.0、GPIOF.0、GPIOG.0,GPIOH.0,GPIOI.0。而中斷線每次只能連接到 1 個(gè) IO口上,這樣就需要通過配置來決定對(duì)應(yīng)的中斷線配置到哪個(gè) GPIO 上了。
? ? ? ? 有16個(gè)中斷線EXTI 線 0~15,是否意味著有16個(gè)中斷服務(wù)函數(shù)呢?不是的,IO口外部中斷在中斷向量表中只分配了7個(gè)中斷向量,也就是 只能使用7個(gè)中斷服務(wù)函數(shù),如下表所示:
? ? ? ? 從上表中可以看出,外部中斷線5~9分配了一個(gè)中斷向量,共用一個(gè)服務(wù)函數(shù),外部中斷線10~15分配了一個(gè)中斷向量,共用一個(gè)中斷服務(wù)函數(shù)。中斷服務(wù)函數(shù)列表如下:
- EXTI0_IRQHandler
- EXTI1_IRQHandler
- EXTI2_IRQHandler
- EXTI3_IRQHandler
- EXTI4_IRQHandler
- EXTI9_5_IRQHandler
- EXTI15_10_IRQHandler
?外部中斷庫函數(shù)配置過程:
? ? ? ? 1.?使能 IO 口時(shí)鐘,初始化 IO 口為輸入?
? ? ? ? 2.?開啟 SYSCFG 時(shí)鐘,設(shè)置 IO 口與中斷線的映射關(guān)系:
? ? ? ? 初始化IO口之后,我們需要配置GPIO與中斷線的映射關(guān)系,首先需要打開SYSCFG 時(shí)鐘,只要使用到外部中斷,就必須打開SYSCFG時(shí)鐘:
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);//使能 SYSCFG 時(shí)鐘
然后配置GPIO與中斷線的映射關(guān)系,調(diào)用如下函數(shù):
void SYSCFG_EXTILineConfig( uint8_t EXTI_PortSourceGPIOx, uint8_t EXTI_PinSourcex ); // 使用范例 SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOA, EXTI_PinSource0);
? ? ? ? 3. 初始化線上中斷,設(shè)置出發(fā)條件等:
????????中斷線上中斷的初始化是通過函數(shù) EXTI_Init()實(shí)現(xiàn)的。EXTI_Init()函數(shù)的定義是:
void EXTI_Init(EXTI_InitTypeDef* EXTI_InitStruct);
其輸入?yún)?shù)EXTI_InitTypeDef結(jié)構(gòu)體成員函數(shù)如下所示:
typedef struct { // 中斷標(biāo)號(hào),對(duì)于外部中斷,取值范圍為EXTI_Line0~EXTI_Line15 uint32_t EXTI_Line; // 要初始化的中斷線 // 中斷模式,可選為中斷EXTI_Mode_Interrupt 和事件 EXTI_Mode_Event。 EXTIMode_TypeDef EXTI_Mode; // 中斷出發(fā)方式,可以是下降沿觸發(fā)EXTI_Trigger_Falling,上升沿觸發(fā) EXTI_Trigger_Rising,或者任意電平(上升沿和下降沿)觸發(fā) EXTI_Trigger_Rising_Falling, EXTITrigger_TypeDef EXTI_Trigger; // 使能中斷線 FunctionalState EXTI_LineCmd; }EXTI_InitTypeDef;
? ? ? ? 4. 配置中斷分組(NVIC)并使能中斷
? ? ? ? NVIC的配置如上文所述,此處給出一個(gè)配置示例,如下所示:
NVIC_InitTypeDef NVIC_InitStructure; NVIC_InitStructure.NVIC_IRQChannel = EXTI2_IRQn; //使能外部中斷通道 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02; //搶占優(yōu)先級(jí)2, NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02; //響應(yīng)優(yōu)先級(jí)2 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能外部中斷通道 NVIC_Init(&NVIC_InitStructure); //中斷優(yōu)先級(jí)分組初始化
? ? ? ? 5. 編寫中斷服務(wù)函數(shù)
????????在配置完中斷優(yōu)先級(jí)之后,接著要做的就是編寫中斷服務(wù)函數(shù)。在編寫中斷服務(wù)函數(shù)的時(shí)候會(huì)經(jīng)常使用到兩個(gè)函數(shù),第一個(gè)函數(shù)是判斷某個(gè)中斷線上的中斷是否發(fā)生(標(biāo)志位是否置位):
ITStatus EXTI_GetITStatus(uint32_t EXTI_Line);
這個(gè)函數(shù)一般使用在中斷服務(wù)函數(shù)的開頭判斷中斷是否發(fā)生。另一個(gè)函數(shù)是清除某個(gè)中斷線上的中斷標(biāo)志位:?
void EXTI_ClearITPendingBit(uint32_t EXTI_Line);
中斷配置示例:
#include "exti.h"
#include "led.h"
#include "SysTick.h"
#include "beep.h"
/**
* IO口外部中斷
*/
void KEYExti_Init()
{
NVIC_InitTypeDef NVIC_InitStructure;
EXTI_InitTypeDef EXTI_InitStructure;
//1.使能IO
GPIO_InitTypeDef GPIO_InitStructure; //定義結(jié)構(gòu)體變量
//時(shí)鐘使能
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF,ENABLE);
GPIO_InitStructure.GPIO_Pin=KEY0_PIN|KEY1_PIN|KEY2_PIN|KEY3_PIN;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN; //GPIO模式為普通輸入模式
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; //100M
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉
GPIO_Init(GPIOF, &GPIO_InitStructure); //初始化GPIOF6,7,8,9
//2.開啟SYSCFC時(shí)鐘,以便設(shè)置IO口與中斷線之間的映射關(guān)系。
//只要使用外部中斷,就必須打開SYSCFG時(shí)鐘
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG,ENABLE);
//配置IO口與中斷線之間的映射關(guān)系
//配置IO口與中斷線之間的映射關(guān)系
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOF,EXTI_PinSource9);//將中斷線9與GPIOF映射
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOF,EXTI_PinSource8);//將中斷線8與GPIOF映射
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOF,EXTI_PinSource7);//將中斷線7與GPIOF映射
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOF,EXTI_PinSource6);//將中斷線6與GPIOF映射
//3.配置中斷分組,使能中斷
NVIC_InitStructure.NVIC_IRQChannel=EXTI9_5_IRQn; //中斷通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2; //搶占優(yōu)先級(jí)
NVIC_InitStructure.NVIC_IRQChannelSubPriority =3; //響應(yīng)優(yōu)先級(jí)
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE; //中斷通道使能
//中斷優(yōu)先級(jí)分組初始化
NVIC_Init(&NVIC_InitStructure);
//4.初始化EXTI,設(shè)置中斷觸發(fā)條件
EXTI_InitStructure.EXTI_Line=EXTI_Line9|EXTI_Line8|EXTI_Line7|EXTI_Line6; //中斷線標(biāo)號(hào),
EXTI_InitStructure.EXTI_Mode=EXTI_Mode_Interrupt; //中斷模式,可選的值為中斷和事件
EXTI_InitStructure.EXTI_Trigger=EXTI_Trigger_Falling; //觸發(fā)方式:下降沿觸發(fā)或上升沿觸發(fā)
EXTI_InitStructure.EXTI_LineCmd=ENABLE;
EXTI_Init(&EXTI_InitStructure);
}
//中斷函數(shù)
void EXTI9_5_IRQHandler()
{
if(EXTI_GetITStatus(EXTI_Line9) != RESET)
{
GPIO_SetBits(GPIOG,GPIO_Pin_7);//復(fù)位F9 點(diǎn)亮D1
delay_ms(500); //精確延時(shí)500ms
GPIO_ResetBits(GPIOG,GPIO_Pin_7);
delay_ms(500); //精確延時(shí)500ms
EXTI_ClearITPendingBit(EXTI_Line9);
}
if(EXTI_GetITStatus(EXTI_Line8) != RESET)
{
GPIO_ResetBits(GPIOE,GPIO_Pin_3);//復(fù)位F9 點(diǎn)亮D1
GPIO_SetBits(GPIOE,GPIO_Pin_4);
GPIO_SetBits(GPIOG,GPIO_Pin_9);
EXTI_ClearITPendingBit(EXTI_Line8);
}
if(EXTI_GetITStatus(EXTI_Line7) != RESET)
{
GPIO_ResetBits(GPIOE,GPIO_Pin_4);//復(fù)位F9 點(diǎn)亮D1
GPIO_SetBits(GPIOE,GPIO_Pin_3);
GPIO_SetBits(GPIOG,GPIO_Pin_9);
EXTI_ClearITPendingBit(EXTI_Line7);
}
if(EXTI_GetITStatus(EXTI_Line6) != RESET)
{
GPIO_ResetBits(GPIOG,GPIO_Pin_9);//復(fù)位F9 點(diǎn)亮D1
GPIO_SetBits(GPIOE,GPIO_Pin_3);
GPIO_SetBits(GPIOE,GPIO_Pin_4);
EXTI_ClearITPendingBit(EXTI_Line6);
}
}
三、定時(shí)器
3.1 三種定時(shí)器區(qū)別
3.2 通用定時(shí)器
????????STM32F4 的通用定時(shí)器包含一個(gè) 16 位或 32 位自動(dòng)重載計(jì)數(shù)器(CNT),該計(jì)數(shù)器由可編程預(yù)分頻器(PSC)驅(qū)動(dòng)。STM32F4 的通用定時(shí)器可以被用于:測(cè)量輸入信號(hào)的脈沖長(zhǎng)度(輸入捕獲)或者產(chǎn)生輸出波形(輸出比較和 PWM)等;
????????STM3 的通用 TIMx (TIM2~TIM5 和TIM9~TIM14)定時(shí)器功能包括:
- 16 位/32 位(僅 TIM2 和TIM5)向上、向下、向上/向下自動(dòng)裝載計(jì)數(shù)器(TIMx_CNT)
- 16 位可編程(可以實(shí)時(shí)修改)預(yù)分頻器(TIMx_PSC),計(jì)數(shù)器時(shí)鐘頻率的分頻系數(shù)為 1~65535 之間的任意數(shù)值。 (預(yù)分頻器配置幾個(gè)時(shí)鐘計(jì)數(shù)一次)
-
4 個(gè)獨(dú)立通道(TIMx_CH1~4,TIM9~TIM14 最多 2 個(gè)通道),這些通道可以用來作為:
- A.輸入捕獲?
- B.輸出比較
- C.PWM 生成(邊緣或中間對(duì)齊模式)?
- D.單脈沖模式輸出
- 可使用外部信號(hào)(TIMx_ETR)控制定時(shí)器和定時(shí)器互連(可以用 1 個(gè)定時(shí)器控制另外一個(gè)定時(shí)器)的同步電路。?
-
如下事件發(fā)生時(shí)產(chǎn)生中斷/DMA(TIM9~TIM14 不支持 DMA):
- A.更新:計(jì)數(shù)器向上溢出/向下溢出,計(jì)數(shù)器初始化(通過軟件或者內(nèi)部/外部觸發(fā))?
- B.觸發(fā)事件(計(jì)數(shù)器啟動(dòng)、停止、初始化或者由內(nèi)部/外部觸發(fā)計(jì)數(shù))?
- C.輸入捕獲
- D.輸出比較?
- E.支持針對(duì)定位的增量(正交)編碼器和霍爾傳感器電路(TIM9~TIM14 不支持)
- F.觸發(fā)輸入作為外部時(shí)鐘或者按周期的電流管理(TIM9~TIM14 不支持)?
通用定時(shí)器框圖如下所示:
? ? ? ? ?以上框圖可以分為幾個(gè)部分,其中:
1. 左上部分為時(shí)鐘來源,通用定時(shí)器的時(shí)鐘來源有四種可選:
- 內(nèi)部時(shí)鐘(CK_INT)
- 外部時(shí)鐘模式1:外部輸入引腳TIx(x=1,2,3,4)
- 外部時(shí)鐘模式 2:外部觸發(fā)輸入 ETR
- 內(nèi)部觸發(fā)輸入(ITRx(x=0,1,2,3)):使用一個(gè)定時(shí)器作為另一個(gè)定時(shí)器的預(yù)分頻器,如可以配置一個(gè)定時(shí)器Timer1作為另一個(gè)定時(shí)器Timer2的預(yù)分頻器。
2. 時(shí)基單元
????????通用定時(shí)器時(shí)基單元包括 3 個(gè)寄存器,分別是計(jì)數(shù)器寄存器(TIMx_CNT)、預(yù)分頻器寄存器(TIMx_PSC)、自動(dòng)重載寄存器(TIMx_ARR)。
????????預(yù)分頻器寄存器(TIMx_PSC),用于對(duì)計(jì)數(shù)器時(shí)鐘頻率進(jìn)行分頻,通過寄存器內(nèi)的相應(yīng)位設(shè)置,分頻系數(shù)值可在 1 到 65536 之間。
????????通用定時(shí)器計(jì)數(shù)方式有向上計(jì)數(shù)、向下計(jì)數(shù)、向上向下計(jì)數(shù)(中心對(duì)齊計(jì)數(shù))。
3. 輸入捕獲
????????輸入捕獲可以對(duì)輸入的信號(hào)的上升沿,下降沿或者雙邊沿進(jìn)行捕獲,通常用于測(cè)量輸入信號(hào)的脈寬、測(cè)量 PWM 輸入信號(hào)的頻率及占空比。
4. 輸出比較
????????輸出比較就是通過定時(shí)器的外部引腳對(duì)外輸出控制信號(hào),可以輸出有效電平、無效電平、翻轉(zhuǎn)、強(qiáng)制變?yōu)闊o效電平、強(qiáng)制變?yōu)橛行щ娖健?PWM1 和 PWM2等模式,具體使用哪種模式由寄存器 CCMRx 的位 OCxM[2:0]配置。其中 PWM 模式是輸出比較中的特例,使用的也最多。
內(nèi)部時(shí)鐘的選擇:
?在默認(rèn)調(diào)用SystemInit函數(shù)的情況下:
? ? ? ? SYSCLK=168M
? ? ? ? AHB1時(shí)鐘=168M
? ? ? ? APB1時(shí)鐘=42M
? ? ? ? 所以,APB1的分頻系數(shù)=AHB/APB1時(shí)鐘=4,所以通用定時(shí)器時(shí)鐘CK_INT=2*42=84M
3.3 定時(shí)器配置過程(以TIM3為例)
1. TIM3時(shí)鐘使能:
????????TIM3 是掛載在 APB1 之下,所以我們通過 APB1 總線下的使能使能函數(shù)來使能 TIM3。調(diào)用的函數(shù)是:?
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE); ///使能 TIM3 時(shí)鐘
2.?初始化定時(shí)器參數(shù),設(shè)置自動(dòng)重裝值,分頻系數(shù),計(jì)數(shù)方式等:
????????在庫函數(shù)中,定時(shí)器的初始化參數(shù)是通過初始化函數(shù) TIM_TimeBaseInit 實(shí)現(xiàn)的:
void TIM_TimeBaseInit(
TIM_TypeDef*TIMx,
TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct
);
????????第一個(gè)參數(shù)是確定是哪個(gè)定時(shí)器。第二個(gè)參數(shù)是定時(shí)器初始化參數(shù)結(jié)構(gòu)體指針,結(jié)構(gòu)體類型為 TIM_TimeBaseInitTypeDef,如下所示:
{
uint16_t TIM_Prescaler; // 設(shè)置分頻系數(shù)
uint16_t TIM_CounterMode; // 設(shè)置計(jì)數(shù)方式(向上,向下,中心)
uint16_t TIM_Period; // 自動(dòng)重載計(jì)數(shù)周期值
uint16_t TIM_ClockDivision; // 時(shí)鐘分頻因子
uint8_t TIM_RepetitionCounter;
} TIM_TimeBaseInitTypeDef;
3. 定時(shí)器中斷類型設(shè)置
????????因?yàn)槲覀円褂?TIM3 的更新中斷,寄存器的相應(yīng)位便可使能更新中斷。在庫函數(shù)里面定時(shí)器中斷使能是通過 TIM_ITConfig 函數(shù)來實(shí)現(xiàn)的:?
void TIM_ITConfig(
TIM_TypeDef* TIMx, // 定時(shí)器號(hào),取值TIM1~TIM7
uint16_t TIM_IT, // 指定定時(shí)器中斷類型,包括更新中斷TIM_IT_Update、觸發(fā)中斷TIM_IT_Trigger,以及輸入捕獲中斷等
FunctionalState NewState // 使能或失能
);
4. TIM3中斷優(yōu)先級(jí)配置
????????在定時(shí)器中斷使能之后,因?yàn)橐a(chǎn)生中斷,必不可少的要設(shè)置 NVIC 相關(guān)寄存器,設(shè)置中
斷優(yōu)先級(jí)。
5. 使能定時(shí)器
????????光配置好定時(shí)器還不行,沒有開啟定時(shí)器,照樣不能用。我們?cè)谂渲猛旰笠_啟定時(shí)器,通過 TIM3_CR1 的CEN 位來設(shè)置。在固件庫里面使能定時(shí)器的函數(shù)是通過 TIM_Cmd 函數(shù)來實(shí)現(xiàn)的:?
void TIM_Cmd(TIM_TypeDef* TIMx, FunctionalState NewState)
// 使用示例
TIM_Cmd(TIM3, ENABLE); //使能 TIMx 外設(shè)
6. 編寫中斷服務(wù)函數(shù)
????????在最后,還是要編寫定時(shí)器中斷服務(wù)函數(shù),通過該函數(shù)來處理定時(shí)器產(chǎn)生的相關(guān)中斷。在中斷產(chǎn)生后,通過狀態(tài)寄存器的值來判斷此次產(chǎn)生的中斷屬于什么類型。然后執(zhí)行相關(guān)的操作,我們這里使用的是更新(溢出)中斷,所以在狀態(tài)寄存器 SR 的最低位。在處理完中斷之后應(yīng)該向 TIM3_SR 的最低位寫 0,來清除該中斷標(biāo)志。?
????????在固件庫函數(shù)里面,用來讀取中斷狀態(tài)寄存器的值判斷中斷類型的函數(shù)是:?
ITStatus TIM_GetITStatus(TIM_TypeDef* TIMx, uint16_t)
????????該函數(shù)的作用是,判斷定時(shí)器 TIMx 的中斷類型 TIM_IT 是否發(fā)生中斷。比如,我們要判斷定時(shí)器 3 是否發(fā)生更新(溢出)中斷,方法為:?
if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET){}
????????固件庫中清除中斷標(biāo)志位的函數(shù)是:?
void TIM_ClearITPendingBit(TIM_TypeDef* TIMx, uint16_t TIM_IT)
????????該函數(shù)的作用是,清除定時(shí)器 TIMx 的中斷 TIM_IT 標(biāo)志位。使用起來非常簡(jiǎn)單,比如我們?cè)?br> TIM3 的溢出中斷發(fā)生后,我們要清除中斷標(biāo)志位,方法是:
TIM_ClearITPendingBit(TIM3, TIM_IT_Update );
配置示例:
/**
* per:定時(shí)器數(shù)值
* psc:分頻系數(shù)
*/
void TIM4_Init(u16 per,u16 psc)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
//1.使能定時(shí)器時(shí)鐘 TIM4
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4,ENABLE); //使能
//2.初始化定時(shí)器參數(shù),包含自動(dòng)重裝載、分頻系數(shù)、計(jì)數(shù)方式等
TIM_TimeBaseInitStructure.TIM_Period=per; //自動(dòng)裝載值
TIM_TimeBaseInitStructure.TIM_Prescaler=psc; //分頻系數(shù)
TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1; //時(shí)鐘分頻因子
TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up; //設(shè)置向上計(jì)數(shù)模式
TIM_TimeBaseInit(TIM4,&TIM_TimeBaseInitStructure);
//3.設(shè)置定時(shí)器中斷類型
TIM_ITConfig(TIM4,TIM_IT_Update,ENABLE); //開啟定時(shí)器中斷
TIM_ClearITPendingBit(TIM4,TIM_IT_Update);
//4.設(shè)置定時(shí)器中斷優(yōu)先級(jí)
NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn;//定時(shí)器中斷通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2;//搶占優(yōu)先級(jí)
NVIC_InitStructure.NVIC_IRQChannelSubPriority =3; //子優(yōu)先級(jí)
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC_Init(&NVIC_InitStructure);
//5.開啟定時(shí)器
TIM_Cmd(TIM4,ENABLE); //使能定時(shí)器
}
/*******************************************************************************
* 函 數(shù) 名 : TIM4_IRQHandler
* 函數(shù)功能 : TIM4中斷函數(shù)
* 輸 入 : 無
* 輸 出 : 無
*******************************************************************************/
void TIM4_IRQHandler(void)
{
if(TIM_GetITStatus(TIM4,TIM_IT_Update))
{
GPIO_SetBits(GPIOG,GPIO_Pin_7);//復(fù)位F9 點(diǎn)亮D1
delay_ms(500); //精確延時(shí)500ms
GPIO_ResetBits(GPIOG,GPIO_Pin_7);
delay_ms(500); //精確延時(shí)500ms
}
TIM_ClearITPendingBit(TIM4,TIM_IT_Update);
}
3.4 PWM輸出
????????PWM 是 Pulse Width Modulation 的縮寫,中文意思就是脈沖寬度調(diào)制,簡(jiǎn)稱脈寬調(diào)制。它是利用微處理器的數(shù)字輸出來對(duì)模擬電路進(jìn)行控制的一種非常有效的技術(shù),其控制簡(jiǎn)單、靈活和動(dòng)態(tài)響應(yīng)好等優(yōu)點(diǎn)而成為電力電子技術(shù)最廣泛應(yīng)用的控制方式,其應(yīng)用領(lǐng)域包括測(cè)量,通信,功率控制與變換,電動(dòng)機(jī)控制、伺服控制、調(diào)光、開關(guān)電源,甚至某些音頻放大器,因此學(xué)習(xí) PWM 具有十分重要的現(xiàn)實(shí)意義。參考:PWM概念。
? ? ? ? PWM原理圖下圖所示:
? ? ? ? 在上圖中,我們假定定時(shí)器工作在向上計(jì)數(shù)PWM模式,那么,當(dāng)CNT<CCRx時(shí),輸出0,當(dāng)CNT>=CCRx時(shí)輸出1。那么就可以得到下發(fā)的PWM示意圖:當(dāng)CNT值小于CCRx的時(shí)候,IO輸出低電平,當(dāng)CNT值大于等于CCRx的時(shí)候,IO輸出高電平。當(dāng)CNT達(dá)到ARR值得時(shí)候,重新歸零,然后重新向上計(jì)數(shù),一次循環(huán)。通過改變CCRx的值,就可以改變PWM輸出的占空比。改變ARR的值,就可以改變PWM輸出的頻率,這就是PWM輸出的原理。
PWM工作過程:
上圖中:
- CCR1:捕獲比較寄存器(x=1,2,3,4),用來設(shè)置比較的值。
- CCMR1:捕獲/比較模式設(shè)置寄存器,OC1M[2:0]位:對(duì)于PWM方式下,用于設(shè)置PWM模式,可設(shè)置位模式1[110]或模式2[111],這兩種模式的區(qū)別就是輸出電平的極性相反。
- CCER:CC1P位,表示輸入/捕獲1的輸出極性位,若為0表示高電平有效,為1表示低電平有效。
- CCER:CC1E位,表示輸入/捕獲1的輸出使能位,若為0表示關(guān)閉,為1表示打開。
上文分析:
? ? ? ? 上文描述中,CCR1寄存器是比較值設(shè)置寄存器,用于定時(shí)器計(jì)數(shù)器CNT與該寄存器進(jìn)行比較,來決定輸出的電平。
? ? ? ? CCMR1寄存器是模式設(shè)置寄存器,通過設(shè)置寄存器0C1M[2:0]位,可以配置PWM是模式1還是模式2,PWM模式1,在向上計(jì)數(shù)時(shí),CNT<CCR1時(shí)通道1為有效電平,否則為無效電平,在向下計(jì)數(shù)時(shí),CNT>CCR1時(shí),通道1為無效電平,否則為有效電平。PWM模式2,在向上計(jì)數(shù)時(shí),CNT<CCR1時(shí)通道1為無效電平,否則為有效電平,在向下計(jì)數(shù)時(shí),CNT>CCR1時(shí),通道1為有效電平,否則為無效電平。
? ? ? ? 那么,有效電平是高電平還是低電平呢?不一定!有效電平由寄存器CCER寄存器的CC1P位決定,當(dāng)該位為0,表示高電平是有效電平。該位為1,則表示低電平有效。
PWM配置過程(以TIM14為例):
????????1. 開啟?TIM14 和 GPIO 時(shí)鐘,配置 PF9 選擇復(fù)用功能 AF9(TIM14)輸出。
? ? ? ? 2.?初始化 TIM14,設(shè)置 TIM14 的 ARR 和 PSC 等參數(shù)
?????????在開啟了 TIM14 的時(shí)鐘之后,我們要設(shè)置 ARR 和 PSC 兩個(gè)寄存器的值來控制輸出 PWM的周期。
? ? ? ? 3.?設(shè)置 TIM14_CH1 的 PWM 模式,使能 TIM14 的 CH1 輸出
????????接下來,要設(shè)置 TIM14_CH1 為PWM 模式(默認(rèn)是凍結(jié)的),在庫函數(shù)中,PWM 通道設(shè)置是通過函數(shù) TIM_OC1Init()~TIM_OC4Init()來初始化的,不同的通道的設(shè)置函數(shù)不一樣。
void TIM_OC1Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);
其中入?yún)⒔Y(jié)構(gòu)體格式如下:
typedef struct { uint16_t TIM_OCMode; // 設(shè)置模式是PWM還是輸出比較,此處為PWM uint16_t TIM_OutputState; // 設(shè)置比較輸出使能,也就是使能PWM輸出到端口 uint16_t TIM_OutputNState; */ uint16_t TIM_Pulse; // 比較的值 uint16_t TIM_OCPolarity; // 用來設(shè)置極性是高還是低 uint16_t TIM_OCNPolarity; uint16_t TIM_OCIdleState; uint16_t TIM_OCNIdleState; } TIM_OCInitTypeDef; // 應(yīng)用示例如下 TIM_OCInitTypeDef TIM_OCInitStructure; TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //選擇模式 PWM TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比較輸出使能 TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; //輸出極性低 TIM_OC1Init(TIM14, &TIM_OCInitStructure); //根據(jù)T 指定的參數(shù)初始化外設(shè)TIM1 4OC1
? ? ? ? 4. 使能TIM14
? ? ? ? 5. 修改TIM14_CCR1來控制占空比
void TIM_SetCompare1(TIM_TypeDef* TIMx, uint16_t Compare2);
示例程序:文章來源:http://www.zghlxwxcb.cn/news/detail-407405.html
#include "pwm.h"
void TIM14_CH1_PWM_Init(u16 per,u16 psc)
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
//1.使能定時(shí)器時(shí)鐘 TIM4
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF,ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM14,ENABLE);//使能TIM14時(shí)鐘
//2.設(shè)置引腳復(fù)用器映射
//由于TIM14_CH1對(duì)應(yīng)的輸出引腳并不是我們要用的LED引腳,所以使用通道映射將其映射到LED所在的IO口
GPIO_PinAFConfig(GPIOF,GPIO_PinSource11,GPIO_AF_TIM14);//管腳復(fù)用
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF; //復(fù)用輸出模式
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_11; //管腳設(shè)置
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_100MHz;//速度為100M
GPIO_InitStructure.GPIO_OType=GPIO_OType_PP;//推挽輸出
GPIO_InitStructure.GPIO_PuPd=GPIO_PuPd_UP;//上拉
GPIO_Init(GPIOF,&GPIO_InitStructure); //初始化結(jié)構(gòu)體
//3.初始化PWM輸出參數(shù)。
TIM_OCInitStructure.TIM_OCMode=TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OCPolarity=TIM_OCPolarity_Low;
TIM_OCInitStructure.TIM_OutputState=TIM_OutputState_Enable;
TIM_OC1Init(TIM14,&TIM_OCInitStructure); //輸出比較通道1初始化
//4.開啟定時(shí)器
TIM_TimeBaseInitStructure.TIM_Period=per; //自動(dòng)裝載值
TIM_TimeBaseInitStructure.TIM_Prescaler=psc; //分頻系數(shù)
TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up; //設(shè)置向上計(jì)數(shù)模式
TIM_TimeBaseInit(TIM14,&TIM_TimeBaseInitStructure);
TIM_OC1PreloadConfig(TIM14,TIM_OCPreload_Enable); //使能TIMx在 CCR1 上的預(yù)裝載寄存器
TIM_ARRPreloadConfig(TIM14,ENABLE);//使能預(yù)裝載寄存器
TIM_Cmd(TIM14,ENABLE); //使能定時(shí)
}
?文章來源地址http://www.zghlxwxcb.cn/news/detail-407405.html
到了這里,關(guān)于STM32F4基礎(chǔ):時(shí)鐘系統(tǒng)、中斷及定時(shí)器的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!