在前面的分析过程中,我们总结出了通用的pin驱动模板,下一步就是对瑞萨适配的pin驱动进行进一步的分析。
瑞萨对应实现分析
注册入口
const static struct rt_pin_ops _ra_pin_ops =
{
.pin_mode = ra_pin_mode,
.pin_write = ra_pin_write,
.pin_read = ra_pin_read,
.pin_attach_irq = ra_pin_attach_irq,
.pin_detach_irq = ra_pin_dettach_irq,
.pin_irq_enable = ra_pin_irq_enable,
.pin_get = ra_pin_get,
};
#ifdef R_ICU_H
static struct rt_pin_irq_hdr pin_irq_hdr_tab[RA_IRQ_MAX] = {0};
struct ra_pin_irq_map pin_irq_map[RA_IRQ_MAX] = {0};
static void ra_irq_tab_init(void)
{
for (int i = 0; i < RA_IRQ_MAX; ++i)
{
pin_irq_hdr_tab[i].pin = -1;
pin_irq_hdr_tab[i].mode = 0;
pin_irq_hdr_tab[i].args = RT_NULL;
pin_irq_hdr_tab[i].hdr = RT_NULL;
}
}
static void ra_pin_map_init(void)
{
#ifdef VECTOR_NUMBER_ICU_IRQ0
pin_irq_map[0].irq_ctrl = &g_external_irq0_ctrl;
pin_irq_map[0].irq_cfg = &g_external_irq0_cfg;
#endif
#ifdef VECTOR_NUMBER_ICU_IRQ1
pin_irq_map[1].irq_ctrl = &g_external_irq1_ctrl;
pin_irq_map[1].irq_cfg = &g_external_irq1_cfg;
#endif
#ifdef VECTOR_NUMBER_ICU_IRQ2
pin_irq_map[2].irq_ctrl = &g_external_irq2_ctrl;
pin_irq_map[2].irq_cfg = &g_external_irq2_cfg;
#endif
#ifdef VECTOR_NUMBER_ICU_IRQ3
pin_irq_map[3].irq_ctrl = &g_external_irq3_ctrl;
pin_irq_map[3].irq_cfg = &g_external_irq3_cfg;
#endif
#ifdef VECTOR_NUMBER_ICU_IRQ4
pin_irq_map[4].irq_ctrl = &g_external_irq4_ctrl;
pin_irq_map[4].irq_cfg = &g_external_irq4_cfg;
#endif
#ifdef VECTOR_NUMBER_ICU_IRQ5
pin_irq_map[5].irq_ctrl = &g_external_irq5_ctrl;
pin_irq_map[5].irq_cfg = &g_external_irq5_cfg;
#endif
#ifdef VECTOR_NUMBER_ICU_IRQ6
pin_irq_map[6].irq_ctrl = &g_external_irq6_ctrl;
pin_irq_map[6].irq_cfg = &g_external_irq6_cfg;
#endif
#ifdef VECTOR_NUMBER_ICU_IRQ7
pin_irq_map[7].irq_ctrl = &g_external_irq7_ctrl;
pin_irq_map[7].irq_cfg = &g_external_irq7_cfg;
#endif
#ifdef VECTOR_NUMBER_ICU_IRQ8
pin_irq_map[8].irq_ctrl = &g_external_irq8_ctrl;
pin_irq_map[8].irq_cfg = &g_external_irq8_cfg;
#endif
#ifdef VECTOR_NUMBER_ICU_IRQ9
pin_irq_map[9].irq_ctrl = &g_external_irq9_ctrl;
pin_irq_map[9].irq_cfg = &g_external_irq9_cfg;
#endif
#ifdef VECTOR_NUMBER_ICU_IRQ10
pin_irq_map[10].irq_ctrl = &g_external_irq10_ctrl;
pin_irq_map[10].irq_cfg = &g_external_irq10_cfg;
#endif
#ifdef VECTOR_NUMBER_ICU_IRQ11
pin_irq_map[11].irq_ctrl = &g_external_irq11_ctrl;
pin_irq_map[11].irq_cfg = &g_external_irq11_cfg;
#endif
#ifdef VECTOR_NUMBER_ICU_IRQ12
pin_irq_map[12].irq_ctrl = &g_external_irq12_ctrl;
pin_irq_map[12].irq_cfg = &g_external_irq12_cfg;
#endif
#ifdef VECTOR_NUMBER_ICU_IRQ13
pin_irq_map[13].irq_ctrl = &g_external_irq13_ctrl;
pin_irq_map[13].irq_cfg = &g_external_irq13_cfg;
#endif
#ifdef VECTOR_NUMBER_ICU_IRQ14
pin_irq_map[14].irq_ctrl = &g_external_irq14_ctrl;
pin_irq_map[14].irq_cfg = &g_external_irq14_cfg;
#endif
#ifdef VECTOR_NUMBER_ICU_IRQ15
pin_irq_map[15].irq_ctrl = &g_external_irq15_ctrl;
pin_irq_map[15].irq_cfg = &g_external_irq15_cfg;
#endif
}
#endif /* R_ICU_H */
int rt_hw_pin_init(void)
{
// 硬件资源初始化,从后续的代码分析,可以发现宏 R_ICU_H 对应的功能应该是和pin中断有关,
// 也就是说,在瑞萨的框架里,如果不使用中断,注册时甚至不需要初始化硬件资源
#ifdef R_ICU_H
ra_irq_tab_init();
ra_pin_map_init();
#endif
return rt_device_pin_register("pin", &_ra_pin_ops, RT_NULL);
}
rt_weak void rt_hw_board_init()
{
// 其他初始化
......
/* pin驱动注册 */
#ifdef RT_USING_PIN
rt_hw_pin_init();
#endif
// 其他初始化
......
}设置pin模式入口
static void ra_pin_mode(rt_device_t dev, rt_base_t pin, rt_uint8_t mode)
{
fsp_err_t err;
/* Initialize the IOPORT module and configure the pins */
// 完整看驱动代码会发现,这两个参数都是RASC生成的,应该是资源的预设置部分
err = R_IOPORT_Open(&g_ioport_ctrl, &g_bsp_pin_cfg);
if (err != FSP_SUCCESS)
{
LOG_E("GPIO open failed");
return;
}
switch (mode)
{
case PIN_MODE_OUTPUT: // 设置成输出模式
err = R_IOPORT_PinCfg(&g_ioport_ctrl, (bsp_io_port_pin_t)pin, BSP_IO_DIRECTION_OUTPUT);
if (err != FSP_SUCCESS)
{
LOG_E("PIN_MODE_OUTPUT configuration failed");
return;
}
break;
case PIN_MODE_INPUT: // 设置成输入模式
err = R_IOPORT_PinCfg(&g_ioport_ctrl, (bsp_io_port_pin_t)pin, BSP_IO_DIRECTION_INPUT);
if (err != FSP_SUCCESS)
{
LOG_E("PIN_MODE_INPUT configuration failed");
return;
}
break;
case PIN_MODE_OUTPUT_OD: // 设置成开漏模式
err = R_IOPORT_PinCfg(&g_ioport_ctrl, (bsp_io_port_pin_t)pin, IOPORT_CFG_NMOS_ENABLE);
if (err != FSP_SUCCESS)
{
LOG_E("PIN_MODE_OUTPUT_OD configuration failed");
return;
}
break;
}
}从驱动上看,瑞萨适配的驱动并不支持输入上拉和输入下拉两种模式。也就是说,可能瑞萨整个RA系列平台硬件上都不支持这样的功能,也可能是这种接口未暴露出来。
写pin入口
typedef enum e_bsp_io_level
{
BSP_IO_LEVEL_LOW = 0, ///< Low
BSP_IO_LEVEL_HIGH ///< High
} bsp_io_level_t;
static void ra_pin_write(rt_device_t dev, rt_base_t pin, rt_uint8_t value)
{
bsp_io_level_t level = BSP_IO_LEVEL_HIGH;
if (value != level)
{
level = BSP_IO_LEVEL_LOW;
}
R_BSP_PinAccessEnable();
#ifdef SOC_SERIES_R9A07G0
R_IOPORT_PinWrite(&g_ioport_ctrl, (bsp_io_port_pin_t)pin, (bsp_io_level_t)level);
#else
R_BSP_PinWrite(pin, level);
#endif
R_BSP_PinAccessDisable();
}很好理解,就是一个写高低电平的操作。不过有个疑惑的点,前面输入的value非1就设为0,这是什么逻辑?正常操作不应该是非0就设为1吗?
读pin入口
static rt_ssize_t ra_pin_read(rt_device_t dev, rt_base_t pin)
{
if ((pin > RA_MAX_PIN_VALUE) || (pin < RA_MIN_PIN_VALUE))
{
return -RT_EINVAL;
}
#ifdef SOC_SERIES_R9A07G0
bsp_io_level_t io_level;
R_IOPORT_PinRead(&g_ioport_ctrl, (bsp_io_port_pin_t)pin, &io_level);
return io_level;
#else
return R_BSP_PinRead(pin);
#endif
}读接口就很好理解了,就是读电平并返回电平值。
注册中断回调入口
#ifdef R_ICU_H
static rt_base_t ra_pin_get_irqx(rt_uint32_t pin)
{
PIN2IRQX_TABLE(pin) //其实就是通过pin编号转化成中断表编号
}
#endif
static rt_err_t ra_pin_attach_irq(struct rt_device *device, rt_base_t pin,
rt_uint8_t mode, void (*hdr)(void *args), void *args)
{
#ifdef R_ICU_H
rt_int32_t irqx = ra_pin_get_irqx(pin);
if (0 <= irqx && irqx < (sizeof(pin_irq_map) / sizeof(pin_irq_map[0])))
{
int level = rt_hw_interrupt_disable();
if (pin_irq_hdr_tab[irqx].pin == irqx &&
pin_irq_hdr_tab[irqx].hdr == hdr &&
pin_irq_hdr_tab[irqx].mode == mode &&
pin_irq_hdr_tab[irqx].args == args)
{ // 如果都一样,说明已经注册了,就直接返回
rt_hw_interrupt_enable(level);
return RT_EOK;
}
if (pin_irq_hdr_tab[irqx].pin != -1)
{ // 如果已注册其他回调函数,则不注册新的回调函数
rt_hw_interrupt_enable(level);
return -RT_EBUSY;
}
// 注册回调函数
pin_irq_hdr_tab[irqx].pin = irqx;
pin_irq_hdr_tab[irqx].hdr = hdr;
pin_irq_hdr_tab[irqx].mode = mode;
pin_irq_hdr_tab[irqx].args = args;
rt_hw_interrupt_enable(level);
}
else return -RT_ERROR;
return RT_EOK;
#else
return -RT_ERROR;
#endif
}注销中断回调入口
static rt_err_t ra_pin_dettach_irq(struct rt_device *device, rt_base_t pin)
{
#ifdef R_ICU_H
rt_int32_t irqx = ra_pin_get_irqx(pin);
if (0 <= irqx && irqx < sizeof(pin_irq_map) / sizeof(pin_irq_map[0]))
{
int level = rt_hw_interrupt_disable();
if (pin_irq_hdr_tab[irqx].pin == -1)
{ // 如果已经注销,则直接返回
rt_hw_interrupt_enable(level);
return RT_EOK;
}
// 注销资源
pin_irq_hdr_tab[irqx].pin = -1;
pin_irq_hdr_tab[irqx].hdr = RT_NULL;
pin_irq_hdr_tab[irqx].mode = 0;
pin_irq_hdr_tab[irqx].args = RT_NULL;
rt_hw_interrupt_enable(level);
}
else
{
return -RT_ERROR;
}
return RT_EOK;
#else
return -RT_ERROR;
#endif
}使能关闭中断入口
#ifdef R_ICU_H
void irq_callback(external_irq_callback_args_t *p_args)
{
rt_interrupt_enter();
if (p_args->channel == pin_irq_hdr_tab[p_args->channel].pin)
{
pin_irq_hdr_tab[p_args->channel].hdr(pin_irq_hdr_tab[p_args->channel].args);
}
rt_interrupt_leave();
};
#endif /* R_ICU_H */
static rt_err_t ra_pin_irq_enable(struct rt_device *device, rt_base_t pin, rt_uint8_t enabled)
{
#ifdef R_ICU_H
rt_err_t err;
rt_int32_t irqx = ra_pin_get_irqx(pin);
if (PIN_IRQ_ENABLE == enabled)
{
if (0 <= irqx && irqx < sizeof(pin_irq_map) / sizeof(pin_irq_map[0]))
{
err = R_ICU_ExternalIrqOpen((external_irq_ctrl_t *const)pin_irq_map[irqx].irq_ctrl,
(external_irq_cfg_t const * const)pin_irq_map[irqx].irq_cfg);
/* Handle error */
if (FSP_SUCCESS != err)
{
/* ICU Open failure message */
LOG_E("\r\n**R_ICU_ExternalIrqOpen API FAILED**\r\n");
return -RT_ERROR;
}
err = R_ICU_ExternalIrqEnable((external_irq_ctrl_t *const)pin_irq_map[irqx].irq_ctrl);
/* Handle error */
if (FSP_SUCCESS != err)
{
/* ICU Enable failure message */
LOG_E("\r\n**R_ICU_ExternalIrqEnable API FAILED**\r\n");
return -RT_ERROR;
}
}
}
else if (PIN_IRQ_DISABLE == enabled)
{
err = R_ICU_ExternalIrqDisable((external_irq_ctrl_t *const)pin_irq_map[irqx].irq_ctrl);
if (FSP_SUCCESS != err)
{
/* ICU Disable failure message */
LOG_E("\r\n**R_ICU_ExternalIrqDisable API FAILED**\r\n");
return -RT_ERROR;
}
err = R_ICU_ExternalIrqClose((external_irq_ctrl_t *const)pin_irq_map[irqx].irq_ctrl);
if (FSP_SUCCESS != err)
{
/* ICU Close failure message */
LOG_E("\r\n**R_ICU_ExternalIrqClose API FAILED**\r\n");
return -RT_ERROR;
}
}
return RT_EOK;
#else
return -RT_ERROR;
#endif
}会发现,使能关闭接口,也就是直接调用瑞萨自己的应用适配层接口。另外,个人理解irq_callback这个函数应该也是RASC生成配置时填入的。
从字符串获取pin编号入口
static rt_base_t ra_pin_get(const char *name)
{
int pin_number = -1, port = -1, pin = -1;
if (rt_strlen(name) != 4)
return -1;
if ((name[0] == 'P' || name[0] == 'p'))
{
if ('0' <= name[1] && name[1] <= '9')
{
port = (name[1] - '0') * 16 * 16;
if ('0' <= name[2] && name[2] <= '9' && '0' <= name[3] && name[3] <= '9')
{
pin = (name[2] - '0') * 10 + (name[3] - '0');
pin_number = port + pin;
return pin_number;
}
}
else if ('A' <= name[1] && name[1] <= 'Z')
{
port = (name[1] - '0' - 7) * 16 * 16;
if ('0' <= name[2] && name[2] <= '9' && '0' <= name[3] && name[3] <= '9')
{
pin = (name[2] - '0') * 10 + (name[3] - '0');
pin_number = port + pin;
return pin_number;
}
}
}
return -1;
}由于瑞萨gpio命名规则都是P加三位数字,比如P101,因此此函数实际的功能也就将管脚的名称转换成驱动所需要的pin编号并返回给上层使用。
总结
至此,瑞萨适配的pin驱动已经分析完毕。从分析内容上看,我们大致可以获取以下信息:
1. 可能受限于硬件,瑞萨支持的pin驱动能并不完整
2. RASC生成时可以生成带中断和不带中断的配置,带中断时,应该会自动生成宏R_ICU_H,另外,可能有个irq_callback函数名需要填写。
3. 瑞萨的pin驱动,可以直接给到软件i2c框架使用,前提是,所选的管脚支持开漏模式。
我要赚赏金
