版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
第七章:
单片机与D/A转换、A/D转换目录CONTENTS01数模/模数转换基础认知D/A转换、A/D转换核心概念、基本原理与应用场景04PCF8591与I2C总线应用芯片工作原理、I2C通信协议及呼吸灯项目实现03
ADC0804模数转换应用工作原理、工作流程及电压采集项目实现02DAC0832数模转换应用工作原理、工作方式及Proteus仿真实践05DS18B20与单总线应用温度传感器原理、1-Wire协议及温度测量系统搭建学习目标01基础概念掌握理解D/A转换、A/D转换的核心原理,掌握模拟量与数字量的转换逻辑,了解转换技术在嵌入式系统中的典型应用场景。02数模/模数芯片精通掌握DAC0832、ADC0804的硬件接口、工作方式及编程实现方法,能独立完成Proteus仿真电路搭建与程序调试。03串行通信技术吃透理解PCF8591的I2C通信协议与读写流程,掌握DS18B20的单总线(1-Wire)通信机制,熟练配置控制寄存器并完成数据交互。04工程实践落地能结合D/A、A/D转换技术,完成呼吸灯、数字电压表、温度测量系统等综合项目设计与仿真实现,具备嵌入式模拟信号处理的工程实践能力。7.1DAC0832的工作原理及应用7.1.1DAC0832的工作原理1.D/A转换的概念2.DAC的应用DAC广泛应用于工业自动化和控制系统中,通过将数字信号转换为模拟信号输出来控制电动机驱动、温度调节、液位测量等工业设备,其高分辨率能够提供更精确的控制。在音频处理、音频放大和信号调制等通信设备中,DAC能够将数字音频信号转换为模拟信号,以便在扬声器、耳机或其他音频设备上进行放大和播放。在测试和测量领域中,DAC可用于产生可变的模拟信号,以实现对电压、电流和频率等参数的精确控制,验证和校准各种测量设备。DAC也常用于音频合成器、音序器和其他电子乐器中,通过将数字信号转换为模拟音频输出,实现音高、音色和音量的精确调节。D/A转换即输入数字量,输出一个与数字量相对应的模拟量(电流或电压信号)。例如,参考电压VREF为5V,采用8位DAC,当输入数字量为00000000时,输出电压为0V,当输入数字量为11111111时,输出电压为5V。当输入数字量从00000000变化到11111111时,输出电压从0V变化到5V。这样每个数字量对应一个电压,即D/A转换,DAC是实现D/A转换的电子器件。3.DAC0832
D/A转换即输入数字量,输出一个与数字量相对应的模拟量(电流或电压信号)。例如,参考电压VREF为5V,采用8位DAC,当输入数字量为00000000时,输出电压为0V,当输入数字量为11111111时,输出电压为5V。当输入数字量从00000000变化到11111111时,输出电压从0V变化到5V。这样每个数字量对应一个电压,即D/A转换,DAC是实现D/A转换的电子器件。DAC0832是一款综合性价比高且应用广泛的D/A转换集成芯片。其I/O电平与TTL/CMOS电平相兼容,使用单电源+5~+15V供电,参考电压为-10~+10V,转换时间约为1μs,功耗仅为15mW,采用20脚DIP或SOIC(SmallOutlineIntegratedCircuit,小外形集成电路)式封装。4.DAC0832的D/A转换原理输出电压的大小与数字量的对应关系如下。
DAC0832是常用的8位电流输出型并行低速D/A转换芯片,当需要转换为电压输出时,可外接运算放大器,运算放大器的反馈电阻可通过RFB端引用片内固有电阻(图7.2中的RFB),也可外接电阻。DAC0832内部集成输入锁存器和DAC寄存器,数据输入可采用双缓冲、单缓冲或直通方式,以便满足各种电路的需要(如要求多路D/A异步输入、同步转换等)。DAC由T型R-2R电阻网络、模拟开关、运算放大器等部分组成。5.DAC0832的引脚
DAC的分辨率是指最小输出电压(对应于输入数字量只有最低位为1时的输出电压)和最大输出电压(对应于输入数字量所有有效位均为1时的输出电压)之比,即输入数字量的最低有效位(LSB)发生变化时,所对应的输出模拟量(电压或电流)的变化量。它反映了输出模拟量的最小变化值。DAC的位数越多,分辨率就越高。CS:片选引脚,低电平有效。WR1:输入寄存器的写选通引脚。GND:第3脚的GND为模拟信号地(AGND),第10脚的GND为数字信号地(DGND)。DI0~DI7:数字信号输入引脚,TTL电平。VREF:基准电压输入引脚,要求外接精密电压源(-10~+10V)。RFB:反馈信号输入引脚,反馈电阻集成在芯片内部。IOUT1、IOUT2:电流输出引脚,电流IOUT1和IOUT2的和为常数(约为VREF/R),当输入全为1时IOUT1最大,当输入全为0时,IOUT2最大。IOUT1和IOUT2随DAC寄存器中内容的变化而变化。单极性输出时,IOUT2通常接地。XFER:数据传送引脚,低电平有效。WR2:DAC寄存器写选通引脚。ILE:数据允许锁存引脚,高电平有效。VCC:电源输入引脚(+5~+15V)7.1.2DAC0832的工作方式及应用1.DAC0832的工作方式
DAC0832的内部结构框图如图7.4所示,在进行D/A转换时,可通过对输入锁存器和DAC寄存器的不同控制,实现以下三种工作方式:单缓冲方式、双缓冲方式、直通方式。(1)单缓冲方式。单缓冲方式是指控制输入锁存器和DAC寄存器同时接收数据,或者只控制输入锁存器接收数据,把DAC寄存器接成直通方式,如图7.5所示。此方式适用于只有一路模拟量输出或几路模拟量异步输出的场景。(2)双缓冲方式。
双缓冲方式是指先使输入锁存器接收数据,再控制输入锁存器输出数据到DAC寄存器,即分两次锁存输入,如图7.6所示。此方式适用于多个D/A转换同步输出的场景。(3)直通方式。
直通方式是指数据不经输入锁存器和DAC寄存器锁存,即WR1、CS、WR2、XFER引脚均接地,ILE引脚接高电平,如图7.7所示。数字量一旦输入,就直接进入DAC寄存器,进行D/A转换。此方式适用于连续反馈控制线路,不过在使用时,必须另加I/O口与CPU连接,以匹配CPU与DAC。2.DAC0832的单片机应用
根据图7.9设计一个用单片机控制DAC0832的Proteus仿真实例,仿真电路如图7.9所示,单片机P0口接DAC0832的DI0~DI7,通过用单片机控制DAC0832输出锯齿波、方波、正弦波等信号,用运算放大器LM324将输出的电流信号转换为电压信号。用单片机控制DAC0832输出锯齿波(使用输入锁存器和DAC寄存器)的参考例程mcu701.c的代码如下。#include<reg51.h>#include<absacc.h>#defineucharunsignedchar#defineuintunsignedint#defineDAC0832XBYTE[0x7FFF]//DAC0832在系统中的地址为0x7FFFvoiddelay(uintx)//延时函数{uchari;while(x)for(i=0;i<120;i++);}voidmain(){uchari;while(1){for(i=0;i<256;i++)//产生锯齿波DAC0832=i;delay(1);}}上机操作一下吧,注意虚拟示波器的波形调试,也可以在输出端连接1只LED,观察LED的明暗变化!
在例程mcu701.c中,由于输入锁存器和DAC寄存器地址相同,因此同时接收输入数据,仍然是单缓冲的工作方式。采用另一种更加直接的单缓冲方式,只控制使用输入锁存器而把DAC寄存器接成直通方式,即将DAC0832的XFER、WR2引脚全部接单片机的数字地,DAC0832的CS引脚接单片机的P3.2/INT0引脚,DAC0832的WR1引脚接单片机的P3.6/WR引脚,DAC0832的VREF引脚接参考电压+5V。DAC0832单缓冲方式接口电路如图7.10所示,参照该电路设计并实现Proteus仿真,输出端电路可参照图7.9。用单片机控制DAC0832输出锯齿波(只使用输入锁存器)的参考例程mcu702.c的代码如下。#include<reg51.h>sbitdawr=P3^6;sbitcsda=P3^2;unsignedchara,j,k;voiddelay(unsignedchari){for(j=i;j>0;j--)for(k=125;k>0;k--);}voidmain(){csda=0;a=0;dawr=0;while(1){unsignedcharm;for(m=0;m<255;m++){P0=m;delay(20);}}}上机验证并实现仿真,比较一下例程mcu701.c和例程mcu702.c,思考并理解其中的异同之处。完成上述工作后,现在就可以轻松写出方波、正弦波等其他波形信号的对应程序了,实际上把复合语句段while(1){}数据输入部分的代码进行改写即可,如用单片机控制DAC0832输出方波的复合语句段while(1){}的代码可以改写如下。while(1){P0=0xff;delay(500);P0=0x00;delay(500);}那么三角波、正弦波等其他波形如何实现呢?课后尝试实现一下吧!7.2ADC0804的工作原理及应用7.2.1ADC0804的工作原理1.A/D转换的概念
A/D转换是指输入模拟量(如电压信号),输出一个与模拟量相对应的数字量(常为二进制形式)。例如,参考电压VREF为5V,采用8位ADC,当输入电压为0V时,输出数字量为00000000,当输入电压为5V时,输出数字量为11111111。当输入电压从0V到5V变化时,输出数字量从00000000到11111111变化。这样每个输入电压对应一个输出数字量,即实现了A/D转换。ADC是实现A/D转换的电子器件,主要功能是将模拟信号(如来自传感器或信号转换器的连续电信号)转换为数字信号,便于计算机或微处理器进行处理、存储和进一步分析。ADC通常由模拟前端和数字后端组成,模拟前端负责将模拟信号转换为数字信号,而数字后端则负责处理这些数字信号。2.ADC的应用3.ADC0804
ADC广泛应用于音频图像处理、传感器数据采集、数据记录系统及通信设备等场景中,在这些场景中,模拟信号需要被转换为数字形式以供处理和存储。在音视频处理领域,数字照相机、摄像机和视频编辑软件等应用都需要ADC,用于捕获、处理、显示图像和视频信息。在工业测量和控制领域,ADC在自动化、过程控制、传感器数据采集等方面发挥着重要作用,用于将模拟传感器信号转换为数字信号进行分析和控制。在医疗设备中,ADC用于将各种生理信号(如心电图、脑电图和血压等)转换为数字信号,以便进行诊断和监测。4.ADC0804的A/D转换原理
模拟信号转换为数字信号的过程包括采样、量化和编码。(1)采样。采样是指将连续的模拟信号在特定的时间点上取样,得到离散的样本,采样频率必须高于信号中的最高频率,以满足奈奎斯特定理。(2)量化。量化是指将每个样本的幅度值映射为有限数量的离散值,形成数字信号。例如,一个8位ADC可能将电压范围划分为256个级别,并为每个电压点分配一个相应的二进制值。(3)编码。编码是指将量化后的值转换为二进制数字形式,形成数字输出,通常涉及查找表或编码机制的应用。ADC按转换原理划分,主要分为并行比较式ADC、逐次逼近式ADC、计数式ADC、积分式(间接式)ADC等类型,其中并行比较式ADC的速度快,积分式ADC的抗干扰能力强,计数式ADC的造价低。ADC0804是属于逐次逼近式ADC,其内部结构原理图如图7.12所示。
逐次逼近式ADC的A/D转换原理与天平称重原理相似,下面以ADC0804为例说明逐次逼近式ADC的A/D转换原理,其动作步骤如图7.13、图7.14、图7.15所示(原则上先从左侧最高位寻找起)第一次寻找结果:10000000(若假设值≤输入值,则寻找位=假设位=1)。第二次寻找结果:11000000(若假设值≤输入值,则寻找位=假设位=1)。第三次寻找结果:11000000(若假设值>输入值,则寻找位=假设位=0)。第四次寻找结果:11010000(若假设值≤输入值,则寻找位=假设位=1)。第五次寻找结果:11010000(若假设值>输入值,则寻找位=假设位=0)。第六次寻找结果:11010100(若假设值≤输入值,则寻找位=假设位=1)。第七次寻找结果:11010110(若假设值≤输入值,则寻找位=假设位=1)。第八次寻找结果:11010110(若假设值>输入值,则寻找位=假设位=0)。这样使用二分法的寻找方式,8位ADC只需进行8次寻找,12位ADC只需进行12次寻找,就能完成A/D转换,其中的输入值代表模拟输入电压VI。5.ADC0804的引脚ADC0804的引脚排列如图7.16所示。
ADC的分辨率是指使输出数字量变化1时对应的输入模拟量的变化值。分辨率与ADC的位数有确定的关系,可以表示成FS/2n。FS表示满量程输入值,n为ADC的位数。ADC的位数越多,分辨率就越高。
7.2.2ADC0804的工作过程及应用1.ADC0804的工作过程ADC0804的转换时序图如图
所示。A/D转换主要包含三个过程。2.ADC0804的单片机应用
设计一个用单片机控制ADC0804的Proteus仿真实例,向ADC0804的模拟量通道输入一个可变模拟电压信号,转换成数字量0~255并显示,即实现一个量程为5V的直流电压表,仿真电路如图7.18所示,ADC0804的引脚VCC接5V电源,VREF/2引脚悬空(悬空相当于与VCC共接5V电源),因此A/D转换的参考电压为5V。VIN-引脚接地,而VIN+引脚连接滑动变阻器RV1,因此VIN+的电压输入范围为0~+5V,控制ADC0804对VIN+引脚输入的电压进行正确采样,读取采样结果。
引脚接地,
和
引脚分别连接单片机的P3.6和P3.7引脚,而DB0~DB7连接单片机的P1口。单片机的P0口接数码管的段选线,P2口低四位接数码管的位选线。数字电压表的参考例程mcu703.c的代码如下。#include<reg51.h>#include<intrins.h>#defineuintunsignedint#defineucharunsignedcharsbitwr=P3^6;sbitrd=P3^7;ucharcodedis[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90};//共阳极数码管显示代码
voiddelay(uintx)//延时函数delay延时0.992ms,大约为1ms{uchari;while(x--)for(i=0;i<120;i++);}voiddisplay(uchardb)//数码管显示函数,用于显示A/D转换后得到的数字量{ucharbw,sw,gw;//bw、sw、gw分别为db的百位、十位、个位上的数
bw=db/100;sw=db%100/10;gw=db%10;P2=0x01;//点亮第一只数码管P0=dis[bw]&0x7f;//最高位置0,点亮第一只数码管的小数点delay(5);P2=0x02;//点亮第二只数码管P0=dis[sw];delay(5);P2=0x04;//点亮第三只数码管P0=dis[gw];delay(5);P2=0x08;//点亮第四只数码管P0=dis[0];//第四只数码管一直显示0delay(5);}voidmain(){uchari;while(1)
{wr=0;//片选信号CS为低电平,CS接地_nop_();wr=1;//WR由低电平到高电平时,ADC开始转换delay(1);//延时1ms,等待转换结束P1=0xff;//接收数据前先写1rd=0;//将RD引脚置低电平后,再延时大于135ns(这里延时1μs)即可从DB口读出//有效的转换结果,并将其传送到单片机P1口_nop_();for(i=0;i<10;i++)//刷新显示一段时间display(P1);//显示从DB口得到的数字量
}}
上机操作一下吧,改变滑动变阻器的阻值,观察数码管的显示数值变化。思考既然我们设计的是一个量程为5V的数字电压表,那么该如何显示实际转换的电压呢?你可能会说“要不乘2?”但是2.55×2=5.10,显然并不可行。考虑在显示函数部分对转换后的0~255的数据进行处理,如通过(X/255)×5的运算可得到实际电压的精确数值,然后取整,送显即可,参考代码如下。voiddisplay(uchardb)//数码管显示函数,用于显示A/D转换后得到的数字量{ucharbw,sw,gw;//bw、sw、gw分别等于db的百位、十位、个位上的数value=db*5.0/255.0;//转换value0=(unsignedint)(value*1000);//将value强制转换为整型数据,然后取出各位的值zheng=value0/1000;xiao1=value0/100%10;xiao2=value0/10%10;xiao3=value0%10;//四舍五入if(xiao3>=5)xiao2++;P2=0x01;//点亮第一只数码管
P0=dis[zheng]&0x7f;//最高位置0,点亮第一只数码管的小数点,若极性相反,则和0x80相与delay(5);P2=0x02;//点亮第二只数码管P0=dis[xiao1];delay(5);P2=0x04;//点亮第三只数码管P0=dis[xiao2];delay(5);P2=0x08;//点亮第四只数码管P0=dis[0];//第四只数码管一直显示0delay(5);}7.3工程实践
单片机针对信号转换的应用,一般采用能够同时兼顾A/D转换和D/A转换两种功能的芯片,以提高应用系统的集成度与功能性,本书配套设计的开发板就采用了这样一款常用的基于I2C总线的8位信号转换芯片PCF8591。PCF8591的接口电路如图7.19所示,内部结构框图如图7.20所示,该芯片的D/A转换和A/D转换原理与DAC0832和ADC0804一样,采用的也是电阻网络和逐次逼近方法。7.3.1PCF8591及其应用1.PCF8591
PCF8591是单片、单电源、低功耗的8位CMOS数据采集器件,如图7.21所示。其具有4个模拟输入引脚(用作A/D转换通道)、1个模拟输出引脚(用作D/A转换通道)和1个串行I2C总线接口(与单片机进行通信)。3个地址引脚A0、A1和A2用于硬件地址编程,允许将最多8个器件连接至I2C总线而不需要额外硬件。器件的地址、控制和数据通过两条双向I2C总线以串行方式进行传输。PCF8591的功能包括多路模拟输入、内置跟踪保持、8位A/D转换和8位D/A转换,最大转化速率取决于I2C总线的最高速率。PCF8591的引脚如图7.22所示,其中,AIN0~AIN3是4个模拟输入引脚;VSS为负电源引脚;VDD为正电源引脚;VREF为参考电压引脚,可接VCC;EXT是内部/外部时钟的切换开关引脚,使用内部时钟时EXT接地;OSC是外部时钟信号的输入引脚;A0~A2为I2C相关的硬件地址输入引脚;AGND为模拟地;SDA和SCL为I2C总线的数据线、时钟线(SDA即A/D转换数字输出引脚);AOUT为D/A转换模拟输出引脚。2.I2C总线技术
I2C总线是一种串行二线制同步半双工(双向)总线,由飞利浦公司于20世纪80年代开发,目的是使主板、嵌入式系统或手机连接低速周边设备。I2C硬件结构简单、接口连接方便、成本低,因此应用很广。I2C总线在源设备与被控外设(目标设备)之间、外设与外设之间等都可以进行双向数据通信,使用多主多从架构,所有设备上的串行数据线SDA都接到总线的SDA上,各设备的时钟线SCL接到总线的SCL上。通过对SCL和SDA两根线高、低电平时序的控制,产生I2C总线所需要的信号,从而进行数据传递。
通常情况下,把带I2C接口的模块作为主设备(Master),主设备负责产生定时时钟,启动数据发送或终止数据接收;把挂接在I2C总线上的其他设备作为从设备(Slave),从设备被动接收和发送数据。I2C总线的数据传输速率在标准模式下可达100kbit/s,在快速模式下可达400kbit/s,在高速模式下可达3.4Mbit/s。一般通过I2C接口编程时钟SCL来实现数据传输速率的调整,数据传输速率也和所接的上拉电阻的阻值有关。I2C通信协议规则如下。(1)数据的读写,即数据域的高稳定、低变化。主、从设备都必须遵循SDA的电平变化在SCL的一个时钟周期内,在SCL被拉至低电平时,SDA上的高低电平状态才允许变化,即此时才可以写入数据;在SCL被拉至高电平时,SDA必须保持数据的稳定,此时才可以读取数据。I2Cstretch功能(时钟SCL拉长)是指在进行数据通信时,主设备发送速度太快会导致从设备来不及实现内部的数据操作,因此从设备会控制SCL时钟线主动拉低SCL电平,使主设备暂时停止数据的交互,等待从设备完成内部的数据操作后,再主动释放拉低的SCL,恢复正常通信。向数据线SDA串行写入数据的参考代码如下。voidSend(unsignedcharData){unsignedcharBitCounter=8;unsignedchartemp;do{temp=Data;Scl=0;_nop_();if((temp&0x80)==0x80)Sda=1;elseSda=0;Scl=1;temp=Data<<1;Data=temp;BitCounter--;}while(BitCounter);Scl=0;}从数据线SDA串行读取数据的参考代码如下。unsignedcharRead(void){unsignedchartemp=0;unsignedchartemp1=0;unsignedcharBitCounter=8;Sda=1;do{Scl=0;_nop_();Scl=1;_nop_();if(Sda)temp=temp|0x01;elsetemp=temp&0xfe;if(BitCounter-1){temp1=temp<<1;temp=temp1;}BitCounter--;}while(BitCounter);return(temp);}voidStart(void){Sda=1;_nop_();Scl=1;_nop_();Sda=0;_nop_();Scl=0;}I2C总线结束信号的参考代码如下。voidStop(void){Sda=0;_nop_();Scl=1;_nop_();Sda=1;_nop_();Scl=0;}I2C总线起始信号的参考代码如下。(2)Start(起始)和Stop(结束)这两个信号都由主设备产生,不属于数据域交互。在SCL的一个时钟周期内,不再遵循高稳定低变化规则,而是遵循相反的逻辑,即在SCL为高电平时,主设备将SDA的电平由高拉低视为Start信号(下降沿);在SCL为高电平时,主设备将SDA的电平由低拉高视为Stop信号(上升沿)。
起始和结束信号总是由主设备产生,这意味着从设备不可以主动通信,所有通信都是由主设备发起的,主设备可以发出询问命令,然后等待从设备的通信。ACK信号的参考代码如下。NACK信号的参考代码如下。voidAck(void){Sda=0;_nop_();Scl=1;_nop_();Scl=0;_nop_();}voidNoAck(void){Sda=1;_nop_();Scl=1;_nop_();Scl=0;_nop_();}(3)总线应答ACK是指主设备在数据线SDA上将1字节数据逐次传输完后,从设备紧接着在SCL为高电平时,将SDA的电平由高拉低(或者继续保持低电平状态),即回传给主设备一个应答位(应答信号ACK),此时才认为1字节数据真正地传输完成。当然,并不是所有的数据传输都必须有一个应答位,当从设备不能接收到主设备发送的数据时,主设备在SCL为高电平时,检测到SDA为高电平则是NACK信号,即从设备回传了一个非应答位(非应答信号)。3.PCF8591的应用
主设备(单片机)先向SDA发送一个起始信号S后,再发送从设备(PCF8591)地址(7位)+读/写位(0写/1读)。然后从设备开始将自己的地址和主设备发送的地址进行匹配,如果发现相同,则从设备会发送一个应答信号ACK。主设备接收到从设备发送的ACK信号后,会再发送一个从设备内部内存的地址来指定主设备要写入的内存位置。从设备接收到数据后会产生应答信号ACK并回传给主设备,主设备接收到从设备发送的ACK信号后,开始进行数据传输。每次从设备接收到数据都会发送ACK信号,当主设备发送完数据不想继续发送时,会向SDA发送结束信号P。
注意:无论是主设备还是从设备,发送数据都是把数据先放入自己的移位寄存器中保存之后再通过总线进行数据的发送,接收数据则相反。
用单片机控制PCF8591进行D/A转换的程序设计思路及流程:(1)发送起始信号。(2)发送从设备PCF8591的地址。(3)等待PCF8591回应,获取从设备应答。(4)写入控制字节。(5)等待PCF8591回应。(6)写DAC数据寄存器的值。(7)等待PCF8591回应。(8)发送结束信号,I2C数据传输结束。
第(6)步和第(7)步可以一直循环进行,且DAC数据寄存器的值可以一直改变,只要没有新的起始信号或者结束信号,DAC输出就一直是最后一个输出的值。PCF8591的D/A转换时序图如图7.23所示。PCF8591D/A转换的参考代码如下。#defineAddWr0x90//写数据地址#defineAddRd0x91//读数据地址voidDAC(unsignedcharData){Start();Send(AddWr);//写入芯片地址waitAck();//获取应答Send(0x40);//写入控制字节,使能模拟输出waitAck();Send(Data);//写数据waitAck();Stop();}
代码中的设备地址用预处理命令#define进行了宏定义,分别为读数据地址0x91和写数据地址0x90,D/A转换显然只涉及写数据地址。控制字节内容为0x40,作用是使能模拟输出AOUT。
PCF8591采用I2C接口器件寻址,其设备地址字节由固定部分、可编程部分和方向位组成。固定部分为1001。可编程部分必须根据地址引脚A0、A1和A2进行设置,其值由用户选择,因此I2C系统中最多可接=8个具有I2C接口的PCF8591。设备地址字节的最后一位是方向位,它决定了接下来数据传输的方向(为0是写,为1是读)。PCF8951的控制字节用于实现器件的各种功能,控制字节存储在控制寄存器中,I2C总线操作时,其为主设备发送的第二个字节。
其中,D0、D1是A/D转换通道选择位,D1D0为00时选择通道AIN0,D1D0为01时选择通道AIN1,D1D0为10时选择通道AIN2,D1D0为11时选择通道AIN3。D2是自动递增标志位,若将其置1,则每次检测完一个通道后会自动检测下一个通道。D3恒为0。D4、D5用于控制模拟输入的模式,不同模式对应不同数量的通道。D4D5为00时,四路单端输入;D4D5为01时,三路差分输入;D4D5为10时,单端与差分混合输入;D4D5为11时,两路差分输入。一般用单端输入模式(D4D5=00)较多。D6用于控制模拟信号输出或输入,A/D转换时该位置0,D/A转换时该位置1D7恒为0。
在实际应用中,如果使用DAC输出功能,那么控制字节为0x40。
如果使用通道0为单端输入通道,采集A/D转换的数据,那么控制字节为0x00。
PCF8591的ADC采用逐次逼近转换技术,其A/D转换周期在传输一个有效的读数据地址到PCF8591后开始,A/D转换时序图如图7.26所示,A/D转换周期在应答时钟脉冲的下降沿触发,一旦A/D转换周期被触发,所选通道的输入电压样本就被转换成相应的8位二进制码,开始A/D转换。
用单片机控制PCF8591进行A/D转换的程序设计思路及流程:(1)发送起始信号。
(2)发送PCF8591写数据地址。(3)等待PCF8591回应,获取应答。(4)写控制字节,设置转换通道及方式。(5)等待PCF8591回应。
(6)发送起始信号。(7)发送PCF8591读数据地址。(8)等待PCF8591回应。(9)读取PCF8591串行输出数据。(10)发送非应答信号。(11)发送结束信号,I2C数据传输结束。第(9)步和第(10)步可以循环进行,直到不需要A/D转换。PCF8591的通道AIN1接可变电位器,模式为单端输入,进行A/D转换,参考代码如下。#defineSLAVEADDR0x90biti2c_readbyte(uint8*dat){i2c_start();i2c_sendbyte(SLAVEADDR);//发送写数据地址if(i2c_waitACK())//获取应答return1;i2c_sendbyte(0x01);//发送控制字节if(i2c_waitACK())return1;i2c_start();i2c_sendbyte(SLAVEADDR+1);//发送读数据地址if(i2c_waitACK())return1;*dat=i2c_recbyte();//读数据并存入该地址i2c_ACK(0);//发送非应答信号NACKi2c_stop();return0;}
A/D转换既涉及写数据地址,也涉及读数据地址。上述代码中用预处理命令#define对设备写数据地址进行了宏定义,读数据地址通过写数据地址0x90+1来设置。控制字节内容为0x01,表明使用单端输入模式下的通道AIN1。任务1呼吸灯
利用PCF8591输出模拟电压信号,练习PCF8591的D/A转换使用方法。如图7.19所示,
芯片PCF8591的模拟输出端AOUT接LED指示灯D12,设定PCF8591为D/A转换模式,实现呼吸灯的亮度变化效果,参考例程mcu704.c的代码如下。#include<reg52.h>//包含头文件#include<intrins.h>//包含NOP空指令函数_nop_()#definenops()do{_nop_();_nop_();_nop_();_nop_();_nop_();}while(0)//定义空指令#defineAddWr0x90//写数据地址#defineAddRd0x91//读数据地址#defineLIGHTNUM150//数字越小,LED越亮#defineDIMNUM200//数字越大,LED越暗sbitSda=P2^0;//定义I2C接口sbitScl=P2^1;voidmDelay(unsignedcharj){unsignedinti;for(;j>0;j--){for(i=0;i<125;i++){;}}}voidInit_Timer1(void){TMOD|=0x10;TH1=0xff;//设置计数初值TL1=0x00;PT1=1;//设置优先级EA=1;//开总中断ET1=1;//开T1中断TR1=1;}/*------------------------------------------------启动I2C总线数据传输------------------------------------------------*/voidStart(void){Sda=1;nop_();Scl=1;_nop_();Sda=0;_nop_();Scl=0;}/*------------------------------------------------停止I2C总线数据传输------------------------------------------------*/voidStop(void){Sda=0;_nop_();Scl=1;_nop_();Sda=1;_nop_();Scl=0;}/*------------------------------------------------获取I2C总线应答------------------------------------------------*//***函数:waitACK()*功能:单片机等待I2C器件发送应答信号*说明:返回值为0表示收到ACK,返回值为1表示没收到ACK*/bitwaitACK(){Sda=1;nops();Scl=1;nops();if(Sda){Scl=0;Stop();return1;}else{Scl=0;return0;}}/*------------------------------------------------发送一个字节------------------------------------------------*/voidSend(unsignedcharData){unsignedcharBitCounter=8;unsignedchartemp;do{temp=Data;Scl=0;_nop_();if((temp&0x80)==0x80)Sda=1;elseSda=0;Scl=1;temp=Data<<1;Data=temp;BitCounter--;}while(BitCounter);Scl=0;}/*------------------------------------------------写入D/A转换值------------------------------------------------*/voidDAC(unsignedcharData){Start();Send(AddWr);//写入芯片地址if(waitACK())nops();Send(0x40);//写入控制字节,使能模拟输出if(waitACK())nops();Send(Data);//写数值if(waitACK())nops();Stop();}/*------------------------------------------------主函数------------------------------------------------*/voidmain(){unsignedcharnum=DIMNUM,flag=0;//定义DAC输入值unsignedcharADtemp;//定义中间变量Init_Timer1();while(1){if(num==LIGHTNUM)flag=1;if(num==DIMNUM)flag=0;if(flag==0)num--;//num不断减小,直到为LIGHTNUM,LED逐渐变亮elsenum++;//num不断增大,直到为DIMNUM,LED逐渐变暗DAC(num);//DAC输出,可以用LED反映电压变化,150~190之间亮,值越小LED越亮mDelay(30);}}//延时,用于清晰看出变化
上机操作一下吧,注意观察LED的明暗变化是否有呼吸灯的效果。若采用8位DAC,VREF=VCC(+5V),则VLSB约等于0.0195V,DAC输入值num在0~255之间。可以尝试改变DAC输入值,观察LED的明暗变化,思考D/A转换的数值逻辑。试一试改写程序,实现方波、锯齿波等其他波形的输出,观察现象并验证。任务2数字电压表利用PCF8591采集模拟电压信号,练习PCF8591的A/D转换使用方法。在图7.19所示电路的基础上,将芯片PCF8591的模拟输入引脚AIN1接电位器,设定PCF8591为A/D转换模式,实现1个量程为+5V的数字电压表,参考例程mcu705.c的代码如下。#include<reg52.h>#include<intrins.h>//包含NOP空指令函数_nop_()#include"stdio.h"#include"stdlib.h"typedefunsignedcharu8;//重命名unsignedchar为u8typedefunsignedintu16;//重命名unsignedint为u16typedefunsignedlongu32;//重命名unsignedlong为u32typedefchars8;//重命名char为s8typedefints16;//重命名int为s16typedeflongs32;typedefunsignedcharuint8;typedefunsignedintuint16;#definenops()do{_nop_();_nop_();_nop_();_nop_();_nop_();}while(0)//定义空函数延时#defineSLAVEADDR0x90//PCF8591器件写地址#defineAddWr0x90//写数据地址
#defineAddRd0x91//读数据地址//函数声明voiddelay_ms(u16ms);voiddisp_one_data(u8dd,u8wei);voiddisp_one_data_dot(u8dd,u8wei);//显示带小数点的1位数voiddisplay(u32x,u8dot_pos);//变量定义sbitSCL=P2^1;//I2C总线时钟SCLsbitSDA=P2^0;//I2C总线数据SDAsbitSda=P2^0;//定义I2C总线接口sbitScl=P2^1;sbitseg_latch=P2^6;//段锁存控制位,P2.6引脚发送段锁存脉冲给74HC573锁存器sbitbit_latch=P2^7;//位锁存控制位,P2.7引脚发送位锁存脉冲给74HC573锁存器codeu8tab[]={0X3f,0X06,0X5B,0X4F,0X66,0X6D,0X7D,0X07,0X7F,0X6F,x77,0x7C,0x39,0x5E,0x79,0x71};//共阴极数码管段码表codeu8tab_dot[]={0XBf,0X86,0XDB,0XCF,0XE6,0XED,0XFD,0X87,0XFF,0XEF};//带小数点的0~9codeu8tab_wei[7]={0xff,0xdf,0xef,0xf7,0xfb,0xfd,0xfe};//位码表/*起始信号*/voidi2c_start(){SCL=1;nops();SDA=1;nops();SDA=0;nops();SCL=0;}/***函数:i2c_stop()*功能:停止I2C总线数据传输*/voidi2c_stop(){SCL=0;nops();SDA=0;nops();SCL=1;nops();SDA=1;nops();}/***函数:i2c_ACK(bitck)*功能:单片机向I2C器件发送应答信号*ck为1时发送应答信号ACK,ck为0时不发送ACK*/voidi2c_ACK(bitck){if(ck)SDA=0;elseSDA=1;nops();SCL=1;nops();SCL=0;nops();SDA=1;nops();}/*单片机等待PCF8951发送应答信号*返回值为0表示收到ACK,返回值为1表示没收到ACK*/biti2c_waitACK(){SDA=1;nops();SCL=1;nops();if(SDA){SCL=0;i2c_stop();return1;}else{SCL=0;return0;}}*函数:i2c_sendbyte(uint8bt)*功能:单片机向PCF8951发送1字节数据bt*/voidi2c_sendbyte(uint8bt){uint8i;
for(i=0;i<8;i++){if(bt&0x80)SDA=1;elseSDA=0;nops();SCL=1;bt<<=1;nops();SCL=0;}}/***函数:i2c_recbyte()*功能:单片机从I2C总线上接收1字节数据*/uint8i2c_recbyte(void){uint8d,i;for(i=0;i<8;i++){SCL=1;nops();d<<=1;if(SDA)d|=0x01;SCL=0;nops();}returnd;}/***函数:i2c_readbyte()*功能:单片机从PCF8951读出1字节数据*输入:cmd为控制字节(地址);*dat用于存储读出的数据*返回值:0表示成功,1表示失败*/biti2c_readbyte(uint8cmd,uint8*dat){i2c_start();i2c_sendbyte(SLAVEADDR);//发送写数据地址if(i2c_waitACK())return1;i2c_sendbyte(cmd);//发送控制字节if(i2c_waitACK())return1;i2c_start();i2c_sendbyte(SLAVEADDR+1);//发送读数据地址if(i2c_waitACK())return1;*dat=i2c_recbyte();//读数据i2c_ACK(0);//只读取1字节数据,不发送ACK信号i2c_stop();return0;}voiddisp_one_data(u8dd,u8wei)//dd为要显示数字,wei表示显示位置{P0=0xff;//关闭原来的所有显示bit_latch=1;//送位码bit_latch=0;P0=tab[dd];//要显示的数seg_latch=1;//送段码seg_latch=0;P0=tab_wei[wei];//送6位数码管位码bit_latch=1;//送位码bit_latch=0;delay_ms(1);//显示时间_nop_();}voiddisp_one_data_dot(u8dd,u8wei)//显示一位带小数点的数字//wei表示在哪一位上显示,1:最低位(个位);6:最高位(十万位){P0=0xff;//关闭原来的所有显示bit_latch=1;//送位码bit_latch=0;P0=tab_dot[dd];//要显示的数seg_latch=1;//送段码seg_latch=0;P0=tab_wei[wei];bit_latch=1;//送位码bit_latch=0;delay_ms(1);//显示时间}voiddisplay(u32x,u8dot_pos)//x为要显示的数,dot_pos为显示小数点的数的位置{u8swan,wan,qian,bai,shi,ge;swan=x/100000;//得到十万位数wan=(x/10000)%10;//取整得到万位数qian=(x/1000)%10;//取整得到千位数bai=(x/100)%10;//取整得到百位数shi=(x/10)%10;//取整得到十位数ge=x%10;//取整得到个位数/*显示十万位数*/if(swan!=0)//如果十万位上的数值不为0则显示,否则不显示{if(dot_pos==1)disp_one_data_dot(swan,6);elsedisp_one_data(swan,6);}/*显示带小数点的万位数*/if((wan!=0)||(swan!=0)){if(dot_pos==2)disp_one_data_dot(wan,5);elsedisp_one_data(wan,5);}/*显示千位数*/if((qian!=0)||(wan!=0)||(swan!=0)){if(dot_pos==3)disp_one_data_dot(qian,4);elsedisp_one_data(qian,4);}/*显示带小数点的百位数*/if((bai!=0)||(qian!=0)||(wan!=0)||(swan!=0))//如果百位上的数值不为0则显示,//否则不显示{if(dot_pos==4)disp_one_data_dot(bai,3);elsedisp_one_data(bai,3);}/*显示十位数*/if((shi!=0)||(bai!=0)||(qian!=0)||(wan!=0)||(swan!=0))//如果十位上的数值不//为0则显示,否则不显示{if(dot_pos==5)disp_one_data_dot(shi,2);elsedisp_one_data(shi,2);}/*显示个位数*/{if(dot_pos==6)disp_one_data_dot(ge,1);elsedisp_one_data(ge,1);}}voiddelay_ms(u16ms)//定义延时函数{u16i,j;for(i=0;i<ms;i++)for(j=0;j<123;j++);}main(){uint8ans;//存储PCF8591的A/D转换结果while(1){i2c_readbyte(0x41,&ans);//控制字节为0x41,转换结果存储于变量ans地址单元display(ans,6);//显示最多6位整数,6表示最右边数码管显示小数点}}
上机操作一下吧,PCF8591的A/D转换结果为8位二进制数,通过数据线SDA发送给单片机,单片机再发送给数码管予以显示。旋转电位器的旋钮,改变输入的模拟电压,注意观察数码管显示数值的变化!
观察到数码管的显示数值在0~255范围内变化,既然设计的是一个量程为+5V的数字电压表,那么该如何显示实际转换的电压呢?参考本章例程mcu703.c,将原送显数据通过算法处理后再送显,快去试一试吧,注意小数点位置变化的处理。7.3.2数字温度传感器DS18B20及其应用1.数字温度传感器DS18B20
DS18B20是由DALLAS半导体公司推出的一种单总线通信接口的温度传感器,其控制命令和数据都是以数字信号的方式输入或输出的,测温范围为-55~+125℃。相较于模拟温度传感器,DS18B20具有功能强大、硬件简单、易扩展、抗干扰性强等特点。DS18B20的封装跟三极管一样,为TO-92封装。DS18B20的主要特性如下:适应电压范围宽,为3.0~5.5V,可由数据线供电;单总线接口方式,一根口线即可实现与微处理器的双向通信;支持多点组网,其64位ROM中的64位序列号可被看作是该DS18B20的唯一地址识别码;一根总线上可挂接多个DS18B20,但在实际应用中单总线上挂接的DS1820一般不超过8个,否则就需要解决总线驱动问题;可编程的分辨率为9~12位;最小可分辨温度为0.0625℃,可实现高精度测温;测量结果直接以数字温度信号形式输出,利用单总线串行传送给CPU,同时可传送CRC(CyclicRedundancyCheck,循环冗余校验)码,具有极强的抗干扰纠错能力。
DS18B20的功能引脚有三个,如图7.27所示,其中,DQ为数字信号I/O端;GND为电源地;VDD为外接供电电源输入端(在寄生电源供电方式下接地)。
(1)DS18B20的工作原理。
如图7.28所示,DS18B20集成了模拟温度传感器和微控制器。其读取内部的模拟温度传感器,然后通过内部转换(利用内部的控制器把温度数据读出来)把温度数据存储在RAM中。我们只需要通过引脚和单总线通信协议把该温度数据读取出来即可。整个转换过程都是以数字量输入或输出的,不需要单片机有A/D转换功能,也不需要外部A/D转换芯片,因此应用起来比较简单。
DS18B20内部的模拟温度传感器测温是通过计数比较两个不同温度系数晶振的频率来实现的。低温度系数晶振的振荡频率受温度的影响很小,用于产生相当于固定频率的脉冲信号并传送给减法计数器1。高温度系数晶振是敏感振荡器,其产生的信号作为减法计数器2的输入脉冲,为减法计数器2提供一个频率随温度变化的计数脉冲。
当计数门打开时,DS18B20就对低温度系数晶振产生的时钟脉冲进行计数,进而完成温度测量。计数门的开启时间由高温度系数晶振决定,每次测量前,首先将-55℃所对应的基数分别置入减法计数器1和温度寄存器中。减法计数器1对低温度系数晶振产生的脉冲信号进行减法计数,当减法计数器1的预置值减到0时,温度寄存器的值将加1,减法计数器1的预置值将重新被装入,减法计数器1重新开始对低温度系数晶振产生的脉冲信号进行计数,如此循环直到减法计数器2计数到0时,停止温度寄存器值的累加,此时温度寄存器中的数值即为被测温度。斜率累加器用于补偿和修正测温过程中的非线性,其输出用于修正减法计数器1的预置值,只要计数门仍未关闭就重复上述过程,直至温度寄存器值达到被测温度。(2)DS18B20的存储器。DS18B20的主要数据部件有三个。①64位ROM,即光刻ROM。其中储存的64位序列号是出厂前被光刻好的,可以将其看作该DS18B20的地址识别码。64位序列号的排列顺序:开始8位是产品类型标号,接下来的48位是该DS18B20自身的序列号,每个DS18B20的序列号都不相同,最后8位是前面56位的CRC码。由于每一个DS18B20都拥有全球唯一的序列号,因此微控制器可以通过单总线对多个DS18B20进行寻址,从而达到一根总线上挂接多个DS18B20的目的。②
高速暂存器,即RAM寄存器,用于总线数据交互,由9字节组成,其分配如表7.1所示。当温度转换指令发布后,经转换所得的温度以二字节补码形式存储于字节0~字节1中。单片机可通过单总线接口读取,读时低位在前,高位在后。高字节和低字节合起来组成16位数据,这16位数据的前5位,即D11~D15表示符号位,如果温度是负的,那么这5位全是1,如果温度是正的,那么这5位全是0;后4位用于存小数,D3是1代表存的是0.5,D2是1代表存的是0.25,D1是1代表存的是0.125,D0是1代表存的是0.0625,所以最低位变化一次,整个温度变化0.0625;剩下的D4~D10都是温度的整数部分。
参数配置寄存器的低5位一直都是“1”,TM是测试模式位,用于设置DS18B20处于工作模式还是处于测试模式,在DS18B20出厂时该位被设置为0,用户不需要改动。R1和R0用来设置温度分辨率,如表7.3所示,DS18B20出厂时,温度分辨率被设置为12位。
字节5~字节7是保留位,没有使用,器件升级后可能会用到。
字节8是CRC码,就是把前面8个字节进行运算得到的校验码,用于通信时判断接收的数据是否正确无误。③EEPROM用于保存温度阈值和配置参数。当向高速暂存器字节2~字节4写数据时,需要发送一条指令把高速暂存器中的内容复制到EEPROM中。当然,也可以发送指令将EEPROM中的内容调回高速暂存器中。也就是说,高速暂存器是用来进行系统参数设置和运行时操作控制的,EEPROM则对应进行备份存储,在上电时,会自动把EEPROM中3字节的内容读入对应高速暂存器的字节单元中。(3)DS18B20的工作流程。
根据DS18B20的单总线通信协议,主设备(单片机)控制DS18B20完成温度转换必须经过三个步骤:每
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2025届浙江省海运集团股份有限公司校园招聘17人笔试历年参考题库附带答案详解
- 2025届中国市政工程中南设计研究总院有限公司秋季校园招聘69人启动啦笔试历年参考题库附带答案详解
- 2025中铁一局集团有限公司生态资源建设分公司招聘19人笔试历年参考题库附带答案详解
- 跨境电商平台支付结算流程标准化手册
- 风力发电设备运行与维护手册
- 青春期发育时相和发育速度与儿童睡眠时长关联的队列研究
- 营销团队绩效评估与激励制度
- 河南省南阳市新未来联考2025-2026学年高二下学期4月期中测评历史试卷(含答案)
- 2026年员工培训计划告知7篇范文
- 人工智能应用行业研究报告
- 2026年储能电站运维人员考试题库
- 中国典籍英译概述课件
- 2024年6月浙江省高考生物试卷真题(含答案解析)
- 高中语文新课标必背古诗文72篇
- 水闸设计过水流量和水闸设计规范毕业论文
- 《国际市场营销》课程标准
- 小学道法6 人大代表为人民1课件
- 色盲检测图(俞自萍第六版)
- 以焦炉气为原料合成甲醇项目可行性研究报告
- 文胸基础知识培训专家讲座
- 海产鱼类增养殖试题库
评论
0/150
提交评论