找回密碼
 立即注冊(cè)

QQ登錄

只需一步,快速開(kāi)始

搜索
查看: 5321|回復(fù): 1
打印 上一主題 下一主題
收起左側(cè)

STM32外部中斷相關(guān)分析-日志之總結(jié)篇3

[復(fù)制鏈接]
跳轉(zhuǎn)到指定樓層
樓主
//======================================================//

**基于STM32的按鍵控制與外部中斷實(shí)例詳解
**為了減少重復(fù)的內(nèi)容,所以將外部中斷的例程與按鍵控制實(shí)例
  放在一起學(xué)習(xí)。
//======================================================//
/********************************************************/
因?yàn)樯婕暗膬?nèi)容較多,這里大體說(shuō)一下整片文章的內(nèi)容分布:
1)、按鍵控制實(shí)例
  1、按鍵的相關(guān)信息描述
  2、按鍵的初始化代碼實(shí)現(xiàn)
  3、常用按鍵檢測(cè)及控制代碼(主要學(xué)習(xí)其實(shí)現(xiàn)思路)
2)、外部中斷實(shí)例
  1、外部中斷的相關(guān)信息描述
  2、外部中斷的初始化代碼實(shí)現(xiàn)


/********************************************************/
==================================華麗的分界線=================================
//=========================按鍵控制實(shí)例=============================//
==================================華麗的分界線=================================

首先,先來(lái)講述簡(jiǎn)單的按鍵控制,下面來(lái)看看按鍵是什么東西,開(kāi)發(fā)的時(shí)候按鍵又有哪些要注意的事情.

釋義:
按鍵開(kāi)關(guān)是一種電子開(kāi)關(guān),屬于電子元器件類,使用時(shí)以滿足操作力的條件向開(kāi)關(guān)操作方向施壓開(kāi)關(guān)功能閉合接通,當(dāng)撤銷壓力時(shí)開(kāi)關(guān)即斷開(kāi),其內(nèi)部結(jié)構(gòu)是靠金屬?gòu)椘芰ψ兓瘉?lái)實(shí)現(xiàn)通斷的。

一般在開(kāi)發(fā)中涉及按鍵的一般是按鍵消抖。按鍵消抖通常的按鍵所用開(kāi)關(guān)為機(jī)械彈性開(kāi)關(guān),當(dāng)機(jī)械觸點(diǎn)斷開(kāi)、閉合時(shí),由于機(jī)械觸點(diǎn)的彈性作用,一個(gè)按鍵開(kāi)關(guān)在閉合時(shí)不會(huì)馬上穩(wěn)定地接通,在斷開(kāi)時(shí)也不會(huì)一下子斷開(kāi)。也就是在閉合及斷開(kāi)的那一瞬間會(huì)有一小段的不穩(wěn)定狀態(tài),即伴隨著連續(xù)的抖動(dòng)。按鍵在電路中一般也就0跟1兩中狀態(tài),正常情況下,按鍵沒(méi)有按下的情況下,按鍵所連的引腳狀態(tài)一般都是為1。原因是一般會(huì)在引腳上加上一個(gè)上拉,然后再與按鍵相連,按鍵的另一端接地,所以在按鍵沒(méi)有按下的情況下,引腳返回的一般都是高電平,這是電路決定的。如下圖:

按鍵的抖動(dòng)就是介于1跟0的不定狀態(tài)。所以為了取得穩(wěn)定狀態(tài),需要在延遲取狀態(tài)值,也就是既然知道有抖動(dòng)存在了,那么就把取值的時(shí)間延遲一下。還有一個(gè)東西跟抖動(dòng)有關(guān),那就是觸發(fā)沿的設(shè)置,比如上升沿和下降沿的觸發(fā)。上升沿是按鍵松開(kāi)后那一瞬間,電平從0到1的過(guò)程,下降沿就與之相反,也就是按鍵按下的一瞬間,電平從1降為0的過(guò)程。邊沿觸發(fā)一般會(huì)用在外部中斷的過(guò)程中。下面的內(nèi)容也會(huì)講到。
按鍵相對(duì)來(lái)說(shuō)比較簡(jiǎn)單,按鍵的初始化跟LED和BEEP蜂鳴器幾乎一致,不同之處也就在于按鍵是輸入設(shè)備,所以GPIO的模式上一般選為輸入模式,緊接著就要設(shè)置上拉/下拉輸入模式,最后設(shè)置初始狀態(tài),這個(gè)要根據(jù)電路來(lái)間接。
//=======================key.c===============================//
/************************************************
接口:
     key0引腳接在 PE4 低電平有效
key1引腳接在 PE3 低電平有效
     key_up引腳接在 PA0 高電平有效
************************************************/
#include "stm32f10x.h"
#include "key.h"
#include "delay.h"

void key_Init(void)
{
RCC ->APB2ENR |= 1 << 2;//使能 PORTA 時(shí)鐘
RCC ->APB2ENR |= 1 << 6;//使能 PORTE 時(shí)鐘

//key_up配置
GPIOA ->CRL   &= ~(15 << 0);//先設(shè)置輸入模式
GPIOA ->CRL   |= 1 << 3;   //PA.0 下拉輸入模式
GPIOA ->ODR   &= ~(1 << 0);    //設(shè)置下拉,即設(shè)置初始狀態(tài)為低電平

//key1配置
GPIOE ->CRL   &= ~(3 << 13);//先設(shè)置輸入模式
GPIOE ->CRL   |= 1 << 15;   //PE.3 上拉輸入模式
GPIOE ->ODR   |= 1 << 3;    //設(shè)置上拉,即設(shè)置初始狀態(tài)為高電平

//key0配置
GPIOE ->CRL   &= ~(3 << 17);//先設(shè)置輸入模式
GPIOE ->CRL   |= 1 << 19;   //PE.4 上拉輸入模式
GPIOE ->ODR   |= 1 << 4;    //設(shè)置上拉,即設(shè)置初始狀態(tài)為高電平

}

/*************************************************************
**功    能:按鍵處理函數(shù)
**輸入?yún)?shù):無(wú)
**輸出參數(shù):
//mode:0,不支持連續(xù)按;1,支持連續(xù)按;
//0,沒(méi)有任何按鍵按下
//1,KEY0 按下
//2,KEY1 按下
//3,KEY_UP 按下 即 WK_UP
**注  釋:注意此函數(shù)有響應(yīng)優(yōu)先級(jí),KEY0>KEY1>KEY_UP!!
***********************************************************/
u8 KEY_Scan(u8 mode)
{
static u8 key_up=1; //按鍵按松開(kāi)標(biāo)志

if(mode)key_up=1; //支持連按
if(key_up&&(KEY0==0||KEY1==0||WK_UP==1))
{
delay_ms(10); //去抖動(dòng)
key_up=0;
if(KEY0==0)return 1;
else if(KEY1==0)return 2;
else if(WK_UP==1)return 3;
}
else if(KEY0==1&&KEY1==1&&WK_UP==0)key_up=1;

return 0;// 無(wú)按鍵按下
}


//======================================================//
注釋:按鍵檢測(cè)函數(shù),原理是檢測(cè)按鍵的電平,而mode的作用就是一直把key_up置1,那么就會(huì)一直進(jìn)入按鍵按下的檢測(cè),而不會(huì)進(jìn)入按鍵松開(kāi)的檢測(cè)。
if(key_up&&(KEY0==0||KEY1==0||WK_UP==1))這句是進(jìn)行按鍵按下的檢測(cè),
else if(KEY0==1&&KEY1==1&&WK_UP==0)這句是進(jìn)行按鍵松開(kāi)的檢測(cè)。
如果沒(méi)有按鍵按下的話,雖然key_up為1,但是沒(méi)有按鍵,那么就只會(huì)返回0。
==================================華麗的分界線=================================
//===========================key.h===========================//

#ifndef __KEY_H_
#define __KEY_H_

#include "stm32f10x.h"
#include "sys.h"
#define KEY0 PEin(4) //PE4
#define KEY1 PEin(3) //PE3
#define WK_UP PAin(0) //PA0 WK_UP 即 KEY_UP

#define KEY0_PRES 1  //KEY0 按下
#define KEY1_PRES  2  //KEY1 按下
#define WKUP_PRES 3 //KEY_UP 按下(即 WK_UP/KEY_UP)
extern void key_Init(void);
extern u8 KEY_Scan(u8 mode);
#endif


//======================================================//


==================================華麗的分界線=================================
//==========================main.c============================//
/*************************************************************
**功    能:按鍵處理函數(shù)
//1,KEY0 按下 控制LED0
//2,KEY1 按下 控制LED1
//3,KEY_UP 按下 即 WK_UP 控制蜂鳴器
***********************************************************/
#include "stm32f10x.h"
#include "sys.h"
#include "delay.h"
#include "led.h"
#include "beep.h"
#include "key.h"

