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\n",__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\n", 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\n",__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\n", __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\n"); } 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;