驱动层适应框架层的通用模板
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。
我要赚赏金
