51单片机Proteus仿真+Keil工程-实验7-单片机AD和DA实验

实验7-单片机AD和DA实验

  之前做的一次实验,51单片机AD和DA实验,数模使用芯片0832,模数转换使用0809。在做实验时曾经遇到一个问题,模数转换0809是不是只能输出0~ 5V,能不能输出-5V~5V的?当时在网上找了很多,没什么收获,在寻求帮助的过程中,一个朋友说可以,当时他分享了一个链接:http://m.elecfans.com/article/576838.html,看后明白解决方法是加一个反相器,最后把实验的要求圆满完成。如有问题欢迎指正。

实验目的:

  掌握数模和模数转换的原理;
  学会数模0832和模数0809的操作和使用。

实验内容:

  用单片机、数模0832、模数0809,可变电阻实现0 ~ 5v电压输出,或者选择电压调节器(如果有该器件),数模转换实验,将可变电阻分压后的电压值,应用数码管显示,保留小数点后2位。模数转换实验,输出的幅值0~5v的方波,三角波,锯齿波(-5V ~ 5V),应用示波器进行显示。

实验环境:

   MDK-ARM V5.21a、Proteus 8.6

Proteus原理图

51单片机Proteus仿真+Keil工程-实验7-单片机AD和DA实验_第1张图片
   方波(out输出0~5V,out1输出-5 V ~5V,考虑到反相器,两个输出波形相位差180°):
51单片机Proteus仿真+Keil工程-实验7-单片机AD和DA实验_第2张图片
   三角波(out输出0~5V,out1输出-5 V ~5V)
51单片机Proteus仿真+Keil工程-实验7-单片机AD和DA实验_第3张图片
   锯齿波(out输出0~5V,out1输出-5 V ~5V)
51单片机Proteus仿真+Keil工程-实验7-单片机AD和DA实验_第4张图片
   P34按下后,进入ADC转换,模拟电压量在数码管上显示。
51单片机Proteus仿真+Keil工程-实验7-单片机AD和DA实验_第5张图片

主要元器件:

DEVICES 说明
7SEG-MPX8-CC-BLUE 八位共阴数码管
8255A 可编程并行接口芯片
ADC0808 模数转换芯片
DAC0832 模数转换芯片
74HC573 锁存器
74LS373 锁存器
AT89C51 MCU
BUTTON 按键
CAP 普通电容
CAP-ELEC 电解电容
CRYSTAL 晶振
RES 电阻
RESPACK-8 排阻

  51单片机的P0口做IO口使用时是漏极开路输出,其引脚一般需要在片外接一定阻值的上拉电阻,此时端口不存在高阻抗的悬浮状态,因此它是一个准双向口。同时,P0口每一位的驱动能力是P1~P3口的两倍,每位可以驱动8个LSTTL(Low-power Schottky TTL,即低功耗肖特基TTL)输入,89C51等单片机任何一个端口想要获得较大的驱动能力,必须采用低电平输出。
  时钟晶体振荡频率为 f o s c = 11.0592 M H Z f_{osc}=11.0592MHZ fosc=11.0592MHZ
  时钟周期相当于 T o s c = 1 f o s c ≈ 90.42 n s T_{osc}=\frac{1}{f_{osc}} \approx 90.42ns Tosc=fosc190.42ns
  复位电路的话通过给89C51等单片机的复位引脚RST加上大于2个机器周期的高电平(即24个时钟振荡周期)就可以使单片机复位。

KEIL工程:

  1.DAC0832的输出电压Vo与输入数字量B的关系是(采用的基准电压为-5V):
V o = − ( B ∗ V R E F ) 256 V_o=-\frac{(B*V_{REF})} {256} Vo=256(BVREF)
  2.两路0~5V的被测电压分别加到ADC0809的IN0和IN1通道,进行A/D转换,两路输入电压的大小可通过手动调节RV1和RV2来实现。通过鼠标滚轮来放大虚拟电压表的图标,可清楚地看到输入电压的测量结果。
  3.ADC0809采用的基准电压为+5V,转换所得结果二进制数字addata所代表的电压的绝对值为addata5V/256,而若将其显示到小数点后两位,不考虑小数点的存在(将其乘以100),其计算的数值为: (addata 100/256)5V≈addata1.96 V。控制小数点显示在左边第2、6位数码管上,即为实际的测量电压。
  4.使用功能键(P3^4)选择工作模式:当为高电平时,为DAC工作模式,数字量转模拟量,在示波器显示;当为低电平时为ADC工作模式,模拟量转数字量,在数码管显示。目前,单片机开机后只能切换一次模式,再返回原来模式要重启,待改进。
  5.鉴于51单片机的端口可能不够,我采用了82C55芯片来进行端口扩展,设置PA口为输入,其他口(PB、PC高四位、PC低四位)全为输出。

  头文件以及宏定义等:

/*====================================================
Program:   单片机模/数和数/模实验
******
Encoding:  ANSI
Time:	   2020/06/21
Task:     选用单片机、数模0832、模数0809,可变电阻实现
		   0~5v电压输出,或者选择电压调节器(如果有该器
		   件)。
		   数模转换实验:将可变电阻分压后的电压值,应用
		   数码管显示,保留小数点后2位。
		   模数转换实验:输出的幅值0~5v的方波,三角波,
		   锯齿波(-5V~5V),应用示波器进行显示。
Introduction:使用功能键(P3^4)选择工作模式:
		  当为高电平时,为DAC工作模式,数字量转模拟量,
		  在示波器显示-DAC;(注意要等示波器稳定后观察)
		  当为低电平时为ADC工作模式,模拟量转数字量,
		  在数码管显示-ADC。
		  目前,单片机开机后只能切换一次模式,再返回原
		  来模式要重启,待改进。
=====================================================*/
#include "reg52.h"
#include "absacc.h"					//定义地址所需的头文件
#include 				//有各种移位和NOP指令
#include 
#define  PA8255  XBYTE[0xff7c]		//0xff7c为82C55PA端口地址
#define  PB8255  XBYTE[0xff7d]		//0xff7d为82C55PB端口地址
#define  PC8255  XBYTE[0xff7e]		//0xff7e为82C55PC端口地址
#define  COM8255 XBYTE[0xff7f]		//0xff7f为82C55控制寄存器地址
#define DAC_PORT P2 //DAC0832连接端口
#define K_SQU 1	// 方波标志
#define K_TRI 2	//三角波标志
#define K_SAW 3	//锯齿波标志
typedef unsigned char u8;
typedef unsigned int u16;
typedef unsigned long u32;

sbit rst_8255=P3^5; 				//82C55复位引脚
sbit START	= P1^4;//连了ADC0808的ALE(CBA编码的锁存信号)
sbit OE		= P1^3;
sbit add_a  = P1^2;
sbit add_b	= P1^1;
sbit add_c	= P1^0;

sbit DAC_CS_WR=P3^0; //DAC0832使能口
sbit K1=P3^1;
sbit K2=P3^2;
sbit K3=P3^3;
sbit Func=P3^4;
u8 code Duanma[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};/*0123456789abcdef*/
u8 code Weima[] ={0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f};/*自左向右*/
u8 TempData[8];					//全局显示变量
u16 addata=0,addata1=0,i,adcflag=0,dacflag=0;
//全局变量
u8 mode;	//模式:
u16 freq;	//频率
u8 time;	//计次参数
u8 AM;		//调幅

  主函数:

int main(void)
{
	while(1)
	{
		while(Func==1)
		{
			if(dacflag==0)
			{
				DAC_Init();
				dacflag=1;
			}
			scanKey();//数字量转模拟量,在示波器显示-DAC
		}
		while(Func==0)
		{
			if(adcflag==0)
			{
				ADC_Init();
				adcflag=1;
			}
			Gain();	  //模拟量转数字量,在数码管显示-ADC
			Display(0,8);
		}

		
	}
}

  初始化及子函数:

void DAC_Init()
{
	time=0;
	DAC_CS_WR=0;
	DAC_PORT=0;
	mode=0;
	freq=100;	//默认频率100Hz
	AM=255;		//最大幅度
	
	TMOD &= 0xF0;	//设置定时器模式
	TMOD |= 0x02;	//设置定时器模式
	TL0 = 0x9C;		//设置定时初值
	TH0 = 0x9C;		
	TF0 = 0;		//清除TF0标志
	TR0 = 1;		//定时器0开始计时
	EA = 1;         //开总中断
	ET0 = 1;        //开定时器0中断
}
void ADC_Init(void)		
{
	//EA = 0;        		//关闭定时器
	rst_8255=1;			//reset_8255
	delayms(1);
	rst_8255=0;
	COM8255=0x90;      	//方式0,PA输入  PC高四位/PB/PC低四位全输出
}
void Timer0Work() interrupt 1 //中断服务函数
{

	switch(mode)
	{
	    case K_SQU:squ_wave((u8)((time*freq/100)%100));break;  //计算出波的位置
		case K_TRI:tri_wave((u8)((time*freq/100)%100));break;
		case K_SAW:saw_wave((u8)((time*freq/100)%100));break;
	}
	time++;
	if(time>=100)//计数100次
		time=0;
}

  显示、数据处理函数等:

void Display(u8 FirstBit,u8 Num)
{
	static u8 i=0;
	PB8255=0xff;		//位码消隐
	PC8255=0x00;		//段码消隐
	
	PB8255=Weima[i+FirstBit];		//位码数据,从数码管从左到右的第一位开始对应
	if(i==1||i==5)
	{
		PC8255=TempData[i]+128;	//段码数据
	}
	else
		PC8255=TempData[i];		//段码数据
	//delayms(1);
	i++;
	if(i==Num)
		i=0;
}
void Gain(void)
{
	START=0;
	add_a=0; 				//采集第一路信号
	add_b=0;
	add_c=0;
	START=1; 				//根据时序图启动ADC0808的AD程序
	START=0;
	OE=1;					//转换结果允许输出
 	addata=PA8255;
	addata=addata*1.96;	 	//根据AD原理将采得的二进制数转换成可读的电压
	OE=0;	
	
	START=0;
	add_a=1; 				//采集第二路信号
	add_b=0;
	add_c=0;
	START=1; 				//根据时序图启动ADC0808的AD程序
	START=0;
	OE=1;					//转换结果允许输出
 	addata1=PA8255;
	addata1=addata1*1.96;	//根据AD原理将采得的二进制数转换成可读的电压
	OE=0;
	Display(0,8);	//防止断帧
    TempData[3]=Duanma[addata%10];	 //显示到数码管上
	TempData[2]=Duanma[addata/10%10];
    TempData[1]=Duanma[addata/100%10];
	TempData[0]=Duanma[addata/1000];
	Display(0,8);	//防止断帧
	TempData[7]=Duanma[addata1%10];	 //显示到数码管上
	TempData[6]=Duanma[addata1/10%10];
    TempData[5]=Duanma[addata1/100%10];
	TempData[4]=Duanma[addata1/1000];
	
}
void scanKey(void)
{
	if(K1==0)
	{
		mode=1;
	}
	if(K2==0)
	{
		mode=2;
	}
	if(K3==0)
	{
		mode=3;
	}
}

void squ_wave(u8 location)//方波函数
{
	
	if(location<50){
		DAC_PORT=AM;
	}
	else{
		DAC_PORT=0x00;
	}
}

void tri_wave(u8 location)//三角波函数
{
	u8 y;
	if(location<50)
		y=(50-location)*AM/50;
	else
		y=(location-50)*AM/50;
	DAC_PORT=y;
}

void saw_wave(u8 location)//锯形波函数
{
	DAC_PORT=location*AM/100;
}

void delayms(u16 j)
{
	u8 i;
	for(;j>0;j--)
	{
		i=250;
		while(--i);
		i=249;
		while(--i);
	}
}

更新说明 (2022.0609)

  我看到有很多同学问了参数问题,我把重要的参数设置截一下图。Proteus的单片机仿真强大,能做很多东西,但也有一些bug。个人经验而谈,有的时候做不出来,基本上是代码或者仿真的某一部分出了问题,熬了好久实在没有解决可能就是仿真bug。好久没做单片机了,有些问题我也只能说说可能的解决办法,具体解决还是要靠小伙伴们自己啦。
51单片机Proteus仿真+Keil工程-实验7-单片机AD和DA实验_第6张图片

51单片机Proteus仿真+Keil工程-实验7-单片机AD和DA实验_第7张图片
51单片机Proteus仿真+Keil工程-实验7-单片机AD和DA实验_第8张图片
51单片机Proteus仿真+Keil工程-实验7-单片机AD和DA实验_第9张图片
51单片机Proteus仿真+Keil工程-实验7-单片机AD和DA实验_第10张图片
  有人说芯片名字一样封装不一样,其实连线对了是可以使用的,新版本的有的库比较少,在此提供了当时搜集的一些库,放到指定位置即可。希望对大家有帮助:
链接:https://pan.baidu.com/s/1H2eezob1qyDtHPueyMk5Yw
提取码:4ui5
51单片机Proteus仿真+Keil工程-实验7-单片机AD和DA实验_第11张图片

参考文献
1.《单片机原理与接口技术》张毅刚
资源

返回目录

你可能感兴趣的:(51单片机,Proteus仿真,Keil工程,单片机)