IGKBoard(imx6ull)-ADC编程MQ-2烟雾传感器采样

news2024/9/29 23:30:58

文章目录

    • 1- ADC介绍
    • 2- MQ-2烟雾传感器介绍
      • (1)工作原理
      • (2)MQ-2应用电路
    • 3- MQ-2烟雾传感器硬件连接
    • 4- ADC驱动配置
    • 5- 编程查看当前浓度


1- ADC介绍

ADC是Analog-to-Digital Converter的缩写,指模数转换器。真实世界的模拟信号,例如温度、压力、声音或者图像等,需要转换成更容易储存、处理和发射的数字形式,模数转换器可以实现这个功能。ADC就是数模转换,将模拟量转换为数字量。

在这里插入图片描述
总之,只要是需要模拟信号转为数字信号的场合,那么肯定要用到ADC。很多数字传感器内部会集成 ADC,传感器内部使用 ADC来处理原始的模拟信号,最终给用户输出数字信号。

在这里插入图片描述
学习ADC需要注意注意几个参数:

  1. 分辨率:采集精准度的重要因素。就是尺子上的能量出来的最小测量刻度,例如我们常用的厘米尺它的最小刻度就是1毫米,表示最小测量精度就是1毫米。假如 ADC的测量范围为 0-5V,分辨率设置为12位,那么我们能测出来的最小电压就是 5V除以 2的 12次方,也就是5/4096=0.00122V。很明显,分辨率越高,采集到的信号越精确,所以分辨率是衡量 ADC的一个重要指标。
  2. 精度:是影响结果准确度的因素之一。经过计算我们 ADC在 12位分辨率下的最小测量值是 0.00122V,但是我们ADC的精度最高只能到11位也就是 0.00244V。也就是 ADC测量出0.00244V的结果是要比0.00122V要可靠,也更准确。
  3. 采样时间:当 ADC在某时刻采集外部电压信号的时候,此时外部的信号应该保持不变,但实际上外部的信号是不停变化的。所以在 ADC内部有一个保持电路,保持某一时刻的外部信号,这样 ADC就可以稳定采集了,保持这个信号的时间就是采样时间。
  4. 采样率:也就是在一秒的时间内采集多少次。很明显,采样率越高越好,当采样率不够的时候可能会丢失部分信息,所以 ADC采样率是衡量 ADC性能的另一个重要指标。

2- MQ-2烟雾传感器介绍

(1)工作原理

MQ-2烟雾传感器属于二氧化锡半导体气敏材料,属于表面离子式N型半导体。

在这里插入图片描述
处于200~300摄氏度时,二氧化锡吸附空气中的氧,形成氧的负离子吸附,使半导体中的电子密度减少,从而使其电阻值增加。当与烟雾接触时,如果晶粒间界处的势垒收到烟雾的调至而变化,就会引起表面导电率的变化。利用这一点就可以获得这种烟雾存在的信息,烟雾的浓度越大,导电率越大,输出电阻越低,则输出的模拟信号就越大。

(2)MQ-2应用电路

MQ-2常用的电路有两种,一种使用采用比较器电路监控,另一种为ADC电路检测。
以下图中的模块仅有比较器电路,ADC部分使用IGKBoard的主控集成的ADC进行检测,下文进行详细介绍和使用。
在这里插入图片描述
MQ-2的引脚4输出随烟雾浓度变化的直流信号,被加到比较器U1A的2脚,Rp构成比较器的门槛电压。当烟雾浓度较高输出电压高于门槛电压时,比较器输出低电平(0v),此时LED亮报警;当浓度降低传感器的输出电压低于门槛电压时,比较器翻转输出高电平(Vcc),LED熄灭。调节Rp,可以调节比较器的门槛电压,从而调节报警输出的灵敏度。

R1串入传感器的加热回路,可以保护加热丝免受冷上电时的冲击。


3- MQ-2烟雾传感器硬件连接

