硬件第四、五次培训——示波器、IIC、ADDA

本次博客涵盖第四次和第五次培训内容。第四次介绍示波器原理,包括用途、参数、组成等,还提及元器件等效模型;第五次讲解IIC,如I2C总线组成、工作原理、数据传输等,最后介绍数模转换,包含PCF8591芯片及AD转换器技术指标。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

第四次培训内容

示波器原理

简介

示波器的主要用途就是将随时间变化的电信号以图形的方式画出来,多数的示波器是用时间为x轴,电压为y轴产生的二维图形。

在示波器屏幕周边的控制按钮可以调节图形的显示比例,显示的横轴和纵轴刻度都能够调节,这样就可以对信号在时间和幅度两个维度进行缩放查看,还有可以调节“触发”的旋钮,帮助“稳定”波形的显示。

除了这些基础的功能之外,示波器还能够帮助工程师快速定量被测信号的频率、幅度以及其它的波形参数。总之示波器可以测试基于时间和基于电压的参数,如下:

基于时间的参数: 频率和周期、占空比、上升时间和下降时间等

电压参数: 幅度、最大电压、最小电压、平均电压等

用途

在调试电路的输入、输出以及中间系统的时候用以确定信号的频率和幅度,基于这些信息可以判断电路的工作是否正常。

确定电路中噪声的大小

判断波形的形状 – 正弦波、方波、三角波、锯齿波、复合波形等等

测量两个不同信号的相位差

参数

1.通道数 – 可以同时处理的模拟信号输入的数量,2通道最为常见,其次是4通道。
2.带宽 – 能够可靠测量的模拟信号的频率范围,一般以MHz为单位来表示。
3.采样率 – 这是数字示波器特有的指标,反映了对模拟信号以每秒多少次的速度进行采样。有的多通道示波器,当多个通道同时使用的时候采样率可能会降低,一般以MSa/S来表示,根据Nyquist采样定理,当对一个最高频率为f的带限信号进行采样时,采样频率SF必须大于f的两倍以上。
4.时基 –由于DSO的水平刻度分为10格,每格的所代表的时间长度即为时基(time base),单位是t/div,所以采样时间= time base × 10. 由以上关系式我们知道,提高示波器的存储深度可以间接提高示波器的采样率:当要测量较长时间的波形时,由于存储深度是固定的,所以只能降低采样率来达到,但这样势必造成波形质量的下降;如果增大存储深度,则可以以更高的采样率来测量,以获取不失真的波形。
5.上升时间 – 示波器的上升时间决定了其能够测量的最快的上升脉冲,这个指标与带宽高度相关,可以用这个公式来换算:Rise Time = 0.35 / Bandwidth。
6.最大输入电压 – 每种电子产品都有其能够承受电压的最高极限,示波器的最高输入电压指的是,如果输入的信号电压超过这个值,极有可能会损毁示波器。
7.分辨率 – 表征了对输入电压的量化精度,一般高速的示波器都采用8bit的高速ADC对模拟信号进行量化采样。
8.垂直灵敏度 – 这个值表征了垂直显示的电压量程的最小和最大值,单位是伏/格。
9.输入阻抗 – 如果被测信号为很高频率的信号,即便是非常小的阻抗(电阻、电容、电感)叠加在电路上都会对信号带来比较大的影响。每一个示波器都会对测量的电路增加一定的阻抗,这个阻抗就是输入阻抗,它一般是比较大的电阻(>1 MΩ)与比较小的电容(在pF的范围)并联 (||). 在测量非常高频率的信号的时候输入阻抗的影响就变得比较明显,可以通过调节使用的探头来进行补偿。

组成

各种示波器的功能基本上都是一样的,它们都有一些共同的属性 - 显示、水平线、垂直线、触发、输入等。

显示部分

示波器的显示界面一般都是通过多条水平和竖直的线交错构成的格状,竖直的刻度单位为伏/格,水平的刻度单位为秒/格。一般来讲示波器的显示屏在竖向(伏)有8-10个格,在横向(秒)有10-14个格。
越来越多的数字示波器使用多色的LCD显示屏,能方便在一个屏幕上显示多个波形(以不同的颜色)。显示屏周边(右侧或下面)一般会有5个输入按键,用以菜单切换以及设置的控制。

