IOMUX
MCU 芯片的管脚有限,大部分功能都存在 IO 复用的情况,所以每个功能模块都要根据实际板图配置 IOMUX ,以 rk2108b_evb 为例,代码路径为:board/rk2108b_evb/iomux.c,这里选了其中比较复杂的 LCDC 的 IOMUX,具体如下:
RT_UNUSED static void lcdc_iomux_config(void)
{
#ifdef RT_USING_PANEL_GC9306_CTC28
/* 设置功能 */
HAL_PINCTRL_SetIOMUX(GPIO_BANK0,
GPIO_PIN_A0 | // LCDC_D0
GPIO_PIN_A1 | // LCDC_D1
GPIO_PIN_A2 | // LCDC_D2
GPIO_PIN_A3 | // LCDC_D3
GPIO_PIN_A6 | // LCDC_D6
GPIO_PIN_A7 | // LCDC_D7
GPIO_PIN_B0 | // LCDC_WR
GPIO_PIN_B1, // LCDC_RD
PIN_CONFIG_MUX_FUNC3); // 功能号可以查询datasheet获得
HAL_PINCTRL_SetIOMUX(GPIO_BANK0,
GPIO_PIN_A4 | // LCDC_D4
GPIO_PIN_A5, // LCDC_D5
PIN_CONFIG_MUX_FUNC6);
HAL_PINCTRL_SetIOMUX(GPIO_BANK1,
GPIO_PIN_A4 | // LCDC_CMD
GPIO_PIN_A5, // LCDC_CS
PIN_CONFIG_MUX_FUNC2);
HAL_PINCTRL_SetIOMUX(GPIO_BANK0,
GPIO_PIN_D2, // PWM2
PIN_CONFIG_MUX_FUNC0);//GPIO
/* set brightness 100 */
HAL_GPIO_SetPinDirection(GPIO0, GPIO_PIN_D2, GPIO_OUT); // 设置IO方向为输出
HAL_GPIO_SetPinLevel(GPIO0, GPIO_PIN_D2, GPIO_LOW); // 拉低
#elif defined RT_USING_PANEL_ST7703_DS
HAL_PINCTRL_SetIOMUX(GPIO_BANK1,
GPIO_PIN_B4, // LCDC_RST
PIN_CONFIG_MUX_FUNC0);
HAL_GPIO_SetPinDirection(GPIO1, GPIO_PIN_B4, GPIO_OUT);
HAL_GPIO_SetPinLevel(GPIO1, GPIO_PIN_B4, GPIO_HIGH);
HAL_PINCTRL_SetIOMUX(GPIO_BANK1,
GPIO_PIN_B2, // LCDC_en
PIN_CONFIG_MUX_FUNC0);
HAL_GPIO_SetPinDirection(GPIO1, GPIO_PIN_B2, GPIO_OUT);
HAL_GPIO_SetPinLevel(GPIO1, GPIO_PIN_B2, GPIO_HIGH);
HAL_PINCTRL_SetIOMUX(GPIO_BANK0,
GPIO_PIN_D2, // PWM2
PIN_CONFIG_MUX_FUNC0);//PWM
#endif
}
每个功能所对应的 PIN_CONFIG_MUX_FUNCx 可以在芯片的 TRM 里找到,而 IO 极性是根据外设的需求配置,驱动强度只有在默认的驱动强度无法满足的情况下才需要配置。配置完成之后,只要在 rt_hw_iomux_config 调用 lcdc_iomux_config 即可。如果模块存在分时复用的情况,例如某个产品的 PDM 和 I2S 需要分时复用,则可以在 iomux.h 里同时导出两个 IOMUX 函数,并去掉 iomux.c 里的 static 限定,这样驱动就可以在外部动态去切换 IO 功能,例如:
/* in iomux.h */
void pdm_iomux_config(void);
void i2s_iomux_config(void);
/* in iomux.c */
RT_UNUSED void pdm_iomux_config(void)
{
// for pdm input
HAL_PINCTRL_SetIOMUX(GPIO_BANK0,
GPIO_PIN_A0 | // PDM_IN_CLK0
GPIO_PIN_A1 | // PDM_IN_CLK1
GPIO_PIN_A2 | // PDM_IN_SDI0
GPIO_PIN_A3, // PDM_IN_SDI1
PIN_CONFIG_MUX_FUNC1);
}
RT_UNUSED void i2s_iomux_config(void)
{
// for i2s input
HAL_PINCTRL_SetIOMUX(GPIO_BANK0,
GPIO_PIN_A0 | // I2S_IN_SCLK
GPIO_PIN_A1 | // I2S_IN_LRCK
GPIO_PIN_A2 | // I2S_IN_SDI0
GPIO_PIN_A3, // I2S_IN_SDI1
PIN_CONFIG_MUX_FUNC2);
HAL_PINCTRL_SetIOMUX(GPIO_BANK0,
GPIO_PIN_A4, //I2S_IN_MCLK
PIN_CONFIG_MUX_FUNC1);
}
这里要注意了:
HAL_PINCTRL_SetIOMUX 操作的是 GPIO_BANK0
而HAL_GPIO_xxx 操作的是GPIO0
上面的接口多了一个“BANK ”,它实际是0;下面那个实际是GPIO0的寄存器地址。
rt_pin接口
HAL提供以下几个操作GPIO的接口
HAL_GPIO_SetPinDirection(GPIO1, GPIO_PIN_B2, GPIO_OUT);
HAL_GPIO_SetPinLevel(GPIO1, GPIO_PIN_B2, GPIO_HIGH);
HAL_GPIO_GetPinLevel(GPIO1, GPIO_PIN_B2);读GPIO输入的电平
HAL_GPIO_GetPinData(GPIO1, GPIO_PIN_B2); 注意这个API是读取GPIO输出的电平
这些接口都能找到相应的rt_pin接口。
上面的例子GPIO1_B2对应的pin num是:
#define TEST_PIN BANK_PIN(GPIO_BANK1, 10) // 10 对应的是B2,0~7对应的是 A0~A7, 8~15对应B0~B7,16~23对应C0~C7, 24~31对应D0~D7
rt_pin_mode(TEST_PIN, PIN_MODE_OUTPUT);
rt_pin_write(TEST_PIN, PIN_LOW);
rt_pin_read(TEST_PIN);
这几个rt_pin的接口最终也是调用HAL_GPIO接口实现的。
注意:在使用rt_pin接口前,还是要用HAL_PINCTRL_SetIOMUX接口把对应的IO口配置成GPIO模式。