Tuesday, September 09, 2008

操作 memory mapping register I/O

在寫 driver 時,最基本的動作通常是練習控制簡單的 I/O 動作讓某個裝置起作用。
在此便解釋如何控制,以及一些簡單的範例,取自:

  • 現代嵌入式系統開發專案實務:菜鳥成長日誌與專案經理的私房菜
  • linux-ldp-v1.2

操作 memory mapping register I/O

  • Memory Mapping Register: 寫驅動程式時,要控制接在 CPU 外部的晶片勢必是透過 CPU 的 PIN 腳,可能只是單純的將 GPIO pin 設為 high/low 來控制外部的 chip,同理外部的設備也可以透過 GPIO pin 將訊號傳給 CPU;或者驅動程式可以透過某種通訊協定來控制 I/C,例如 SPI 或 I2C等。但無論是何種 protocol,CPU 和 外部 chip 之間的資料與命令傳遞還是得透過 CPU 的 pin 腳。
  • 程式寫法
    *(volatile unsigned char *)(0x302CF)=0x00;
    • register 其實是一個位址,使用指標就可以對其設值或取值,要注意的是一定要用 volatile 關鍵字,否則 compiler 的最佳化功能很可能會把某些程式當作是沒意義的。

範例

  • 以下為一 LED driver example
    /* drv_LED.c
    - 假設 CPU 用 P1 這樣 pin 與 LED 相接
    - 當 P1 為 high/low 時, LED 會 on/off
    */

    void drv_init_LED(void)
    {
    //設定 P1 為 output pin
    *(volatile unsigned char*)0x200023 |= 0x02; //0000 0010

    //default: P1 is low, LED off
    *(volatile unsigned char*)0x200022 &= ~0x02; //1111 1101
    }

    void drv_LED_off()
    {
    //set P1 is low, LED off
    *(volatile unsigned char*)0x200022 &= ~0x02; //1111 1101
    }

    void drv_LED_on()
    {
    //set P1 is low, LED off
    *(volatile unsigned char*)0x200022 |= 0x02; //0000 0010
    }
  • 解釋 *(volatile unsigned char*)0x200022 |= 0x02;
    void drv_LED_on(void)
    {
    volatile unsigned char data; // value of register context
    volatile unsigned char* reg; // the point to register

    reg = (volatile unsigned char*) 0x200022;
    data = *reg; // get the value of the register in 0x200022
    data = data | 0x2; // set 1 to 2th bit
    *reg = data; // set the new data to register
    }
  • linux-ldp-v1.2 例子
    • omap_writel((omap_readl(OMAP2_CONTROL_PBIAS_1) | 0x6) & ~0x1, OMAP2_CONTROL_PBIAS_1);
    • /*
      * Functions to access the OMAP IO region
      *
      * NOTE: - Use omap_read/write[bwl] for physical register addresses
      * - Use __raw_read/write[bwl]() for virtual register addresses
      * - Use IO_ADDRESS(phys_addr) to convert registers to virtual addresses
      * - DO NOT use hardcoded virtual addresses to allow changing the
      * IO address space again if needed
      */
      #define omap_readb(a) (*(volatile unsigned char *)IO_ADDRESS(a))
      #define omap_readw(a) (*(volatile unsigned short *)IO_ADDRESS(a))
      #define omap_readl(a) (*(volatile unsigned int *)IO_ADDRESS(a))

      #define omap_writeb(v,a) (*(volatile unsigned char *)IO_ADDRESS(a) = (v))
      #define omap_writew(v,a) (*(volatile unsigned short *)IO_ADDRESS(a) = (v))
      #define omap_writel(v,a) (*(volatile unsigned int *)IO_ADDRESS(a) = (v))
    • #define IO_OFFSET 0x90000000
      #define IO_ADDRESS(pa) ((pa) + IO_OFFSET) /* Works for L3 and L4 */

No comments: