极海半导体APM32F407 GPIO的配置
极海半导体-APM32F407 GPIO的配置

# 通用功能
# 浮空输入
> 该模式常应用于按键检测。
#define KEY1_PIN                  GPIO_PIN_8                 
#define KEY1_GPIO_PORT            GPIOA                     
#define KEY1_GPIO_CLK             RCM_AHB1_PERIPH_GPIOA

void GPIO_Config(void)
{
    /*定义一个GPIO_Config_T类型的结构体*/
    GPIO_Config_T configStruct;

    /*开启相关的GPIO外设时钟*/
    RCM_EnableAHB1PeriphClock (KEY1_GPIO_CLK);

    /*选择要控制的GPIO引脚*/
    configStruct.pin = KEY1_PIN;
    /*设置引脚模式为输出模式*/
    configStruct.mode = GPIO_MODE_IN;
    /*设置引脚为不上拉也不下拉模式*/
    configStruct.pupd = GPIO_PUPD_NOPULL;

    /*初始化GPIO*/
    GPIO_Config(KEY1_GPIO_PORT, &configStruct);
}

# 上拉/下拉输入
> 用于给定外部输入一个初始电平状态。

#define KEY1_INT_GPIO_PORT                GPIOA
#define KEY1_INT_GPIO_CLK                 RCM_AHB1_PERIPH_GPIOA
#define KEY1_INT_GPIO_PIN                 GPIO_PIN_0
#define KEY1_INT_EXTI_PORTSOURCE          SYSCFG_PORT_GPIOA
#define KEY1_INT_EXTI_PINSOURCE           SYSCFG_PIN_0
#define KEY1_INT_EXTI_LINE                EINT_LINE_0
#define KEY1_INT_EXTI_IRQn                EINT0_IRQn

void GPIO_Config(void)
{
    GPIO_Config_T configStruct;
    EINT_Config_T EINT_configStruct;

    /*开启按键GPIO口的时钟*/
    RCM_EnableAHB1PeriphClock(KEY1_INT_GPIO_CLK);

    /*使能 SYSCFG 时钟 ,使用GPIO外部中断时必须使能SYSCFG时钟*/
    RCM_EnableAPB2PeriphClock(RCM_APB2_PERIPH_SYSCFG);

    /*选择按键1的引脚 */
    configStruct.pin = KEY1_INT_GPIO_PIN;
    /*设置引脚为输入模式 */
    configStruct.mode = GPIO_MODE_IN;
    /*设置引脚下拉,用于给引脚一个默认电平状态 */
    configStruct.pupd = GPIO_PUPD_DOWN;
    /*使用上面的结构体初始化按键 */
    GPIO_Config(KEY1_INT_GPIO_PORT, &configStruct);

    /*连接 EXTI 中断源 到key1引脚 */
    SYSCFG_ConfigEINTLine(KEY1_INT_EXTI_PORTSOURCE,KEY1_INT_EXTI_PINSOURCE);

    /*选择 EXTI 中断源 */
    EINT_configStruct.line = KEY1_INT_EXTI_LINE;
    /*中断模式 */
    EINT_configStruct.mode = EXTI_Mode_Interrupt;
    /*下降沿触发 */
    EINT_configStruct.trigger = EXTI_Trigger_Rising;
    /*使能中断/事件线 */
    EINT_configStruct.lineCmd = ENABLE;
    EXTI_EINT_ConfigInit(&EINT_configStruct);

    NVIC_EnableIRQRequest(KEY1_INT_EXTI_IRQn, 0x0f, 0x0f);
}


# 模拟功能
  • 输入
> 用于ADC外设输入配置。

#define ADC_GPIO_PORT    GPIOB
#define ADC_GPIO_PIN     GPIO_PIN_0
#define ADC_GPIO_CLK     RCM_AHB1_PERIPH_GPIOB

void GPIO_Config(void)
{
    GPIO_Config_T configStruct;

    /*使能 GPIO 时钟*/
    RCM_EnableAHB1PeriphClock(ADC_GPIO_CLK);

    /*配置ADC IO*/
    configStruct.pin = ADC_GPIO_PIN;
    /*配置为模拟模式*/
    configStruct.mode = GPIO_MODE_AN;
    configStruct.pupd = GPIO_PUPD_NOPULL ;
    GPIO_Config(ADC_GPIO_PORT, &configStruct);
}

 
  • 输出
> 用于DAC外设输出配置。