在这里插入图片描述

  • GND:该引脚要连到开发板的GND扩展引脚上
  • VCC:该引脚要连到开发板的 5v 供电引脚上
  • AOUT:是MQ-2模块的模拟输出引脚,该引脚应该连开发板上ADC功能的GPIO引脚上

在IGKBoard开发板上,提供了两个ADC模拟输入的引脚,位于开发板的8pin扩展口上,其管脚名为TS_XP和TS_YN,在Linux系统启动时如果启用了ADC overlay后,它俩会默认作为ADC模拟输入口使用。这样,MQ-2的AO引脚应该连接上其中之一即可。

在这里插入图片描述
在这里插入图片描述


4- ADC驱动配置

如果想作为MQ-2的模拟信号采样引脚使用的话,我们需要修改开发板上的DTOverlay配置文件,添加该引脚的 ADC 支持,然后关机重启系统就可以了。

root@igkboard:~# vi /run/media/mmcblk1p1/config.txt
# Enable extra overlays
dtoverlay_extra=adc

然后在/sys/bus/iio/devices目录下可以查看到ADC对应的iio设备:iio:device0

root@igkboard:/sys/bus/iio/devices# ls
iio:device0
root@igkboard:/sys/bus/iio/devices/iio:device0# ls
buffer              in_voltage1_raw                in_voltage_scale              scan_elements
buffer0             in_voltage2_raw                name                          subsystem
dev                 in_voltage3_raw                of_node                       trigger
in_conversion_mode  in_voltage4_raw                power                         uevent
in_voltage0_raw     in_voltage_sampling_frequency  sampling_frequency_available

进入设备我们可以看见很多文件,但是我们只关心三个文件:

  • in_voltage1_ra :ADC1通道 1原始值文件,即**TS_YN(GPIO01_IO01)**管脚的输入模拟值转换的数字值,范围0-4095;
  • in_voltage4_raw :ADC1通道 4原始值文件,即**TS_XP(GPIO01_IO04)**管脚的输入模拟值转换的数字值,范围0-4095;
  • in_voltage_scale: ADC1比例文件 (分辨率 ),单位为 mV。实际电压值 (mV) = in_voltage1_raw * in_voltage_scale。
root@igkboard:/sys/bus/iio/devices/iio:device0# cat in_voltage1_raw 
1448
root@igkboard:/sys/bus/iio/devices/iio:device0# cat in_voltage_scale 
0.805664062

当前TS_YN管脚上的实际电压是 1448 * 0.805664062 ≈ 1,166.6 mV,即1.1666V

MQ-2的浓度值也就可以计算出为 1448/4095 * 100% = 35.4%


5- 编程查看当前浓度

还是来说一下流程:

  1. 第一步肯定要打开我们文件的路径读取文件,因为是两个文件,直接用一个指针数组;
  2. 因为要每时每刻都需要读取数据,所以需要在一个while(1)循环中进行;
  3. 打开文件读取数据我们可以创建一个自定义函数,传进文件路径以及buf保存我们读取的数据;
  4. 当然我们需要将我们读取的字符串数据进行类型转换;
  5. 然后公式计算输出。
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>

/*我们要访问的文件路径*/
static char *file_path[] = 
{
        "/sys/bus/iio/devices/iio:device0/in_voltage_scale",    
        "/sys/bus/iio/devices/iio:device0/in_voltage1_raw",    
};

static int file_data_read(char *filename, char *buf, size_t buf_size)
{
    int ret = 0;//打开文件之后的标注值,小于0说明文件打开了需要关闭
    int fd = -1;//文件描述符

    if(!filename || !buf || !buf_size)
    {
        printf("[%s] Error filename or buf or buf_size error\n", __FUNCTION__);
        return -1;
    }

    fd = open(filename, O_RDONLY);
    if(fd < 0)
    {
        printf("Open file '%s' failure:%s\n", filename, strerror(errno));
        ret = -2;
        goto CleanUp;
    }

    memset(buf, 0, sizeof(buf));
    fd = read(fd, buf, buf_size);
    if(fd < 0)
    {
        printf("Read file '%s' failure:%s\n", filename, strerror(errno));
        ret = -3;
        goto CleanUp;
    }

CleanUp:
    if (ret < 0)
    {
        close(fd);
    }
    return ret;
}

