外部引腳可以觸發(fā)芯片內(nèi)部的中斷,這是每一個通用MCU都具備的基本功能。
在LPC800中,所有外部引腳都可以配置為產(chǎn)生中斷的觸發(fā)源。每個引腳不但可以獨立地觸發(fā)中斷,還可以和其它引腳的信號狀態(tài)進行組合,由軟件指定某種特定的組合觸發(fā)中斷。
前面幾章已經(jīng)介紹了引腳的特性配置由IOCON模塊實現(xiàn),開關(guān)矩陣則負責(zé)把引腳與片內(nèi)外設(shè)對應(yīng)起來。所有的數(shù)字信號,不管配置為輸入還是輸出,都可以被指定為引腳中斷和引腳組合邏輯的一個輸入選項。本章只介紹引腳中斷和引腳組合邏輯模塊,其它部分請參看對應(yīng)章節(jié)。
下圖給出了引腳中斷和引腳組合邏輯模塊(圖中藍色部分)與其它部分的關(guān)系示意。
圖1.引腳中斷和引腳組合邏輯模塊與開關(guān)矩陣(SWM)和引腳配置模塊(IOCON)的關(guān)系示意圖
1.1.引腳中斷功能和使用任何引腳,只要它在開關(guān)矩陣或IOCON中被指定為數(shù)字引腳,不管是輸入還是輸出,這個引腳都可以被指定為引腳中斷和引腳組合邏輯的輸入端。所有的LPC800產(chǎn)品,都允許有最多8個引腳作為引腳中斷和引腳組合邏輯的輸入端。在SYSCON中通過8個PINTSEL寄存器,指定哪個引腳可以作為引腳中斷的輸入,軟件只需把引腳編號寫入對應(yīng)的PINTSEL寄存器即可。對于PIO1_n的引腳,引腳編號為(32 + n)。
例如:
LPC_SYSCON->PINTSEL[0] = 20;// 指定PIO0_20為引腳中斷0的輸入源
LPC_SYSCON->PINTSEL[3] = 20;// 指定PIO0_20為引腳中斷3的輸入源
LPC_SYSCON->PINTSEL[6] = 33;// 指定PIO1_1為引腳中斷6的輸入源
1.1.1 指定中斷觸發(fā)源和觸發(fā)方式
在LPC800中有8個引腳中斷向量,它們分別為PININT0_IRQ ~ PININT7_IRQ,每個PINTSEL寄存器指定的引腳對應(yīng)一個中斷向量。以下10個寄存器用于控制中斷觸發(fā)源和觸發(fā)方式: 表1.控制中斷觸發(fā)源和觸發(fā)方式的寄存器
下面這個表格是按照要求的觸發(fā)方式,標示出應(yīng)該如何設(shè)置寄存器控制位。
表2.觸發(fā)方式的配置
表2中可以看出,SIENR和CIENR都是只寫寄存器,一個用于設(shè)置IENR寄存器位,另一個用于清除IENR寄存器位;這兩個寄存器的目的是為了在修改IENR寄存器時的“讀-修改-寫”的操作,只需寫操作,即可改變所有需要設(shè)置的位或需要清除的位。
同樣,SIENF和CIENF也都是只寫寄存器,用于設(shè)置IENF寄存器位。
由于獨立的IENR和IENF寄存器,用戶可以配置同一個信號的上升沿和下降沿都產(chǎn)生中斷。在中斷處理中,可以通過讀出RISE和FALL寄存器判斷是哪個邊沿產(chǎn)生的中斷。在RISE和/或FALL寄存器中寫’1’可以清除中斷狀態(tài),也可以在IST寄存器中寫’1’ 清除中斷狀態(tài)。
1.1.2 電平觸發(fā)方式的使用
因為通過軟件本身,在MCU內(nèi)部不能清除電平觸發(fā)所產(chǎn)生的中斷,軟件必須執(zhí)行某種操作,讓外部電路改變信號線上的電平,才能使MCU不再產(chǎn)生中斷,所以使用電平中斷時要小心處理。
可以通過在IST寄存器中寫’1’的方式,改變觸發(fā)的電平,從而間接地清除中斷狀態(tài)。例如當(dāng)高電平觸發(fā)中斷后,在IST寄存器中寫’1’,控制器將變?yōu)橛械碗娖綍r產(chǎn)生中斷,同時清除中斷狀態(tài)。
一般情況下,建議用邊沿觸發(fā)方式,通過軟件處理實現(xiàn)電平中斷的效果。對于高電平中斷的控制方式,改變位在上升沿中斷的中斷處理程序返回之前,檢測該信號線是否為高電平的方式實現(xiàn)相同的邏輯功能。同理,低電平中斷的控制方式,可以用下降沿中斷,再加上檢測低電平的方式實現(xiàn)相同邏輯功能。
1.2.引腳中斷的實用函數(shù)為了方便使用,這里呈現(xiàn)幾個實用函數(shù),方便使用PINT功能。
1.2.1復(fù)位所有引腳中斷寄存器(PinInt_Reset)
PinInt_Reset()函數(shù)的功能就是清除所有未處理的引腳中斷標志,同時關(guān)閉所有的引腳中斷。
代碼片段1.復(fù)位所有引腳中斷函數(shù)
01 void PinInt_Reset()
02 {
03 LPC_PIN_INT->ISEL = 0; // 邊沿觸發(fā)
04 LPC_PIN_INT->IENR = 0; // 關(guān)閉上升沿或電平中斷
05 LPC_PIN_INT->IENF = 0; // 關(guān)閉下降沿中斷
06 LPC_PIN_INT->RISE = 0xFF; // 清除上升沿檢測標志
07 LPC_PIN_INT->FALL = 0xFF; // 清除下降沿檢測標志
08 }
1.2.2使能引腳的中斷共有4個函數(shù)分別使能引腳的中斷為上升沿觸發(fā)、下降沿觸發(fā)、高電平觸發(fā)和低電平觸發(fā),這些函數(shù)的功能是按照給定的輸入參數(shù),使能對應(yīng)的引腳中斷,它的輸入?yún)?shù)是需要設(shè)置的引腳中斷的位域值。
輸入?yún)?shù)的第0位為’1’表示需要設(shè)置PININT0,輸入?yún)?shù)的第1位為’1’表示需要設(shè)置PININT1,依次類推直至第7位。
注意,不要把PININTn和引腳編號混淆,使用以下函數(shù)之前,需要指定PININTn和引腳的關(guān)系,由LPC_SYSCON->PINTSEL[0]定義,見1.1節(jié)。
下面分別是這4個函數(shù)的代碼:
代碼片段2.函數(shù)PinInt_Enable_Rising()
01 void PinInt_Enable_Rising(uint32_t pins_mask)
02 {
03 uint32_t Pint_mode;
04 Pint_mode = LPC_PIN_INT->ISEL;
05 if (Pint_mode & pins_mask) {
06 Pint_mode &= ~pins_mask;
07 LPC_PIN_INT->ISEL = Pint_mode; // 邊沿觸發(fā)
08 }
09 LPC_PIN_INT->SIENR = pins_mask; // 使能上升沿中斷
10 }
代碼片段3.函數(shù)PinInt_Enable_Falling()
01 void PinInt_Enable_Falling(uint32_t pins_mask)
02 {
03 uint32_t Pint_mode;
04 Pint_mode = LPC_PIN_INT->ISEL;
05 if (Pint_mode & pins_mask) {
06 Pint_mode &= ~pins_mask;
07 LPC_PIN_INT->ISEL = Pint_mode; // 邊沿觸發(fā)
08 }
09 LPC_PIN_INT->SIENF = pins_mask; // 使能下降沿中斷
10 }
代碼片段4.函數(shù)PinInt_Enable_High()
01 void PinInt_Enable_High(uint32_t pins_mask)
02 {
03 uint32_t Pint_mode;
04 Pint_mode = LPC_PIN_INT->ISEL;
05 if ((Pint_mode & pins_mask) != pins_mask) {
06 Pint_mode |= pins_mask;
07 LPC_PIN_INT->ISEL = Pint_mode; // 電平觸發(fā)
08 }
09 LPC_PIN_INT->SIENF = pins_mask; // 使能高電平中斷
10 }
代碼片段5.函數(shù)PinInt_Enable_Low ()
01 void PinInt_Enable_Low(uint32_t pins_mask)
02 {
03 uint32_t Pint_mode;
04 Pint_mode = LPC_PIN_INT->ISEL;
05 if ((Pint_mode & pins_mask) != pins_mask) {
06 Pint_mode |= pins_mask;
07 LPC_PIN_INT->ISEL = Pint_mode; // 電平觸發(fā)
08 }
09 LPC_PIN_INT->CIENF = pins_mask; // 使能低電平中斷
10 }
1.2.3關(guān)閉對應(yīng)的引腳中斷(PinInt_Disable)PinInt_Disable()函數(shù)的功能是按照給定的輸入?yún)?shù),關(guān)閉對應(yīng)的引腳中斷,它的輸入?yún)?shù)和上面那些函數(shù)的輸入?yún)?shù)意義一致,是需要設(shè)置的引腳中斷的位域值。
代碼片段6.函數(shù)PinInt_ Disable ()
01 void PinInt_Disable(uint32_t pins_mask)
02 {
03 uint32_t Pin_mask;
04 for (Pin_mask = 1; Pin_mask < 0x100; Pin_mask <<= 1) {
05 if ((pins_mask & Pin_mask) == 0)
06 continue; // 對未指定的位,不做任何操作
07 if (LPC_PIN_INT->ISEL & Pin_mask) { // 電平中斷
08 LPC_PIN_INT->CIENR = Pin_mask; // 關(guān)閉對應(yīng)中斷
09 }
10 else { // 邊沿觸發(fā)
11 LPC_PIN_INT->CIENR = Pin_mask; // 關(guān)閉上升沿中斷
12 LPC_PIN_INT->CIENF = Pin_mask; // 關(guān)閉下降沿中斷
13 }
14 }
15 }
1.2.4清除對應(yīng)的引腳中斷標志(PinInt_Clear)
PinInt_Clear()函數(shù)的功能是按照給定的輸入?yún)?shù),清除對應(yīng)的引腳中斷標志,它的輸入?yún)?shù)和上面那些函數(shù)的輸入?yún)?shù)意義一致,是需要操作的引腳中斷標志位的位域值。
通常這個函數(shù)是在中斷處理函數(shù)中調(diào)用。
代碼片段7.函數(shù)PinInt_Clear()
01 void PinInt_Clear(uint32_t pins_mask)
02 {
03 uint32_t Pin_mask;
04 for (Pin_mask = 1; Pin_mask < 0x100; Pin_mask <<= 1) {
05 if ((pins_mask & Pin_mask) == 0)
06 continue;
07 if (!(LPC_PIN_INT->ISEL & Pin_mask))
08 LPC_PIN_INT->IST = Pin_mask;
09 }
10 }
1.3.引腳中斷的使用實例下面借用GPIO章的例程,略作修改演示引腳中斷的操作。本例程也是使用LPC824-Lite開發(fā)板,循環(huán)執(zhí)行下列操作:
■通過GPIO循環(huán)點亮板上的八個紅色LED
■分別點亮八個紅色LED,模擬一個小蟲爬行
設(shè)置USER(S4)、WAKEUP(S3)和ISP(S2)按鍵分別產(chǎn)生引腳中斷0~2(PINTSEL0/1/2),對應(yīng)的中斷處理的意義如下:
A.按下USER鍵:循環(huán)體中跳過上述第2項操作,再按一次USER鍵恢復(fù)1-2循環(huán)。
B.按下WAKEUP鍵:循環(huán)體中跳過上述第1項操作,再按一次WAKEUP鍵恢復(fù)1-2循環(huán)。
C.按下ISP鍵:循環(huán)體中的操作速度變慢50%,再按一次ISP鍵恢復(fù)默認的速度。
D.如果上述2項操作都被跳過,則連續(xù)閃爍所有的LED燈。在例程中引入3個變量,分別向主循環(huán)傳遞3個按鍵的不同狀態(tài):
下面的PINT_Init()函數(shù)用于指定哪個按鍵,作為哪個中斷的觸發(fā)源,同時指定由按鍵的上升沿觸發(fā)中斷,最后使能對應(yīng)的中斷并設(shè)置中斷優(yōu)先級。
首先需要定義中斷的編號:
代碼片段8.引腳中斷初始化函數(shù)
01 void PINT_Init(void)
02 {
03 LPC_GPIO_PORT->DIRCLR0 = PIN_KEYS_MASK;
04
05 LPC_SYSCON->PINTSEL[INT_USERKEY] = PIN_USERKEY;
06 LPC_SYSCON->PINTSEL[INT_WAKEKEY] = PIN_WAKEKEY;
07 LPC_SYSCON->PINTSEL[INT_ISPKEY] = PIN_ISPKEY;
08
09 PinInt_Reset();
10 PinInt_Enable_Rising((1<
11 | (1<
12 | (1<
13
14 NVIC_EnableIRQ(PININT0_IRQn);
15 NVIC_EnableIRQ(PININT1_IRQn);
16 NVIC_EnableIRQ(PININT2_IRQn);
17
18 NVIC_SetPriority(PININT0_IRQn, 3);
19 NVIC_SetPriority(PININT1_IRQn, 3);
20 NVIC_SetPriority(PININT2_IRQn, 3);
21 }
下面是本例程的主函數(shù)片斷,和中斷處理程序的代碼,略去了上面描述的2項操作的具體實現(xiàn),讀者可以參考GPIO章節(jié)中的代碼。
代碼片段9.引腳中斷主函數(shù)片斷
01 PINT_Init();
02 skip_step1 = skip_step2 = 0;
03 leds_speed = 100;
04 while (1) {
05 if (skip_step1 == 0) {
06 LPC_GPIO_PORT->SET0 = PIN_LEDS_MASK; // 熄滅所有燈
07
08 }
09
10 if (skip_step2 == 0) {
11 LPC_GPIO_PORT->SET0 = PIN_LEDS_MASK; // 熄滅所有燈
12
13 }
14 }
15
16 if (skip_step1 && skip_step2) {
17 LPC_GPIO_PORT->NOT0 = PIN_LEDS_MASK; // 翻轉(zhuǎn)所有燈
18 Wait1ms(leds_speed);
19 }
20 } // 結(jié)束while(1)循環(huán)
代碼片段10.引腳中斷的中斷處理函數(shù)
01 void PININT0_IRQHandler(void)
02 {
03 if (LPC_PIN_INT->RISE & (1<
04 skip_step1 = !skip_step1;
05 PinInt_Clear(1 << INT_USERKEY);
06 }
07
08 void PININT1_IRQHandler(void)
09 {
10 if (LPC_PIN_INT->RISE & (1<
11 skip_step2 = !skip_step2;
12 PinInt_Clear(1<
13 }
14
15 void PININT2_IRQHandler(void)
16 {
17 if (LPC_PIN_INT->RISE & (1<
18 if (leds_speed == DEFAULT_SPEED)
19 leds_speed = DEFAULT_SPEED + DEFAULT_SPEED/2;
20 else
21 leds_speed = DEFAULT_SPEED;
22 PinInt_Clear(1<
23 }
上述三個中斷處理程序的最后一個語句,都是調(diào)用PinInt_Clear ()函數(shù)清除對應(yīng)的中斷標志。這個清除標志的語句都是不受是否有上升沿中斷影響,即使進入該中斷處理函數(shù)時發(fā)生的事件不是上升沿,也會執(zhí)行清除標志操作,這樣安排是為防止有遺漏的中斷,而造成反復(fù)進入中斷處理程序而死機。
1.4 模式匹配引擎功能和使用模式匹配引擎實現(xiàn)引腳的組合邏輯,組合邏輯結(jié)果可以通過狀態(tài)位由軟件檢測,也可以是引腳中斷的延伸,產(chǎn)生中斷請求,還可以向CPU核心發(fā)送事件信號和/或在某個引腳上輸出。
前面介紹的引腳中斷功能,每個中斷只能由來自某個單個引腳的狀態(tài)變化而產(chǎn)生;而通過模式匹配引擎,根據(jù)多個引腳的組合邏輯運算結(jié)果,可以產(chǎn)生對應(yīng)的中斷。例如可以實現(xiàn)一個鍵盤,鍵盤的每個按鍵可以單獨產(chǎn)生中斷,用于判斷哪個鍵被按下,也可以使用組合邏輯功能,當(dāng)某個特定的按鍵組合被按下,才能產(chǎn)生中斷,這樣可以更加方便地檢測諸如Ctrl-C、Ctrl-V這樣的組合功能。
1.4.1模式匹配引擎
一個邏輯運算表達式或布爾運算表達式,是由布爾變量經(jīng)基本的”與”、”或”、”非”和”異或”等布爾運算構(gòu)成。
下面是一些布爾運算表達式的例子:
例1. A*B + B*C + A*C
例2. D*C + A*B*C + A*D*E
例3. A + B*C*D + B*E*F + G*A*D
在LPC800的模式匹配引擎中,每個布爾變量與一個輸入引腳一一對應(yīng),最多允許有8個布爾變量參與運算,同時所有變量出現(xiàn)次數(shù)的總和不能多于8個。
LPC800的模式匹配引擎可以直接支持”與”、”或”、”非”運算,但不能原生支持”異或”運算,需要由軟件配置實現(xiàn)”異或”運算,如下:
A ^ B = A*/B + /A*B
在上述例1中總共有3個布爾變量:A、B和C,它們出現(xiàn)的次數(shù)總和為6次,可以由模式匹配引擎實現(xiàn)。例2有A~D共5個布爾變量,出現(xiàn)的總次數(shù)為8次,也可以由模式匹配引擎實現(xiàn)。但例3中有A~G共7個布爾變量,出現(xiàn)的總次數(shù)為10次,不能由模式匹配引擎實現(xiàn)。
1.4.2布爾項的實現(xiàn)
在模式匹配引擎中,布爾變量的每一次出現(xiàn)以一個布爾項來實現(xiàn),在用戶手冊中“布爾項”以slice表示。
內(nèi)部實現(xiàn)中,只有8個布爾項(slice),因此所有變量出現(xiàn)次數(shù)的總和不能多于8個。
對每一個布爾項,用戶可以按照輸入信號的不同變化,選擇多達8種不同的條件。這些條件分別是:
1.恒為“高”。這種情況與輸入信號無關(guān),對應(yīng)的布爾項始終為“高”。一般是按照應(yīng)用邏輯,用于設(shè)置閑置的布爾項。
2.鎖存的上升沿。從輸入信號出現(xiàn)一個上升沿,到再次寫入模式匹配引擎的控制寄存器之前,對應(yīng)的布爾項為“高”。在此期間不管輸入信號如何變化,對應(yīng)的布爾項不再變化。寫入控制寄存器會清除這個布爾項為“低”。
3.鎖存的下降沿。從輸入信號出現(xiàn)一個下降沿,到再次寫入模式匹配引擎的控制寄存器之前,對應(yīng)的布爾項為“高”。在此期間不管輸入信號如何變化,對應(yīng)的布爾項不再變化。寫入控制寄存器會清除這個布爾項為“低”。
4.鎖存的邊沿。這是上面2個條件的結(jié)合,輸入信號的上升沿或下降沿,都會使對應(yīng)的布爾項為“高”。同樣,寫入控制寄存器會清除這個布爾項為“低”。
5.高電平。當(dāng)輸入信號為高電平時,對應(yīng)的布爾項為“高”。注意這個條件沒有經(jīng)過鎖存,即當(dāng)輸入信號變低時,對應(yīng)的布爾項也變?yōu)椤暗汀薄?/span>
6.低電平。當(dāng)輸入信號為低電平時,對應(yīng)的布爾項為“高”。注意這個條件沒有經(jīng)過鎖存,即當(dāng)輸入信號變高時,對應(yīng)的布爾項也變?yōu)椤暗汀?。這個條件相當(dāng)于布爾“非”運算。
7.恒為“低”。這種情況與輸入信號無關(guān),對應(yīng)的布爾項始終為“低”。一般是按照應(yīng)用邏輯。用于設(shè)置閑置的布爾項。
8.實時的邊沿事件。當(dāng)檢測到輸入信號的上升沿或下降沿時,對應(yīng)的布爾項為“高”,在一個時鐘周期之后,對應(yīng)的布爾項會自動變?yōu)椤暗汀薄O麓卧俅螜z測到上升沿或下降沿時,重復(fù)這個“高-低”的過程。這個條件與上面的2、3、4不同,沒有經(jīng)過鎖存,只持續(xù)一個時鐘周期。
? ? ? ? ? ? ? ? ?圖2.布爾項(slice)的構(gòu)成邏輯
1.4.3 模式匹配引擎的實現(xiàn)
如果我們把8個布爾項按順序排列,在每兩個布爾項之間就可以指定“與”或“或”操作。下圖給出了布爾項與操作(符)之間的關(guān)系。
? ? ? ? ? ? ? 圖3.布爾項與操作(符)直接的關(guān)系
參看1.4.1布爾表達式的例子,下面是把它們映射到內(nèi)部布爾項的示意圖。
? ? ? ? ? ? ? 圖4.布爾算式的內(nèi)部實現(xiàn)示意圖1
? ? ? ? ? ? ? ??圖5.布爾算式的內(nèi)部實現(xiàn)示意圖2
整個模式匹配引擎是由多級的“與-或”門的級聯(lián)實現(xiàn),下圖是完整內(nèi)部實現(xiàn)示意圖,有經(jīng)驗的讀者可以參照用戶手冊加深理解。
? ? ? ? ? ? ? ? ?圖6.模式匹配引擎完整示意圖
1.4.4引腳組合邏輯功能的配置
解釋完布爾項的概念和布爾項的選項,以及它們之間的運算關(guān)系,接下來看看與配置寄存器的對應(yīng)關(guān)系。
共有三個寄存器用于設(shè)置模式匹配引擎:
▲PMCTRL:模式匹配引擎的控制位和結(jié)果狀態(tài)位。
■ SEL_PMATCH?– 用于指定8個引腳中斷是來自于引腳中斷功能(見9.1節(jié))還是來自模式匹配功能(見9.4節(jié))。
■ENA_RXEV – 模式匹配結(jié)果為“真”時,用戶可以配置該位使能向CPU核心發(fā)送RXEV事件并在引腳GPIO_INT_BMAT產(chǎn)生輸出。
RXEV事件用于觸發(fā)WFE(Wait For Event: 等待事件)指令結(jié)束等待狀態(tài)。
■PMAT[7:0] – 這里每一位對應(yīng)布爾組合(i)的輸出,見圖37。
▲PMSRC:指定每個布爾項使用哪個輸入引腳
■如果要指定PINTSEL[n]對應(yīng)的引腳,作為布爾項i的輸入,只需要設(shè)置SRC(i)=n即可。
▲PMCFG:指定每個布爾項如何參與運算,同時指定結(jié)果輸出結(jié)點。
■CFG[7:0] – 每一個分量用于選擇對應(yīng)布爾項與輸入信號的關(guān)系,見1.4.2描述的8種情況。
■Prod_Endpts[6:0] – 如果某一位為‘0’,表示對應(yīng)的操作為‘與’,見圖3。某位為‘1’表示對應(yīng)的操作為‘或’,并且需要輸出前面各個布爾項相與的結(jié)果到PMAT[i],和對應(yīng)的中斷請求。
注意,不存在Prod_Endpts7。即使有這一位,它也會始終為‘1’。
1.4.5 模式匹配引擎的中斷
可以用模式匹配(引腳組合邏輯)的結(jié)果產(chǎn)生中斷,當(dāng)PMATn為’1’時,如果使能了對應(yīng)的PININTn中斷,則這些分項(見圖6中的綠色標注的信號),可以觸發(fā)中斷。這些中斷的觸發(fā)方式,在芯片內(nèi)部固定設(shè)置為高電平觸發(fā),不能由軟件配置。
既然是電平觸發(fā),這個中斷不能由軟件清除,只要這個信號為高,中斷就會反復(fù)出現(xiàn),直到該分項變?yōu)榈汀H绻幌M磸?fù)進入中斷,可以嘗試使用“實時的邊沿事件”作為組合條件(見1.4.2的第8個選項)。
1.5.模式匹配引擎的設(shè)置在配置模式匹配引擎之前,建議用戶先按照自己的布爾表達式,填寫下面的表格。
■第一行PinInt的每個位置,填寫對應(yīng)PININT0~7的引腳編號。未用位置不填。
■第二行SliceSrc的每個位置,填寫每個布爾項對應(yīng)的PININTn編號n,取值范圍是0~7。
■第三行SliceCfg的每個位置,填寫如何配置每個布爾項,見1.4.2節(jié),取值使用下述定義的常量:
■第四行SliceEndp的每個位置,填寫’0’表示這個位置執(zhí)行“與”運算并沒有結(jié)果輸出;填寫非’0’的值,表示這個位置執(zhí)行“或”運算并輸出結(jié)果。芯片中沒有SliceEndp第7個位置對應(yīng)的配置位,此處內(nèi)容無效。
此表格的目的是在寫代碼之前有一個完整的概念,這樣寫代碼時不會產(chǎn)生各個寄存器內(nèi)容不匹配的問題。
下面再定義幾個宏,配合上述表格就可以很容易地實現(xiàn)對模式匹配引擎的設(shè)置。
首先參考1.4.4節(jié)的寄存器說明,定義一組移位操作的宏,每三位代碼為一組進行移位:
#define PMPOS0(src) (((src)&0x7)<<8)
|
|
#define PMPOS1(src) (((src)&0x7)<<11)
|
|
#define PMPOS2(src) (((src)&0x7)<<14)
|
|
#define PMPOS3(src) (((src)&0x7)<<17)
|
|
#define PMPOS4(src) (((src)&0x7)<<20)
|
|
#define PMPOS5(src) (((src)&0x7)<<23)
|
|
#define PMPOS6(src) (((src)&0x7)<<26)
|
|
#define PMPOS7(src) (((src)&0x7)<<29)
|
#define PMep0(ep) (((ep)&0x1)<<0)
|
|
#define PMep1(ep) (((ep)&0x1)<<1)
|
|
#define PMep2(ep) (((ep)&0x1)<<2)
|
|
#define PMep3(ep) (((ep)&0x1)<<3)
|
|
#define PMep4(ep) (((ep)&0x1)<<4)
|
|
#define PMep5(ep) (((ep)&0x1)<<5)
|
|
#define PMep6(ep) (((ep)&0x1)<<6)
|
定義SliceCfg如下,逐個填入上述表格第三行的所有內(nèi)容:定義ProdEndp如下,逐個填入上述表格第四行的所有內(nèi)容,再和SliceCfg宏的結(jié)果相’或’ ,寫入PMCFG寄存器進行初始化:
下面再用實例說明初始化設(shè)置的過程。
1.6.模式匹配引擎的使用實例1.6.1異或的實現(xiàn)
假定有一個電梯控制器,它只有兩個按鈕,一個“向上”,一個“向下”,如果分別單獨按下任一個按鈕,電梯會按指定方向運行,如果兩個按鈕同時按下,則電梯不會運行。這就是“異或”操作邏輯。
下面的代碼用USERKEY和ISPKEY,分別代表“向上”和“向下”按鈕,用模式匹配引擎實現(xiàn)這個“異或”操作邏輯。
整個的布爾表達式是:
USERKEY * NOT(ISPKEY) + NOT(USERKEY) * ISPKEY
按照這個表達式填寫前述表格如下:
參照前面的介紹,解讀這個表格就很容易了:■第一行:USERKEY對應(yīng)PININT0;ISPKEY對應(yīng)PININT1。
■第二行:PININT0對應(yīng)布爾項0、2;PININT1對應(yīng)布爾項1、3。
■第三行:布爾項0、3的輸入為“高電平”有效;布爾項1、2的輸入為“低電平”有效。布爾項4~7的輸入恒為“低”,在最終結(jié)果中不產(chǎn)生影響。
■第四行:PMAT[1]得到布爾項0、1相與的結(jié)果;PMAT[3]得到布爾項2、3相與的結(jié)果。
下面的初始化代碼實現(xiàn)了上述表格的內(nèi)容:
代碼片段11.“異或”模式匹配初始化函數(shù)
01 void PM_Init(void)
02 {
03 LPC_SYSCON->PINTSEL[0] = PIN_USERKEY;
04 LPC_SYSCON->PINTSEL[1] = PIN_ISPKEY;
05
06 #if !PM_POLLING
07 NVIC_EnableIRQ(PININT1_IRQn);
08 NVIC_EnableIRQ(PININT3_IRQn);
09
10 NVIC_SetPriority(PININT1_IRQn, 3);
11 NVIC_SetPriority(PININT3_IRQn, 3);
12 #endif // PM_POLLING
13
14 LPC_PIN_INT->PMSRC = SliceSrc(0, 1, 1, 0, 0, 0, 0, 0);
15 LPC_PIN_INT->PMCFG = SliceCfg(SLICE_DIRECT,
16 SLICE_NOT,
17 SLICE_DIRECT,
18 SLICE_NOT,
19 SLICE_CONST0,
20 SLICE_CONST0,
21 SLICE_CONST0,
22 SLICE_CONST0) |
23 ProdEndp(0, 1, 0, 1, 0, 0, 0);
24 LPC_PIN_INT->PMCTRL = 0x03;
25
26 ConfigSWM(GPIO_INT_BMAT, PIN_LED0);
27 }
上述代碼段的14、15行是對模式匹配寄存器的初始化,這里用到了上一節(jié)定義的宏。
本例程有兩種操作模式:輪詢和中斷模式。在輪詢模式中,軟件循環(huán)地檢測PMCTRL寄存器的PMAT域,當(dāng)檢測到布爾輸出項1或3為’1’時,分別點亮LED1或LED3,否則熄滅LED。下面的PM_Polling()函數(shù)會被主循環(huán)調(diào)用,執(zhí)行循環(huán)檢測。代碼片段12.循環(huán)檢測PMAT狀態(tài)函數(shù)
01 void PM_Polling(void)
02 {
03 uint32_t PMAT = LPC_PIN_INT->PMCTRL>>24;
04
05 if (PMAT & (1<<1))
06 LPC_GPIO_PORT->CLR0 = 1<
07 else if (PMAT & (1<<3))
08 LPC_GPIO_PORT->CLR0 = 1<
09 else
10 LPC_GPIO_PORT->SET0 = 1<
11 }
在中斷模式下,需要安排兩個中斷處理程序PININT1_IRQHandler()和PININT3_IRQHandler(),分別接收布爾輸出項1和3的中斷。
代碼片段13.中斷處理函數(shù)
01
02 void PININT1_IRQHandler(void)
03 {
04 LPC_GPIO_PORT->CLR0 = 1<
05 }
06
07 void PININT3_IRQHandler(void)
08 {
09 LPC_GPIO_PORT->CLR0 = 1<
10 }
11
最后是主函數(shù)。代碼片段14.模式匹配初始化函數(shù)
01 int main()
02 {
03 GPIO_Init();
04
05 LPC_GPIO_PORT->DIRSET0 = PIN_LEDS_MASK; // 設(shè)置LED對應(yīng)的引腳為輸出
06 LPC_GPIO_PORT->SET0 = PIN_LEDS_MASK; // 熄滅所有LED燈
07
08 PINT_Init(); // 初始化引腳中斷模塊
09 PM_Init(); // 初始化模式匹配模塊,見代碼片段21
10
11 while(1) {
12 #if PM_POLLING
13 PM_Polling();
14 #else
15 LPC_GPIO_PORT->SET0 = 1<
16 #endif
17 LPC_PIN_INT->RISE = 0xFF;
18 LPC_PIN_INT->FALL = 0xFF;
19 }
20 }
在中斷模式下,當(dāng)沒有中斷時,主函數(shù)的第15行會不斷地熄滅LED。但當(dāng)有某個按鈕按下時,由于模式匹配的中斷是電平中斷,一旦產(chǎn)生中斷的條件存在,代碼片段23的中斷處理函數(shù)就會不斷地被調(diào)用,CPU將不能執(zhí)行主函數(shù)中的指令。這樣當(dāng)僅按下一個按鈕時,使用者就會看見對應(yīng)的LED常亮,直到松開按鈕。松開按鈕后,產(chǎn)生中斷的條件消失,CPU才能返回主函數(shù),LED被熄滅。
在代碼片段21的模式匹配初始化函數(shù)的第26行,模式匹配的結(jié)果輸出信號GPIO_INT_BMAT被映射到對應(yīng)LED0的引腳,通過LED0的狀態(tài)可以直觀地看到整個邏輯運算的結(jié)果。綜合起來會有如下效果:1.6.2三人表決器
三人表決器就是每人一個按鈕,任意兩人按下按鈕則表示表決通過。
本實例使用開發(fā)板上的三個按鍵USERKEY、WAKEKEY和ISPKEY。首先填寫初始化的表格:
所有的布爾項都使用“鎖存的上升沿”作為模式匹配的輸入。電路的配置是按下按鍵會把引腳短接到地,因此抬起按鍵的動作會產(chǎn)生上升沿,經(jīng)過內(nèi)部鎖存,可以保證不同按鍵的按下時間不同,但狀態(tài)不會丟失。
下面是這個三人表決器的初始化函數(shù),可以看出這個函數(shù)內(nèi)容基本和上面例子的函數(shù)一樣,只是代入的參數(shù)不同:
代碼片段15.“三人表決器”模式匹配初始化函數(shù)
01 void PM_Init(void)
02 {
03 LPC_SYSCON->PINTSEL[0] = PIN_USERKEY;
04 LPC_SYSCON->PINTSEL[1] = PIN_WAKEKEY;
05 LPC_SYSCON->PINTSEL[2] = PIN_ISPKEY;
06
07 #if !PM_POLLING
08 NVIC_EnableIRQ(PININT1_IRQn);
09 NVIC_EnableIRQ(PININT3_IRQn);
10 NVIC_EnableIRQ(PININT5_IRQn);
11
12 NVIC_SetPriority(PININT1_IRQn, 3);
13 NVIC_SetPriority(PININT3_IRQn, 3);
14 NVIC_SetPriority(PININT5_IRQn, 3);
15 #endif // PM_POLLING
16
17 LPC_PIN_INT->PMSRC = SliceSrc(0, 1, 1, 2, 2, 0, 0, 0);
18 LPC_PIN_INT->PMCFG = SliceCfg(SLICE_RISE,
19 SLICE_RISE,
20 SLICE_RISE,
21 SLICE_RISE,
22 SLICE_RISE,
23 SLICE_RISE,
24 SLICE_CONST0,
25 SLICE_CONST0) |
26 ProdEndp(0, 1, 0, 1, 0, 1, 0);
27 LPC_PIN_INT->PMCTRL = 0x03;
29
30 ConfigSWM(GPIO_INT_BMAT, PIN_LED0);
31 }
以下是主函數(shù)中的主循環(huán)部分。
代碼片段16.“三人表決器”主循環(huán)部分
01 while(1) {
02 TimeOut_Ctrl();
03 #if PM_POLLING
04 PM_Polling();
05 #else
06 LPC_GPIO_PORT->SET0 = 1<
07 #endif
08 }
在主循環(huán)下,同樣有中斷模式和輪詢模式的區(qū)分。操作和前面的“異或”例程一致。
特殊的是,這個例程設(shè)置了一個超時處理,能夠定期地清除投票狀態(tài)。
代碼片段17.“三人表決器”的超時處理
01 void TimeOut_Ctrl()
02 {
03 if (uwTick > 10) {
04 LPC_GPIO_PORT->NOT0 = 1<
05 uwTick = 0;
06
07 LPC_PIN_INT->PMCTRL = 0x00;
08 LPC_PIN_INT->PMCTRL = 0x03;
09 }
10 }
代碼片段18.“三人表決器”中斷模式的中斷處理
01 void PININT1_IRQHandler(void)
02 {
03 LPC_GPIO_PORT->CLR0 = 1<
04 TimeOut_Ctrl();
05 }
以下是這個例程演示的效果:
表中的“投”表示按鍵被按下并被釋放。如果只按下按鍵,而保持按下而不釋放,模式匹配模塊不會判斷它為按下狀態(tài),這種情況可以理解為,投票者還在猶豫。
當(dāng)LED0亮?xí)r表示不夠兩個人以上投票,投票沒有通過;LED0熄滅表示投票通過。
在使用這個例程的中斷模式時,由于各個中斷的優(yōu)先級一致,同時由于電平中斷的緣故,如果一旦進入了某個中斷,有可能另一個布爾項的中斷不能被響應(yīng),結(jié)果LED1/3/5中有些時候不能被同時點亮。這種現(xiàn)象,不會出現(xiàn)在輪詢模式下。讀者由此可以體會到輪詢與中斷的區(qū)別。
-
mcu
+關(guān)注
關(guān)注
146文章
16885瀏覽量
349922 -
恩智浦
+關(guān)注
關(guān)注
14文章
5817瀏覽量
106329 -
引腳
+關(guān)注
關(guān)注
16文章
1172瀏覽量
50151 -
組合邏輯
+關(guān)注
關(guān)注
0文章
46瀏覽量
10026 -
LPC800
+關(guān)注
關(guān)注
1文章
17瀏覽量
22445
原文標題:LPC800前生今世-第八章 引腳中斷和引腳組合邏輯 (Pin Interrupt & Pin Pattern)
文章出處:【微信號:NXP_SMART_HARDWARE,微信公眾號:恩智浦MCU加油站】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
相關(guān)推薦
評論