int main(void)
{
    u8 key=0;
    Stm32_Clock_Init(9); //系統(tǒng)時(shí)鐘設(shè)置
    delay_init(72); //延時(shí)初始化
    led_Init();//LED初始化
    beep_Init();    //beep初始化
    key_Init();     //key初始化
while(1)
{
key=KEY_Scan(0); //得到鍵值
if(key)
{
switch(key)
{
case WKUP_PRES:  //控制蜂鳴器
GPIOB ->ODR   |= (1 << 8);//PB.8 輸出高,即蜂鳴器開(kāi)
delay_ms(10000);//延時(shí)
GPIOB ->ODR   &= ~(1 << 8);//PB.8 輸出低,即蜂鳴器關(guān)
break;

case KEY1_PRES: //控制 LED1
GPIOB ->ODR   &= ~ (1 << 5);//PB.5輸出低電平,即點(diǎn)亮LED
delay_ms(10000);    //延時(shí)
GPIOB ->ODR   |= 1 << 5;//PB.5輸出高電平,即滅掉LED
break;

case KEY0_PRES: //控制 LED0
GPIOE ->ODR   &= ~(1 << 5); //PB.5輸出低電平,即點(diǎn)亮LED
delay_ms(10000);    //延時(shí)
GPIOE ->ODR   |= 1 << 5;//PB.5輸出高電平,即滅掉LED
break;
}
}
else delay_ms(10);

}
}
//======================================================//


==================================華麗的分界線=================================
//=========================外部中斷實(shí)例=============================//
**注釋:特別鳴謝CSDN博客的博主->“zzwdkxx”的博文,在外部中斷的學(xué)習(xí)中直接的幫助!
博文地址:http://blog.csdn.net/zzwdkxx/article/details/9036679

==================================華麗的分界線=================================
接著下面將進(jìn)行外部和中斷的講解,講之前先對(duì)中斷先做一定了解。

//============================百度搜索==========================//
簡(jiǎn)介:
中斷是處理器處理外部突發(fā)事件的一個(gè)重要技術(shù)。它能使處理器在運(yùn)行過(guò)程中對(duì)外部事件發(fā)出的中斷請(qǐng)求及時(shí)地進(jìn)行處理,處理完成后又立即返回?cái)帱c(diǎn),繼續(xù)進(jìn)行處理器原來(lái)的工作。引起中斷的原因或者說(shuō)發(fā)出中斷請(qǐng)求的來(lái)源叫做中斷源。根據(jù)中斷源的不同,可以把中斷分為硬件中斷和軟件中斷兩大類,而硬件中斷又可以分為外部中斷和內(nèi)部中斷 兩類。

外部中斷一般是由計(jì)算機(jī)外設(shè)發(fā)出的中斷請(qǐng)指求,如:鍵盤(pán)中斷、打印機(jī)中斷、定時(shí)器中斷等。外部中斷是可以屏蔽的中斷,也就是說(shuō),利用中斷控制器可以屏蔽這些外部設(shè)備 的中斷請(qǐng)求。

內(nèi)部中斷是指因硬件出錯(cuò)(如突然掉電、奇偶校驗(yàn)錯(cuò)等)或運(yùn)算出錯(cuò)(除數(shù)為零、運(yùn)算溢出、單步中斷等)所引起的中斷。內(nèi)部中斷是不可屏蔽的中斷。

軟件中斷其實(shí)并不是真正的中斷,它們只是可被調(diào)用執(zhí)行的一般程序。

優(yōu)先級(jí):
CPU為了處理并發(fā)的中斷請(qǐng)求,規(guī)定了中斷的優(yōu)先權(quán),中斷優(yōu)先權(quán)由高到低的順序是: (1)除法錯(cuò)、溢出中斷、軟件中斷 (2)不可屏蔽中斷 (3)可屏蔽中斷 (4)單步中斷。
//======================================================//

我們要明確一點(diǎn),就是所有的端口都有外部中斷的能力,但是為了使用外部中斷線,端口必須配置為輸入模式。
外部中斷的配置過(guò)程步驟如下:

1)、設(shè)置GPIO口為輸入
2)、開(kāi)啟GPIO口復(fù)用時(shí)鐘,設(shè)置GPIO口與中斷線的映射關(guān)系。
3)、開(kāi)啟與該GPIO口相對(duì)的線上中斷/事件,設(shè)置觸發(fā)條件。
4)、配置中斷分組(NVIC),并使能中斷。
5)、編寫(xiě)中斷服務(wù)函數(shù)。

其中我們的外部中斷初始化函數(shù)就是需要做前面的4步,然后再編寫(xiě)中斷服務(wù)函數(shù)。
說(shuō)到底其實(shí)一個(gè)中斷要做的事有:使能中斷、中斷分組、屏蔽、IO口映射。完成以上工作就是一個(gè)完整的中斷設(shè)置。
具體外部中斷的配置如下:
==================================華麗的分界線=================================
//下面的設(shè)置是以人體紅外熱釋電模塊為例的一個(gè)外部中斷,當(dāng)然也可以選用按鍵的電平的邊沿觸發(fā)的來(lái)寫(xiě)一個(gè)外部中斷。只要會(huì)用外部中斷的初始化設(shè)置 ,那么用其他的模塊來(lái)設(shè)置外部中斷都是沒(méi)問(wèn)題的。
//==========================exti.c============================//
#include "EXTI.h"
#include "delay.h"
#include "led.h"
#include "key.h"
#include "beep.h"

#define RENTIPAin(5)//PA5
u8 flag=0;

//PA5紅外人體感應(yīng)的引腳
void EXTIX_Init(void)
{
//1)先設(shè)置IO為輸入模式
   RCC ->APB2ENR |= 1 << 2;//使能 PORTA 時(shí)鐘
GPIOA->CRL&=0XFF0FFFFF;  //PA5設(shè)置成輸入
GPIOA->ODR &=~(1<<5);//設(shè)置PA5下拉

//2)開(kāi)啟IO口復(fù)用時(shí)鐘,設(shè)置IO口與中斷線的映射關(guān)系
RCC->APB2ENR|=0x01;//使能io復(fù)用時(shí)鐘
AFIO->EXTICR[1] &=0xff0f; //EXTI5映射到PA5

//3)開(kāi)啟與該IO口相對(duì)的線上中斷/事件,設(shè)置觸發(fā)條件
EXTI->IMR|=1<<5;//開(kāi)啟line BITx上的中斷(EXTI->IMR是中斷屏蔽寄存器)
// EXTI->IMR&=~(1<<5);  //屏蔽exti5線上的中斷

//EXTI->EMR是事件屏蔽寄存器
//EXTI->EMR|=1<<BITx;//不屏蔽line BITx上的事件 (如果不屏蔽這句,在硬件上是可以的,但是在軟件仿真的時(shí)候無(wú)法進(jìn)入中斷!)

EXTI->RTSR|=1<<5;//Exti5上事件上降沿觸發(fā)
//EXTI->FTSR|=1<<5;//Exti5上事件下降沿觸發(fā)

//4)配置中斷分組(NVIC),并使能中斷
MY_NVIC_Init(2,3,EXTI9_5_IRQn,2);//搶占2,子優(yōu)先級(jí)3,組2  
//5)編寫(xiě)中斷服務(wù)函數(shù)

}

//中斷服務(wù)函數(shù)
void EXTI9_5_IRQHandler(void)
{
if(RENTI==1) //有人
{
flag=1;//設(shè)置標(biāo)志位為1,在main函數(shù)里標(biāo)志位被清零
}
EXTI->PR=1<<5; //清除 LINE4 上的中斷標(biāo)志位
}
===================================================================
在寫(xiě)外部中斷的時(shí)候有幾個(gè)問(wèn)題困惑我的有:
1、 中斷線的映射問(wèn)題,就是怎么把PA5映射收到EXTI5?
2、中斷服務(wù)函數(shù)的書(shū)寫(xiě),一開(kāi)始還以為EXTI1的中斷服務(wù)函數(shù)是EXTI1_IRQHandler();那么EXRTI5肯定是EXTI5_IRQHandler();了可是這是錯(cuò)的。

解答區(qū):
1、中斷線的映射問(wèn)題。最主要的語(yǔ)句是
AFIO->EXTICR[1] &=0xff0f; //EXTI5映射到PA5
這句AFIO->EXTICR[1] 是什么意思呢?右鍵調(diào)進(jìn)去查看以后是

它相對(duì)應(yīng)的是4個(gè)外部中斷配置寄存器,每個(gè)寄存器有32個(gè)位,但是高16位是保留的,有效的就只有低16位,即15~0位。


且看外部中斷的配置寄存器,外部中斷的寄存器有4個(gè),它分為AFIO_EXTICR1、AFIO_EXTICR2、AFIO_EXTICR3、AFIO_EXTICR4,也就是EXTICR[0]~EXTICR[3],只是在寄存器里從0開(kāi)始。如下圖所示:
(就AFIO->EXTICR[1] &=0xff0f; 而言,它的意思呢,就是設(shè)置PA5的中斷線為EXTI5,因?yàn)槭荘A端口,那么依寄存器的0000為PA端,那么PA5怎么表示呢,其實(shí)就從值的賦值上選定的,0xff0f 依次為7 6 5 4的順序,再如 AFIO->EXTICR[0]|=0X0020; 就是將EXTI1映射到PC1)

值得非常注意的是:通過(guò)AFIO_CRX配置GPIO線上的外部中斷/事件,必須先使能AFIO時(shí)鐘。切記!切記!這很重要!
  









2、中斷處理函數(shù)的書(shū)寫(xiě)問(wèn)題。


如圖所示,所有中斷線的中斷服務(wù)函數(shù)依次為
void EXTI0_IRQHandler(void);
void EXTI1_IRQHandler(void);
void EXTI2_IRQHandler(void);
void EXTI3_IRQHandler(void);
void EXTI4_IRQHandler(void);   
void EXTI9_5_IRQHandler(void);
void EXTI15_10_IRQHandler(void);

==================================華麗的分界線=================================

STM32的每個(gè)IO都可以作為外部中斷的中斷輸入口。STM32F103的中斷控制器支持19個(gè)外部中斷/事件請(qǐng)求。每個(gè)中斷設(shè)有狀態(tài)位,每個(gè)中斷/事件都有獨(dú)立的觸發(fā)和屏蔽設(shè)置。
STM32F103 的19 個(gè)外部中斷為:
線 0~15:對(duì)應(yīng)外部 IO 口的輸入中斷。
線 16:連接到 PVD 輸出。
線 17:連接到 RTC 鬧鐘事件。
線 18:連接到 USB 喚醒事件。
線 19:連接到以太網(wǎng)喚醒時(shí)間。



注意:通過(guò)AFIO_CRX配置GPIO線上的外部中斷/事件,必須先使能AFIO時(shí)鐘。(其實(shí)在STM32很多開(kāi)發(fā)中,時(shí)鐘就是生命。很多人就會(huì)問(wèn),為什么STM32的設(shè)計(jì)要那么麻煩呢,弄什么都要使能相關(guān)的時(shí)鐘。其實(shí)這個(gè)很簡(jiǎn)單,很大一方面是為了處于功耗的考慮。如果沒(méi)有用到的相關(guān)引腳一直處于工作狀態(tài),那么功耗這個(gè)就大了。)

==================================華麗的分界線=================================
================================== 截至 CSDN博客的博主->“zzwdkxx”的博文=================================

外部中斷函數(shù)不能進(jìn)入的原因分析分析,可能為以下幾個(gè)方面:
1)GPIO或者AFIO的時(shí)鐘沒(méi)有開(kāi)啟;
2)GPIO和配置的中斷線路不匹配;
3)中斷觸發(fā)方式和實(shí)際不相符合;
4)中斷處理函數(shù)用庫(kù)函數(shù)時(shí),寫(xiě)錯(cuò),經(jīng)常可能出現(xiàn)數(shù)字和字母之間沒(méi)有下劃線;
5)外部中斷是沿觸發(fā),有可能檢測(cè)不到沿,比如中斷線是低電平(浮空輸入),觸發(fā)是下降沿觸發(fā),可能會(huì)出現(xiàn)一直是低電平,高電平的時(shí)候是一樣的情況,電平持續(xù)         為高電平;
6)沒(méi)有用軟件中斷來(lái)觸發(fā)外部中斷,調(diào)用函數(shù)EXTI_GenerateSWInterrupt;,因?yàn)檐浖袛嘞扔谶呇刂袛嗵幚怼?/div>
/*************************************************************************************/
PS:如有錯(cuò)誤,歡迎指錯(cuò)!期待學(xué)習(xí)!
/*************************************************************************************/


評(píng)分

參與人數(shù) 1黑幣 +100 收起 理由
admin + 100 共享資料的黑幣獎(jiǎng)勵(lì)!

查看全部評(píng)分

分享到:  QQ好友和群QQ好友和群 QQ空間QQ空間 騰訊微博騰訊微博 騰訊朋友騰訊朋友
收藏收藏1 分享淘帖 頂 踩
回復(fù)

使用道具 舉報(bào)

沙發(fā)
ID:176258 發(fā)表于 2017-3-21 11:01 | 只看該作者
做的非常好,可以用。

評(píng)分

參與人數(shù) 1黑幣 +1 收起 理由
MR_x + 1

查看全部評(píng)分

回復(fù)

使用道具 舉報(bào)

本版積分規(guī)則

小黑屋|51黑電子論壇 |51黑電子論壇6群 QQ 管理員QQ:125739409;技術(shù)交流QQ群281945664

Powered by 單片機(jī)教程網(wǎng)

快速回復(fù) 返回頂部 返回列表