垂直调节

示波器显示屏的竖向显示的是测量信号的电压,它的显示控制一般会通过两个旋钮:一个调节波形在竖直方向的位置,另一个调节每格的刻度(伏/格)。

水平调节

示波器的水平部分为时间标尺,就像垂直调节一样,水平调节按钮也有两个 - 调节左右移动 和改变刻度的大小(单位为秒/每格)。

触发系统

触发系统主要是为了稳定波形的显示并让示波器能聚焦,通过调节“触发”按钮,你可以告诉示波器在哪一个起始点开始测量。如果被测的信号是周期性的波形,通过触发的设置,可以让波形在屏幕上稳定显示,像静止不动一样。如果触发没有调节好,波形就会在屏幕上跑来跑去,不能稳定下来。

自动触发

自动触发模式下触发线路会在一定时间内等待满足你要求的信号,当有符合触发条件的信号进来时,示波器会显示满足触发条件的信号,但是如果在规定时间内一直没有符合触发条件的信号,示波器也会显示不满足触发条件的信号,该触发模式适合观察未知信号;

标准触发

标准触发模式下触发线路会一直的去等待符合触发条件的信号,当有符合触发条件的信号时,示波器会显示满足触发条件的信号并实时更新,如果都没有满足触发条件的信号,触发线路会一直等待,该触发模式一般应用在已知的信号类型,以便进行信号分析或者稳定的解码;

单次触发

单次触发模式下示波器只要捕获到一次满足触发条件的信号即自动停止捕获,该模式一般针对已知信号进行异常或者瞬态信号捕获,比如上下电测试,偶发毛刺抓取等。
在这里插入图片描述

自动定标

一种最简单的方法,非常适合完全没有示波器使用经验但是又需要示波器波形的:那就是示波器‘自动定标’功能:只需要把探头连接到待测设备上,然后按下‘自动定标(Auto Scale)’示波器即会自动设置并以最佳的方式显示所有接入信号通道的波形。

如果不使用‘自动定标’功能,使用示波器测量波形的步骤为:
1、连接示波器探头到待测物;
2、按下示波器‘默认设置’;
3、打开所有有输入信号的通道;
4、调整垂直刻度和偏置以同时显示所有通道的波形;
5、设置触发源通道,并把触发电平调整到源信号幅值的50%;
6、调整水平时基使屏幕上显示几个周期的波形以方便查看。

探头部分

示波器的测量离不开同被测电路连接的探头,它是一个单输入的设备,将电信号从待测的电路上传递到示波器。它有一个比较尖的头用以接触你要检测的电路的测试点,很多时候这个尖头会配上钩子、镊子或夹子以方便连接到被测的电路上。每个探头都有一个接地夹子,测试的时候需要将这个接地夹子安全地连接到待测电路的公共地的位置。
理想状况下,示波器的探头应该对被测的信号没有任何影响,但现实却是它长长的连线不可避免地有着杂散电感、电容、以及电阻。因此,无论如何,它们都会影响到示波器对待测信号的解读,尤其在非常高的频率的时候。

探头有多种,最常用的是多数示波器自带的无源(Passive)衰减探头,它内部有着大的电阻并联一个很小的电容,以帮助减小探头的长电缆给待测电路带来的负载效应。这个内部的高电阻同示波器输入端的电阻串联,对输入信号构成了分压。

多数的示波器探头的内部阻抗为9MΩ的电阻,它同示波器输入端的标准的1MΩ的输入电阻相连接,构成了1/10的分压,这种探头被称为10X衰减探头。很多探头都有一个开关,可以切换是10:1衰减(10X)还是不做衰减(1X)。衰减探头在高频应用中能够保证比较高的精准度,但不好的地方就是对输入信号先衰减了10倍,如果你要测量的信号是非常小幅度的微弱信号,最好还是使用不做衰减的1x探头,这时候你需要设置示波器的菜单以告知其衰减发生了变化,很多示波器能够自动检测到探头是衰减还是不衰减。除了刚才讲的无源衰减探头,还有有源探头(单独供电),能够在送入示波器之前对待测信号进行放大甚至预处理;有能够测量交流或直流电流的探头,电流探头一般是环绕着待测的信号线,而不接触到被测的电路。