int main(int argc, char *argv[])
{
    int   raw = 0;//原始数据
    float scale = 0;//精度值
    float act = 0;//实际值
    float conc = 0;//浓度值
    char adc_buf[30] = {};

    while(1)
    {
        if(file_data_read(file_path[0],adc_buf, sizeof(adc_buf)) < 0)
        {
            printf("Error:Read %s failuere.\n", file_path[0]);
        }
        scale = atof(adc_buf);//字符串类型转换为浮点数类型
        printf("scale:%f ", scale);

        if(file_data_read(file_path[1],adc_buf, sizeof(adc_buf)) < 0)
        {
            printf("Error:Read %s failuere.\n", file_path[1]);
        }
        raw = atoi(adc_buf);//字符串类型转换为浮点数类型
        printf("raw:%d\n", raw);

        act = (raw*scale)/1000.f;
        conc = ((float)raw/4095.f)*100.f;

        printf("MQ-2 实际电压:%.3fV, 浓度为 %.1f%%\n", act, conc);
        sleep(1);
    }
    return 0;
}

Makefile文件:

CC=arm-linux-gnueabihf-gcc
APP_NAME=adc_mq2

all:clean
    @${CC} ${APP_NAME}.c -o ${APP_NAME}

clean:
    @rm -f ${APP_NAME}

make之后文件就是在ARM架构上运行的文件啦。

wangdengtao@wangdengtao-virtual-machine:~/wangdengtao/tftpboot$ make
wangdengtao@wangdengtao-virtual-machine:~/wangdengtao/tftpboot$ file adc_mq2
adc_mq2: ELF 32-bit LSB pie executable, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-armhf.so.3, BuildID[sha1]=38c11b2cad6acaa418e19672fba8008004e43b50, for GNU/Linux 3.2.0, not stripped

对tftp服务器搭建不了解的可以参考这篇文章:wpa_supplicant无线网络配置imx6ull以及搭建tftp服务器
tftp服务器下载到我们的开发板,然后运行:

root@igkboard:~# tftp -gr adc_mq2 192.168.0.134
root@igkboard:~# chmod a+x adc_mq2                          
root@igkboard:~# ./adc_mq2 
scale:0.805664 raw:1029
MQ-2 virtual voltage: 0.829V, concentration: 25.1%
scale:0.805664 raw:1029
MQ-2 virtual voltage: 0.829V, concentration: 25.1%
scale:0.805664 raw:1029
MQ-2 virtual voltage: 0.829V, concentration: 25.1%
0000scale:0.805664 raw:1224
MQ-2 virtual voltage: 0.986V, concentration: 29.9%
0000scale:0.805664 raw:1329
MQ-2 virtual voltage: 1.071V, concentration: 32.5%
0.scale:0.805664 raw:1385
MQ-2 virtual voltage: 1.116V, concentration: 33.8%
.....scale:0.805664 raw:1446
MQ-2 virtual voltage: 1.165V, concentration: 35.3%
scale:0.805664 raw:1372
MQ-2 virtual voltage: 1.105V, concentration: 33.5%
scale:0.805664 raw:1332
MQ-2 virtual voltage: 1.073V, concentration: 32.5%
scale:0.805664 raw:1373
MQ-2 virtual voltage: 1.106V, concentration: 33.5%
scale:0.805664 raw:1526
MQ-2 virtual voltage: 1.229V, concentration: 37.3%
scale:0.805664 raw:1450
MQ-2 virtual voltage: 1.168V, concentration: 35.4%
scale:0.805664 raw:1412
MQ-2 virtual voltage: 1.138V, concentration: 34.5%

