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