测量技巧

1 采用比较方便、安全、不影响性能的连接方式 – 将探头的接地夹子接到这个点上。有时候你需要焊接一根很细的导线在电路板上以方便探头的接地夹夹住,探头的尖头端也可以通过带弹簧的夹子、钩子等方便地连接待测的信号点 - 总之要找到一种方法,你不必要一直用手拿着探头。
2. 避免测量方法不当导致的噪声 - 如果待测的信号为高频(几十MHz)信号,用示波器测试的时候要做到地线的连接尽可能短,否则会由于探头的接地线同探头的尖头构成的环路形成天线,将待测点附近的高频信号(空间的无线电波、板子上开关信号辐射)接收下来叠加在待测信号上,会给自己的调试带来很大的干扰。多数情况下需要将同轴线直接焊接在电路板上,避免产生接收回路。
3. 熟悉你使用的仪器的所有测量工具 - 不同的示波器内部带的测量功能不同,你可以查看说明书以及调节各个按键先对你用的仪器功能全面熟悉一下,比如周期、峰峰值、脉宽、占空比、上升沿、下降沿、平均电压等的测量以及如何使用FFT功能,有哪些是能够自动测量并显示的。
4. 手动测量波形参数 - 可以通过移动光标读数、计算得到,移动光标的时候时间和电压值都会发生变化。一般光标都是成对出现,你可以通过读取两个光标之间的差值得到需要的信息。
5. 波形对比 - 基于你的测量结果,可以对电路进行调整,并调整后再次测量,有一些示波器具有保持、打印波形的功能,因此你可以调出前面测试的信号进行对比。

元器件等效模型

电阻

同一个电阻元件在通以直流和交流电时测得的电阻值是不相同的。在高频交流下,须考虑电阻元件的引线电感L0和分布电容C0的影响,其等效电路如图1所示,图中R为理想电阻。

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

电感

电感元件除电感L外,也总是有损耗电阻RL和分布电容CL。一般情况下RL和CL的影响很小。电感元件接于直流并达到稳态时,可视为电阻;若接于低频交流电路则可视为理想电感L和损耗电阻RL的串联;在高频时其等效电路如图2所示。比较图1和图 2可知二者实际上是相同的,电感元件的高频等效阻抗可参照式 1来确定
在这里插入图片描述
在这里插入图片描述

电容

在交流下电容元件总有一定介质损耗,此外其引线也有一定电阻Rn和分布电感Ln,因此电容元件等效电路如图 3所示。图中C是元件的固有电容,Rc是介质损耗的等效电阻。

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

二极管:正向

二极管正向导通时可用一电压降等效,该电压与温度和所流过的电流有关,温度升高,该电压变小;电流增加,该电压增加。详细的关系曲线可从制造商的手册中获得。

在这里插入图片描述

二极管:反向

二极管反向截止时可用一电容等效,其容量与所加的反向电压、环境温度等有关,大小可从制造商的手册中获得。

在这里插入图片描述

MOSFET

功率 MOSFET 正向导通时可用一电阻等效,该电阻与温度有关,温度升高,该电阻变大;它还与门极驱动电压的大小有关,驱动电压升高,该电阻变小。详细的关系曲线可从制造商的手册中获得。
门极不加控制
在这里插入图片描述
门极加控制
在这里插入图片描述

在这里插入图片描述
寄生参数
在这里插入图片描述

第五次培训内容

IIC介绍

I2C串行总线的组成及工作原理

I2C总线是PHLIPS公司推出的一种串行总线,它只有两根双向信号线。一根是数据线SDA(serial data I/O),另一根是时钟线SCL(serial clock)。如下图所示,IIC总线上可以挂多个器件,而每个器件都有唯一的地址,这样可以标识通信目标。数据的通信的方式采用主从方式,主机负责主动联系从机,而从机则被动回应数据。

