4.1 GPIO簡介
????????輸入輸出(I/O)是一個非常重要的概念。I/O泛指所有類型的輸入輸出端口,包括單向的端口如邏輯門電路的輸入輸出管腳和雙向的GPIO端口。而GPIO(General-Purpose Input/Output)則是一個常見的術語,指的是通用輸入輸出接口。
? ? ? ? 下面有請DeepSeek發言
?
LPC1110系列Cortex-M0微控制器的GPIO口的結構特點:
1 | 端口可由軟件配置為輸入輸出 |
2 | 引腳默認為輸入(所以點燈時需要改下方向) |
3 | 端口引腳的讀寫操作可屏蔽 |
4 | 每個單獨引腳可被用作外部中斷輸入引腳 |
5 | 每個GPIO中斷可配置為 高、低電平、下降、上升沿或雙邊沿觸發 |
6 | 可對單獨端口的中斷級別進行配置 |
4.2 GPIO口的寄存器
????????所有GPIO寄存器都為32位?
? ? ? ? GPIO端口基址為
端口0? | ?0x5000 0000 |
端口1? | ?0x5001 0000 |
端口2 | ?0x5002 0000 |
端口3 | ?0x5003 0000 |
4.2.1 數據寄存器? GPIOnDATA
? ? ? ? 用于讀取輸入引腳的狀態數據或者配置輸出引腳的輸出狀態
? ? ? ? 對應端口位置后四位范圍為 0000 ~ 3FFC
11:0 | PIOn_0 ~ PIOn_11的輸入/輸出數據 |
31:12 | 保留 |
4.2.2 方向寄存器 GPIOnDIR
11:0 | PIOn_0 ~ PIOn_11的輸入/輸出方向? ?0為輸入, 1為輸出? 位數0-11與0-11引腳一一對應 |
31:12 | 保留 |
4.2.3 中斷觸發寄存器 GPIOnIS
? ? ? ? 相較于基地址偏移量0x8004 即0x500n 8004
11:0 | PIOn_x? ? 0為邊沿觸發,1為電平觸發 |
31:12 | 保留 |
4.2.4 中斷雙邊沿觸發寄存器?GPIOnIBE
? ? ? ? 相較于基地址偏移量0x8008 即0x500n 8008
11:0 | 0為通過4.2.5中寄存器GPIOnIEV控制PIOn_x的中斷 1為通過PIOn_x上雙邊沿觸發中斷 |
31:12 | 保留 |
4.2.5 中斷事件寄存器 GPIOnIEV
????????相較于基地址偏移量0x800C?即0x500n 800C
11:0 | 0為上升沿或者高電平觸發中斷 1為下降沿或者低電平觸發中斷 具體邊沿還是電平 看4.2.3中GPIOnIS的設置 |
31:12 | 保留 |
4.2.6 中斷屏蔽寄存器 GPIOnIE
????????相較于基地址偏移量0x8010?即0x500n 8010
11:0 | 0為中斷被屏蔽 1為中斷不被屏蔽 |
31:12 | 保留 |
4.2.7 原始中斷狀態寄存器 GPIOnIRS
? ? ? ??相較于基地址偏移量0x8014?即0x500n 8014
? ? ? ? 屏蔽之前的中斷狀態
11:0 | 0為無中斷 1為滿足中斷要求 |
31:12 | 保留 |
4.2.8 屏蔽中斷狀態寄存器 GPIOnMIS
????????相較于基地址偏移量0x8018?即0x500n 8018
? ? ? ? 考慮了屏蔽操作之后是否有中斷
11:0 | 0為無中斷,或者中斷被屏蔽 1為滿足中斷要求 |
31:12 | 保留 |
4.2.9 中斷清除寄存器 GPIOnIC
????????相較于基地址偏移量0x801C?即0x500n 801C
11:0 | 0無操作 1為清除PIOn_x上的邊沿檢測邏輯 |
31:12 | 保留 |
4.3 LPC上的GPIO按鍵
? ? ? ? 按鍵按下引腳低電平,不按是高電平
4.4 按鍵控制LED閃爍頻率
任務:
1.?BUTTON(PIO3_5)按鍵按下,閃爍頻率為1Hz,再次按下,恢復閃爍頻率為0.5Hz;
2. WEAKUP(PIO1_4)按鍵按下,閃爍頻率為2Hz,再次按下,恢復閃爍頻率為0.5Hz;
3. 適當考慮按鍵防抖功能。? ? ? ? ? ??
思路:
? ? ? ? 對于閃爍頻率的修改,首先考慮用什么控制LED閃爍,結合上章可以用SysTick,然后按鍵按下改變SysTick周期即可
? ? ? ? 對于按鍵防抖,由于按鍵固有的物理結構,按下后彈簧一上一下會影響中斷,需要用延時函數過濾抖動。
抖動時間大概10ms這樣, 我們可以用個延時函數過濾掉這個抖動過程,延時20ms就足夠了
代碼:
利用之前寫過的函數即可,復制個新工程,然后main文件里代碼如下
#include <LPC11xx.h>
#include "LED.h"//延時ms函數 // 太粗糙了,而且要根據機器指令與時鐘周期關系調整,也就防抖延時用一下
__inline void delay_ms(uint32_t a) //約1ms延時函數
{ uint32_t i;while( a -- != 0){for(i = 0; i<5500; i++);}
}int flag1 = 0, flag2 = 0; // 判斷botton 和 wakeup 按鍵上一次狀態
int main()
{LED_Init(); // PIO1_4LPC_IOCON->PIO1_4 &= ~(0x1F); // 清除之前的配置LPC_IOCON->PIO1_4 |= 0x00; // 配置為GPIO功能LPC_GPIO1->DIR &= ~(1UL << 4);// 設置GPIO方向為輸入LPC_GPIO1->IS &= ~(0x1 << 4); // 清除第 4 位,設置為邊沿觸發LPC_GPIO1->IBE &= ~(0x1 << 4); // 清除第 4 位,設置為單邊沿觸發LPC_GPIO1->IEV &= ~(0x1 << 4); // 清除第 4 位,設置為低電平觸發LPC_GPIO1 -> IE |= (0x1<<4); // 使能端口中斷LPC_IOCON->PIO1_4 |= (1UL << 5); // 使能滯后模式LPC_GPIO1->IC |= (1UL << 4); // 清除中斷標志位NVIC_EnableIRQ(EINT1_IRQn); // 使能GPIO1中斷// PIO3_5LPC_IOCON->PIO3_5 &= ~(0x1F); // 清除之前的配置LPC_IOCON->PIO3_5 |= 0x00; // 配置為GPIO功能LPC_GPIO3->DIR &= ~(1UL << 5);// 設置GPIO方向為輸入LPC_GPIO3->IS &= ~(0x1 << 5); // 清除第 5 位,設置為邊沿觸發LPC_GPIO3->IBE &= ~(0x1 << 5); // 清除第 5 位,設置為單邊沿觸發LPC_GPIO3->IEV &= ~(0x1 << 5); // 清除第 5 位,設置為低電平觸發LPC_GPIO3 -> IE |= (0x1<<5); // 使能端口中斷LPC_IOCON->PIO3_5 |= (1UL << 5); // 使能滯后模式LPC_GPIO3->IC |= (1UL << 5); //清除中斷標志NVIC_EnableIRQ(EINT3_IRQn);SysTick_Config(SystemCoreClock/100); // 0.01s進一次中斷 1s翻轉一次 0.5 Hzwhile(1){}
}void SysTick_Handler() /// 系統節拍定時器中斷函數
{static unsigned long ticks;if(ticks++ >= 99){ticks = 0;LED_Toggle();}
}// GPIO3_5的中斷服務函數,處理BUTTON按鍵按下事件
void PIOINT3_IRQHandler(void)
{if((LPC_GPIO3->MIS & (1UL << 5)) == (1UL << 5))// 檢查是否是PIO3_5的中斷{ delay_ms(20); // 消抖while((LPC_GPIO3->DATA & (1UL << 5)) == 0);delay_ms(20);if(flag1)SysTick_Config(SystemCoreClock/100); // 0.01s進一次中斷 1s翻轉一次 0.5 Hzelse SysTick_Config(SystemCoreClock/200); // 0.005s進一次中斷 0.5s翻轉一次 1 Hzflag1 = !flag1;LPC_GPIO3->IC |= (1UL << 5); // 清除中斷標志}
}
// GPIO1_4的中斷服務函數,處理WAKEUP按鍵按下事件
void PIOINT1_IRQHandler(void)
{if((LPC_GPIO1->MIS & (1UL << 4)) == (1UL << 4)) // 檢查是否是PIO1_4的中斷{delay_ms(20);while((LPC_GPIO1->DATA & (1UL << 4)) == 0);delay_ms(20);if(flag2)SysTick_Config(SystemCoreClock/100); // 0.01s進一次中斷 1s翻轉一次 0.5 Hzelse SysTick_Config(SystemCoreClock/400); // 0.0025s進一次中斷 0.2s翻轉一次 2 Hz flag2 = !flag2;LPC_GPIO1->IC |= (1UL << 4); // 清除中斷標志}
}
模塊化一下,新建Button.c Button.h文件,便于之后移植工程
main.c
#include <LPC11xx.h>
#include "LED.h"
#include "Button.h"int main()
{LED_Init(); WAKEUP_Init();Button_Init();while(1){}
}void SysTick_Handler() /// 系統節拍定時器中斷函數
{static unsigned long ticks;if(ticks++ >= 99){ticks = 0;LED_Toggle();}
}
Button.c
#include "Button.h"
int flag1 = 0, flag2 = 0; // 判斷botton 和 wakeup 按鍵上一次狀態//延時ms函數 // 太粗糙了,而且要根據機器指令與時鐘周期關系調整,也就防抖延時用一下
__inline void delay_ms(uint32_t a) //約1ms延時函數
{ uint32_t i;while( a -- != 0){for(i = 0; i<5500; i++);}
}void WAKEUP_Init(void)
{LPC_SYSCON -> SYSAHBCLKCTRL |= (1UL << 6) | (1UL << 16); // 使能GPIO時鐘和IO時鐘// PIO1_4LPC_IOCON->PIO1_4 &= ~(0x1F); // 清除之前的配置LPC_IOCON->PIO1_4 |= 0x00; // 配置為GPIO功能LPC_GPIO1->DIR &= ~(1UL << 4);// 設置GPIO方向為輸入LPC_GPIO1->IS &= ~(0x1 << 4); // 清除第 4 位,設置為邊沿觸發LPC_GPIO1->IBE &= ~(0x1 << 4); // 清除第 4 位,設置為單邊沿觸發LPC_GPIO1->IEV &= ~(0x1 << 4); // 清除第 4 位,設置為低電平觸發LPC_GPIO1 -> IE |= (0x1<<4); // 使能端口中斷LPC_IOCON->PIO1_4 |= (1UL << 5); // 使能滯后模式LPC_GPIO1->IC |= (1UL << 4); // 清除中斷標志位NVIC_EnableIRQ(EINT1_IRQn); // 使能GPIO1中斷
}void Button_Init(void)
{LPC_SYSCON -> SYSAHBCLKCTRL |= (1UL << 6) | (1UL << 16); // 使能GPIO時鐘和IO時鐘// PIO3_5LPC_IOCON->PIO3_5 &= ~(0x1F); // 清除之前的配置LPC_IOCON->PIO3_5 |= 0x00; // 配置為GPIO功能LPC_GPIO3->DIR &= ~(1UL << 5);// 設置GPIO方向為輸入LPC_GPIO3->IS &= ~(0x1 << 5); // 清除第 5 位,設置為邊沿觸發LPC_GPIO3->IBE &= ~(0x1 << 5); // 清除第 5 位,設置為單邊沿觸發LPC_GPIO3->IEV &= ~(0x1 << 5); // 清除第 5 位,設置為低電平觸發LPC_GPIO3 -> IE |= (0x1<<5); // 使能端口中斷LPC_IOCON->PIO3_5 |= (1UL << 5); // 使能滯后模式LPC_GPIO3->IC |= (1UL << 5); //清除中斷標志NVIC_EnableIRQ(EINT3_IRQn);
}// GPIO3_5的中斷服務函數,處理BUTTON按鍵按下事件
void PIOINT3_IRQHandler(void)
{if((LPC_GPIO3->MIS & (1UL << 5)) == (1UL << 5))// 檢查是否是PIO3_5的中斷{ delay_ms(20); // 消抖while((LPC_GPIO3->DATA & (1UL << 5)) == 0);delay_ms(20);if(flag1)SysTick_Config(SystemCoreClock/100); // 0.01s進一次中斷 1s翻轉一次 0.5 Hzelse SysTick_Config(SystemCoreClock/200); // 0.005s進一次中斷 0.5s翻轉一次 1 Hzflag1 = !flag1;LPC_GPIO3->IC |= (1UL << 5); // 清除中斷標志}
}
// GPIO1_4的中斷服務函數,處理WAKEUP按鍵按下事件
void PIOINT1_IRQHandler(void)
{if((LPC_GPIO1->MIS & (1UL << 4)) == (1UL << 4)) // 檢查是否是PIO1_4的中斷{delay_ms(20);while((LPC_GPIO1->DATA & (1UL << 4)) == 0);delay_ms(20);if(flag2)SysTick_Config(SystemCoreClock/100); // 0.01s進一次中斷 1s翻轉一次 0.5 Hzelse SysTick_Config(SystemCoreClock/400); // 0.0025s進一次中斷 0.2s翻轉一次 2 Hz flag2 = !flag2;LPC_GPIO1->IC |= (1UL << 4); // 清除中斷標志}
}
Button.h
#ifndef _BUTTON_H_
#define _BUTTON_H_#include <LPC11xx.h>void WAKEUP_Init(void);
void Button_Init(void);#endif