#define DAC_CH1_GPIO_CLK            RCM_AHB1_PERIPH_GPIOA
#define DAC_CH1_GPIO_PORT           GPIOA
#define DAC_CH1_GPIO_PIN            GPIO_PIN_4

void GPIO_Config(void)
{
    GPIO_Config_T configStruct;
    DAC_Config_T  dacConfig;

    /*使能GPIOA时钟 */
    RCM_EnableAHB1PeriphClock(DAC_CH1_GPIO_CLK, ENABLE);

    /*使能DAC时钟 */
    RCM_EnableAPB1PeriphClock(RCM_APB1_PERIPH_DAC);

    /*DAC的GPIO配置,模拟输入 */
    configStruct.pin =  DAC_CH1_GPIO_PIN;
    configStruct.mode = GPIO_MODE_AN;
    configStruct.pupd = GPIO_PUPD_NOPULL;
    configStruct.speed = GPIO_SPEED_100MHz;
    configStruct.otype = GPIO_OTYPE_PP;
    GPIO_Config(DAC_CH1_GPIO_PORT, &configStruct);
}

# (复用)开漏输出
> 常用于驱动外部数字芯片,如I2C接口的芯片。

#define I2C_SCL_PIN                  GPIO_PIN_8
#define I2C_SCL_GPIO_PORT            GPIOB
#define I2C_SCL_GPIO_CLK             RCM_AHB1_PERIPH_GPIOB
#define I2C_SCL_SOURCE               GPIO_PIN_SOURCE_8
#define I2C_SCL_AF                   GPIO_AF_I2C1

#define I2C_SDA_PIN                  GPIO_PIN_9
#define I2C_SDA_GPIO_PORT            GPIOB
#define I2C_SDA_GPIO_CLK             RCM_AHB1_PERIPH_GPIOB
#define I2C_SDA_SOURCE               GPIO_PIN_SOURCE_9
#define I2C_SDA_AF                   GPIO_AF_I2C1

void GPIO_Config(void)
{
  GPIO_Config_T  configStruct;

  /*I2C Periph clock enable */
  RCM_EnableAPB1PeriphClock(RCM_APB1_PERIPH_I2C1);

  /*I2C_SCL_GPIO_CLK and I2C_SDA_GPIO_CLK Periph clock enable */
  RCM_EnableAHB1PeriphClock(I2C_SCL_GPIO_CLK | I2C_SDA_GPIO_CLK);

  /*GPIO configuration */
  /*Connect PXx to I2C_SCL*/
  GPIO_ConfigPinAF(I2C_SCL_GPIO_PORT, I2C_SCL_SOURCE, I2C_SCL_AF);
  /*Connect PXx to I2C_SDA*/
  GPIO_ConfigPinAF(I2C_SDA_GPIO_PORT, I2C_SDA_SOURCE, I2C_SDA_AF);

  /*Configure I2C pins: SCL */
  configStruct.pin = I2C_SCL_PIN;
  configStruct.mode = GPIO_Mode_AF;
  configStruct.speed = GPIO_SPEED_50MHz;
  configStruct.otype = GPIO_OTYPE_OD;
  configStruct.pupd  = GPIO_PUPD_NOPULL;
  GPIO_Config(I2C_SCL_GPIO_PORT, &configStruct);

  /*I2C pins: SDA */
  configStruct.pin = I2C_SDA_PIN;
  GPIO_Config(I2C_SDA_GPIO_PORT, &configStruct);
}

# (复用)推挽输出
> MCU输出电流有限,该模式常用于驱动小功率负载,如LED。

#define LED1_PIN                  GPIO_PIN_6                 
#define LED1_GPIO_PORT            GPIOA                     
#define LED1_GPIO_CLK             RCM_AHB1_PERIPH_GPIOA

void GPIO_Config(void)
{
    /*定义一个GPIO_Config_T类型的结构体*/
    GPIO_Config_T configStruct;

    /*开启相关的GPIO外设时钟*/
    RCM_EnableAHB1PeriphClock (LED1_GPIO_CLK);

    /*选择要控制的GPIO引脚*/
    configStruct.pin = LED1_PIN;
    /*设置引脚模式为输出模式*/
    configStruct.mode = GPIO_Mode_OUT;
    /*设置引脚的输出类型为推挽输出*/
    configStruct.otype = GPIO_OTYPE_PP;
    /*设置引脚为上拉模式*/
    configStruct.pupd = GPIO_PuPd_UP;
    /*设置引脚速率为100MHz */
    configStruct.speed = GPIO_SPEED_100MHz;

    /*初始化GPIO*/
    GPIO_Config(LED1_GPIO_PORT, &configStruct);
}

