2015年5月23日 星期六

Raspberry Pi硬體暫存器的設定方式



以BCM2835 C library 解說關於硬體暫存器的設定方式, 手邊請備妥Raspberry Pi  "BCM2835 ARM Peripherals" 以方便查閱。底下以GPIO 方式 解說,, 先說明要寫入或讀取的方法, 不解釋其設定後的物理意義.

 先跳到page 89 第六章 General Purpose I/O (GPIO) 閱讀, 將有助於下列code的理解.

// Physical addresses for various peripheral register sets
/// Base Physical Address of the BCM 2835 peripheral registers
#define BCM2835_PERI_BASE               0x20000000
/// Base Physical Address of the System Timer registers
#define BCM2835_ST_BASE (BCM2835_PERI_BASE + 0x3000)
/// Base Physical Address of the Pads registers
#define BCM2835_GPIO_PADS               (BCM2835_PERI_BASE + 0x100000)
/// Base Physical Address of the Clock/timer registers
#define BCM2835_CLOCK_BASE              (BCM2835_PERI_BASE + 0x101000)
/// Base Physical Address of the GPIO registers
#define BCM2835_GPIO_BASE               (BCM2835_PERI_BASE + 0x200000)
/// Base Physical Address of the SPI0 registers
#define BCM2835_SPI0_BASE               (BCM2835_PERI_BASE + 0x204000)

.....................

/// \brief bcm2835PortFunction
/// Port function select modes for bcm2835_gpio_fsel()
typedef enum
{
    BCM2835_GPIO_FSEL_INPT  = 0b000,   ///< Input
    BCM2835_GPIO_FSEL_OUTP  = 0b001,   ///< Output
    BCM2835_GPIO_FSEL_ALT0  = 0b100,   ///< Alternate function 0
    BCM2835_GPIO_FSEL_ALT1  = 0b101,   ///< Alternate function 1
    BCM2835_GPIO_FSEL_ALT2  = 0b110,   ///< Alternate function 2
    BCM2835_GPIO_FSEL_ALT3  = 0b111,   ///< Alternate function 3
    BCM2835_GPIO_FSEL_ALT4  = 0b011,   ///< Alternate function 4
    BCM2835_GPIO_FSEL_ALT5  = 0b010,   ///< Alternate function 5
    BCM2835_GPIO_FSEL_MASK  = 0b111    ///< Function select bits mask
} bcm2835FunctionSelect;


// Low level convenience functions
//

// Defines for GPIO
// The BCM2835 has 54 GPIO pins.
//      BCM2835 data sheet, Page 90 onwards.
/// GPIO register offsets from BCM2835_GPIO_BASE. Offsets into the GPIO Peripheral block in bytes per 6.1 Register View
#define BCM2835_GPFSEL0                      0x0000 ///< GPIO Function Select 0
#define BCM2835_GPFSEL1                      0x0004 ///< GPIO Function Select 1
#define BCM2835_GPFSEL2                      0x0008 ///< GPIO Function Select 2
#define BCM2835_GPFSEL3                      0x000c ///< GPIO Function Select 3
#define BCM2835_GPFSEL4                      0x0010 ///< GPIO Function Select 4
#define BCM2835_GPFSEL5                      0x0014 ///< GPIO Function Select 5
#define BCM2835_GPSET0                       0x001c ///< GPIO Pin Output Set 0
#define BCM2835_GPSET1                       0x0020 ///< GPIO Pin Output Set 1
#define BCM2835_GPCLR0                       0x0028 ///< GPIO Pin Output Clear 0
#define BCM2835_GPCLR1                       0x002c ///< GPIO Pin Output Clear 1


// Initialise this library.
int bcm2835_init(void)
{
    if (debug)
    {
bcm2835_pads = (uint32_t*)BCM2835_GPIO_PADS;
bcm2835_clk  = (uint32_t*)BCM2835_CLOCK_BASE;
bcm2835_gpio = (uint32_t*)BCM2835_GPIO_BASE;
bcm2835_pwm  = (uint32_t*)BCM2835_GPIO_PWM;
bcm2835_spi0 = (uint32_t*)BCM2835_SPI0_BASE;
bcm2835_bsc0 = (uint32_t*)BCM2835_BSC0_BASE;
bcm2835_bsc1 = (uint32_t*)BCM2835_BSC1_BASE;
bcm2835_st   = (uint32_t*)BCM2835_ST_BASE;
return 1; // Success
    }
    int memfd = -1;
    int ok = 0;
    // Open the master /dev/memory device
    if ((memfd = open("/dev/mem", O_RDWR | O_SYNC) ) < 0)
    {
fprintf(stderr, "bcm2835_init: Unable to open /dev/mem: %s\n",
strerror(errno)) ;
goto exit;
    }

    // GPIO:
   //mapmem內部即是進行mmap 系統呼叫, 將kenel map的硬體暫存器位址map到user-space裡,以方便進行 user space driver 開發
    bcm2835_gpio = (volatile uint32_t *)mapmem("gpio", BCM2835_BLOCK_SIZE, memfd, BCM2835_GPIO_BASE);
    if (bcm2835_gpio == MAP_FAILED) goto exit;


   ..............................
}


// Function select
// pin is a BCM2835 GPIO pin number NOT RPi pin number
//      There are 6 control registers, each control the functions of a block
//      of 10 pins.
//      Each control register has 10 sets of 3 bits per GPIO pin:
//
//      000 = GPIO Pin X is an input
//      001 = GPIO Pin X is an output
//      100 = GPIO Pin X takes alternate function 0
//      101 = GPIO Pin X takes alternate function 1
//      110 = GPIO Pin X takes alternate function 2
//      111 = GPIO Pin X takes alternate function 3
//      011 = GPIO Pin X takes alternate function 4
//      010 = GPIO Pin X takes alternate function 5
//
// So the 3 bits for port X are:
//      X / 10 + ((X % 10) * 3)



設定GPIO 功能
void bcm2835_gpio_fsel(uint8_t pin, uint8_t mode)
{
   
  //除以4 , 是因為bcm2835_gpio 為一個 int pointer, 所以加1 隠函移動4個byte了, 故除以4才能得    到正確位址
  //除以10 是為了計算GPIO pin 號碼在那一個 Function selects 的暫存器裡
      每一個暫存器(32 bit word) 管 GPIO 10 根 ,(3 bits per pin)

    volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPFSEL0/4 + (pin/10);

//  找到pin 的暫存器, 還要再找到這個pin 對應的bit, 故要再 shift  ((X % 10) * 3) bits 
    uint8_t   shift = (pin % 10) * 3;
    uint32_t  mask = BCM2835_GPIO_FSEL_MASK << shift;
    uint32_t  value = mode << shift;
    bcm2835_peri_set_bits(paddr, value, mask);
}

-----------------
// Set/clear only the bits in value covered by the mask
void bcm2835_peri_set_bits(volatile uint32_t* paddr, uint32_t value, uint32_t mask)
{


  // 先讀出原來的值 v, 再去改變要更新的欄位', 而其他欄位仍須保留原來的值
    uint32_t v = *paddr;

//   要改的欄位先清成0  ==> v & ~mask, 再去設定其value ==> OR (value & mask)
    v = (v & ~mask) | (value & mask);

   *paddr=v;
}


Note: volatile uint32_t* paddr  ,volatile 這個修飾字使compiler 針對paddr所指的硬體位址的存取, 不要進行任何最佳化的處理,以免產生其他site effect.








沒有留言 :

張貼留言