對中斷的理解
首先我們知道內核與外設需要交互,那交互的方式有哪些呢?
瞭解過一些單片機或者操作系統等知識的應該能想到是:輪詢和中斷。
輪詢其實就是由CPU定時發出詢問,依序詢問每一個周邊設備是否需要其服務,有即給予服務,服務結束後再問下一個周邊,接着不斷週而復始。這樣我們也就能想到輪詢的方式貌似公平,實際工作效率很低,且不能及時響應緊急事件;
中斷其實就是在執行CPU當前程序時,由於系統中出現了某種急需處理的情況,CPU暫停正在執行的程序,轉而去執行另外一段特殊程序來處理出現的緊急事務,處理結束後,CPU自動返回到原來暫停的程序中去繼續執行。
本次我們主要說說CC2530中斷這種交互方式
有兩個重要概念:
<1> 中斷服務函數: 內核響應中斷後執行的相應處理程序,即上面提到的特殊程序。
<2> 中斷向量:中斷服務程序的入口地址。每個中斷源都對應一個固定的入口地址。當內核響應中斷請求時,就會暫停當前的程序執行,然後跳轉到該入口地址執行代碼。
CC2530中斷系統
CC2530具有18箇中斷源,每個中斷源都由各自的一系列特殊功能寄存器來進行控制。可以編程設置相關特殊功能寄存器,設置18箇中斷源的優先級以及使能中斷申請響應等。我們常用的中斷源有下面幾個:
大家也不需要將全部給記住,我們只需要將一些常用的記熟,然後對於其他的當遇見了網上查查資料就行了。
CC2530的中斷處理函數
當發生中斷請求,需要暫停CPU目前的工作轉去執行另外一份特殊程序。而這個特殊程序就是我們所說的中斷處理函數。
中斷服務函數與一般自定義函數不同,有特定的書寫格式:
<1> 在每一箇中斷服務函數之前,都要加上一句起始語句:
#pragma vector = <中斷向量>
<中斷向量>表示接下來要寫的中斷服務函數是爲那個中斷源服務的,該語句有兩種寫法:
#pragma vector = 0x7B 或者 #pragma vector = P1INT_VECTOR
前者是中斷向量的入口地址,後者是頭文件**“ioCC2530.h”中的宏定義。
<2> _ _interrupt 關鍵字表示該函數是一箇中斷服務函數,<函數名稱>可以 自定義,函數體 不能帶有參數,也不能有返回值。
CC2530的外部中斷
CC2530的P0、P1和P2端口中的每個引腳都具有外部中斷輸入功能,要使某些引腳具有外部中斷功能,需要對IENx寄存器、PxIEN寄存器和PICTL寄存器進行適當的設置。 除了各個中斷源都有自己的中斷使能開關之外,中斷系統還有一個總開關,可以同“EA = 1;”來打開總中斷。
中斷產生後當然後有一個中斷標誌位來標誌有中斷產生。
P0、P1和P2端口分別使用P0IF、P1IF和P2IF作爲中斷標誌位,任何一個端口組上的引腳產生外部中斷時,都會將對應端口組的中斷標誌自動置位。注意,外部中斷標誌必須在中斷服務函數中手工清除,否則CPU會反覆進入中斷。 端口狀態標誌寄存器P0IFG、P1IGF和P2IFG,分別對應3個端口中各引腳的中斷觸發狀態,當某引腳發生外部中斷觸發時,對應的標誌位會自動置位,這個標誌同樣需要手工清除。
對於這些中斷寄存器的具體介紹就請大家參考這個博客吧
中斷寄存器介紹
點小燈實驗
依次點亮兩燈
首先我們應該初始化led1(藍燈),led2(黃燈)即設置以前講過的PxSEL,PxDIR,PxINP等寄存器。
這裏再次貼下流程圖:
代碼:
#include "led.h"
#include "delay.h"
#include "iocc2530.h"
void main(void)
{
P1SEL &=~0x03;//設置兩個led爲通用io
P1DIR |=0x03;//設置兩個led爲輸出模式
led1=1;//初始化小燈1亮
led2=0;//初始化小燈2滅
while(1)
{
led1=!led1;
delay(30000);
led2=!led2;
delay(30000);
}
}
具體的led1,led2的引腳定義我也定義在delay.h文件裏面,具體的引腳需要大家根據芯片的硬件原理圖進行定義。delay延遲函數我也在delay.h文件定義好了。延遲函數很簡單大家網上可以搜搜。
按鍵實現亮燈效果
這個任務多了按鍵,其實也是一樣,我們只需要多初始化一個按鍵爲通用io,輸入模式,三態就行。
亮燈效果:按一下藍燈亮,黃燈滅;再按一下藍燈滅,黃燈亮;再按一下兩燈都亮;再按一下兩燈都滅
代碼:
#include "led.h"
#include "delay.h"
#include "iocc2530.h"
void main(void)
{
uint count=0;
uint temp=0;
P1SEL &= ~0x04;//設置按鍵1 P1_2爲通用io
P1DIR &= ~0x04;//設置按鍵1 P1_2爲輸入模式
P1INP |= 0x04;//設置爲三態上拉模式
P1SEL &=~0x03;//設置兩個led爲通用io
P1DIR |=0x03;//設置兩個led爲輸出模式
led1=0;//初始化小燈1滅
led2=0;//初始化小燈2滅
while(1)
{
if(key1==0) //等待按鍵按下
{
delay(100);
if(key1==0) //消抖
{
while(key1==0);//等待按鍵鬆開
count++; //記錄按鍵次數
temp=count%4;
}
}
switch(temp) //對按鍵取餘
{
case 0: //兩個燈滅
led1=0;
//delay(30000);
led2=0;
//delay(30000);
break;
case 1: //小燈1亮小燈2滅
led1=1;
delay(30000);
led2=0;
delay(30000);
break;
case 2: //小燈1滅小燈2亮
led1=0;
delay(30000);
led2=1;
delay(30000);
break;
case 3: //兩個燈亮
led1=1;
delay(30000);
led2=1;
delay(30000);
break;
}
}
}
按鍵中斷實現兩燈效果
這次就是我們用今天所介紹的中斷方式來實現亮燈效果
亮燈效果:按一下藍燈亮,黃燈滅;再按一下藍燈滅,黃燈亮;再按一下兩燈都亮;再按一下兩燈都滅
實現一個基本的中斷功能我們所要完成的步驟有:
<1> 配置IENx寄存器,使能端口組的中斷功能。
<2> 配置PxIEN寄存器,使能具體的外部中斷引腳。
<3> 配置PICTL寄存器,設置中斷觸發方式
<4>設計外部中斷服務函數
在編寫中斷服務函數的時候,書寫格式要正確,中斷向量不能搞錯。特別要注意:在函數裏面把端口組和引腳的標誌位清除,否則CPU將會反覆進入中斷,必須先清除引腳標誌位PxIFG,再清除端口組標誌位PxIF。
代碼:
#include "led.h"
#include "delay.h"
#include "iocc2530.h"
//外部中斷1服務函數
#pragma vector=P1INT_VECTOR //外部中斷1的向量入口
__interrupt void led_show(void)
{
int count=1;
do{
switch(count) //對按鍵取餘
{
case 1: //小燈1亮小燈2滅
led1=1;
delay(50000);
led2=0;
delay(50000);
break;
case 2: //小燈1滅小燈2亮
led1=0;
delay(50000);
led2=1;
delay(50000);
break;
case 3: //兩個燈亮
led1=1;
delay(50000);
led2=1;
delay(50000);
break;
case 4: //兩個燈滅
led1=0;
//delay(50000);
led2=0;
//delay(50000);
break;
}
delay(50000);
count++;
}while(count<=4);
P1IFG &= ~0x04;//軟件清除P1_2引腳標誌位
P1IF =0;//軟件清除P1端口組的標誌位
}
void main(void)
{
P1SEL &= ~0x04;//設置按鍵1 P1_2爲通用io
P1INP |= 0x04;//設置爲三態上拉模式
P1IEN |= 0x04;//P1_2設置爲中斷方式
PICTL |= 0x04;//下降沿觸發
EA=1;//打開總中斷
IEN2 |=0x10;//端口1中斷使能
P1IFG |=0x00;//初始化中斷標誌位
P1SEL &=~0x03;//設置兩個led爲通用io
P1DIR |=0x03;//設置兩個led爲輸出模式
led1=0;//初始化小燈1滅
led2=0;//初始化小燈2滅
while(1)
{
}
}
本次介紹就到這了,謝謝大家的觀看!