GPIO

1718677326108367.jpg

Allen 先楫资深 FAE

8年产品研发经验,具有变频器、PLC等工业产品开发经验,也参与过汽车仪表、中控屏等车载产品的研发工作。在产品底层驱动、伺服驱动器、显示仪表等领域有着丰富开发经验。

在嵌入式产品应用开发中,经常需要MCU芯片产生任意的方波信号,从而驱动外设执行相应的操作。比如,驱动模拟量芯片、miniLED屏等。不同于PWM波这种占空比固定的信号,这些驱动信号往往是由等宽的高低电平任意排列的方波。

1.png

传统的GPIO模拟方波时序,不仅占用CPU资源,而且波形的脉宽较大。在驱动miniLED屏这类外设时,达不到系统的功能要求。先楫半导体的全系列MCU可以将内存中的数据通过DMA来设置GPIO的电平,以TRGM互联管理器和PWM比较器配合使用来设置脉冲宽度,从而产生任意时序的方波信号。该方案基于硬件方式来实现,不会占用CPU处理时间,波形宽度可达到50ns。下面介绍该方案的实现过程,例程基于HPM6360EVK实现。

定义波形数据

定义数组如下所示,其中32位无符号整数可以映射32个GPIO,每个数据位对应一个管脚。数组长度4096对应4096个方波周期。

ATTR_PLACE_AT_NONCACHEABLE_WITH_ALIGNMENT(8) uint32_t g_u32LedBufForGpio32[4096] = {0xFFFFFFFF, 0, 0xFFFFFFFF, 0, 0xFFFFFFFF, 0, 0xFFFFFFFF, 0, 0xFFFFFFFF, 0, 0xFFFFFFFF, 0, 0xFFFFFFFF, 0, 0xFFFFFFFF, 0};

初始化GPIO管脚

此处将24个GPIO配置为输出模式。

static void zh_led_gpio_config(void)
{
    uint32_t pad_ctl = IOC_PAD_PAD_CTL_PE_SET(1) | IOC_PAD_PAD_CTL_PS_SET(1);
 
    HPM_IOC->PAD[IOC_PAD_PC00].FUNC_CTL = IOC_PC00_FUNC_CTL_GPIO_C_00;
    HPM_IOC->PAD[IOC_PAD_PC00].PAD_CTL = pad_ctl;
 
    HPM_IOC->PAD[IOC_PAD_PC01].FUNC_CTL = IOC_PC01_FUNC_CTL_GPIO_C_01;
    HPM_IOC->PAD[IOC_PAD_PC01].PAD_CTL = pad_ctl;
 
    HPM_IOC->PAD[IOC_PAD_PC02].FUNC_CTL = IOC_PC02_FUNC_CTL_GPIO_C_02;
    HPM_IOC->PAD[IOC_PAD_PC02].PAD_CTL = pad_ctl;
    
    HPM_IOC->PAD[IOC_PAD_PC03].FUNC_CTL = IOC_PC03_FUNC_CTL_GPIO_C_03;
    HPM_IOC->PAD[IOC_PAD_PC03].PAD_CTL = pad_ctl;
    
    HPM_IOC->PAD[IOC_PAD_PC04].FUNC_CTL = IOC_PC04_FUNC_CTL_GPIO_C_04;
    HPM_IOC->PAD[IOC_PAD_PC04].PAD_CTL = pad_ctl;
 
    HPM_IOC->PAD[IOC_PAD_PC05].FUNC_CTL = IOC_PC05_FUNC_CTL_GPIO_C_05;
    HPM_IOC->PAD[IOC_PAD_PC05].PAD_CTL = pad_ctl;
 
    HPM_IOC->PAD[IOC_PAD_PC06].FUNC_CTL = IOC_PC06_FUNC_CTL_GPIO_C_06;
    HPM_IOC->PAD[IOC_PAD_PC06].PAD_CTL = pad_ctl;
 
    HPM_IOC->PAD[IOC_PAD_PC07].FUNC_CTL = IOC_PC07_FUNC_CTL_GPIO_C_07;
    HPM_IOC->PAD[IOC_PAD_PC07].PAD_CTL = pad_ctl;
 
    HPM_IOC->PAD[IOC_PAD_PC08].FUNC_CTL = IOC_PC08_FUNC_CTL_GPIO_C_08;
    HPM_IOC->PAD[IOC_PAD_PC08].PAD_CTL = pad_ctl;
 
    HPM_IOC->PAD[IOC_PAD_PC09].FUNC_CTL = IOC_PC09_FUNC_CTL_GPIO_C_09;
    HPM_IOC->PAD[IOC_PAD_PC09].PAD_CTL = pad_ctl;
 
    HPM_IOC->PAD[IOC_PAD_PC10].FUNC_CTL = IOC_PC10_FUNC_CTL_GPIO_C_10;
    HPM_IOC->PAD[IOC_PAD_PC10].PAD_CTL = pad_ctl;
 
    HPM_IOC->PAD[IOC_PAD_PC11].FUNC_CTL = IOC_PC11_FUNC_CTL_GPIO_C_11;
    HPM_IOC->PAD[IOC_PAD_PC11].PAD_CTL = pad_ctl;
 
    HPM_IOC->PAD[IOC_PAD_PC12].FUNC_CTL = IOC_PC12_FUNC_CTL_GPIO_C_12;
    HPM_IOC->PAD[IOC_PAD_PC12].PAD_CTL = pad_ctl;
 
    HPM_IOC->PAD[IOC_PAD_PC13].FUNC_CTL = IOC_PC13_FUNC_CTL_GPIO_C_13;
    HPM_IOC->PAD[IOC_PAD_PC13].PAD_CTL = pad_ctl;
 
    HPM_IOC->PAD[IOC_PAD_PC14].FUNC_CTL = IOC_PC14_FUNC_CTL_GPIO_C_14;
    HPM_IOC->PAD[IOC_PAD_PC14].PAD_CTL = pad_ctl;
 
    HPM_IOC->PAD[IOC_PAD_PC15].FUNC_CTL = IOC_PC15_FUNC_CTL_GPIO_C_15;
    HPM_IOC->PAD[IOC_PAD_PC15].PAD_CTL = pad_ctl;
 
    HPM_IOC->PAD[IOC_PAD_PC16].FUNC_CTL = IOC_PC16_FUNC_CTL_GPIO_C_16;
    HPM_IOC->PAD[IOC_PAD_PC16].PAD_CTL = pad_ctl;
 
    HPM_IOC->PAD[IOC_PAD_PC17].FUNC_CTL = IOC_PC17_FUNC_CTL_GPIO_C_17;
    HPM_IOC->PAD[IOC_PAD_PC17].PAD_CTL = pad_ctl;
 
    HPM_IOC->PAD[IOC_PAD_PC18].FUNC_CTL = IOC_PC18_FUNC_CTL_GPIO_C_18;
    HPM_IOC->PAD[IOC_PAD_PC18].PAD_CTL = pad_ctl;
 
    HPM_IOC->PAD[IOC_PAD_PC19].FUNC_CTL = IOC_PC19_FUNC_CTL_GPIO_C_19;
    HPM_IOC->PAD[IOC_PAD_PC19].PAD_CTL = pad_ctl;
 
    HPM_IOC->PAD[IOC_PAD_PC20].FUNC_CTL = IOC_PC20_FUNC_CTL_GPIO_C_20;
    HPM_IOC->PAD[IOC_PAD_PC20].PAD_CTL = pad_ctl;
 
    HPM_IOC->PAD[IOC_PAD_PC21].FUNC_CTL = IOC_PC21_FUNC_CTL_GPIO_C_21;
    HPM_IOC->PAD[IOC_PAD_PC21].PAD_CTL = pad_ctl;
 
    HPM_IOC->PAD[IOC_PAD_PC22].FUNC_CTL = IOC_PC22_FUNC_CTL_GPIO_C_22;
    HPM_IOC->PAD[IOC_PAD_PC22].PAD_CTL = pad_ctl;
 
    HPM_IOC->PAD[IOC_PAD_PC23].FUNC_CTL = IOC_PC23_FUNC_CTL_GPIO_C_23;
    HPM_IOC->PAD[IOC_PAD_PC23].PAD_CTL = pad_ctl;
 
    HPM_IOC->PAD[IOC_PAD_PC24].FUNC_CTL = IOC_PC24_FUNC_CTL_GPIO_C_24;
    HPM_IOC->PAD[IOC_PAD_PC24].PAD_CTL = pad_ctl;
 
    for(int i=0; i<24; i++) {
        gpio_set_pin_output(HPM_GPIO0, GPIO_DO_GPIOC, i);
        gpio_write_pin(HPM_GPIO0, GPIO_DO_GPIOC, i, 0);
    }
}

配置DMA

在每次输出波形之前都要进行一下DMA的设置,用户通过调整波形数组的数据来设置想要的波形。DMA将数组的数据不断搬运到DO[VALUE]寄存器中,因此要注意GPIO管脚和数组数据的对应关系。例程是输出占空比为50%的PWM波。

static void zh_led_dma_config(void)
{
    dma_channel_config_t ch_config = { 0 };
    unsigned int i = 0;
 
    for(i = 0;i<4096;i++)
    {
      g_u32LedBufForGpio32[i] = (i&1)?(~0):(0);
      //g_u32LedBufForGpio32[i] = ~0;
      //if(i%5 == 0)
      //{
      //  g_u32LedBufForGpio32[i] = ~0;
      //}
      //else
      //{
      //  g_u32LedBufForGpio32[i] = 0;
      //}
    }
 
    dma_reset(HPM_HDMA);
 
    intc_m_enable_irq_with_priority(BOARD_APP_HDMA_IRQ, 1);
 
    dma_default_channel_config(HPM_HDMA, &ch_config);
 
    ch_config.src_addr = (uint32_t)&g_u32LedBufForGpio32[0];
 
    ch_config.dst_addr = (uint32_t)&HPM_GPIO0->DO[GPIO_DO_GPIOC].VALUE;
    ch_config.src_width = DMA_TRANSFER_WIDTH_WORD;                                                             // 32位
    ch_config.dst_width = DMA_TRANSFER_WIDTH_WORD;                                                             // 32位
    ch_config.src_addr_ctrl = DMA_ADDRESS_CONTROL_INCREMENT;
    ch_config.dst_addr_ctrl = DMA_ADDRESS_CONTROL_FIXED;
    ch_config.size_in_byte = sizeof(g_u32LedBufForGpio32); //32 * sizeof(uint32_t); 
    ch_config.dst_mode = DMA_HANDSHAKE_MODE_NORMAL;
    ch_config.src_burst_size = 0;
 
    if (status_success != dma_setup_channel(HPM_HDMA, 0, &ch_config, false))
    {
        printf(" dma setup channel failed\n");
        return;
    }
 
    dmamux_config(HPM_DMAMUX, DMAMUX_MUXCFG_HDMA_MUX0, HPM_DMA_SRC_MOT0_0, false);
 
    trgm_dma_request_config(HPM_TRGM0, 0, 18);
 
    pwm_enable_dma_request(HPM_PWM0, PWM_IRQ_CMP(18));
 
    synt_enable_counter(HPM_SYNT, true);
    pwm_start_counter(HPM_PWM0);
    dmamux_enable_channel(HPM_DMAMUX, DMAMUX_MUXCFG_HDMA_MUX0);
    dma_enable_channel(HPM_HDMA, 0);
}

设置DMA中断,在输出波形结束以后会触发中断响应函数。

void isr_dma(void)
{
    uint32_t stat;
 
    stat = dma_check_transfer_status(HPM_HDMA, 0);
 
    if (0 != (stat & DMA_CHANNEL_STATUS_TC)) {
      printf("Transfer done!");
    }
}
SDK_DECLARE_EXT_ISR_M(BOARD_APP_HDMA_IRQ, isr_dma)

配置CLK时钟

以PWM比较器产生需要的波形周期。

/**
 * u8Phase from 1~18, 其中设置1 == 设置18, 设置1时 上升沿的边与数据的开始时刻对齐,随着设置的数字增大,上升沿的边向右移动,设置8时,上升沿的边大约在中间。
 *  */
static int zh_led_clk_config(uint8_t u8Phase)
{
    pwm_cmp_config_t cmp_config_ch0[4] = {0};
    pwm_config_t pwm_config = {0};
    uint32_t u32CmpValue[4];
 
    switch (u8Phase)
    {
        case 1:
            u32CmpValue[0] = 6;
            u32CmpValue[1] = 16;
            u32CmpValue[2] = 18;
            u32CmpValue[3] = 18;
            break;
        case 2:
            u32CmpValue[0] = 7;
            u32CmpValue[1] = 18;
            u32CmpValue[2] = 18;
            u32CmpValue[3] = 18;
            break;
        case 3:
            u32CmpValue[0] = 17;
            u32CmpValue[1] = 0;
            u32CmpValue[2] = 18;
            u32CmpValue[3] = 8;
            break;
        case 4:
            u32CmpValue[0] = 17;
            u32CmpValue[1] = 1;
            u32CmpValue[2] = 18;
            u32CmpValue[3] = 9;
            break;
        case 5:
            u32CmpValue[0] = 17;
            u32CmpValue[1] = 2;
            u32CmpValue[2] = 18;
            u32CmpValue[3] = 10;
            break;
        case 6:
            u32CmpValue[0] = 17;
            u32CmpValue[1] = 3;
            u32CmpValue[2] = 18;
            u32CmpValue[3] = 11;
            break;
        case 7:
            u32CmpValue[0] = 17;
            u32CmpValue[1] = 4;
            u32CmpValue[2] = 18;
            u32CmpValue[3] = 12;
            break;
        case 8:
            u32CmpValue[0] = 17;
            u32CmpValue[1] = 5;
            u32CmpValue[2] = 18;
            u32CmpValue[3] = 13;
            break;
        case 9:
            u32CmpValue[0] = 17;
            u32CmpValue[1] = 6;
            u32CmpValue[2] = 18;
            u32CmpValue[3] = 14;
            break;
        case 10:
            u32CmpValue[0] = 18;
            u32CmpValue[1] = 7;
            u32CmpValue[2] = 17;
            u32CmpValue[3] = 15;
            break;
        case 11:
            u32CmpValue[0] = 18;
            u32CmpValue[1] = 8;
            u32CmpValue[2] = 17;
            u32CmpValue[3] = 16;
            break;
        case 12:
            u32CmpValue[0] = 17;
            u32CmpValue[1] = 9;
            u32CmpValue[2] = 18;
            u32CmpValue[3] = 18;
            break;
        case 13:
            u32CmpValue[0] = 0;
            u32CmpValue[1] = 10;
            u32CmpValue[2] = 18;
            u32CmpValue[3] = 18;
            break;
        case 14:
            u32CmpValue[0] = 1;
            u32CmpValue[1] = 11;
            u32CmpValue[2] = 18;
            u32CmpValue[3] = 18;
            break;
        case 15:
            u32CmpValue[0] = 2;
            u32CmpValue[1] = 12;
            u32CmpValue[2] = 18;
            u32CmpValue[3] = 18;
            break;
        case 16:
            u32CmpValue[0] = 3;
            u32CmpValue[1] = 13;
            u32CmpValue[2] = 18;
            u32CmpValue[3] = 18;
            break;
        case 17:
            u32CmpValue[0] = 4;
            u32CmpValue[1] = 14;
            u32CmpValue[2] = 18;
            u32CmpValue[3] = 18;
            break;
        case 18:
            u32CmpValue[0] = 5;
            u32CmpValue[1] = 15;
            u32CmpValue[2] = 18;
            u32CmpValue[3] = 18;
            break;
            break;
        default:
            break;
    }
 
    pwm_stop_counter(HPM_PWM0);
    pwm_set_reload(HPM_PWM0, 0, zh_led_PWM_FREQ-1);
    pwm_set_start_count(HPM_PWM0, 0, 0);
 
    pwm_config.enable_output = true;
    pwm_config.invert_output = false;
    pwm_config.update_trigger = pwm_shadow_register_update_on_modify;
    pwm_config.fault_mode = pwm_fault_mode_force_output_highz;
    pwm_config.fault_recovery_trigger = pwm_fault_recovery_on_fault_clear;
    pwm_config.force_source = pwm_force_source_software;
    pwm_config.dead_zone_in_half_cycle = 0;
 
    /*cmp0 cmp1 cmp2 cmp3 for pwm ch0*/
    cmp_config_ch0[0].cmp = u32CmpValue[0];
    cmp_config_ch0[0].enable_ex_cmp = false;
    cmp_config_ch0[0].mode = pwm_cmp_mode_output_compare;
    cmp_config_ch0[0].update_trigger = pwm_shadow_register_update_on_hw_event;
    cmp_config_ch0[0].ex_cmp = 0;    
    cmp_config_ch0[0].half_clock_cmp = 0;
    cmp_config_ch0[0].jitter_cmp = 0;   
 
    cmp_config_ch0[1].cmp = u32CmpValue[1];
    cmp_config_ch0[1].enable_ex_cmp = false;
    cmp_config_ch0[1].mode = pwm_cmp_mode_output_compare;
    cmp_config_ch0[1].update_trigger = pwm_shadow_register_update_on_hw_event;
    cmp_config_ch0[1].ex_cmp = 0;    
    cmp_config_ch0[1].half_clock_cmp = 0;
    cmp_config_ch0[1].jitter_cmp = 0;  
 
    cmp_config_ch0[2].cmp = u32CmpValue[2];
    cmp_config_ch0[2].enable_ex_cmp = false;
    cmp_config_ch0[2].mode = pwm_cmp_mode_output_compare;
    cmp_config_ch0[2].update_trigger = pwm_shadow_register_update_on_hw_event;
    cmp_config_ch0[2].ex_cmp = 0;    
    cmp_config_ch0[2].half_clock_cmp = 0;
    cmp_config_ch0[2].jitter_cmp = 0;  
 
    cmp_config_ch0[3].cmp = u32CmpValue[3];
    cmp_config_ch0[3].enable_ex_cmp = false;
    cmp_config_ch0[3].mode = pwm_cmp_mode_output_compare;
    cmp_config_ch0[3].update_trigger = pwm_shadow_register_update_on_hw_event;
    cmp_config_ch0[3].ex_cmp = 0;    
    cmp_config_ch0[3].half_clock_cmp = 0;
    cmp_config_ch0[3].jitter_cmp = 0;  
    if (status_success != pwm_setup_waveform(HPM_PWM0, 1, &pwm_config, 0, cmp_config_ch0, 4)) {
        printf("failed to setup waveform for ch0\n");
        return status_fail;
    }
 
    cmp_config_ch0[0].cmp = zh_led_PWM_FREQ-1;
    cmp_config_ch0[0].update_trigger = pwm_shadow_register_update_on_modify;
    pwm_load_cmp_shadow_on_match(HPM_PWM0, 4, &cmp_config_ch0[0]);
 
    pwm_issue_shadow_register_lock_event(HPM_PWM0);
 
    /* enable pwm fault protect */
    pwm_fault_source_config_t config;
    //config.external_fault_active_low = false;
    config.source_mask = PWM_GCR_FAULTI0EN_MASK;
    config.fault_recover_at_rising_edge = false;
    config.fault_output_recovery_trigger = 0;
    pwm_config_fault_source(HPM_PWM0, &config);
 
 
    return status_success;
}

配置TRGM互联管理器

互联管理器TRGM以PWM的周期来触发DMA进行数据搬运。

static void zh_led_trgm_config(void)
{
    trgm_output_t stTrgmOutput;
 
    stTrgmOutput.invert = false;
    stTrgmOutput.type = trgm_output_same_as_input;
    
    stTrgmOutput.input = 44;
    trgm_output_config(HPM_TRGM0, 14, &stTrgmOutput);
    
    pwm_enable_reload_at_synci(HPM_PWM0);
 
    synt_reset_counter(HPM_SYNT);
    synt_set_reload(HPM_SYNT, zh_led_PWM_FREQ-1);
    synt_set_comparator(HPM_SYNT, 0, zh_led_PWM_FREQ-1);
}

测试程序如下:

int main(void)
{
    unsigned int j=3;
    board_init();
    zh_led_gpio_config();
    zh_led_clk_config(2);
    zh_led_trgm_config();
    zh_led_dma_config();
 
    printf("start\n");
 
    while(j--)
    {
      printf("j = %d\n", j);

      zh_led_dma_config();
      pwm_start_counter(HPM_PWM0);
   
   board_delay_ms(500);
    };

    printf("stop\n");
 
    while(1);

    return 0;
}

串口打印结果如下:

2.png

测量GPIO的波形:

3.png
4.png

因为HDMA访问AHB SRAM速度更快,可以将数据存储到AHB SRAM来提高刷新速度。

 __attribute__ ((section(".ahb_sram"))) uint32_t g_u32LedBufForGpio32[1024]={0xFFFFFFFF, 0, 0xFFFFFFFF, 0, 0xFFFFFFFF, 0, 0xFFFFFFFF, 0, 0xFFFFFFFF, 0, 0xFFFFFFFF, 0, 0xFFFFFFFF, 0, 0xFFFFFFFF, 0};

使用逻辑分析仪测试波形如下:

5.png

用示波器测量波形如下图所示,信号质量表现优异。

6.png

来源:先楫半导体HPMicro

免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。

围观 56

GPIO简介

GPIO是通用输入输出端口的简称,也是CKS32可控制的引脚,CKS32芯片的GPIO引脚与外部设备连接起来,从而实现与外部通讯、控制以及数据采集的功能。CKS32芯片的GPIO被分成很多组,每组有16个引脚,如型号为CKS2F107VET6型号的芯片有GPIOA、GPIOB、GPIOC至GPIOE共5组GPIO,芯片一共100个引脚,其中GPIO就占了一大部分,所有的GPIO引脚都有基本的输入输出功能。

最基本的输出功能是由CKS32控制引脚输出高、低电平,实现开关控制,如把GPIO引脚接入到LED灯,那就可以控制LED灯的亮灭,引脚接入到继电器或三极管,那就可以通过继电器或三极管控制外部大功率电路的通断。最基本的输入功能是检测外部输入电平,如把 GPIO引脚连接到按键,通过电平高低区分按键是否被按下。

GPIO框图结构分析

CKS32F107系列MCU的GPIO内部硬件结构如下图所示,通过GPIO硬件结构框图,可以从整体上深入了解GPIO外设及它的各种应用模式。该图从最右端看起,最右端就是代表 MCU引出的 GPIO引脚,其余部件都位于MCU芯片内部。

1.png

图1 GPIO硬件结构框图

序号①是引脚的两个保护二级管,可以防止引脚外部过高或过低的电压输入,当引脚电压高于VDD时,上方的二极管导通,当引脚电压低于VSS时,下方的二极管导通,防止不正常电压引入芯片导致芯片烧毁。尽管有这样的保护,并不意味着CKS32的GPIO能直接外接大功率驱动器件,如直接驱动电机,如果强制驱动可能会造成电机不转或者导致芯片烧坏,必须要在GPIO和电机之间增加大功率及隔离电路驱动。

序号②是GPIO引脚线路经过两个保护二极管后,下方“输出模式”电路中的一个由P-MOS和N-MOS管组成的结构单元。这个结构使GPIO具有了“推挽输出”和“开漏输出”两种模式,输出模式是根据这两个MOS管的工作方式来命名的。在该结构中输入高电平时,经过反向后,上方的P-MOS导通,下方的N-MOS关闭,对外输出高电平;而在该结构中输入低电平时,经过反向后,N-MOS管导通,P-MOS关闭,对外输出低电平。当引脚高低电平切换时,两个管子轮流导通,P管负责灌电流,N管负责拉电流,使其负载能力和开关速度都比普通的方式有很大的提高。推挽输出的低电平为0伏,高电平为3.3伏,推挽等效电路如下图(左)。推挽输出模式一般应用在输出电平为0和3.3伏而且需要高速切换开关状态的场合。在实际应用中,除了必须用开漏模式的场合,一般都习惯使用推挽输出模式。

2.png

图2 GPIO硬件结构框图

在开漏输出模式时,上方的P-MOS管完全不工作。如果我们控制输出为0低电平,则 P-MOS管关闭,N-MOS管导通,使输出接地,若控制输出为1 (它无法直接输出高电平) 时,则P-MOS管和N-MOS管都关闭,所以引脚既不输出高电平,也不输出低电平,为高阻态,因此正常使用时必须外部接上拉电阻。开漏等效电路如上图(右),它具有“线与”特性,若有很多个开漏模式引脚连接到一起时,只有当所有引脚都输出高阻态,才由上拉电阻提供高电平。若其中一个引脚为低电平,那线路就相当于短路接地,使得整条线路都为低电平0伏。开漏输出一般应用在I2C、SMBUS通讯等需要“线与”功能的总线电路中。除此之外,还用在电平不匹配的场合,如需要输出5伏的高电平,就可以在外部接一个上拉电阻,上拉电源为5伏,并且把GPIO设置为开漏模式,当输出高阻态时,由上拉电阻和电源向外输出5伏的电平。

序号③是GPIO输出数据寄存器组,前面提到的双MOS管结构电路输入信号,就是由这个寄存器组中的GPIOx_ODR提供的,因此我们通过修改输出数据寄存器的值就可以修改GPIO引脚的输出电平。而“置位/复位寄存器GPIOx_BSRR”可以通过修改输出数据寄存器的值从而影响电路的输出。

序号④是连接MCU片内外设和GPIO引脚的复用功能输出模块,通过此功能可以将GPIO引脚用作指定外设功能的一部分,算是GPIO的第二用途。从其它外设引出来的“复用功能输出信号”与GPIO本身的数据据寄存器都连接到双MOS管结构的输入中,通过内部开关切换选择。例如我们使用USART串口通讯时,需要用到某个GPIO引脚作为通讯发送引脚,这个时候就可以把该GPIO引脚配置成USART串口复用功能,由串口外设控制该引脚发送数据。

序号⑤是输入数据寄存器组,位于GPIO结构框图的上半部分,GPIO引脚经过内部的上、下拉电阻,可以配置成上/下拉输入,然后再连接到施密特触发器,信号经过触发器后,模拟信号转化为0/1数字信号,然后存储在“输入数据寄存器GPIOx_IDR”中,通过读取该寄存器就可以获取GPIO引脚的电平状态。

序号⑥是连接MCU片内外设和GPIO引脚的复用功能输入模块,与序号④类似,在“复用功能输入模式”时,GPIO引脚的信号传输到指定片内外设,由该外设读取引脚状态。例如我们使用USART串口通讯时,需要用到某个GPIO引脚作为通讯接收引脚,这个时候就可以把该GPIO引脚配置成USART串口复用功能,由串口外设控制该引脚接收外部数据。

序号⑦是用于ADC采集电压输入通道的专用“模拟输入”功能,由于ADC外设要采集到原始的模拟信号,所以输入信号不经过施密特触发器,因为经过施密特触发器后信号只有0/1两种状态。类似地,当GPIO引脚作为“模拟输出”功能用于DAC模拟电压输出通道时,模拟信号输出也不经过双MOS管结构而直接输出到GPIO引脚。

GPIO工作模式总结

根据上述结构分析,可以总结出在固件库中GPIO可以配置成如下8种工作模式,且大致归为三类。

//Configuration Mode enumeration
typedef enum
{    
    GPIO_Mode_AIN = 0x0,                    //模拟输入    
    GPIO_Mode_IN_FLOATING = 0x04,     //浮空输入    
    GPIO_Mode_IPD = 0x28,                  //下拉输入    
    GPIO_Mode_IPU = 0x48,                  //上拉输入    
    GPIO_Mode_Out_OD = 0x14,            //开漏输出    
    GPIO_Mode_Out_PP = 0x10,             //推挽输出    
    GPIO_Mode_AF_OD = 0x1C,             //复用开漏输出   
    GPIO_Mode_AF_PP = 0x18               //复用推挽输出
} GPIOMode_TypeDef;

第一类是输入模式(模拟/浮空/上拉/下拉),在输入模式时,施密特触发器打开,输出被禁止,可通过输入数据寄存器GPIOx_IDR读取I/O状态。其中输入模式,可设置为上拉、下拉、浮空和模拟输入四种。上拉和下拉输入很好理解,默认的电平由上拉或者下拉决定。浮空输入的电平是不确定的,完全由外部的输入决定,一般接按键的时候用的是这个模式。模拟输入则专用于ADC采集。

第二类是输出模式(推挽/开漏),在推挽模式时双MOS管以轮流方式工作,输出数据寄存器GPIOx_ODR可控制I/O输出高低电平。开漏模式时,只有N-MOS管工作,输出数据寄存器可控制I/O输出高阻态或低电平。输出速度可配置,此处的输出速度即I/O支持的高低电平状态最高切换频率,支持的频率越高,功耗越大。在输出模式时施密特触发器是打开的,即输入可用,通过输入数据寄存器GPIOx_IDR可读取I/O的实际状态。

第三类是复用功能模式(推挽/开漏),复用功能模式中,输出使能,输出速度可配置,可工作在开漏及推挽模式,但是输出信号源于其它外设,输出数据寄存器GPIOx_ODR无效;输入可用,通过输入数据寄存器可获取I/O实际状态,但一般直接用外设的寄存器来获取该数据信号。

以上各类型的GPIO口每一个都可以自由编程,此外,CKS32F107的很多IO口都是5V兼容的,这些IO口在与5V电平的外设连接的时候很有优势,具体哪些IO口是5V兼容的,可以从该芯片的数据手册管脚描述章节查到(I/O Level标FT的就是5V电平兼容的)。

GPIO寄存器

CKS32的GPIO口寄存器必须要按32位字被访问,每个IO端口都有7个寄存器来控制。分别是:配置模式的2个32位的端口配置寄存器CRL和CRH;2个32位的数据寄存器IDR和ODR;1个32位的置位/复位寄存器BSRR;一个16位的复位寄存器BRR;1个32位的锁存寄存器LCKR。如果想要了解每个寄存器的详细使用方法,可以参考《CKS32F107参考手册》。

(1)CRL和CRH控制着每个IO口的模式及输出速率,本文以CRL为例,看看端口低配置寄存器的描述,如下图所示。该寄存器的复位值为0x44444444,从图中可以看到,复位值其实就是配置端口为浮空输入模式。从下图还可以得出:CRL控制着每组IO端口的低8位模式。每个IO端口的位占用CRL的4个位,高两位为CNF,低两位为MODE。这里我们可以记住几个常用的配置,比如0x0表示模拟输入模式(ADC用)、0x3表示推挽输出模式(做输出口用,50M速率)、0x8表示上/下拉输入模式(做输入口用)、0xB表示复用输出(使用IO口的第二功能,50M速率)。CRH的作用和CRL完全一样,只是CRL控制的是低8位输出口,而CRH控制的是高8位输出口。这里我们对CRH就不做详细介绍了。

3.png

图3 GPIOx_CRL寄存器

(2)IDR是一个端口输入数据寄存器,低16位有效。该寄存器为只读寄存器,并且只能以16位的形式读出。该寄存器各位的描述如下图所示:

4.png

图4 GPIOx_CRL寄存器

(3)ODR是一个端口输出数据寄存器,也只用了低16位。该寄存器为可读写,从该寄存器读出来的数据可以用于判断当前IO口的输出状态。而向该寄存器写数据,则可以控制某个IO口的输出电平。该寄存器的各位描述如下图所示:

5.png

图5 GPIOx_CRL寄存器

(4)BSRR寄存器是端口位设置/清除寄存器。该寄存器和ODR寄存器具有类似的作用,都可以用来设置GPIO端口的输出位是1还是0。

6.png

图6 GPIOx_CRL寄存器

通过固件库操作GPIO

CKS32F107系列GPIO相关的函数和定义分布在固件库文件cks32f10x_gpio.c和头文件 cks32f10x_gpio.h文件中。在固件库开发中,操作寄存器CRH和CRL来配置IO口的模式和速度是通过GPIO初始化函数完成的。

void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* 
GPIO_InitStruct);

这个函数有两个参数,第一个参数是用来指定GPIO,取值范围为GPIOA~GPIOG。第二个参数为初始化参数结构体指针,结构体类型为GPIO_InitTypeDef。结构体的定义如下:

typedef struct

{

    uint16_t GPIO_Pin;

    GPIOSpeed_TypeDef GPIO_Speed;

    GPIOMode_TypeDef GPIO_Mode;

}GPIO_InitTypeDef; 

下面通过一个GPIO初始化实例来讲解这个结构体的成员变量的含义。代码的意思是设置GPIOB的第5个端口为推挽输出模式,同时速度为50M。结构体GPIO_InitStructure的第一个成员变量GPIO_Pin用来设置是要初始化哪个或者哪些IO口;第二个成员变量GPIO_Mode是用来设置对应IO端口的输出输入模式;第三个参数是IO口速度设置。

GPIO_InitTypeDef GPIO_InitStructure;

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;               //PB5端口配置

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;   //推挽输出

GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;   //速度50MHz

GPIO_Init(GPIOB, &GPIO_InitStructure);                                   //根据设定参数配置 GPIO

在固件库中操作IDR寄存器读取IO端口数据是通过GPIO_ReadInputDataBit函数实现的。比如我要读GPIOA5的电平状态,那么方法是:

uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_5);

在固件库中设置ODR寄存器的值来控制IO口的输出状态是通过函数GPIO_Write来实现的,该函数一般用来一次性往一个GPIO的多个端口设值。

void GPIO_Write(GPIO_TypeDef* GPIOx, uint16_t PortVal);

该寄存器通过举例子可以很清楚了解它的使用方法。例如你要设置GPIOA的第1个端口值为1,那么你只需要往寄存器BSRR的低16位对应位写1即可。该寄存器往相应位写0是无影响的,所以我们要设置某些位,我们不用管其他位的值。

GPIOA->BSRR = 1 << 1;

在固件库中,通过BSRR和BRR寄存器设置GPIO端口输出是通过函数GPIO_SetBits()和函数GPIO_ResetBits()来完成的。在多数情况下,我们都是采用这两个函数来设置GPIO端口的输入和输出状态。比如我们要设置GPIOB5输出1和0,那么方法为:

void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
GPIO_SetBits(GPIOB, GPIO_Pin_5);
GPIO_ResetBits(GPIOB, GPIO_Pin_5);

GPIO相关的库函数我们先讲解到这里。虽然IO操作步骤很简单,这里还是做个概括总结,操作步骤为:

(1)使能IO口时钟,调用函数RCC_APB2PeriphClockCmd();

(2)初始化IO参数,调用函数GPIO_Init();

(3)操作IO。

下面我们来讲解一个基于CKS32F107VxT6开发板的GPIO按键输入和GPIO输出实验软件例程。

实验例程

编程要点

(1)使能GPIO端口时钟;

(2)初始化GPIO按键引脚为输入模式(上拉输入);

(3)初始化GPIO LED引脚为输出模式(推挽输出);

(4)编写简单测试程序,检测按键的状态,实现按键控制LED灯。

代码分析

(1)按键检测引脚相关宏定义

//  引脚定义
#define    KEY1_GPIO_CLK     RCC_APB2Periph_GPIOC
#define    KEY1_GPIO_PORT    GPIOC      
#define    KEY1_GPIO_PIN     GPIO_Pin_0
#define    LED1_GPIO_PORT    GPIOB         
#define    LED1_GPIO_CLK     RCC_APB2Periph_GPIOB
#define    LED1_GPIO_PIN    GPIO_Pin_15

(2)按键GPIO初始化函数

void Key_GPIO_Config(void)
{    
    GPIO_InitTypeDef GPIO_InitStructure;   
    /*开启按键端口的时钟*/    
    RCC_APB2PeriphClockCmd(KEY1_GPIO_CLK,ENABLE);                                                    
                        //选择按键的引脚    
    GPIO_InitStructure.GPIO_Pin = KEY1_GPIO_PIN;                                             
                        // 设置按键的引脚为浮空输入    
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;                                            
                        //使用结构体初始化按键    
    GPIO_Init(KEY1_GPIO_PORT, &GPIO_InitStructure);}

(3)LED GPIO初始化函数

void LED_GPIO_Config(void)
{    
    /*定义一个GPIO_InitTypeDef类型的结构体*/     
    GPIO_InitTypeDef GPIO_InitStructure;  
    /*开启LED相关的GPIO外设时钟*/    
    RCC_APB2PeriphClockCmd(LED1_GPIO_CLK, ENABLE);  
    /*选择要控制的GPIO引脚*/    
    GPIO_InitStructure.GPIO_Pin = LED1_GPIO_PIN;  
    /*设置引脚模式为通用推挽输出*/    
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;     
    /*设置引脚速率为50MHz */       
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;  
    /*调用库函数,初始化GPIO*/    
    GPIO_Init(LED1_GPIO_PORT, &GPIO_InitStructure);  
    /* 关闭led灯 */    
    GPIO_SetBits(LED1_GPIO_PORT, LED1_GPIO_PIN);
}

