大家好,我是痞子衡,是正經搞技術的痞子。今天痞子衡給大家介紹的是恩智浦全系列MCU(包含Kinetis, LPC, i.MXRT, MCX)的GPIO電平中斷設計差異。
在痞子衡舊文 《以i.MXRT1xxx的GPIO模組為例談談中斷處理常式(IRQHandler)的標準流程》裡,痞子衡主要介紹得是 GPIO 一般控制以及最常用的輸入邊沿中斷相關知識。最近恩智浦官方社群有使用者反映 i.MXRT1060 上 GPIO 中斷狀態暫存器(GPIO->ISR)在發生有效電平中斷後的置位並不需要手動清零(W1C),其會在 I/O 輸入電平狀態切換後自動清零,這和手冊裡描述不一致。
首先在痞子衡的認知裡 GPIO 輸入電平中斷沒有什麼具體應用場景,想象一下,如果 GPIO 中斷事件由輸入電平值來觸發,如果發生了有效輸入電平且其狀態不改變,那麼 GPIO 中斷響應函數就會被不斷重複執行(此時 CPU 時間片無法再分給主函數),什麼樣的任務需要這樣的處理呢?暫且不論應用場景,痞子衡今天就從恩智浦全系列 MCU 這方面的行為角度來做一下對比吧。
恩智浦現有的經典 Arm Cortex-M MCU 產品線共有如下五大類,它們在 GPIO 一般控制和中斷控制外設上是有差異的。首先 i.MXRT四位數/Kinetis/LPC 這三條線各自是完全不同的外設,然後 i.MXRT三位數是在 LPC 外設基礎上做了增強,而最新的 MCX 系列則是組合了 Kinetis 和 LPC 外設。
晶片系列 | I/O一般控制 | I/O中斷控制 |
---|---|---|
Kinetis | GPIO type1 | PORT |
LPC | GPIO type2 | PINT |
i.MXRT四位數 | GPIO type3 | GPIO type3 |
i.MXRT三位數 | GPIO type2 | GPIO type2(增加interrupt A/B) PINT |
MCX | GPIO type1 | GPIO type1(整合Kinetis PORT) PINT |
根據上一節外設情況我們知道,只要測試了 i.MXRT四位數/Kinetis/LPC 這三個系列的情況,剩下兩個系列自然也就不用測試了。
Kinetis 系列分為 K/KL/KE/KS/KW/KV/KM/K32L 等若干子系列,但是它們關於 GPIO 中斷設計這一塊是一樣的。痞子衡選取了 MKL03Z 這顆晶片來做的測試,檢視其手冊 PORTx->PCRn[ISF] 位或者 PORTx->ISFR 暫存器均標記了中斷狀態,並且標明瞭需要做 W1C 操作。
我們可以直接在 \SDK_2.3.1_FRDM-KL03Z\boards\frdmkl03z\driver_examples\gpio\input_interrupt 例程上做測試,只需要做簡單修改,痞子衡摘取了主要程式碼如下。FRDM-KL03Z 板上 SW3 按鍵對應 PTB5 引腳(按下為低電平,鬆開為高電平),程式碼設計裡按一次 SW3 便列印一次。測試結果來看,在 Kinetis 上即使是電平中斷,PORTx->ISFR 暫存器也是必須要手動清零的,與手冊描述一致。
IRQ函數中是否清零Flag | SW3動作 | IRQ執行情況 | 列印輸出結果 |
---|---|---|---|
是 | 上電預設鬆開(高電平) | IRQ函數未觸發 | 無 |
SW3按下(低電平) | IRQ函數重複執行 | 無 | |
SW3鬆開(高電平) | IRQ函數不再觸發 | 出現一次列印 | |
否 | 上電預設鬆開(高電平) | IRQ函數未觸發 | 無 |
SW3按下(低電平) | IRQ函數重複執行 | 無 | |
SW3鬆開(高電平) | IRQ函數重複執行 | 無 |
volatile bool g_ButtonPress = false;
void PORTB_IRQHandler(void)
{
// 清除中斷標誌
PORTB->ISFR = 1U << 5U;
g_ButtonPress = true;
}
int main(void)
{
// 省略 PTB5 引腳的 PINMUX 設定
gpio_pin_config_t sw_config = {
kGPIO_DigitalInput, 0,
};
// 僅需此處修改:將 GPIO 中斷模式改為低電平觸發
PORT_SetPinInterruptConfig(PORTB, 5U, kPORT_InterruptLogicZero);
NVIC_EnableIRQ(PORTB_IRQn);
GPIO_PinInit(GPIOB, 5U, &sw_config);
while (1)
{
if (g_ButtonPress)
{
delay();
PRINTF(" %s is pressed \r\n", "SW3");
g_ButtonPress = false;
}
}
}
i.MXRT四位數系列分為 RT1010/1015/1020/1040/1050/1060/1160/1170/1180 等若干子型號,但是它們關於 GPIO 中斷設計是一樣的。痞子衡選取了 i.MXRT1062 這顆晶片來做的測試,檢視其手冊 GPIOx->ISR 暫存器標記了中斷狀態,同樣標明瞭需要做 W1C 操作。
我們可以直接在 \SDK_2_12_1_EVK-MIMXRT1060\boards\evkmimxrt1060\driver_examples\gpio\input_interrupt 例程上做測試,只需要做簡單修改,主要程式碼如下。MIMXRT1060-EVK 板上 SW8 按鍵對應 WAKEUP_GPIO5[0] 引腳(按下為低電平,鬆開為高電平),程式碼設計裡按一次 SW8 便列印一次。測試結果來看,在 i.MXRT 四位數上如果是電平中斷,GPIOx->ISR 暫存器會在電平狀態切換時自動清零,跟手冊描述有點差異,不過這樣的設計比 Kinetis 上看起來更合理。
IRQ函數中是否清零Flag | SW8動作 | IRQ執行情況 | 列印輸出結果 |
---|---|---|---|
是/否 | 上電預設鬆開(高電平) | IRQ函數未觸發 | 無 |
SW8按下(低電平) | IRQ函數重複執行 | 無 | |
SW8鬆開(高電平) | IRQ函數不再觸發 | 出現一次列印 |
volatile bool g_InputSignal = false;
void GPIO5_Combined_0_15_IRQHandler(void)
{
// 清除中斷標誌
GPIO5->ISR = 1U << 0U;
g_InputSignal = true;
__DSB();
}
int main(void)
{
// 省略 WAKEUP 引腳的 PINMUX 設定
gpio_pin_config_t sw_config = {
kGPIO_DigitalInput,
0,
kGPIO_IntLowLevel, // 僅需此處修改:將 GPIO 中斷模式改為低電平觸發
};
GPIO_PortEnableInterrupts(GPIO5, 1U << 0U);
NVIC_EnableIRQ(GPIO5_Combined_0_15_IRQn);
GPIO_PinInit(GPIO5, 0U, &sw_config);
while (1)
{
if (g_InputSignal)
{
delay();
PRINTF(" %s is turned on. \r\n", "SW8");
g_InputSignal = false;
}
}
}
LPC系列分為 800/1x00/4000/4300/51Uxx/54000/5500 等若干子型號,但是它們關於 GPIO 中斷設計是一樣的。痞子衡選取了 LPC54114 這顆晶片來做的測試,檢視其手冊 PINT->IST 暫存器標記了中斷狀態,這裡關於 W1C 操作做了邊沿方式和電平方式的區別,其中對於電平方式,W1C 是切換有效電平邏輯。
我們可以直接在 \SDK_2_9_0_LPCXpresso54114\boards\lpcxpresso54114\driver_examples\pint\pin_interrupt 例程上做測試,只需要做簡單修改,主要程式碼如下。LPCXpresso-54114 板上 SW1 按鍵對應 PIO0[24] 引腳(按下為低電平,鬆開為高電平),程式碼設計裡按一次 SW1 便列印一次。測試結果來看,在 LPC 上如果是電平中斷,PINT->IST 暫存器會在電平狀態切換時自動清零,跟手冊描述有點差異,並且中斷處理常式裡如果主動加上 W1C 操作其效果就變成了雙邊沿中斷,這樣的設計比 i.MXRT 四位數更進了一步。
IRQ函數中是否清零Flag | SW1動作 | IRQ執行情況 | 列印輸出結果 |
---|---|---|---|
否 | 上電預設鬆開(高電平) | IRQ函數未觸發 | 無 |
SW1按下(低電平) | IRQ函數重複執行 | 無 | |
SW1鬆開(高電平) | IRQ函數不再觸發 | 出現一次列印 | |
是 | 上電預設鬆開(高電平) | IRQ函數未觸發 | 無 |
SW1按下(低電平) | IRQ函數執行一次 | 出現一次列印 | |
SW1鬆開(高電平) | IRQ函數執行一次 | 出現一次列印 |
volatile bool g_ButtonPress = false;
void PIN_INT0_DriverIRQHandler(void)
{
uint32_t pmstatus = PINT_PatternMatchResetDetectLogic(PINT);
if (s_pintCallback[kPINT_PinInt0] != NULL)
{
s_pintCallback[kPINT_PinInt0](kPINT_PinInt0, pmstatus);
}
// 清除中斷標誌
PINT->IST = (1UL << (uint32_t)kPINT_PinInt0);
__DSB();
}
void pint_intr_callback(pint_pin_int_t pintr, uint32_t pmatch_status)
{
g_ButtonPress = true;
}
int main(void)
{
INPUTMUX_Init(INPUTMUX);
INPUTMUX_AttachSignal(INPUTMUX, kPINT_PinInt0, kINPUTMUX_GpioPort0Pin24ToPintsel);
PINT_Init(PINT);
// 僅需此處修改:將 GPIO 中斷模式改為低電平觸發
PINT_PinInterruptConfig(PINT, kPINT_PinInt0, kPINT_PinIntEnableLowLevel, pint_intr_callback);
PINT_EnableCallbackByIndex(PINT, kPINT_PinInt0);
while (1)
{
if (g_ButtonPress)
{
delay();
PRINTF(" %s Pin Interrupt event detected \r\n", "SW1");
g_ButtonPress = false;
}
}
}
至此,恩智浦全系列MCU的GPIO電平中斷設計差異痞子衡便介紹完畢了,掌聲在哪裡~~~
文章會同時釋出到我的 部落格園主頁、CSDN主頁、知乎主頁、微信公眾號 平臺上。
微信搜尋"痞子衡嵌入式"或者掃描下面二維條碼,就可以在手機上第一時間看了哦。
最後歡迎關注痞子衡個人微信公眾號【痞子衡嵌入式】,一個專注嵌入式技術的公眾號,跟著痞子衡一起玩轉嵌入式。
衡傑(痞子衡),目前就職於某知名外企半導體公司MCU系統部門,擔任嵌入式系統應用工程師。
專欄內所有文章的轉載請註明出處:http://www.cnblogs.com/henjay724/
與痞子衡進一步交流或諮詢業務合作請發郵件至 https://www.cnblogs.com/henjay724/p/[email protected]
可以關注痞子衡的Github主頁 https://github.com/JayHeng,有很多好玩的嵌入式專案。
關於專欄文章有任何疑問請直接在部落格下面留言,痞子衡會及時回覆免費(劃重點)答疑。
痞子衡郵箱已被私信擠爆,技術問題不推薦私信,堅持私信請先掃碼付款(5元起步)再發。