第一章 器件清单以及器件介绍
表1 器件清单
序号 |
器件 |
型号 |
数量 |
1 |
51开发板 |
MCS-51 |
1 |
2 |
AD/DA转换模块 |
PCF8591P |
1 |
3 |
杜邦线 |
15cm |
11 |
4 |
指示灯 |
\ |
8 |
5 |
光敏电阻 |
\ |
1 |
1. 1 AD/DA转换模块:
图1 AD/DA转换模块电路图
AIN0~AIN3为模拟量输入(可以选择输入方式)
A0~A2为硬件地址引脚,用来编码地址
AOUT应该为模拟量输出(DA转换)
SCL、SDA即为IIC通信时的总线引脚
1. 1. 1 设备地址
图2 设备地址
高四位为固定的编码,用来标识PCF8591,第四位就是硬件引脚+读写位。
1. 1. 2 控制字
图3 控制字示意图
第6位:控制模拟量输出使能
第5和4位:模拟量输入的方式
第2位:自动增量使能,每次A/D 转换后通道号将自动增加
第1和0位:模拟输入的通道选择
图4 原理图
根据原理图可以确定硬件设备地址。
1. 1. 3 AD转换的操作
分别来看写和读的时序
写:
图5 总线协议写模式,D/A转换
读:
图6 总线协议写模式,A/D转换
若要完成一次AD转换,应严格按照时序图进行:
开启总线-----发送地址+写-----发送控制字节-----等待PCF8591响应-----停止总线-----重新启动总线-----发送地址+读------读取数据-----主机发送非应答信号-----停止总线
1. 2 MCS-51
图7 51单片机原理图
由于AD-DA转换模块由SDA、SCL两个的状态来控制,故可以通过控制51单片机的P2.0以及P2.1来控制AD-DA转换的读入与读出。同时,也可以通过控制51单片机来控制数码管的显示。
图8 STC89C52RC 实物图
VCC(40脚)、VSS(20脚)—单片机的电源引脚,不同型号的单片机需要接入对应的电源电源电压。开发板上配带的单片机的供电电压为5V,低压单片机的电压为3.3V,用户在使用时要查看芯片手册,确保接入正确的电压。
XTAL1(19脚)、XTAL2(18脚)—外部时钟引脚,XTAL1为内部振荡电路的输入端,XTAL2为内部振荡电路的输出端。8051的时钟有两种方式,一种是片内时钟振荡方式,需要在这两个引脚上外接石英晶体和振荡电容,振荡电容的值一般为10pf~30pf;另一种是外部时钟方式,需要将XTAL1接地,外部时钟信号由XTAL2脚输入。
RST(9脚)—单片机复位引脚。当输入连续两个机器周期以上为高电平时为有效,用来完成单片机的复位初始化操作,复位后程序计数器PC=0000H,即复位后将从程序存储器的0000H单元读取第一条指令码,通俗的讲,就是单片机从头开始执行程序。
PSEN(29脚)—程序存储器允许输出控制端。在读外部程序存储器时PSEN低电平有效,以实现外部程序存储器单元的读操作,由于现在我们使用的单片机内部已经有足够大的ROM,所以几乎没有人再去扩展外部ROM,因此这个引脚大家只需了解即可。
ALE/PROG(30脚)—在单片机扩展外部RAM时,ALE用于控制把P0口的输出低8位地址送锁存器锁存起来,以实现低位地址和数据的隔离。ALE有可能是高电平也可能是低电平,当ALE是高电平时,允许地址锁存信号,当访问外部存储器时,ALE信号会跳变(即由正变负)将P0口上低8位地址信号送入锁存器;当ALE是低电平时,P0口上的内容和锁存器输出一致。关于锁存器的内容,我们后面会有详细介绍。在没有访问外部存储器期间,ALE以1/6振荡周期频率输出(即6分频),当访问外部存储器时,以1/12振荡周期输出(即12分频)。从这里可以看到,当系统没有进行扩展时,ALE会以1/6振荡周期的固定频率输出,因此可以作为外部时钟,或作为外部定时脉冲使用。PROG为编程脉冲的输入端,单片机的内部有程序存储器(ROM),它的作用是用来存放用户需要执行的程序,那么我们怎样才能将写好的程序存入这个ROM中呢?实际上,我们是通过编程脉冲输入才写进去的,这个脉冲的输入端口就是PROG。现在有很多单片机都已经不需要编程脉冲引脚往内部写程序了,比如我们用的STC单片机,它可以直接通过串口往里面写程序,只需要三条线与计算机相连即可。而且现在的单片机内部都已经带有丰富的RAM,所以也不需要再扩展RAM了,因此ALE/PROG这个引脚的用处也已经不大。
EA(31脚)— EA接高电平时,单片机读取内部程序存储器。当扩展有外部ROM时,当读取完内部ROM后自动读取外部ROM。EA接低电平时,单片机直接读取外部ROM。8031单片机内部是没有ROM的,所以在使用8031单片机时,这个引脚是一直接低电平的。8751单片机烧写内部EPROM时,利用此引脚输入21V的烧写电压。因为现在我们用的单片机都有内部ROM,所以一般在设计电路时此引脚始终接高电平。
I/O口引脚—P0口、P1口、P2口和P3口。
P0口(32脚~39脚)—双向8位三态I/O口,每个口可独立控制。51单片机P0口内部没有上拉电阻,为高阻状态,所以不能正常地输出高/低电平,因此该组I/O口在使用时务必要外接上拉电阻,一般我们选择接入10k欧的上拉电阻。
P1口(1脚~8脚)—准双向8位I/O口,每个口可独立控制,内带上拉电阻,这种接口输出没有高阻状态,输入也不能锁存,故不是真正的双向I/O口。之所以称它为“准双向”是因为该口在作为输入使用前,要先向该口进行写1操作,然后单片机内部才可正确读出外部信号,也就是要使其先有个“准”备过程,所以才称为准双向口。单片机P1.0引脚的第二功能为T2定时器/计数器的外部输入,P1.1引脚的第二功能为T2EX捕捉、重装触发,即T2的外部控制端。
P2口(21脚~28脚)—准双向8位I/O口,每个口可独立控制,内带上拉电阻,与P1口相似。
P3口(10脚~17脚)—准双向8位I/O口,每个口可独立控制,内带上拉电阻,作为第一功能使用时就当做普通I/O口,与P1口相似,作为第二功能使用时,各引脚的定义如下表所示。值得强调的是,P3口的每一个引脚均可独立定义为第一功能的输入/输出或第二功能。
P3口的第二功能定义
图9 P3口的第二功能定义
1. 3 数码管以及驱动电路
图10 六位数码管原理图
数码管的各段是由发光二极管组成的,按显示格式可分为八段和“米”字段,若按驱动方式可分为共阴和共阳。本项目使用的是八段码共阴数码管。
图11 数码管驱动电路原理图
图12 数码管硬件电路示意图
第二章 方案设计
2. 1 AD/DA转换模块
先由光敏电阻由于外界环境中的光强变化引起电阻的大小的变化,再由AD转换模块吧电信号转换为数字信号,传输到51单片机里,通过51单片机来显示当前光强(相对值0~255),显示在数码管上,再通过比较数值的大小来确定灯的强弱(由灯珠亮的数量大小来确定)。
表2 亮灯情况与环境亮度的关系
序号 |
显示数 |
灯珠数量 |
1 |
0~20 |
0 |
2 |
20~40 |
1 |
3 |
40~60 |
2 |
4 |
60~80 |
3 |
5 |
80~100 |
4 |
6 |
100~120 |
5 |
7 |
120~140 |
6 |
8 |
140~160 |
7 |
9 |
160~255 |
8 |
这里的灯珠亮的数量与灯的光强大小成正比,灯珠亮的数量越多,说明灯越亮,在0~20之间很亮,故灯不亮,而大于160时几乎没有光线,则为最亮。
将AIN0通道的模拟电压进行A/D转换后,将数字量通过LED数码管显示出来,同时再将该数字量写入PCF8591中,通过其内部的D/A转换为模拟电压输出驱动LED发光二极管。动态数码管显示电压值(相对值0~255),通过光敏电阻改变电压值,使用到了I2C总线驱动程序,PCF8591驱动程序。
I2C总线特点可以概括如下:
(1)在硬件上,I2C总线只需要一根数据线和一根时钟线两根线,总线接口已经集成在芯片内部,不需要特殊的接口电路,而且片上接口电路的滤波器可以滤去总线数据上的毛刺。因此I2C总线简化了硬件电路 PCB 布线,降低了系统成本,提高了系统可靠性。因为I2C芯片除了这两根线和少量中断线,与系统再没有连接的线,用户常用IC可以很容易形成标准化和模块化,便于重复利用。
(2)I2C总线是一个真正的多主机总线,如果两个或多个主机同时初始化数据传输,可以通过冲突检测和仲裁防止数据破坏,每个连接到总线上的器件都有唯一的地址,任何器件既可以作为主机也可以作为从机,但同一时刻只允许有一个主机。数据传输和地址设定由软件设定,非常灵活。
图13 I2C总线原理示意图
2. 2 数码管模块
图14 数字编码表
数码管要正常显示,就要用驱动电路来驱动数码管的各个段码,从而显示出我们要的数字。
图2.LED数码管引脚定义
数码管动态显示接口是单片机中应用最为广泛的一种显示方式之一,动态驱动是将所有数码管的8个显示笔划"a,b,c,d,e,f,g,dp"的同名端连在一起,另外为每个数码管的公共极COM增加位选通控制电路,位选通由各自独立的I/O线控制,当单片机输出字形码时,所有数码管都接收到相同的字形码,但究竟是哪个数码管会显示出字形,取决于单片机对位选通COM端电路的控制,所以我们只要将需要显示的数码管的选通控制打开,该位就显示出字形,没有选通的数码管就不会亮。通过分时轮流控制各个数码管的的COM端,就使各个数码管轮流受控显示,这就是动态驱动。在轮流显示过程中,每位数码管的点亮时间为1~2ms,由于人的视觉暂留现象及发光二极管的余辉效应,尽管实际上各位数码管并非同时点亮,但只要扫描的速度足够快,给人的印象就是一组稳定的显示数据,不会有闪烁感,动态显示的效果和静态显示是一样的,能够节省大量的I/O端口,而且功耗更低。
以上都是废话看代码(Keil、VW(推荐)下运行,使用的为TX-1C 51单片机开发板)
;//程序将AIN0通道的模拟电压进行A/D转换后,将数字量通过LED数码管显示出来,同时再将该数字量写入PCF8591中,通过其内部的D/A转换为模拟电压输出驱动LED发光二极管。
ACK BIT 20H.0 ;应答标志位ACK=0表示无应答
SCL BIT P2.1 ;时钟线
SDA BIT P2.0 ;数据线
WADD EQU 21H ;器件地址(即从地址)
SUBD EQU 22H ;器件内部地址(即子地址)
NUMBR EQU 23H ;需读取的字节数
NUMBW EQU 24H ;需写入的字节数
RDATA EQU 25H ;读出数据的存放首地址
WDATA EQU 26H ;写入数据的存放首地址
ORG 0000H
SJMP MAIN
ORG 0030H
MAIN:
MOV 55H,#11H
MOV 54H,#00H
MOV 53H,#00H
MOV 60H,#00H ;显示的百位清零
MOV 61H,#05H ;显示的十位清零
MOV 62H,#00H ;显示的个位清零
MOV WADD,#90H ;写入PCF8591的地址
MOV P0,#0FFH ;关闭显示
MOV P2,#0FFH
MOV SUBD,#40H ;写入PCF8591的控制字
MOV NUMBR,#1 ;写入需读取的字节数
MOV NUMBW,#1 ;写入需写入的字节数
MOV RDATA,#30H ;送入首地址
MOV WDATA,#40H ;送入首地址
MOV 50H,#0 ;显示的百位清零
MOV 51H,#0 ;显示的十位清零
MOV 52H,#0 ;显示的个位清零
M1: LCALL READI2C ;读PCF8591子程序
MOV 40H,30H
MOV NUMBR,#1
MOV NUMBW,#1
LCALL DAT
LCALL DISP
LCALL WRITEI2C ;写PCF8591子程序
SJMP M1
;数据处理子程序
DAT:
MOV A,30H
MOV B,#100
DIV AB
MOV 52H,A
MOV A,#10
XCH A,B
DIV AB
MOV 51H,A
MOV 50H,B
CMPDAT:
MOV A,#00 ;将被减数送累加器A
CLR C ;清借位
SUBB A,52H ;减法运算
JC CMPD1 ;如果有借位转
;说明数小于100
MOV A,#01 ;将被减数送累加器A
CLR C ;清借位
SUBB A,51H ;减法运算
JC CMPD2 ;如果有借位转
;说明数小于20
MOV P1,#11111111B;0个灯珠亮
;说明数大于20
RET
CMPD2:
MOV A,#03 ;将被减数送累加器A
CLR C ;清借位
SUBB A,51H ;减法运算
JC CMPD3 ;如果有借位转
;说明数小于40
MOV P1,#11111110B;1个灯珠亮
RET
;说明数大于40
CMPD3:
MOV A,#05 ;将被减数送累加器A
CLR C ;清借位
SUBB A,51H ;减法运算
JC CMPD4 ;如果有借位转
;说明数小于60
MOV P1,#11111100B;2个灯珠亮
RET
;说明数大于60
CMPD4:
MOV A,#07 ;将被减数送累加器A
CLR C ;清借位
SUBB A,51H ;减法运算
JC CMPD5 ;如果有借位转
;说明数小于80
MOV P1,#11111000B;3个灯珠亮
RET
;说明数大于80
CMPD5:
MOV P1,#11110000B;4个灯珠亮
RET
CMPD1:
MOV A,#01 ;将被减数送累加器A
CLR C ;清借位
SUBB A,52H ;减法运算
JC CMPD8 ;如果有借位转
MOV A,#01 ;将被减数送累加器A
CLR C ;清借位
SUBB A,51H ;减法运算
JC CMPD6 ;如果有借位转
;说明数小于120
MOV P1,#11100000B;5个灯珠亮
;说明数大于120
RET
CMPD6:
MOV A,#03 ;将被减数送累加器A
CLR C ;清借位
SUBB A,51H ;减法运算
JC CMPD7 ;如果有借位转
;说明数小于140
MOV P1,#11000000B;6个灯珠亮
RET
;说明数大于140
CMPD7:
MOV A,#05 ;将被减数送累加器A
CLR C ;清借位
SUBB A,51H ;减法运算
JC CMPD8 ;如果有借位转
;说明数小于160
MOV P1,#10000000B;7个灯珠亮
RET
;说明数大于160
CMPD8:
MOV P1,#00000000B;8个灯珠亮
RET
;显示子程序
DISP:
MOV R3,#0FEH
MOV R6,#6
MOV R1,#55H
LP:
MOV P0,#0FFH
SETB P2.7
CLR P2.7
MOV A,@R1
DEC R1
MOV DPTR,#TABLE ;查表取得段码
MOVC A,@A+DPTR
MOV P0,A
SETB P2.6
CLR P2.6
MOV P0,R3
SETB P2.7
CLR P2.7
MOV A,R3
RL A
MOV R3,A
LCALL DELAY
DJNZ R6,LP
MOV P0,#0FFH
SETB P2.7
CLR P2.7
RET
;从I2C总线读取N个字节数据子程序
READI2C:
LCALL STARTI2C ;启动I2C总线
MOV A,WADD ;送入器件地址
LCALL WI2C ;向I2C总线发送一个字节数据子程序
LCALL RACK ;读取从机应答位子程序
JNB ACK,ROUT ;判断从机有无应答ACK=0则无应答
MOV A,SUBD ;送入器件内部地址
LCALL WI2C
LCALL RACK
LCALL STARTI2C
MOV A,WADD
INC A ;改总线为读取状态
LCALL WI2C
LCALL RACK
JNB ACK,READI2C
MOV R0,RDATA ;送入读出数据的存放首地址
RI2C2:
LCALL RI2C
MOV @R0,A
DJNZ NUMBR,RI2C1 ;判断字节读取完否
LCALL NMACK ;调主机非应答子程序
ROUT:
LCALL STOPI2C ;调I2C总线停止子程序
MOV RDATA,#30H
RET
RI2C1:
LCALL MACK ;调主机应答子程序
INC R0
SJMP RI2C2 ;向I2C总线发送N个字节数据子程序
WRITEI2C:
LCALL STARTI2C ;启动I2C总线
MOV A,WADD ;送入器件地址
LCALL WI2C ;向I2C总线发送一个字节数据子程序
LCALL RACK ;读取从机应答位子程序
JNB ACK,WOUT ;判断从机有无应答ACK=0则无应答
MOV A,SUBD ;送入器件内部地址
LCALL WI2C
LCALL RACK
JNB ACK,WOUT
MOV R0,WDATA ;送入写入数据的存放首地址
WI2C1:
MOV A,@R0
LCALL WI2C
LCALL RACK
JNB ACK,WRITEI2C
INC R0
DJNZ NUMBW,WI2C
WOUT:
LCALL STOPI2C
MOV WDATA,#40H
RET
;I2C总线启动子程序
STARTI2C:
SETB SDA
SETB SCL
NOP
NOP
NOP
NOP
NOP
CLR SDA
NOP
NOP
NOP
NOP
NOP
CLR SCL
RET
;I2C总线停止子程序
STOPI2C:
CLR SDA
SETB SCL
NOP
NOP
NOP
NOP
NOP
SETB SDA
RET
;主机应答子程序
MACK:
CLR SDA
SETB SCL
NOP
NOP
NOP
NOP
NOP
CLR SCL
RET
;主机非应答子程序
NMACK:
SETB SDA
SETB SCL
NOP
NOP
NOP
NOP
NOP
CLR SCL
RET
;向I2C总线发送一个字节数据子程序
WI2C:
MOV R7,#8
WLP:
RLC A
MOV SDA,C
NOP
SETB SCL
NOP
NOP
NOP
NOP
NOP
CLR SCL
DJNZ R7,WLP
RET
;从I2C总线读取一个字节数据子程序
RI2C:
MOV R7,#8
CLR A
SETB SDA
NOP
RLP:
SETB SCL
NOP
NOP
MOV C,SDA
NOP
CLR SCL
RLC A
NOP
NOP
NOP
NOP
DJNZ R7,RLP
NOP
RET
;读取从机应答位子程序
RACK:
SETB SDA
NOP
NOP
SETB SCL
CLR ACK
NOP
NOP
MOV C,SDA
JC ACKEND
SETB ACK
ACKEND:
NOP
CLR SCL
NOP
RET
DELAY:
MOV R4,#5
D1:
MOV R5,#100
D2:
DJNZ R5,D2
DJNZ R4,D1
RET
TABLE: DB 3FH,06H,5BH,4FH,66H,6DH,7DH,07H
DB 7FH,6FH,77H,7CH,39H,5EH,79H,71H
DB 0F6H,0F3H,00H
END
结语:
有了AD/DA转换其他都很简单多加几个传感器可以完成很多天马行空的东西。本文只是基础中的基础。