(4)主函数

int main(void)
{    
    /* LED端口初始化 */    
    LED_GPIO_Config();    
    /* 按键端口初始化 */    
    Key_GPIO_Config();    
    /* 轮询按键状态,若按键按下则反转LED */   
    while(1)                                 
    {          
        if(GPIO_ReadInputDataBit(KEY1_GPIO_PORT,        KEY1_GPIO_PIN) == Bit_RESET)        
        {          
            GPIO_WriteBit(LED1_GPIO_PORT, LED1_GPIO_PIN, Bit_SET);        
        }        
        else        
        {          
            GPIO_WriteBit(LED1_GPIO_PORT, LED1_GPIO_PIN, Bit_RESET);        
        }        
    }
}

代码中先初始化LED灯及按键后,在while函数里不断读取查询并判断按键是否按下,若返回值表示按键按下,则反转LED灯的状态。

来源:中科芯MCU

免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。

围观 22

MCU的GPIO是它众多功能模块里面最基础的,也是每个电路设计里必用的功能,尤其是在小型的控制电路里,或者一个SOT-23-6封装的MCU,只提供若干个GPIO就已经能满足产品的需求了,因此GPIO口的应用是我们先深入探讨的第一课;我们先从基本的工作模式开始:

(1)输入模式:输入浮空、输入上拉、输入下拉、模拟输入这四种

1.png

上图为输入端口的内部逻辑图,当设置为输入浮空时,上、下拉电阻上的开关都是断开的,输入信号通过TTL 触发器进入到输入数据寄存器,并被MCU内核读取;这种情况下,如果MCU外围IO口上没有接线路,则这个管脚就是处于浮空状态的;端口的电平状态不确定,完全受外部输入所影响;这种应用在一些键盘矩阵输入应用里较为常见;但是这个浮空状态因为它的不确定性,在使用时要对你外围电路的电平状态有充分的了解。

2.png

上图设置为输入上拉,则是将IO端口上部的开关闭合,内置1个上拉电阻;这样在IO外部无任何输入信号时,IO的输入状态也是高电平,这就解决了IO状态不确定性的问题。但注意这个上拉电阻阻值是在40~100K的范围里,它的上拉驱动能力是有限的,在使用这个模式对接 类似I2C、1-wire 总线时,这个上拉电阻阻值是偏大的,还是需要在外部单独增加上拉电阻,才能确保信号的稳定性。    

3.png

上图设置为输入下拉,则是将IO端口下部的开关闭合,下拉状态将IO的初始状态确定为低电平,阻值跟上拉是一样的,在40~100K的范围里。    

4.png

上图设置为模拟输入,这种一般是内部还要接入到ADC的功能模块或COMP比较器、运放等模块,用于模拟量的输入检测,这也是用的比较多的设计,如采用NTC热敏电阻测温电路。

(2)输出模式:开漏输出及上/下拉可配、推挽式输出及上/下拉可配、推挽式复用功能及上下拉可配、开漏复用功能及上下拉可配 这四种模式。

5.png

如上图,开漏输出是指PMOS断开(不起作用)情况下,下方的N-MOS管可控制输出开、关;开漏输出的配置非常实用,可以解决MCU工作电平与外部电路电平不匹配的问题,比如MCU的IO口电平为3.3V,而外部被驱动电路为5V供电,也是可以通过开漏输出去驱动的,因为MCU内部的N-Mos管是可以承受5V的电平的;但要注意外部电路灌入N-MOS管的电流需要限制在芯片规格书要求的范围内,通常是15mA上下(具体要仔细看MCU的规格书,千万别超标了)。注意,这个模式里还可以配置上、下拉电阻,但是如果选择了上拉电阻,那它的工作电压就是MCU的VDD了,这点要注意;当你需要明确输出电平的状态为高电平时,可以配置为上拉;如果没有配置上拉,那开漏输出为1时,是得不到高电平的,实际上是IO口浮空状态,也就是这个浮空状态,才有利于我们外部加我们想要的上拉电平,从而实现电平的转换。

6.png

上图为推挽输出设置为高电平输出时的信号流,IO端口的上部分的PMOS管导通,下部的NMOS管断开;输出高电平驱动外部电路;推挽输出的驱动能力也是在15mA左右,特殊说明的端口除外;它们也是可以配置上下拉电阻的,但是对于推挽输出模式,上下拉电阻的作用并不实用,反而增加端口的功耗。

7.png

上图为推挽式复用功能,它是由片上外设功能模块进行驱动的输出,其他的功能跟普通推挽的原理是一样的;主要是它复用到哪个内部模块里去,比如UART串口、SPI串口等等;他们也都是可以设置上、下拉电阻的;在UART总线上还是有作用的。

8.png

上图为开漏式复用功能,它也是由片上外设模块所驱动,其他部分则跟普通的开漏输出原理一致,也都可以设置上下拉电阻;比如内部连到I2C功能模块,那配置了上拉电阻后,就是可以在端口总线上级联多个I2C的设备进行通信了。

前面详细说明了GPIO口的工作原理,但都还没有真正进入今天的正题,他们跟低功耗设计有什么关系?其实不然,我们只有深度了解了GPIO口的工作原理才能合理规范的使用它。接下来我们将开发实践中积累的低功耗设计的经验分享给大家:

(1)注意MCU的选型,这个非常重要的,同一个品牌的芯片里也有普通功耗和低功耗的芯片(价格会贵一点点,但是对于整体设计来说,还是值得的)

(2)MCU内部的功能模块,没有用到的,全部关闭;当你只用到一个简单的输出控制,那内部的什么ADC、UART、I2C等等都是不用的,这些在功能模块使能时都可以关闭。

(3)合理设置IO的输入、输出模式;合理设置上拉下拉电阻。这个就是上面花那么大篇幅介绍工作原理的目的。当外部电路初始状态为高,那如果时输入IO口,则不需要再设置上拉;如果是输出属性,则需要把输出设置为高,这样在端口上就不会出现损耗。注意没有使用的端口也是进行初始化配置,让这些端口处于最省电状态。

(4)合理使用GPIO口的休眠、唤醒功能;现在先进一点的arm内核的MCU,都已经具备端口休眠功能,可通过电平或边沿状态变化进行唤醒,这样可以节约MCU运行功耗。

(5)在设计程序时,使用合适的工作主频,决定MCU的功耗水平,并不是主频越高越好,能满足设计的需要即可;另外在设计GPIO口的输入功能时,能使用中断触发的,就不要采用定期查询的方式,定期查询的方式对于MCU的功耗来说肯定是大于中断触发式的。

(6)我们一般在应用GPIO口时,都是用来驱动一些外围电路,最简单的就是LED状态灯,千万别小看这个LED灯的设计,或者一个最基本的设计也会让你多付出 1~2mA的功耗,对于一个超低功耗的设计来说,也是不能忍受的。因此不要忽略每个细节外围电路,在选型LED灯器件时,选择高转换效率的高亮灯,只需要0.5mA就能让它的亮度满足指示所需,而且如果能设计成间歇式指示(如呼吸灯),就不要设计成常亮式指示;这些都是节能的细节设计。

(7)外部功能模块如果不是需要持续工作的电路,都可以采用带控制端的方式,需要时将其打开,不需要时,关闭它的电源。下面举个最常见的一个NTC热敏电阻采样电路的设计:

9.png

上图为普通的设计电路,能实现功能,但是这个分压电路在时时刻刻损耗着功率;我们优化成如下:

10.png

上图利用一个GPO口,设置为开漏输出,初始化为开漏输出高,无上下拉,这时候电路是不起作用的,也不会有功率损耗;当需要进行温度采集时,可以将GPO设置为输出低,这时候,电路起作用了,可以通过GPI去检测ADC的电压;实现对温度的检测,这个电路里省略了中间的一些防护和滤波电路,只是想给大家说明一个节约功耗的机理,希望大家是实际设计电路时能综合全面的考虑问题。

其实低功耗设计,它涉及到MCU及外围电路的全面配合,肯定不只是GPIO这个功能模块所能覆盖的,但是所有的设计理念应该都是贯穿在我们每个设计环节里面的,因此,从小做起,让我们的产品指标更加的优秀,这就是我们开发人员的追求,欢迎各位小伙伴们留下你们宝贵的经验,可以私信交流。

来源:电路开发实战小课堂

免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。

围观 661

GPIO特性

  • 最大封装(64pin)具有55个多功能双向的I/O口

  • 所有I/O口都可以映射到16个外部中断

  • 几乎所有I/O口可容忍5V输入信号

  • 所有I/O口均为快速I/O,寄存器存取速度最高fAHB

  • I/O引脚的外设功能可以通过一个特定的操作锁定,以避免意外的写入I/O寄存器

  • 每个GPIO引脚都可以由软件配置成输出(推挽或开漏)、输入(带或不带上拉或下拉)或复用的外设功能端口

  • 可选的每个I/O口的电流推动/吸入能力

  • GPIO设置/清除寄存器(GPIOx_SCR)和GPIO清除寄存器(GPIOx_CLR)为GPIOx_ODT寄存器提供位访问能力

GPIO

GPIO在复位期间和刚复位后,复用功能未开启,大部分I/O端口被配置成浮空输入模式。当作为输出配置时,写到输出数据寄存器(GPIOx_ODT)上的值会输出到相应的I/O引脚。可以以推挽模式或开漏模式(仅低电平被驱动,高电平表现为高阻)使用输出驱动器。输入数据寄存器(GPIOx_IDT)在每个AHB时钟周期捕捉I/O引脚上的数据。所有GPIO引脚有一个内部弱上拉和弱下拉,它们被激活或断开有赖于GPIOx_PULL寄存器的值。

1.png

图1. GPIO基本结构

2.png

表1. GPIO配置表

GPIO toggle

AT32F425提供的I/O口均为快速I/O,寄存器存取速度最高为fAHB,所以可以看到GPIO翻转频率能够轻松达到48MHz:

3.png

图2. I/O翻转速度

IO引脚的5V or 3.3V容忍

一、标准3.3V容忍引脚(TC)

所有振荡器用到的引脚都是标准3.3V容忍引脚。

  • PA9–PA12(TSSOP20封装的PA9/10引脚不具FT 5V电平容忍特性)

  • PC14/PC15(LEXT_IN/OUT)

  • PF0/PF1(HEXT_IN/OUT)

4.png

表2. TC引脚示例

二、带模拟功能5V容忍引脚(FTa)

ADC占用端口为带模拟功能5V容忍引脚。

  • PA0–PA7

  • PB0–PB2

  • PC0–PC5

  • FTa引脚设置为输入浮空、输入上拉、或输入下拉时,具有5V电平容忍特性;设置为模拟模式时,不具5V电平容忍特性,此时输入电平必须小于VDD+0.3V

5.png

表3. FTa引脚示例

三、带20mA吸入能力5V容忍引脚(FTf)

部分I2C占用端口为带20mA吸入能力的5V容忍引脚,用以支持I2C的增强快速模式。

  • PB8–PB9

  • PB13–PB14

6.png

表4. FTf引脚示例

四、5V容忍引脚(FT)

其余的GPIO都为5V容忍引脚。

7.png

表5. FT引脚示例

IOMUX

I/O复用功能输入/输出

  • 大多数外设共享同一个GPIO引脚(比如PA0,可作为TMR1_EXT/USART2_CTS/I2C2_SCL/USART4_TX..)

  • 而对某个具体的GPIO引脚,在任意时刻只有一个外设能够与之相连

  • 某些外设功能还可以重映射到其他引脚,从而使得能同时使用的外设数量更多

选择每个端口线的有效复用功能之一是由两个寄存器来决定的,分别是GPIOx_MUXL和GPIOx_MUXH复用功能寄存器。可根据应用的需求用这两寄存器连接复用功能模块到其他引脚。

8.png

表6. 通过GPIOA_MUX寄存器配置端口A的复用功能

9.png

表7. 通过GPIOB_MUX寄存器配置端口B的复用功能

