GD MCU 例子分析

以一个I2c EEPROM为例子

 

int main(void)
{   
    /* configure systick */
    systick_config();
    
    /* configure LEDs */
    led_config();
    
    /* configure USART */
    gd_eval_com_init(EVAL_COM1);

    printf("I2C-24C02 configured....\n\r");
    
    /* configure GPIO */
    gpio_config();
    
    /* configure I2C */
    i2c_config();
    
    /* initialize EEPROM */
    i2c_eeprom_init();

    printf("\r\nThe I2C0 is hardware interface ");
    printf("\r\nThe speed is %d", I2C0_SPEED);
    
    if(I2C_OK == i2c_24c02_test()){
        while(1){
           /* turn off all LEDs */
           gd_eval_led_off(LED1);
           gd_eval_led_off(LED2);
           gd_eval_led_off(LED3);
           gd_eval_led_off(LED4);
           /* turn on a LED */
           led_turn_on(count%4);
           count++;
           if(count >= 4){
               count = 0;
           }
           delay_1ms(500);
        }
    }
    /* turn on all LEDs */
    gd_eval_led_on(LED1);
    gd_eval_led_on(LED2);
    gd_eval_led_on(LED3);
    gd_eval_led_on(LED4);
    
    while(1);

}

 

 

gpio_config();

 

void gpio_config(void)
{
    /* enable GPIOB clock */
    rcu_periph_clock_enable(RCU_GPIOB);
    /* enable I2C0 clock */
    rcu_periph_clock_enable(RCU_I2C0);

    /* connect PB6 to I2C0_SCL */
    gpio_af_set(GPIOB, GPIO_AF_1, GPIO_PIN_6);
    /* connect PB7 to I2C0_SDA */
    gpio_af_set(GPIOB, GPIO_AF_1, GPIO_PIN_7);
    /* configure GPIO pins of I2C0 */
    gpio_mode_set(GPIOB, GPIO_MODE_AF, GPIO_PUPD_PULLUP,GPIO_PIN_6);
    gpio_output_options_set(GPIOB, GPIO_OTYPE_OD, GPIO_OSPEED_50MHZ,GPIO_PIN_6);
    gpio_mode_set(GPIOB, GPIO_MODE_AF, GPIO_PUPD_PULLUP,GPIO_PIN_7);
    gpio_output_options_set(GPIOB, GPIO_OTYPE_OD, GPIO_OSPEED_50MHZ,GPIO_PIN_7);
}
void rcu_periph_clock_enable(rcu_periph_enum periph)
{
    RCU_REG_VAL(periph) |= BIT(RCU_BIT_POS(periph));
}

涉及到三个部分,1是 RCU_REG_VAL(periph),2是 BIT(RCU_BIT_POS(periph)),3是 参数 periph

 

下面第一部分RCU_REG_VAL

#define RCU_REG_VAL(periph) (REG32(RCU + ((uint32_t)(periph)>>6)))

 

/* bit operations */ #define REG32(addr) (*(volatile uint32_t *)(uint32_t)(addr))

 

/* RCU definitions */ #define RCU RCU_BASE

 

#define RCU_BASE (AHB1_BUS_BASE + 0x00001000U) /*!< RCU base address */

 

#define AHB1_BUS_BASE ((uint32_t)0x40020000U) /*!< ahb1 base address */

GD MCU 例子分析_第1张图片

从Memory map中可以看到,AHB1_BUS_BASE的内存地址定义为了0x4002 0000,也合SEPC中介绍的一致;

RUC的内存地址是RCU_BASE 也和SPEC上写的0x4002 1000对应

这里说一下,GD厂商给的SPEC中memory map是用内存地址来映射到硬件地址,

ARM是统一编址的,也就是外设和内存进行统一的编址,共同形成了4G物理地址空间(32位为例子)。

大家知道操作外设时,实际上操作的是读写设备相关的寄存器,这些与外设相关的寄存器与不同操作模式下R0-R15那些寄存器是不同的,这些寄存器并不是所谓的物理上的寄存器,实际上是所谓的IO端口,通常会有控制、状态、数据的分类。他们被连续地编址,对于其编址的方式有两种一种是IO映射、一种是内存映射。IO映射是对x86为例的复杂指令集来说的,需要专门的IO控制指令,不详谈。

 

内存映射是对于统一编址的精简指令集计算机ARM等来说的。具体的方法就是将IO端口映射成和内存一样的物理地址,然后与内存一起进行统一编址,或者说成为了内存的一部分,当然还可以理解内存映射的意思是可以用访问内存的方式进行IO地址的访问,内存和IO地址一起编码为cpu识别的地址哦。然后内存+IO端口地址=4GB的寻址空间。

 