在这里插入图片描述

I2C总线通过上拉电阻接正电源。当总线空闲时,两根线均为高电平。连到总线上的任一器件输出的低电平,都将使总线的信号变低,即各器件的SDA及SCL都是线“与”关系。

I2C总线传输协议

在这里插入图片描述

数据位的有效性规定

SCL为高电平期间,数据线上的数据必须保持稳定,只有SCL信号为低电平期间,SDA状态才允许变化。

在这里插入图片描述

I2C的起始和终止信号

SCL线为高电平期间,SDA线由高电平向低电平的变化表示起始信号;SCL线为高电平期间,SDA线由低电平向高电平的变化表示终止信号。

在这里插入图片描述

I2C字节的传送与应答

每一个字节必须保证是8位长度。数据传送时,先传送最高位(MSB),每一个被传送的字节后面都必须跟随一位应答位(即一帧共有9位)。

在这里插入图片描述

应答位的作用

主机在发送数据时,每次发送一字节数据,都需要读取从机应答位,当从机空闲可以接收该字节数据时,从机会发出应答(一帧数据的第9位为“0”),当从机正忙于其他工作的处理来不及接收主机发送的数据时,从机会发出非应答(一帧数据的第9位为“1”)主机则应发出终止信号以结束数据的继续传送,主机通过从机发出的应答位来判断从机是否成功接收数据。

当主机接收数据时,它收到最后一个数据字节后,必须向从机发出一个结束传送的信号。这个信号是由对从机的“非应答”来实现的。然后,从机释放SDA线,以允许主机产生终止信号。

I2C写数据流程

在起始信号后必须传送一个从机的地址(7位)我们开发板上的AT24C02地址为0xa0,第8位是数据的传送方向位(R/T),用“0”表示主机发送数据(T),“1”表示主机接收数据(R)。

在这里插入图片描述

I2C读数据流程

在读数据时也要先发送器件地址,读写方向为写,因为我们下一帧需要发送从AT24C02内那个单元开始读,之后需在发一次器件地址这个时候读写方向就为读了,接着我们就可以从总线上读取数据。

在这里插入图片描述

软件模拟I2C通信时序

I2C总线的数据传送有严格的时序要求。I2C总线的起始信号、终止信号、发送“0”及发送“1”的模拟时序 :

在这里插入图片描述

参考例程