10.png

表8. 通过GPIOC_MUX寄存器配置端口B的复用功能

11.png

表9. 通过GPIOD_MUX寄存器配置端口D的复用功能

12.png

表10. 通过GPIOF_MUX寄存器配置端口F的复用功能

特殊I/O

一、调试复用引脚

  • 在复位时,和复位后不像其他GPIO一样处于浮空输入状态,而是处于复用模式

  • PA13:SWDIO,复用上拉

  • PA14:SWCLK,复用下拉

二、振荡器复用引脚

  • 振荡器关闭的状态下(复位后的默认状态),相关引脚可用作GPIO

  • 振荡器使能状态下,相应引脚的GPIO配置无效

  • 振荡器处于bypass模式(使用外部时钟源)时,LEXT_IN/HEXT_IN为振荡器时钟输入引脚,LEXT_OUT/HEXT_OUT可做GPIO使用

三、电池供电域下的引脚

  • 电池供电域下的引脚包括PC13、PC14以及PC15,电池供电域由VDD供电。

  • PC13可以作为通用I/O口、TAMPER引脚、ERTC校准时钟、ERTC闹钟或秒输出,PC14和PC15可以用于GPIO或LEXT引脚。(PC13至PC15作为I/O口的速度必须限制在2MHz以下,最大负载为30pF,而且这些I/O口绝对不能当作电流源)。

GPIO固件驱动程序API

Artery提供的固件驱动程序包含了一系列固件函数来管理GPIO的下列功能:

  • 初始化配置

  • 读取输入端口或某个输入引脚

  • 读取输出端口或某个输出引脚

  • 设置或清除某个引脚的输出

  • 锁定引脚

  • 引脚的复用功能配置

注:所有project都是基于keil5而建立,若用户需要在其他编译环境上使用,请参考AT32xxx_Firmware_Library_V2.x.x\project\at_start_xxx\templates中各种编译环境(例如IAR6/7,keil4/5)进行简单修改即可。

输出模式

GPIO提供了两种不同类型的输出模式分别是,推挽输出以及开漏输出,下面是输出模式的配置示例:

13.png

输入模式

GPIO提供了三种不同类型的输入模式分别是,浮空输入、上拉输入以及下拉输入,下面是输入模式的配置示例:

14.png

模拟模式

当需要使用ADC通道作为输入时,需要将相应的引脚配置为模拟模式,下面是模拟模式的配置示例:

15.png

复用模式

1. 不论使用何种外设模式,都必须将I/O配置为复用功能,之后系统才能正确使用I/O(输入或输出)。

2. I/O引脚通过复用器连接到相应的外设,该复用器一次只允许一个外设的复用功能(MUX)连接到I/O引脚。这样便可确保共用同一个I/O引脚的外设之间不会发生冲突。每个I/O引脚都有一个复用器,该复用器具有16路复用功能输入/输出(MUX0到MUX15),可通过gpio_pin_mux_config()函数对这些引脚进行配置:

  • 复位后,所有I/O都会连接到系统的复用功能0(MUX0)

  • 通过配置MUX1到MUX7可以映射外设的复用功能

3. 除了这种灵活的I/O复用架构之外,各外设还具有映射到不同I/O引脚的复用功能,这可以针对不同器件封装优化外设I/O功能的数量;例如,可将USART2_TX引脚映射到PA2或PA14引脚上。

4. 配置过程:

  • 使用gpio_pin_mux_config()函数将引脚连接到所需的外设复用功能(MUX),例如配置PA0作为TMR1_EXT输入gpio_pin_mux_config(GPIOA,GPIO_PINS_SOURCE0,GPIO_MUX_4);

  • 使用GPIO_Init()函数配置I/O引脚:

- 通过以下方式配置复用功能模式下的所需引脚

gpio_init_struct.gpio_mode=GPIO_MODE_MUX;

- 通过以下成员选择类型、上拉/下拉和驱动力

gpio_out_type、gpio_pull和gpio_drive_strength成员

根据上述配置过程,下面将介绍几种外设的常用配置示例。

一、USART I/O复用模式配置

16.png

二、TMR I/O复用模式配置

17.png

三、I2C I/O复用模式配置

18.png

来源:AT32 MCU 雅特力科技

免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。

围观 290

GPIO特性

  • 最大封装(144pin)具有116个多功能双向的I/O口;

  • 所有I/O口都可以映射到16个外部中断;

  • 绝大部分I/O口可容忍5V输入信号;

  • 所有I/O口均为快速I/O,寄存器存取速度最高fAHB;

  • I/O引脚的外设功能可以通过一个特定的操作来开启写保护,以避免意外的写入I/O寄存器;

  • 每个GPIO引脚都可以由软件配置成输出(推挽或开漏)、输入(带或不带上拉或下拉)或复用的外设功能端口;

  • 可选的每个I/O口的电流推动/吸入能力;

  • 端口位设置/清除寄存器(GPIOx_SCR)和端口位清除寄存器(GPIOx_CLR)为GPIOx_ODT寄存器提供位访问能力。

GPIO

GPIO在复位期间和刚复位后,复用功能未开启,大部分I/O端口被配置成浮空输入模式。

当作为输出配置时,写到输出数据寄存器(GPIOx_ODT)上的值会输出到相应的I/O引脚。可以以推挽模式或开漏模式(仅低电平被驱动,高电平表现为高阻)使用输出驱动器。

输入数据寄存器(GPIOx_IDT)在每个AHB时钟周期捕捉I/O引脚上的数据。

所有GPIO引脚有一个内部弱上拉和弱下拉,它们被激活或断开有赖于GPIOx_PULL寄存器的值。

1.png

图1. I/O端口位的基本结构

2.png

表1. I/O端口位配置表

GPIO toggle

AT32F435/437提供的I/O口均为快速I/O,寄存器存取速度最高为fAHB,所以可以看到在主频为240MHz时,GPIO翻转频率能够轻松达到120MHz:

3.png

图2. I/O翻转速度

IO引脚的5V or 3.3V容忍

标准3.3V容忍引脚(TC)

所有振荡器和USB_OTG用到的引脚都是标准3.3V容忍引脚。

  • PC14/PC15(LEXT_IN/OUT)

  • PH0/PH1(HEXT_IN/OUT)

  • PA11/PA12(OTGFS1_D-/D+)

  • PB14/PB15(OTGFS2_D-/D+)

4.png

表2. TC引脚示例

带模拟功能5V容忍引脚(FTa)

ADC占用端口为带模拟功能5V容忍引脚。

  • PA0–PA7,PB0–PB1,PC0–PC5,PF3–PF10

  • FTa引脚设置为输入浮空、输入上拉、或输入下拉时,具有5V电平容忍特性;设置为模拟模式时,不具5V电平容忍特性,此时输入电平必须小于VDD+0.3V

5.png

表3. FTa引脚示例

带20mA吸入能力5V容忍引脚(FTf)

部分I2C可提供带20mA吸入能力的5V容忍引脚。

6.png

表4. FT引脚示例

5V容忍引脚(FT)

其余的GPIO都为5V容忍引脚。

7.png

表5. FT引脚示例

IOMUX

I/O复用功能输入/输出

  • 大多数外设共享同一个GPIO引脚(比如PA0,可作为TMR2_CH1/TMR2_EXT/TMR5_CH1/TMR8_EXT/I2C2_SCL/USART2_CTS)

  • 而对某个具体的GPIO引脚,在任意时刻只有一个外设能够与之相连

  • 某些外设功能还可以重映射到其他引脚,从而使得能同时使用的外设数量更多

选择每个端口线的有效复用功能之一是由两个寄存器来决定的,分别是GPIOx_MUXL和GPIOx_MUXH复用功能寄存器。可根据应用的需求用这两寄存器连接复用功能模块到其他引脚。

8.png9.png

表6. 通过GPIOA_AFR寄存器配置端口A的复用功能

10.png

11.png

表7. 通过GPIOB_AFR寄存器配置端口B的复用功能

12.png

13.png

表8. 通过GPIOF_AFR寄存器配置端口C的复用功能

14.png

15.png

表9. 通过GPIOF_AFR寄存器配置端口D的复用功能

16.png

17.png

表10. 通过GPIOF_AFR寄存器配置端口E的复用功能

18.png

19.png

表11. 通过GPIOF_AFR寄存器配置端口F的复用功能

20.png

21.png

表12. 通过GPIOF_AFR寄存器配置端口G的复用功能

22.png

23.png

表13. 通过GPIOF_AFR寄存器配置端口H的复用功能

特殊I/O

调试复用引脚

在复位时,和复位后不像其他GPIO一样处于浮空输入状态,而是处于AF模式

  • PA13:JTMS/SWDIO,AF上拉

  • PA14:JTCK/SWCLK,AF下拉

  • PA15:JTDI,AF上拉

  • PB3:JTDO/SWO,AF浮空

  • PB4:JNTRST,AF上拉

振荡器复用引脚

  • 振荡器关闭的状态下(复位后的默认状态),相关引脚可用作GPIO

  • 振荡器使能状态下,相应引脚的GPIO配置无效

  • 振荡器处于bypass模式(使用外部时钟源)时,HEXT_IN/LEXT_IN为振荡器时钟输入引脚,HEXT_OUT/LEXT_OUT可做GPIO使用

电池供电域引脚

  • 电池供电域引脚包括PC13、PC14以及PC15。电池供电域由VDD或VBAT引脚供电,当VDD主 电源被切断时,电池供电域自动切换至VBAT引脚供电,以保障ERTC正常工作。

  • 当电池供电域由VDD供电时,PC13可以作为通用I/O口、TAMPER引脚、ERTC校准时钟、ERTC闹钟或秒输出,PC14和PC15可以用于GPIO或LEXT引脚。(PC13至PC15作为I/O口的速度必须限制在2MHz以下,最大负载为30pF,而且这些I/O口绝对不能当作电流源)。

  • 当电池供电域由VBAT供电时,PC13可以作为TAMPER引脚、ERTC闹钟或秒输出,PC14和PC15只能用于LEXT引脚。

GPIO固件驱动程序API

Artery提供的固件驱动程序包含了一系列固件函数来管理GPIO的下列功能:

  • GPIO寄存器复位

  • 初始化配置

  • 读取输入端口或某个输入引脚

  • 读取输出端口或某个输出引脚

  • 设置或清除某个引脚的输出

  • 锁定引脚

  • 引脚的复用功能配置

输出模式

GPIO提供了两种不同类型的输出模式分别是,推挽输出以及开漏输出,下面是输出模式的配置示例:

24.png

输入模式

GPIO提供了三种不同类型的输入模式分别是,浮空输入、上拉输入以及下拉输入,下面是输入模式的配置示例:

25.png

模拟模式

当需要使用ADC或COMP通道作为输入时,需要将相应的引脚配置为模拟模式,下面是模拟模式的配置示例:

26.png

复用模式

  1. 不论使用何种外设模式,都必须将I/O配置为复用功能,之后系统才能正确使用I/O(输入或输出)。

  2. I/O引脚通过复用器连接到相应的外设,该复用器一次只允许一个外设的复用功能(IOMUX)连接到I/O引脚。这样便可确保共用同一个I/O引脚的外设之间不会发生冲突。每个I/O引脚都有一个复用器,该复用器具有16路复用功能输入/输出(MUX0到MUX15),可通过gpio_pin_mux_config()函数对这些引脚进行配置:

    —复位后,所有I/O都会连接到系统的复用功能0(MUX_0)

    —通过配置MUX0到MUX15可以映射外设的复用功能

  3. 除了这种灵活的I/O复用架构之外,各外设还具有映射到不同I/O引脚的复用功能,这可以针对不同器件封装优化外设I/O功能的数量;例如,可将USART2_TX引脚映射到PA2或PA14引脚上。

  4. 配置过程:

      —使用gpio_pin_mux_config()函数将引脚连接到所需的外设复用功能,例如配置PA0作为                  TMR2_EXT输入

      gpio_pin_mux_config(GPIOA,GPIO_PINS_SOURCE0,GPIO_MUX_1);

      —使用gpio_init()函数配置I/O引脚:

          -通过以下方式配置复用功能模式下的所需引脚

          gpio_init_struct.gpio_mode=GPIO_MODE_MUX;

          -通过以下成员选择类型、上拉/下拉和驱动能力

          gpio_pull、gpio_out_type和gpio_drive_strength成员

