1.adc设备树配置
一般来说,rk中已经封装好了标准adc的dts参数属性,我们要做的就是添加自己的adc通道,并引用标准接口即可,不同平台,标准dts中的adc节点名有所不同,其配置属性基本一样。
例如: 1)adc: adc@ff100000 { compatible = "rockchip,saradc"; ...... }; 2)saradc: saradc@ff100000 { compatible = "rockchip,saradc"; ...... };
添加自己的dts通道:(在系统对应的dts中添加)
adc_name{ //自定义节点名 compatible = "adc_name"; //匹配驱动的属性字符串,可以与节点名不同 io-channels = <&saradc(&adc) 2>; //设定我们需要读取的adc通道,参考硬件原理图,其中&表示应用系统封装的标准dts配置,需要系统标准的adc节点名,保证与本系统的一致,这里我们设定的为adc通道2 io-channel-names = "adc2" //自己取的名字,后续iio_channel_get的第二个参数会使用到这个参数 status = "okay"; //设备节点状态 };
由于我使用的是RV1109板卡,使用的是rv1126.dtsi。
可以看到已经配置好了。
再rv1126-evb-v10.dtsi中使能saradc,并配置基准电压(这个根据自身硬件决定)。
2.adc内核配置
直接在kernel中make ARCH=arm menuconfig,查找saradc。
然后编译下载到板卡上。
3.编写光敏驱动
3.1 查看光敏在adc的通道几
可以看到lightsensor是在通道2。
3.2 配置设备树
3.3 编写light sensor驱动
其中主要注意iio_channel_get(&(pdev->dev), "adc2")和iio_read_channel_raw(chan, &raw),作用分别是获取adc通道以及获取adc原始数据。
#include <linux/kernel.h> #include <linux/gpio.h> #include <linux/moduleparam.h> #include <linux/stat.h> #include <linux/init.h> #include <linux/module.h> #include <linux/delay.h> #include <linux/of.h> #include <linux/of_gpio.h> #include <linux/of_platform.h> #include <linux/gpio.h> #include <linux/platform_device.h> #include <linux/adc.h> #include <linux/version.h> #include <linux/init.h> #include <linux/fs.h> #include <linux/sched.h> #include <linux/pm.h> #include <linux/sysctl.h> #include <linux/proc_fs.h> #include <asm/uaccess.h> #include <asm/io.h> #include <linux/iio/iio.h> #include <linux/iio/machine.h> #include <linux/iio/driver.h> #include <linux/iio/consumer.h> #include <linux/uaccess.h> #define GPIO_LOW 0 #define GPIO_HIGH 1 int major; int count; static struct class *cls; struct iio_channel *chan; static int light_sensor_open(struct inode *inode, struct file *file) { printk(KERN_EMERG "%s-%d: enter ",__FUNCTION__,__LINE__); return 0; } static ssize_t light_sensor_read(struct file *filp,char *buf,size_t len,loff_t *off) { int raw; int ret = -1; int result = 20; ret = iio_read_channel_raw(chan, &raw); if (ret < 0) { printk("read hook adc channel() error: %d ", ret); return 0; } result = (1800*raw)/1023; ret = copy_to_user(buf, &result, len); return 1; } static struct file_operations adc_fops = { .owner = THIS_MODULE, .open = light_sensor_open, .read = light_sensor_read, }; static int adc_probe(struct platform_device *pdev) { printk(KERN_EMERG "%s-%d: enter ",__FUNCTION__,__LINE__); major = register_chrdev(0, "light_sensor", &adc_fops); cls = class_create(THIS_MODULE, "light_sensor"); device_create(cls, NULL, MKDEV(major, 0), NULL, "light_sensor"); chan = iio_channel_get(&(pdev->dev), "adc2"); if (IS_ERR(chan)) { chan = NULL; return -1; } return 0; } static int adc_remove(struct platform_device *pdev) { printk(KERN_INFO "Enter %s ", __FUNCTION__); iio_channel_release(chan); device_destroy(cls, MKDEV(major, 0)); class_destroy(cls); unregister_chrdev(major, "light_sensor"); return 0; } static const struct of_device_id of_adc_match[] = { { .compatible = "light_sensor" }, { /* Sentinel */ } }; static struct platform_driver adc_driver = { .probe = adc_probe, .remove = adc_remove, .driver = { .name = "light_sensor", .owner = THIS_MODULE, .of_match_table = of_adc_match, }, }; static int __init adc_init(void) { printk("init light_sensor driver"); return platform_driver_register(&adc_driver); return 0; } static void __exit adc_exit(void) { platform_driver_unregister(&adc_driver); printk(KERN_INFO "Exit light sensor driver "); } module_init(adc_init); module_exit(adc_exit); MODULE_LICENSE("GPL");
将以上源码保存为 drivers/iio/adc/light_sensor.c ,并在 drivers/iio/adc/Makefile 后加入
obj-$(CONFIG_LIGHT_SENSOR) += light_sensor.o
在drivers/iio/adc/Kconfig中添加:
config LIGHT_SENSOR tristate "light sensor config"
最后在make ARCH=arm menuconfig配置选择LIGHT_SENSOR。编译下载到板卡。
可以到对应的设备节点。
4.获取光敏的数值
通过用户态接口获取adc值,其中*表示adc第多少通道:
cat /sys/bus/iio/devices/iio:device0/in_voltage*_raw
由于我们是使用的通道2
其中数值就改变了。
5.adc相关api
struct iio_channel *iio_channel_get(struct device *dev, const char *consumer_channel);
-
功能:获取 iio 通道描述
-
参数:
-
dev: 使用该通道的设备描述指针
-
consumer_channel: 通道名称
-
void iio_channel_release(struct iio_channel *chan);
-
功能:释放 iio_channel_get 函数获取到的通道
-
参数:
-
chan:要被释放的通道描述指针
-
int iio_read_channel_raw(struct iio_channel *chan, int *val);
-
功能:读取 chan 通道 AD 采集的原始数据。
-
参数:
-
chan:要读取的采集通道指针
-
val:存放读取结果的指针
-
6.计算采集到的电压
使用标准电压将 AD 转换的值转换为用户所需要的电压值。其计算公式如下:
Vref / (2^n-1) = Vresult / raw
注: Vref 为标准电压 n 为 AD 转换的位数 Vresult 为用户所需要的采集电压 raw 为 AD 采集的原始数据 例如,标准电压为 1.8V,AD 采集位数为 10 位,AD 采集到的原始数据为 568,则:
Vresult = (1800mv * 568) / 1023;