那么接下来还有个问题就是这种编址的硬件实现?这就要详见arm的AMBA(Advanced Microcontroller BusArchitecture)了,这是目前芯片总线的主流标准。共定义了3组总线:高性能总线(Advanced High Performance Bus,AHB)、系统总线(Advanced System Bus,ASB)和外设总线(Advanced Peripheral Bus,APB)。不同的总线上挂接着不同的外设和存储器,大部分由三态门控制。比如,当AHB总线上的主设备读写从设备时,发出的地址经过AHB总线的译码器产生该地址所对应从设备的选择信号,选中从设备;这样就可以对从设备进行读写啦。或者可以这样想AHB总线上的译码器根据地址产生相应的片选信号,选中对应的设备。对于硬件的实现我们这是简单地理解以便更好地体会上述编址。涉及到具体的硬件读写操作,地址的硬件实现会提到很多的译码器、三态门、总线、还有时序等等具体情况具体分析,在此不做详述。

 

<>

第二部分:BIT(RCU_BIT_POS(periph))

#define BIT(x) ((uint32_t)((uint32_t)0x01U<<(x)))

很简单,不说

#define RCU_BIT_POS(val) ((uint32_t)(val) & 0x1FU)

这里把val先与上0x1Fu,表示给对应寄存器设置的最大值就0x1F,你 超过这个值肯定是不对的,所以把它做个限制

 

第三部分 3是 参数 periph 也就是 RCU_GPIOB

/* peripheral clock enable */
typedef enum
{
    /* AHB peripherals */
    RCU_DMA     = RCU_REGIDX_BIT(IDX_AHBEN, 0U),                  /*!< DMA clock */
    RCU_CRC     = RCU_REGIDX_BIT(IDX_AHBEN, 6U),                  /*!< CRC clock */
    RCU_GPIOA   = RCU_REGIDX_BIT(IDX_AHBEN, 17U),                 /*!< GPIOA clock */
    RCU_GPIOB   = RCU_REGIDX_BIT(IDX_AHBEN, 18U),                 /*!< GPIOB clock */
    RCU_GPIOC   = RCU_REGIDX_BIT(IDX_AHBEN, 19U),                 /*!< GPIOC clock */
    RCU_GPIOD   = RCU_REGIDX_BIT(IDX_AHBEN, 20U),                 /*!< GPIOD clock */
    RCU_GPIOF   = RCU_REGIDX_BIT(IDX_AHBEN, 22U),                 /*!< GPIOF clock */
    RCU_TSI     = RCU_REGIDX_BIT(IDX_AHBEN, 24U),                 /*!< TSI clock */

 

看这两个宏RCU_REGIDX_BIT 和 IDX_AHBEN

/* constants definitions */
/* define the peripheral clock enable bit position and its register index offset */
#define RCU_REGIDX_BIT(regidx, bitpos)      (((uint32_t)(regidx)<<6) | (uint32_t)(bitpos))
/* register index */
typedef enum
{
    /* peripherals enable */
    IDX_AHBEN      = 0x14U, 
    IDX_APB2EN     = 0x18U, 
    IDX_APB1EN     = 0x1CU, 
    IDX_ADDAPB1EN  = 0xF8U,
    /* peripherals reset */
    IDX_AHBRST     = 0x28U, 
    IDX_APB2RST    = 0x0CU, 
    IDX_APB1RST    = 0x10U, 

 

RCU_REGIDX_BIT(IDX_AHBEN, 18U) => (((uint32_t)(0x14)<<6) | (uint32_t)(0x18))

 

很明显就是根据参数来通过一些操作处理成需要的值。

具体怎么处理刚才找了下没有找到对应的具体的寄存器表,就不说了。

 

回到

RCU_REG_VAL(periph) |= BIT(RCU_BIT_POS(periph));

 

这里通过periph参数,1是找到需要设置的具体的内存地址,另外就是通过这参数来确定需要给对应地址设置什么值。一举两得

回到这个函数的作用上来,这个函数是 /* enable GPIOB clock */”

有人问,为什么要enable gpio clock?

因为在当前处理器上对每类外设(Cortex-M3之外的部分)都有clock的设置,你用GPIO,我只用打开GPIO的clock就可以了,其他的模块的clock我不用开,可以省功耗。

这里用到GPIOB,就只开GPIOB的clock,用到I2C 就用I2C的clock就可以了。

 

有人又要问,你说enable I2C的Clock我理解,因为I2C有clk线,但是你说GPIO 的clk也要enable ,我就不理解了。

 

以下是在网上找的的一些答案,但是我觉的说的还不是特别具体。

所有寄存器都需要时钟才能配置吧,寄存器是由D触发器组成的,只有送来了时钟,触发器才能被改写值。

任何MCU的任何外设都需要有时钟,8051也是如此(只不过8051使用统一的时钟);GD为了让用户更好地掌握功耗,对每个外设的时钟都设置了开关,让用户可以精确地控制,关闭不需要的设备,达到节省供电的目的

你可能感兴趣的:(MCU)