基于FPGA控制PCF8591开展ADC采样,以采样烟雾模块输出模拟电压为例(IIC通信)

基于FPGA控制PCF8591开展ADC采样

  • 前言
  • 一、芯片手册阅读
    • 1.设备地址
    • 2.字节地址
    • 3.IIC通信协议
  • 二、仿真分析
  • 三、代码分析
  • 总结
    • 视频演示

前言

这段时间做设计总是遇到一些传感器模块输出模拟电压,采集模拟电压进而了解传感器输出的浓度占比,在淘宝上找到了一个4通道的ADC采集模块,用这个来获取不同传感器的模拟电压输出正好,其实物如图所示:
该模块具有ADC采样以及DAC输出的功能,其中4路为ADC采样输入AIN0-4,一路DAC输出AOUT。
与FPGA相连接的端口为VCC、GND、SCL、SDA,主要就是通信协议的实现,实现之前需要阅读芯片手册,获取一下有用的关键信息,通信速率,从机设备地址、控制指令信息等等
产品参数:
在这里插入图片描述

在这里插入图片描述

使用注意事项:
供电:5V,你的adc输入范围就是0-5V,如果用3.3V供电,你的输入范围就是0-3.3V。不同的供电可能效果不同,我目前遇到过好多供电问题,然后换一种供电就工作正常,所以大家遇到问题时要不断排除问题。
跳帽:你如果需要用adc通道采集外部的模拟电压,你就需要对应通道的跳帽拔下来。否则数据一直是模块本身自带的数据。

一、芯片手册阅读

1.设备地址

结合两个图,发现还有A0-A2三个未知的比特,通过查看模块原理图,发现这三个引脚接地。所以设备地址确定为:0x1001000
写数据就是0x90 读数据就是0x91
在这里插入图片描述在这里插入图片描述

2.字节地址

一堆英文不想看,看图就行了,如下图所示,每个比特干啥的都说的比较清楚。
从低位这边看,[0:1]是负责说明对哪个通道进行操作的,前面也提到了总共4个通道。
然后[2]这个比特默认为1
然后[3]默认为0
然后[4:5]说明这四个通道的输入模式,有差分输入啥的,不选择,我只需要最普通的那种输入即可,所以默认00
然后[6]在ADC时为0,在DAC时为1
然后最高位默认为0

比如现在要对通道1进行ADC采样转化的功能实现,就要发送0x00000000
比如现在要对通道2进行ADC采样转化的功能实现,就要发送0x00000001
比如现在要对通道3进行ADC采样转化的功能实现,就要发送0x00000010
比如现在要对通道4进行ADC采样转化的功能实现,就要发送0x00000011

比如现在要对通道1进行DAC采样转化的功能实现,就要发送0x01000000
在这里插入图片描述

3.IIC通信协议

了解一下基本的信息,对后面实现通信流程有所参考。
在这里插入图片描述
如果发送完设备地址了,后面采样转化可以进行连续操作。

在这里插入图片描述
数字量跟模拟量的关系
在这里插入图片描述

完整的通信流程应该就是如下图所示。
在这里插入图片描述

二、仿真分析

仿真测试文件忘了放哪里了。就是简单的iic驱动,设备地址,字节地址,读取数据的命令都知道了,就是按照时序进行发送就可以了。
在这里插入图片描述

三、代码分析