# 重映射功能
> 该功能多见于早期F1xx系列中,用于映射外设功能到不同IO。

#define TIM3_CH2_GPIO_CLK           RCM_APB2_PERIPH_GPIOB
#define TIM3_CH2_GPIO_PORT          GPIOB
#define TIM3_CH2_GPIO_PIN           GPIO_PIN_5

void GPIO_Config(void)
{
    GPIO_Config_T configStruct;

    /*使能定时器3时钟*/
    RCM_EnableAPB1PeriphClock(RCM_APB1_PERIPH_TMR3);
    /*使能GPIO外设*/
    RCM_EnableAPB2PeriphClock(TIM3_CH2_GPIO_CLK);
    /*Timer3部分重映射  TIM3_CH2->PB5*/
    GPIO_ConfigPinRemap(GPIO_PARTIAL_REMAP_TMR3);

    /*设置该引脚为复用输出功能,输出TIM3 CH2的PWM脉冲波形*/
    configStruct.pin = TIM3_CH2_GPIO_PIN;
    /*复用推挽输出*/
    configStruct.mode = GPIO_MODE_AF_PP;
    configStruct.speed = GPIO_SPEED_50MHz;
    GPIO_Config(TIM3_CH2_GPIO_PORT, &configStruct);
}

# 锁定功能
> 常用于锁定不能被轻易更改工作模式的IO控制,如电机控制IO。

#define MOTOR_A_PIN                  GPIO_PIN_10                 
#define MOTOR_A_GPIO_PORT            GPIOB                     
#define MOTOR_A_GPIO_CLK             RCM_AHB1_PERIPH_GPIOB

void GPIO_Config(void)
{
    /*定义一个GPIO_Config_T类型的结构体*/
    GPIO_Config_T configStruct;

    /*开启相关的GPIO外设时钟*/
    RCM_EnableAHB1PeriphClock (MOTOR_A_GPIO_CLK);

    /*选择要控制的GPIO引脚*/
    configStruct.pin = MOTOR_A_PIN;
    /*设置引脚模式为输出模式*/
    configStruct.mode = GPIO_Mode_OUT;
    /*设置引脚的输出类型为推挽输出*/
    configStruct.otype = GPIO_OTYPE_PP;
    /*设置引脚为上拉模式*/
    configStruct.pupd = GPIO_PUPD_UP;
    /*设置引脚速率为100MHz */
    configStruct.speed = GPIO_SPEED_100MHz;

    /*初始化GPIO*/
    GPIO_Config(MOTOR_A_GPIO_PORT, &configStruct);
    /*锁定IO工作模式*/
    GPIO_ConfigPinLock(MOTOR_A_GPIO_PORT,MOTOR_A_PIN)
}

# 位带操作
位带操作就是可以单独的对一个比特位读和写。在F407 中,有两个地方实现了位带,一个是SRAM 区的最低1MB 空间,另一个是外设区最低1MB 空间。这两个1MB 的空间除了可以像正常的RAM 一样操作外,他们还有自己的位带别名区,位带别名区把这1MB 的空间的每一个位扩展成一个32 位的字,当访问位带别名区的这些字时,就可以达到访问位带区某个比特位的目的。

> 1.这部分位带区大家可以看之前的《APM32存储器和寄存器》中讲到的Cortex-M4存储器映射。
> 2.位带区的一个比特位经过扩展之后,虽然变大到4 个字节,但是还是LSB才有效。
> 3.Cortex-M4系统总线是32bit的,所以这里把别名区扩展成32bit,使访问更高效。


/**
* @ brief  这里只定义了 GPIO ODR和IDR这两个寄存器的位带别名区地址,其他寄存器的没有定义
*          SRAM 位带区:    0X2000 0000~0X200F 0000
*          SRAM 位带别名区:0X2200 0000~0X23FF FFFF
*          外设 位带区:    0X4000 0000~0X400F FFFF
*          外设 位带别名区:0X4200 0000~0X43FF FFFF
*/
/*把“位带地址+位序号”转换成别名地址的宏*/
#define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x02000000+((addr & 0x000FFFFF)<<5)+(bitnum<<2))

