Linux下的ADC
ADC
ADC简介
ADC是 Analog Digital Converter 的缩写,翻译过来为模数转换器,ADC可以将模拟值转换成数字值。模拟值是什么呢?比如我们日常生活中的温度,速度,湿度等等都是模拟值。所以如果我们想测量这些模拟值的值是多少,就需要使用 adc把模拟值转换成数字值。
ADC的分辨率
分辨率就是 ADC可以分辨量化的做小信号的能力,好比尺子上的最小刻度,有的尺子上的最小刻度是厘米,有的尺子上的最小刻度是毫米。所以分辨率越高,得到的结果精度就越准确。(得到的数字信号再用 DAC转换成模拟信号就越接近原来的模拟值)。ADC的分辨率按位数可分为8位,10位,12位等等,位数越搞,分辨率也越高。
计算公式:分辨率=量程/2的n次方,n位 ADC的位数。
ADC的参考电压
有了最小刻度,那量程又是什么呢?
这里我们需要在了解一个重要的术语叫做参考电压,参考电压是一个已知的电压。通过已知的电压找到未知电压,这个是参考电压的作用。所以参考电压就是上面我们提到的量程。
举个例子:3位分辨率,2V参考电压
ADC框架
ADC的操作
在应用程序中使用sys接口来读取ADC的值
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main(int argc, char *argv[]){
float adc_value = 0;
int sacle = 0;
FILE *fp = NULL;
fp = fopen("/sys/bus/iio/devices/iio:device0/in_voltage0_raw", "r");
fscanf(fp, "%d", &sacle);
adc_value = (1.8/1024)*(float)sacle; //1.8V is the voltage of the ADC
// 1024 is the resolution of the ADC
// so the real adc_value = (Measurement value) * ((reference voltage)/(ADC resolution))
printf("The ADC value is %f\n", adc_value);
fclose(fp);
return 0;
}
ADC的驱动
1.在设备树中添加ADC设备
&iio_adc0 {
compatible = "myadc";
io-channels = <&saradc 4>; //adc控制器,使用adc通道4
status = "okay";
};
2.在驱动中获得ADC的测量值
#include <linux/module.h>
#include <linux/init.h>
#include <linux/platform_device.h> //platform_device_register
#include <linux/of.h> //of_match_ptr
#include <linux/of_device.h> //of_device_id
#include <linux/platform_driver.h> //platform_driver_register
#include <linux/mod_devicetable.h>
#include <linux/iio/consumer.h> //iio_channel_get
#include <linux/miscdevice.h> //misc_register
#include <linux/uaccess.h> //copy_to_user
#include <linux/fs.h> //file_operations
#define CMD_READ_SACLE _IOR('A',1,int)
struct iio_channel adc_channel= NULL;
int adc_open(struct inode *inode, struct file *file){
return 0;
}
ssize_t adc_read(struct file *file, char __user *user_buf, size_t size, loff_t *ppos){
return 0;
}
long adc_ioctl(struct file *file, unsigned int cmd, unsigned long arg){
int scale = 0;
if(cmd == CMD_READ_SACLE){
iio_read_channel_raw(adc_channel, &scale); // get sacle value(Measurement value) from adc channel
if(copy_to_user((int *)arg, &scale, sizeof(scale)) != 0){
return -EFAULT;
}
}
return 0;
}
const struct file_operations adc_fops = {
.owner = THIS_MODULE,
.read = adc_read,
.open = adc_open,
.unlocked_ioctl = adc_ioctl,
};
struct miscdevice adc_miscdev = {
.minor = MISC_DYNAMIC_MINOR,
.name = "adc",
.fops = &adc_fops,
};
int adc_probe(struct platform_device *pdev){
printk(KERN_INFO "ADC driver probe\n");
adc_channel = iio_channel_get(&(pdev->dev),NULL); //get adc channel
if(!adc_channel){
printk(KERN_INFO "ADC channel get failed\n");
return -1;
}
misc_deregister(&adc_miscdev);
return 0;
}
int adc_remove(struct platform_device *pdev){
printk(KERN_INFO "ADC driver remove\n");
return 0;
}
const struct of_device_id adc_of_match[] = {
{ .compatible = "myadc" },
{ },
};
struct platform_driver adc_driver = {
.driver = {
.name = "adc_driver",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(adc_of_match),
}
.probe = adc_probe,
.remove = adc_remove,
};
static int __init adc_init(void){
printk(KERN_INFO "ADC driver init\n");
platform_register_drivers(&adc_driver);
return 0;
}
static void __exit adc_exit(void){
printk(KERN_INFO "ADC driver exit\n");
platform_unregister_drivers(&adc_driver);
misc_deregister(&adc_miscdev);
}
module_init(adc_init);
module_exit(adc_exit);
MODULE_LICENSE("GPL");