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

共3条 1/1 1 跳转至

瑞萨RA系列已适配的ADC框架分析

助工
2024-08-27 22:10:23   被打赏 50 分(兑奖)     打赏

驱动层适应框架层的通用模板

   RTT的adc框架分析完毕后,我们基本上可以知道驱动实现drv_adc.c的模板了,即对应的伪代码为:

struct adc_handle
{
    struct rt_adc_dev dev;
    // 驱动内部维护参数
};

const struct rt_adc_ops adc_ops =
{
     .enabled = adc使能关闭函数,
     .convert = 读取ADC寄存器值函数,
     .get_resolution = 返回adc精度函数,
     .get_vref = 返回参考电压函数,
}

static int adc_init(void)
{
    const char *device_name = "xxxx";
    struct adc_handle *dev = (struct adc_handle *)rt_malloc(sizeof(struct adc_handle));

    /* 硬件ADC初始化内容 */

    /* 注册adc函数 */
    if(rt_hw_adc_register(&dev->adc_device,
                                   device_name,
                                   &adc_ops,
                                   dev) != RT_EOK)
    {
        LOG_E("%s register failed", device_name);
        return -RT_ERROR;
    }
    return RT_EOK;
}
INIT_DEVICE_EXPORT(adc_init);

   带着这么个模板,我们去进一步分析瑞萨的ADC驱动适配代码。

瑞萨适配的ADC驱动分析

注册入口

struct ra_adc_map ra_adc[] =
{
#ifdef BSP_USING_ADC0
    {
      .device_name = "adc0",
      .g_cfg = &g_adc0_cfg,
      .g_ctrl = &g_adc0_ctrl,
      .g_channel_cfg = &g_adc0_channel_cfg,
    },
#endif
#ifdef BSP_USING_ADC1
    {
      .device_name = "adc1",
      .g_cfg = &g_adc1_cfg,
      .g_ctrl = &g_adc1_ctrl,
      .g_channel_cfg = &g_adc1_channel_cfg,
    },
#endif
};

static struct rt_adc_dev adc_obj[sizeof(ra_adc) / sizeof(ra_adc[0])] = {0};

static const struct rt_adc_ops ra_adc_ops =
{
    .enabled = ra_adc_enabled,
    .convert = ra_get_adc_value,
};

static int ra_adc_init(void)
{
    rt_err_t result = 0;
    rt_size_t obj_num = sizeof(adc_obj) / sizeof(struct rt_adc_dev);

    for (int i = 0; i < obj_num; i++)
    {
        /* init ADC object */
        result = R_ADC_Open((adc_ctrl_t *)ra_adc[i].g_ctrl, ra_adc[i].g_cfg);

        result = R_ADC_ScanCfg((adc_ctrl_t *)ra_adc[i].g_ctrl, ra_adc[i].g_channel_cfg);

        /* register ADC device */
        if(rt_hw_adc_register(&adc_obj[i].adc_device,
                                       ra_adc[i].device_name,
                                       &ra_adc_ops,
                                       &ra_adc[i]) == RT_EOK)
        {
            LOG_D("%s init success", ra_adc[i].device_name);
        }
        else
        {
            LOG_E("%s register failed", ra_adc[i].device_name);
            result = -RT_ERROR;
        }
        RT_ASSERT(result == RT_EOK);
    }
    return RT_EOK;
}
INIT_DEVICE_EXPORT(ra_adc_init);

   从代码实现上来看,注册入口与前面分析硬件ADC框架所梳理出来的伪代码思路一致,这里需要着重注意的是struct ra_adc_map ra_adc[]这个结构体数组,实际上,从注册函数上来看,这个结构体应该就是适配RASC的关键(后面RASC需要新增ADC接口,也应该是直接适配这个结构体提供的内容)。但是这里也有个一需要注意的点,就是他的实现并没有把struct rt_adc_dev这个结构体放置于前面梳理出来的数据结构中,看起来这种实现是更优的实现方法。

adc使能关闭函数

static rt_err_t ra_adc_enabled(struct rt_adc_device *device, rt_int8_t channel, rt_bool_t enabled)
{
    RT_ASSERT(device != RT_NULL);
    struct ra_adc_map *adc = (struct ra_adc_map *)device->parent.user_data;
    /**< start adc*/
    if (enabled)
    {
        if (FSP_SUCCESS != R_ADC_ScanStart((adc_ctrl_t *)adc->g_ctrl))
        {
            LOG_E("start %s failed.", adc->device_name);
            return -RT_ERROR;
        }
    }
    else
    {
        /**< stop adc*/
        if (FSP_SUCCESS != R_ADC_ScanStop((adc_ctrl_t *)adc->g_ctrl))
        {
            LOG_E("stop %s failed.", adc->device_name);
            return -RT_ERROR;
        }
    }
    return RT_EOK;
}

   这个实现非常的简洁,就是上面需要开, 驱动就执行开,并将执行结果上报。上面执行馆,则驱动执行关后将执行结果上报。

读取ADC寄存器值函数

static rt_err_t ra_get_adc_value(struct rt_adc_device *device, rt_int8_t channel, rt_uint32_t *value)
{
    RT_ASSERT(device != RT_NULL);
    struct ra_adc_map *adc = (struct ra_adc_map *)device->parent.user_data;
    if (RT_EOK != R_ADC_Read32((adc_ctrl_t *)adc->g_ctrl, channel, value))
    {
        LOG_E("get adc value failed.\n");
        return -RT_ERROR;
    }
    return RT_EOK;
}

   这个函数实际上的功能与伪代码分析的功能也是一致的,直接读出寄存器对应的值并上报。

返回adc精度函数

   此函数驱动并未实现

返回参考电压函数

   此函数并未实现

关闭ADC模块函数

rt_err_t ra_adc_close(struct rt_adc_device *device)
{
    RT_ASSERT(device != RT_NULL);
    struct ra_adc_map *adc = (struct ra_adc_map *)device->parent.user_data;
    if (FSP_SUCCESS != R_ADC_Close((adc_ctrl_t *)adc->g_ctrl))
    {
        LOG_E("close %s failed.", adc->device_name);
        return -RT_ERROR;
    }
    return RT_EOK;
}

   此函数并未被调用,但是瑞萨将其保留在驱动中,可能是为了未来的低功耗场景下预留接口吧。

总结

   分析完瑞萨已经适配的ADC驱动,我们已经可以确认,如果我们需要适配已有的驱动,若RASC未配置,则按照struct ra_adc_map ra_adc[]中对应名字生成RASC配置即可。若未配置,则需要仿照现有的配置代码,新增配置,并用对应的宏包裹。另外,需要注意的是,如果新增了宏,对应的KConfig文件也需要增加这个宏的开关,以便配置后使能对应的adc。






关键词: 瑞萨     驱动     ADC     分析    

高工
2024-08-28 11:32:23     打赏
2楼

感谢分享


专家
2024-08-28 11:53:23     打赏
3楼

感谢分享


共3条 1/1 1 跳转至

回复

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