• RT-Thread 设备驱动ADC浅析与改进


    OS版本:RT-Thread 4.0.0

    芯片:STM32F407

    下面时官方ADC提供的参考访问接口

    访问 ADC 设备

    应用程序通过 RT-Thread 提供的 ADC 设备管理接口来访问 ADC 硬件,相关接口如下所示:

    函数描述
    rt_device_find() 根据 ADC 设备名称查找设备获取设备句柄
    rt_adc_enable() 使能 ADC 设备
    rt_adc_read() 读取 ADC 设备数据
    rt_adc_disable() 关闭 ADC 设备

    下面对驱动源码主要实现方式做简要分析:

    在drv_adc.c中,缺少对 RT_USING_DEVICE_OPS 项的支持,增加如下代码

    #ifdef RT_USING_DEVICE_OPS  //增加对RT_USING_DEVICE_OPS的支持
    const static struct rt_device_ops adc_ops = 
    {
        RT_NULL,
        RT_NULL,
        RT_NULL,
        _adc_read,
        RT_NULL,
        _adc_control
    };
    #endif
    
    rt_err_t rt_hw_adc_register(rt_adc_device_t device, const char *name, const struct rt_adc_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_Miscellaneous;
        
    #ifdef RT_USING_DEVICE_OPS
        device->parent.ops          = &adc_ops;
    #else
        device->parent.init = RT_NULL;
        device->parent.open = RT_NULL;
        device->parent.close = RT_NULL;
        device->parent.read = _adc_read;
        device->parent.write = RT_NULL;
        device->parent.control = _adc_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;
    }

    其中设备ops接口要实现 adc_ops

    const static struct rt_device_ops adc_ops = 
    {
        RT_NULL,
        RT_NULL,
        RT_NULL,
        _adc_read,
        RT_NULL,
        _adc_control
    };

    设备的子类 rt_adc_device 需要实现的ops 为 rt_adc_ops

    static const struct rt_adc_ops stm_adc_ops =
    {
        .enabled = stm32_adc_enabled,
        .convert = stm32_get_adc_value,
    };

    其中 _adc_control 调用 stm32_adc_enabled, _adc_read 调用 stm32_get_adc_value;

    官方示例为了简化ADC驱动操作,直接export相关adc操作函数供用户使用,使用方式如下:

        rt_adc_device_t adc_dev;
        rt_uint32_t value, vol;
        rt_err_t ret = RT_EOK;
    
        /* 查找设备 */
        adc_dev = (rt_adc_device_t)rt_device_find(ADC_DEV_NAME);
        if (adc_dev == RT_NULL)
        {
            rt_kprintf("adc sample run failed! can't find %s device!
    ", ADC_DEV_NAME);
            return RT_ERROR;
        }
        
        /* 使能设备 */
        ret = rt_adc_enable(adc_dev, ADC_DEV_CHANNEL);
    
        /* 读取采样值 */
        value = rt_adc_read(adc_dev, ADC_DEV_CHANNEL);
        rt_kprintf("the value is :%d 
    ", value);
    
        /* 转换为对应电压值 */
        vol = value * REFER_VOLTAGE / CONVERT_BITS;
        rt_kprintf("the voltage is :%d.%02d 
    ", vol / 100, vol % 100);
    
        /* 关闭通道 */
        ret = rt_adc_disable(adc_dev, ADC_DEV_CHANNEL);

    既然有I/O设备模型,再使用这种方式现得驱动接口太分散了,下面以 I/O device接口实现adc采集示例:

    使用 rt_device_read 时,注意 pos 和 size 的含义

        rt_device_t adc_dev = rt_device_find(ADC_DEV_NAME);    
        rt_device_open(adc_dev, RT_DEVICE_FLAG_RDWR);    //记住一定要有打开设备操作,否则后面的rt_device_read无法使用
        
        ret = rt_device_control(adc_dev, RT_ADC_CMD_ENABLE, (void*)ADC_DEV_CHANNEL);    //使能ADC
        
        ret = rt_device_read(adc_dev, ADC_DEV_CHANNEL, &value, 4);    //程序不做修改时,_adc_read函数的pos项表示adc通道,size为4的倍数,大于4则依序读取后面的通道
    //    value = rt_adc_read((rt_adc_device_t)adc_dev, ADC_DEV_CHANNEL);
        rt_kprintf("the value is :%d  %d  %d
    ", value, ret, *_rt_errno());
        vol = value * REFER_VOLTAGE / CONVERT_BITS;
        rt_kprintf("the voltage is :%d.%02d 
    ", vol / 100, vol % 100);
        
        ret = rt_device_control(adc_dev, RT_ADC_CMD_DISABLE, (void*)ADC_DEV_CHANNEL);    //禁止ADC

     改进建议:

    读取adc使用 rt_device_read 很不方便,建议取消 rt_device_read 项,读取采用 rt_device_control 来实现;

    const static struct rt_device_ops adc_ops = 
    {
        RT_NULL,
        RT_NULL,
        RT_NULL,
        RT_NULL,
        RT_NULL,
        _adc_control
    };

    在adc.h中添加对 RT_ADC_CMD_READ 的支持,并添加对于读取数据参数结构体

    struct rt_device_adc_value
    {
        rt_uint32_t channel;
        rt_uint32_t value;
    };
    
    
    typedef enum
    {
        RT_ADC_CMD_ENABLE,
        RT_ADC_CMD_DISABLE,
        RT_ADC_CMD_READ,
    } rt_adc_cmd_t;

    在adc.c中的_adc_control 函数添加对 RT_ADC_CMD_READ 的支持

    static rt_err_t _adc_control(rt_device_t dev, int cmd, void *args)
    {
        rt_err_t result = RT_EOK;
        rt_adc_device_t adc = (struct rt_adc_device *)dev;
        
        if (adc->ops->enabled == RT_NULL)
        {
            return -RT_ENOSYS;
        }
        if (cmd == RT_ADC_CMD_ENABLE)
        {
            result = adc->ops->enabled(adc, (rt_uint32_t)args, RT_TRUE);
        }
        else if (cmd == RT_ADC_CMD_DISABLE)
        {
            result = adc->ops->enabled(adc, (rt_uint32_t)args, RT_FALSE);
        }
        else if (cmd == RT_ADC_CMD_READ)  //通过control读取adc指定通道
        {
            struct rt_device_adc_value *adc_value;
            adc_value = (struct rt_device_adc_value *) args;
            if (adc_value == RT_NULL) return -RT_ERROR;
            
            result = adc->ops->convert(adc, adc_value->channel, &(adc_value->value));
        }
    
        return result;
    }

    使用方式如下:

        rt_device_t adc_dev = rt_device_find(ADC_DEV_NAME);    
        rt_device_open(adc_dev, RT_DEVICE_FLAG_RDWR);    //记住一定要有打开设备操作,否则后面的rt_device_read无法使用
        
        ret = rt_device_control(adc_dev, RT_ADC_CMD_ENABLE, (void*)ADC_DEV_CHANNEL);    //使能ADC
        
        struct rt_device_adc_value adc_value;
        adc_value.channel = ADC_DEV_CHANNEL;
        ret = rt_device_control(adc_dev, RT_ADC_CMD_READ, &adc_value);
        value = adc_value.value;
    
        rt_kprintf("the value is :%d  %d  %d
    ", value, ret, *_rt_errno());
        vol = value * REFER_VOLTAGE / CONVERT_BITS;
        rt_kprintf("the voltage is :%d.%02d 
    ", vol / 100, vol % 100);
        
        ret = rt_device_control(adc_dev, RT_ADC_CMD_DISABLE, (void*)ADC_DEV_CHANNEL);    //禁止ADC
  • 相关阅读:
    UVA 679 Dropping Balls 由小见大,分析思考 二叉树放小球,开关翻转,小球最终落下叶子编号。
    2017-5-14 湘潭市赛 Similar Subsequence 分析+四维dp+一些简单优化
    Problem #3263 丽娃河的狼人传说 区间满足灯数,r排序后贪心。
    2017-5-14 湘潭市赛 Longest Common Subsequence 想法题
    2017-5-14 湘潭市赛 Parentheses 转化思想+贪心 使括号序列合法的最小花费。满足前面左括号的数量>=有括号的数量。
    deque双端队列用法
    Shell字符串截取
    keepAlived发生故障切换VIP—邮件通知方案2
    KeepAlived发生故障切换VIP—邮件通知方案1
    缺少依赖 libmysqlclient.so.18(64bit)的解决办法
  • 原文地址:https://www.cnblogs.com/silencehuan/p/10929387.html
Copyright © 2020-2023  润新知