其实对着我们的烟雾传感器哈一口气都是可以看见变化的。


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/381182.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【C#基础】C# 预处理器指令

序号系列文章8【C#基础】C# 面向对象编程9【C# 基础】C# 异常处理操作10【C#基础】C# 正则表达式文章目录前言1&#xff0c;预处理器指令的概念2&#xff0c;预处理器指令的定义与使用2.1&#xff0c;可为空上下文2.2&#xff0c;定义符号2.3&#xff0c;条件编译2.4&#xff0…

KConfig语言学习(一文全览)

KConfig 语言学习菜单项菜单属性类型定义prompt: 输入提示default: 默认值depends on/requires: 依赖关系select: 反向依赖关系imply: 弱反向依赖关系visible if: 选项可见range: 数据范围help: 帮助信息菜单依赖关系菜单结构关系KConfig语法config: 配置项menuconfig: 配置菜单…

发布依赖到maven仓库

maven中央仓库是一个开放的仓库&#xff0c;所以我们也可以把自己开发的jar推送到远程仓库&#xff0c;这样可以直接引入pom依赖使用我们的库。 准备工作 ● 需要一个github账号&#xff08;程序员必备&#xff09; ● 网络代理&#xff08;涉及到的网站通常没版本在国内直接访…

Computers Graphics(CAG)及Elsevier常见期刊投稿记录

1.期刊地址 Editorial Managerhttps://www.editorialmanager.com/cag/default2.aspx先进行用户注册&#xff0c;登录后进入首页点击Submit New Manuscript开始提交手稿&#xff0c;其他期刊流程相同&#xff0c;CAG所有的投稿注意事项见&#xff1a;Guide for authors - Comp…

数据库连接与properties文件

管理properties数据库&#xff1a; 现在pom文件中加入Druid的坐标&#xff1a; <dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.2.8</version></dependency>配置文件中添加相应的数据&…

拒绝B站邀约,从月薪3k到年薪47W,我的经验值得每一个测试人借鉴

有时候&#xff0c;大佬们总是会特立独行。因为像我这样的常人总是想不通&#xff0c;究竟是怎样的情境&#xff0c;连B站这样的大厂面试都可以推掉&#xff1f; 缘起一通电话&#xff0c;踏出了改变人生轨迹的第一步 我是小瑾&#xff0c;今年28岁&#xff0c;2016年毕业于陕…

线程池的基本认识与使用

线程池的基本认识与使用线程池线程池工作原理&#xff1a;优点&#xff1a;传统的创建线程方式线程池创建线程使用线程池 池化思想&#xff1a;线程池、字符串常量池、数据库连接池可以提高资源的利用率 线程池工作原理&#xff1a; 预先创建多个线程对象 放入线程池种&#…

数据库基础-数据库基本概念(1-1)

你好&#xff0c;欢迎来到数据库基础系列专栏&#xff0c;欢迎留言互动哦~ 目录一、数据库基础1. 数据库基本概念1.1 数据库1.2 什么是数据库管理软件1.3 表1.4 行1.5 列和数据类型1.6 主键1.7 什么是 SQL一、数据库基础 1. 数据库基本概念 1.1 数据库 数据库是一个以某种有…

射频调试的习惯

三月开工了&#xff0c;一个月的调试即将开始。其实调试的重心是测试&#xff0c;核心的推动力是做事的习惯和思维。测试很重要&#xff0c;数据不对&#xff0c;能力和时间都浪费了上面了。测试的问题初步解完了&#xff0c;今天吃饭的时候碰到大领导。领导好忙&#xff0c;我…

SQL报错注入(上)

SQL报错注入报错注入概述报错注入的前提条件Xpath型函数&#xff08;需要数据库版本>5.15&#xff09;extractvalue&#xff08;&#xff09;extractvalue&#xff08;&#xff09;实操![在这里插入图片描述](https://img-blog.csdnimg.cn/5c7bfbc6565045d4bb352448c17f0869…

docker搭建redis集群、哨兵

集群搭建 本机IP 192.168.1.149 分别采用映射 192.168.1.149 的6379 6380 6381 三个端口模拟三台服务器。搭建三主无从的集群 首先可以在本机上创建三份redis.conf配置文件,分别命名为redis1.conf, redis2.conf, redis3.conf &#xff0c;我这里放在/opt/redis/conf/中 redis*.…

MyBatis学习笔记(九) —— 自定义映射resultMap

9、自定义映射resultMap 9.1、resultMap处理字段和属性的映射关系 若字段名和实体类中的属性名不一致&#xff0c;则可以通过resultMap设置自定义映射 resultType 是一个具体的类型。 resultMap 是resultMap的标签。 id 是处理主键和属性的映射关系&#xff1b; result 是处…

FirePower X2 14.0.1 for RAD Studio Alexandria

介绍 FirePower X2 FirePower X2 集成了 RAD Studio 11.0 Alexandria 中的新功能&#xff0c;并预览了我们的新特色组件 TwwDataGrouper。 FirePower X2 还允许您为 Apple 的新 M1 芯片构建应用程序&#xff0c;这样您就可以进一步利用 M1 芯片来提高本机应用程序的性能&#x…

set和map的基本使用

目录 关联式容器 要点分析 键值对 pair介绍 set 模板参数列表&#xff1a; set的构造&#xff1a; 常用接口 操作 multiset map map的构造 插入 make_pair map的迭代器 operator[] multimap multimap中为什么没有重载operator[] 关联式容器 关联式容器也是用…

(五十六)针对主键之外的字段建立的二级索引,又是如何运作的?

上一次我们已经给大家彻底讲透了聚簇索引这个东西&#xff0c;其实聚簇索引就是innodb存储引擎默认给我们创建的一套基于主键的索引结构&#xff0c;而且我们表里的数据就是直接放在聚簇索引里的&#xff0c;作为叶子节点的数据页&#xff0c;如下图。 而且我们现在也对基于主键…

日志框架以及如何使用LogBack记录程序

使用日志框架可以记录一个程序运行的过程和详情&#xff0c;同时便捷地存储到文件里面&#xff0c;并且性能和灵活性都比较好。日志的体系结构包括两类日志规范接口&#xff1a;Commons Logging&#xff0c;简称&#xff1a;JCL&#xff1b;Simple Logging Facade for Java&…

JavaScript高级程序设计读书分享之8章——8.2创建对象

JavaScript高级程序设计(第4版)读书分享笔记记录 适用于刚入门前端的同志 创建Object的实例 let person new Object(); person.name "Nicholas"; person.age 29; person.job "Software Engineer"; person.sayName function() { console.log(this…

增长乏力?创造产品和项目需求的6大方法【一杯咖啡谈项目】

我这里所说的创造需求&#xff0c;类似于PMI在《需求管理实践指南》中所写的专业术语“需要评估”&#xff08;needs assessment&#xff09;&#xff0c;这个需要评估&#xff0c;没有写进PMI的《项目管理知识体系指南&#xff08;PMBOK指南&#xff09;》&#xff08;以下称为…

fork()出来一个进程,这个进程的父进程是从哪来的?

基本概念fork() creates a new process by duplicating the calling process. The new process is referred to as the child process. The calling process is referred to as the parent process.fork()是一个系统调用&#xff0c;不是一个函数。详细信息可以&#xff0c;man…

day(22) Echarts和nacos

day(22) Echarts和nacos一、Echarts和nacos1.1 数据展示1.2 查询日期之间的数据二、配置中心2.1 配置中心spring cloud config2.1.1 缺点2.1.2 其他配置中心2.2 nacos2.2.1 pom2.2.2 配置文件2.2.3 Data id是微服务名称2.2.4 优先级2.2.5 动态刷新2.2.6 namespace2.2.7 多配置文…