1 模拟量与数字量概述

        如温度、压力、位移、图像等都是模拟量,电子线路中模拟量通常包括模拟电压和模拟电流,生活用电220V交流正弦波就属于模拟电压,随着负载大小的变化,其电流大小也跟着变化,这里的电流信号也属于模拟电流,下图所表示的信号就属于模拟量。

像上图所示信号的幅值随着时间变化而连续变化的量就是模拟量,模拟量有可能是标准的正弦波,有可能是不规则的任何波形,也有可能是规则的方波、三角波等,当我们用数值表示其大小时,通常用十进制数表示,如2.3V,5A,47N等
        单片机系统内部运算时用的全部是数字量,即0和1,因此对单片机系统而言,我们无法直接操作模拟量,必须将模拟量转换成数字量。所谓数字量,就是用一系列0和1组成的二进制代码表示某个信号大小的量。用数字量表示同一个模拟量时,数字位数可以多也可以少,位数越多则表示的精度越高,位数越少表示的精度就越低。比如对上右图中的正弦波模拟量,我们可以用一个00000~11111的5位二进制数字量来表示它,5位二进制数最多只能有32种组合形式,因此需把这个正弦波最大值与最小值之间分成32等分,每一等分用一组5位二进制数来表示。很显然,如果用32等分数字量表示这个模拟量的话,任意两相邻等分之间的模拟量我们便无法表示出来,唯有增加等分,也就是再增加数字量的位数才可表示出来。因此,若要用数字量完全表示一个模拟量的话,其数字量位数就为无穷多位。但若要设计出这样的硬件,当今的技术还无法实现。
        单片机在采集模拟信号时,通常都需要在前端加上模拟量/数字量转换器,简称模/数转换器,即常说的A/D(AnalogtoDigital) 芯片。当单片机在输出模拟信号时,通常在输出级要加上数字量/模拟量转换器,简称数/模转换器,即常说的D/A(DigitaltoAnalog) 芯片,下面几节分别讲解A/D和D/A的原理。

2 A/D 转换原理及参数指标

在A/D转换器中,因为输入的模拟信号在时间上是连续的,而输出的数字信号代码是离散的,所以A/D转换器在进行转换时,必须在一系列选定的瞬间(时间坐标轴上的一些规定点上)对输入的模拟信号采样,然后再把这些采样值转换为数字量。因此,一般的A/D转换过程是通过采样保持、量化和编码这三个步骤完成的,即首先对输入的模拟电压信号采样,采样结束后进入保持时间,在这段时间内将采样的电压量转化为数字量,并按一定的编码形式给出转换结果,然后开始下一次采样。下图给出模拟量到数字量转换过程的框图。

        采样定理:

可以证明, 为了正确无误地用下左图中所示的采样信号 Vs 表示模拟信号 VI,必须满足:fs≥2fimax
        式中,fs为采样频率,fimax为输入信号VI的最高频率分量的频率。上式就是所谓的采样定理。
        在满足采样定理的条件下,可以用一个低通滤波器将信号Vs还原为VI,这个低通滤波器的电压传输系数IA(f)I在低于fimax的范围内应保持不变,而在fs-fimax以前应迅速下降为零,如下右图所示。因此,采样定理规定了A/D转换的频率下限。

因此,A/D转换器工作时的采样频率必须高于上式所规定的频率。采样频率提高以后,留给A/D转换器每次进行转换的时间也相应缩短了,这就要求转换电路必须具备更快的工作速度。因此,不能无限制地提高采样频率,通常取fs=(3~5)fimax已经能够满足要求。
        因为每次把采样电压转换为相应的数字量都需要一定的时间,所以在每次采样以后,必须把采样电压保持一段时间。可见,进行A/D转换时所用的输入电压,实际上是每次采样结束时的VI值。

        量化和编码

我们知道,数字信号不仅在时间上是离散的,而且数值上的变化也不是连续的。这就是说,任何一个数字量的大小,都是以某个最小数量单位的整倍数来表示的。因此,在用数字量表示采样电压时,也必须把它化成这个最小数量单位的整倍数,这个转化过程就叫做量化。所规定的最小数量单位叫做量化单位,用△表示。显然,数字信号最低有效位中的1表示的数量大小,就等于△。把量化的数值用二进制代码表示,称为编码。这个二进制代码就是A/D转换的输出信号。
        既然模拟电压是连续的,那么它就不一定能被△整除,因而不可避免地会引入误差,我们把这种误差称为量化误差。在把模拟信号划分为不同的量化等级时,用不同的划分方法可以得到不同的量化误差。
        假定需要把0~1v的模拟电压信号转换成三位二进制代码,这时便可以取△=(1/8)V,并规定凡数值在0~(1/8)V之间的模拟电压都当做0x△,看待,用二进制的000表示;凡数值在(1/8)~(2/8)V之间的模拟电压都当做1X△看待,用二进制的001表示,……,如下图(a)所示。不难看出,最大的量化误差可达△,即(1/8)V。

为了减少量化误差,通常采用上图(b)所示的划分方法,取量化单位△= (2/15)V,并将000代码所对应的模拟电压规定为0~ (1/15)V,即0~△/2。这时,最大量化误差将减少为△/2=(1/15)V。这个道理不难理解,因为现在把每个二进制代码所代表的模拟电压值规定为它所对应的模拟电压范围的中点,所以最大的量化误差自然就缩小为△/2了。

