MCU單晶片韌體設計

2015年9月30日 星期三

ARM MCU Board (ARM Cortex-M0)

 
這塊板子MCU採用的是新唐科技的NANO130低功耗系列晶片(NANO130SC2BN) , 其相關規格如下:
  • I2C延伸腳座x2
  • UART延伸腳座
  • DAC延伸腳座x2
  • ADC延伸腳座x7
  • PWM延伸腳座x7
  • ICE燒入腳座
  • GPIO發光二極體x1
  • GPIO按鈕x1
  • USB 2.0 Port
  • ADC可變電阻
  • Reset 按鈕開關
  • External Power Supply延伸腳座
  • OPT INT Power Jump 腳座
  • External Power Supply 穩定電容座
  • 三色led判斷Power Source狀態(紅:ICE 綠:PI 2 藍:USB)
  • 40 pin Head ( for raspberry Pi )



2015年9月27日 星期日

MX25L4005A SPI NOR Flash的使用方式



Raspberry Pi I/O Shield 上的MX25L4005A  SPI NOR Flash的使用方式


相關規格如下:
Flash size: 4Mbit (512KB), 其內部記憶體的組織結構為
  1. 64K Block size (for Block Erase) 
  2. 4K Sector size (for Sector Erase) 
  3. 256 Byte Page size (for Page Program) 



工作電壓為 .7 to 3.6 volt for read, erase, and program 操作
SPI 工作模式為 : Mode 0 & Mode 1

操作的相關注意事項: 
1.Flash 要寫入page之前,要先Erase
2.執行Page Program動作,byte 寫入"0", 無法寫入"1", 要執行Erase指令才能 將byte 設回"1"
3.資料送出的順序為 MSB First

執行動作

SE, BE, CE, and WRSR 會改變Flash內容的Command, 都要先設定WEL bit=1 (in RDSR bit 1), 一旦操作完成後, 它會自動reset (將WEL bit設回0)

操作方法: 

CS# goes low
     sending WREN (0x6); // set WEL=1
CS# goes high

CS# goes low
     sending <CMD>+{Flash Address}+{Data}//see command table
CS# goes high

CS# goes low
  deadline = jiffies + MAX_READY_WAIT_JIFFIES;
   do {
           status=sending RDSR(0x5) ;
            if (!(status & WIP) ) return 0;   //判斷是否操作完成
              cond_resched();                   // 再排程
        } while (!time_after_eq(jiffies, deadline));
CS# goes high 


Flash Address 的定址方法

NOR Flash 定址方法和一般SRAM,ROM, EEPROM 的定址方址相同,容量有多大,位址線就有多少條。容量有512 Kbyte 表示存取某一位置為需要19條位址線來定址。一般Parallel NOR Flash 介面,就會在IC元件上看到位址線 A0~A18的pin腳。但若為使用較少Pin count 以縮小IC 大小, 使用serial  Flash 是另一種解決方案。MX25L4005A  即是採用serial 的 SPI 介面。在送出位址時得用到3個byte ,也才足以表示這19條位址訊號。

位址結構:

位址用以定址到某一個block下的某一個sector, 再到某一個sector下的某一個page, 再到該page 內的offset。