/*把一个地址转换成一个指针*/
#define MEM_ADDR(addr)  *((volatile unsigned long  *)(addr))

/*把位带别名区地址转换成指针*/
#define BIT_ADDR(addr, bitnum)   MEM_ADDR(BITBAND(addr, bitnum))

/*GPIO ODR 和 IDR 寄存器地址映射*/
#define GPIOA_ODR_Addr    (GPIOA_BASE+20)
#define GPIOB_ODR_Addr    (GPIOB_BASE+20)
#define GPIOC_ODR_Addr    (GPIOC_BASE+20)
#define GPIOD_ODR_Addr    (GPIOD_BASE+20)
#define GPIOE_ODR_Addr    (GPIOE_BASE+20)
#define GPIOF_ODR_Addr    (GPIOF_BASE+20)
#define GPIOG_ODR_Addr    (GPIOG_BASE+20)
#define GPIOH_ODR_Addr    (GPIOH_BASE+20)
#define GPIOI_ODR_Addr    (GPIOI_BASE+20)
#define GPIOJ_ODR_Addr    (GPIOJ_BASE+20)
#define GPIOK_ODR_Addr    (GPIOK_BASE+20)

#define GPIOA_IDR_Addr    (GPIOA_BASE+16)
#define GPIOB_IDR_Addr    (GPIOB_BASE+16)
#define GPIOC_IDR_Addr    (GPIOC_BASE+16)
#define GPIOD_IDR_Addr    (GPIOD_BASE+16)
#define GPIOE_IDR_Addr    (GPIOE_BASE+16)
#define GPIOF_IDR_Addr    (GPIOF_BASE+16)
#define GPIOG_IDR_Addr    (GPIOG_BASE+16)
#define GPIOH_IDR_Addr    (GPIOH_BASE+16)
#define GPIOI_IDR_Addr    (GPIOI_BASE+16)
#define GPIOJ_IDR_Addr    (GPIOJ_BASE+16)
#define GPIOK_IDR_Addr    (GPIOK_BASE+16)


/*单独操作 GPIO的某一个IO口,n(0,1,2...16),n表示具体是哪一个IO口*/
#define PAout(n)   BIT_ADDR(GPIOA_ODR_Addr,n)
#define PAin(n)    BIT_ADDR(GPIOA_IDR_Addr,n)

#define PBout(n)   BIT_ADDR(GPIOB_ODR_Addr,n)
#define PBin(n)    BIT_ADDR(GPIOB_IDR_Addr,n)

#define PCout(n)   BIT_ADDR(GPIOC_ODR_Addr,n)
#define PCin(n)    BIT_ADDR(GPIOC_IDR_Addr,n)

#define PDout(n)   BIT_ADDR(GPIOD_ODR_Addr,n)
#define PDin(n)    BIT_ADDR(GPIOD_IDR_Addr,n)

#define PEout(n)   BIT_ADDR(GPIOE_ODR_Addr,n)
#define PEin(n)    BIT_ADDR(GPIOE_IDR_Addr,n)

#define PFout(n)   BIT_ADDR(GPIOF_ODR_Addr,n)
#define PFin(n)    BIT_ADDR(GPIOF_IDR_Addr,n)

#define PGout(n)   BIT_ADDR(GPIOG_ODR_Addr,n)
#define PGin(n)    BIT_ADDR(GPIOG_IDR_Addr,n)

#define PHout(n)   BIT_ADDR(GPIOH_ODR_Addr,n)
#define PHin(n)    BIT_ADDR(GPIOH_IDR_Addr,n)

#define PIout(n)   BIT_ADDR(GPIOI_ODR_Addr,n)
#define PIin(n)    BIT_ADDR(GPIOI_IDR_Addr,n)

#define PJout(n)   BIT_ADDR(GPIOJ_ODR_Addr,n)
#define PJin(n)    BIT_ADDR(GPIOJ_IDR_Addr,n)

#define PKout(n)   BIT_ADDR(GPIOK_ODR_Addr,n)
#define PKin(n)    BIT_ADDR(GPIOK_IDR_Addr,n)

void Application(void)
{
    /*LED 端口初始化 */
    LED_GPIO_Config();

    while (1)
    {
        /*PA6 = 0,点亮LED*/
        PAout(6) = 0;
        Delay_ms(100);

        /*PA6 = 1,熄灭LED*/
        PAout(6) = 1;
        Delay_ms(100);
    }
}