目录

  • 1 单片机概述
    • 1.1 单片机概述
  • 2 单片机预备知识
    • 2.1 keil4使用教程
    • 2.2 利用Proteus和keil仿真51单片机流水灯
    • 2.3 PROTEUS中文手册
    • 2.4 芯片手册
    • 2.5 KEIL及PROTEUS软件下载
    • 2.6 单片机预备知识
  • 3 单片机结构与原理
    • 3.1 单片机最小系统
    • 3.2 使用Altium Designer绘制单片机最小电路原理图
    • 3.3 电源、晶振和复位电路
    • 3.4 单片机上下拉电阻
    • 3.5 单片机结构与原理
  • 4 C51语言
    • 4.1 4.1二进制、十进制和十六进制
    • 4.2 4.2 C 语言变量类型和范围
    • 4.3 4.3 C 语言基本运算符
    • 4.4 4.4 C 语言 for 循环语句
    • 4.5 4.5 C 语言 while 循环语句
    • 4.6 4.6 C 语言函数的简单介绍
    • 4.7 4.7 单片机延时方法(Keil 软件延时)
    • 4.8 C51语言
  • 5 LED灯实例
    • 5.1 工程仿真实例
    • 5.2 单片机 LED 灯闪烁程序
    • 5.3 单片机 LED 流水灯程序
    • 5.4 LED灯实例
    • 5.5 独立按键控制LED灯
  • 6 数码管/键盘实例
    • 6.1 单片机按键介绍
    • 6.2 单片机按键消抖程序
    • 6.3 单片机矩阵按键的扫描
    • 6.4 键 盘
    • 6.5 点亮数码管
    • 6.6 数码管的显示原理
    • 6.7 数码管实例
  • 7 中断系统
    • 7.1 51单片机中断详解(上)
    • 7.2 51单片机中断详解(中)
    • 7.3 51单片机中断详解(下)
    • 7.4 INT0中断计数实例
    • 7.5 中断系统
  • 8 定时/计数器
    • 8.1 51单片机定时计数器
    • 8.2 TIME0控制LED闪烁
    • 8.3 定时器 计数器
    • 8.4 实例一
    • 8.5 实例二
    • 8.6 实验-用计数器/中断实现 100 以内的按键计数
  • 9 串行通信
    • 9.1 51单片机串口通信
    • 9.2 单片机之间双向通信
    • 9.3 串行通信
  • 10 LCD1602实例
    • 10.1 1602液晶
    • 10.2 1602实验
  • 11 IIC总线技术
    • 11.1 51单片机IIC
    • 11.2 IIC总线实例
    • 11.3 I2C串行总线
  • 12 A/D&D/A转换
    • 12.1 单片机AD和DA
    • 12.2 ADC0808模数转换实例
    • 12.3 DAC0832数模转换实例
    • 12.4 D A 与 A D转换器
单片机AD和DA

51单片机AD和DA实验

内容:

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

环境:

   MDK-ARM V5.21a、Proteus 8.6

Proteus原理图

在这里插入图片描述
   方波(out输出0~5V,out1输出-5 V ~5V,考虑到反相器,两个输出波形相位差180°):
在这里插入图片描述
   三角波(out输出0~5V,out1输出-5 V ~5V)
三角波
   锯齿波(out输出0~5V,out1输出-5 V ~5V)
在这里插入图片描述
   P34按下后,进入ADC转换,模拟电压量在数码管上显示。
在这里插入图片描述

主要元器件:

DEVICES说明
7SEG-MPX8-CC-BLUE八位共阴数码管
8255A可编程并行接口芯片
ADC0808模数转换芯片
DAC0832模数转换芯片
74HC573锁存器
74LS373锁存器
AT89C51MCU
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.0592MHZfosc=11.0592MHZ
  时钟周期相当于T o s c = 1 f o s c ≈ 90.42 n s T_{osc}=\frac{1}{f_{osc}} \approx 90.42nsTosc=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低四位)全为输出。

  头文件以及宏定义等:

3536373839404142434445464748495051525354555657

  主函数:

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);
}


}
}
123456789101112131415161718192021222324252627

  初始化及子函数:

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;
}

12345678910111213141516171819202122232425262728293031323334353637383940

  显示、数据处理函数等:

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);
}
}