• 《RV1126 —— 配置adc并读取adc通道上传感器数值》


    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;
  • 相关阅读:
    大数的四则运算
    整数划分问题(递归法)
    浅谈C++中内存分配、函数调用和返回值问题
    栈的模拟运用 SOJ3897 dance2
    C/C++:sizeof('a')的值为什么不一样?
    浅谈C++中指针和引用的区别
    n!的分解 soj 2666
    char *a 和char a[] 的区别(指针和数组的区别)
    错排公式的推导
    浅谈C语言中的指针
  • 原文地址:https://www.cnblogs.com/zhuangquan/p/15353324.html
Copyright © 2020-2023  润新知