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!\n", 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 \n", value);

    /* 轉換為對應電壓值 */
    vol = value * REFER_VOLTAGE / CONVERT_BITS;
    rt_kprintf("the voltage is :%d.%02d \n", 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\n", value, ret, *_rt_errno());
    vol = value * REFER_VOLTAGE / CONVERT_BITS;
    rt_kprintf("the voltage is :%d.%02d \n", 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\n", value, ret, *_rt_errno());
    vol = value * REFER_VOLTAGE / CONVERT_BITS;
    rt_kprintf("the voltage is :%d.%02d \n", vol / 100, vol % 100);
    
    ret = rt_device_control(adc_dev, RT_ADC_CMD_DISABLE, (void*)ADC_DEV_CHANNEL);    //禁止ADC

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM