这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » 嵌入式开发 » 软件与操作系统 » 瑞萨已适配的pin驱动分析

共3条 1/1 1 跳转至

瑞萨已适配的pin驱动分析

助工
2024-11-15 21:05:16     打赏

在前面的分析过程中,我们总结出了通用的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框架使用,前提是,所选的管脚支持开漏模式。






关键词: 瑞萨     驱动     分析     pin    

专家
2024-11-15 23:29:13     打赏
2楼

感谢分享


专家
2024-11-16 09:43:38     打赏
3楼

谢谢分享


共3条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]