2.1 采样-保持电路

        电路组成及工作原理:
        N沟道MOS管T作为采样开关用。

当控制信号VL为高电平时,T导通,输入信号VI经电阻Ri和T向电容Ch充电。若取Ri=Rf,则充电结束后Vo=-VI=Vc。
        当控制信号返回低电平,T截止。由于Ch无放电回路,所以Vo的数值被保存下来。
        缺点:采样过程中需要通过Ri和T向Ch充电,所以使采样速度受到了限制。同时,Ri的数值又不允许取得很小,否则会进一步降低采样电路的输入电阻。
        改进电路及其工作原理:
        下图是单片集成采样-保持电路LE198的电路原理图及符号,它是一个经过改进的采样-保持电路。下图中AI、A2是两个运算放大器,S是电子开关,L是开关的驱动电路,当逻辑输入VL为1,即VL为高电平时,S闭合;VL为0,即低电平时,S断开。

当S闭合时,A1、A2均工作在单位增益的电压跟随器状态,所以vo=v’o=vI。如果将电容Ch接到R2的引出端和地之间,则电容上的电压也等于vI。当vL返回低电平以后,虽然S断开了,但由于Ch上的电压不变,所以输出电压vo的数值得以保持下来。
        在S再次闭合以前的这段时间里,如果vI发生变化,v’o可能变化非常大,甚至会超过开关电路所能承受的电压,因此需要增加D1和D2构成保护电路。当v’o比vo所保持的电压高(或低)一个二极管的压降时,D1(或D2)导通,从而将v’o限制在叩+vI+vD以内。而在开关S闭合的情况下,v’o和vo相等,故D1和D2均不导通,保护电路不起作用。
        直接A/D转换器:
        直接A/D转换器能把输入的模拟电压直接转换成输出的数字量而不需要经过中间变量。常用的电路有并行比较型和反馈比较型两类。
        1)并行比较型A/D转换器:
        三位并行比较型A/D转换原理电路如下图所示,它由电压比较器、寄存器和代码转换器三部分组成。电压比较器中量化电平的划分采用上上上图(b)所示的方式,用电阻链把参考电压VREF分压,得到从(1/15)*VREF(13/15)*VREF之间7个比较电平,量化单位△=(2/15)*VREF。然后,把这7个比较电平分别接到7个比较器C1~C7的输入端作为比较基准。同时将要输入的模拟电压同时加到每个比较器的另一个输入端上,与这7个比较基准进行比较。

单片集成并行比较型A/D转换器的产品较多,如AD公司AD9012 (8位)、AD9002(8位)AD9020(10位)等。
        并行AD转换器具有如下特点:
                ①由于转换是并行的,其转换时间只受比较器、触发器和编码电路延迟时间限制,因此转换速度快。
                ②随着分辨率的提高,元件数目要按几何级数增加。一个n位转换器,所用的比较器个数为2n-1,如8位的并行A/D转换器就需要28-1=255个比较器。由于位数愈多,电路愈复杂,因此制成分辨率较高的集成并行A/D转换器是比较困难的。
                ③使用这种含有寄存器的并行A/D转换电路时,可以不用附加采样-保持电路,因为比较器和寄存器这两部分也兼有采样-保持功能,这也是该电路的一个优点。

并行比较型A/D转换器的缺点:需要用很多的电压比较器和触发器。从上上图得知,输出为n位二进制代码的转换器中应当有2n-1个电压比较器和2n-1个触发器。电路的规模随着输出代码位数的增加而急剧增加。如果输出为10位二进制代码,则需要用2^10-1=1023个比较器和1023个触发器以及一个规模相当大的代码转换电路。
        2)反馈比较型A/D转换器:
        反馈比较型A/D转换器的构思是:取一个数字量加到D/A转换器上,于是得到一个对应的输出模拟电压,将这个模拟电压和输入的模拟电压信号比较,如果两者不相等,则调整所取的数字量,直到两个模拟电压相等为止,最后所取的这个数字量就是所求的转换结果。
        在反馈比较型A/D转换器中经常采用的有计数型和逐次比较型两种方案。
        下图为计数型A/D转换器原理框图。转换电路由比较器C、D/A 转换器、计数器、脉冲源、控制门G以及输出寄存器等几部分组成。

转换开始前先用复位信号将计数器置0,而且转换信号应停留在VL=0的状态,这时门G被封锁,计数器不工作。计数器加给D/A转换器的是全0信号,所以D/A转换器输出的模拟电压vo=0。如果VI为正电压信号,比较器的输出电压为1。依同样方法比较完DA的全部位数。
        因为在转换过程中计数器中的数字不停地变化,所以不宜将计数器的状态直接作为输出信号,为此在输出端设置了输出寄存器,在每次转换完成以后,用转换控制信号的下降沿将计数器输出的数字置入输出寄存器中,而以寄存器的状态作为最终的输出信号。这个方案的明显问题是:转换时间长,当输出为n位二进制数码时,最长的转换时间可达到2^n-1倍的时钟信号周期,因此这种方法只能用在对转换速度要求不高的场合。然而由于它的电路非常简单,所以在对转换速度没有严格要求时仍是一种可取的方案。
        为了提高转换速度,在计数型A/D转换的基础上又产生了逐次比较型A/D转换器。逐次逼近转换过程与用天平称物重非常相似。按照天平称重的思路,逐次比较型A/D转换器,就是将输入模拟信号与不同的参考电压做多次比较,使转换所得的数字量在数值上逐次逼近输入模拟量的对应值。
        三位逐次比较型A/D转换器的逻辑电路如下图所示。这是一个输出为三位二进制数的逐次比较型A/D转换器。下图中C为电压比较器,当vI>=vo时,比较器的输出为0;反过来,输出为1。FFA,FFB,FFC三个触发器组成了三位数码寄存器,触发器FF1~FF5和门电路G1~G9组成控制逻辑电路。