根据上述配置过程,下面将介绍几种外设的常用配置示例。
USART I/O复用模式配置

27.png

TMR I/O复用模式配置

28.png

I2C I/O复用模式配置

29.png

案例 LED翻转

功能简介

通过系统时钟延时来对LED进行翻转。

资源准备

1) 硬件环境:

对应产品型号的AT-START BOARD

2) 软件环境:

project\at_start_f437\examples\gpio\led_toggle

软件设计

1) 配置流程

  • 配置系统时钟;

  • 初始化延时函数和LED;

  • 翻转LED。

2) 代码介绍

  • main函数代码描述

30.png

  • LED翻转代码描述

31.png

实验效果

  • 上电运行会看到LED2、LED3和LED4以间隔200ms时间交替的进行翻转。

案例 SWJTAG接口复用

功能简介

对SWJTAG接口的I/O进行复用。

资源准备

3) 硬件环境:

对应产品型号的AT-START BOARD

4) 软件环境:

project\at_start_f437\examples\gpio\swjtag_mux

软件设计

3) 配置流程

  • 配置系统时钟;

  • 初始化延时函数;


  • 配置SWJTAG接口的复用和USART2初始化。

4) 代码介绍

  • main函数代码描述

32.png

  • SWJ配置代码描述

33.png

实验效果


将PA13接示波器,PA14接入串口打印工具;

程序运行过程中PA13每隔500ms会翻转一次,表示jtms/swdio引脚已被用为GPIO使用;

PA14接入串口打印工具后,每隔500ms会看到USART2_TX打印主循环执行次数。

来源:AT32 MCU 雅特力科技
免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。

围观 765

GPIO(General-purpose input/output)是通用输入输出端口的简称,CKS32F4xx系列产品通过GPIO引脚与外部设备连接起来,从而实现与外部通讯、控制以及数据采集的功能。最基本的输出功能是由CKS32F4xx系列产品控制引脚输出高、低电平,实现开关控制,如把GPIO引脚接入到LED灯,那就可以控制LED灯的亮灭,引脚接入到继电器或三极管,那就可以通过继电器或三极管控制外部大功率电路的通断。最基本的输入功能是检测外部输入电平,如把 GPIO引脚连接到按键,通过电平高低区分按键是否被按下。

GPIO硬件结构框图

1.png

该图从最右端看起,最右端标注着“I/O”的就是代表CKS32F4xx系列产品引出的GPIO引脚,其余部件都位于芯片内部。引脚处的两个保护二级管可以防止引脚外部过高或过低的电压输入,当引脚电压高于VDD_FT时,上方的二极管导通,当引脚电压低于VSS时,下方的二极管导通,由此可以防止不正常电压引入芯片导致芯片的烧毁。这里要特别注意VDD_FT 代表IO口兼容3.3V和5V,如果没有标注“FT”,就代表着不兼容5V。在芯片数据手册的引脚定义中,会看到有“电平I/O”一列,有FT标注的即为支持5V,如下图所示:

2.png

标号1处是上拉、下拉电阻,从它的结构我们可以看出,通过上、下拉对应的开关配置,我们可以控制引脚默认状态下的电压,开启上拉的时候引脚电压为高电平,开启下拉的时候引脚电压为低电平。同时也可以设置“既不上拉也不下拉模式”,我们也把这种状态称为浮空模式。

标号2处是一个由P-MOS和N-MOS管组成的单元电路。这个结构使GPIO具有了“推挽输出”和“开漏输出”两种模式。上方的P-MOS管高电平导通,低电平关闭,下方的N-MOS低电平导通,高电平关闭。

标号3处是输出数据寄存器,它为标号2处的双MOS管结构电路提供输入控制信号,因此通过修改输出数据寄存器的值就可以修改GPIO引脚的输出电平。而图中“置位/复位寄存器 GPIOx_BSRR”可以通过修改输出数据寄存器的值从而影响电路的输出。

标号4处是复用功能输出,“复用”是指CKS32F4xx系列产品的其它片上外设可以对GPIO引脚进行控制,此时GPIO引脚用作该外设功能的一部分,算是第二用途。例如我们使用USART串口通讯时,需要用到某个GPIO引脚作为通讯发送引脚,这个时候就可以把该GPIO引脚配置成USART串口复用功能,由串口外设控制该引脚,发送数据。

标号5处是输入数据寄存器,它连接到图中的TTL施密特触发器,触发器的基本原理是当输入电压高于正向阈值电压时,输出为高;当输入电压低于负向阈值电压时,输出为低;IO口信号经过触发器后,模拟信号转化为0和1的数字信号,也就是高低电平,并且是TTL电平协议, 然后存储在“输入数据寄存器。因此,通过读取该寄存器就可以了解GPIO引脚的电平状态。

标号6处是复用功能输入,与“复用功能输出”模式类似,同样,如果我们使用USART串口通讯时,需要用到某个GPIO引脚作为通讯接收引脚,这个时候就可以把该GPIO引脚配置成USART串口复用功能,使USART可以通过该通讯引脚接收远端数据。

标号7处是模拟输入输出,当 GPIO引脚用于ADC采集电压的输入通道时,用作“模拟输入”功能,此时信号是不经过施密特触发器的,因为经过施密特触发器后信号只有0、1 两种状态,所以ADC外设要采集到原始的模拟信号,信号源输入必须在施密特触发器之前。同样的,当 GPIO引脚用于DAC作为模拟电压输出通道时,此时作为“模拟输出”功能,DAC 的模拟信号输出就不经过双MOS管结构了,在GPIO结构框图的右下角处,模拟信号直接输出到引脚。

GPIO的工作模式

- 4种输入模式 -

1.浮空输入

浮空输入状态下,IO的电平状态是不确定的,完全由外部输入决定。如果在该引脚悬空的情况下,读取该端口的电平是不确定的。

2.上拉输入

在该模式下,如果IO口外部没有信号输入或者引脚悬空,IO口默认为高电平。如果I/O口输入低电平,那么引脚就为低电平,MCU读取到的就是低电平。

3.下拉输入

在该模式下如果IO口外部没有信号输入或者引脚悬空,IO口默认为低电平,如果I/O口输入高电平,那么引脚就为高电平,MCU读取到的就是高电平。

4.模拟功能

当GPIO用于模拟功能时,引脚的上、下拉电阻是不起作用的,这个时候即使配置了上拉或下拉模式,也不会影响到模拟信号的输入输出。除了ADC和DAC要将IO配置为模拟功能模式之外,其他外设功能一律要配置为复用功能模式。

- 4种输出模式 -

1.开漏输出(带上拉或者下拉)

在该模式下,若MCU控制输出为高电平1时,输出指令是不会起到作用的。此时I/O端口的电平就不会由输出的高电平决定,而是由I/O端口外部的上拉或者下拉决定,如果没有上拉或者下拉,IO口就处于高阻态。虽然通过软件设置内部上拉,也可以输出高电平,但是CKS32F4xx系列产品内部上拉是"弱上拉",即通过此上拉输出的电流是很弱的,驱动能力很弱。但是在该模式下,当MCU控制输出为低电平0时,即使没有上拉或者下拉,I/O端口也会输出低电平。另一方面,在开漏模式下,施密特触发器是打开的,即输入可用,可以通过输入数据寄存器GPIOx_IDR读取I/O的实际状态。开漏输出主要有以下两点作用:

a. I/O端口设置成开漏输出模式时,可以用来连接不同电平的器件,用来匹配电平,因为开漏引脚不连接外部的上拉电阻时,只能输出低电平,如果需要同时具备输出高电平的功能,则需要接上拉电阻。因此我们通过改变上拉电源的电压,便可以改变传输电平。比如通过加上上拉电阻就可以提供TTL电平-CMOS电平的输出。

b.当多个设置为开漏输出的引脚连接到一条总线上时。通过外加一个上拉电阻,在不增加任何器件的情况下,这些引脚形成了“与逻辑”关系,即“线与”。如果有一个引脚输出为逻辑0,相当于接地,那么与之并联的回路“相当于被一根导线短路”,所以总线上的逻辑电平便为0。只有都为高电平时,总线上的电平才为1。在IIC通信中,引脚通常设置为开漏输出模式。

2.复用开漏输出(带上拉或者下拉)

此时GPIO复用为其他外设,输出数据寄存器GPIOx_ODR无效;即输出的高低电平来源于其它外设,除了输出信号的来源改变之外,其他的与开漏输出功能相同。

3.推挽输出(带上拉或者下拉)

在该模式下,如果我们控制输出为0,低电平,则I/O端口的电平就是低电平。若控制输出为1,高电平,则I/O端口的电平就是高电平。此时,外部上拉和下拉的作用是控制在没有输出IO口的默认电平。在该模式下,施密特触发器也是打开的,即输入可用,通过输入数据寄存器GPIOx_IDR可读取I/O的实际状态。

4.复用推挽输出(带上拉或者下拉)

此时GPIO复用为其他外设,输出数据寄存器GPIOx_ODR无效;即输出的高低电平来源于其它外设,除了输出信号的来源改变之外,其他的与推挽输出功能相同。

- 4种输出速度 -


1. 2MHZ(低速)

2. 25MHZ(中速)

3. 50MHZ(快速)

4. 100MHZ(高速)

GPIO的引脚速度又称输出驱动电路的响应速度,即一个驱动电路可以不失真地通过信号的最大频率。比如信号频率为10MHz,而我们把GPIO速度配置成了2MHz,则10MHz的方波很可能就变成了正弦波,发生了失真。芯片内部在I/O口的输出部分安排了多个响应速度不同的输出驱动电路,我们可以根据自己的需要选择合适的驱动电路,通过选择速度来选择不同的输出驱动模块,达到最佳的噪声控制和降低功耗的目的。因为GPIO口的速度配置越高,噪声越大,功耗也越大。比如在USART串口通信中,若最大波特率只需115.2k,那用2M的速度就够了,既省电,噪声也小;而在SPI接口中,若使用18M或9M的波特率,则需要选用50M的GPIO的引脚速度。

注意:CKS32F407用于配置PA13输出速度的GPIOA_OSPEEDR寄存器的端口 13配置位(GPIOA_OSPEEDR_OSPEEDR13[1:0])初始值为00,即PA13的I/O输出速度默认为低速。因此客户在实际使用CKS32F407时,要按照参考手册中的介绍来配置GPIOA_OSPEED寄存器中 OSPEEDR13[1:0]位,从而来选择PA13的I/O输出指定的速度。

GPIO的配置

接下来我们讲解如何利用CKS32F4xx系列固件库对GPIO口的工作模式进行配置。首先,固件库中定义了一个如下的结构体:

typedef struct
{  
  uint32_t GPIO_Pin;               
  GPIOMode_TypeDef GPIO_Mode;      
  GPIOSpeed_TypeDef GPIO_Speed;    
  GPIOOType_TypeDef GPIO_OType;    
  GPIOPuPd_TypeDef GPIO_PuPd;    
} GPIO_InitTypeDef;

通过对该结构体成员中各个变量的初始化,就可以完成对2.2小节中所讲的GPIO口的工作模式配置。结构体中各个成员变量的介绍及初始化时可被赋的值如下:

1)GPIO_Pin:用来选择要控制的GPIO引脚,在标准库函数中可选择的值及其定义如下:

#define GPIO_Pin_0                 ((uint16_t)0x0001) 
#define GPIO_Pin_1                 ((uint16_t)0x0002) 
#define GPIO_Pin_2                 ((uint16_t)0x0004)  
#define GPIO_Pin_3                 ((uint16_t)0x0008) 
#define GPIO_Pin_4                 ((uint16_t)0x0010)  
#define GPIO_Pin_5                 ((uint16_t)0x0020)  
#define GPIO_Pin_6                 ((uint16_t)0x0040) 
#define GPIO_Pin_7                 ((uint16_t)0x0080)  
#define GPIO_Pin_8                 ((uint16_t)0x0100) 
#define GPIO_Pin_9                 ((uint16_t)0x0200)  
#define GPIO_Pin_10                ((uint16_t)0x0400) 
#define GPIO_Pin_11                ((uint16_t)0x0800) 
#define GPIO_Pin_12                ((uint16_t)0x1000) 
#define GPIO_Pin_13                ((uint16_t)0x2000) 
#define GPIO_Pin_14                ((uint16_t)0x4000)  
#define GPIO_Pin_15                ((uint16_t)0x8000) 
#define GPIO_Pin_All               ((uint16_t)0xFFFF)

2) GPIO_Mode:用来设置已经选择的GPIO引脚的模式,在标准库函数中可选择的值及其定义如下:

typedef enum
{   
   GPIO_Mode_IN   = 0x00, /*!设置为输入模式 */  
   GPIO_Mode_OUT  = 0x01, /*!设置为输出模式*/  
   GPIO_Mode_AF   = 0x02, /*!设置为复用模式 */  
   GPIO_Mode_AN   = 0x03  /*!设置为模拟模式*/
}GPIOMode_TypeDef;

3) GPIO_Speed:用来设置已经选择的GPIO引脚的速度,在标准库函数中可选择的值及其定义如下:

#define  GPIO_Speed_2MHz    GPIO_Low_Speed    
#define  GPIO_Speed_25MHz   GPIO_Medium_Speed 
#define  GPIO_Speed_50MHz   GPIO_Fast_Speed 
#define  GPIO_Speed_100MHz  GPIO_High_Speed

4) GPIO_OType:用来设置已经选择的GPIO引脚的输出模式,只有输出模式才需要该配置,输入模式下不需要该配置。在标准库函数中可选择的值及其定义如下:

typedef enum
{   
   GPIO_OType_PP = 0x00, /*!设置为推挽输出模式 */  
   GPIO_OType_OD = 0x01  /*!设置为开漏输出模式 */
}GPIOOType_TypeDef;

5) GPIO_PuPd:用来设置已经选择的GPIO引脚的上下拉,在标准库函数中可选择的值及其定义如下:

typedef enum
{   
   GPIO_PuPd_NOPULL = 0x00, /*!设置为既不上拉也不下拉/浮空模式 */  
   GPIO_PuPd_UP     = 0x01, /*!设置为上拉模式*/  
   GPIO_PuPd_DOWN   = 0x02  /*!设置为下拉模式*/
}GPIOPuPd_TypeDef;

根据上面所讲解的配置方法,我们讲解标准库下的3个实际配置实例。

1) 作为普通的GPIO口输出,控制LED灯的亮灭,其GPIO口初始化函数如下:

void LED_GPIO_Init(void)
{         
   GPIO_InitTypeDef  GPIO_InitStructure;  
   RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF,ENABLE);//使能GPIOF时钟  
   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;//LED1对应的IO口  
   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//输出模式  
   GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽模式  
   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//速度100MHz  
   GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉  
   GPIO_Init(GPIOF, &GPIO_InitStructure);//初始化GPIO
}

2) 复用为CAN外设的输出。

void CAN1_GPIO_Init(void)
{    
   GPIO_InitTypeDef         
   GPIO_InitStructure;   
   RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);//使能PORTA时钟                                          
   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11| GPIO_Pin_12;    
   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;  //复用功能    
   GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出    
   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz    
   GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;  //上拉    GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化PA11,PA12   
   GPIO_PinAFConfig(GPIOA,GPIO_PinSource11,GPIO_AF_CAN1); //GPIOA11复用为CAN1   
   GPIO_PinAFConfig(GPIOA,GPIO_PinSource12,GPIO_AF_CAN1); //GPIOA12复用为CAN1

3) 当ADC采集的输入通道,作为普通模拟输入。

void  ADC_Init(void)
{      
    GPIO_InitTypeDef  GPIO_InitStructure;  
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);//使能GPIOA时钟  
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;//PA5 通道5  
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;//模拟输入  
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ;//不带上下拉  
    GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化
 }

来源:中科芯MCU

免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。

围观 129

相关文章:

敏矽微电子Cortex-M0学习笔记01——芯片简介

敏矽微电子Cortex-M0学习笔记02——Cortex-M0开发环境的建立及调试

敏矽微电子Cortex-M0学习笔记03——时钟系统设计例程

前面我们已经对敏矽微电子的基于cortex m0内核的ME32F030R8T6的基本功能做了介绍,然后详细讲解了开发环境MDK的安装,pack包的安装,工程的建立及程序的仿真,紧接着讲解了ME32F030R8T6的时钟系统。

如果说前面都是基础知识介绍和理论讲解,那么从本篇开始,我们不但会有理论介绍,还会结合敏矽微电子提供的ME32F030R8T6开发板进行实例验证,让大家看到实例运行的结果。

1、敏矽微电子Cortex-M0学习笔记04-GPIO详解及实例

首先我们对ME32F030R8T6芯片的I/O端口做一个大致的了解。

1.1. ME32F030R8T6的GPIO 概述

ME32F030R8T6提供多达 57个 GPIO 管脚。主要的特点有:

• 数字管脚可以用软件定义为输入或输出

• 管脚读写可以被屏蔽

• 多个管脚的置位、清零位用一条指令实现

• 管脚的输出取反

• 每一个管脚可作为外部中断信号

• 可编程的中断触发条件及中断优先级

• 所有GPIO管脚在复位后被配置成带上拉电阻的输入管脚

MCU的端口除了灵活易用的特点之外,其外设功能也十分强大。管脚功能由IO控制寄存器配置,除电源管脚,其余管脚均可复用。

系统复位后,管脚功能将被设置成默认值。

GPIO可以复用的功能如下:LED/LCD 驱动、触摸按键、ADC、定时器6输出、定时器7输出、PWM输出、UART0、UART1、SPI、I2C。

在使用端口复用功能时,通过 I/O 配置寄存器IOCON对端口进行功能设置,每个引脚对应的复用功能,请参照官方数据手册的详细说明。

1.2. ME32F030R8T6的GPIO 寄存器详解

对于使用过单片机开发产品的人来说,功能繁多、名称各异的寄存器应该是最重要也是最令人头疼的地方,但是没办法,想要开发出功能稳定、性能可靠的产品,就必须来硬着头皮查看各个寄存器的功能和配置方法。

当然,对于ME32F030R8T6来说,官方推出了库函数,利用这些库函数可以不必关心具体的寄存器就能编写出合适的程序。但是对于想要深入了解的话,还是要看看寄存器的。

1.2.1. GPIO功能配置寄存器

MCU有一系列的GPIO 控制寄存器来实现对I/O口的灵活控制。

首先我们对 I/O 配置寄存器IOCON做一个介绍,因为它决定着I/O端口的功能选择和电气特性。所以需要拿出来单独进行介绍,该寄存器的每一位的功能如下:

1.png

2.png

①、管脚功能:

IOCON 寄存器中的 FUNC 位可设为 GPIO (FUNC = 000) 或外设功能。如果将管脚配置为 GPIO 管脚,则 DIR 寄存器决定管脚是配置为输入还是输出。对于任何外设功能,会根据管脚的功能自动控制管脚方向。GPIO 的 DIR 寄存器此时对外设功能无效。

②、管脚模式:

IOCON 寄存器的 MODE 位允许为每个管脚使能或禁止片内上拉电阻。默认情况下,所有管脚的上拉电阻都被使能。

③、管脚驱动

对于每个正常驱动管脚,可以选择两种电流的输出驱动,即低电流模式和高电流模式。用户可以根据自己的实际需求进行选择。

④、开漏模式

所有数字 I/O 管脚都可为开漏模式。但是请注意,该模式并不是真正的开漏模式。输入上拉至不能超过VDD。

⑤、模式功能

I/O 管脚可以配置为模拟功能,作为模拟信号的输出和输入管脚,例如复用为AD口的时候,可以作为检测电压的模拟信号输入口。

⑤、电压转换速率模式

在作用AD转换口的时候,可以设置端口转换速率为快速模式或慢速模式,默认为快速模式。相对于快速模式,慢速模式转满时间会变长,随之电流功耗也增大,但时精度会更加精确,根据应用场景和自身需求,选择适合的模式。

1.2.2. GPIO控制寄存器总览

MCU所有的GPIO被分布到4个端口: PA,、PB、PC、PD。每个端口都拥有自己独立的控制寄存器去管理 GPIO 的功能。下面的表列出了所有的寄存器以供参考。接下来会对每个寄存器做出详解。

3.png

4.png

1.2.3. GPIO屏蔽寄存器

GPIO屏蔽寄存器可屏蔽下列寄存器的读和写操作:PIN、OUT、SET、CLR和 NOT,相当于给这些寄存器上了个“锁”,只有当MASK寄存器相应的BIT位被置 0,被屏蔽的寄存器才能进行读和写操作。该寄存器上电后默认为0,即不进行GPIO屏蔽。当配置1启动屏蔽功能后,对处于输出功能的端口进行任何写操作都无效,不会改变其当前的输出状态。对处于输入功能的端口进行读操作,无论此时端口处于何种电平,都会返回0.

1.2.4. GPIO管脚值寄存器

对配置为数字功能的端口,对该寄存器进行读操作将返回管脚的当前逻辑值,不管该管脚是配置为输入还是输出,也不管它是配置为 GPIO 还是任何其它适用的备用数字功能,都可以直接进行读取。但也有例外,在以下两种情况中, PIN 寄存器中读出的管脚值无效:①、如果选择了管脚的模拟功能(如适用),则不能读取管脚状态,例如将管脚选作 ADC 输入会断开管脚的数字功能。②、该引脚被GPIO屏蔽寄存器MASK给屏蔽了。

1.2.5. GPIO管脚输出寄存器

在没有被GPIO屏蔽寄存器MASK屏蔽的情况下,向该寄存器写0或1将在相应端口管脚产生低电平或高电平。但是对于所有其他配置(输入、非GPIO功能),OUT 寄存器位的值对管脚输出电平无效。读取该寄存器将返回 GPIO 输出寄存器的内容,不管数字管脚配置和方向如何。

通过SET、CLR和NOT寄存器可以对OUT 寄存器执行写操作,允许按位对单个端口管脚进行置位、清除、取反操作。以此来控制OUT 寄存器的输出内容。

1.2.6. GPIO管脚输出置位寄存器

在没有被GPIO屏蔽寄存器MASK屏蔽,端口DIR为输出方向且端口功能为数字GPIO功能的情况下,写1会将相应端口管脚设为高电平。写0对GPIO输出电平无效。另外该寄存器为只写寄存器,对其进行读操作是无效的。

1.2.7. GPIO管脚输出清除寄存器

在没有被GPIO屏蔽寄存器MASK屏蔽,端口DIR为输出方向且端口功能为数字GPIO功能的情况下,写1会将相应端口管脚设为低电平。写0对GPIO输出电平无效。另外该寄存器为只写寄存器,对其进行读操作是无效的。

1.2.8. GPIO管脚输出取反寄存器

在没有被GPIO屏蔽寄存器MASK屏蔽,端口DIR为输出方向且端口功能为数字GPIO功能的情况下,写1会将相应端口的输出状态进行反转。写0对GPIO输出电平无效。另外该寄存器为只写寄存器,对其进行读操作是无效的。

1.2.9. GPIO数据方向寄存器

在使用数字GPIO前,首先要确定的就是端口的数据方向。向该寄存器写1会将端口设置为输出模式,写0设置为输入模式。上电后端口默认为输入状态。

1.2.10. GPIO中断感应寄存器

在文章开头的介绍中,我们就说过MCU的每一个管脚是可以作为外部中断信号。因此会有一系列与之对应的中断管理寄存器,来对每个端口的中断进行管理。这就是接下来要介绍的中断寄存器。在使用端口中断前,需要明确需要触发中断的条件。向中断感应寄存器ISENSEx写入1,端口中断的触发方式为电平中断。向寄存器写入0,端口中断的触发方式为沿中断触发,具体需要什么样的沿来触发,这个还需要下面的中断配置寄存器来设置。

1.2.11. GPIO中断配置寄存器

紧接上文,在明确使用端口触发方式为沿中断触发后,我们通过中断配置寄存器IBEx来选择沿触发条件。向寄存器写入1,管脚的上升沿和下降沿都触发中断。向寄存器写入0,管脚只能由上升沿或下降沿中的一种来触发中断,具体由哪种来触发,需要下面的中断事件寄存器IEVx来决定。

1.2.12. GPIO中断事件寄存器

当中断配置寄存器IBEx值为0时,中断事件寄存器就决定着中断触发的条件,向寄存器写1,上升沿触发中断。写入0,则下降沿产生中断。

1.2.13. GPIO中断屏蔽寄存器

