天天看點

嵌入式位址雜談

最近對嵌入式中的位址有了更深入的認識,簡單記錄一下。

以STM32F103為例,在寄存器模闆中我們都知道隻要對寄存器簡單地進行指派就可以完成流水燈等操作,那麼這些寄存器是怎麼封裝的呢?

首先,先通過一串的宏定義确定了硬體的基位址,以STM32F103中的GPIOB為例:

在stm32f10x.h這個頭檔案中首先定義:

#define GPIOB_BASE            (APB2PERIPH_BASE + 0x0C00)
#define APB2PERIPH_BASE       (PERIPH_BASE + 0x10000)
#define PERIPH_BASE           ((uint32_t)0x40000000) 
           

這樣,GPIOB_BASE的基位址就是0x40010C00,這和手冊中記錄的位址是一緻的:

嵌入式位址雜談

接着,是寄存器的定義。其實,向寄存器中寫值從本質上說就是往那個位址上指派,隻不過為了友善了解,程式員們把每個不同的位址給抽象成了不同的寄存器,還是以GPIOB為例,手冊中有:

嵌入式位址雜談

于是偏移量為000h開始的32位位址,這一塊被定義為GPIOx_CRL,偏移量為004h開始的32位位址,這一塊被定義位GPIOx_CRH,以此類推。

那麼有了手冊,我們在Keil中是怎麼操作這些寄存器的呢?它們又是怎麼定義的呢?回到軟體,在stm32f10x_h中,有這樣的宏定義:

GPIOB_BASE是位址,那麼它前面的那個一串是什麼呢?繼續找:

typedef struct
{
  __IO uint32_t CRL;
  __IO uint32_t CRH;
  __IO uint32_t IDR;
  __IO uint32_t ODR;
  __IO uint32_t BSRR;
  __IO uint32_t BRR;
  __IO uint32_t LCKR;
} GPIO_TypeDef;
           

原來這是一個結構體,這個結構體中的每個寄存器都是按照手冊中的順序排列的,每一個寄存器都是32位的,這樣你就可以開始你熟悉的操作了,比如:

GPIOB->CRL&=0XFF0FFFFF; 
	GPIOB->CRL|=0X00300000;  	 
    GPIOB->ODR|=1<<5;      
           

那麼可能又有人想問了,手冊中的寄存器的順序是定死的嗎?當然不是了,這是根據做晶片的時候設計人員的喜好決定的,比如拿我在FPGA上移植的一個Cortex-M3核心的工程為例,我在給核心添加UART接口時,我這麼定義了一串寄存器:

if (PADDR[11:5] == 7'h00) begin
     case (PADDR[4:2])
     3'h0: read_mux_byte0 =  reg_rx_buf;
     3'h1: read_mux_byte0 =  {{4{1'b0}},uart_status};
     3'h2: read_mux_byte0 =  {{1{1'b0}},reg_ctrl};
     3'h3: read_mux_byte0 =  {{4{1'b0}},intr_state};
     3'h4: read_mux_byte0 =  reg_baud_div[7:0];
     endcase
   end
           

可以看出,不同的位址我給命名了不同的寄存器。

那麼我在Keil中,就将這麼定義這些寄存器:

typedef struct
{
  __IO   uint32_t  DATA;          /*!< Offset: 0x000 Data Register    (R/W) */
  __IO   uint32_t  STATE;         /*!< Offset: 0x004 Status Register  (R/W) */
  __IO   uint32_t  CTRL;          /*!< Offset: 0x008 Control Register (R/W) */
  union {
    __I    uint32_t  INTSTATUS;   /*!< Offset: 0x00C Interrupt Status Register (R/ ) */
    __O    uint32_t  INTCLEAR;    /*!< Offset: 0x00C Interrupt Clear Register ( /W) */
	};
  __IO   uint32_t  BAUDDIV;       /*!< Offset: 0x010 Baudrate Divider Register (R/W) */

}  UART_TypeDef;
           

除這以外,當初還有一個概念始終困擾着我,就是在I2C通信中的位址。

在I2C通信的寫控制中,首先寫的是I2C器件的位址,這個位址是I2C器件挂在I2C總線上的位址,然後發送的寄存器位址,是寄存器在I2C器件的位置。