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