在实际的开发过程中,使用到中断功能的端口毕竟是少数,因此MCU在上电后就默认屏蔽了端口的中断功能。如果要开启端口的中断功能,向对应的寄存器位写1即可。

1.2.14. GPIO原始中断状态寄存器

该寄存器的位读出为高时反映了对应管脚上的原始(屏蔽之前)中断状态,表示在触发 IE 之前所有的要求都满足。位读出为0时表示对应的输入管脚还未启动中断。该寄存器为只读。

1.2.15. GPIO中断状态寄存器

该寄存器中的位读为高反映了输入触发中断的状态。读出为低则表示对应的输入管脚没有中断产生,或者中断被屏蔽。读出为高则表示对应的输入管脚有中断产生。该寄存器为只读。

1.2.16. GPIO中断清除寄存器

在中断发生后,程序会进入中断服务子程序。在中断服务子程序中处理完中断程序后,需要向中断清除寄存器CLRx写1来清除中断标志。

1.3. ME32F030R8T6的GPIO 库函数函数

为了便于开发者快速上手,敏矽微电子官方例程中提供了gpio.c文件,其中包含了设置端口方向、读取端口状态、配置端口中断等函数,供开发者直接使用。

1、设置GPIO为输入方向

void GPIO_ConfigPinsAsInput(PA_Type *port, uint16_t pins)

{

    port->DIR &= ~pins;

    return;

}

2、设置GPIO位输出方向

void GPIO_ConfigPinsAsOutput(PA_Type *port, uint16_t pins)

{

    port->DIR|=pins;

    return;

}

3、设置GPIO输出高

void GPIO_SetPin(PA_Type *port, uint16_t pin)

{

    port->SET |= pin;

    return;

}

4、设置GPIO输出低

void GPIO_ResetPin (PA_Type *port, uint16_t pin)

{

    port->CLR |= pin;

    return;

}

5、设置GPIO输出反转

void GPIO_InvertOutPin (PA_Type *port, uint16_t pin)

{

    port->NOT |= pin;

    return;

}

6、读取GPIO某个引脚的输入状态

uint8_t GPIO_GetPinState(PA_Type *port, uint16_t pin)

{

    if (port->PIN & pin)

    return 1;

    else

    return 0;

}

7、读取GPIO整个引脚的输入状态

uint16_t GPIO_GetPortState(PA_Type *port)

{

    return (uint16_t)port->PIN;

}

8、屏蔽GPIO引脚

void GPIO_SetPortMask(PA_Type *port, uint16_t pins)

{

    port->MASK |= pins;

    return;

}

9、使能GPIO引脚

void GPIO_SetPortMask(PA_Type *port, uint16_t pins)

{

    port->MASK |= pins;

    return;

}

10、配置GPIO引脚的中断功能

void GPIO_EnableInt(PA_Type *port, uint16_t pin, uint8_t triggeredge)

{

	port->IS &= ~pin;


        port->IE |= pin;

    	switch(triggeredge)

    	{

    		case RISE_EDGE:
					port->IBE &= ~pin;

					port->IEV |= pin;			break;			case FALL_EDGE:

					port->IBE &= ~pin;

					port->IEV &= ~pin;			break;			case BOTH_EDGE:

					port->IBE |= pin;			break;				default:			break;



    		}

    		return;

}

11、清除GPIO引脚的中断标志

void GPIO_ClrInt(PA_Type *port, uint16_t pins)

{

    	port->IC =pins;

    	return;
}

1.4. ME32F030R8T6的GPIO 开发实例

介绍完GPIO的原理和函数,接下来就用最经典的LED小灯试验来进行示例。

本例使用敏矽微电子专门为ME32F030R8T6提供的库函数编写程序。

实例程序的代码如下:

int main(void

{
	WDT->MOD=0;          //关闭看门狗   

	PB_9_INIT(PB_9_GPIO);          //PB9(LED)设置为GPIO功能

	GPIO_ConfigPinsAsOutput(PB, IO_PIN9);  //PB9(LED)设置为输出方向

	while (1)

	{

		GPIO_InvertOutPin(PB, IO_PIN9);  //PB9(LED)端口输出反转

		SYS_DelaymS(500);                //延时500ms

	}

}

程序下载成功后,先在端口反转这句话处打上一个断点,然后全速运行程序(快捷键F5)。随后程序会停在断点处,

5.png

此时先暂停观察下LED小灯的状态,发现红圈标注的小灯并没有被点亮。

6.png

运行结果1

接下来单步运行程序,观察执行完端口反转这段程序后的状态。这时我们发现小灯已经被点亮了。

7.png


继续单步运行,当再次执行端口反转这段程序后,端口输出就会反转,接下来小灯就会熄灭。最后取消程序中的所有断点,让程序全速运行起来,小灯便开始周期性的闪烁。

来源:敏矽MCU

免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。

围观 234

GPIO特性

  • 最大封装(48pin)具有39个多功能双向的I/O口

  • 所有I/O口都可以映射到16个外部中断

  • 几乎所有I/O口可容忍5V输入信号(4个LEXT / HEXT引脚除外)

  • 所有I/O口均为快速I/O,寄存器存取速度最高fAHB

  • I/O引脚的外设功能可以通过一个特定的操作锁定,以避免意外的写入I/O寄存器

  • 每个GPIO引脚都可以由软件配置成输出(推挽或开漏)、输入(带或不带上拉或下拉)或复用的外设功能端口

  • 可选的每个I/O口的电流推动/吸入能力

  • GPIO设置/清除寄存器(GPIOx_SCR)和GPIO清除寄存器(GPIOx_CLR)为GPIOx_ODT寄存器提供位访问能力

GPIO

GPIO在复位期间和刚复位后,复用功能未开启,大部分I/O端口被配置成浮空输入模式。

当作为输出配置时,写到输出数据寄存器(GPIOx_ODT)上的值会输出到相应的I/O引脚。可以以推挽模式或开漏模式(仅低电平被驱动,高电平表现为高阻)使用输出驱动器。

输入数据寄存器(GPIOx_IDT)在每个AHB时钟周期捕捉I/O引脚上的数据。

所有GPIO引脚有一个内部弱上拉和弱下拉,它们被激活或断开有赖于GPIOx_PULL寄存器的值。

“图1.
图1. GPIO基本结构

“表1.
表1. GPIO 配置表

GPIO toggle

AT32F421提供的I/O口均为快速I/O,寄存器存取速度最高为fAHB,所以可以看到GPIO翻转频率能够轻松达到60MHz:

“图2.
<图2. I/O翻转速度

IO引脚的5V or 3.3V容忍

标准3.3V容忍引脚(TC)

所有振荡器用到的引脚都是标准3.3V容忍引脚。

  • PC14/PC15 (HEXT_IN/ OUT)
  • PF0/PF1 (LEXT_IN/ OUT)

“表2.
表2. TC引脚示例

带模拟功能5 V容忍引脚(FTa)

比较器输入引脚以及ADC占用端口为带模拟功能5 V容忍引脚。

  • PA0 – PA7
  • PB0 – PB2,PB12 – PB15
  • FTa引脚设置为输入浮空、输入上拉、或输入下拉时,具有5V电平容忍特性;设置为模拟模式时,不具5V电平容忍特性,此时输入电平必须小于VDD + 0.3V

“表3.
表3. FTa引脚示例

其余的GPIO都为5V容忍引脚。

“表4.
表4. FT引脚示例

IOMUX

I/O复用功能输入/输出

  • 大多数外设共享同一个GPIO引脚(比如PA0,可作为TMR1_EXT / USART2_CTS /I2C2_SCL / CMP_OUT)
  • 而对某个具体的GPIO引脚,在任意时刻只有一个外设能够与之相连
  • 某些外设功能还可以重映射到其他引脚,从而使得能同时使用的外设数量更多

选择每个端口线的有效复用功能之一是由两个寄存器来决定的,分别是GPIOx_MUXL和GPIOx_MUXH复用功能寄存器。可根据应用的需求用这两寄存器连接复用功能模块到其他引脚。

“表5.
表5. 通过GPIOA_MUX*寄存器配置端口A的复用功能

“表6.
表6. 通过GPIOB_MUX*寄存器配置端口B的复用功能

“表7.
表7. 通过GPIOF_MUX*寄存器配置端口F的复用功能

特殊I/O

调试复用引脚

  • 在复位时,和复位后不像其他GPIO一样处于浮空输入状态,而是处于复用模式
  • PA13:SWDIO,复用上拉
  • PA14:SWCLK,复用下拉

振荡器复用引脚

  • 振荡器关闭的状态下(复位后的默认状态),相关引脚可用作GPIO
  • 振荡器使能状态下,相应引脚的GPIO配置无效
  • 振荡器处于bypass模式(使用外部时钟源)时,LEXT_IN/HEXT_IN为振荡器时钟输入引脚,LEXT_OUT/HEXT_OUT可做GPIO使用

备份域引脚

  • 当1.2V区域断电(当器件进入待机模式)时,PC13/PC14/PC15失去GPIO功能。在这种情况下,若GPIO配置没有被RTC配置为bypass,则这些引脚被设为模拟输入模式。

  • 以下内容在F421系列不存在: 模拟开关(power switch)只能通过少量的电流(3mA),在输出模式下使用PC13/PC14/PC15的I/O口功能是有限制的:只能工作在适中电流推动/吸入能力模式下,最大负载为30pF,而且这些I/O口绝对不能当作电流源(如驱动LED)。

GPIO固件驱动程序API

Artery提供的固件驱动程序包含了一系列固件函数来管理GPIO的下列功能:

  • 初始化配置
  • 读取输入端口或某个输入引脚
  • 读取输出端口或某个输出引脚
  • 设置或清除某个引脚的输出
  • 锁定引脚
  • 引脚的复用功能配置

注:所有project都是基于keil 5而建立,若用户需要在其他编译环境上使用,请参考AT32xxx_Firmware_Library_V2.x.x\project\at_start_xxx\templates中各种编译环境(例如IAR6/7,keil 4/5)进行简单修改即可。

输出模式

GPIO提供了两种不同类型的输出模式分别是,推挽输出以及开漏输出,下面是输出模式的配置示例:

“AT32F421

输入模式

GPIO提供了三种不同类型的输入模式分别是,浮空输入、上拉输入以及下拉输入,下面是输入模式的配置示例:

“AT32F421

模拟模式

当需要使用ADC或CMP通道作为输入时,需要将相应的引脚配置为模拟模式,下面是模拟模式的配置示例:

“AT32F421

复用模式

1. 不论使用何种外设模式,都必须将I/O配置为复用功能,之后系统才能正确使用I/O(输入或输出)。

2. I/O引脚通过复用器连接到相应的外设,该复用器一次只允许一个外设的复用功能(MUX)连接到I/O引脚。这样便可确保共用同一个I/O引脚的外设之间不会发生冲突。每个I/O引脚都有一个复用器,该复用器具有16路复用功能输入/输出(MUX0到MUX15),可通过gpio_pin_mux_config()函数对这些引脚进行配置:

— 复位后,所有I/O都会连接到系统的复用功能0(MUX0)
— 通过配置MUX1到MUX7可以映射外设的复用功能

3. 除了这种灵活的I/O复用架构之外,各外设还具有映射到不同I/O引脚的复用功能,这可以针对不同器件封装优化外设I/O功能的数量;例如,可将USART2_TX引脚映射到PA2或PA14引脚上。

4. 配置过程:

— 使用gpio_pin_mux_config()函数将引脚连接到所需的外设复用功能(MUX),例如配置PA0作为TMR1_EXT输入
gpio_pin_mux_config(GPIOA, GPIO_PINS_SOURCE0, GPIO_MUX_4);

— 使用GPIO_Init()函数配置I/O引脚:

- 通过以下方式配置复用功能模式下的所需引脚
gpio_init_struct.gpio_mode = GPIO_MODE_MUX;

- 通过以下成员选择类型、上拉/下拉和驱动力
gpio_out_type、gpio_pull和gpio_drive_strength成员

根据上述配置过程,下面将介绍几种外设的常用配置示例。

USARTI/O复用模式配置

“AT32F421

TMR I/O复用模式配置

“AT32F421

I2C I/O复用模式配置

“AT32F421

来源:AT32 MCU 雅特力科技
免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。

围观 779

页面

订阅 RSS - GPIO