Gpio


FAQ1 为什么配置了pin脚不生效?

当发现配置pin的pinmux没有生效时,可以检查HAL_PIN_Set()的参数是否正确:

int HAL_PIN_Set(int pad, pin_function func, int flags, int hcpu)

例:PA28,对应的是GPIO_A28,复制/粘贴后,忘记改了。

../../_images/hal_gpio_config_err.png
  • flags:内部上下拉状态;配置原则,参考: 低功耗指南 第3节 配置上下拉

  • hcpu :用于区分大小核。大核pin PA是1,小核PIN PB是0。 发现有使用者复制,粘贴忘记改了,导致配置不生效。


FAQ2 为什么finsh命令: pin mode 配置上下拉无效?

现象描述:
计划通过finsh命令: pin mode 50 2 配置PA50 gpio输入上拉;
从下表枚举看,2 对应的输入上拉类型;

#define PIN_MODE_OUTPUT         0x00
#define PIN_MODE_INPUT          0x01
#define PIN_MODE_INPUT_PULLUP   0x02
#define PIN_MODE_INPUT_PULLDOWN 0x03
#define PIN_MODE_OUTPUT_OD      0x04

通过pin status 50检查效果,实际上PA50 并没有改变为上拉;
原因分析:
finsh命令 pin mode 对应的函数原型为cmd_pin。
cmd_pin 处理mode 参数的代码如下:

if (strcmp(argv[1], "mode") == 0)
{
    struct rt_device_pin_mode m;
    m.pin = atoi(argv[2]);
    m.mode = atoi(argv[3]);
    rt_device_control(device, 0, &m);
    ......
}

检查调用层级:rt_device_control()->sifli_pin_mode()->HAL_GPIO_Init()
HAL_GPIO_Init() 只配置hwp_gpiox寄存器功能,不具备配置pinmux的功能.
配置上下拉通过修改hwp_pinmux寄存器实现的。
解决方案: 改变上下拉的正确方式必须通过HAL_PIN_Set(int pad, pin_function func, int flags, int hcpu)。
例如:

//配置PA50为PULLUP
HAL_PIN_Set(PAD_PA50, GPIO_A50, PIN_PULLUP, 1);

FAQ3 为什么在bsp_pinmux.c 配置PA43为GPIO,实际通过pin status查看却变成了LCDC?

现象描述:
bsp_pinmux.c 配置PA43为GPIO,输出高电平。但实际上电平却为低。
通过finsh命令 pin status 43 检查发现PA43被配置成了LCDC。
原因分析:
通过检查原理图PA43被应用于LCDC控制, 如下图:

../../_images/gpio_faq3_sch_PA43.png

lcd 驱动会在开机和唤醒调用配置pinmux

../../_images/gpio_faq3_lcd_pinmux.png

解决方案: 最好根据原理图来分配合适gpio来使用,对于其他功能模块已经用的pin,最好不要使用。


FAQ4 PBR0作为电源控制Pin时,为什么必须被配置为force1?

原因分析:
PBR作为低功耗IO会被应用在低功耗场景,例如:睡眠和关机后依然输出pwm波形等。
PBR0 相对于其他PBR增加有一个特殊功能就是force1,即硬件强制PBR0输出高电平。
force1 一般会用在电源控制。
特别是当PBR0作为psram电源控制Pin时,没有被配置为force1, 在睡眠唤醒过程中可能存在瞬间的低电平,导致psram存储数据异常。对于 nand方案, 代码也是放在psram,所以代码会跑乱掉。
56X 一般会有PBR0 控制psram的电源,如下图:

../../_images/gpio_faq4_pbr0_psram.png

解决方案:
一般PBR0作为电源控制Pin使用时, 都会在bsp_pinmux.c让PBR0 进入force1, 方法如下:

MODIFY_REG(hwp_rtc->PBR0R, RTC_PBR0R_SEL_Msk, MAKE_REG_VAL(0, RTC_PBR0R_SEL_Msk, RTC_PBR0R_SEL_Pos));       // 恢复PBR0 功能为 0
HAL_PBR0_FORCE1_ENABLE();   //使能PBR0 force 1 

如果想PBR0退出force1,可以参考如下方法:

HAL_PBR0_FORCE1_DISABLE();      //禁止PBR0 force 1 
MODIFY_REG(hwp_rtc->PBR0R, RTC_PBR0R_SEL_Msk, MAKE_REG_VAL(1, RTC_PBR0R_SEL_Msk, RTC_PBR0R_SEL_Pos));            // 切换PBR0 功能为 GPIO
HAL_PBR_ConfigMode(0, 1);       // 配置 PBR0 为输出模式
HAL_PBR_WritePin(0, 0);         // 配置 PBR0 输出0

FAQ5 如何睡眠后PBR1继续输出PWM波形?

PBR支持PBR_CLK_LP的功能,支持睡眠后持续输出LP的波形。
LP的波形和LXT_DISABLE有关:
LXT_DISABLE 使能, 去掉外部32k晶振,使用内部rc10k, lp 输出的是rc10k波形;
LXT_DISABLE 禁止, 使用外部32k晶振, lp 输出的是32k波形;

../../_images/gpio_faq5_32k_rc10k.png