若要進行Flash Block Erase (BE) 時, 描述Flash Addr [18:16] 。
若要進行Flash Sector Erase (SE)時, 描述Flash Addr [18:12]。
若要進行Flash 讀取(READ)時, 描述Flash Addr [18:0]。
若要進行Flash 寫入(WRITE)時, 描述Flash Addr [18:0]。
(每次寫入都是基於某一個page #內寫入)




2015年9月21日 星期一

ESP8266 wifi 通訊模組韌體燒錄簡介


ESP8266 wifi 通訊模組韌體燒錄簡介

ESP8266是一款UART轉WiFi的SoC晶片,被拿來組合成各種五花八門的擴充板,其中最常見也最容易買到的模組型號就 ESP-01,尺寸迷你價格便宜。
此篇文章進行燒錄 ESP模組是使用mac os x 平台,但是如果是使用Linux平台的話做法應該是一樣。

pin 腳定義

買到手的時候會發現,模組上有八根沒有標示的 pin 腳。上網查了一下,可以知道pin 腳配置如下:


UART介面的 wifi 模組

ESP8266模組到手的時候,就可以透過模組上的 Tx/Rx (UART 介面)的 pin腳來下AT command 進行簡單的 WiFi 連線。本身支援AP模式(無線網路基地台)和一般模式。通訊協定上支援 TCP 和 UDP,可在無線區域網路中扮演伺服器(Server)等候連入,或扮演客戶端(Client)去與Server連線。
我一般使用可以簡單買到的 USB-UART 轉接線和ESP模組連接。而接法如下:
ESP8266 pin:
GND
UTxD ----> RxD (USB-UART 轉接線)
GPIO_2 --> 不用接
CH_PD ---> 3.3V (電源,不能接5V!會燒掉)
GPIO_0 --> 不要接!!
RST -----> 不用接
URxD ----> TxD (USB-UART 轉接線)
VCC -----> 3.3V (電源,不能接5V!會燒掉)

電流需求 : 200 ~ 300 mA
ESP8266對於電源電流供應會有一些要求,如果電源不夠力的話,會導致模組運作不穩定。
使用UART 做通訊的時候記得要設定連線設定如下: (115200 8N1)
  • Baudrate:115200
  • byte size: 8
  • Parity: None
  • Stop Bit: 1
UART連線下指令的時候換行符號是 "\r\n" ,換行符號要下對指令才會被接受,不然是不會動的。連線之後可以先傳送指令 "AT\r\n" 看看 ESP 模組是否會回傳 "OK" 的訊息。如果成功了就表示模組可正常運作。

使用UART 介面進行韌體更新

ESP8266是可程式化的通用微控制器(具有WiFi功能),具有少數的 GPIO。ESP-01模組上只拉出兩根。而從拍賣網站上買到手的時候,裡面其實已經燒好最新版本的韌體來支援 AT command。網路上也流傳著許多其他版本的韌體來支援其他功能,像是較為知名的支援可在模組上執行 Lua 程式語言的韌體(nodeMCU),其原始碼就放在github上。不過要自行編譯原始碼的話會是一項大工程,所幸的是,有網站提供客製化的 nodeMCU 韌體線上編譯的免費服務,網址為
http://frightanic.com/nodemcu-custom-build/
編譯好的韌體會傳送到網頁上要求輸入的 email 位址,筆者收到了兩個檔案:
nodemcu-master-7-modules-2015-09-18-02-26-58-float.bin
nodemcu-master-7-modules-2015-09-18-02-26-58-integer.bin
燒錄的時候,我是選檔案大小比較小的那個。拿到韌體之後,我們還需要一個燒錄程式,在這邊我是使用 esptool.py 這個指令工具,下載安裝之後。就可以準備開始燒錄韌體了。
進行燒錄時使用下面所示的接線配置,可以使ESP模組進入燒錄模式,
  • CH_PD --> 3.3v
  • VCC --> 3.3V
  • UTXD --> RXD (USB-UART 轉接線)
  • URXD --> TXD (USB-UART 轉接線)
  • GPIO0 --> GND <重點>
  • GND --> GND
接好線之,把 USB-UART 轉接線接上mac os x 系統會自動跳出裝置檔檔名
/dev/tty.usbserial
如果是 Linux 系統的話則是出現
/dev/ttyUSB0
然後打開 command-line ,然後使用esptool.py指令:
esptool.py --port /dev/tty.usbserial write_flash 0x000000 nodemcu-master-7-modules-2015-09-18-02-26-58-integer.bin
如果是 Linux 系統的話則是
esptool.py --port /dev/ttyUSB0 write_flash 0x000000 nodemcu-master-7-modules-2015-09-18-02-26-58-integer.bin
接著就會出現燒錄中的訊息,等到燒錄完成後畫面是如下所示
onionys$ esptool.py --port /dev/tty.usbserial write_flash 0x000000 nodemcu-master-7-modules-2015-09-18-02-26-58-integer.bin
Connecting...
Erasing flash...
Wrote 354304 bytes at 0x00000000 in 34.6 seconds (82.0 kbit/s)...

Leaving...
onionys$
如此一來,就完成燒錄的工作了。重新接線後,會發現AT command 已經不能使用了。之後,我們會再介紹如何在 nodeMCU 的韌體下使用 Lua 語言控制 ESP8266 模組。


2015年9月14日 星期一

救援隨身碟各種遺失的資料,誤刪了怎麼辦呢?要如何把檔案救回來。

  相信各位也跟我們一樣都常常使用到隨身碟, 隨身碟的便利性從128M小型資料的時代,到目前已把32G隨身碟普遍使用了,相信也帶給大家很大的方便性,讓使用者可以隨身攜帶自己的資料,同樣的,有時候也因為方便而導致隨身碟搞丟,或者是不小心的把資料給誤刪了!

  當下一定會很惱羞慘了資料不見了,只好拍拍屁股在重新的做一份了!
在這邊就是要大家不需要再重新做一份了,只需要再把檔案從新救援出來就好,可是到底為什麼可以把一個刪掉的資料救回來呢?

為什麼已刪除的檔案可以救回?


   Windows儲存檔案時,每一個檔案有分為檔案的檔頭,以及真正儲存的資料內容兩個部分。檔頭的作用是用來記錄檔案名稱,以及資料內容儲存在哪些磁碟空間中的資訊。而系統是靠檔頭的資訊,才知道檔案的資料內容要去哪裡找。而當Windows刪除檔案時,事實上是將檔頭的資訊修改,將檔案標記為刪除。這個動作可以視為是將原本這個檔案佔用硬碟的空間釋放出來,讓日後其他要儲存進硬碟的檔案,可以取用。

  因此,如果你刪除檔案之後,沒有放進其他的檔案到硬碟中,那麼這些空間所儲存原先檔案的資料內容是仍然存在的。你只要找到原先記錄檔案的檔頭,把資訊修補回來,就可以把檔案拯救回來,這就是檔案救援的原理。
硬碟裡面的資料並沒有被完全刪除,而只是檔案標頭被刪除,檔案的位子被釋放出來。


  回到本題重點,今天要在這邊分享的是我們所遇到的,當我隨身碟一插上電腦上的USB傳輸孔,突然看到我的隨身碟裡面居然是空白而且無資料,當下我心想慘了,隨身碟有32G裡面的東西全部都消失了,在裡面放了好多重要的資料,而且沒有備份一份在電腦中,要怎麼辦呢?




  馬上看一下是否是我帶錯隨身碟還是電腦抓錯呢?當我把下又插上去的時候我突然發現這個隨身硬碟是我原本的沒錯阿!可是怎會32G記憶體有8M是被佔用的呢?接下來就使用了以下的軟體去做救援了,真的所有的資料都救回來了,在救援之後我也做了一些小實驗,想說那只是把資料釋放而已,那要如何真的完全都刪除呢?不然哪一天把自己的隨身碟借給別人的時候,不小心的就把個人所有的資料都送出去給別人了呢~~~

推薦的免費檔案救援軟體


Recuva







 一、選擇要救援的檔案類型。
  

 二、選擇儲存媒體,如記憶卡、隨身碟等。



三、按下開始,開始搜尋中。

相片救援Recuva-5



硬碟格式化之後檔案可以救回嗎?

  Windows的格式化有兩種,一種是快速格式化,一種是完整格式化。快速格式化的原理其實與前述的檔案刪除原理類似,他只是修改了硬碟用來管理所有檔案的檔案配置表,讓系統檢查的時候,顯示並沒有檔案在硬碟中。但是事實上資料還是保存在原本硬碟的位置上,只是整顆硬碟所有的空間都被釋放出來而已。在這種情況下,資料救回來的機率是很大的。



預設是快速格式化,而並非真正的清除唷!






2015年9月10日 星期四

Raspberry Pi mini UART


mini UART 規格如下:

The mini Uart has the following features:
• 7 or 8 bit operation.
• 1 start and 1 stop bit.
• No parities.
• Break generation.
8 symbols deep FIFOs for receive and transmit.
• SW controlled RTS, SW readable CTS.
• Auto flow control with programmable FIFO level.
• 16550 like registers.
• Baudrate derived from system clock.


mini UART 暫存器控制整理如下: 


AUX BaseAddr 0x7E210000

0x7E21 5000 AUX_IRQ Auxiliary Interrupt status
0x7E21 5004 AUX_ENABLES Auxiliary enables
0x7E21 5040 AUX_MU_IO_REG Mini Uart I/O Data
0x7E21 5044 AUX_MU_IER_REG Mini Uart Interrupt Enable
0x7E21 5048 AUX_MU_IIR_REG Mini Uart Interrupt Identify
0x7E21 504C AUX_MU_LCR_REG Mini Uart Line Control
0x7E21 5050 AUX_MU_MCR_REG Mini Uart Modem Control
0x7E21 5054 AUX_MU_LSR_REG Mini Uart Line Status
0x7E21 5058 AUX_MU_MSR_REG Mini Uart Modem Status
0x7E21 505C AUX_MU_SCRATCH Mini Uart Scratch
0x7E21 5060 AUX_MU_CNTL_REG Mini Uart Extra Control
0x7E21 5064 AUX_MU_STAT_REG Mini Uart Status
0x7E21 5068 AUX_MU_BAUD_REG Mini UART Baud Rate

  • AUXIRQ : the mini UART has an interrupt pending  if bit 0 is set
  • AUXENB : used to enable miniUART function
  • AUX_MU_CNTL_REG : used to enable RX & TX operation on miniUART
  • AUX_MU_IO_REG : used to write data (1 byte) to and read data (1 byte) from the RX/TX FIFO (8 byte buffer).
  • AUX_MU_IER_REG:  used to enable interrupts and to clear RX/TX FIFO
  • AUX_MU_IIR_REG: used to identify/determine TX or RX interrupt and to clear interrupt pending bit
  • AUX_MU_LCR_REG : used to set data bit length (8 bit or 7 bit)  and gives access to the baudrate register (DLAB access=1)
  • AUX_MU_BAUD : set the 16-bit wide baudrate counter.
  • AUX_MU_STAT_REG: Some information about the internal status of the miniUART.
    bit[27:24] Transmit FIFO fill level: TX FIFO有多少資料 (範圍:0~8 byte)
    bit[19:16] Receive FIFO fill level : RX FIFO有多少資料 (範圍:0~8 byte)
Note: 

  1. AUXENB : If the enable bits are clear you will have no access to a peripheral. You can not even read or write the registers! 
  2. GPIO pins should be set up first the before enabling the UART.  So when it is enabled any data at the inputs will immediately be received . If the UART1_RX line is low (because the GPIO pins have not been set-up yet)  that will be seen as a start bit and the UART will start receiving 0x00-characters. 
  3. After a reset: the baudrate will be zero and the system clock will be 250 MHz
  4. Baud Rate:

Figure 2. UART receive frame synchronization and data sampling points.

Figure  shows a common method used by a UART receiver to synchronize itself to a received frame. The receive UART uses a clock that is 16 times the data rate. A new frame is recognized by the falling edge at the beginning of the active-low START bit. This occurs when the signal changes from the active-high STOP bit or bus idle condition. The receive UART resets its counters on this falling edge, expects the mid-START bit to occur after 8 clock cycles, and anticipates the midpoint of each subsequent bit to appear every 16 clock cycles thereafter. The START bit is typically sampled at the middle of bit time to check that the level is still low and ensure that the detected falling edge was a START bit, not a noise spike. Another improvement is to sample the START bit three times (clock counts 7, 8, and 9, out of 16) instead of sampling it only at the midbit position (clock count 8 out of 16).

 arm bootloader

BCM2835設定BaudRate counter兩種方式






void rpi_aux_mu_init() {
    volatile uint32_t reg_val;
    uint32_t t;

    /* Enable aux uart */
    RPI_AUX->ENABLES = RPI_AUX_MU_ENABLE;

    RPI_AUX->MU_IER = 0;

    /* Disable flow control */
    RPI_AUX->MU_CNTL = 0;

    RPI_AUX->MU_LCR = RPI_AUX_MU_LCR_8BIT_MODE;

    RPI_AUX->MU_MCR = 0;

    /* Diable all interrupts from mu and clear the fifos */
    RPI_AUX->MU_IER = 0;
    RPI_AUX->MU_IIR = 0xC6;


    //#define bits 8
  //RPI_AUX->MU_BAUD = ( _RPI_SYS_FREQ / ( 8 * bits )) - 1;
    RPI_AUX->MU_BAUD = 270;

    /* Setup GPIO 14 and 15 as alternative function 5 which is
     UART 1 TXD/RXD. These need to be set before enabling the UART */

    reg_val = memio_read32(_GPIO_FSEL1);
    reg_val &= ~(7 << 12); // GPIO 14
    reg_val |= 2 << 12;      // ALT5
    reg_val &= ~(7 << 15); // GPIO 15
    reg_val |= 2 << 15;      // ALT5

    memio_write32(_GPIO_FSEL1, reg_val);
    memio_write32(_GPIO_PUD, 0);
    dummy(150);
    memio_write32(_GPIO_PUD_CLK0, (1 << 14) | (1 << 15));
    dummy(150);
    memio_write32(_GPIO_PUD_CLK0, 0);

    RPI_AUX->MU_CNTL = RPI_AUX_MU_CNTL_TX_ENABLE |            RPI_AUX_MU_CNTL_RX_ENABLE;
}

其他相關文件:

  1. 關於 Raspberry Pi 的UART
  2. Determining Clock Accuracy Requirements for UART Communications
  3. 程式範例  https://github.com/dwelch67/raspberrypi



2015年9月7日 星期一

Linux中斷控制器的抽象封裝



BCM2835內部的中斷控制器:


BCM2835內部的中斷控制器的架構:  主要包含GPU本身中斷(64個中斷)及21個基本中斷, 其中在 21個基本中斷,除了ARM IRQ外也同時包含了部份GPU的中斷在內 , 例如IRQ 79是GPU的 IRQ 53 (此為I2C中斷觸發),IRQ 80是GPU的 IRQ 54 (此為SPI中斷觸發)。而BCM2835 其全部54根GPIO 都是有產生中斷能力的, 由GPIO controller 產生中斷進入到中斷控制器的IRQ是配置在IRQ 49~52。



GPU  Interrupt Pending Bit (0~63)

ARM Basic Interrupt Pending Bit (0~20)



關於BCM2835 GPIO控制器

 共有54根GPIO分成2個bank , 一個管0~31, 另一個管32~53。由GPIO所產生中斷訊號共有3條, gpio_int[0],gpio_int[1], gpio_int[3], 分別是中斷控制器IRQ49, IRQ50及IRQ52,gpio_intp[0]是表示GPIO 0~31有產生中斷,而gpio_intp[1]是表示GPIO為32~53有中斷產生,而gpio_intp[3]是表示任一根GPIO中斷產生,不論是bank 0或bank1。





GPIO Event Detect Status Register


GPIO若要要產生中斷能力, 則必須設定 GPRENn GPFENn GPHENn, GPLENn, 4個暫存器,
分別用來設定欲中斷觸發類型, Rising Edge Enable, .Falling Edge, Enable,High Level Enable, Low Level Enable。若某根GPIO要Rising 及Falling Edge 都要觸發中斷, 則須別在GPRENn 及GPFENn 對應的bit設1.
每當有中斷產生時, 其對應pending register 的bit都會為1, 用以標示該中斷已產生。每當ISR 跑完後,都必須將其bit 清除, 否則該中斷將無法再繼續產生。而通常清除的方法,就是在對應的bit 上"寫 1" 。在BCM2835的GPIO中斷其等同於pending register的意義是 EDS Register (Event Detect Status ) 用以記錄GPIO 的中斷觸發事件 (不論是Edge 或 Level 都記錄)。


Linux中斷通用層的資料結構

Linux 必須有能力處理不同Interrupt controller 硬體間的差異,如此一來才具備可攜性。Linux底下主要透過三個資料結構完成:

  1. struct irq_chip: 中斷硬體控制器,實作中斷控制晶片的行為,例如 mask IRQ, unmask IRQ
  2. struct irqaction: 指向中斷發生時動作行為, 即描述ISR
  3. struct irq_desc: 中斷描述表 Interrupt Descriptor Table (IRQ Line描述)

Linux中斷通用層的資料結構之關聯


建立GPIO Interrupt Descriptor Table

用一個for loop 產生多張Interrupt Descriptor (struct irq_dest), 每個Descriptor均有不同GPIO IRQ Line編號,但都指向同一個Interrupt controller Chip。

static void bcm2708_gpio_irq_init(struct bcm2708_gpio *ucb)
{
  unsigned irq;
  ucb->gc.to_irq = bcm2708_gpio_to_irq;

 //for loop 針對每一個 IRQ Line 建立 irq_desc 
  for (irq = GPIO_IRQ_START; irq < (GPIO_IRQ_START + GPIO_IRQS); irq++) 
 { 
  irq_set_chip_data(irq, ucb);  //set chip specific data for an irq (170+i)
  irq_set_chip (irq, &bcm2708_irqchip); //set the irq chip for an irq
  set_irq_flags (irq, IRQF_VALID);
  }

  setup_irq (IRQ_GPIO3, &bcm2708_gpio_irq);  // 稍後說明
}



註冊GPIO IRQ 使用 request_irq() API

每一個IRQ Line都有了irq descriptor 之後, 就可以開始註冊每一個GPIO ISR , 可直接透過來完成。

 request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags, const char *name, void *dev)