`# include <reg52.h>
# include <intrins.h>

#define uchar unsigned char

#define uint unsigned  int

#define AT24C02_ADDR  0xa0 //AT24C02地址

/*I2C IO口定义*/

sbit SDA = P2^0;

sbit SCL = P2^1;

/*5us延时*/

void delay_5us()  

{

 _nop_();

}

/*1Ms延时*/

void delay(uint z)

{

 uint x,y;

 for(x = z; x > 0; x--)

  for(y = 114; y > 0 ; y--);

}

/*I2C初始化*/

void I2C_init() 

{

 SDA = 1;

 _nop_();

 SCL = 1;

 _nop_();

}

/*I2C起始信号*/

void I2C_Start()  

{

 SCL = 1;

 _nop_();

 SDA = 1;

 delay_5us();

 SDA = 0;

 delay_5us();

}

/*I2C终止信号*/

void I2C_Stop()

{

 SDA = 0;

 _nop_();

 SCL = 1;

 delay_5us();

 SDA = 1;

 delay_5us();

}

/*主机发送应答*/

void Master_ACK(bit i)  

{

 SCL = 0; // 拉低时钟总线允许SDA数据总线上的数据变化

 _nop_(); // 让总线稳定

 if (i)  //如果i = 1 那么拉低数据总线 表示主机应答

 {

  SDA = 0;

 }

 else  

 {

  SDA = 1;  //发送非应答

 }

 _nop_();//让总线稳定

 SCL = 1;//拉高时钟总线 让从机从SDA线上读走 主机的应答信号

 delay_5us();

 SCL = 0;//拉低时钟总线, 占用总线继续通信

 _nop_();

 SDA = 1;//释放SDA数据总线。

 _nop_();

}

/*检测从机应答*/

bit Test_ACK()

{

 SCL = 1;

 delay_5us();

 if (SDA)

 {

  SCL = 0;

  _nop_();

  I2C_Stop();

  return(0);

 }

 else

 {

  SCL = 0;

  _nop_();

  return(1);

 }

}

/*发送一个字节*/

void I2C_send_byte(uchar byte)

{

 uchar i;

 for(i = 0 ; i < 8 ; i++)

 {

  SCL = 0;

  _nop_();

  if (byte & 0x80)

  {    

   SDA = 1; 

   _nop_();       

  }    

  else

  {

   SDA = 0;

   _nop_();

  }

  SCL = 1;

  _nop_();

  byte <<= 1; // 0101 0100B 

 }

 SCL = 0;

 _nop_();

 SDA = 1;

 _nop_();

}



/*I2C 读一字节*/

uchar I2C_read_byte()

{

 uchar dat,i;

 SCL = 0;

 _nop_();

 SDA = 1;

 _nop_();

 for(i = 0 ; i < 8 ; i++)

 {

  SCL = 1;

  _nop_();

  if (SDA)       

  {

    dat |= 0x01; //

  }

  else

  {

   dat &=  0xfe; //1111 1110

  }

  _nop_();

  SCL = 0 ;

  _nop_();

  if(i < 7)

  {

   dat = dat << 1; 

  }

 }

 return(dat);

}

/*I2C发送数据*/

bit I2C_TransmitData(uchar ADDR, DAT)

{

 I2C_Start();

 I2C_send_byte(AT24C02_ADDR+0);

 if (!Test_ACK())

 {

  return(0);

 }

 I2C_send_byte(ADDR); 

 if (!Test_ACK())

 {

  return(0);

 }

 I2C_send_byte(DAT);

 if (!Test_ACK())

 {

  return(0);

 }

 I2C_Stop();

 return(1); 

}

/*I2C接收数据*/

uchar I2C_ReceiveData(uchar ADDR)

{

 uchar DAT;

 I2C_Start();

 I2C_send_byte(AT24C02_ADDR+0);

 if (!Test_ACK())

 {

  return(0);

 }

 I2C_send_byte(ADDR);

 Master_ACK(0);

 I2C_Start();

 I2C_send_byte(AT24C02_ADDR+1);

 if (!Test_ACK())

 {

  return(0);

 }

 DAT = I2C_read_byte();

 Master_ACK(0);

 I2C_Stop();

 return(DAT); 

}

void main()

{

 I2C_init();//I2C初始化

 if(!I2C_TransmitData(255,0xf0)); //往AT24C02第255个单元中写入数据0XF0

 {

  P1 = 0;

 }

 delay(5);

 /**/

 P1 = I2C_ReceiveData(255);//从AT24C02第255个单元中读取数据

 while(1);  

}



数模转换

概述

模拟量:自然界连续变化的物理量。所谓连续,包含两个方面的含义;

一方面从时间上来说,它是随时间连续变化的;

另一方面从数值上来说,它的数值也是连续变化的。这种连续变化的物理量通常称为模拟量。

数字量:计算机中处理的是不连续变化的量,离散性的数字量。

   当计算机用于数据采集和过程控制时,采集的对象往往是连续变化的物理量(模拟信号)如温度、压力、摄像头采集图像、照度、等,但计算机处理的是离散的数字量,因此需要对连续变化的物理量进行A/D转换为不连续的数字量交给计算机处理,保存等。计算机输出的数字量有时需要通过D/A转换为模拟量去控制某些执行元件。A/D转换器完成模拟量至数字量的转换,D/A转换器完成数字量至模拟量的转换。

PCF8591的介绍

     PCF8591

是单电源,低功耗8 位CMOS
数据采集器件,具有4 个模拟输入、一个输出和一个串行I2C 总线接口。3 个地址引脚A0、A1 和A2 用于编程硬件地址,允许将最多8 个器件连接至I2C总线而不需要额外硬件。PCF8591由于其使用的简单方便和集成度高,在单片机应用系统中得到了广泛的应用。

特点:
1 单电源供电
2 工作电压:2.5V ~ 6 V
3 I2C总线串行输入/输出
4 通过 3 个硬件地址引脚编址
5 采样速率取决于I2C 总线传输速率决定
6 4 个模拟输入可编程为单端或差分输入
7 自动增量通道选择
8 8位逐次比较型A/D转换

管脚定义及原理图

AIN0-AIN3:模拟输入(A/D转换)。
AOUT:模拟输出(D/A转换)。
A0-A2:硬件设备地址。
VDD:电源正极
VSS:电源负极地
VREF:参考电压输入
EXT:振荡器输入时,内部/外部的切换开关。
OSC:振荡器输入/输出。
SCL: I2C BUS时钟输入。
SDA:I2C
BUS 数据输入/输出。
AGND:模拟地,模拟信号和基准电源的参考地

在这里插入图片描述

AD转换器的主要技术指标概述

分辨率

分辩率指数字量变化一个最小量时模拟信号的变化量,定义为满刻度与2^n的比值(n为AD器件的位数)。对于5V的满刻度,采用8位的AD时,分辨率为5V/256≈0.01953V;当采用12位的AD时,分辨率则为5V/4096≈0.00122V。显然,位数越多分辨率就越高。

转换速率

转换速率是指完成一次从模拟转换到数字的AD转换所需的时间的倒数。积分型AD的转换时间是毫秒级属低速AD,逐次比较型AD是微秒级属中速AD,全并行/串并行型AD可达到纳秒级。采样时间则是另外一个概念,是指两次转换的间隔。为了保证转换的正确完成,采样速率必须小于或等于转换速率。

量化误差

由于AD的有限分辩率而引起的误差,即有限分辩率AD的阶梯状转换特性曲线与无限分辩率AD(理想AD)的转换特性曲线(直线)之间的最大偏差。通常是1 个或半个最小数字量的模拟变化量,表示为1LSB、1/2LSB。

零值误差
满刻度误差
线性误差
绝对精度
在一个转换器中,任何数码所对应的实际模拟量输入与理论模拟输入之差的最大值,称为绝对精度。

AD的差分输入与单端输入

单端输入,输入信号均以共同的地线为基准.这种输入方法主要应用于输入信号电压较高(高于1 V),信号源到模拟输入硬件的导线较短,且所有的输入信号共用一个基准地线.如果信号达不到这些标准,此时应该用差分输入.对于差分输入,每一个输入信号都有自有的基准地线;由于共模噪声可以被导线所消除,从而减小了噪声误差.
单端输入时, 是判断信号与
GND 的电压差. 差分输入时, 是判断两个信号线的电压差.
信号受干扰时, 差分的两线会同时受影响, 但电压差变化不大. (抗干扰性较佳) 而单端输入的一线变化时, GND 不变, 所以电压差变化较大. (抗干扰性较差)

PCF8591地址

I2C 总线系统中的每一片PCF8591 通过发送有效地址到该器件来激活。该地址包括固定部分和可编程部分。可编程部分必须根据地址引脚A0、A1 和A2 来设置,因此I2C系统中最多可接 =8个PCF8591。在I2C
总线协议中地址必须是起始条件后作为第一个字节发送。地址字节的最后一位是用于设置以后数据传输方向的读/写位1为读操作,0为写操作。

在这里插入图片描述

PCF8591控制字节

发送到
PCF8591 的第二个字节将被存储在控制寄存器,用于控制器件功能。控制寄存器的高半字节用于允许模拟输出,和将模拟输入编程为单端或差分输入。低半字节选择一个由高半字节定义的模拟输入通道。如果自动增量(auto-increment)标志置1,每次A/D
转换后通道号将自动增加。

参考例程



#include
<reg52.h>

#include
<intrins.h>

#define
MAIN_Fosc     11059200UL   //宏定义主时钟HZ

#define
PCF8591_ADDR  0x90      //PCF8591地址

#define
DACOUT_EN     0x40      //DAC输出使能

 

/*====================================

 自定义类型名

====================================*/

typedef
unsigned char INT8U;

typedef
unsigned char uchar;

 

typedef
unsigned int INT16U;

typedef
unsigned int uint;

 

/*====================================

 硬件接口位声明

====================================*/

sbit
SDA = P2^0;   //I2C串行数据

sbit
SCL = P2^1;   //I2C串行时钟

sbit
DU  = P2^6;   //数码管段选

sbit
WE  = P2^7;   //数码管位选

sbit
LED1= P1^0;   //读取AD的值是否成功(亮成功,灭失败)

sbit
LED2= P1^1;   //DAC成功输出(亮成功,灭失败)

sbit
BEEP= P2^3;   //蜂鸣器引脚定义

 

uchar
AD_Value; //存储AD转换回的数字量

/*====================================

共阴极数码管段选码

====================================*/

uchar
code table[]={ 

//0   1  
2     3     4    
5     6     7    
8

0x3F,
0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F,

//9     A    
B     C    D    E    F    -   .   关显示

0x6F,
0x77, 0x7C, 0x39, 0x5E, 0x79, 0x71, 0x40, 0x80, 0x00

                   };

 

/*====================================

数码管位选码

====================================*/

              
//第1位 2位     3位  4位   5位   6位     7位 8位

uchar
code T_COM[] = {0xfe, 0xfd, 0xfb, 0xf7, 0xef, 0xdf, 0xbf, 0x7f};//数码管位码

 

/*====================================

函数:void
Delay_Ms(INT16U ms)

参数:ms,毫秒延时形参

描述:12T
51单片机自适应主时钟毫秒级延时函数

====================================*/

void
Delay_Ms(INT16U ms)

{

     INT16U i;

    do{

         i
= MAIN_Fosc / 96000; 

       
while(--i);   //96T per loop

     }while(--ms);

}

 

/*====================================

函数:void
Delay5us()

描述:12T
51单片机5微秒延时函数自适应时钟(11.0592M,12M,22.1184M)

====================================*/

void
Delay5us()

{

   #if MAIN_Fosc == 11059200

      _nop_();

   #elif MAIN_Fosc == 12000000

      _nop_();

   #elif MAIN_Fosc == 22118400

      _nop_(); _nop_(); _nop_();

   #endif

}

 

/*====================================

函数:void
Display(INT8U Value)

参数:Value,显示值 取值0-255

描述:共阴极数码管显示函数可显示一个字节的数

====================================*/

void
Display(INT8U Value)

{

//------------------------------

   DU = 1;

   P0 = table[Value/100]; //管显示百位

   DU = 0;

 

   P0 = 0xff;         
   //清除断码

 

   WE = 1;

   P0 = T_COM[0];         //第一位数码管

   WE = 0;

   Delay_Ms(5);

//-------------------------------

   DU = 1;

   P0 = table[Value%100/10]; //显示十位

   DU = 0;

 

   P0 = 0xff;              //清除断码

 

   WE = 1;

   P0 = T_COM[1];          //第二位数码管

   WE = 0;

   Delay_Ms(5);

//-------------------------------

   DU = 1;

   P0 = table[Value%10];    //显示个位

   DU = 0;

                         

   P0 = 0xff;               //清除断码

 

   WE = 1;

   P0 = T_COM[2];           //第三位数码管

   WE = 0;

   Delay_Ms(5);

}

 

/*====================================

函数:I2C_init()

描述:I2C总线初始化

====================================*/

void
I2C_init()

{

   SDA = 1;  
//数据总线高

   _nop_();

   SCL = 1;  
//时钟总线高

   _nop_();

}

 

/*====================================

函数:I2C_Start()

描述:I2C起始信号

====================================*/

void
I2C_Start()  

{

   SCL = 1;

   _nop_();

   SDA = 1;

   Delay5us();

   SDA = 0;

   Delay5us();

}

 

/*====================================

函数:I2C_Stop()

描述:I2C停止信号

====================================*/

void
I2C_Stop()

{

   SDA = 0;

   _nop_();

   SCL = 1;

   Delay5us();

   SDA = 1;

   Delay5us();

}

 

/*====================================

函数:Master_ACK(bit
i)

参数:i 为0时发送非应答 为1时发送应答

描述:I2C主机发送应答

====================================*/

void
Master_ACK(bit i) 

{

   SCL = 0; // 拉低时钟总线允许SDA数据总线上的数据变化

   _nop_(); // 让总线稳定

   if (i) 
//如果i =
1 那么拉低数据总线 表示主机应答

   {

      SDA = 0;

   }

   else   

   {

      SDA = 1;  
//发送非应答

   }

   _nop_();//让总线稳定

   SCL = 1;//拉高时钟总线 让从机从SDA线上读走 主机的应答信号

   _nop_();

   SCL = 0;//拉低时钟总线, 占用总线继续通信

   _nop_();

   SDA = 1;//释放SDA数据总线。

   _nop_();

}

 

/*====================================

函数:Test_ACK()

返回:0为非应答 1为应答

描述:I2C检测从机应答

====================================*/

bit
Test_ACK()   // 检测从机应答

{

   SCL = 1;//时钟总线为高电平期间可以读取从机应答信号

   Delay5us();

   if (SDA)

   {

      SCL = 0;

      I2C_Stop();

      return(0);

   }

   else

   {

      SCL = 0;

      return(1);

   }

}

 

/*====================================

函数:I2C_send_byte(uchar
byte)

参数:byte
要发送的字节

描述:I2C发送一个字节

====================================*/

void
I2C_send_byte(uchar byte)

{

   uchar i;

   for(i = 0 ; i < 8 ; i++)

   {

      SCL = 0;

      _nop_();

      if (byte & 0x80)   //

      {

          SDA = 1;

          _nop_();

      }

      else

      {

          SDA = 0;

          _nop_();

      }

      SCL = 1;

      _nop_();

      byte <<= 1;

   }

   SCL = 0;

   _nop_();

   SDA = 1;

   _nop_();  

}

 

/*====================================

函数:I2C_read_byte()

返回:读取的字节

描述:I2C读一个字节

====================================*/

uchar
I2C_read_byte()

{

   uchar i, dat;

   SCL = 0 ;

   _nop_();

   SDA = 1;

   _nop_();

   for(i = 0 ; i < 8 ; i++)

   {

      SCL = 1;

      _nop_();

      dat <<= 1;     

      if (SDA)

      {

          dat |= 0x01;  

      }

      _nop_();

      SCL = 0;

      _nop_();

   }

   return(dat);

}

 

/*DAC输出*/

bit
DAC_OUT(uchar DAT)

{

   I2C_Start();

   I2C_send_byte(PCF8591_ADDR+0);

   if (!Test_ACK())

   {

      return(0);

   }

   I2C_send_byte(DACOUT_EN); //DA输出使能 

   if (!Test_ACK())

   {

      return(0);

   }

   I2C_send_byte(DAT);

   if (!Test_ACK())

   {

      return(0);

   }

   I2C_Stop();

   return(1);   

}

 

/*读AD数据*/

bit
ADC_Read(uchar CON)

{

   I2C_Start();

   I2C_send_byte(PCF8591_ADDR+0);

   if (!Test_ACK())

   {

      return(0);

   }

   I2C_send_byte(CON);

   Master_ACK(0);

   I2C_Start();

   I2C_send_byte(PCF8591_ADDR+1);

   if (!Test_ACK())

   {

      return(0);

   }

   AD_Value = I2C_read_byte();

   Master_ACK(0);

   I2C_Stop();

   return(1);   

}

 

void
main()

{

   I2C_init();

   while(1)

   {     

      //单端输入,读出通道2的值

      if (ADC_Read(0x02)) LED1 = 0; else  LED1 = 1; 

      if (DAC_OUT(AD_Value)) LED2 = 0; else  LED2 = 1;

      Display(AD_Value);

      if (AD_Value > 150) BEEP = 0; else  BEEP = 1;

      Delay_Ms(5);

   }  


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值