由以上分析可见,逐次比较型A/D转换器完成一次转换所需时间与其位数和时钟脉冲频率有关,位数愈少,时钟频率越高,转换所需时间越短。这种A/D转换器具有转换速度快,精度高的特点。
        集成逐次比较型A/D转换器有ADC0804/0808/0809系列(8)位、AD575(10位)、AD574A(12位)等。
        间接A/D转换器:
        目前使用的间接A/D转换器多半都属于电压-时间变换型(V-T变换型)电压-频率变换型(V-F变换型)两类。
        在V-T变换型A/D转换器中,首先把输入的模拟电压信号转换成与之成正比的时间宽度信号,然后在这个时间宽度里对固定频率的时钟脉冲计数,计数的结果就是正比于输入模拟电压的数字信号。
        在V-F变换型A/D转换器中,则首先把输入的模拟电压信号转换成与之成正比的频率信号,然后在一个固定的时间间隔里对得到的频率信号计数,所得到的结果就是正比于输入模拟电压的数字量。
        下面给出V-T变换型和V-F变换型两种A/D的结构框图,大家了解即可。

        A/D转换器的参数指标:
        ①分辨率—它说明A/D转换器对输入信号的分辨能力。
        A/D转换器的分辨率以输出二进制数的位数表示。从理论上讲,n位输出的A/D转换器能区分2n个不同等级的输入模拟电压,能区分输入电压的最小值为满量程输入的1/2n,在最大输入电压一定时,输出位数愈多,量化单位愈小,分辨率愈高。常用的有8、10、12、16、24、32位等。例如,A/D转换器输出为8位二进制数,输入信号最大值为5V,那么这个转换器应能区分输入信号的最小电压为19.53mV(5Vx1/28≈19.53mV)。再如,某A/D转换器输入模拟电压的变化范围为-10V~+10V,转换器为8位,若第一位用来表示正、负号,其余7位表示信号幅值,则最末一位数字可代表80mV模拟电压(10Vxl/27≈80mV),即转换器可以分辨的最小模拟电压为80mV。而同样情况下,用一个10位转换器能分辨的最小模拟电压为20mV(10Vx1/2^9≈20mV)。
        ②转换误差—表示A/D转换器实际输出的数字量与理论输出数字量之间的差别。
        在理想情况下,输入模拟信号所有转换点应当在一条直线上,但实际的特性不能做到输入模拟信号所有转换点在一条直线上。转换误差是指实际的转换点偏离理想特性的误差,一般用最低有效位来表示。例如,给出相对误差< ±LSB/2,这就表明实际输出的数字量和理论上应得到的输出数字量之间的误差小于最低位的一半。注意,在实际使用中当使用环境发生变化时,转换误差也将发生变化。
        ③转换精度—它是A/D转换器的最大量化误差和模拟部分精度的共同体现。
        具有某种分辨率的转换器在量化过程中由于采用了四舍五入的方法,因此最大量化误差应为分辨率数值的一半。如上例,8位转换器最大量化误差应为40mV(80mVx0.5=40mV),全量程的相对误差则为0.4%(40mV/10Vx100%)。可见,A/D转换器数字转换的精度由最大量化误差决定。实际上,许多转换器末位数字并不可靠,实际精度还要低一些。
        由于含有A/D转换器的模/数转换模块通常包括有模拟处理和数字转换两部分,因此整个转换器的精度还应考虑模拟处理部分(如积分器、比较器等)的误差。一般转换器的模拟处理误差与数字转换误差应尽量处在同一数量级,总误差则是这些误差的累加和。例如,一个10位A/D转换器用其中9位计数时的最大相对量化误差为2^9x0.5≈0.1%,若模拟部分精度也能达到0.1%,则转换器总精度可接近0.2%。
        ④转换时间—指A/D转换器从转换控制信号到来开始,到输出端得到稳定的数字信号所经过的时间。
        不同类型的转换器转换速度相差甚远。其中并行比较A/D转换器转换速度最高,8位二进制输出的单片集成A/D转换器转换时间可达50μs以内。逐次比较型A/D转换器次之,它们多数转换时间在10~50µs之间,也有达几百纳秒的。间接A/D转换器的速度最慢,如双积分A/D转换器的转换时间大都在几十毫秒至几百毫秒之间。在实际应用中,应从系统数据总的位数、精度要求、输入模拟信号的范围及输入信号极性等方面综合考虑A/D转换器的选用。
        例如:某信号采集系统要求用一片A/D转换集成芯片在1s(秒)内对16个热电偶的输出电压分时进行A/D转换。已知热电偶输出电压范围为0-0.025V(对应于0~450°C温度范围),需要分辨的温度为0.1°C,试问应选择多少位的A/D转换器,其转换时间为多少?
        解:对于从0-450°C温度范围,信号电压范围为0~0.025v,分辨温度为0.1°C,这相当于0.1/450=1/4500的分辨率。12位A/D转换器的分辨率为1/2^12=1/4096,所以必须至少选用13位的A/D转换器。
        系统的采样速率为16次每秒,采样时间为62.5ms。对于这样慢的采样速度,任何一个A/D转换器都可以达到。可选用13位或13位以上带有采样-保持(S/H)的逐次比较型A/D转换器或不带S/H的双积分式A/D转换器均可。