52x/56x/58x都支持PBR睡眠继续输出LP波形, 但配置方法有些许差异:
52x 配置PBR1 睡眠输出lp
52x的PBR0~PBR3 和 PA24 ~PA27复用pin。
所以,配置PBR1 的同时需要配置PA25.
步骤1: 配置PA25
PA25 配置上下拉为NOPULL,功能配置为模拟;

    HAL_PIN_Set(PAD_PA25, GPIO_A25, PIN_NOPULL, 1);
    HAL_PIN_Set_Analog(PAD_PA25, 1);

步骤2: 配置PBR1
PBR1 配置为功能1,即:PBR_CLK_LP, 输出

    uint32_t val = hwp_rtc->PBR1R;
    val &= ~(RTC_PBR1R_OE_Msk | RTC_PBR1R_SEL_Msk);
    val |= (RTC_PBR1R_OE_Msk | MAKE_REG_VAL(1, RTC_PBR1R_SEL_Msk, RTC_PBR1R_SEL_Pos));
    hwp_rtc->PBR1R = val;

由于验证时,LXT_DISABLE 使能,查看效果输出的频率在9k左右;

../../_images/gpio_faq5_pbr1_output_rc10k.png

56x/58x 配置PBR1 睡眠输出lp
56x/58x的PBR都是单独的pin,可以直接配置PBR1。

    uint32_t val = hwp_rtc->PBR1R;
    val &= ~(RTC_PBR1R_OE_Msk | RTC_PBR1R_SEL_Msk);
    val |= (RTC_PBR1R_OE_Msk | MAKE_REG_VAL(1, RTC_PBR1R_SEL_Msk, RTC_PBR1R_SEL_Pos));
    hwp_rtc->PBR1R = val;

FAQ6 睡眠怎么用pin脚唤醒系统?

52x: solution方案上52x 待机用的是deep模式,所有GPIO都支持中断唤醒。 配置示例:

    rt_pin_mode(38, PIN_MODE_INPUT);//配置PA38为输入
    rt_pin_attach_irq(38, PIN_IRQ_MODE_FALLING, (void *) wifi_wakeup_hcpu_irq_pin_handle,
                      (void *)(rt_uint32_t) 38);   ////配置PA38,下降沿中断
    rt_pin_irq_enable(38, 1);//使能PA38中断

55x, 56x, 58x系列: 这些系列方案的默认待机模式是Standby模式,普通GPIO无法通过中断唤醒系统。 只有带有 #WKUP 功能的pin脚,使用pm_enable_pin_wakeup函数配置后才能唤醒系统。配置的唤醒中断触发方式必须与GPIO中断的触发方式一致,否则唤醒后无法触发普通的GPIO中断处理函数。 配置示例:

// WIFI_WAKEUP_HCPU_PIN 为38,对应PA38脚
rt_pin_mode(WIFI_WAKEUP_HCPU_PIN, PIN_MODE_INPUT);  

// 查询该GPIO对应的唤醒源引脚号
GPIO_TypeDef *gpio = GET_GPIO_INSTANCE(WIFI_WAKEUP_HCPU_PIN);
uint16_t gpio_pin = GET_GPIOx_PIN(WIFI_WAKEUP_HCPU_PIN);
int8_t wakeup_pin;
if (WIFI_WAKEUP_HCPU_PIN > 96)   // 大于96的是PB脚
    wakeup_pin = HAL_LPAON_QueryWakeupPin(gpio, gpio_pin);
else
    wakeup_pin = HAL_HPAON_QueryWakeupPin(gpio, gpio_pin);
RT_ASSERT(wakeup_pin >= 0);

// 使能该引脚的唤醒功能,触发方式为下降沿
pm_enable_pin_wakeup(wakeup_pin, AON_PIN_MODE_NEG_EDGE); 

// 配置并使能普通GPIO中断(触发方式需与唤醒方式一致)
rt_pin_attach_irq(WIFI_WAKEUP_HCPU_PIN, PIN_IRQ_MODE_FALLING, (void *) wifi_wakeup_hcpu_irq_pin_handle,
                  (void *)(rt_uint32_t) WIFI_WAKEUP_HCPU_PIN);
rt_pin_irq_enable(WIFI_WAKEUP_HCPU_PIN, 1);


FAQ7 唤醒系统后怎么确认是不是我配置的pin脚?

开启pm_debug宏后,UART日志中会出现[pm]相关的打印信息。

  • [pm]S: // 系统进入睡眠时会打印此信息。

  • [pm]W: // 系统唤醒时会打印此信息。

  • [pm]WSR: // 系统唤醒原因,其后的16进制值需要参考hpsys_aon.hlpsys_aon.h中定义的WSR寄存器位。

例如:

[pm]WSR: 0x4

在52x系列芯片上,0x4对应定时器唤醒,对应WSR寄存器的bit位为100(即bit 2)。

// 在 hpsys_aon.h 中的定义
#define HPSYS_AON_WSR_LPTIM1_Pos        (2U)
#define HPSYS_AON_WSR_LPTIM1_Msk        (0x1UL << HPSYS_AON_WSR_LPTIM1_Pos)
#define HPSYS_AON_WSR_LPTIM1            HPSYS_AON_WSR_LPTIM1_Msk