request_irq的第一個參數irq, 即是向系統的IDT(Interrupt Descriptor Table), (IDT為struct irq_desc的集合) 找到對應的irq_desc, 然後登記ISR,即 irq_desc->action=handler。例如,
request_irq(193, .....); 從IDT尋找179 是屬於GPIO interrupt chip controller, 所以一旦request_irq成功, 則IRQ 193 是掛在名為GPIO irq_chip, 而非ARMCTRL irq_chip。

誰來執行我們的GPIO IRQ Handler

在一個程式代碼中, 最後一行呼叫   setup_irq (IRQ_GPIO3, &bcm2708_gpio_irq); 其目的是向Interrupt controller Chip(ARMCTRL) 註冊IRQ 52 及其ISR bcm2708_gpio_irq 。
bcm2708_gpio_irq為一個 struct irqaction 結構, 其中 .handler指向一個 interrupt handler "bcm2708_gpio_interrupt" ,由此handler 來執行我的IRQ Handler。

bcm2708_gpio_interrupt 函式作法就是掃描 GPIO EDS Register , 若bit 為1, 則執行 generic_handle_irq (gpio_to_irq(gpio));  目的是透過 irq 去搜尋其 irq_desc, 然後再由irq_desc->action 呼叫對應註冊的ISR。struct irqaction為一個串列, 所以會不斷呼叫下去,直到有一個ISR 傳回IRQ_HANDLED。當IRQ為Shard IRQ 時,則發生此情況,若此次非由該裝置產生,則傳回IRQ_NONE, 但一定要有一個ISR傳IRQ_HANDLED,否則將會導致kernel pacnic.