3 PCF8591工作原理及其实现方法

        1)特性:
                单电源供电
                工作电压: 2.5V~6V
                待机电流低
                I2C 总线串行输入输出
                通过 3个硬件地址引脚编址
                采样速率取决于 I2C总线速度
                4个模拟输入可编程为单端或差分输入
                自动增量通道选择
                模拟电压范围: vss VDD
                片上跟踪与保持电路
                8位逐次逼近式AD转换
                带一个模拟输出的乘法 DAC2
        2)应用:
                闭环控制系统
                用于远程数据采集的低功耗转换器
                电池供电设备
                在汽车、音响和TV应用方面的模拟数据采集
        3)概述:
                PCF8591是单片、单电源低功耗8位CMOS数据采集器件,具有4个模拟输入、一个输出和一个串行I2C总线接口。3个地址引脚A0、A1和A2用于编程硬件地址,允许将最多8个器件连接至I2C总线而不需要额外硬件。器件的地址、控制和数据通过两线双向I2C总线传输。
                器件功能包括多路复用模拟输入、片上跟踪和保持功能、8位模数转换和8位数模拟转换。最大转换速率取决于I2C总线的最高速率。
        4)内部框图:

        5)引脚:

        6)功能描述:
                ①地址
                I2C总线系统中的每一-片 PCF8591通过发送有效地址到该器件来激活。该地址包括固定部分和可编程部分。可编程部分必须根据地址引脚A0、Al和A2来设置。在I2C总线协议中地址必须是起始条件后作为第一个字节发送。地址字节的最后-位是用于设置以后数据传输方向的读/写位。(见图4、16、17)

              ②控制字
                发送到PCF8591的第二个字节将被存储在控制寄存器,用于控制器件功能。控制寄存器的高半字节用于允许模拟输出,和将模拟输入编程为单端或差分输入。低半字节选择-个由高半字节定义的模拟输入通道(见图5)。如果自动增量(auto-incremnent) 标志置1,每次AD转换后通道号将自动增加。
                如果自动增量(auto-increment) 模式是使用内部振荡器的应用中所需要的,那么控制字中模拟输出允许标志应置1。这要求内部振荡器持续运行,因此要防止振荡器启动延时的转换错误结果。模拟输出允许标志可以在其他时候复位以减少静态功耗。
                选择一个不存在的输入通道将导致分配最高可用的通道号。所以,如果自动增量(auto-increment) 被置1,下一个被选择的通道将总是通道0。两个半字节的最高有效位(即bit7和bit 3)是留给未来的功能,必须设置为逻辑0。控制寄存器的所有位在上电复位后被复位为逻辑0。D/A转换器和振荡器在节能时被禁止。模拟输出被切换到高阻态。

                ③D/A转换
                发送给PCF8591的第三个字节被存储到DAC数据寄存器,并使用片上D/A转换器转换成对应的模拟电压。这个D/A转换器由连接至外部参考电压的具有256个接头的电阻分压电路和选择开关组成。接头译码器切换-一个接头至DAC输出线(见图6)。
                模拟输出电压由自动清零单位增益放大器缓冲。这个缓冲放大器可通过设置控制寄存器的模拟输出允许标志来开户或关闭。在激活状态,输出电压将保持到新的数据字节被发送。

片上D/A转换器也可用于逐次逼近A/D转换(sccessive approximation AD conversion)。为释放用于AD转换周期的DAC,单位增益放大器还配备了一个跟踪和保持电路。在执行AD转换时该电路保持输出电压。
                提供给模拟输出AOUT的输出电压由图7中的公式给出。D/A转换顺序的波形见图8。

                ④A/D转换
                AD转换器采用逐次逼近转换技术。在AD转换周期将临时使用片上D/A转换器和高增益比较器。一个AD转换周期总是开始于发送一个有效读模式地址给PCF8591之后。AD转换周期在应答时钟 脉冲的后沿被触发,并在传输前一-次转换结果时执行 (见图9)。

一旦一个转换周期被触发,所选通道的输入电压采样将保存到芯片并被转换为对应的8位二进制码。
                取自差分输入的采样将被转换为8位二进制补码。(见图10和图11)。

