单片机介绍
开发板介绍
-
超声波模块接口
-
NRF24L01无线模块接口
-
源蜂鸣器
-
DS18B20温度传感器接口
-
红外接收头
-
电源开关
-
自动下载电路(CH340转换芯片)
-
miniuSB接口(兼容安卓手机数据线,既可供电又可下载程序,还能串口通信)
-
ISP下载接口(为AT及AVR系列单片机下载)
-
复位按键
-
LCD12864模块接口
-
LCD1602模块接口
-
数码管模块(2个四位一体共阴数码管)
-
DS1302时钟模块
-
LED模块(8个LED)
-
独立按键模块(4个独立按键)
-
单片机所有I/O口(方便用户外扩功能模块)
-
单片机紧缩座(出厂时安装STC89C52单片机)
-
矩阵按键模块
-
8*8LED点阵模块(可显示字符及简单汉字)
-
74HC245芯片(驱动数码管模块)
-
步进电机模块(五线四项步进电机,驱动芯片ULN2003,也可驱动直流电机)
-
74HC138译码器芯片(控制数码管位选)
-
EEPROM模块(24C02芯片)
-
74HC595模块(内含8个LED)
-
AD/DA模块
51单片机介绍
学单片机需要什么基础?
单片机与以前所学的知识关联很少;只需要掌握很基本的数电模电知识,如二进制、十进制、十六进制之间的转换,与、或、非逻辑关系等;
对各种器件的概念基本上是从0开始;如果要用C语言编程,需具备简单的C语言基础。
什么是单片机?
- 单板机
将CPU芯片、存储器芯片、I/O接口芯片和简单的I/O设备(小键盘、LED显示器)等装配在一块印刷电路板上,再配上监控程序(固化在ROM中),就构成了一台单板微型计算机(简称单板机)。
单板机:
- 单片机
在一片集成电路芯片上集成微处理器、存储器。IO接口电路,从而构成了单芯片微型计算机,即单片机。 Intel公司推出了MCS-51系列单片机:集成8位CPU、4K字寸ROM、128字寸RAM、4个8位并口、1个全双工串行口、2个16位定时/计数器。寻址范围64K,并有控制功能较强的布尔处理器。
单片机能做什么?
工业自动化:数据采集、测控技术。
智能仪器仪表:数字示波器、数字信号源、数字万用表、感应电流表等。
消费类电子产品:洗衣机、电冰箱、空调机、电视机、微波炉、手机、IC卡、汽车电子设备等。
通讯方面:调制解调器、程控交换技术、手机、小灵通等。
武器装备:飞机、军舰、坦克、导弹、航天飞机、鱼雷制导、智能武器等
等等…
凡是与控制或简单计算有关的电子设备都可以用单片机来实现,根据具体实际情况还可以选择不同性能的单片机,如:atmel,stc,pic,avr,凌阳,80C51,arm等。
单片机的预备知识
电平特性
数字电路中只有两种电平:高电平和低电平
-
高电平:5V或者3.3V,取决单片机电源
-
低电平:0V
RS232电平:计算机串口的电平
-
高电平:-12V
-
低电平:+12V
所以当我们用单片机跟电脑通信的时候,我们要通过各种元器件将单片机的电平转换为计算机可识别的电平才能跟电脑进行通信。
2进制与16进制的表示及转换
由于数字电路中的只有两种电平的特性,计算机中使用的数字采用都是二进制的。二进制是使用0和1两个数码来表示的数,它的基数是2,进位规则是“逢二进一”。
十六进制的基数是F,进位规则是“逢十六进一”。
二进制和十六进制之间的转换:
十进制 | 二进制 | 十六进制 | 十进制 | 二进制 | 十六进制 |
---|---|---|---|---|---|
0 | 0 | 0 | 9 | 1001 | 9 |
1 | 1 | 1 | 10 | 1010 | A |
2 | 10 | 2 | 11 | 1011 | B |
3 | 11 | 3 | 12 | 1100 | C |
4 | 100 | 4 | 13 | 1101 | D |
5 | 101 | 5 | 14 | 1110 | E |
6 | 110 | 6 | 15 | 1111 | F |
7 | 111 | 7 | 16 | 10000 | 10 |
8 | 1000 | 8 |
二进制数的逻辑运算
“与”运算
有0得0
1 & 1 = 1 1\&1=1 1&1=1; 1 & 0 = 0 1\&0=0 1&0=0; 0 & 0 = 0 0\&0=0 0&0=0;
“或”运算
有1得1
$1|1=1 $; 1 ∣ 0 = 1 1 | 0=1 1∣0=1 ; 0 ∣ 0 = 0 0|0=0 0∣0=0;
“非”运算
1的非得0,0的非得1。
∼ 1 = 0 \sim1=0 ∼1=0; ∼ 0 = 1 \sim0=1 ∼0=1;
“异或”运算
必须不同,否则没有(0)
1 ∧ 1 = 0 1\land1=0 1∧1=0; 1 ∧ 0 = 1 1\land0=1 1∧0=1; 0 ∧ 0 = 0 0\land0=0 0∧0=0;
8051单片机介绍
80C51是MCS-51系列中的一个典型品种:其它厂商以8051为基核开发出的CMOS工艺单片机产品统称为80C51系列。当前常用的80C51系列单片机主要产品有:
Intel的:80C31、80C51、87C51,80C3280C52、87C52等;
ATMEL的:89C51、89C52、89C2051等;
Philips、华邦、Dallas、STCSiemens(Infineon)等公司的许多产品 。
80C51的引脚封装:
P3口第二功能各引脚功能定义:
P3.0:RXD串行口输入
P3.1:TXD串行口输出
P3.2:INTO外部中断0输入
P3.3:INT1外部中断1输入
P3.4:TO定时器0外部输入
P3.5:T1定时器1外部输入
P3.6:WR外部写控制
P3.7:RD外部读控制
8051 内部结构:
总线(BUS)是计算机各部件之间传送信息的公共通道。微机中有内部总线和外部总线两类。内部总线是CPU内部之间的连线。外部总线是指CPU与其它部件之间的连线。外部总线有三种:数据总线DB(Data Bus)地址总线 AB(Address Bus)和控制总线CB(Control Bus)。
CPU:由运算和控制逻辑组成,同时还包括中断系统和部分外部特殊功能寄存器;
RAM:用以存放可以读写的数据,如运算的中间结果、最终结果以及欲显示的数据;
ROM:用以存放程序、一些原始数据和表格;
I/O口:四个8位并行IO口,既可用作输入,也可用作输出;
T/C:两个定时/记数器,既可以工作在定时模式,也可以工作在记数模式;
五个中断源的中断控制系统;
一个全双工UART(通用异步接收发送器)的串行I/O口,用于实现单片机之间或单片机与微机之间的串行通信;
片内振荡器和时钟产生电路,石英品体和微调电容需要外接。最高振荡频率取决于单片机型号及性能。
单片机工作的基本时序:
机器周期和指令周期
(1)振荡周期:也称时钟周期,是指为单片机提供时钟脉冲信号的振荡源的周期,我们开发板上为12MHZ。
(2)状态周期::每个状态周期为时钟周期的2倍,是振荡周期经二分频后得到的。
(3)机器周期:一个机器周期包含6个状态周期S1~S6,也就是 12 个时钟周期。在一个机器周期内,CPU可以完成一个独立的操作。
(4)指令周期:它是指CPU完成一条操作所需的全部时间。每条指令执行时间都是有一个或几个机器周期组成。MCS-51系统中,有单周期指令、双周期指令和四周期指令。
C51基础知识
学单片机到底学什么
-
对I/O口的控制,无论单片机对外界进行何种控制,都是通过I/O日进行的。
-
接受外部的控制,通过I/O来感受外部的电压。
-
51单片机总共有P0、P1、P2、P3四个8位双向输入输出端口,每个端口都有锁存器、输出驱动器和输入缓冲器。4个I/O端口都能作输入输出口用。
单片机IO口的结构
上下拉电阻
上拉电阻就是将不确定的信号通过一个电阻拉到高电平,同时此电阻起到一个限流的作用,下拉就是下拉到低电平。
- OC门要输出高电平,外部必须加上拉电阻。
- 加大普通IO口的驱动能力。
- 起到限流的作用。
- 抵抗电磁干扰。
上下拉电阻的选取原则
1、从降低功耗方面考虑应该足够大,因为电阻越大,电流越小。
2、从确保足够的引脚驱动能力考虑应该足够小,电阻越小,电流才能越大。
3、开漏输出时,过大的上拉电阻会导致信号上升沿变缓。
单片机最小系统
CPU管脚:
电源电路
其实不太重要。
复位电路
复位电路原理图(开发板):
RST接在单片机第9脚。
时钟电路
晶振电路:
XT1和XT2分别接在单片机第18脚和第19脚。
下载电路
官方烧录软件需要冷启动,普中的下载软件不用。
开发软件与工程建立
keil
keil4或者keil5,版权问题,需要破解一下。可以网上搜索一下就好。
工程文件搭建
-
Project
(上方导航栏)->new uVision Project
(第一个下拉选项) -
弹出文档管理器(选择保存的路径),在下方文件名处为文件起名,之后点击保存。
-
弹出是否增加模板文件(选择否)。
-
弹出对话框,选择单片机(CPU)的类型,这里选择
Atmel
->AT89C52
。 -
弹出对话框,是否加载启动文件(选择否)。keil中已经集成。
-
点击
File
->New File
,新建文件。 -
ctrl+S
或者点击保存按钮
保存文件(会跳出对话框选择文件保存路径和文件名,文件名记得加后缀,如main.c
)。 -
保存好文件后需要右键
Source Group
添加已经存在的文件进入项目,选择对应文件确定即可。也可以在第5步之后直接
右键点击Source Group
在项目中创建文件。 -
之后就可以在
main.c
或者其他创建好的文件中编写程序了。
写好文件编程通过之后记得点击工具栏中的
魔法棒
(或者说锤子图标),在Output
栏中选择生成.hex
文件,这样才能在之后将.hex
文件储存的程序烧录到单片机中。
LED
LED工作原理
LED,即发光二极管,是一种半导体固体发光器件。
LED的工作是有方向性的,只有当正级接到LED阳极,负极接到LED的阴极的时候才能工作,如果反接LED是不能正常工作的。
LED原理图(开发板):
开发板上面LED的原理图如上图,LED的阳极串联一个电阻,然后连接到电源VCC,而LED的阴极连接到单片机的P2口,如果你想点亮一盏LED就对把单片机相对应的IO赋为低电平。
点亮LED(编程)
点亮一个LED:
#include <reg52.h>
sbit led=P2^0;
void main(){
while(1){
led=0;
}
}
LED流水灯:
/*
流水灯循环左移
*/
#include <reg52.h>
#include <instrins.h>
#define led P2
sbit led1=P2^0;
sbit led2 = P2^1;
void delay(unsigned int i) {
while(i--);
}
void main() {
unsigned int i;
led = 0xfe; //1111 1110
delay(50000);
while(1){
for(i=0; i<7; i++){
led=_crol_(led,1);//左移函数,在头文件instrins.h中
delay(50000);
}
}
}
蜂鸣器
蜂鸣器工作原理
蜂鸣器简介
在图片上认识蜂鸣器:有绿色电路板的一种是无源蜂鸣器,没有电路板而用黑胶封闭的一种是有源蜂鸣器。
有源和无源是根据蜂鸣器中是否有振荡电路来区分的。
电磁式蜂鸣器由振荡器、电磁线圈、磁铁、振动膜片及外壳等组成。接通电源后,振荡器产生的音频信号电流通过电磁线圈,使电磁线圈产生磁场,振动膜片在电磁线圈和磁铁的相互作用下,周期性地振动发声。
压电式蜂鸣器主要由多谐振荡器、压电蜂鸣片、阻抗匹配器及共鸣箱、外壳等组成。多谐振荡器由品体管或集成电路构成,当接通电源后(1.5~15V直流工作电压),多谐振荡器起振,输出1.5~2.5kH的音频信号,阻抗匹配器推动压电蜂鸣片发声。
在单片机应用的设计上,很多方案都会用到蜂鸣器,大部分都是使用蜂鸣器来做提示或报警,比如按键按下、开始工作、工作结束或是故障等等。
自激蜂鸣器(有源)是直流电压驱动的,不需要利用交流信号进行驱动,只需对驱动口输出驱动电平并通过放大电路放大驱动电流就能使蜂鸣器发出声音,非常简单。(无源蜂鸣器需要特定频率的电流信号来驱动,且单片机管脚输出信号需要进行放大才能驱动无源蜂鸣器)
改变单片机引脚输出波形的频率,就可以调整控制蜂鸣器音调,产生各种不同音色、音调的声音。
改变输出电平的高低电平占空比,则可以控制蜂鸣器的声音大小。
蜂鸣器原理图(开发板):
ULN2003D起电流放大的作用。
ULN2003简介
ULN2003是高耐压、大电流达林顿陈列,由七个硅NPN达林顿管组成。
ULN2003是大电流驱动阵列,多用于单片机、智能仪表、PLC、数字量输出卡等控制电路中。可直接驱动蜂鸣器、继电器等负载。
可以上网搜索一下ULN2003的手册查看详细信息。
驱动蜂鸣器(编程)
/*
蜂鸣器
*/
#include <reg52.h>
typedef unsigned char u8;
typedef unsigned int u16;
sbit beep=P1^5;
//10um
void delay(u16 i) {
while(i--);
}
void main() {
u16 i;
while(1){
beep=~beep; //~表示取反
delay(100);
}
}
数码管
显示器及其接口
单片机系统中常用的显示器有:
发光二极管LED(Light Emitting Diode)显示器、液晶LCD(Liquid Crystal Display)显示器、TFT液晶显示器等。LED显示器有两种显示结构:段显示(7段、米字型等)和点阵显示(5x8、8x8点阵等)。
LED数码管根据LED的不同接法可以分为2类:共阴和共阳。
使用LED显示器时,要注意区分这两种不同的接法。为了显示数字或字符,必须对数字或字符进行编码。七段数码管加上一个小数点,共计8段。因此为LED显示器提供的编码正好是一个字节。我们实验板用共阴LED显示器,根据电路连接图显示16进制数的编码己列在下表。
0 | 1 | 2 | 3 |
---|---|---|---|
0x3f | 0x06 | 0x5b | 0x4f |
4 | 5 | 6 | 7 |
0x66 | 0x6d | 0x7d | 0x07 |
8 | 9 | a | b |
0x7f | 0x6f | 0x77 | 0x7c |
c | d | e | f |
0x39 | 0x5e | 0x79 | 0x71 |
数码管原理图(开发板):
静态数码管
静态显示原理
LED显示器工作方式有两种:静态显示方式和动态显示方式。静态显示的特点是每个数码管的段选必须接一个8位数据线来保持显示的字形码。当送入一次字形码后,显示字形可一直保持,直到送入新字形码为止。这种方法的优点是占用CPU时间少,显示便于监测和控制。缺点是硬件电路比较复杂,成本较高。
74H573锁存器的使用
-
OE为使能端,当他为低电平的时候,锁存器开始工作。
-
VCC和GND为电源和地端。
-
LE为锁存端,当LE为高电平的时候,Q0~Q7都跟D0~D7状态一样,当LE为低电平的时候,Q0~Q7都锁存数据,无论D0~D7怎么变化,Q0~Q7都保持锁存之前的那个状态。
动态数码管
动态数码管显示原理
动态显示的特点是将所有数码管的段选线并联在一起,由位选线控制是哪一位数码管有效。选亮数码管采用动态扫描显示。所谓动态扫描显示即轮流向各位数码管送出字形码和相应的位选,利用发光管的余辉和人眼视觉暂留作用,使人的感觉好像各位数码管同时都在显示。动态显示的亮度比静态显示要差一些,所以在选择限流电阻时应略小于静态显示电路中的。
74HC138译码器的使用
管脚说明:
名称 | 功能说明 | 管脚号 |
---|---|---|
Y0 — Y6,Y7 | 数据输出 | 15—9,7 |
A0—A2 | 数据输入 | 1—3 |
E1,E2,E3 | 使能控制 | 4—6 |
VDD | 逻辑电源 | 16 |
GND | 逻辑地 | 8 |
数码管显示(编程)
#include <reg52.h>
#define lcd P0
typedef unsigned int u16;
typedef unsigned char u8;
sbit LSA = P2^4;
sbit LSB = P2^3;
sbit LSC = P2^2;
//code是c语言的关键字,将数组保存在快存中(下方的code区)
u8 code smgduan[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71} ;
void delay(u16 i) {
while(i--);
}
void DigDisplay(){
u8 i;
for(i=0;i<8;i++){
switch(i){
case 0:
LSA=0;LSB=0;LSC=0;break;
case 1:
LSA=0;LSB=0;LSC=1;break;
case 2:
LSA=0;LSB=1;LSC=0;break;
case 3:
LSA=0;LSB=1;LSC=1;break;
case 4:
LSA=1;LSB=0;LSC=0;break;
case 5:
LSA=1;LSB=0;LSC=1;break;
case 6:
LSA=1;LSB=1;LSC=0;break;
case 7:
LSA=1;LSB=1;LSC=1;break;
}
lcd=smgduan[i];
delay(100);
lcd=0x00; //消影
}
}
void main() {
while(1){
DigDisplay();
}
}
独立按键
独立按键工作原理
轻触开关是一种电子开关,使用时,轻轻按开关按钮就可使开关接通,当松开手时,开关断开。我们使用的开关如下图:
原理图(开发板):
这里和CPU的管脚图对应,可以得知,RXD接单片机P31口,TXD接P30口。
P0口内部没有上拉电阻。
按键在闭合和断开时,触点会存在抖动现象。
硬件消抖电路:
独立按键(编程)
独立按键控制led开关。
#include <reg52.h>
typedef unsigned int u16;
typedef unsigned char u8;
sbit k1 = P3^1;
sbit led = P2^0;
void delay(u16 i) {
while(i--);
}
void keypros() {
delay(1000);
if(k1==0) {
led=~led;
}
while(!k1);
}
void main() {
while(1) {
keypros();
}
}
矩阵按键
矩阵按键的工作原理
矩阵按键原理图(开发板):
矩阵按键扫描原理
方法一:
逐行扫描:我们可以通过高四位轮流输出低电平来对矩阵键盘进行逐行扫描,当低四位接收到的数据不全为1的时候,说明有按键按下,然后通过接收到的数据是哪一位为0来判断是哪一个按键被按下。
方法二:
行列扫描:我们可以通过高四位全部输出低电平,低四位输出高电平。当接收到的数据,低四位不全为高电平时,说明有按键按下,然后通过接收的数据值,判断是哪一列有按键按下,然后再反过来,高四位输出高电平,低四位输出低电平,然后根据接收到的高四位的值判断是那一行有按键按下,这样就能够确定是哪一个按键按下了。
矩阵按键(编程)
显示每个按键的值(0~f)
#include <reg52.h>
#define DIG P0
#define KEY P1
typedef unsigned int u16;
typedef unsigned char u8;
sbit LSA = P2^4;
sbit LSB = P2^3;
sbit LSC = P2^2;
//code是c语言的关键字,将数组保存在快存中(下方的code区)
u8 code smgduan[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71} ;
u8 keyValue;
void delay(u16 i) {
while(i--);
}
void keyDown(){
u8 a=0;
KEY = 0x0f;
if(KEY!=0x0f){
delay(1000);
if(KEY!=0x0f){
KEY=0x0f;
switch(KEY){
case(0x07): keyValue=0;break;
case(0x0b): keyValue=1;break;
case(0x0d): keyValue=2;break;
case(0x0e): keyValue=3;break;
}
KEY=0xf0;
switch(KEY){
case(0x70): keyValue=keyValue;break;
case(0xb0): keyValue=keyValue+4;break;
case(0xd0): keyValue=keyValue+8;break;
case(0xe0): keyValue=keyValue+12;break;
}
//按键一直按着、松开的判断
while((a<50)&&(KEY!=0xf0)){
delay(1000);
a++;
}
}
}
}
void main() {
while(1){
LSA=0;
LSB=0;
LSC=0;
while(1){
keyDown();
DIG=smgduan[keyValue];
}
}
}
8X8LED点阵
8X8LED点阵工作原理
内部结构图:
8X8LED点阵原理图(开发板):
通过74HC595串行输入、并行输出D0~D7的数据进行控制。详细介绍可以通过查阅74HC595手册查看。
74HC595引脚功能:
符号 | 引脚 | 描述 |
---|---|---|
Q0···Q7 | 15, 1, 7 | 并行数据输出 |
GND | 8 | 地 |
Q7’ | 9 | 串行数据输出 |
MR | 10 | 主复位(低电平) |
SHCP | 11 | 移位寄存器时钟输入 |
STCP | 12 | 存储寄存器时钟输入 |
OE | 13 | 输出有效(低电平) |
DS | 14 | 串行数据输入 |
VCC | 16 | 电源 |
主要看引脚号,符号名称可能不一样。
74595 的控制端说明:
/SCLR(10 脚):低电平时将移位寄存器的数据清零。通常我将它接 Vcc。
SCK(11 脚):上升沿时数据寄存器的数据移位。QA–>QB–>OC–>…->QH;下降沿移位寄存器数据不变。(脉冲宽度:5V时,大于几十纳秒就行了。)
RCK(12 脚):上升沿时移位寄存器的数据进入数据存储寄存器,下降沿时存储寄存器数据不变。通常我将 RCK 置为低点平,当移付结束后,在RCK端产生一个正脉冲(5V 时,大于几十纳秒就行了。我通常都选微秒级),更新显示数据。
/G(13 脚):高电平时禁止输出(高阻态)。如果单片机的引脚不紧张,用一个脚控制它 可以方便地产生闪烁和熄灭效果。比通过数据端移位控制要省时省力。
8X8LED点阵实验(编程)
#include <reg51.h>//这里注意改成51,不然可能会有管脚定义冲突
#include <intrins.h>
typedef unsigned int u16;
typedef unsigned char u8;
sbit SRCLK=P3^6;
sbit RCLK=P3^5;
sbit SER=P3^4;
u8 ledduan[]={0x38,0x7c,0x7e,0x3f,0x3f,0x7e,0x7c,0x38};
u8 ledwei[]={0x7f,0xbf,0xdf,0xef,0xf7,0xfb,0xfd,0xfe};
void delay(u16 i) {
while(i--);
}
void hc595SendByte(u8 dat){
u8 a;
SRCLK=0;
RCLK=0;
for(a=0;a<8;a++){
SER=dat>>7;
dat<<=1;
SRCLK=1;
_nop_();
_nop_();
SRCLK=0;
}
RCLK=1;
_nop_();
_nop_();
RCLK=0;
}
void main() {
//hc595SendByte(0x80); //1000 0000
//P0=0x7f; //0111 1111
u8 i;
while(1){
P0=0x7f;
for(i=0;i<8;i++){
P0=ledwei[i];
hc595SendByte(ledduan[i]);
delay(50);
hc595SendByte(0x00);
}
}
}
中断系统
中断的原理
中断的概念
CPU在处理某一事件A时,发生了另一事件B请求CPU迅速去处理(中断发生);
CPU暂时中断当前的工作,转去处理事件B(中断响应和中断服务);
待CPU将事件B处理完毕后,再回到原来事件A被中断的地方继续处理事件A(中断返回),这一过程称为中断。
引起CPU中断的根源,称为中断源。中断源向CPU提出的中断请求。CPU暂时中断原来的事务A,转去处理事件B。对事件B处理完毕后,再回到原来被中断的地方(即断点),称为中断返回。实现上述中断功能的部件称为中断系统。
随着计算机技术的应用,人们发现中断技术不仅解决了快速主机与慢速IO设备的数据传送问题,而且还具有如下优点:
- 分时操作。CPU可以分时为多个IO设备服务,提高了计算机的利用率;
- 实时响应。CPU能够及时处理应用系统的随机事件,系统的实时性大大增强;
- 可靠性高。CPU具有处理设备故障及掉电等突发性事件能力,从而使系统可靠性提高。
89C51/52的中断系统有5个中断源,2个优先级,可实现二级中断嵌套:
(P3.2)
可由IT0(TCON.0)
选择其为低电平有效还是下降沿有效。当CPU检测到P3.2
引脚上出现有效的中断信号时,中断标志IE0(TCON.1)
置1
,向CPU申请中断。(P3.3)
可由IT1(TCON.2)
选择其为低电平有效还是下降沿有效。当CPU检测到P3.3
引脚上出现有效的中断信号时,中断标志IE1(TCON.3)
置1
,向CPU申请中断。TF0(TCON.5)
:片内定时/计数器T0
溢出中断请求标志。当定时/计数器T0
发生溢出时,置位TF0
(即TF0=1
),并向CPU申请中断。TF1(TCON.7)
:片内定时/计数器T1
流出中断请求标志。当定时/计数器T1
发生溢出时,置位TF1
(即TF1=1
),并向CPU申请中断。RI(SCON.0)
或TI(SCON.1)
,串行口中断请求标志。当串行口接收完一帧串行数据时置位RI
或当串行口发送完一帧串行数据时置位T1
,向CPU申请中断。
中断允许的控制
CPU对中断系统所有中断以及某个中断源的开放和屏蔽是由中断允许寄存器IE控制的。
位 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | |
---|---|---|---|---|---|---|---|---|---|
字节地址:A8H | EA | ES | ET1 | EX1 | ET0 | EX0 | IE |
EX0(IE.0)
:外部中断0允许位;ETO(IE.1)
:定时/计数器T0
中断允许位;EX1(IE.2)
:外部中断1允许位;ET1(IE.3)
:定时/计数器T1
中断允许位;ES (IE.4)
:串行口中断允许位;EA (IE.7)
:CPU中断允许(总允许)位。这一位控制整个CPU的中断允许(总开关),没打开中断无法产生,打开才有可能产生中断。
中断请求标志
控制寄存器TCON的中断标志
位 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | |
---|---|---|---|---|---|---|---|---|---|
字节地址:88H | TF1 | TR1 | TF0 | TR0 | IE1 | IT1 | IE0 | IT0 | TCON |
-
ITO(TCON.0)
:外部中断0触发方式控制位。- 当
IT0=0
时,为电平触发方式(低电平有效) - 当
IT0=1
时,为边沿触发方式(下降沿有效)
- 当
-
IEO(TCON.1)
:外部中断0中断请求标志位。 -
IT1(TCON.2)
:外部中断1触发方式控制位。 -
IE1(TCON.3)
:外部中断1中断请求标志位。 -
TFO(TCON.5)
:定时/计数器T0
溢出中断请求标志位。 -
TF1(TCON.7)
:定时/计数器T1
溢出中断请求标志位。这里第6位和第4位
TR0
和TR1
下文定时器章节有介绍,用来控制定时/计数器的启停。
同一优先级中的中断申请不止一个时,则有中断优先权排队问题。同一优先级的中断优先权排队,由中断系统硬件确定的自然优先级形成,其排列如所示:
各中断源响应优先级及中断服务程序入口表
中断源 | 中断标志 | 中断服务程序入口 | 优先级顺序 |
---|---|---|---|
外部中断 0 (/INT0) | IE0 | 0003H | 高 |
定时/计数器 0 (T0) | TF0 | 000BH | ↓ |
外部中断 1 (/INT1) | IE1 | 0013H | ↓ |
定时/计数器 1 (T1) | TF1 | 001BH | ↓ |
串行口 | RI 或 TI | 0023H | 低 |
中断源
中断源符号 | 名称 | 中断引起原因 | 中断号 |
---|---|---|---|
/INT0 | 外部中断0 | P3.2引脚低电平或下降沿信号 | 0 |
T0 | 定时器0中断 | 定时/计数器0计数回0溢出 | 1 |
/INT1 | 外部中断1 | P3.3引脚低电平或下降沿信号 | 2 |
T1 | 定时器1中断 | 定时/计数器1计数回0溢出 | 3 |
TI/RI | 串行口中断 | 串行通信完成一帧数据发送或接收引起中断 | 4 |
中断引起原因即中断的触发条件,中断号在编程时与中断处理函数相对应
举个例子:在
HC6800-ES V2.0
中,独立按键K3
和CPU的P3.2
引脚相连(默认高电平,当K3
按下后,P3.2
和GND
导通电平被拉低),当外部中断0条件满足时(即EA=1
:CPU总中断打开、EX0=1
:外部中断0允许打开、IT0=0
:设置好外部中断0触发方式低电平有效),同时写好中断处理函数void int0 () interrupt 0 {}
:int0
是函数名;interrupt
是关键字;0
是中断号;当写好程序烧录进芯片时,按下K3
触发了外部中断0的触发条件,此时对应的中断处理函数int0
将会执行(中断响应了)。示例代码
中断优先级有三条原则
- CPU同时接收到几个中断时,首先响应优先级别最高的中断请求。
- 正在进行的中断过程不能被新的同级或低优先级的中断请求所中断。
- 正在进行的低优先级中断服务,能被高优先级中断请求所中断。
为了实现上述后两条原则,中断系统内部设有两个用户不能寻址的优先级状态触发器。其中一个置1,表示正在响应高优先级的中断,它将阻断后来所有的中断请求;另一个置1,表示正在响应低优先级中断,它将阻断后来所有的低优先级中断请求。(2个优先级)
中断处理过程
中断响应条件
- 中断源有中断请求;
- 此中断源的中断允许位为1;
- CPU开中断(即EA=1)。
以上三条同时满足时,CPU才有可能响应中断。
使用中断,程序员需要做什么
- 你想使用的中断是哪个?选择相应的中断号;
- 你所希望的触发条件是什么?->中断源
- 你希望在中断之后干什么?->(中断处理函数)
以外部中断0为例
主程序中需要有以下代码:
EA=1; //打开总中断开关
EX0=1; //开外部中断
IT0=0/1; //设置外部中断的触发方式
//中断服务函数:
void int0 () interrupt 0 using 1{ //interrupt后面是中断号,using 1 可以省略不用管
do anything that you want
}
外部中断0(编程)
STC89c52单片机引脚
独立按键
外部中断0示例程序:
#include <reg52.h>
typedef unsigned int u16;
typedef unsigned char u8;
sbit k3 = P3^2; //定义独立按键k3和P3.2引脚相连,接收P3.2传来的由k3出发的信号
sbit led = P2^0; //定义D1号灯和P2.0引脚相连,由P2.0引脚输出信号控制D1开关
void delay(u16 i) {
while(i--);
}
void IntoInit(){ //初始化外部中断允许触发开关
IT0=1; //选择中断触发方式为下降沿有效
EA=1; //CPU中断总开关打开
EX0=1; //外部中断0允许位打开
}
void int0 () interrupt 0{ //中断处理函数(如果k3按下)
delay(1000); //延时消抖
if(k3==0){ //如果k3按下(P3.2引脚电平被拉低)
led=~led; //D1状态取反
}
}
void main() {
IntoInit();
while(1);
}
定时器和计数器
定时器的原理
CPU时序相关知识
- 振荡周期:为单片机提供定时信号的振荡源的周期(晶振周期或外加振荡周期)
- 状态周期:2个振荡周期为1个状态周期,用S表示。状态周期又称S周期或时钟周期。
- 机器周期:1个机器周期含6个状态周期,12个振荡周期。
- 指令周期:完成1条指令所占用的全部时间,它以机器周期为单位。
例如:外接晶振为
12MHz
(频率)时,51单片机相关周期的具体值为:振荡周期=
1/12us
;(频率的倒数1/12M
s -->1/12
us)状态周期=
1/6us
;机器周期=
1us
;指令周期=
1~4us
;
定时器相关知识
51单片机有两组定时器/计数器,因为既可以定时,又可以计数,故称之为定时器/计数器。
定时器/计数器和单片机的CPU是相互独立的。定时器/计数器工作的过程是自动完成的,不需要CPU的参与。
51单片机中的定时器/计数器是根据机器内部的时钟或者是外部的脉冲信号对寄存器中的数据加1。
有了定时器/计数器之后,可以增加单片机的效率,一些简单的重复加1的工作可以交给定时器/计数器处理。CPU转而处理一些复杂的事情。同时可以实现精确定时作用。
之前是用delay()函数使CPU占用时间来达到延时的效果。可以用定时器完成秒表的开发。
定时/计数器工作原理
定时/计数器实质上是一个加1计数器。它随着计数器的输入脉冲进行自加1,也就是每来一个脉冲,计数器就白动加1,当加到计数器为全1时,再输入一个脉冲就使计数器回零,且计数器的溢出使相应的中断标志位置1,向CPU发出中断请求(定时/计数器中断允许时)。如果定时/计数器工作于定时模式,则表示定时时间己到;如果工作于计数模式,则表示计数值已满。
可见,由溢出时计数器的值减去计数初值才是加1计数器的计数值。
51单片机定时器结构
定时/计数器的实质是加1计数器(16位),由高8位和低8位两个寄存器THx
和TLx
组成(x可以是0或1,TH0
、TH1
、TL0
、TL1
)。TMOD
是定时/计数器的工作方式寄存器,确定工作方式和功能;TCON
是控制寄存器,控制T0
、T1
的启动和停止及设置溢出标志。
可以通过外部脉冲进行计数,也可以通过内部机器脉冲进行计数。
根据传入的脉冲信号,先累加低8位
TL0
,当TL0
满了(0xff
)之后再次累加会将TL0
清空并给TH0
进行一次累加。当TL0
和TH0
都加满了之后再次加1则会溢出,此时T0
溢出中断请求标志位TF0
置1,若此时定时/计数器0相关中断允许配置完成的话(即EA=1
:CPU总中断打开、TFO(TCON.5)=1
:T0
溢出中断请求标志位打开),则会产生中断。
定时/计数器的控制
51单片机定时/计数器的工作由两个特殊功能寄存器控制。TMOD
用于设置其工作方式;TCON
用于控制其启动和中断申请。
工作方式寄存器TMOD
工作方式寄存器TMOD
用于设置定时/计数器的工作方式,低四位用于控制T0
,高四位用于控制T1
。其格式如下:
位 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | |
---|---|---|---|---|---|---|---|---|---|
字节地址: 89H | GATE | C/¯T | M1 | M0 | GATE | C/¯T | M1 | M0 | TMOD |
GATE
是门控位,GATE=0
时,用于控制定时器的启动是否受外部中断源信号的影响。只要用软件使TCON
中的TRO
或TR1
为1
,就可以启动定时/计数器工作;GATA=1
时,要用软件使TRO
或TR1
为1
,同时外部中断引脚INT0/1
也为高电平时,才能启动定时/计数器工作。即此时定时器的启动条件,加上了INTO/1
引脚为高电平这一条件。(GATE
位控制的是定时/计数器的启动条件,通常使GATE=0
,这样只需要控制TRO
或TR1
就可以控制定时/计数器的启停)
CT
:定时/计数模式选择位。C/T=0
为定时模式;C/T=1
为计数模式。(即C高电平有效,T低电平有效)
M1\M0
:工作方式设置位。定时/计数器有四种工作方式。
定时/计数器工作方式设置表:
M1/M0 | 工作方式 | 说明 |
---|---|---|
00 | 方式 0 | 13 位定时/计数器 |
01 | 方式 1 | 16 位定时/计数器 |
10 | 方式 2 | 8 位自动重装定时/计数器 |
11 | 方式 3 | T0 分成两个独立的 8 位定时/计数器;T1 此方式停止计数 |
通常使用方式2和方式3,详见下文。
控制寄存器TCON
TCON
的低4位用于控制外部中断,已在前面介绍。TCON
的高4位用于控制定时/计数器的启动和中断申请。其格式如下:
位 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | |
---|---|---|---|---|---|---|---|---|---|
字节地址: 88H | TF1 | TR1 | TF0 | TR0 | IE1 | IT1 | IE0 | IT0 | TCON |
TF1(TCON.7)
:T1
溢出中断请求标志位。T1
计数溢出时由硬件自动置TF1
为1。CPU响应中断后TF1
由硬件自动清0。T1
工作时,CPU
可随时查询TF1
的状态。所以,TF1
可用作查询测试的标志。TF1
也可以用软件置1或清0,同硬件置1或清0的效果一样。TR1(TCON.6)
:T1
运行控制位。TR1
置1时,T1
开始工作;TR1
置0时,T1
停止工作。TR1
由软件置1或清0。所以,用软件可控制定时计数器的启动与停止。TFO(TCON.5)
:T0
溢出中断请求标志位,其功能与TF1
类同。TRO(TCON.4)
:T0
运行控制位,其功能与TR1
类同。
定时/计数器的工作方式
-
方式0
方式0为13位计数,由
TL0
的低5位(高3位未用)和TH0
的8位组成。TL0
的低5位溢出时向TH0
进位,TH0
溢出时,置位TCON
中的TFO
标志,向CPU发出中断请求。(配置好中断触发环境则会产生中断)T1
的TL1
和TH1
与T0
相同定时器模式时有: N = t / T c y N=t/ Tcy N=t/Tcy
计数初值计算的公式为: X = 2 13 − N X=2^{13}-N X=213−N
定时器的初值还可以采用计数个数直接取补法获得。
计数模式时,计数脉冲是
T0
引脚上的外部脉冲。门控位
GATE
具有特殊的作用。当GATE=0
时,经反相后使或门输出为1,此时仅由TR0
控制与门的开启,与门输出1时,控制开关接通,计数开始;当GATE=1
时,由外中断引脚INTO
信号控制或门的输出,此时控制与门的开启由外中断引脚信号INTO
和TR0
共同控制。当TR0=1
时,外中断引脚信号引脚的高电平启动计数,外中断引脚信号引脚的低电平停止计数。这种方式常用来测量外中断引脚上正脉冲的宽度。 -
方式1
方式1的计数位数是16位,由TL0
作为低8位,TH0
作为高8位,组成了16位加1计数器。计数个数与计数初值的关系为: X = 2 16 − N X=2^{16}-N X=216−N
后面编写程序时会使用这个公式来计算初值。
-
方式2
方式2为自动重装初值的8位计数方式。(低8位计满就重新装载初值,不向高8位进位)
计数个数与计数初值的关系为: X = 2 8 − N X=2^{8}-N X=28−N
工作方式2特别适合于用作较精确的脉冲信号发生器。
-
方式3
方式3只适用于定时/计数器T0
,定时器T1
处于方式3时相当于TR1=0
,停止计数。工作方式3将
T0
分成为两个独立的8位计数器TL0
和TH0
。
使用定时器,该做哪些工作
初始化程序应完成如下工作:
- 对
TMOD
赋值,以确定T0
和T1
的工作方式。(C/T
选择、M0M1
选择) - 计算初值,并将其写入
TH0
、TL0
或TH1
、TL1
。 - 中断方式时,则对
EA
(CPU总中断)赋值,开放定时器中断。 - 使
TRO
或TR1
置位,启动定时/计数器定时或计数。
计数器初值计算
-
机器周期也就是CPU完成一个基本操作所需要的时间
-
机器周期=1/单片机的时钟频率。
-
51单片机内部时钟频率是外部时钟的12分频。也就是说当外部晶振的频率输入到单片机里面的时候要进行12分频。比如说你用的是 12 M H z 12MHz 12MHz的晶振,那么单片机内部的时钟频率就是 12 / 12 M H z 12/12MHz 12/12MHz,当你使用 12 M H z 12MHz 12MHz的外部晶振的时候, 机器周期 = 1 / 1 M = 1 u s 机器周期=1/1M=1us 机器周期=1/1M=1us。
-
而我们定时 1 m s 1ms 1ms的初值是多少呢, 1 m s / l u s = 1000 1ms/lus=1000 1ms/lus=1000。也就是要计数1000个数。
以工作方式1来计算:方式1计数位数为16位,全满时值为 2 16 − 1 = 65535 2^{16}-1=65535 216−1=65535
那么 初值 = 65535 − 1000 + 1 (因为实际上计数器计数到 66636 才溢出) = 64536 = F C 18 ( H e x ) 初值=65535-1000+1(因为实际上计数器计数到66636才溢出)=64536=FC18(Hex) 初值=65535−1000+1(因为实际上计数器计数到66636才溢出)=64536=FC18(Hex)
这里的初值计算就是要我们算出定时/计数器从某个值X开始,计1000个数(定时具体时间所需要计的数)就产生溢出(中断),求X的值。
定时器中断(编程)
/*
实现小灯1秒闪烁
*/
#include <reg52.h>
typedef unsigned int u16;
typedef unsigned char u8;
sbit led = P2^0;
void timer0Init(){
TMOD|=0x01; //TMOD = TMOD | 0x01;
TH0=0xfc;
TL0=0x18;
ET0=1;
EA=1;
TR0=1;
}
void main() {
timer0Init();
while(1);
}
void time0 () interrupt 1{
static u16 i;
TH0=0xfc;
TL0=0x18;
i++;
if(i==1000){ //1s
i=0;
led=~led;
}
}
串口通信
串口通信原理
计算机串行通信基础
随着多微机系统的广泛应用和计算机网络技术的普及,计算机的通信功能显得愈来愈重要。计算机通信是指计算机与外部设备或计算机与计算机之间的信息交换。
通信有并行通信和串行通信两种方式。在多微机系统以及现代测控系统中信息的交换多采用串行通信方式。
计算机通信是将计算机技术和通信技术相结合,完成计算机与外部设备或计算机与计算机之间的信息交换。可以分为两大类:并行通信与串行通信。
并行通信通常是将数据字节的各位用多条数据线同时进行传送。
并行通信控制简单、传输速度快;由于传输线较多,长距离传送时成本高且接收方的各位同时接收存在困难。
串行通信是将数据字节分成一位一位的形式在条传输线上逐个地传送。
串行通信的特点:传输线少,长距离传送时成本低,且可以利用电话网等现成的设备,但数据的传送控制比并行通信复杂。
串行通信的基本概念
异步通信与同步通信
-
异步通信
异步通信是指通信的发送与接收设备使用各自的时钟控制数据的发送和接收过程。为使双方的收发协调,要求发送和接收设备的时钟尽可能一致。异步通信是以字符(构成的帧)为单位进行传输,字符与字符之间的间隙(时间间隔)是任意的,但每个字符中的各位是以固定的时间传送的,即字符之间不一定有“位间隔”的整数倍的关系,但同一字符内的各位之间的距离均为“位间隔”的整数倍。
异步通信的数据格式:
异步通信的特点:不要求收发双方时钟的严格一致,实现容易,设备开销较小,但每个字符要附加2~3位用于起止位,各帧之间还有间隔,因此传输效率不高。
-
同步通信
同步通信时要建立发送方时钟对接收方时钟的直接控制,使双方达到完全同步。此时,传输数据的位之间的距离均为“位间隔”的整数倍,同时传送的字符间不留间隙,即保持位同步关系,也保持字符同步关系。发送方对接收方的同步可以通过两种方法实现。
这里不太清楚,应该就是两种发送数据的方式吧,时钟信号应该都是要同步的。
串行通信的传输方向
-
单工是指数据传输仅能沿一个方向,不能实现反向传输。
-
半双工是指数据传输可以沿两个方向,但需要分时进行。
-
全双工是指数据可以同时进行双向传输。
串行通信常见的错误校验
-
奇偶校验
在发送数据时,数据位尾随的1位为奇偶校验位(1或0)。奇校验时,数据中“1”的个数与校验位“1”的个数之和应为奇数;偶校验时,数据中“1”的个数与校验位“1”的个数之和应为偶数。接收字符时,对“1”的个数进行校验,若发现不一致,则说明传输数据过程中出现了差错。
-
代码和校验
代码和校验是发送方将所发数据块求和(或各字节异或),产生一个字节的校验字符(校验和)附加到数据块末尾。接收方接收数据同时对数据块(除校验字节外)求和(或各字节异或),将所得的结果与发送方的“校验和”进行比较,相符则无差错,否则即认为传送过程中出现了差错。 -
循环冗余校验
这种校验是通过某种数学运算实现有效信息与校验位之间的循环校验,常用于对磁盘信息的传输、存储区的完整性校验等。这种校验方法纠错能力强广泛应用于同步通信中。
传输速率
比特率(波特率)是每秒钟传输二进制代码的位数,单位是:位/秒(bps)。如每秒钟传送240个字符,而每个字符格式包含10位(1个起始位、1个停止位、8个数据位),这时的比特率为: 10 位 × 240 个 / 秒 = 2400 b p s 10位×240个/秒=2400 bps 10位×240个/秒=2400bps
传输距离与传输速率的关系:
串行接口或终端直接传送串行信息位流的最大距离与传输速率及传输线的电气特性有关。当传输线使用每 0.3 m(约1英尺)有 50 PF 电容的非平衡屏蔽双绞线时,传输距离随传输速率的增加而减小。当比特率超过 1000 bps 时,最大传输距离迅速下降,如 9600 bps 时最大距离下降到只有 76 m(约250英尺)。
串行通信接口标准
RS-232C接口
RS-232C是EIA(美国电子工业协会)1969年修订RS-232C标准。RS-232C定义了数据终端设备(DTE)与数据通信设备(DCE)之间的物理接口标准。
机械特性
RS-252C接口规定使用25针连接器,连接器的尺寸及每个插针的排列位置都有明确的定义。(阳头)
左图为25针标准接口,右图为9针非标准接口。现多用9针的。
功能特性
RS-232C 标准接口主要引脚定义:
插针序号 | 信号名称 | 功能 | 信号方向 |
---|---|---|---|
1 | PGND | 保护接地 | |
2 (3) | TXD | 发送数据(串行输出) | DTE→DCE |
3 (2) | RXD | 接收数据(串行输入) | DTE←DCE |
4 (7) | RTS | 请求发送 | DTE→DCE |
5 (8) | CTS | 允许发送 | DTE←DCE |
6 (6) | DSR | DCE就绪(数据建立就绪) | DTE←DCE |
7 (5) | SGND | 信号接地 | |
8 (1) | DCD | 载波检测 | DTE←DCE |
20 (4) | DTR | DTE就绪(数据终端准备就绪) | DTE→DCE |
22 (9) | RI | 振铃指示 | DTE←DCE |
注:插针序号()内为9针非标准连接器的引脚号。
主要使用
TXD
、RXD
、SGND
这三个管脚
过程特性
过程特性规定了信号之间的时序关系,以便正确地接收和发送数据。
远程通信连接:
近程通信连接:
RS-232C电平与TTL电平转换驱动电路:
采用RS-232C接口存在的问题
-
传输距离短,传输速率低
RS-232C总线标准受电容允许值的约束,使用时传输距离一般不要超过15米(线路条件好时也不超过几十米)。最高传送速率为20Kbps。
-
有电平偏移
RS-232C总线标准要求收发双方共地。通信距离较大时,收发双方的地电位差别较大,在信号地上将有比较大的地电流并产生压降。
-
抗干扰能力差
RS-232C在电平转换时采用单端输入输出,在传输过程中当干扰和声混在正常的信号中。为了提高信噪比,RS-232C总线标准不得不采用比较大的电压摆幅
RS-422A接口
RS-422A输出驱动器为双端平衡驱动器。如果其中一条线为逻辑“1”状态,另一条线就为逻辑“0”,比采用单端不平衡驱动对电压的放大倍数大一倍。差分电路能从地线干扰中拾取有效信号,差分接收器可以分辨200mV以上电位差。若传输过程中混入了干扰和噪声,由于差分放大器的作用,可使干扰和噪声相互抵消。因此可以避免或大大减弱地线干扰和电磁干扰的影响。RS-422A传输速率(90Kbps)时,传输距离可达1200米。
RS-485接口
RS-485是RS-422A的变型:RS-422A用于全双工,而RS-485则用于半双工。RS-485是一种多发送器标准,在通信线路上最多可以使用32对差分驱动器/接收器。如果在一个网络中连接的设备超过32个,还可以使用中继器。
RS-485的信号传输采用两线间的电压来表示逻辑1和逻辑0。由于发送方需要两根传输线,接收方也需要两根传输线。传输线采用差动信道,所以它的干扰抑制性极好,又因为它的阻抗低,无接地问题,所以传输距离可达1200米,传输速率可达1Mbps。
RS-485是一点对多点的通信接口,一般采用双绞线的结构。普通的PC机一般不带RS485接口,因此要使用RS-232C/RS-485转换器。对于单片机可以通过芯片MAX485来完成TTL/RS-485的电平转换。在计算机和单片机组成的RS-485通信系统中,下位机由单片机系统组成,上位机为普通的PC机,负责监视下位机的运行状态,并对其状态信息进行集中处理,以图文方式显示下位机的工作状态以及工业现场被控设备的工作状况。系统中各节点(包括上位机)的识别是通过设置不同的站地址来实现的。
80C51串行口
80C51串行口的结构:(80C52相同)
有两个物理上独立的接收、发送缓冲器SBUF,它们占用同一地址99H;接收器是双缓冲结构;发送缓冲器,因为发送时CPU是主动的,不会产生重叠错误。
80C51串行口的控制寄存器SCON
SCON
是一个特殊功能寄存器,用以设定串行口的工作方式接收/发送控制以及设置状态标志:
位 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | |
---|---|---|---|---|---|---|---|---|---|
字节地址: 98H | SM0 | SM1 | SM2 | REN | TB8 | RB8 | TI | RI | SCON |
SM0
和SM1
为工作方式选择位,可选择四种工作方式:
SM0 | SM1 | 方式 | 说明 | 波特率 |
---|---|---|---|---|
0 | 0 | 0 | 移位寄存器 | fosc/12 |
0 | 1 | 1 | 10位异步收发器(8位数据) | 可变 |
1 | 0 | 2 | 11位异步收发器(9位数据) | fosc/64 或 fosc/32 |
1 | 1 | 3 | 11位异步收发器(9位数据) | 可变 |
通常使用方式1
SM2
,多机通信控制位,主要用于方式2和方式3。
当接收机的SM2=1
时可以利用收到的RB8来控制是否激活RI(RB8=0
时不激活RI
,收到的信息丢弃;RB8=1
时收到的数据进入SBUF
,并激活RI
,进而在中断服务中将数据从SBUF
读走)。当SM2=0
时,不论收到的RB8
为0和1,均可以使收到的数据进入SBUF
,并激活RI
(即此时RB8
不具有控制RI
激活的功能)。通过控制SM2
,可以实现多机通信。
在方式0时,SM2
必须是0。在方式1时,如果SM2=1
,则只有接收到有效停止位时,RI
才置1。
REN
,允许串行接收位。
由软件置REN=1
,则启动串行口接收数据;若软件置REN=0
,则禁止接收。
TB8
,在方式2或方式3中,是发送数据的第九位,可以用软件规定其作用。
可以用作数据的奇偶校验位,或在多机通信中,作为地址帧/数据帧的标志位。在方式0和方式1中,该位未用。
RB8
,在方式2或方式3中,是接收到数据的第九位作为奇偶校验位或地址帧/数据帧的标志位。
在方式1时,若SM2=0
,则RB8
是接收到的停止位。
TI
,发送中断标志位。
在方式0时,当串行发送第8位数据结束时,或在其它方式,串行发送停止位的开始时,由内部硬件使TI
置1,向CPU发中断申请。在中断服务程序中,必须用软件将其清0,取消此中断申请。
RI
,接收中断标志位。
在方式0时,当串行接收第8位数据结束时,或在其它方式,串行接收停止位的中间时,由内部硬件使RI
置1,同CPU发中断申请。也必须在中断服务程序中,用软件将其清0,取消此中断申请。
PCON
中只有一位SMOD
与串行口工作有关
位 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | |
---|---|---|---|---|---|---|---|---|---|
字节地址:97H | SMOD | PCON |
SMOD(PCON.7)
:波特率倍增位。在串行口方式1、方式2、方式3时,波特率与SMOD
有关,当SMOD=1
时,波特率提高一倍。复位时,SMOD=0
。
80C51串行口的工作方式
方式0
方式0时,串行口为同步移位寄存器的输入输出方式。主要用于扩展并行输入或输出口。数据由RXD(P3.0)
引脚输入或输出,同步移位脉冲由TXD(P3.1)
引脚输出。发送和接收均为8位数据,低位在先,高位在后。波特率固定为
f
o
s
c
/
12
fosc/12
fosc/12。
-
方式0输出:
-
方式0输入:
方式1
方式1是10位数据的异步通信口。TXD
为数据发送引脚,RXD
为数据接收引脚,传送一数据的格式如图所示。其中1位起始位,8位数据位,1位停止位。
-
方式1输出:
-
方式1输入:
用软件置
REN
为1时,接收器以所选择波特率的16倍速率采样RXD
引脚电平,检测到RXD
引脚输入电平发生负跳变时则说明起始位有效,将其移入输入移位寄存器,并开始接收这一帧信息的其余位。接收过程中,数据从输入移位寄存器右边移入,起始位移至输入移位寄存器最左边时控制电路进行最后一次移位。当RI0
,且SM2=0
(或接收到的停止位为1)时,将接收到的9位数据的前8位数据装入接收SBUF
,第9位(停止位)进入RB8
,并置RI1
,向CPU请求中断。
方式2和方式3
方式2或方式3时为11位数据的异步通信口。TXD
为数据发送引脚,RXD
为数据接收引脚。
方式2和方式3时起始位1位,数据9位(含1位附加的第9位,发送时为SCON
中的TB8
,接收时为RB8
),停止位1位,一帧数据为11位。方式2的波特率固定为晶振频率的1/64或1/32,方式3的波特率由定时器T1
的溢出率决定。
-
方式2和方式3输出:
发送开始时,先把起始位0输出到
TXD
引脚,然后发送移位寄存器的输出位(D0
)到TXD
引脚。每一个移位脉冲都使输出移位寄存器的各位右移一位,并由TXD
引脚输出。
第一次移位时,停止位“1”移入输出移位寄存器的第9位上,以后每次移位,左边都移入0。当停止位移至输出位时,左边其余位全为0,检测电路检测到这一条件时,使控制电路进行最后一次移位,并置TI=1
,向CPU请求中断。 -
方式2和方式3输入:
接收时,数据从右边移入输入移位寄存器,在起始位0移到最左边时,控制电路进行最后一次移位。当
RI=0
,且SM2=0
(或接收到的第9位数据为1)时,接收到的数据装入接收缓冲器SBUF
和RB8
(接收数据的第9位),置RI=1
,向CPU请求中断。如果条件不满足,则数据丢失,且不置位RI
,继续搜索RXD
引脚的负跳变。
波特率的计算
在串行通信中,收发双方对发送或接收数据的速率要有约定。通过软件可对单片机串行口编程为四种工作方式,其中方式0和方式2的波特率是固定的,而方式1和方式3的波特率是可变的,由定时器T1
的溢出率来决定。
串行口的四种工作方式对应三种波特率。由于输入的移位时钟的来源不同,所以,各种方式的波特率计算公式也不相同。
方式 0 的波特率 = f o s c / 12 方式0的波特率= fosc/12 方式0的波特率=fosc/12
方式 2 的波特率 = ( 2 S M O D / 64 ) ⋅ f o s c 方式2的波特率=(2^{SMOD}/64)·fosc 方式2的波特率=(2SMOD/64)⋅fosc
方式 1 的波特率 = ( 2 S M O D / 32 ) ⋅ ( T 1 溢出率 ) 方式1的波特率=(2^{SMOD}/32)·(T1溢出率) 方式1的波特率=(2SMOD/32)⋅(T1溢出率)
方式 3 的波特率 = ( 2 S M O D / 32 ) ⋅ ( T 1 溢出率 ) 方式3的波特率=(2^{SMOD}/32)·(T1溢出率) 方式3的波特率=(2SMOD/32)⋅(T1溢出率)
当T1
作为波特率发生器时,最典型的用法是使T1
工作在自动再装入的8位定时器方式(即方式2,且TCON
的TR1=1
,以启动定时器)。这时溢出率取决于TH1
中的计数值。
T 1 溢出率 = f o s c 12 × [ 256 − ( T H 1 ) ] T1溢出率=\frac{fosc}{12\times[256 -(TH1)]} T1溢出率=12×[256−(TH1)]fosc
在单片机的应用中,常用的晶振频率为:12MHz和11.0592MHz。所以,选用的波特率也相对固定。常用的串行口波特率以及各参数的关系如表所示。
常用波特率与定时器1的参数关系
串口工作方式及波特率/(b/s) | fosc(MHz) | SMOD | (定时器 T1) C/T | 工作方式 | 初值 |
---|---|---|---|---|---|
(方式 1、3 )62.5 k | 12 | 1 | 0 | 2 | FFH |
19.2 k | 11.0592 | 1 | 0 | 2 | FDH |
9600 | 11.0592 | 0 | 0 | 2 | FDH |
4800 | 11.0592 | 0 | 0 | 2 | FAH |
2400 | 11.0592 | 0 | 0 | 2 | F4H |
1200 | 11.0592 | 0 | 0 | 2 | E8H |
★串口如何使用
串行口工作之前,应对其进行初始化,主要是设置产生波特率的定时器1、串行口控制和中断控制。具体步骤如下:
- 确定**
T1
的工作方式**(编程TMOD
寄存器); - 计算**
T1
的初值**,装载TH1
、TL1
; - 启动
T1
(编程TCON
中的TR1
位); - 确定串行口控制(编程
SCON
寄存器); - 串行口在中断方式工作时,要进行中断设置(编程
IE
、IP
寄存器)。
定时器
T1
用于产生脉冲信号;IE
中断允许寄存器之前有介绍;IP
寄存器是控制优先级的,通常使用默认的,不进行设置。
单片机与单片机的通信
点对点通信
硬件连接
实验中使用CH340芯片进行USB/串口转换,进行PC和单片机之间的通信,所以没有使用MAX232芯片。
HC6800-ES V2.0 开发板上没有配置9针非标准接口和MAX232芯片,想要进行单片机之间点对点通信需要自己搭建电路。
多机通信
硬件连接
单片机构成的多机系统常采用总线型主从式结构。所谓主从式,即在数个单片机中,有一个是主机,其余的是从机,从机要服从主机的调度、支配。80C51单片机的串行口方式2和方式3适于这种主从式的通信结构。当然采用不同的通信标准时,还需进行相应的电平转换,有时还要对信号进行光电隔离。在实际的多机应用系统中,常采用RS-485串行标准总线进行数据传输。
串口通信(编程)
#include <reg52.h>
typedef unsigned int u16;
typedef unsigned char u8;
//波特率4800
void UsartInit(){
TMOD|=0x20; //TMOD = 0010 0000;
TH1=0xf3;
TL1=0xf3;
PCON=0x80;
TR1=1;
SCON=0x50;
EA=1;
ES=1;
}
void main() {
UsartInit();
while(1);
}
void usart() interrupt 4{
u8 receiveData;
receiveData=SBUF; //保存数据
RI=0;
SBUF=receiveData;
while(!TI);
TI=0;
}
EEPROM(IIC总线)
串行总线的组成及工作原理
采用串行总线技术可以使系统的硬件设计大大简化系统的体积减小、可靠性提高。同时,系统的更改和扩充极为容易。
常用的串行扩展总线有:IIC(Inter IC BUS)总线、单总线(1-WIRE BUS)、SPI(Serial Peripheral Interface)总线及Microwire/PLUS等。这里我们仅讨论IIC串行总线。
IIC总线
IIC串行总线概述
IIC总线是PHLIPS公司推出的一种串行总线,是具备多主机系统所需的包括总线裁决和高低速器件同步功能的高性能串行总线。
IIC总线只有两根双向信号线。一根是数据线SDA,另根是时钟线SCL。
IIC总线通过上拉电阻接正电源。当总线空闲时,两根线均为高电平。连到总线上的任一器件输出的低电平,都将使总线的信号变低,即各器件的SDA及SCL都是线“与”关系。
每个接到IIC总线上的器件都有唯一的地址。主机与其它器件间的数据传送可以是由主机发送数据到其它器件,这时主机即为发送器。由总线上接收数据的器件则为接收器。
在多主机系统中,可能同时有几个主机企图启动总线传送数据。为了避免混乱,IIC总线要通过总线仲裁,以决定由哪一台主机控制总线。
在80C51单片机应用系统的串行总线扩展中我们经常遇到的是以80C51单片机为主机,其它接口器件为从机的单主机情况。
IIC总线的数据传送
数据位的有效性规定
IIC总线进行数据传送时,时钟信号为高电平期间,数据线上的数据必须保持稳定,只有在时钟线上的信号为低电平期间,数据线上的高电平或低电平状态才允许变化。
起始和终止信号
SCL线为高电平期间,SDA线由高电平向低电平的变化表示起始信号;SCL线为高电平期间,SDA线由低电平向高电平的变化表示终止信号。
起始和终止信号都是由主机发出的,在起始信号产生后,总线就处于被占用的状态;在终止信号产生后,总线就处于空闲状态。
连接到IIC总线上的器件,若具有IIC总线的硬件接口,则很容易检测到起始和终止信号。
接收器件收到一个完整的数据字节后,有可能需要完成些其它工作,如处理内部中断服务等,可能无法立刻接收下一个字节,这时接收器件可以将SCL线拉成低电平,从而使主机处于等待状态。直到接收器件准备好接收下一个字节时,再释放SCL线使之为高电平,从而使数据传送可以继续进行。
数据传送格式
(1)字节传送与应答
每一个字节必须保证是8位长度。数据传送时,先传送最高位(MSB),每一个被传送的字节后面都必须跟随一位应答位(即一帧共有9位)。
注意:由之前的串行总线连接图可以看出,此处主机和从机的SDA和SCL线是连在一起的,所以主机和从机发出的信号是在同一条线上的。也就是说在一条SDA线上建立连接之后(主机寻址且得到从机应答),数据传输就像主机和从机在SDA线上“聊天”一样:主机发送数据,然后从机回答收到,如此重复。
由于某种原因从机不对主机寻址信号应答时(如从机正在进行实时性的处理工作而无法接收总线上的数据),它必须将数据线置于高电平,而由主机产生一个终止信号以结束总线的数据传送。
主机:从机有时间吗?(发出起始信号,主机寻址)
从机:忙着呢,没时间。(不应答:高电平)
主机:行吧,拜拜。(产生终止信号结束传送)
如果从机对主机进行了应答,但在数据传送一段时间后无法继续接收更多的数据时,从机可以通过对无法接收的第一个数据字节的“非应答”通知主机,主机则应发出终止信号以结束数据的继续传送。
进行了应答(开启了聊天),但是无法继续接收数据(聊不下去了)
主机:巴拉巴拉…(此时从机已经聊不下去了)
从机:…从机不想聊了,选择沉默。(不应答:高电平)
主机:不说话算了,拜拜。(产生终止信号结束传送)
当主机接收数据时,它收到最后一个数据字节后,必须向从机发出一个结束传送的信号。这个信号是由从机的“非应答”来实现的。然后,从机释放SDA线,以允许主机产生终止信号。
主机接收从机发来的数据时也需要进行应答,这里主机接收数据完成后发送的结束传送信号应该就是对从机发来的数据进行的不应答来完成的。
从下文的数据帧格式中可以得到详细的体现。
(2)数据帧格式
IIC总线上传送的数据信号是广义的,既包括地址信号,又包括真正的数据信号。
在起始信号后必须传送一个从机的地址(7位),第8位是数据的传送方向位(R/T)用“0”表示主机发送数据(T),“1”表示主机接收数据(R)。每次数据传送总是由主机产生的终止信号结束。但是若主机希望继续占用总线进行新的数据传送,则可以不产生终止信号,马上再次发出起始信号对另一从机进行寻址。
在总线的一次数据传送过程中,可以有以下几种组合方式:
-
主机向从机发送数据,数据传送方向在整个传送过程中不变:
注:有阴影部分表示数据由主机向从机传送,无阴影部分则表示数据由从机向主机传送。
A表示应答,A非表示非应答(高电平)。S表示起始信号,P表示终止信号。 -
主机在第一个字节后,立即从从机读数据
-
在传送过程中,当需要改变传送方向时,起始信号和从机地址都被重复产生一次,但两次读/写方向位正好反相。
总线的寻址
IIC总线协议有明确的规定:采用7位的寻址字节(寻址字节是起始信号后的第一个字节)。
(1)寻址字节的位定义
D7~D1位组成从机的地址。D0(R/W)位是数据传送方向位,为“0”时表示主机向从机写数据,为“1”时表示主机由从机读数据。
主机发送地址时,总线上的每个从机都将这7位地址码与自己的地址进行比较,如果相同,则认为自己正被主机寻址,根据R/T(R/W)位将自己确定为发送器或接收器。
从机的地址由固定部分和可编程部分组成。在一个系统中可能希望接入多个相同的从机从机地址中可编程部分决定了可接入总线该类器件的最大数目。如一个从机的7位寻址位有4位是固定位,3位是可编程位,这时仅能寻址8个同样的器件,即可以有8个同样的器件接入到该IIC总线系统中。
80C51单片机IC串行总线器件的接口
总线数据传送的模拟
主机可以采用不带IIC总线接口的单片机,如80C51、AT89C2051等单片机,利用软件实现IIC总线的数据传送,即软件与硬件结合的信号模拟。
51单片机没有带IIC总线的接口
一、典型信号模拟
为了保证数据传送的可靠性,标准的IIC总线的数据传送有严格的时序要求。IIC总线的起始信号、终止信号、发送“0”及发送“1”的模拟时序:
由上图中的标注可知,IIC总线的起始信号、终止信号、发送“0”及发送“1”的高低电平的持续时间是有要求的,在编写程序模拟的时候需要进行延时来满足信号持续时间的要求。
二、典型信号模拟子程序
/**起始信号**/
Void 12CStart(void){
SDA=1;
SomeNop( );//延时
SCL=1;
SomeNop( );
SDA =0;
SomeNop( );
}
/**终止信号**/
Void 12CStop(void){
SDA=0;
SomeNop( );//延时
SCL=1;
SomeNop( );
SDA =1;
SomeNop( );
}
IIC总线器件的扩展
一、扩展电路
串行E2PROM的扩展
(1)串行E2PROM典型产品
ATMEL公司的AT24C系列:
-
AT24C01:128字节(128x8位)
-
AT24C02:256字节(256x8位)(开发板使用的就是这个)
-
AT24C04:512字节(512x8位)
-
AT24C08:1K字节(1Kx8位)
-
AT24C16:2K字节(2Kx8位)
(2)写入过程
AT24C系列EEPROM芯片地址的固定部分为1010,A2、A1、A0引脚接高、低电平后得到确定的3位编码(默认全部接地,即000)。形成的7位编码即为该器件的地址码。(还有第8位是选择传输方向的位)
单片机进行写操作时,首先发送该器件的7位地址码和写方向位“0”(共8位,即一个字节),发送完后释放SDA线并在SCL线上产生第9个时钟信号。被选中的存储器器件在确认是自己的地址后,在SDA线上产生一个应答信号作为响应,单片机收到应答后就可以传送数据了。
传送数据时,单片机首先发送一个字节的被写入器件的存储区的首地址,收到存储器器件的应答后,单片机就逐个发送各数据字节,但每发送一个字节后都要等待应答。(发一答一)
咋知道的被写入器件存储区的首地址的?
AT24C系列器件片内地址在接收到每一个数据字节地址后自动加1,在芯片的“一次装载字节数”(不同芯片字节数不同)限度内,只需输入首地址。装载字节数超过芯片的“一次装载字节数”时,数据地址将“上卷”(从首地址重新装载),前面的数据将被覆盖。
当要写入的数据传送完后,单片机应发出终止信号以结束写入操作。写入n个字节的数据格式:
(3)读出过程
单片机先发送该器件的7位地址码和写方位“0”(“伪写”,因为只是为了确定器件,真正的目的是为了后面的读操作),发送完后释放SDA线并在SCL线上产生第9个时钟信号。被选中的存储器器件在确认是自己的地址后,在SDA线上产生一个应答信号作为回应。
然后,再发一个字节的要读出器件的存储区的首地址,收到应答后,单片机要重复一次起始信号并发出器件地址和读方向位(“1”),收到器件应答后就可以读出数据字节,每读出一个字节,单片机都要回复应答信号。当最后一个字节数据读完后,单片机应返回以“非应答”(高电平),并发出终止信号以结束读出操作。
移位操作
-
左移时最低位补0,最高位移入PSW的CY位
-
右移时最高位保持原数,最低位移除。
PSW是啥?
AT24CXX存储器工作原理
特点
- 与400KHz IIC总线兼容
- 1.8到6.0伏工作电压范围
- 低功耗CMOS技术
- 写保护功能当WP为高电平时进入写保护状态
- 页写缓冲器
- 自定时擦写周期
- 100万次编程/擦除周期
- 可保存数据100年
- 8脚DIP SOIC或TSSOP封装
- 温度范围商业级和工业级
概述
CAT24WC01/02/04/08/16是一个1K/2K/4K/8K/16K位串行CMOS,EEPROM内部含有128/256/512/1024/2048个8位字节,CATALYST公司的先进CMOS技术实质上减少了器件的功耗,CAT24WC01有一个8字节页写缓冲器。CAT24WC02/04/08/16有一个16字节页写缓冲器,该器件通过IIC总线接口进行操作有一个专门的写保护功能。
引脚说明:
总线时序:
读写周期范围:
这几张图要结合起来看,便于编程。其中一些上文已经提到过。
开发板上EEPROM原理图:
AT24C02驱动程序编写(编程)
创建多文件工程模块化编程
main.c文件
/*
实现使用4个独立按键使单片机与24C02进行读写
并在数码管上进行显示
k1:保存数据
k2:读出数据
k3:数据+1
k4:数据归零(不是归零已保存的数据)
*/
#include <reg52.h>
#include "i2c.h"
typedef unsigned int u16;
typedef unsigned char u8;
//定义四个独立按键
sbit k1=P3^1;
sbit k2=P3^0;
sbit k3=P3^2;
sbit k4=P3^3;
//定义3-8译码器的三个位
sbit LSA=P2^2;
sbit LSB=P2^3;
sbit LSC=P2^4;
//数码管段选(0-9),code是c语言的关键字,将数组保存在快存中(下方的code区)
u8 code smgduan[10]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};
u8 num=0,disp[4];
//延时函数
void delay(u16 i) {
while(i--);
}
void keypros(){
if(k1==0){
delay(1000);
if(k1==0){
at24c02Write(1,num);
}
while(!k1);
}
if(k2==0){
delay(1000);
if(k2==0){
num=at24c02Read(1);
}
while(!k2);
}
if(k3==0){
delay(1000);
if(k3==0){
num++;
if(num>255){
num=0;
}
}
while(!k3);
}
if(k4==0){
delay(1000);
if(k4==0){
num=0;
}
while(!k4);
}
}
void datapros(){
disp[0]=smgduan[num/1000];
disp[1]=smgduan[num%1000/100];
disp[2]=smgduan[num%1000%100/10];
disp[3]=smgduan[num%1000%100%10];
}
void digDisplay(){
u8 i;
for(i=0;i<4;i++){
switch(i){
case 0:
LSA=0;LSB=0;LSC=0;break;
case 1:
LSA=1;LSB=0;LSC=0;break;
case 2:
LSA=0;LSB=1;LSC=0;break;
case 3:
LSA=1;LSB=1;LSC=0;break;
}
P0=disp[3-i];
delay(100);
P0=0x00; //消影
}
}
void main() {
while(1){
keypros();
datapros();
digDisplay();
}
}
i2c.h文件
#ifndef _I2C_H
#define _I2C_H
#include <reg52.h>
sbit SCL=P2^1;
sbit SDA=P2^0;
void at24c02Write(unsigned char addr, unsigned char dat);
unsigned char at24c02Read(unsigned char addr);
#endif
i2c.c文件
#include "i2c.h"
//程序生成的10us的延时函数,暂时不知道怎么算的
void delay10us(){
unsigned char a,b;
for(b=1;b>0;b--){
for(a=2;a>0;a--){
}
}
}
/*
开发板上没有IIC的硬件接口,因此使用软件模拟信号
*/
//起始信号(根据时序图)
void i2cStart(){
SDA=1;
delay10us();
SCL=1;
delay10us();
SDA=0;
delay10us();
SCL=0; //好像是需要这一步让下一次数据可以改变,SCL=0时SDA才能改变
delay10us();
}
//停止信号(根据时序图)
void i2cStop(){
SDA=0;
delay10us();
SCL=1;
delay10us();
SDA=1;
delay10us();
}
/*
** 发送字节函数
成功与否取决于应答时间
成功返回1,失败返回0;
**
*/
unsigned char i2cSendByte(unsigned char dat){
unsigned char a=0,b;
for(a=0;a<8;a++){
SDA=dat>>7; //dat是8bit,右移7位之后只剩最高位,赋值给SDA
dat=dat<<1; //左移一位去掉最高位
//delay10us(); //大于4us
SCL=1;
delay10us(); //高电平数据保持稳定
SCL=0;
delay10us(); //低电平使数据SDA可以变化
}
SDA=1; //释放SDA线等待应答
delay10us();
SCL=1;
while(SDA){ //收到应答(低电平)则跳过循环执行下面的代码
b++;
if(b>200){ //至少超过2000us,就认为没有产生应答
SCL=0;
delay10us();
return 0;
}
}
SCL=0;
delay10us();
return 1;
}
/*
读取字节函数
要先移位再接收数据(或运算),否则dat接收的数据的最高位会被移走
先移位则是开始时将没有数据的dat中的最高位移走(就不会影响数据接收)
*/
unsigned char i2cReadByte(){
unsigned char a=0,dat=0;
SDA=1;
delay10us();
for(a=0;a<8;a++){
SCL=1; //此时进入数据保持状态
delay10us();
dat<<=1; //将dat左移一位准备好接收下一位的数据
dat|=SDA; //将dat的最低位和SDA进行或运算(即把SDA数据写入dat最低位)
delay10us();
SCL=0; //此时进入数据可变状态,SDA内的数据会进行刷新。
delay10us();
}
return dat;
}
/*
对at24c02进行读写
*/
void at24c02Write(unsigned char addr, unsigned char dat){
i2cStart();
i2cSendByte(0xa0);
i2cSendByte(addr);
i2cSendByte(dat);
i2cStop();
}
unsigned char at24c02Read(unsigned char addr){
unsigned char num;
i2cStart();
i2cSendByte(0xa0);
i2cSendByte(addr);
i2cStart();
i2cSendByte(0xa1);
num = i2cReadByte();
i2cStop();
return num;
}
DS18B20温度传感器
DS18B20温度传感器工作原理
DS18B20简介
DS18B20数字温度传感器接线方便,封装后可应用于多种场合,如管道式,螺纹式,磁铁吸附式,不锈钢封装式。主要根据应用场合的不同而改变其外观。封装后的DS18B20可用于电缆沟测温,高炉水循环测温,锅炉测温,机房测温,农业大棚测温洁净室测温,弹药库测温等各种非极限温度场合。耐磨耐碰,体积小,使用方便,封装形式多样,适用于各种狭小空间设备数字测温和控制领域。
DS18B20特点
- 适应电压范围更宽,电压范围:3.0~5.5 V,在寄生电源方式下可由数据线供电。
- 独特的单线接口方式,DS18B20在与微处理器连接时仅需要一条口线即可实现微处理器与DS18B20的双向通讯。
- DS18B20支持多点组网功能,多个DS18B20可以并联在唯一的三线上,实现组网多点测温。
- DS18B20在使用中不需要任何外围元件,全部传感元件及转换电路集成在形如一只三极管的集成电路内。
- 温范围-55℃~+125℃,在-10~+85℃时精度为±0.5℃
- 可编程的分辨率为9~12位,对应的可分辨温度分别为0.5℃、0.25℃、0.125℃和0.0625℃,可实现高精度测温。
- 在9位分辨率时最多在93.75 ms内把温度转换为数字,12位分辨率时最多在750 ms内把温度值转换为数字,速度更快。
- 测量结果直接输出数字温度信号,以“一根总线”串行传送给CPU,同时可传送CRC校验码,具有极强的抗干扰纠错能力。
- 负压特性:电源极性接反时,芯片不会因发热而烧毁,但不能正常工作。
DS18B20实物图
DS18B20内部结构
- 64位(激)光刻只读存储器
光刻ROM中的64位序列号是出厂前被光刻好的,它可以看作是该DS18B20的地址序列号。64位光刻ROM的排列是:开始8位(28H)是产品类型标号,接着的48位是该DS18B20自身的序列号,最后8位是前面56位的循环冗余校验码。光刻ROM的作用是使每一个DS18B20都各不相同,这样就可以实现一根总线上挂接多个DS18B20的目的。
- DS18B20温度转换规则
DS18B20的核心功能是它可以直接读出数字的温度数值。温度传感器的精度为用户可编程的9,10,11或12位,分别以0.5℃,0.25℃,0.125℃和0.0625℃增量递增。在上电状态下默认的精度为12位。
DS18B20启动后保持低功耗等待状态,当需要执行温度测量和AD转换时,总线控制器必须发出[44h]命令。转换完以后,产生的温度数据以两个字节的形式被存储到高速暂存器的温度寄存器中,DS18B20继续保持等待状态。
这是12位转化后得到的12位数据,存储在DS18B20的两个8位的RAM中,高字节的前5位是符号位,如果测得的温度大于0,这5位为“0”,只要将测到的数值乘以0.0625即可得到实际温度;如果温度小于0,这5位为“1”测到的数值需要取反加1先减1取反再乘以0.0625即可得到实际温度。
这里是补码反码的知识…了解一下
上面的温度寄存器中的数据和下面的输出数据不一样。
温度寄存器中的数据需要按上文描写计算才能得到实际温度,下面的输出数据是已经经过转换后的实际温度(后11位即为实际温度的二进制数)
温度/数据关系:
温度℃ | 数据输出(二进制) | 数据输出(十六进制) |
---|---|---|
+125 | 0000 0111 1101 0000 | 07D0 h |
+85 | 0000 0101 0101 0000 | 0550 h |
+25.0625 | 0000 0001 1001 0001 | 0191 h |
+10.125 | 0000 0000 1010 0010 | 00A2 h |
+0.5 | 0000 0000 0000 1000 | 0008 h |
0 | 0000 0000 0000 0000 | 0000 h |
-0.5 | 1111 1111 1111 1000 | FFF8 h |
-10.125 | 1111 1111 0101 1110 | FF5E h |
-25.0625 | 1111 1110 0110 1111 | FE6E h |
-55 | 1111 1100 1001 0000 | FC90 h |
※上电复位时温度寄存器默认值为+85℃
- DS18B20温度传感器的存储器
DS18B20温度传感器的内部存储器包括一个高速的暂存器RAM和一个非易失性的可电擦除的EEPROM,后者存放高温度和低温度触发器TH、TL和结构寄存器。
- 配置寄存器
存储器的第4位为配置寄存器,其组织见图8,用户可按表3所示设置R0和R1位来设定DS18B20的精度。上电默认设置:R0=1、R1=1(12位精度)。注意:精度和转换时间之间有直接的关系。暂存器的位7和位0-4被器件保留,禁止写入。
温度计精确度配置:
R1 | R0 | 精度 | 最大转换时间 |
---|---|---|---|
0 | 0 | 9 - bit | 93.75 ms ( t C O N V / 8 t_{CONV}/8 tCONV/8) |
0 | 1 | 10 - bit | 187.5 ms ( t C O N V / 4 t_{CONV}/4 tCONV/4) |
1 | 0 | 11 - bit | 375 ms ( t C O N V / 2 t_{CONV}/2 tCONV/2) |
1 | 1 | 12 - bit | 750 ms ( t C O N V t_{CONV} tCONV) |
DS18B20的ROM指令表
指令 | 约定代码 | 功能 |
---|---|---|
读ROM | 33H | 读DS1820温度传感器ROM中的编码(即64位地址) |
符合 ROM | 55H | 发出此命令之后,接着发出64位ROM编码,访问单总线上与该编码相对应的DS1820使之作出响应,为下一步对该DS1820的读写做准备。 |
搜索 ROM | 0FOH | 用于确定挂接在同一总线上DS1820的个数和识别64位ROM地址。为操作各器件作好准备。 |
跳过 ROM | 0CCH | 忽略64位ROM地址,直接向DS1820发温度变换命令。适用于单片工作。 |
告警搜索命令 | 0ECH | 执行后只有温度超过设定值上限或下限的片子才做出响应。 |
DS18B20的RAM指令表
指令 | 约定代码 | 功能 |
---|---|---|
温度变换 | 44H | 启动 DS1820 进行温度转换,12 位转换最长 750ms(9 位为 93.75ms),结果存入内部 9 字节 RAM中。 |
读暂存器 | 0BEH | 读取内部 RAM 中 9 字节内容。 |
写暂存器 | 4EH | 向内部 RAM 的 3、4 字节写入上下限温度数据命令,紧跟该命令后,传送两字节的数据。 |
复制暂存器 | 48H | 将 RAM 中第 3、4 字节内容复制到 EEPROM。 |
重调 EEPROM | 0B8H | 将 EEPROM 中内容恢复到 RAM 的第 3、4 字节。 |
读供电方式 | 0B4H | 读取 DS1820 供电模式:寄生供电时DS1820 返回 “0”,外接电源供电DS1820 发送 “1”。 |
DS18B20初始化
- 数据线拉到低电平“0”。
- 延时480微妙(该时间的时间范围可以从480到960微秒)。
- 数据线拉到高电平“1”。
- 延时等待80微秒。如果初始化成功则在15到60微妙时间内产生一个由DS18B20所返回的低电平“0”,根据该状态可以来确定它的存在,但是应注意不能无限的进行等待,不然会使程序进入死循环,所以要进行超时判断。
- 若CPU读到了数据线上的低电平“0”后,还要做延时,其延时的时间从发出的高电平算起(第(3)步的时间算起)最少要480微秒。
注意结合图中下方的图例来看时序图。
DS18B20读时序
- 将数据线拉低“0”。
- 延时1微秒。
- 将数据线拉高“1”,释放总线准备读数据。
- 延时10微秒。
- 读数据线的状态得到1个状态位,并进行数据处理。
- 延时45微秒。
- 重复1~7步骤,直到读完一个字节。
DS18B20写时序
- 数据线先置低电平“0”。
- 延时15微秒。
- 按从低位到高位的顺序发送数据(一次只发送一位)。
- 延时60微秒。
- 将数据线拉到高电平。
- 重复1~5步骤,直到发送完整的字节。
- 最后将数据线拉高。
温度传感器原理图(开发板)
DS18B20温度传感器(编程)
main.c文件:
/*
实现数码管显示环境温度
*/
#include <reg52.h>
#include "temperature.h"
typedef unsigned int u16;
typedef unsigned char u8;
//定义3-8译码器的三个位
sbit LSA=P2^2;
sbit LSB=P2^3;
sbit LSC=P2^4;
//数码管段选(0-9),code是c语言的关键字,将数组保存在快存中(下方的code区)
u8 code smgduan[10]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};
u8 num=0,displayData[8];
//延时函数
void delay(u16 i) {
while(i--);
}
void datapros(int temp){
float tp;
if(temp<0){
displayData[0]=0x40;
temp=temp-1;
temp=~temp;
tp=temp;
temp=tp*0.0625*100+0.5; //数据放大100倍,浮点会被强制转换为整型,低位是小数
}else{
displayData[0]=0x00;
tp=temp;
temp=tp*0.0625*100+0.5;
}
displayData[1]=smgduan[temp/10000];
displayData[2]=smgduan[temp%10000/1000];
displayData[3]=smgduan[temp%1000/100]|0x80;
displayData[4]=smgduan[temp%100/10];
displayData[5]=smgduan[temp/10];
}
void digDisplay(){
u8 i;
for(i=0;i<6;i++){
switch(i){
case 0:
LSA=0;LSB=0;LSC=0;break;
case 1:
LSA=1;LSB=0;LSC=0;break;
case 2:
LSA=0;LSB=1;LSC=0;break;
case 3:
LSA=1;LSB=1;LSC=0;break;
case 4:
LSA=0;LSB=0;LSC=1;break;
case 5:
LSA=1;LSB=0;LSC=1;break;
}
P0=displayData[5-i];
delay(100);
P0=0x00; //消影
}
}
void main() {
while(1){
datapros(ds18b20ReadTemp());
digDisplay();
}
}
temperature.c文件:
#include "temperature.h"
void delay1ms(uint y){
uint x;
for(;y>0;y--){
for(x=110;x>0;x--);
}
}
//返回:1表示成功,0表示失败
uchar ds18b20Init(){
uchar i=0;
DSPORT=0;
i=70;
while(i--); //642us用仿真测试
DSPORT=1;
i=0;
while(DSPORT){
delay1ms(1);
i++;
if(i>5){
return 0;
}
}
return 1;
}
void ds18b20WriteByte(uchar dat){
uchar i,j;
for(i=0;i<8;i++){
DSPORT=0;
j++; //延时
DSPORT=dat&0x01;
j=6;
while(j--); //延时68us
DSPORT=1;
dat=dat>>1;
}
}
uchar ds18b20ReadByte(){
uint i,j;
uchar bi,byte;
for(i=0;i<8;i++){
DSPORT=0;
j++;
DSPORT=1;
j++;
j++;
bi=DSPORT;
byte=(byte>>1)|(bi<<7);
j=4;
while(j--);
}
return byte;
}
void ds18b20ChangeTemp(){
ds18b20Init();
delay1ms(1);
ds18b20WriteByte(0xcc);
ds18b20WriteByte(0x44);
}
void ds18b20ReadTempCom(){
ds18b20Init();
delay1ms(1);
ds18b20WriteByte(0xcc);
ds18b20WriteByte(0xbe);
}
int ds18b20ReadTemp(){
int temp;
uchar tmh,tml;
ds18b20ChangeTemp();
ds18b20ReadTempCom();
tml=ds18b20ReadByte();
tmh=ds18b20ReadByte();
temp=tmh;
temp<<=8;
temp|=tml;
return temp;
}
temperature.h文件:
#ifndef _temp_H
#define _temp_H
#include <reg52.h>
#ifndef uchar
#define uchar unsigned char
#endif
#ifndef uint
#define uint unsigned int
#endif
sbit DSPORT=P3^7;
int ds18b20ReadTemp();
#endif
DS1302时钟
DS1302时钟工作原理
DS1302的主要性能指标
- DS1302实时时钟具有能计算2100年之前的秒分、时、日、日期、星期、月、年的能力,还有闰年调整的能力。
- 内部含有31个字节静态RAM,可提供用户访问。
- 采用串行数据传送方式,使得管脚数量最少,简单SPI3线接口。
- 工作电压范围宽:2.0~5.5V。
- 工作电流:2.0V时,小于300nA。
- 时钟或RAM数据的读/写有两种传送方式:单字节传送和多字节传送方式。
- 采用8脚DIP封装或SOIC封装。
- 与TTL兼容,Vcc=5V。
- 可选工业级温度范围:-40℃C~+85℃。
- 具有涓流充电能力。
- 采用主电源和备份电源双电源供应。
- 备份电源可由电池或大容量电容实现。
SPI总线概念
- SPI接日的全称是“SerialPeripheral Interface”,意为串行外围接口。
- SPI接口主要应用在EEPROM,FLASH,实时时钟,AD转换器,还有数字信号处理器和数字信号解码器之间。
- SPI接口是在CPU和外围低速器件之间进行同步串行数据传输,在主器件的移位脉冲下,数据按位传输高位在前,地位在后,为全双工通信,数据传输速度总体来说比IIC总线要快,速度可达到几Mbps。
- SPI接口是以主从方式工作的,这种模式通常有一个主器件和一个或多个从器件,其接口包括以下四种信号:
- MOSI—主器件数据输出,从器件数据输入
- MISO—主器件数据输入,从器件数据输出
- SCLK—时钟信号,由主器件产生
- /CS—从器件使能信号,由主器件控制
DS1302采用的是3线通信。51单片机没有SPI总线的接口,需要使用软件模拟信号。
SPI接口内部硬件图示
最后,SPI接口的一个缺点:没有指定的流控制,没有应答机制确认是否接收到数据。
引脚功能
DS1302的引脚如图所示:
X1
、X2
:32.768KHz晶振接入引脚。GND
:地。RST
:复位引脚,低电平有效,操作时高电平。I/O
:数据输入/输出引脚,具有三态功能。SCLK
:串行时钟输入引脚。Vcc1
:工作电源引脚。Vcc2
:备用电源引脚。接入电池断电时提供1302电源
DS1302的寄存器及片内RAM
DS1302有一个控制寄存器、12个日历、时钟寄存器和31个RAM。可读写
- 控制寄存器
控制寄存器用于存放DS1302的控制命令字,DS1302的RST
引脚回到高电平后写入的第一个字就为控制命令,它用于对DS1302读写过程进行控制,它的格式如下:
D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 |
---|---|---|---|---|---|---|---|
1 | RAM/CK | A4 | A3 | A2 | A1 | A0 | RD/W |
- D7:固定为1
- D6:RAM/CK位,
=1
片内RAM,=0
日历、时钟寄存器选择位。 - D5~D1:地址位,用于选择进行读写的日历、时钟寄存器或片内RAM。对日历、时钟寄存器或片内RAM的选择见表。
- D0:读写选择,
=0
写,=1
读。
选择表:
寄存器名称 | D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 |
---|---|---|---|---|---|---|---|---|
1 | RAM/CK | A4 | A3 | A2 | A1 | A0 | R/W | |
秒寄存器 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0或1 |
分寄存器 | 1 | 0 | 0 | 0 | 0 | 0 | 1 | 0或1 |
小时寄存器 | 1 | 0 | 0 | 0 | 0 | 1 | 0 | 0或1 |
日寄存器 | 1 | 0 | 0 | 0 | 0 | 1 | 1 | 0或1 |
月寄存器 | 1 | 0 | 0 | 0 | 1 | 0 | 0 | 0或1 |
星期寄存器 | 1 | 0 | 0 | 0 | 1 | 0 | 1 | 0或1 |
年寄存器 | 1 | 0 | 0 | 0 | 1 | 1 | 0 | 0或1 |
寄存器名称 | 1 | 0 | 0 | 0 | 1 | 1 | 1 | 0或1 |
写保护寄存器 | 1 | 0 | 0 | 0 | 1 | 1 | 1 | 0或1 |
慢充电寄存器 | 1 | 0 | 0 | 1 | 0 | 0 | 0 | 0或1 |
时钟突发模式 | 1 | 0 | 1 | 1 | 1 | 1 | 1 | 0或1 |
RAM0 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0或1 |
… | 1 | 1 | … | … | … | … | … | 0或1 |
RAM30 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0或1 |
RAM突发模式 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0或1 |
- 日历、时钟寄存器
DS1302共有12个寄存器,其中有7个与日历、时钟相关,存放的数据为BCD码形式。日历、时钟寄存器的格式如表。
寄存器名称 | 取值范围 | D7 | D6 | D5 | D4 | D3、D2、D1、D0 |
---|---|---|---|---|---|---|
秒寄存器 | 00~59 | CH | 秒的十位 | ← | ← | 秒的个位 |
分寄存器 | 00~59 | 0 | 分的十位 | ← | ← | 分的个位 |
小时寄存器 | 01~12或00~23 | 12/24 | 0 | A/P | HR | 小时的个位 |
日寄存器 | 01~31 | 0 | 0 | 日的十位 | ← | 日的个位 |
月寄存器 | 01~12 | 0 | 0 | 0 | 1或0 | 月的个位 |
星期寄存器 | 01~07 | 0 | 0 | 0 | 0 | 星期几 |
年寄存器 | 01~99 | 年的十位 | ← | ← | ← | 年的个位 |
写保护寄存器 | WP | 0 | 0 | 0 | 0 | |
慢充电寄存器 | TCS | TCS | TCS | TCS | 前2:DS | 后2:RS | |
时钟突发寄存器 | \ | \ | \ | \ | \ | \ |
数据都以BCD码形式。
BCD码是通过4位二进制码来表示1位十进制中的0~9这10个数码。二进制码转换为BCD码的方式为:4位二进制码大于1001时,加6。
如:BCD码00001100的二进制码为:00001100 ( 12 ) 10 (12)_{10} (12)10+6=0001 0010 即用4位二进制单独表示十进制的每一位数
十进制 BCD 十进制 BCD 0 0000 5 0101 1 0001 6 0110 2 0010 7 0111 3 0011 8 1000 4 0100 9 1001 小时寄存器的D7位为12小时制/24小时制的选择位,当为1时选12小时制,当为0时选24小时制。当12小时制时,D5位为1是上午,D5位为0是下午,D4为小时的十位。当24小时制时,D5、D4位为小时的十位。
秒寄存器中的CH位为时钟暂停位,当为1时钟暂停,为0时钟开始启动。
写保护寄存器中的WP为写保护位,当WP=1,写保护,当WP=0未写保护,当对日历、时钟寄存器或片内RAM进行写时WP应清零,当对日历、时钟寄存器或片内RAM进行读时WP一般置1。
慢充电寄存器的TCS位为控制慢充电的选择当它为1010才能使慢充电工作。DS为二极管选择位:DS为01选择一个二极管,DS为10选择二个二极管,DS为11或00充电器被禁止,与TCS无关。RS用于选择连接在Vcc2与Vcc1之间的电阻,RS为00,充电器被禁止,与TCS无关,电阻选择情况见表。
RS位 电阻器 阻值 00 无 无 01 R1 2K 10 R2 4K 11 R3 8K
- 片内RAM
DS1302片内有31个RAM单元,对片内RAM的操作有两种方式:单字节方式和多字节方式。当控制命今字为COH~FDH时为单字节读写方式,命令字中的D5~D1用于选择对应的RAM单元,其中奇数为读操作,偶数为写操作。
当控制命令字为FEH、FFH时为多字节操作(表中的RAM突发模式),多字节操作可一次把所有的RAM单元内容进行读写。FEH为写操作,FFH为读操作。
数据输入输出(I/O)
在控制指令字输入后的下一个SCLK时钟的上升沿时,数据被写入DS1302,数据输入从低位即位0开始。同样,在紧跟8位的控制指令字后的下一个SCLK脉冲的下降沿读出DS1302的数据,读出数据时从低位0位到高位7。
单字节读写:
DS1302是通过SPI串行总线跟单片机通信的,当进行一次读写操作时最少得读写两个字节,第一个字节是控制字节,就是一个命令,告诉DS1302是读还是写操作,是对RAM还是对CLOK寄存器操作。第二个字节就是要读或写的数据了。
单字节读写:只有在SCLK为低电平时,才能将CE置为高电平。所以在进行操作之前先将SCLK置低电平,然后将CE置为高电平,接着开始在IO上面放入要传送的电平信号,然后跳变SCLK。数据在SCLK上升沿时,DS1302读写数据,在SCLK下降沿时,DS1302放置数据到IO上。
DS1302原理图(开发板)
DS1302时钟(编程)
main.c文件
#include <reg52.h>
#include "ds1302.h"
typedef unsigned int u16;
typedef unsigned char u8;
sbit LSA=P2^2;
sbit LSB=P2^3;
sbit LSC=P2^4;
char num=0;
u8 displayData[8];
u8 code smgduan[10]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};
//延时大约10us
void delay(u16 i){
while(i--);
}
void datapros(){
ds1302ReadTime();
displayData[0]=smgduan[TIME[2]/16];
displayData[1]=smgduan[TIME[2]&0x0f];
displayData[2]=0x40;
displayData[3]=smgduan[TIME[1]/16];
displayData[4]=smgduan[TIME[1]&0x0f];
displayData[5]=0x40;
displayData[6]=smgduan[TIME[0]/16];
displayData[7]=smgduan[TIME[0]&0x0f];
}
void digDisplay(){
u8 i;
for(i=0; i<8;i++){
switch(i){
case (0):
LSA=0;LSB=0;LSC=0;break;
case (1):
LSA=1;LSB=0;LSC=0;break;
case (2):
LSA=0;LSB=1;LSC=0;break;
case (3):
LSA=1;LSB=1;LSC=0;break;
case (4):
LSA=0;LSB=0;LSC=1;break;
case (5):
LSA=1;LSB=0;LSC=1;break;
case (6):
LSA=0;LSB=1;LSC=1;break;
case (7):
LSA=1;LSB=1;LSC=1;break;
}
P0=displayData[7-i];
delay(100);
P0=0x00;
}
}
void main(){
ds1302Init();
while(1){
datapros();
digDisplay();
}
}
ds1302.c文件
#include "ds1302.h"
//ds1302写入和读取时分秒的地址命令
//秒、分、时、日、月、周、年最低读写位
uchar code READ_RTC_ADDR[7]={0x81,0x83,0x85,0x87,0x89,0x8b,0x8d};
uchar code WRITE_RTC_ADDR[7]={0x80,0x82,0x84,0x86,0x88,0x8a,0x8c};
//秒、分、时、日、月、星期、年
// 2016/5/7/星期六/12:00:00
uchar TIME[7]={0,0,0x00,0x25,0x05,0x02,0x25};
void ds1302Write(uchar addr, uchar dat){
uchar n;
RST=0;
_nop_(); //一个机器周期
SCLK=0;
_nop_();
RST=1;
_nop_();
for(n=0;n<8;n++){
DSIO=addr&0x01;
addr>>=1;
SCLK=1;
_nop_();
SCLK=0;
_nop_();
}
for(n=0;n<8;n++){
DSIO=dat&0x01;
dat>>=1;
SCLK=1;
_nop_();
SCLK=0;
_nop_();
}
RST=0;
_nop_();
}
uchar ds1302Read(uchar addr){
uchar n,dat,dat1;
RST=0;
_nop_();
SCLK=0;
_nop_();
RST=1;
_nop_();
for(n=0;n<8;n++){
DSIO=addr&0x01;
addr>>=1;
SCLK=1;
_nop_();
SCLK=0;
_nop_();
}
_nop_();
for(n=0;n<8;n++){
dat1=DSIO;
dat = (dat>>1) | (dat1<<7);
SCLK=1;
_nop_();
SCLK=0;
_nop_();
}
RST=0;
_nop_(); //以下是ds1302复位的稳定时间,必须的
SCLK=1;
_nop_();
DSIO=0;
_nop_();
DSIO=1;
_nop_();
return dat;
}
void ds1302Init(){
uchar n;
ds1302Write(0x8e,0x00); //关闭写保护
//写入7个字节时钟信号:分秒时日月周年
for(n=0;n<7;n++){
ds1302Write(WRITE_RTC_ADDR[n],TIME[n]);
}
ds1302Write(0x8e,0x80); //打开写保护
}
void ds1302ReadTime(){
uchar n;
for(n=0;n<7;n++){
TIME[n]=ds1302Read(READ_RTC_ADDR[n]);
}
}
ds1302.h文件
#ifndef _DS1302_H_
#define _DS1302_H_
#include <reg52.h>
#include <intrins.h>
#ifndef uchar
#define uchar unsigned char
#endif
#ifndef uint
#define uint unsigned int
#endif
sbit DSIO=P3^4;
sbit RST=P3^5;
sbit SCLK=P3^6;
void ds1302Write(uchar addr, uchar dat);
uchar ds1302Read(uchar addr);
void ds1302ReadTime();
void ds1302Init();
extern uchar TIME[7];
#endif
红外通信
红外通信工作原理
什么是红外线
人的眼睛能看到的可见光按波长从长到短排列,依次为红、橙、黄、绿、青、蓝、紫。其中红光的波长范围为0.62~0.76 μm;紫光的波长范围为0.38~0.46μm。比紫光波长还短的光叫紫外线,比红光波长还长的光叫红外线。红外线遥控就是利用波长为0.76~1.5μm之间的近红外线来传送控制信号的。
红外线系统的组成
红外线遥控器已被广泛使用在各种类型的家电产品上,它的出现给使用电器提供了很多的便利。红外线系统一般由红外发射装置和红外接收设备两大部分组成。红外发射装置又可由键盘电路、红外编码芯片、电源和红外发射电路组成。红外接收设备可由红外接收电路、红外解码芯片、电源和应用电路组成。通常为了使信号更好的被发射端发送出去,经常会将二进制数据信号调制成为脉冲信号,通过红外发射管发射。常用的有通过脉冲宽度来实现信号调制的脉宽调制(PWM)和通过脉冲串之间的时间间隔来实现信号调制的脉时调制(PPM)两种方法。
红外发射管
红外遥控发射装置,也就是通常我们说的红外遥控器是由键盘电路、红外编码电路、电源电路和红外发射电路组成。红外发射电路的主要元件为红外发光二极管。它实际上是一只特殊的发光二极管;由于其内部材料不同于普通发光二极管,因而在其两端施加一定电压时,它便发出的是红外线而不是可见光。目前大量的使用的红外发光二极管发出的红外线波长为940nm左右,外形与普通中 Φ 5 \Phi5 Φ5发光二极管相同。
红外遥控器发射
通常红外遥控为了提高抗干扰性能和降低电源消耗,红外遥控器常用载波的方式传送二进制编,常用的载波频率为38kHz,这是由发射端所使用的455kHz晶振来决定的。在发射端要对晶振进行整数分频,分频系数一般取12,所以 455 k H z ÷ 12 ≈ 37.9 k H z ≈ 38 k H z 455kHz\div12≈37.9kHz≈38kHz 455kHz÷12≈37.9kHz≈38kHz。也有一些遥控系统采用36kHz、40 kHz、56 kHz等,一般由发射端晶振的振荡频率来决定。所以,通常的红外遥控器是将遥控信号(二进制脉冲码)调制在38KHz的载波上,经缓冲放大后送至红外发光二极管,转化为红外信号发射出去的。
二进制脉冲码的形式有多种,其中最为常用的是PWM码(脉冲宽度调制码)和PPM码(脉冲位置调制码,脉冲串之间的时间间隔来实现信号调制)。如果要开发红外接收设备,一定要知道红外遥控器的编码方式和载波频率,我们才可以选取一体化红外接收头和制定解码方案。
红外线接收
红外接收设备是由红外接收电路、红外解码、电源和应用电路组成。红外遥控接收器的主要作用是将遥控发射器发来的红外光信好转换成电信号,再放大、限幅、检波、整形,形成遥控指令脉冲,输出至遥控微处理器。近几年不论是业余制作还是正式产品,大多都采用成品红外接收头。成品红外接收头的封装大致有两种:一种采用铁皮屏蔽;一种是塑料封装。均有三只引脚,即电源正(VDD)、电源负(GND)和数据输出(VOUT)。在使用时注意成品红外接收头的载波频率,另外在遥控编码芯片输出的波形与接收头端收到的波形。
数据格式
数据格式包括了引导码、用户码、数据码和数据码反码,编码总台32位。数据反码是数据码反相后的编码,编码时可用于对数据的纠错。注意:第二段的用户码也可以在遥控应用电路中被设置成第一段用户码的反码。
位定义
用户码或数据码中的每一个位可以是位‘1’,也可以是过’0’。区分‘0’和’1’是利用脉冲的时间间隔来区分,这种编码方式称为脉冲位置调制方式,英文简写PPM。
红外接收原理图(开发板)
红外通信解码(编程)
mian.c文件
#include <reg52.h>
typedef unsigned int u16;
typedef unsigned char u8;
sbit LSA=P2^2;
sbit LSB=P2^3;
sbit LSC=P2^4;
sbit IRIN=P3^2;
u8 irValue[6];
u8 Time;
u8 displayData[8];
u8 code smgduan[17]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71,0x76};
void delay(u16 i){
while(i--);
}
void irInit(){
IT0=1;
EX0=1;
EA=1;
IRIN=1;
}
void digDisplay(){
u8 i;
for(i=0; i<3;i++){
switch(i){
case (0):
LSA=0;LSB=0;LSC=0;break;
case (1):
LSA=1;LSB=0;LSC=0;break;
case (2):
LSA=0;LSB=1;LSC=0;break;
}
P0=displayData[2-i];
delay(100);
P0=0x00;
}
}
void main(){
irInit();
while(1){
displayData[0]=smgduan[irValue[2]/16];
displayData[1]=smgduan[irValue[2]%16];
displayData[2]=smgduan[16];
digDisplay();
}
}
void readIr() interrupt 0{
u8 j,k;
u16 err;
Time=0;
delay(700);
if(IRIN==0){
err=1000;
while((IRIN==0)&&(err>0)){
delay(1);
err--;
}
if(IRIN==1){
err=500;
while((IRIN==1)&&(err>0)){
delay(1);
err--;
}
for(k=0;k<4;k++){
for(j=0;j<8;j++){
err=60;
while((IRIN==0)&&(err>0)){
delay(1);
err--;
}
err=500;
while((IRIN==1)&&(err>0)){
delay(10);
Time++;
err--;
if(Time>30){
return;
}
}
irValue[k]>>=1;
if(Time>=8){
irValue[k]|=0x80;
}
Time=0;
}
}
}
if(irValue[2]!=~irValue[3]){
return;
}
}
}
AD模数转换
AD模数转换工作原理
A/D转换器的主要技术指标
- 分辨率
ADC的分辨率是指使输出数字量变化一个相邻数码所需输入模拟电压的变化量。常用二进制的位数表示。例如12位ADC的分辨率就是12位,或者说分辨率为满刻度的1/(212)。一个10V满刻度的12位ADC能分辨输入电压变化最小值是 10 V × 1 / ( 2 12 ) = 2.4 m V 10V\times1/(2^{12})=2.4mV 10V×1/(212)=2.4mV。
- 量化误差
ADC把模拟量变为数字量,用数字量近似表示模拟量,这个过程称为量化。量化误差是ADC的有限位数对模拟量进行量化而引起的误差。实际上,要准确表示模拟量,ADC的位数需很大甚至无穷大。一个分辨率有限的ADC的阶梯状转换特性曲线与具有无限分辨率的ADC转换特性曲线(直线)之间的最大偏差即是量化误差。
- 偏移误差
偏移误差是指输入信号为零时,输出信号不为零的值,所以有时又称为零值误差。假定ADC没有非线性误差,则其转换特性曲线各阶梯中点的连线必定是直线,这条直线与横轴相交点所对应的输入电压值就是偏移误差。
- 满刻度误差
满刻度误差又称为增益误差。ADC的满刻度误差是指满刻度输出数码所对应的实际输入电压与理想输入电压之差。
- 线性度
线性度有时又称为非线性度,它是指转换器实际的转换特性与理想直线的最大偏差。
- 绝对精度
在一个转换器中,任何数码所对应的实际模拟量输入与理论模拟输入之差的最大值,称为绝对精度。对于ADC而言,可以在每一个阶梯的水平中点进行测量,它包括了所有的误差。
- 转换速率
ADC的转换速率是能够重复进行数据转换的速度即每秒转换的次数。而完成一次AD转换所需的时间(包括稳定时间),则是转换速率的倒数。
A/D转换器的转换速度主要取决于转换电路的类型,不同类型A/D转换器的转换速度相差很大。
①双积分型A/D转换器的转换速度最慢,需几百毫秒左右;
②逐次逼近式A/D转换器的转换速度较快,需几十微秒;
③并行比较型A/D转换器的转换速度最快,仅需几十纳秒时间。
转换原理
逐次逼近式ADC转换原理
逐次逼近式AD转换器与计数式A/D转换类似,只是数字量由“逐次逼近寄存器SAR”产生。SAR使用“对分搜索法”产生数字量,以8位数字量为例,SAR首先产生8位数字量的一半,即10000000B,试探模拟量Vi的大小,若Vn>Vi,清除最高位,若Vn<Vi,保留最高位。在最高位确定后,SAR又以对分搜索法确定次高位,即以低7位的一半y1000000B(y为已确定位)试探模拟量Vi的大小。在bit6确定后,SAR以对分搜索法确定bit5位,即以低6位的一半yy100000B(y为已确定位)试探模拟量的大小。重复这一过程,直到最低位bit0被确定,转换结束。
双积分式ADC的转换原理
这个没介绍。
AD转换原理图(开发板)
注意Vcc是5V。
XPT2046芯片
详情可以查看XPT2046芯片用户手册。
特性
- 工作电压范围为 2.2V~5.25V
- 支持1.5V~5.25V 的数字 I/O 口
- 内建2.5V参考电压源电源
- 电压测量(0V~6)
- 内建结温测量功能
- 触摸压力测量
- 采用 SPI3 线控制通信接口
- 具有自动 power-down 功能
- 封装:QFN-16、TSSOP-16和 VFBGA-48
- 与 TSC2046、AK4182A 完全兼容
引脚功能描述
QFN引脚号 | TSSOP引脚号 | VFBGA引脚号 | 名称 | 说明 |
---|---|---|---|---|
1 | 13 | A5 | BUSY | 忙时信号线。当CS为高电平时为高阻状态 |
2 | 14 | A4 | DIN | 串行数据输入端。当CS为低电平时,数据在DCLK上升沿锁存进来 |
3 | 15 | A3 | /CS | 片选信号。控制转换时序和使能串行输入输出寄存器,高电平时ADC掉电 |
4 | 16 | A2 | DCLK | 外部时钟信号输入 |
5 | 1 | B1和C1 | VCC | 电源输入端 |
6 | 2 | D1 | XP(X+) | XP位置输入端 |
7 | 3 | E1 | YP(Y+) | YP位置输入端 |
8 | 4 | G2 | XN(X-) | XN位置输入端 |
9 | 5 | G3 | YN(Y-) | YN位置输入端 |
10 | 6 | G4和G5 | GND | 接地 |
11 | 7 | G6 | VBAT | 电池监视输入端 |
12 | 8 | E7 | AUX | ADC辅助输入通道 |
13 | 9 | D7 | VREF | 参考电压输入/输出 |
14 | 10 | C7 | IOVDD | 数字电源输入端 |
15 | 11 | B7 | /PENIRQ | 笔接触中断引脚 |
16 | 12 | A6 | DOUT | 串行数据输出端。数据在DCLK的下降沿移出,当CS高电平时为高阻状态 |
开发板上采用的是QFN封装。
基本原理描述
XPT2046 是一种典型的逐次逼近型模数转换器(SAR ADC),包含了采样/保持、模数转换、串口数据 输出等功能。同时芯片集成有一个2.5V的内部参考电压源、温度检测电路,工作时使用外部时钟。XPT2046 可以单电源供电,电源电压范围为 2.7V~5.5V。参考电压值直接决定ADC的输入范围,参考电压可以使用内部参考电压,也可以从外部直接输入 1V~VCC范用内的参考电压(要求外部参考电压源输出阻抗低)。X、Y、Z、VBAT、Temp和AUX模拟信号经过片内的控制寄存器选择后进入ADC,ADC可以配置为单端或差分模式。选择VBAT、Temp和AUX时应该配置为单端模式;作为触摸屏应用时,应该配置为差分模式,这可有效消除由于驱动开关的寄生电阻及外部的干扰带来的测量误差,提高转换精度。
单端模式输入配置 ( S E R D F R = 1 ) (SER\sqrt{DFR}=1) (SERDFR=1)
A2 | A1 | A0 | VBAT | AUXIN | TEMP | YN | XP | YP | Y-位置 | X-位置 | Z1-位置 | Z2-位置 | X-驱动 | Y-驱动 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 0 | 0 | +IN (TEMPO) | off | off | |||||||||
0 | 0 | 1 | +IN | 测量 | Off | On | ||||||||
0 | 1 | 0 | +IN | Off | Off | |||||||||
0 | 1 | 1 | +IN | 测量 | XN,On | YP,On | ||||||||
1 | 0 | 0 | +IN | 测量 | XN,On | YP,On | ||||||||
1 | 0 | 1 | +IN | 测量 | On | Off | ||||||||
1 | 1 | 0 | +IN | Off | Off | |||||||||
1 | 1 | 1 | +IN (TEMP1) | Off | Off |
差分输入配置 ( S E R D F R = 0 ) (SER\sqrt{DFR}=0) (SERDFR=0)
A2 | A1 | A0 | +REF | -REF | YN | XP | YP | Y-位置 | X-位置 | Z1-位置 | Z2-位置 | 驱动 |
---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 0 | 1 | YP | YN | +IN | 测量 | YP, YN | |||||
0 | 1 | 1 | YP | XN | +IN | 测量 | YP, XN | |||||
1 | 0 | 0 | YP | XN | +IN | 测量 | YP, XN | |||||
1 | 0 | 1 | XP | XN | +IN | 测量 | XP, XN |
数字通信
时序:
控制字节命令:
位7 (MSB) | 位6 | 位5 | 位4 | 位3 | 位2 | 位1 | 位0(LSB) |
---|---|---|---|---|---|---|---|
S | A2 | A1 | A0 | MODE | SER/DFR | PD1 | PD0 |
控制字节各位描述:
位 | 名称 | 功能描述 |
---|---|---|
7 | S | 开始位。为1表示一个新的控制字节到来,为0则忽略PIN引脚上数据 |
6 - 4 | A2 - A0 | 通道选择位。参见表1和表2 |
3 | MODE | 12位/8位转换分辨率选择位。为1选择8位为转换分辨率,为0选择12位分辨率 |
2 | SER/DFR | 单端输入方式/差分输入方式选择位。为1是单端输入方式,为0是差分输入方式 |
1 - 0 | PD1 - PD0 | 低功率模式选择位。若为11,器件总处于供电状态;若为00,器件在变换之间处于低功率模式 |
掉电和内部参考电压选择:
PD1 | PD0 | PEN IRQ | 功能说明 |
---|---|---|---|
0 | 0 | 使能 | 在两次 A/D 转换之间掉电,下次转换一开始,芯片立即进入完全上电状态,而无需额外的延时。在这种模式下,YN 开关一直处于 ON 状态 |
0 | 1 | 禁止 | 参考电压关闭,ADC 打开 |
1 | 0 | 使能 | 参考电压打开,ADC 关闭 |
1 | 1 | 禁止 | 芯片处于上电状态,参考电压和 ADC 总是打开 |
这部分可以详细看下芯片手册相关部分内容便于理解。
如果要检测转换电位器模拟信号,控制字命令寄存器值为0X94或者0XB4。
如果要检测转换热敏电阻模拟信号,控制字命令寄存器值为0XD4。
如果要检测转换光敏电阻模拟信号,控制字命令寄存器值为0XA4。
如果要检测转换AIN3通道上模拟信号,控制字命令寄存器值为0XE4。
AD转换(编程)
main.c文件:
#include "reg52.h"
#include "XPT2046.h"
typedef unsigned int u16;
typedef unsigned char u8;
sbit LSA=P2^2;
sbit LSB=P2^3;
sbit LSC=P2^4;
u8 disp[4];
u8 code smgduan[10]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};
void delay(u16 i){
while(i--);
}
void datapros(){
u16 temp;
static u8 i;
if(i==50){
i=0;
temp=Read_AD_Data(0xd4); //AIN0电位器,改这里就可以改变检测通道
}
i++;
disp[0]=smgduan[temp/1000];
disp[1]=smgduan[temp%1000/100];
disp[2]=smgduan[temp%100/10];
disp[3]=smgduan[temp%10];
}
void digDisplay(){
u8 i;
for(i=0; i<4;i++){
switch(i){
case (0):
LSA=0;LSB=0;LSC=0;break;
case (1):
LSA=1;LSB=0;LSC=0;break;
case (2):
LSA=0;LSB=1;LSC=0;break;
case (3):
LSA=1;LSB=1;LSC=0;break;
}
P0=disp[3-i];
delay(100);
P0=0x00;
}
}
void main(){
while(1){
datapros();
digDisplay();
}
}
XPT2046.h文件:
#ifndef _XPT2046_H_
#define _XPT2046_H_
#include <reg52.h>
#include <intrins.h>
#ifndef uchar
#define uchar unsigned char
#endif
#ifndef uint
#define uint unsigned int
#endif
#ifndef ulong
#define ulong unsigned long
#endif
sbit DOUT=P3^7;
sbit CLK=P3^6;
sbit CS=P3^5;
sbit DIN=P3^4;
uint Read_AD_Data(uchar cmd);
uint SPI_Read();
void SPI_Write(uchar dat);
#endif
XPT2046.c文件:
#include "XPT2046.h"
void SPI_Write(uchar dat){
uchar i;
CLK=0;
for(i=0;i<8;i++){
DIN=dat>>7;
dat<<=1;
CLK=0;
CLK=1;
}
}
uint SPI_Read(){
uint i,dat=0;
CLK=0;
for(i=0;i<12;i++){
dat<<=1;
CLK=1;
CLK=0;
dat|=DOUT;
}
return dat;
}
uint Read_AD_Data(uchar cmd){
uchar i;
uint AD_Value;
CLK=0;
CS=0;
SPI_Write(cmd);
for(i=6;i>0;i--);//延时等待转换结果
CLK=1;
_nop_();
_nop_();
CLK=0;
_nop_();
_nop_();
AD_Value=SPI_Read();
CS=1;
return AD_Value;
}
DA数模转换(PWM输出)
DAC转换原理及主要技术指标
D/A转换器的基本原理及分类
T型电阻网络D/A转换器:
由图可知,运放两个输入端为**“虚地”**,所以电位都约为0。所以无论开关在0或者1,最后两个2R都是并联得R,和电阻R串联又为2R,以此类推,那么到最前端,相当于两个2R的电阻并联,可知电流 I = V r e f / R I=V_{ref}/R I=Vref/R。 I 7 = I / 2 I7=I/2 I7=I/2, 16 = 1 / 2 × I / 2 16=1/2\times I/2 16=1/2×I/2,由此追溯到 I 0 = I / 256 I0=I/256 I0=I/256,如果 R f b = R Rfb=R Rfb=R,那么V0只与Vref有关,即 V 0 = V r e f × z / 256 ( z 是输入的数字量) V0=V_{ref}\times z/256(z是输入的数字量) V0=Vref×z/256(z是输入的数字量)。
这里注意分析上面给出的图片中的电路关系。
D/A转换器的主要性能指标
- 分辨率
分辨率是指输入数字量的最低有效位(LSB)发生变化时,所对应的输出模拟量(电压或电流)的变化量。它反映了输出模拟量的最小变化值。
分辨率与输入数字量的位数有确定的关系,可以表示成 F S / ( 2 n ) FS/(2^n) FS/(2n)。FS表示满量程输入值,n为二进制位数。对于5V的满量程,采用8位的DAC时,分辨率为 5 V / 256 = 19.5 m V 5V/256=19.5mV 5V/256=19.5mV;当采用12位的DAC时,分辨率则为 5 V / 4096 = 1.22 m V 5V/4096=1.22mV 5V/4096=1.22mV。显然,位数越多分辨率就越高。
- 线性度
线性度(也称非线性误差)是是实际转换特性曲线与理想直线特性之间的最大偏差。常以相对于满量程的百分数表示。如±1%是指实际输出值与理论之差在满刻度的±1%以内。
- 绝对精度和相对精度
绝对精度(简称精度)是指在整个刻度范围内,任一输入数码所对应的模拟量实际输出值与理论值之间的最大误差。绝对精度是由DAC的增益误差(当输入数码为全1时,实际输出值与理想输出值之差)零点误差(数码输入为全0时,DAC的非零输出值)、非线性误差和噪声等引起的。绝对精度(即最大误差)应小于1个LSB。
相对精度与绝对精度表示同一含义,用最大误差相对于满刻度的百分比表示。
- 建立时间
建立时间是指输入的数字量发生满刻度变化时,输出模拟信号达到满刻度值的±1/2LSB所需的时间。是描述D/A转换速率的一个动态指标。
电流输出型DAC的建立时间短。电压输出型DAC的建立时间主要决定于运算放大器的响应时间。根据建立时间的长短,可以将DAC分成超高速(<1μs)、高速(10~1μS)、中速(100~10pS)、低速(≥100μS)几档。
应当注意,精度和分辨率具有一定的联系,但概念不同。DAC的位数多时,分辨率会提高,对应于影响精度的量化误差会减小。但其它误差(如温度漂移、线性不良等)的影响仍会使DAC的精度变差。
PWM工作原理
PWM简介
PWM是Pulse Width Modulation的缩写,中文意思就是脉冲宽度调制,简称脉宽调制。它是利用微处理器的数字输出来对模拟电路进行控制的一种非常有效的技术,其控制简单、灵活和动态响应好等优点而成为电力电子技术最广泛应用的控制方式,其应用领域包括测量、通信、功率控制与变换、电动机控制、伺服控制、调光、开关电源,甚至某些音频放大器,因此研究基于 PWM 技术的正负脉宽数控调制号发生器具有十分重要的现实意义。
PWM等效图形:
单片机只有高电平和低电平两种信号,通过高低电平的保持时间来表示不同的模拟量。
PWM 是一种对模拟信号电平进行数字编码的方法。通过高分辨率计数器的使用,方波的占空比被调制用来对一个具体模拟信号的电平进行编码。PWM 信号仍然是数字的,因为在给定的任何时刻,满幅值的直流供电要么完全有(ON),要么完全无(OFF)。电压或电流源是以一种通(ON)或断(OFF)的重复脉冲序列被加到模拟负载上去的。通的时候即是直流供电被加到负载上的时候,断的时候即是供电被断开的时候。只要带宽足够,任何模拟值都可以使用 PWM 进行编码。
占空比 = T 高电平 T × 100 % 占空比=\frac{T_{高电平}}{T}\times 100 \% 占空比=TT高电平×100%
可以设置一个值c,用计数器进行控制,当计数器值小于这个值c则输出低电平,大于c则输出高电平。
原理图(开发板)
上方的是一个差分放大电路,用来放大P21管脚传过来的PWM的信号。
这里需要用短接片短接SIP上的1、2管脚,让DAC0信号输出到DA1上,通过控制DAC0输出的电压值的变化完成DA1呼吸灯的效果。
DA数模转换(编程)
main.c文件:
#include <reg52.h>
typedef unsigned int u16;
typedef unsigned char u8;
sbit PWM=P2^1;
bit DIR;
u16 count,value,timer1;
void timer1Init(){
TMOD|=0x10; //选择定时器模式。工作方式1,仅用TR1打开启动
TH1=0xff;
TL1=0xff;
ET1=1;
EA=1;
TR1=1;
}
void main(){
timer1Init();
while(1){
if(count>100){
count=0;
if(DIR==1){
value++;
}
if(DIR==0){
value--;
}
}
if(value==1000){
DIR=0;
}
if(value==0){
DIR=1;
}
if(timer1>1000){
timer1=0;
}
if(timer1<value){
PWM=1;
}else{
PWM=0;
}
}
}
void time1() interrupt 3{
TH1=0xff;
TL1=0xff;
timer1++;
count++;
}
LCD1602液晶
LCD1602工作原理
LCD1602简介
1602液晶也叫1602字符型液晶,它是一种专门用来显示字母、数字、符号的点阵型液晶模块。它是由若干个5x7或者5x10的点阵字符位组成,每个点阵字符位都可以用显示一个字符,每位之间有一个点距的间隔,每行之间也有间隔,起到了字符间距和行间距的作用,正因为如此,所以它不能很好的显示图片。
LCD1602的引脚
编号 | 符号 | 引脚说明 | 编号 | 符号 | 引脚说明 |
---|---|---|---|---|---|
1 | VSS | 电源地 | 9 | D2 | Data I/O |
2 | VDD | 电源正极 | 10 | D3 | Data I/O |
3 | VL | 液晶显示偏压信号 | 11 | D4 | Data I/O |
4 | RS | 数据/命令选择端(H/L) | 12 | D5 | Data I/O |
5 | R/W | 读/写选择端(H/L) | 13 | D6 | Data I/O |
6 | E | 使能信号 | 14 | D7 | Data I/O |
7 | D0 | Data I/O | 15 | BLA | 背光源正极 |
8 | D1 | Data I/O | 16 | BLK | 背光源负极 |
操作步骤
- 初始化
- 写命令(RS=L)设置显示坐标
- 写数据(RS=H)
时序参数 | 符号 | 极限值 最小值 | 极限值 典型值 | 极限值 最大值 | 单位 |
---|---|---|---|---|---|
E信号周期 | t C t_C tC | 400 | - | - | ns |
E脉冲宽度 | t P W t_{PW} tPW | 150 | - | - | ns |
E上升沿/下降沿时间 | t R , t F t_R, t_F tR,tF | - | - | 25 | ns |
地址建立时间 | t S P 1 t_{SP1} tSP1 | 30 | - | - | ns |
地址保持时间 | t H D 1 t_{HD1} tHD1 | 10 | - | - | ns |
数据建立时间(读操作) | t D t_D tD | - | - | 100 | ns |
数据保持时间(读操作) | t H D 2 t_{HD2} tHD2 | 20 | - | - | ns |
数据建立时间(写操作) | t S P 2 t_{SP2} tSP2 | 40 | - | - | ns |
数据保持时间(写操作) | t H D 2 t_{HD2} tHD2 | 10 | - | - | ns |
LCD1602关键性的指令设置
- 清屏指令
指令功能 | RS | R/W | DB7 | DB6 | DB5 | DB4 | DB3 | DB2 | DB1 | DB0 | 执行时间/ms |
---|---|---|---|---|---|---|---|---|---|---|---|
清屏 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1.64 |
功能:
<1> 清除液晶显示器,即将 DDRAM 的内容全部填入"空白"的 ASCII码 20H;
<2>光标归位,即将光标撤回液晶显示屏的左上方;
<3>将地址计数器(AC)的值设为 0。
- 进入模式设置指令
指令功能 | RS | R/W | DB7 | DB6 | DB5 | DB4 | DB3 | DB2 | DB1 | DB0 | 执行时间/us |
---|---|---|---|---|---|---|---|---|---|---|---|
进入模式设置 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | I/D | S | 40 |
功能:
设定每次定入1位数据后光标的移位方向,并且设定每次写入的个字符是否移动。参数设定的情况如下所示:
位名 | 设置(0) | 设置(1) |
---|---|---|
I/D | 写入新数据后光标左移 | 写入新数据后光标右移 |
S | 写入新数据后显示屏不移动 | 写入新数据后显示屏整体右移1个字符 |
- 显示开关控制指令
指令功能 | RS | R/W | DB7 | DB6 | DB5 | DB4 | DB3 | DB2 | DB1 | DB0 | 执行时间/us |
---|---|---|---|---|---|---|---|---|---|---|---|
显示开关控制 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | D | C | B | 40 |
功能:
控制显示器开/关、光标显示/关闭以及光标是否闪烁。参数设定的情况如下:
位名 | 设置(0) | 设置(1) |
---|---|---|
D | 显示功能关 | 显示功能开 |
S | 无光标 | 有光标 |
B | 光标闪烁 | 光标不闪烁 |
在设置完成之后我们就要明白具体是在哪显示,在哪个位置显示?在对液晶模块的初始化中要先设置其显示模式,在液晶模块显示字符时光标是自动右移的,无需人工干预。每次输入指令前都要判断液晶模块是否处于忙的状态。DDRAM就是显示数据RAM,用来寄存待显示的字符代码。共80个字节,其地址和屏幕的对应关系表如下:
显示位置 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | …… | 40 |
---|---|---|---|---|---|---|---|---|---|
第一行 | 00H | 01H | 02H | 03H | 04H | 05H | 06H | …… | 27H |
第二行 | 40H | 41H | 42H | 43H | 44H | 45H | 46H | …… | 67H |
要显示字符时要先输入显示字符地址,也就是告诉模块在哪里显示字符,例如第二行第一个字符的地址是40H,那么是否直接写入40H就可以将光标定位在第二行第一个字符的位置呢?这样不行,因为写入显示地址时要求最高位D7恒定为高电平1所以实际写入的数据应该是 01000000 B ( 40 H ) + 10000000 B ( 80 H ) = 11000000 B ( C O H ) 01000000B(40H)+10000000B(80H)=11000000B(COH) 01000000B(40H)+10000000B(80H)=11000000B(COH)。在 1602 中我们就用前 16个就行了。第二行也一样用前 16 个地址。对应如下:
00H | 01H | 02H | 03H | 04H | 05H | 06H | 07H | 08H | 09H | 0AH | 0BH | 0CH | 0DH | 0EH | 0FH |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
40H | 41H | 42H | 43H | 44H | 45H | 46H | 47H | 48H | 49H | 4AH | 4BH | 4CH | 4DH | 4EH | 4FH |
可以发现还有很多没有应用到显示的地址,可以用来存放其他数据,例如实现滚动显示字符就可以将还未显示的数据放到后面。
RAM地址映射图:
原理图(开发板)
LCD1602显示(编程)
mian.c文件:
#include <reg52.h>
#include "lcd.h"
typedef unsigned int u16;
typedef unsigned char u8;
u8 disp[]="LCG is handsome!";//显示的字符,最好写16个,如果不满16个会有脏数据
void main(){
u8 i;
lcdInit();
for(i=0;i<16;i++){
lcdWriteData(disp[i]);
}
while(1);
}
lcd.h文件:
#ifndef __LCD_H_
#define __LCD_H_
//#define LCD1602_4PINS
#include <reg52.h>
#ifndef uchar
#define uchar unsigned char
#endif
#ifndef uint
#define uint unsigned int
#endif
#define LCD1602_DATAPINS P0
sbit LCD1602_E=P2^7;
sbit LCD1602_RW=P2^5;
sbit LCD1602_RS=P2^6;
void LCD1602_delay1ms(uint c);
void lcdInit();
void lcdWriteCom(uchar com);
void lcdWriteData(uchar dat);
#endif
lcd.c文件:
#include "lcd.h"
void LCD1602_delay1ms(uint c){
uchar a,b;
for(;c>0;c--){
for(b=199;b>0;b--){
for(a=1;a>0;a--);
}
}
}
#ifndef LCD1602_4PINS
void lcdInit(){
lcdWriteCom(0x38);
lcdWriteCom(0x0c);
lcdWriteCom(0x06);
lcdWriteCom(0x01);
lcdWriteCom(0x80);
}
#else
void lcdInit(){
lcdWriteCom(0x32);
lcdWriteCom(0x28);
lcdWriteCom(0x0c);
lcdWriteCom(0x06);
lcdWriteCom(0x01);
lcdWriteCom(0x80);
}
#endif
#ifndef LCD1602_4PINS
void lcdWriteCom(uchar com){
LCD1602_E=0;
LCD1602_RS=0;
LCD1602_RW=0;
LCD1602_DATAPINS=com;
LCD1602_delay1ms(1);
LCD1602_E=1;
LCD1602_delay1ms(5);
LCD1602_E=0;
}
#else
void lcdWriteCom(uchar com){
LCD1602_E=0;
LCD1602_RS=0;
LCD1602_RW=0;
LCD1602_DATAPINS=com;
LCD1602_delay1ms(1);
LCD1602_E=1;
LCD1602_delay1ms(5);
LCD1602_E=0;
LCD1602_DATAPINS=com<<4;
LCD1602_delay1ms(1);
LCD1602_E=1;
LCD1602_delay1ms(5);
LCD1602_E=0;
}
#endif
#ifndef LCD1602_4PINS
void lcdWriteData(uchar dat){
LCD1602_E=0;
LCD1602_RS=1;
LCD1602_RW=0;
LCD1602_DATAPINS=dat;
LCD1602_delay1ms(1);
LCD1602_E=1;
LCD1602_delay1ms(5);
LCD1602_E=0;
}
#else
void lcdWriteData(uchar dat){
LCD1602_E=0;
LCD1602_RS=1;
LCD1602_RW=0;
LCD1602_DATAPINS=dat;
LCD1602_delay1ms(1);
LCD1602_E=1;
LCD1602_delay1ms(5);
LCD1602_E=0;
LCD1602_DATAPINS=dat<<4;
LCD1602_delay1ms(1);
LCD1602_E=1;
LCD1602_delay1ms(5);
LCD1602_E=0;
}
#endif