`timescale  1ns/1ns

// Author        : EmbedFire
// Create Date   : 2019/04/01
// Module Name   : pcf8591_adda
// Project Name  : ad
// Target Devices: Altera EP4CE10F17C8N
// Tool Versions : Quartus 13.0
// Description   : AD电压测量模块
// 
// Revision      : V1.0
// Additional Comments:
// module  pcf8591_ad
(input   wire            sys_clk     ,   //输入系统时钟,50MHzinput   wire            sys_rst_n   ,   //输入复位信号,低电平有效input   wire            i2c_end     ,   //i2c设备一次读/写操作完成input   wire    [7:0]   rd_data     ,   //输出i2c设备读取数据output  reg             rd_en       ,   //输入i2c设备读使能信号output  reg             i2c_start   ,   //输入i2c设备触发信号output  reg     [15:0]  byte_addr   ,   //输入i2c设备字节地址output  wire    [15:0]  adc_data1    ,     //adc1数据 output  wire    [15:0]  adc_data2          //adc2数据
);//************************************************************************//
//******************** Parameter and Internal Signal *********************//
//************************************************************************//
//parameter     define
parameter   CTRL_DATA1   =   8'b0100_0001;   //AD/DA控制字
parameter   CTRL_DATA2   =   8'b0100_0010;   //AD/DA控制字
parameter   CNT_WAIT_MAX=   18'd6_9999  ;   //采样间隔计数最大值
parameter   IDLE        =   3'b001,AD_START1    =   3'b010,AD_CMD1      =   3'b100,WAIT         =   3'b101,AD_START2    =   3'b110,AD_CMD2      =   3'b111;//wire  define
wire    [31:0]  data_reg1/* synthesis keep */;   //数码管待显示数据缓存
wire    [31:0]  data_reg2/* synthesis keep */;   //数码管待显示数据缓存
//reg   define
reg     [17:0]  cnt_wait;   //采样间隔计数器
reg     [4:0]   state   ;   //状态机状态变量
reg     [7:0]   ad_data1 ;   //AD数据
reg     [7:0]   ad_data2 ;   //AD数据wire [23:0] data_temp;
wire [23:0] data_temp1;wire [23:0] data_temp2;
wire [23:0] data_temp3;wire    [23:0]  po_data1    ; 
wire    [23:0]  po_data2    ; 
//********************************************************************//
//***************************** Main Code ****************************//
//********************************************************************//
//cnt_wait:采样间隔计数器
always@(posedge sys_clk or negedge sys_rst_n)if(sys_rst_n == 1'b0)cnt_wait    <=  18'd0;else    if(state == IDLE || state == WAIT )if(cnt_wait == CNT_WAIT_MAX)cnt_wait    <=  18'd0;elsecnt_wait    <=  cnt_wait + 18'd1;elsecnt_wait    <=  18'd0;//state:状态机状态变量
always@(posedge sys_clk or negedge sys_rst_n)if(sys_rst_n == 1'b0)beginstate   <=  IDLE;byte_addr   <=  16'b0;endelsecase(state)IDLE:beginbyte_addr   <=  16'b0;if(cnt_wait == CNT_WAIT_MAX)state   <=  AD_START1;elsestate   <=  IDLE;endAD_START1:beginstate   <=  AD_CMD1;byte_addr   <=  CTRL_DATA1;endAD_CMD1:if(i2c_end == 1'b1)state   <=  WAIT;elsestate   <=  AD_CMD1;WAIT:beginif(cnt_wait == CNT_WAIT_MAX)beginstate   <=  AD_START2;endelsestate   <=  WAIT;endAD_START2:beginstate   <=  AD_CMD2;byte_addr   <=  CTRL_DATA2;endAD_CMD2:beginif(i2c_end == 1'b1)state   <=  IDLE;elsestate   <=  AD_CMD2;enddefault:state   <=  IDLE;endcase//i2c_start:输入i2c设备触发信号
always@(posedge sys_clk or negedge sys_rst_n)if(sys_rst_n == 1'b0)i2c_start   <=  1'b0;else    if(state == AD_START1 || state == AD_START2)i2c_start   <=  1'b1;elsei2c_start   <=  1'b0;//rd_en:输入i2c设备读使能信号
always@(posedge sys_clk or negedge sys_rst_n)if(sys_rst_n == 1'b0)rd_en   <=  1'b0;else    if(state == AD_CMD1 || state == AD_CMD2)rd_en   <=  1'b1;elserd_en   <=  1'b0;byte_addr:输入i2c设备字节地址
//always@(posedge sys_clk or negedge sys_rst_n)
//    if(sys_rst_n == 1'b0)
//        byte_addr   <=  16'b0;
//    else
//        byte_addr   <=  CTRL_DATA;//ad_data:AD数据
always@(posedge sys_clk or negedge sys_rst_n)if(sys_rst_n == 1'b0)ad_data1 <=  8'b0;else    if((state == AD_CMD1) && (i2c_end == 1'b1)) //(state == AD_CMD) && (i2c_end == 1'b1))ad_data1 <=  rd_data;always@(posedge sys_clk or negedge sys_rst_n)if(sys_rst_n == 1'b0)ad_data2 <=  8'b0;else    if((state == AD_CMD2) && (i2c_end == 1'b1)) //(state == AD_CMD) && (i2c_end == 1'b1))ad_data2 <=  rd_data;//MQ-2:ppm = pow(11.5428 * 35.904 * Vrl/(25.5-5.1* Vrl),(1/0.6549));	
//(1/0.6549)=1.53  
// 11.5428 * 35.904 * Vrl/(25.5-5.1* Vrl)=(414*vrl)/(25.5-5.1* Vrl)
// 
//data_reg:数码管待显示数据缓存assign  data_reg1 = ((ad_data1 * 3300) >> 4'd8);
assign  data_reg2 = ((ad_data2 * 3300) >> 4'd8);assign  data_temp= (414*data_reg1);
assign  data_temp1= 25500-(5*data_reg1);
assign  data_temp2= (414*data_reg2);
assign  data_temp3= 25500-(5*data_reg2);
//po_data:数码管待显示数据
assign  po_data1 = data_temp2/data_temp3;
//转换后的烟雾浓度0-150
assign  po_data2 = data_temp/data_temp1;assign  adc_data1 = po_data1[15:0];
assign  adc_data2 = po_data2[15:0];
endmodule

我这是采集两个通道的adc,对控制字节进行了切换,使用状态机。

在这里插入图片描述
逻辑分析仪读取的正常时序。
该说不说Quartus的逻辑分析仪使用感差于vivado。代码两个平台是通用的

总结

视频演示

FPGA环境监测火灾监测家居监测(温湿度模块、烟雾模块、PM2.5模块等等)蓝牙通信

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

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

相关文章

在Python和C/C++之间共享std::vector<std::vector<int>>数据

在Python和C/C之间共享std::vector<std::vector>数据 在Python和C/C之间共享嵌套向量数据(std::vector<std::vector<int>>)可以通过几种方法实现。以下是几种常见的方法&#xff1a; 方法1: 使用Cython Cython是连接Python和C的很好选择&#xff0c;它可以…

Linux NVIDIA 显卡驱动安装指南(适用于 RHEL/CentOS)

&#x1f4cc; 一、禁用 Nouveau 开源驱动 NVIDIA 闭源驱动与开源的 nouveau 驱动冲突&#xff0c;需先禁用&#xff1a; if [ ! -f /etc/modprobe.d/blacklist-nouveau.conf ]; thenecho -e "blacklist nouveau\noptions nouveau modeset0" | sudo tee /etc/modpr…

Python爬虫实战:获取千库网各类素材图片,为设计师提供参考

一、引言 在当今设计领域,丰富的素材积累对设计师而言至关重要。千库网作为一个素材资源丰富的平台,拥有海量的各类素材图片。然而,手动从该网站收集素材不仅耗时,而且效率低下。Python 作为一种功能强大的编程语言,具备丰富的库和工具,可用于开发高效的爬虫程序。通过 …

vue截图-html2canvas

使用html2canvas进行截图操作 在 Vue 中使用 ​​html2canvas​​ 将 HTML 元素&#xff08;如包含贝塞尔曲线的 Canvas/SVG&#xff09;转换为图片 下载html2canvas npm install html2canvas在页面中使用&#xff0c;要截取哪个div的内容&#xff0c;先给这个div加一个ref标…

介绍Unity中的Dictionary

在 Unity&#xff08;C#&#xff09;中&#xff0c;Dictionary 是一个非常常用的数据结构&#xff0c;它提供 键值对&#xff08;Key-Value Pair&#xff09; 的存储方式。类似于 Python 的 dict 或 JavaScript 的对象&#xff08;Object&#xff09;&#xff0c;但它是强类型的…

MySQL 常用函数(详解)

目录 一、数学函数1.1 四舍五入函数1.2 求绝对值函数二、日期时间函数2.1 获取当前日期和时间三、字符串函数3.1 字符串拼接函数3.2 提取子字符串函数四、聚合函数4.1 计算平均值函数4.2 计算最大值函数五、转换函数5.1 类型转换函数六、总结MySQL 提供了丰富的内置函数,涵盖了…

SOFA编译-Ubuntu20.04-SOFA22.12

一、事前说明 单纯的编译sofa是很简单的&#xff0c;但是想要同时编译SofaPython3则比较难了&#xff0c;我编译了v22.12分支&#xff0c;其他版本sofa的编译也可以参考此篇教程&#xff0c;需注意的是&#xff1a; 1、确定SOFA需要的Python版本&#xff0c;sofa22.12需要的是…

静态BFD配置

AR2配置 int g0/0/0 ip add 10.10.10.2 quit bfd quit bfd 1 bind peer-ip 10.10.10.1 source-ip 10.10.10.2 auto commit AR1配置 int g0/0/0 ip add 10.10.10.1 int g0/0/1 ip add 10.10.11.1 quit bfd quit bfd 1 bind peer-ip 10.0.12.2 source-ip 10.0.12.1 auto co…

关键字where

C# 中的 where 关键字主要用在泛型约束&#xff08;Generic Constraints&#xff09;中&#xff0c;目的是对泛型类型参数限制其必须满足的条件&#xff0c;从而保证类型参数具备特定的能力或特性&#xff0c;增强类型安全和代码可读性。 约束写法说明适用场景举例C#版本要求w…

Arm核的Ubuntu系统上安装Wireshark

Arm核的Ubuntu系统上安装Wireshark 一、安装wireshark 安装命令&#xff1a; sudo apt-get install wireshark-qt 如下图所示&#xff1a; 安装过程弹出如下界面&#xff1a; 鼠标选择Yes&#xff0c;点回车键确认 安装完成。 二、打开wireshark 输入命令行打开wireshark …

编专利或委托他人编专利属于学术不端行为吗?

原文链接&#xff1a;编专利或委托他人编专利属于学术不端行为吗&#xff1f; 自己编专利或委托他人编专利属于学术不端吗&#xff1f; 5月4日&#xff0c;一篇题为《针对性护理干预在子宫肌瘤围手术期的情绪和生活质量临床应用效果》的论文&#xff0c;受到网友的广泛议论。…

Music AI Sandbox:打开你的创作新世界

AI 和音乐人的碰撞 其实&#xff0c;Google 早在 2016 年就启动了一个叫 Magenta 的项目&#xff0c;目标是探索 AI 在音乐和艺术创作上的可能性。一路走来&#xff0c;他们和各种音乐人合作&#xff0c;终于在 2023 年整出了这个 Music AI Sandbox&#xff0c;并且通过 YouTub…

Java游戏服务器开发流水账(2)开发中Maven的管理

Maven 是一款流行的 Java 项目管理工具&#xff0c;它基于项目对象模型&#xff08;Project Object Model&#xff0c;POM&#xff09;的概念来管理项目的构建、依赖和文档等。游戏服务器开发中也会使用. 项目构建 生命周期管理&#xff1a;Maven 定义了一套清晰的项目构建生…

枚举 · 例8扩展-校门外的树:hard

登录—专业IT笔试面试备考平台_牛客网 代码区&#xff1a; #include<algorithm> #include<iostream> #include<vector>using namespace std; struct TREE{int left,right; }; bool compare(const TREE&a,const TREE& b ){if(a.left!b.left){return…

Windows Server 2025 安装AMD显卡驱动

运行显卡驱动安装程序&#xff0c;会提示出问题。但是此时资源已经解压 来到驱动路径 C:\AMD\AMD-Software-Installer\Packages\Drivers\Display\WT6A_INF 打开配置文件&#xff0c;把这两行替换掉 %ATI% ATI.Mfg, NTamd64.10.0...16299, NTamd64.10.0, NTamd64.6.0, NTamd64.…

为什么 MySQL 用 B+ 树作为数据的索引,以及在 InnoDB 中数据库如何通过 B+ 树索引来存储数据以及查找数据

http://www.liuzk.com/410.html 索引是一种数据结构&#xff0c;用于帮助我们在大量数据中快速定位到我们想要查找的数据。 索引最形象的比喻就是图书的目录了。注意这里的大量&#xff0c;数据量大了索引才显得有意义&#xff0c;如果我想要在 [1,2,3,4] 中找到 4 这个数据&am…

AWS VPC架构师指南:从零设计企业级云网络隔离方案

一、VPC核心概念解析 1.1 核心组件 VPC&#xff1a;逻辑隔离的虚拟网络&#xff0c;可自定义IPv4/IPv6地址范围&#xff08;CIDR块&#xff09; 子网&#xff08;Subnet&#xff09;&#xff1a; 公有子网&#xff1a;绑定Internet Gateway&#xff08;IGW&#xff09;&#…

HuggingFace与自然语言处理(从框架学习到经典项目实践)[ 01 API操作 ]

本教程适用与第一次接触huggingface与相应框架和对nlp任务感兴趣的朋友&#xff0c;该栏目目前更新总结如下&#xff1a; ​​Tokenizer​​&#xff1a; 支持单句/双句编码&#xff0c;自动处理特殊符号和填充。 批量编码提升效率&#xff0c;适合训练数据预处理。Datasets​…

【LeetCode 42】接雨水(单调栈、DP、双指针)

题面&#xff1a; 思路&#xff1a; 能接雨水的点&#xff0c;必然是比两边都低&#xff08;小&#xff09;的点。有两种思路&#xff0c;一种是直接计算每个点的最大贡献&#xff08;也就是每个点在纵向上最多能接多少水&#xff09;&#xff0c;另一种就是计算每个点在横向上…

【嵌入式开发-USB】

嵌入式开发-USB ■ USB简介 ■ USB简介