转换结果被保存在ADC数据寄存器等待传输。如果自动增量标志被置1,将选择下一个通道。
                在读周期传输的第一个字节包含前一次读周期的转换结果代码。以上电复位之后读取的第一个字节是0x80。I2C总线协议的读周期如图8、图16和图17。最高AD转换速率取决于实际的I2C总线速度。
                ⑤参考电压
                对DA和AD转换,稳定的参考电压和电源电压必须提供给电阻分压电路(引脚VREP和AGND)。AGND引脚必须连接到系统模拟地,并应该有-一个参考vsS的直流偏置。
                低频可应用于VREF和AGND引脚。这允许D/A转换器作为一-象限乘法器使用:见第15章和图7。 A/ D转换器也可以用作-一个或两个象限模拟除法。模拟输入电压除以参考电压。其结果被转换为二进制码。在这种应用中,用户必须保持在转换周期的参考电压稳定。
                ⑥振荡器
                片上振荡器产生AD转换周期和刷新自动清零缓冲放大器需要的时钟信号。在使用这个振荡器时EXT引脚必须连接到VsS。在OSC引脚振荡频率是可用的。
                如果EXT引脚被连接到VDD,振荡输出OSC将切换到高阻态以允许用户连接外部时钟信号至OSC。
        I2C总线特性:
        IC总线是不同的IC或模块之间的双向两线通信。这两条线是串行数据线(SDA)和串行时钟线(SCL)。这两条线必须通过上拉电路连接至正电源。数据传输只能在总线不忙时启动。
                ①位传输
                个数据位在每一个时钟脉冲期间传输。SDA线上的数据必须在时钟脉冲的高电压期间保持稳定,这个期间数据线上的改变将被当作控制信号。

                ②开始或停止条件
                数据和时钟线在总不忙时保持高电平。在时钟为高电平时,数据线上的一个由高到低的变化被定义为开始条件。时钟为高电平时,数据线上的一一个由低到高的变化被定义为停止条件。

              ③系统配置
                产生信息的器件称作“发送机”,接收信息的器件称作“接收机”。控制信息的器件称作“主机”,被控制的器件称作“从机”。

④应答
                在开始和停止条件之间从发送机传输到接收机的数据字节数是没有限制的。每个8位数据字节之后紧跟着一个应答位。应答位是由发送机放在总线的一个高电平,而主机也产生一个额外的与应答有关的时钟脉冲。地址匹配的从接收机必须在接收每个字节后产生一个应答。然而主机在接收到每个已经被从发送机终止的字节后必须产生一个应答。在应答时钟脉冲期间,应答的器件必须将SDA线拉低,因此在应答相应的时钟脉冲的高电平期间,SDA 线必须保持稳定的低电平。在由从机终止的最后一个字节,主接收机必须通过产生-一个低电平应答向发送机发出一个数据结束信号,这样发送机必须将数据线拉高以允许主机产生停止条件。

                ⑤I2C总线协议:
                在开始条件后-一个有效的硬件地址必须发送至PCF8591。读/写位定义了以后单个或多个字节数据传输的方向。开始条件、停止条件和应答位的格式和定时参考I2C总线特性。在写模式,数据传输通过发送下一个数据传输的停止条件或开始条件来结束。

        应用信息:
        输入未用时必须连接至VSS或VDD。模拟输入也可以连接至AGND或VREF。
        为了防止额外的地线和电源噪声,以及减小数字信号对模拟信号路径的串扰,用户在必须要非常仔细地设计PCB。从电源到PCF8591器件、到噪声数字电路、到地的环路应该避免。建议在电源和参考电压输入端加上去耦电容(10uF)。

4 PCF8591驱动程序

