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

共3条 1/1 1 跳转至

rtthread DAC框架分析

助工
2024-09-03 23:44:44   被打赏 50 分(兑奖)     打赏

后面的驱动适配的分析,基本上都按照RTT对接驱动的框架分析,瑞萨已适配框架代码分析,以及启明6M5适配驱动模块的顺序去看。之所以这么操作,是因为从上至下看接口,能够对框架的设计思路有更深的理解,也能更好的梳理出适配新平台时的操作模板。

RTT的DAC框架

RTT DAC框架的源码位于:\components\drivers\misc\dac.c

由于ADC和DAC本质上是一类设备,因此个人认为,从暴露的接口上,dac和adc都应该是一致的,除了ADC是读电压值,DAC是设置电压值以外。

暴露给驱动的接口

DAC注册接口

#ifdef RT_USING_DEVICE_OPS
const static struct rt_device_ops dac_ops =
{
    RT_NULL,
    RT_NULL,
    RT_NULL,
    RT_NULL,
    _dac_write,
    _dac_control,
};
#endif

rt_err_t rt_hw_dac_register(rt_dac_device_t device, const char *name, const struct rt_dac_ops *ops, const void *user_data)
{
    rt_err_t result = RT_EOK;
    RT_ASSERT(ops != RT_NULL && ops->convert != RT_NULL);

    device->parent.type = RT_Device_Class_DAC;
    device->parent.rx_indicate = RT_NULL;
    device->parent.tx_complete = RT_NULL;

#ifdef RT_USING_DEVICE_OPS
    device->parent.ops         = &dac_ops;
#else
    device->parent.init        = RT_NULL;
    device->parent.open        = RT_NULL;
    device->parent.close       = RT_NULL;
    device->parent.read        = RT_NULL;
    device->parent.write       = _dac_write;
    device->parent.control     = _dac_control;
#endif
    device->ops = ops;
    device->parent.user_data = (void *)user_data;

    result = rt_device_register(&device->parent, name, RT_DEVICE_FLAG_RDWR);

    return result;
}

从注册接口上看,DAC的设计逻辑确实和ADC设计逻辑一致。唯一的区别是,DAC是写,ADC是读。

按框架暴露给应用的接口

DAC写接口

static rt_ssize_t _dac_write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size)
{
    rt_err_t result = RT_EOK;
    rt_size_t i;
    struct rt_dac_device *dac = (struct rt_dac_device *)dev;
    rt_uint32_t *value = (rt_uint32_t *)buffer;

    for (i = 0; i < size; i += sizeof(int))
    {
        result = dac->ops->convert(dac, pos + i, value);
        if (result != RT_EOK)
        {
            return 0;
        }
        value++;
    }

    return i;
}

和ADC类似,DAC也可以实现同一DAC连写多个channel的功能。只是目前自己工作经历上暂未遇到支持一个DAC给多个dac管脚输出不同电压的功能,不确定这种实现是否合理。

DAC控制接口

static rt_err_t _dac_control(rt_device_t dev, int cmd, void *args)
{
    rt_err_t result = -RT_EINVAL;
    rt_dac_device_t dac = (struct rt_dac_device *)dev;

    if (cmd == RT_DAC_CMD_ENABLE && dac->ops->enabled)
    {
        result = dac->ops->enabled(dac, (rt_uint32_t)args);
    }
    else if (cmd == RT_DAC_CMD_DISABLE && dac->ops->enabled)
    {
        result = dac->ops->disabled(dac, (rt_uint32_t)args);
    }
    else if (cmd == RT_DAC_CMD_GET_RESOLUTION && dac->ops->get_resolution)
    {
        rt_uint8_t resolution = dac->ops->get_resolution(dac);
        if(resolution != 0)
        {
            *((rt_uint8_t*)args) = resolution;
            LOG_D("resolution: %d bits", resolution);
            result = RT_EOK;
        }
    }

    return result;
}

不知道为何,对驱动层暴露的接口,开关dac接口变成了两个,一个enable,一个disable。个人理解是,这块最好统一,要么暴露一个enable,通过传参的方式是能或关闭,要么暴露两个接口,不需要传参,否则有编码风格差异的问题。

另外,很遗憾,control层并未要求驱动暴露最大输出电压的接口,这也基本上说明目前的框架,并不能实现应用层直接设置电压的需求。

暴露给应用的接口

rt_dac_write

rt_err_t rt_dac_write(rt_dac_device_t dev, rt_uint32_t channel, rt_uint32_t value)
{
    RT_ASSERT(dev);

    return dev->ops->convert(dev, channel, &value);
}

从接口上来说,这个接口直接向下设置寄存器值,意味着应用层需要知道dac的参考电平,dac的精度才能把电压换算成寄存器值。而我的个人理解为,应用层并不需要关心寄存器值是啥,而是关心设下去的电压值是多少,这部分本就该驱动内部实现的。而在后面的分析中也会发现,dac驱动居然没有对应用实现直接设置电压的接口,这是一个问题点。

rt_dac_enable

rt_err_t rt_dac_enable(rt_dac_device_t dev, rt_uint32_t channel)
{
    rt_err_t result = RT_EOK;

    RT_ASSERT(dev);
    if (dev->ops->enabled != RT_NULL)
    {
        result = dev->ops->enabled(dev, channel);
    }
    else
    {
        result = -RT_ENOSYS;
    }

    return result;
}

这个功能很直观,就是使能dac的某个通道。

rt_dac_disable

rt_err_t rt_dac_disable(rt_dac_device_t dev, rt_uint32_t channel)
{
    rt_err_t result = RT_EOK;

    RT_ASSERT(dev);
    if (dev->ops->disabled != RT_NULL)
    {
        result = dev->ops->disabled(dev, channel);
    }
    else
    {
        result = -RT_ENOSYS;
    }

    return result;
}

同DAC enable,只是这个接口的作用是关闭dac的某个通道。

总结

DAC驱动到此已经分析完毕,遗憾的是,在DAC框架中,并未像ADC那样实现应用层直接设置DAC电压的接口,虽然RTT的官方文档也没推荐应用层直接读ADC电压。因此应用层仅能设置寄存器值,也就导致不同的MCU,还得在应用层自行维护最大输出电压和精度信息,带来的副作用就是,更换MCU,DAC处理部分的应用层程序无法通用,而这可能是一个优化点。






关键词: rtthread     框架     DAC    

专家
2024-09-04 00:43:20     打赏
2楼

感谢分享


专家
2024-09-04 20:46:56     打赏
3楼

感谢分享


共3条 1/1 1 跳转至

回复

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