static struct irqaction bcm2708_gpio_irq = {
  .name = "BCM2708 GPIO catchall handler",
  .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,
  .handler = bcm2708_gpio_interrupt,
};

static irqreturn_t bcm2708_gpio_interrupt (int irq, void *dev_id)
{
  unsigned long edsr;
  unsigned bank;
  int i;
  unsigned gpio;
  for (bank = 0; bank <= 1; bank++) { //scan pending register (Event Detect Status)
  edsr = readl(__io_address(GPIO_BASE) + GPIOEDS(bank));
  for_each_set_bit(i, &edsr, 32) {     // i1bit位置處
  gpio = i + bank * 32;
  generic_handle_irq (gpio_to_irq(gpio)); //call GPIO ISR
      // clear pending regisgter
  writel(1<<i,__io_address(GPIO_BASE) + GPIOEDS(bank));
  }
  }
  return IRQ_HANDLED;
}


定義一個GPIO 中斷的irq chip

static struct irq_chip bcm2708_irqchip = {
 .name = "GPIO",
 .irq_enable = bcm2708_gpio_irq_unmask,  
.irq_disable = bcm2708_gpio_irq_mask,  
.irq_unmask = bcm2708_gpio_irq_unmask, 
.irq_mask = bcm2708_gpio_irq_mask,
 .irq_set_type = bcm2708_gpio_irq_set_type
};


說明: 宣告一個 struct irq_chip, 名稱為"GPIO", 並設定其相關底層的控制設定 ,如底下GPIO mask的程式, 即在對應的pin上. 對GPRENn GPFENn GPHENn, GPLENnBCM2835 設0。(ARM Peripherals Manual GPIO p.89)

static void bcm2708_gpio_irq_mask(struct irq_data *d)
{
  unsigned irq = d->irq;
  struct bcm2708_gpio *gpio = irq_get_chip_data(irq);
  unsigned gn = irq_to_gpio(irq);
  unsigned gb = gn / 32;
  unsigned long rising  = readl(gpio->base + GPIOREN(gb));
  unsigned long falling = readl(gpio->base + GPIOFEN(gb));
  unsigned long high    = readl(gpio->base + GPIOHEN(gb));
  unsigned long low     = readl(gpio->base + GPIOLEN(gb));
  gn = gn % 32;
  writel(rising  & ~(1 << gn), gpio->base + GPIOREN(gb));
  writel(falling & ~(1 << gn), gpio->base + GPIOFEN(gb));
  writel(high    & ~(1 << gn), gpio->base + GPIOHEN(gb));
  writel(low     & ~(1 << gn), gpio->base + GPIOLEN(gb));
}


DEMO


實作出來的結果, GPIO 控制器所產生的中斷, 是註冊在中斷控制器(ARMCTRL)的IRQ 號碼52 (gpio_int[3]), IRQ Handler 的名稱為 "BCM2708 GPIO catchall handler", 此IRQ Handler 再來scan GPIO controller 是那一根gpio pin# 產生中斷? 接著再呼叫該gpio pin#所註冊的ISR,此ISR即為
request_irq(193, irq_handler ,"my_button_int",..) 所註冊的ISR, 此中斷193號碼是掛在GPIO controller 的中斷。GPIO pin# IRQ 號碼為 170+GPIO Pin number。 另外, 因為所有GPIO 產生的中斷都是串接在中斷控制器(ARMCTRL)的, 故IRQ 52 的CPU中斷次數為個別GPIO 中斷次數的加總。

cat /proc/interrupts 輸出內容