基于51单片机的AD/DA转换的串口通信proteus仿真(附源码)


一、前言

`AT89C52是一款经典的8051系列单片机,它通常不包含内置的模数转换器(ADC)或数字模拟转换器(DAC)功能。如果你需要使用ADC功能来进行模拟信号的采集,你可以通过外部连接ADC芯片来实现。ADC芯片将模拟信号转换为数字信号,然后可以通过AT89C52的GPIO端口进行读取和处理。


二、PCF8591

1.介绍

PCF8591是一个单片集成、单独供电、低功耗、8bit CMOS的AD/DA芯片。它具有4个模拟输入、1个模拟输出和1个串行IIC总线接口。PCF8591的3个地址引脚A0,A1和A2可以用于硬件地址编程,允许在同个IIC总线上接入8个PCF8591器件,而无需额外的硬件。

主要性能指标:

  1. 单电源供电,工作电压:2.5V-6V
  2. 通过IiC总线串行输入/输出
  3. PCF8591通过3个硬件地址引脚寻址
  4. PCF8591的采样率由IIC总线速率决定
  5. 4个模拟输入可编程为单端型或差分输入
  6. 自动增量频道选择
  7. PCF8591的模拟电压范围从VSS到VDD
  8. PCF8591内置跟踪保持电路 8-bit 逐次逼近A/D转换器
  9. 通过1路模拟输出实现DAC增益

2.原理图

这里的原理图用的是YL-40 AD模块的原理图。

  1. 模块支持外部4路电压输入采集(电压输入范围0-5V)
  2. 模块集成光敏电阻,可以通过AD采集环境光强精确数值
  3. 模块集成热敏电阻,可以通过AD采集环境温度精确数值
  4. 模块集成1路0-5V电压输入采集(通过蓝色电位器调节接入电压)
  5. 模块带电源指示灯(对模块供电后指示灯会亮)
  6. 模块带DA输出指示灯,当模块DA输出接口电压达到一定值,会点亮板上DA输出指示灯,电压越大,指示灯亮度越明显

在这里插入图片描述

3.引脚介绍

左边:
AOUT:芯片DA输出接口
AIN0:芯片模拟输入接口0,地址0x40
AIN1:芯片模拟输入接口1,地址0x41
AIN2 :芯片模拟输入接口2,地址0x42
AIN3 :芯片模拟输入接口3,地址0x43

右边:
SCL:IIC时钟接口,接单片机IO口
SDA:IIC数据接口,接单片机IO口
GND:模拟地 ,外接地
VCC:电源接口,外接3.3V-5V

三、仿真图

1.未仿真时

注意:这里的AT89C52的晶振为11.0592MHZ

在这里插入图片描述

2.仿真时

这里上电一段时间,AD才能正确采集出来数值,然后DAC也是一段时间过后才有电压输出。

4路ADC换算公式如图所示:ADC数值 = 2^8 * (Vacc / Vcc ) = 255 * (电阻分压 / 3.3V)
(255是因为是8bit)

在这里插入图片描述

四、仿真程序

main.c

void main(void)
{  
	UsartInit();  //	串口初始化
	printf("Uart Test OK\r\n");
	while(1)
	{		
	/********以下AD-DA处理*************/  
	
	    switch(AD_CHANNEL)
		{
			case 0: ISendByte(PCF8591,0x41); 
			     D[0]=IRcvByte(PCF8591); 	 //ADC0 模数转换1      光敏电阻
				 break;  
			
			case 1: ISendByte(PCF8591,0x42); 
			     D[1]=IRcvByte(PCF8591); 	 	//ADC1  模数转换2	  热敏电阻
				 break;  
			
			case 2: ISendByte(PCF8591,0x43); 
			     D[2]=IRcvByte(PCF8591); 	 	 //ADC2	模数转换3	   悬空
				 break;  
			
			case 3: ISendByte(PCF8591,0x40);
			     D[3]=IRcvByte(PCF8591); 	  //ADC3   模数转换4	   可调0-5v
				 break;  
			
			case 4: DACconversion(PCF8591,0x40, 150); 		//DAC	  数模转换
			     break;
		 
		}
			
	   if(++AD_CHANNEL>4) AD_CHANNEL=0;
	
	   /********以下将AD的值通过串口发送出去*************/	
	 printf("D[0]=%d\r\n",D[0]);		   	   
	 delay(20);		  
	 printf("D[1]=%d\r\n",D[1]);				   
	 delay(20);	
	 printf("D[2]=%d\r\n",D[2]);	
	 delay(20);
	 printf("D[3]=%d\r\n",D[3]);	
	 delay(20);
   }
}

void delay(uchar i)
{
  uchar j,k; 
  for(j=i;j>0;j--)
    for(k=125;k>0;k--);
}

/*******************************************************************
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();          //读取数据0

   Ack_I2c(1);           //发送非就答位
   Stop_I2c();           //结束总线
   return(c);
}

/*******************************************************************************
* 函数名         :UsartInit()
* 函数功能		   :设置串口
* 输入           : 无
* 输出         	 : 无
*******************************************************************************/
void UsartInit()
{
    TMOD=0x20;     //即0010 0000,定时器/计数器1,工作方式2
    TH1=0xfd;      //设置波特率为9600
    TL1=0xfd;
    TR1=1;         //启动定时器/计数器1
        
    SCON=0x50;     //SM0=0;SM1=1;REN=1;0101 0000.串口工作方式1,允许串行控制
    PCON=0x00;     //设置SMOD=0
    IE=0x90;       //EA=1;ES=1;CPU允许中断,串行允许中断
        
    TI=1;          //中断请求标志位,直接使用printf必须加入此句才能实现发送
}

/*******************************************************************************
* 函数名         : Usart() interrupt 4
* 函数功能		  : 串口通信中断函数
* 输入           : 无
* 输出         	 : 无
*******************************************************************************/
void Usart() interrupt 4
{
  if (RI) //接收到新字节
  {
			RI = 0; //清零接收中断标志位
			rx_buf = SBUF;
   }
}

IIC.c

#define  NOP()   _nop_()   /* 定义空指令 */
#define  _Nop()  _nop_()   /*定义空指令*/

sbit     SCL=P2^0;       //I2C  时钟 
sbit     SDA=P2^1;       //I2C  数据 
bit      ack;            /*应答标志位*/
/*******************************************************************
                     起动总线函数               
函数原型: 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();    
}

五、总结

今天主要讲了基于51单片机的AD/DA转换的串口通信proteus仿真。

感谢你的观看!

在这里插入图片描述

  • 31
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

xiaobuding_QAQ

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值