#include <reg52.h>
#include "24c02_Sum.h"
#include "pcf8591_Sum.h"
#include "1602_Sum.h"#define  PCF8591 0x90    //PCF8591 地址     该芯片的固定I2C地址 ,PDF资料上提供。unsigned char AD_temp;
unsigned int  D[1]; //定义一个数组的空间,内部有 8个字节,该程序只需使用一个字节void main()
{lcdinit_1602();    //1602液晶初始化Disp_1602(1,1,"  PCF8591-TEST  ",16);  //写1602显示框架Disp_1602(1,2,"  DATA: 000     ",16);while(1){     ISendByte(PCF8591,0x40); // 0x40 对应AD 输入 AIN0   0x41 对应AD 输入 AIN1 以此类推。D[1]=IRcvByte(PCF8591);  //ADC3   模数转换4AD_temp=D[1];DACconversion(PCF8591,0x40, AD_temp); //DAC      数模转换//*********************************************************************write_twoline_1602(9,AD_temp); //把读出的数据送 1602显示//********************************************************************* }
}
/*
* 文 件 名:24c02.c
* 芯    片:24c02
* 晶    振:11.0592MHz
* 创 建 者:XK
* 创建日期:2011.9.17
* 修 改 者:
* 修改日期:
* 功能描述:24c02,读写数据函数
*/#include <reg52.h>
#include <intrins.h>
#include "24c02_Sum.h"#define  NOP()   _nop_()   /*定义空指令*/
#define  _Nop()  _nop_()   /*定义空指令*/sbit     SCL=P2^1;     //I2C  时钟 sbit     SDA=P2^0;     //I2C  数据 bit ack;                 /*应答标志位*///AT2402的功能函数
/*******************************************************************向有子地址器件发送多字节数据函数
函数原型: bit  ISendStr(UCHAR sla,UCHAR suba,ucahr *s,UCHAR no);
功能:     从启动总线到发送地址,子地址,数据,结束总线的全过程,从器件地址sla,子地址suba,发送内容是s指向的内容,发送no个字节。如果返回1表示操作成功,否则操作有误。
注意:    使用前必须已结束总线。
********************************************************************/
bit ISendStr(unsigned char  sla,unsigned char  suba,unsigned char  *s,unsigned char  no)
{unsigned char i;Start_I2c();               /*启动总线*/SendByte(sla);             /*发送器件地址*/if(ack==0)return(0);SendByte(suba);            /*发送器件子地址*/if(ack==0)return(0);for(i=0;i<no;i++){   SendByte(*s);            /*发送数据*/if(ack==0)return(0);s++;} Stop_I2c();                /*结束总线*/ return(1);
}/*******************************************************************向有子地址器件读取多字节数据函数
函数原型: bit  RecndStr(UCHAR sla,UCHAR suba,ucahr *s,UCHAR no);
功能:     从启动总线到发送地址,子地址,读数据,结束总线的全过程,从器件地址sla,子地址suba,读出的内容放入s指向的存储区,读no个字节。如果返回1表示操作成功,否则操作有误。
注意:    使用前必须已结束总线。
********************************************************************/
bit IRcvStr(unsigned char  sla,unsigned char  suba,unsigned char  *s,unsigned char  no)
{unsigned char i;Start_I2c();                  /*启动总线*/SendByte(sla);                /*发送器件地址*/if(ack==0)return(0);SendByte(suba);               /*发送器件子地址*/if(ack==0)return(0);Start_I2c();                 /*重新启动总线*/SendByte(sla+1);if(ack==0)return(0);for(i=0;i<no-1;i++){   *s=RcvByte();               /*发送数据*/Ack_I2c(0);                /*发送就答位*/  s++;} *s=RcvByte();Ack_I2c(1);                   /*发送非应位*/Stop_I2c();                   /*结束总线*/ return(1);
}/******************************************************************* /*******************************************************************起动总线函数
函数原型: void  Start_I2c();
功能:     启动I2C总线,即发送I2C起始条件.
********************************************************************/
void Start_I2c()
{SDA=1;         /*发送起始条件的数据信号*/_Nop();SCL=1;_Nop();        /*起始条件建立时间大于4.7us,延时*/_Nop();_Nop();_Nop();_Nop();    SDA=0;         /*发送起始信号*/_Nop();        /* 起始条件锁定时间大于4μs*/_Nop();_Nop();_Nop();_Nop();       SCL=0;       /*钳住I2C总线,准备发送或接收数据 */_Nop();_Nop();
}/*******************************************************************结束总线函数
函数原型: void  Stop_I2c();
功能:     结束I2C总线,即发送I2C结束条件.
********************************************************************/
void Stop_I2c()
{SDA=0;      /*发送结束条件的数据信号*/_Nop();       /*发送结束条件的时钟信号*/SCL=1;      /*结束条件建立时间大于4μs*/_Nop();_Nop();_Nop();_Nop();_Nop();SDA=1;      /*发送I2C总线结束信号*/_Nop();_Nop();_Nop();_Nop();
}/*******************************************************************字节数据发送函数
函数原型: void  SendByte(UCHAR c);
功能:     将数据c发送出去,可以是地址,也可以是数据,发完后等待应答,并对此状态位进行操作.(不应答或非应答都使ack=0)     发送数据正常,ack=1; ack=0表示被控器无应答或损坏。
********************************************************************/
void  SendByte(unsigned char  c)
{unsigned char  BitCnt;for(BitCnt=0;BitCnt<8;BitCnt++)  /*要传送的数据长度为8位*/{if((c<<BitCnt)&0x80)SDA=1;   /*判断发送位*/else  SDA=0;                _Nop();SCL=1;               /*置时钟线为高,通知被控器开始接收数据位*/_Nop(); _Nop();             /*保证时钟高电平周期大于4μs*/_Nop();_Nop();_Nop();         SCL=0; }_Nop();_Nop();SDA=1;                /*8位发送完后释放数据线,准备接收应答位*/_Nop();_Nop();   SCL=1;_Nop();_Nop();_Nop();if(SDA==1)ack=0;     else ack=1;        /*判断是否接收到应答信号*/SCL=0;_Nop();_Nop();
}/*******************************************************************字节数据接收函数
函数原型: UCHAR  RcvByte();
功能:        用来接收从器件传来的数据,并判断总线错误(不发应答信号),发完后请用应答函数应答从机。
********************************************************************/
unsigned char   RcvByte()
{unsigned char  retc;unsigned char  BitCnt;retc=0; SDA=1;                       /*置数据线为输入方式*/for(BitCnt=0;BitCnt<8;BitCnt++){_Nop();           SCL=0;                  /*置时钟线为低,准备接收数据位*/_Nop();_Nop();                 /*时钟低电平周期大于4.7μs*/_Nop();_Nop();_Nop();SCL=1;                  /*置时钟线为高使数据线上数据有效*/_Nop();_Nop();retc=retc<<1;if(SDA==1)retc=retc+1;  /*读数据位,接收的数据位放入retc中 */_Nop();_Nop(); }SCL=0;    _Nop();_Nop();return(retc);
}/********************************************************************应答子函数
函数原型:  void Ack_I2c(bit a);
功能:      主控器进行应答信号(可以是应答或非应答信号,由位参数a决定)
********************************************************************/
void Ack_I2c(bit a)
{if(a==0)SDA=0;              /*在此发出应答或非应答信号 */else SDA=1;_Nop();_Nop();_Nop();      SCL=1;_Nop();_Nop();                    /*时钟低电平周期大于4μs*/_Nop();_Nop();_Nop();  SCL=0;                     /*清时钟线,钳住I2C总线以便继续接收*/_Nop();_Nop();
}
#include "24c02_Sum.h"
#include "pcf8591_Sum.h"
extern bit ack;
/*******************************************************************
DAC 变换, 转化函数
*******************************************************************/
bit DACconversion(unsigned char sla,unsigned char c,  unsigned char Val)
{Start_I2c();              //启动总线SendByte(sla);            //发送器件地址if(ack==0)return(0);SendByte(c);              //发送控制字节if(ack==0)return(0);SendByte(Val);            //发送DAC的数值  if(ack==0)return(0);Stop_I2c();               //结束总线return(1);
}/*******************************************************************
ADC发送字节[命令]数据函数
*******************************************************************/
bit ISendByte(unsigned char sla,unsigned char c)
{Start_I2c();              //启动总线SendByte(sla);            //发送器件地址if(ack==0)return(0);SendByte(c);              //发送数据if(ack==0)return(0);Stop_I2c();               //结束总线return(1);
}/*******************************************************************
ADC读字节数据函数
*******************************************************************/
unsigned char IRcvByte(unsigned char sla)
{  unsigned char c;Start_I2c();          //启动总线SendByte(sla+1);      //发送器件地址if(ack==0)return(0);c=RcvByte();          //读取数据0Ack_I2c(1);           //发送非就答位Stop_I2c();           //结束总线return(c);
}//******************************************************************/
/*
* 文 件 名:1602led.c.c
* 芯    片:1602液晶
* 晶    振:11.0592MHz
* 创 建 者:XK
* 创建日期:2011.8.6
* 修 改 者:Sumjess
* 修改日期:2019.5.29
* 功能描述:1602,写数据函数
*/#include <reg52.h>
#include "1602_Sum.h"
#include "delay_Sum.h"
#include "74hc595_Sum.h"sbit rs_1602=P2^5; //1602rs信号
sbit rw_1602=P2^6; //1602rw信号
sbit e=P2^4;  /*
void write_zl(uchar zl)
{rs=0;P0=zl;delay(5);e=1;delay(5);e=0;
}void write_sj(uchar sj)
{rs=1;P0=sj;delay(5); e=1;delay(5);e=0;
}     */
void write_order_1602(uchar order_data)
{e=0;rs_1602=0;P0=order_data;e=1;delay(1);e=0;
}void write_data_1602(uchar data_1602)
{   e=0;rs_1602=1;P0=data_1602;e=1;delay(1);e=0;
}/
//功能:按指定位置显示一串字符
///
//输入:
//列显示地址x_1602(取值范围1-16)
//行显示地址y_1602(取值范围1-2),
//指定字符串指针*p_1602,
//要显示的字符个数count_1602 (取值范围1-16)
///
// 子函数使用例子: Disp_1602(1,1," TEMP:    .     ",16);  //在1602第一行第一列写16个字符,既字符串数据
/void Disp_1602(unsigned char x_1602,unsigned char y_1602,unsigned char *p_1602,unsigned char count_1602)
{
unsigned char i;    for(i=0;i<count_1602;i++){if (1 == y_1602) x_1602 |= 0x80; //当要显示第一行时地址码+0x80;else x_1602 |= 0xC0;        //在第二行显示是地址码+0xC0;write_order_1602(x_1602-1);write_data_1602(*p_1602);x_1602++;                  p_1602++;}
}  void write_oneline_1602(uchar add_1602,uint date_1602)
{uchar bai,ge,shi;            bai=date_1602/100;shi=(date_1602%100)/10;ge=date_1602%10;          write_order_1602(0x80+add_1602-1);write_data_1602(0x30+bai);write_order_1602(0x80+add_1602);write_data_1602(0x30+shi);write_order_1602(0x80+add_1602+1);write_data_1602(0x30+ge);//在1602上写时间函数  即在1602第二行指定位置上写显示    //在1602上写时间函数  即在1602第二行指定位置上写显示}void write_twoline_1602(uchar add_1602,uint date_1602)
{uchar bai,ge,shi;            bai=date_1602/100;shi=(date_1602%100)/10;ge=date_1602%10;          write_order_1602(0x80+0x40+add_1602-1);write_data_1602(0x30+bai);write_order_1602(0x80+0x40+add_1602);write_data_1602(0x30+shi);write_order_1602(0x80+0x40+add_1602+1);write_data_1602(0x30+ge);//在1602上写时间函数  即在1602第二行指定位置上写显示}void write_oneline_DS18b20_1602(uchar add_1602,uint date_1602)
{uchar qian,bai,shi,ge;qian=date_1602/1000;bai=(date_1602%1000)/100;shi=(date_1602%100)/10;ge=date_1602%10;write_order_1602(0x40+0x40+add_1602-1);write_data_1602(0x30+qian);write_order_1602(0x40+0x40+add_1602);write_data_1602(0x30+bai);write_order_1602(0x40+0x40+add_1602+1);write_data_1602(0x30+shi);write_order_1602(0x40+0x40+add_1602+3);write_data_1602(0x30+ge);          //在1602上写时间函数  即在1602第二行指定位置上写显示}void lcdinit_1602()
{rw_1602=0;e=0;write_byte_74hc595(0X02);//关流水灯使能 关蜂鸣器 光继电器 打开1602背光打开 write_order_1602(0x38);  //液晶设置不判忙模式write_order_1602(0x0c);  //开显示 不显示光标write_order_1602(0x06);  //当写一个字符是,地址指针加 1write_order_1602(0x01);     //显示清屏
}

STC51-A/D和D/A相关推荐

  1. 配置Keil C51配置开发 STC51单片机过程

    内容 1. Keil C51 下载 2. 配置开发STC51单片机 配置 Keil IDE与TEASOFT编辑 1. 配置Keil编辑器 2. 设置Keil自动开打文件 参考博文 1. Keil C5 ...

  2. STC51几种简单的延时函数

    STC51几种简单的延时函数 1,* 延时子程序 * * * ********************************************************************* ...

  3. 基于STC51:四轴飞控开源项目原理图与源码(入门级DIY)

    目录 前言(作者:宏晶科技) 一.飞控配件 二.接线 三.原理图 四.调试 五.程序 六.完整工程.原理图文件获取 前言(作者:宏晶科技) 本飞控仅仅是姿态飞行控制,没有GPS.电子罗盘.气压高度计. ...

  4. 奥克斯空调红外遥控信号编码协议的分析,STC51单片机读红外程序

    2022.9 网上根本找不到资料,没人免费发布,有发布也是要钱的.哎.自己研究吧. 刚买的奥克斯清芯净空调,遥控器型号YKR-T/101,这样子的,手感不错: 用USB示波器抓了波形看了一下,和标准的 ...

  5. STC51单片机数码管显示程序和仿真

    STC51单片机数码管显示程序和仿真 仿真使用的数码管时共阴极 1.共阳: char code table[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92

  6. STC51单片机23——T2定时器的使用

    /** TF2 定时器2 溢出标志.定时器2 溢出时置位,必须由软件清除. 当RCLK或TCLK=1 时,TF2 将不会置位.TR2 定时器2 启动/ 停止控制位. 置1 时启动定时器.C/T2 定时 ...

  7. 创意发明: 基于STC51单片机超声波测距(精简设计只需三个元件)

    基于STC51单片机超声波测距精简设计只需三个元件(测距模块暂且算做是一个元件吧) 1.所需元件:STC单片机+超声波模块+4位共阳数码管 2.原理:单片机向测距模块trig脚发送20us的高电平触发 ...

  8. CH340T制作STC51单片机下载器

    一直以来在搞 freescale,反倒是51手很生.一直也没有自己做过系统板之类的东西,暑假里准备比赛的这几天,就仔细的翻阅了一下51相关的内容,自己制作了一个小系统版.下载器用的是USB-UART的 ...

  9. STC51和STM32使用串口输出中文乱码问题解决

    STC51和STM32使用串口输出中文乱码问题解决 问题描述:在进行串口实验时,我们想通过串口调试助手来输出中文信息, 但是我们经常会发现在串口调试助手上会输出乱码,跟我们预期的中文信息不符,见以下图 ...

  10. keil5如何兼容MDK 和 STC51

    keil5如何兼容MDK 和 STC51 安装MDK 和 C51 针对曾经下载好了的小伙伴 安装MDK 和 C51 1.新建一个文件夹keil_5 2.官网下载C51 MDK(都存储在第一步新建的ke ...

最新文章

  1. datax 高级_GitHub - xhhx55/DataX
  2. JZOJ 4676. 【NOIP2016提高A组模拟7.21】模板串
  3. Spring的事务传播性
  4. Sql 行转列问题总结
  5. 【ZOJ - 4033】CONTINUE...? (思维,整体思想,分组思想)
  6. 【机器学习】opencv-数据预处理
  7. 3.3亿人都在用小程序,中国首次定义的互联网标准又有新进展
  8. SQLPlus获取oracle表操作SQL
  9. es 的分布式架构原理
  10. Factory(工厂)模式
  11. 单片机开发,推荐开源跨平台的SDCC编译器
  12. 无约束低分辨率人脸识别综述一:用于低分辨率人脸识别的数据集
  13. 科学论文类型分类(letter, perspective, focus, article)以及影响引子,SCI、EI等
  14. 【科研绘图】PS绘制封面中神奇的放大效果
  15. liunx关闭端口命令
  16. linux的usb驱动失败,linux2.6.24下usb驱动完成情况及问题解决
  17. Oracle知识点(一)--创建表
  18. 教你用c++代码制作:仙剑奇侠传,火爆人气!
  19. latex 参考文献显示问号_关于 VS Code、WinEdt、LaTeX 和 Markdown 的一些实用 Tips
  20. 【JavaWeb】eclipse中常见报错解决汇总

热门文章

  1. Java 算法 比赛安排
  2. Java 蓝桥杯 完美的代价
  3. 复制网页中的表格格式后导入到excel、markdown、数据库、json中,并转换表格格式
  4. IDEA(Pycharm)一家子常用快捷键Keymap对应的英文、中文与具体位置
  5. cpu矿工cpuminer-multi编译与使用
  6. pythonapi异步_Python-FastAPI异步博客开发记录--异步篇
  7. android 编程一个程序实现方法,Android中一个应用实现多个图标的几种方式
  8. window7安装MongoDB详细步骤
  9. [bzoj1009](HNOI2008)GT考试 (kmp+矩阵快速幂加速递推)
  10. 企业视觉-大型电商(系统)高性能-用户视觉性能(1)