普中51单片机开发板笔记

普中51单片机开发板笔记


简介


什么是单片机?

单板机

CPU芯片、存储器芯片、I/O接口芯片和简单的I/O设备(小键盘、LED显示器)等装配在一块印刷电路板上,再配上监控程序(固化在ROM中),就构成了一台单板微型计算机(简称单板机)。

单片机

在一片集成电路芯片上集成微处理器、存储器、I/O接口电路,从而构成了单芯片微型计算机,即单片机。 Intel公司推出了MCS-51系列单片机:集成8位CPU、4K字节ROM、128字节RAM、4个8位并口、1个全双工串行口、2个16位定时/计数器。寻址范围64K,并有控制功能较强的布尔处理器

普中51单片机A2开发板

单片机能做什么?

工业自动化:数据采集、测控技术。

智能仪器仪表:数字示波器、数字信号源、数字万用表、感应电流表等。

消费类电子产品:洗衣机、电冰箱、空调机、电视机、微波炉、手机、IC卡、汽车电子设备等。

通讯方面:调制解调器、程控交换技术、手机、小灵通等。

武器装备:飞机、军舰、坦克、导弹、航天飞机、鱼雷制导、智能武器等。

等等…

凡是与控制或简单计算有关的电子设备都可以用单片机来实现,根据具体实际情况还可以选择不同性能的单片机,如:atmel,stc,pic,avr,凌阳,80C51,arm等

怎样开始学习?

1.实践第一。

2.补充必要的理论知识,即缺什么补什么。

3.做工程项目积累经验。(可在网络上搜集题目,也可自己有什么想法大胆的去试验)

单片机的预备知识

电平特性
  • 数字电路中只有两种电平:高电平和低电平

    • 高电平:5V或者3.3V,取决单片机电源。
    • 低电平:0V
  • RS232电平:计算机串口的电平

    • 高电平:-12V
    • 低电平:+12V
  • 所以当我们用单片机跟电脑通信的时候,我们要通过各种元器件将单片机的电平转换为计算机可识别的电平才能跟电脑进行通信。

2进制与16进制的表示及转换
二进制
  • 由于数字电路中的只有两种电平的特性,计算机中使用的数字采用都是二进制的。
  • 二进制是使用0和1两个数码来表示的数,它的基数是2,进位规则是“逢二进一”。
十六进制
  • 十六进制的基数是F,进位规则是”逢十六进一“。
二进制和十六进制之间的转换
十进制二进制十六进制十进制二进制十六进制
000810008
111910019
2102101010A
3113111011B
41004121100C
51015131101D
61106141110E
71117151111F
二进制逻辑运算
运算类型介绍用法
有0得01 & 1 = 1; 1 & 0 = 0; 0 & 0 = 0
有1得11 | 1 = 1; 1 & 0 = 1; 0 | 0 = 0;
1的非得0,0的非得1~1 = 0; ~0 = 1;
异或相同为0,不同为11 ^ 1 = 0; 1 ^ 0 = 1; 0 ^ 0 = 0;
8051单片机介绍

​ 80C51是MCS-51系列中的一个典型品种;其它厂商以8051为基核开发出的CMOS工艺单片机产品统称为80C51系列。当前常用的

80C51系列单片机主要产品有:

  • Intel的:80C31、80C51、87C51,80C32、80C52、87C52等;

  • ATMEL的:89C51、89C52、89C2051等;

  • Philips华邦DallasSTC

  • **Siemens(Infineon)**等公司的许多产品 。

80C51的引脚封装

80C51的引脚封装

P3口第二功能各引脚功能定义:

引脚功能引脚功能
P3.0RXD串行口输入P3.4T0定时器0外部输入
P3.1TXD串行口输出P3.5T1定时器1外部输入
P3.2INT0外部中断0输入P3.6WR外部写控制
P3.3INT1外部中断1输入P3.7RD外部读控制
8051内部结构

8051内部结构

总线(BUS)是计算机各部件之间传送信息的公共通道。微机中有内部总线和外部总线两类。内部总线是CPU内部之间的连线。外部总线是指CPU与其它部件之间的连线。 外部总线有三种:数据总线DB(Data Bus), 地址总线 AB(Address Bus)和控制总线 CB(Control Bus)。

CPU:由运算和控制逻辑组成,同时还包括中断系统和部分外部特殊功能寄存器;

RAM:用以存放可以读写的数据,如运算的中间结果、最终结果以及欲显示的数据;

ROM:用以存放程序、一些原始数据和表格;

I/O口:四个8位并行I/O口,既可用作输入,也可用作输出;

T/C:两个定时/记数器**,**既可以工作在定时模式,也可以工作在记数模式;

五个中断源的中断控制系统;

一个全双工UART(通用异步接收发送器)的串行I/O口,用于实现单片机之间或单片机与微机之间的串行通信;

片内振荡器和时钟产生电路,石英晶体和微调电容需要外接。最高振荡频率取决于单片机型号及性能。

单片机工作的基本时序

机器周期和指令周期

  1. 振荡周期:也称时钟周期, 是指为单片机提供时钟脉冲信号的振荡源的周期,我们开发板上为12MHZ。
  2. 状态周期:每个状态周期为时钟周期的 2 倍, 是振荡周期经二分频后得到的。
  3. 机器周期一个机器周期包含 6 个状态周期S1~S6, 也就是 12 个时钟周期。 在一个机器周期内, CPU可以完成一个独立的操作。
  4. 指令周期:它是指CPU完成一条操作所需的全部时间。 每条指令执行时间都是有一个或几个机器周期组成。MCS - 51 系统中, 有单周期指令、双周期指令和四周期指令。
学单片机到底学什么

对I/O口的控制,无论单片机对外界进行何种控制,都是通过I/O口进行的。

接受外部的控制,通过I/O来感受外部的电压。

51单片机总共有P0、P1、P2、P3四个8位双向输入输出端口,每个端口都有锁存器、输出驱动器和输入缓冲器。4个I/O端口都能作输入输出口用。

C51基础知识
单片机IO口的结构

单片机IO口的结构

上下拉电阻

上拉电阻就是将不确定的信号通过一个电阻拉到高电平,同时此电阻起到一个限流的作用,下拉就是下拉到低电平。

  1. OC门要输出高电平,外部必须加上拉电阻。
  2. 加大普通IO口的驱动能力。
  3. 起到限流的作用。
  4. 抵抗电磁干扰。
上下拉电阻的选取原则
  1. 从降低功耗方面考虑应该足够大,因为电阻越大,电流越小。
  2. 从确保足够的引脚驱动能力考虑应该足够小,电阻越小,电流才能越大。
  3. 开漏输出时,过大的上拉电阻会导致信号上升沿变缓。

上下拉电阻示意图

C语言基础


C语言是一种编译型程序设计语言,它兼顾了多种高级语言的特点,并具备汇编语言的功能。目前,使用C语言进行程序设计已经成为软件开发的一个主流。用C语言开发系统可以大大缩短开发周期,明显增强程序的可读性,便于改进、扩充和移植。

一个简单的单片机C程序要有什么?

#include <reg51.h>
void main() {
	while(1) {
        
	}
}
  • 包涵头文件

  • 程序主函数

其实课堂上学习 C语言的很大一部分内容在初期单片机编程中都用不到,因此没必要因为觉的自己的 C 语言基础不是很好而对单片机望而止步!


C语言中常用的语句


if-else语句

if (表达式) {
    语句1;
} else {
    语句2;
}

其语义是:如果表达式的值为真(非0),则执行语句1,否则执行语句2 。

例如:

if (a > b) {
    c = 1;
} else {
    c = 2;
}

可使用三目运算简化**if-else**语句

表达式 ? 语句1 : 语句2;

上面代码可简化为

c = a > b ? 1 : 2;

while语句

while语句的一般形式为:

while (表达式) {
    循环体
}

其中表达式是循环条件,语句为循环体。

while语句的语义是:当表达式的值为真(非0)时, 执行循环体语句。

例如:

while ( i < 10) {
    i = i + 1;
}

do-while 语句

do {
    循环体
} while (表达式);

这个循环与while循环的不同在于:它先执行循环体中的语句,然后再判断表达式是否为真,如果为真则继续循环;如果为假,则终止循环。因此,do-while循环至少要执行一次循环语句。

例如:

do {
    a = a + 1;
} while (a < 10);

for语句

for(表达式1; 表达式2; 表达式3) {
    循环体;
}

它的执行过程如下:

  1. 执行表达式1
  2. 执行表达式2,若值为真(非0),则执行循环语句;若其值为假(0),则结束循环;
  3. 执行表达式3
  4. 跳转到第2步。

例如:

for (a = 0; a < 10; a++) { // a++相当于a = a + 1;
    b = 0;
}

单目运算与双目运算

表达式说明表达式说明
a++先运算a再自增1。如: a = 3; b = a++ + 5; 执行完后 a = 4, b = 8a *= 2等价于a = a * 2;
–aa先自减1再参与运算。如: a = 3; b = --a + 5; 执行完后 a = 2, b = 7a /= 2等价于a = a / 2;
a %= 2等价于a = a % 2; m % n 表示求m除以n的余数a &= 2等价于a = a & 2;
a |= 2等价于 a = a | 2;a ^= 2等价于a = a ^ 2;
a += 2等价于a = a + 2;a ~= 2等价于a = a ~ 2;
a -= 2等价于a = a - 2;

switch

switch (表达式) {
    case 常量表达式1: 语句1;
    case 常量表达式2: 语句2;case 常量表达式n: 语句n;
    default       : 语句n+1;
}

其语义是:计算表达式的值。 并逐个与其后的常量表达式值相比较,当表达式的值与某个常量表达式的值相等时, 即执行其后的语句,然后不再进行判断,继续执行后面所有case后的语句。如表达式的值与所有case后的常量表达式均不相同时,则执行default后的语句。

例如:

switch (a) {
    case (0):
        b = 0;
        break;
    case (1):
        b = 1;
        break;
    default :
        b=3;
}

break 语句的含义是跳出这个switch语句,如果没有这条语句,则会从满足条件的地方(即与switch(表达式)括号中表达式匹配的case)开始执行,直到switch结构结束。当break语句用于do-whileforwhile循环语句中时,可使程序终止循环 。

函数

函数的定义

返回变量类型 函数名 (函数参数) {
    函数体;
}

如:

unsigned char read (unsigned char addr) {
    unsigned char dat;			 // 定义一个变量存放返回值
    ...					   	     // 函数中的程序。
    return dat;					 // 函数的返回值
}

函数的调用

在函数调用之前要在主函数前面加入上该函数的声明。(或者你可以放在头文件里面,包含该头文件时,就可以声明了。)

C51的数据类型扩充定义

sfr:特殊功能寄存器声明

sfr 变量名 = 地址值;

特殊功能寄存器在reg51.H这个头文件里面都帮我们定义好了,所以平时我们就不要自己去定义寄存器的名字。

sbit:特殊功能位声明

sbit 变量名 = 地址值;

在给某个引脚取名的时候经常会用到。

bit : 位变量声明

用来定义位数据变量

例:sfr SCON = 0X98;

sbit LED = P0^2;

C51中常用到的一些预处理命令

#define
#define A P0

注意后面不用加分号

#typedef
typedef unsigned char int;

注意后面要加分号

重新定义一些常用的关键词,可以增强程序的可移植性,因为在不同的编译软件上面,C语言的数据类型的关键词的位宽是不一样的。

#ifndef…#endif

条件编译,常用于头文件的定义还有一些程序条件编译

C51基本数据类型

类型符号关键字所占位数数的表示范围
整型(signed) int16-32768~32767
(signed) short16-32768~32767
(signed) long32-2147483648~2147483647
unsigned int160~65535
unsigned short int160~65535
unsigned long int320~4294967295
实型float323.4e-38~3.4e38
double641.7e-308~1.7e308
字符型char8-128~127
unsigned char80~255

常用的运算符

+-*/(加、减、乘、除)

>>=<<=(大于、大于等于、小于、小于等于)

==!=(测试等于、测试不等于。它是逻辑运算符)

比如说:

if(a == 1) 或者 if(a != 1) {
  b=2;
}

&&||!(逻辑与、逻辑或、逻辑非 )

比如说:

if ((a > 0) && (b > 0)) {
    do something
}

单片机最小系统


1.电源电路

电源电路

2.复位电路

复位电路

3.时钟电路

时钟电路

4.下载电路

下载电路

开发环境搭建


KEIL4的安装

第一步、正常安装Keil 4,默认一路点下一步,直到安装完成。

第二部、打开Keil软件,选择File >> Licence Management ... >> 复制CID码 >> 打开破解工具 >> 粘贴CID码 >> 点击Generate生成密钥并复制 >> 返回 Keil 软件粘贴密钥后点击Add Lic

创建工程模板

第一步、新建工程:打开Keil软件,选择Projet >> New μVision Projet... >> 选择保存路径,并命名为Template >> 弹出提示后点击取消 >> 选择Atmel中的AT89C52或者AT89C51 >> 弹出提示框点击

第二部、新建main程序,选择 File >> New >> 命名为main.c >> 在编辑区中粘贴以下代码:

#include "reg52.h"
void main() {
    while(1) {
        
    }
}

第三步、在新建工程Source Group 1上单击右键,选择Add Files to Group 'Source Group 1...,添加刚刚创建的main.c文件。

第四步、点击Build编译程序,保证程序无误。

第四步、点击Target Options...图标,点击Output选项卡,勾选Create HEX File选项,点击OK

配置Sublime text 编译51程序

{
    "shell_cmd": "\"%KEIL_CMD%/C51\" \"${file}\" & \"%KEIL_CMD%/LX51\" \"${file_base_name}.obj\" to \"${file_base_name}.abs\" >> nul & \"%KEIL_CMD%/OHx51\" \"${file_base_name}.abs\" >> nul & DEL *.lst *.obj *.map *.abs >> nul",
    "file_regex": "^(..[^:]*):([0-9]+):?([0-9]+)?:? (.*)$",
    "working_dir": "${file_path}",
    "selector": "source.c",
    "encoding":"cp936"
}

其中%KEIL_CMD%Keil软件安装路径的环境变量,需要自己手动配置,我的环境变量配置如下:

环境变量配置

01、点亮一个LED灯


LED是什么

LED,即发光二极管,是一种半导体固体发光器件。如图:

发光二级管

LED工作原理

LED的工作是有方向性的,只有当正级接到LED阳极,负极接到LED的阴极的时候才能工作,如果反接LED是不能正常工作的。

看懂原理图

开发板上面LED的原理图如下图,LED的阳极串联一个电阻,然后连接到电源VCC,而LED的阴极连接到单片机的P2口,如果你想点亮一盏LED就对把单片机相对应的IO赋为低电平。

LED灯原理图

驱动程序

C语言知识点:

sbit 变量名 = 地址值;

在给某个引脚取名的时候经常会用到。

#include "reg52.h"
sbit led = P2^0;

void main(){
  while(1){
    led = 0;
  }
}

02、LED闪烁


C语言知识

typedef
typedef unsigned char u8;
typedef unsigned int u16;

重新定义一些常用的关键词,可以增强程序的可移植性,因为在不同的编译软件上面,C语言的数据类型的关键词的位宽是不一样的。

while循环
while(i < 10) {
 i = i + 1;
}

while语句的语义是:计算表达式的值,当值为真(非0)时, 执行循环体语句。

延时函数

void delay (u16 i) { //大约延时10us
	while(i--);	
}

驱动程序

/*
实验现象:使第一个LED不停地闪烁
*/
#include "reg52.h"

typedef unsigned int u16;

sbit led = P2^0;

void delay(u16 i){
  while(i--);
}

void main(){
  while(1){
	delay(50000);
    led = ~led;
  }
}

03、流水灯


定义宏

#define A P0

将所有的A替换成P0

循环左移右移函数

_crol_(a,b);循环左移函数,a是左移的值,b是左移的位数。

_cror_(a,b);循环右移函数,a是右移的值,b是右移的位数。

以上两个函数都包含在instrins.h库函数里。

驱动程序

方法①
/*
实验现象:LED灯来回流动点亮
*/
#include "reg52.h"
#include <intrins.h>

typedef unsigned int u16;
typedef unsigned char u8; 

#define LED P2

void delay(u16 i) {
  while(i--);
}

void main() {
  u8 flag = 0, i = 0;
  LED = ~0x01;
  while(1) {
      delay(50000);
      if (!(i++ % 7)) flag = !flag;
      LED = flag ? _crol_(LED,1) : _cror_(LED,1);
  }
}
方法②
void main() {
  u8 v = 1, i = 0;
  while(1) {
  	LED = 0xff ^ 1 << i;
  	i += v;
  	if (i >= 7 || i <= 0) v *= -1;
  	delay(50000);
  }
}

04、蜂鸣器


在图片上认识蜂鸣器:有绿色电路板的一种是无源蜂鸣器,没有电路板而用黑胶封闭的一种是有源蜂鸣器。

蜂鸣器

  • 电磁式蜂鸣器由振荡器、电磁线圈、磁铁、振动膜片及外壳等组成。

接通电源后,振荡器产生的音频信号电流通过电磁线圈,使电磁线圈产生磁场,振动膜片在电磁线圈和磁铁的相互作用下,周期性地振动发声。

  • 压电式蜂鸣器主要由多谐振荡器、压电蜂鸣片、阻抗匹配器及共鸣箱、外壳等组成。

多谐振荡器由晶体管或集成电路构成,当接通电源后(1.5~15V直流工作电压),多谐振荡器起振,输出1.5~2.5kHZ的音频信号,阻抗匹配器推动压电蜂鸣片发声。


在单片机应用的设计上,很多方案都会用到蜂鸣器,大部分都是使用蜂鸣器来做提示或报警,比如按键按下、开始工作、工作结束或是故障等等。

自激蜂鸣器是直流电压驱动的,不需要利用交流信号进行驱动,只需对驱动口输出驱动电平并通过放大电路放大驱动电流就能使蜂鸣器发出声音,非常简单。

改变单片机引脚输出波形的频率,就可以调整控制蜂鸣器音调,产生各种不同音色、音调的声音。

改变输出电平的高低电平占空比,则可以控制蜂鸣器的声音大小

电路图

蜂鸣器电路图

ULN2003简介

ULN2003 是高耐压、大电流达林顿陈列,由七个硅NPN 达林顿管组成。

ULN2003是大电流驱动阵列,多用于单片机、智能仪表、PLC、数字量输出卡等控制电路中。可直接驱动蜂鸣器、继电器等负载 。

驱动程序

#include "reg52.h"

typedef unsigned int u16;

sbit beep = P2^5;

void delay(u16 i) {
  while(i--);
}

void main() {
  while(1) {
    beep = ~beep;  
    delay(100); // 通过修改此延时时间达到不同的发声效果
  }
}

知识拓展

可通过改变输出波形的频率,形成不同音调,模拟出音乐,例如:

#include "reg52.h"
#define RldTmr(fr) 65536 - (11059200 / 12) / (fr << 1)
#define FuDian(n) (n << 1) / 3    //附点n分音符的换算
 
typedef unsigned char UCHAR;
typedef unsigned int UINT;
typedef unsigned long ULONG;
 
sbit BUZZ = P2^5;
 
UINT code noteFreq[] = {    //中音 1-7 和高音 1-7对应的频率列表
    523, 587, 659, 698, 784, 880, 988,
    1047, 1175, 1319, 1397, 1568, 1760, 1976
};
UINT code tmrRld[] = {      //中音 1-7 和高音 1-7对应的定时器重载值
    RldTmr(523), RldTmr(587), RldTmr(659), RldTmr(698), RldTmr(784), RldTmr(880), RldTmr(988),
    RldTmr(1047), RldTmr(1175), RldTmr(1319), RldTmr(1397), RldTmr(1568), RldTmr(1760), RldTmr(1976),
};
UCHAR code musicNote[] = {      //音名
    1, 2, 3, 1,
    1, 2, 3, 1,
    3, 4, 5,
    3, 4, 5,
    5, 6, 5, 4, 3, 1,
    5, 6, 5, 4, 3, 1,
    1, 5, 1,
    1, 5, 1
};
UCHAR code noteDuration[] = {       //音名对应的时值,4表示4分音符,8表示8分音符,16表示16分音符
    4, 4, 4, 4,
    4, 4, 4, 4,
    4, 4, 2,
    4, 4, 2,
    FuDian(8), 16, FuDian(8), 16, 4, 4,
    FuDian(8), 16, FuDian(8), 16, 4, 4,
    4, 4, 2,
    4, 4, 2
};
bit enable = 1, tmrFlg = 0;
UCHAR T0RH = 0XFF, T0RL = 0X00;
 
void Delay(UINT n);
void PlayMusic(UCHAR speed);    //固定标准为4分音符的速度:例如speed = 108 表示一分钟扫过108个4分音符
 
void main() {
    EA = 1;
    TMOD = 0X01;
    TH0 = T0RH;
    TL0 = T0RL;
    ET0 = 1;
    TR0 = 1;
    while (1) {
        PlayMusic(72);
        Delay(40000u);
    }
}
 
void Delay(UINT n) {
    while(n--);
}
 
void PlayMusic(UCHAR speed) {
    UCHAR i;
    UCHAR idx;
    UINT cnt = 0;
    UINT durationCnt = 0;	//当前音符的时值对应的定时器计数
    UINT soundCnt = 0;		//当前音符的发声时值对应的计数值
    for (i = 0; i < sizeof (musicNote); ) {
        while (!tmrFlg) ;
        tmrFlg = 0;
        if (cnt == 0) {
            idx = musicNote[i] - 1;
            T0RH = tmrRld[idx] >> 8;
            T0RL = tmrRld[idx];
            durationCnt = (ULONG)240 * (ULONG)noteFreq[idx] / ((ULONG)noteDuration[i] * (ULONG)speed);
            soundCnt = durationCnt - (durationCnt >> 2);	//当前音符时值的前3/4发声,后1/4静音
            enable = 1;
            cnt++;
        } else {
            if (cnt == durationCnt) {
                cnt = 0;
                i++;
            } else {
                cnt++;
                if (cnt == soundCnt) {
                    enable = 0;
                }
            }
        }
    }
}
 
void InterruptTmr0() interrupt 1 {
    TH0 = T0RH;
    TL0 = T0RL;
    tmrFlg = 1;
    if (enable)
        BUZZ = ~BUZZ;
    else
        BUZZ = 1;
}

05、静态数码管


数码管是如何显示出字符

显示器及其接口

​ 单片机系统中常用的显示器有:发光二极管LED(Light Emitting Diode)显示器、液晶LCD(Liquid Crystal Display)显示器、TFT液晶显示器等。LED显示器有两种显示结构:段显示(7段、米字型等)和点阵显示(5×8、8×8点阵等)。

LED数码管根据LED的不同接法可以分为2类:共阴共阳

数码管共阴与共阳

​ 使用LED显示器时,要注意区分这两种不同的接法。为了显示数字或字符,必须对数字或字符进行编码。七段数码管加上一个小数点,共计8段。因此为LED显示器提供的编码正好是一个字节。我们实验板用共阴LED显示器,根据电路连接图显示16进制数的编码已列在下表。

码值显示值码值显示的数据
0x3f00x7f8
0x0610x6f9
0x5b20x77A
0x4f30x7cB
0x6640x39C
0x6d50x5eD
0x7d60x79E
0x0770x71F
0x00无显示

数码管静态显示原理

​ LED显示器工作方式有两种:静态显示方式和动态显示方式。静态显示的特点是每个数码管的段选必须接一个8位数据线来保持显示的字形码。当送入一次字形码后,显示字形可一直保持,直到送入新字形码为止。这种方法的优点是占用CPU时间少,显示便于监测和控制。缺点是硬件电路比较复杂,成本较高。

开发板数码管电路图

数码管电路图

74H573锁存器的使用

74H573锁存器

  1. OE为使能端,当它为低电平的时候, 锁存器开始工作
  2. VCC和GND为电源和地端
  3. LE为锁存端,当LE为高电平的时候,Q0Q7都跟D0D7状态一样,当LE为低电平的时候,Q0Q7都锁存数据,无论D0D7怎么变化,Q0~Q7都保持锁存之前的那个状态。

驱动程序

实例一

/*
实验现象:动态数码管从左至右显示0-7
*/
#include "reg52.h"			 //此文件中定义了单片机的一些特殊功能寄存器

typedef unsigned int u16;	  //对数据类型进行声明定义
typedef unsigned char u8;

// 数码管片选信号引脚(3-8译码器)
sbit LSA = P2^2;
sbit LSB = P2^3;
sbit LSC = P2^4;

// 显示0~F的值
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--);	
}

// 数码管动态扫描函数,循环扫描8个数码管显示
void DigDisplay() {
	u8 i;
	for(i = 0; i < 8; i++) {
		//位选,选择点亮的数码管
		LSA = !(i&1);
		LSB = !(i&2);
		LSC = !(i&4);

		// 发送数据给数码管
		P0 = smgduan[i];
		// 间隔一段时间扫描
		delay(100);
		// 消隐
		P0 = 0x00;
	}
}

void main() {	
	while(1) {
		DigDisplay(); //数码管显示函数
	}
}

06、动态数码管


数码管动态显示原理

​ 动态显示的特点是将所有数码管的段选线并联在一起,由位选线控制是哪一位数码管有效。选亮数码管采用动态扫描显示。所谓动态扫描显示即轮流向各位数码管送出字形码和相应的位选,利用发光管的余辉和人眼视觉暂留作用,使人的感觉好像各位数码管同时都在显示。动态显示的亮度比静态显示要差一些,所以在选择限流电阻时应略小于静态显示电路中的。

74HC138译码器的使用

D74HC138D 是一种三通道输入、八通道输出译码器,主要应用于消费类电子产品。

  • 采用 CMOS 工艺
  • 低功耗
  • 工作电压:3.0V—5.0V
  • 封装形式:SOP16

适用于数字电路中的 3—8 译码功能

管脚说明

名称功能说明管脚号
Y 0 ‾ \overline{Y0} Y0 Y 6 ‾ \overline{Y6} Y6 Y 7 ‾ \overline{Y7} Y7数据输出15 — 9,7
A 0 A0 A0 A 2 A2 A2数据输入1 — 3
E 1 ‾ \overline{E1} E1 E 2 ‾ \overline{E2} E2 E 3 E3 E3使能控制4 — 6
V D D VDD VDD逻辑电源16
G N D GND GND逻辑地8

逻辑图

逻辑图

真值表

真值表

驱动程序

/*
实验现象:动态数码管从左至右显示1314520,并循环右移
*/
#include "reg52.h"			  //此文件中定义了单片机的一些特殊功能寄存器

typedef unsigned int u16;	  //对数据类型进行声明定义
typedef unsigned char u8;

// 数码管片选信号引脚(3-8译码器)
sbit LSA = P2^2;
sbit LSB = P2^3;
sbit LSC = P2^4;

// 显示1314520的值
u8 code smgduan[] = {0x06,0x4f,0x06,0x66,0x6d,0x5b,0x3f};

// 休眠函数
void delay(u16 i) {
	while (i--);	
}

// 数码管动态扫描函数,循环扫描8个数码管显示
void DigDisplay() {
	static u8 n = 0, m = 0;
	u8 i;
	for (i = 0; i < 7; i++) {
		//位选,选择点亮的数码管
		LSA = !(i + m & 1);
		LSB = !(i + m & 2);
		LSC = !(i + m & 4);

		// 发送数据给数码管
		P0 = smgduan[i];
		// 间隔一段时间扫描
		delay(100);
		// 消隐
		P0 = 0x00;
	}
	// 间隔一段时间后左移一位数码管
	if (!(++n % 100)) {
		++m;
		n = 0;
		m %= 7;
	}
}

void main() {	
	while (1) {
		DigDisplay(); //数码管显示函数
	}
}

07、独立按钮


按键介绍

轻触开关是一种电子开关,使用时,轻轻按开关按钮就可使开关接通,当松开手时,开关断开。我们使用的开关如下图:

电子按钮

独立按键原理

按键在闭合和断开时,触点会存在抖动现象。

独立按钮电路图

抖动示意图

硬件消抖电路

硬件消抖

驱动程序

/*
实验现象:按 k1 ~ k4 按钮数码管显示1~4的数字
*/
#include "reg52.h"

typedef unsigned char u8;
typedef unsigned int u16;

// 四个独立按钮串口
sbit k1 = P3^1;
sbit k2 = P3^0;
sbit k3 = P3^2;
sbit k4 = P3^3;

// 38译码器串口
sbit LSA = P2^2;
sbit LSB = P2^3;
sbit LSC = P2^4;

// 数码管显示1、2、3、4对应的16进制信号
u8 code smgduan[16] = {0x06,0x5b,0x4f,0x66};
 
// 延时函数
void delay(u16 i) {
  while(i--);
}

// 处理按钮按下函数
void keyPress() {
  static u8 key = 0;
  // 判断按下了哪个按钮
  if (k1 == 0) {
   	delay(1000); // 消抖
	if (k1 == 0) key = 1;
  } else if (k2 == 0) {
    delay(1000); // 消抖
	if (k2 == 0) key = 2;
  } else if (k3 == 0) {
    delay(1000); // 消抖
	if (k3 == 0) key = 3;
  } else if (k4 == 0) {
    delay(1000); // 消抖
	if (k4 == 0) key = 4;
  }
  // 根据按下的按钮显示数字
  if (key) P0 = smgduan[key-1];
  key = 0;
  delay(100);
}

void main() {
  u8 i = 0;
  while(1) {
    keyPress();
  }
}

08、矩阵按钮


矩阵按键的由来

独立按键与矩阵按键

矩阵按键扫描原理

方法一:

逐行扫描:我们可以通过高四位轮流输出低电平来对矩阵键盘进行逐行扫描,当低四位接收到的数据不全为1的时候,说明有按键按下,然后通过接收到的数据是哪一位为0来判断是哪一个按键被按下。

方法二:

行列扫描:我们可以通过高四位全部输出低电平,低四位输出高电平。当接收到的数据,低四位不全为高电平时,说明有按键按下,然后通过接收的数据值,判断是哪一列有按键按下,然后再反过来,高四位输出高电平,低四位输出低电平,然后根据接收到的高四位的值判断是那一行有按键按下,这样就能够确定是哪一个按键按下了。

矩阵按键怎么变成独立按键

驱动程序

/*
实验现象:按矩阵按钮,数码管依次显示0~15的数字
*/
#include "reg52.h"

typedef unsigned char u8;
typedef unsigned int u16;

#define GPIO_DIG P0
#define GPIO_KEY P1

sbit LSA = P2^2;
sbit LSB = P2^3;
sbit LSC = P2^4;

u8 code smgduan[17] = {0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,
                       0x80,0x90,0x88,0x83,0xc6,0xa1,0x86,0x8e}; //共阴

u8 keyValue;

void delay(u16 i) {
  while(i--);
}

void keyDown()
{
  char a = 0;
  GPIO_KEY = 0x0f;
  if (GPIO_KEY != 0x0f) {
    delay(1000);
	  if (GPIO_KEY != 0x0f) {
      switch(GPIO_KEY) {
        case (0x07): keyValue = 0; break;
        case (0x0b): keyValue = 1; break;
        case (0x0d): keyValue = 2; break;
        case (0x0e): keyValue = 3; break;
        default: break;
      }
      GPIO_KEY = 0xf0;
      switch(GPIO_KEY) {
        case (0x70): keyValue = keyValue; break;
        case (0xb0): keyValue += 4; break;
        case (0xd0): keyValue += 8; break;
        case (0xe0): keyValue += 12; break;
        default: break;
      }
      while(a < 50 && GPIO_KEY != 0xf0) {
        delay(5);
        a++;
      }
    }
  }
}

void lightedDig() {
  switch(keyValue) {
    case 0:
    case 1:
    case 2:
    case 3:
    case 4:
    case 5:
    case 6:
    case 7:
    case 8:
    case 9:
      LSA = 1; LSB = 1; LSC = 1;
      GPIO_DIG = ~smgduan[keyValue];
      break;
    default:
      GPIO_DIG = ~smgduan[1];
      LSA = 1; LSB = 1; LSC = 1;
      delay(100);
      GPIO_DIG = ~smgduan[keyValue % 10];
      LSA = 0; LSB = 1; LSC = 1;
      delay(100);
      break;
  }
}

void main() {
  while(1) {
    keyDown();
    // GPIO_DIG = ~smgduan[keyValue];
    lightedDig();
  }
}

09、单片机IO扩展-74HC595


74HC595芯片介绍

8 位串行输入/输出或者并行输出移位寄存器,具有高阻关断状态,三态。

595 是具有 8 位移位寄存器和一个存储器,三态输出功能。

移位寄存器和存储器分别是两个时钟。

数据在 SCHcp 的上升沿输入,在 STcp 的上升沿进入的存储寄存器中去。如果两个时钟连在一起,则移位寄存器总是比存储寄存器早一个脉冲。

移位寄存器有一个串行移位输入(Ds),和一个串行输出(Q7’),和一 个异步的低电平复位,存储寄存器有一个并行 8 位的,具备三态的总线输出,当 使能 OE 时(为低电平),存储寄存器的数据输出到总线。

特点
  • 8 位串行输入
  • 8 位串行或并行输出
  • 存储状态寄存器,三种状态
  • 输出寄存器可以直接清除
  • 100MHz 的移位频率
输出能力
  • 并行输出,总线驱动
  • 串行输出;标准
  • 中等规模集成电路
应用
  • 串行到并行的数据转换
  • Remote control holding register.

串转并模块

引脚说明

符号引脚描述
QA ~ QH15、1 ~ 7并行数据输出
GND8
QH‘9串行数据输出
SRCLR10主复位(低电平)
SRCLK11移位寄存器时钟输入
RCLK12存储寄存器时钟输入
OE13输出有效(低电平)
SER14串行数据输入
VCC16电源

功能表

输入输出功能
SRCLKRCLKOESRCLRSERQH‘Qn
××L×LNCMR为低电平时仅仅影响移位寄存器。
×LL×LL空移位寄存器到输出寄存器。
××HL×LZ清空移位寄存器,并行输出为高阻状态。
×LHHQG’NC逻辑高电平移入移位寄存器状态0,包含所有的移位寄存器状态移入, 例如,以前的状态G(内部 QG')出现在串行输出位。
×LH×NCQn'移位寄存器的内容到达保持寄存器并从并口输出。
LH×QG’Qn'移位寄存器内容移入,先前的移位寄存器的内容到达保持寄存器并输出。
注:H = 高电平状态、L = 低电平状态、↑ = 上升沿、↓ = 下降沿、Z = 高阻、NC = 无变化、× = 无效

​ 当SRCLR为高电平,OE为低电平时,数据在SRCLK上升沿进入移位寄存器,在RCLK上升沿输出到并行端口。

74595 的数据端:

  • QAQH:八位并行输出端,可以直接控制数码管的 8 个段。
  • QH':级联输出端。我将它接下一个 595 的 SER 端。
  • SER:串行数据输入端。

74595 的控制端说明:

  • SRCLR(10 脚):低电平时将移位寄存器的数据清零。通常将它接 VCC

  • SRCLK(11 脚):上升沿时数据寄存器的数据移位。QA–>QB–>QC–>…–>QH;下降沿移位寄存器数据不变。(脉冲宽度:5V 时,大于几十纳秒就行了。)

  • RCLK(12 脚):上升沿时移位寄存器的数据进入数据存储寄存器,下降沿时存储寄 存器数据不变。通常将RCK置为低点平,当移位结束后,在RCK端产生一 个正脉冲(5V 时,大于几十纳秒就行了。我通常都选微秒级),更新显示数据。

  • OE(13 脚):高电平时禁止输出(高阻态)。如果单片机的引脚不紧张,用一个引 脚控制它,可以方便地产生闪烁和熄灭效果。比通过数据端移位控制要省时省力。

注:74164 和 74595 功能相仿,都是 8 位串行输入转并行输出移位寄存器。74164 的驱动电流(25mA)比 74595(35mA)的要小,14 脚封装,体积也小一些。 74595 的主要优点是具有数据存储寄存器,在移位的过程中,输出端的数据可以 保持不变。这在串行速度慢的场合很有用处,数码管没有闪烁感。 与 164 只有数据清零端相比,595 还多有输出端时能/禁止控制端,可以使输出为 高阻态。 另外,据网上报价,贴片 164 每只 1 元钱,贴片 595 0.8 元/只。

程序说明

​ 每当 spi_shcp上升沿到来时,spi_ds引脚当前电平值在移位寄存器中左移 一位,在下一个上升沿到来时移位寄存器中的所有位都会向左移一位, 同时 QH'也会串行输出移位寄存器中高位的值,这样连续进行 8 次,就可以把数组中每一个数(8 位的数)送到移位寄存器; 然后当spi_stcp上升沿到来时,移位寄存器的值将会被锁存到锁存器里, 并从 QA~QH 引脚输出

驱动程序

/*
实验现象:点亮点阵左下角的LED
*/
#include "reg51.h"			 //此文件中定义了单片机的一些特殊功能寄存器
#include "intrins.h"

typedef unsigned int u16;
typedef unsigned char u8;


// 定义使用的IO口
sbit SRCLK = P3^6;		// 移位寄存器时钟输入口
sbit RCLK  = P3^5;		// 存储寄存器时钟输入口
sbit SER   = P3^4;		// 串行数据输入
sbit LED   = P0^7;		// 点阵P0口,后面数字对应点阵列号
	   
// 延时函数
void delay(u16 i) {
	while(i--);	
}

// 向74H595发送一个字节的数据
void Hc595SendByte(u8 dat) {
	u8 a;

	SRCLK = 1;
	RCLK = 1;

	for(a = 0; a < 8; a++) {	// 发送8位数
	  SER = dat >> 7;			// 从最高位开始发送
	  dat <<= 1;

	  SRCLK = 0;				// 发送时序
	  _nop_();					// 延时两个机器周期
	  _nop_();
	  SRCLK = 1;	
	}
	RCLK = 0;
	_nop_();					// 延时两个机器周期
	_nop_();
	RCLK = 1;
}

void main() {
	LED = 0;  //使第一列为低电平。
	while(1) {
	   Hc595SendByte(0xfe,0x01);
	}
}

10、LED点阵


8×8LED点阵的原理

什么是点阵

点阵

点阵简介

点阵电路图

8×8LED点阵的动态显示

点阵的动态显示

驱动程序

实例一
/*
实验现象:8*8LED点阵第一列从下到上流动显示
*/
#include "reg51.h"			 //此文件中定义了单片机的一些特殊功能寄存器
#include "intrins.h"

typedef unsigned char u8;
typedef unsigned int u16;

// 定义使用的IO口
sbit SRCLK = P3^6;		// 移位寄存器时钟输入口
sbit RCLK  = P3^5;		// 存储寄存器时钟输入口
sbit SER   = P3^4;		// 串行数据输入
sbit LED   = P0^7;		// 点阵P0口,后面数字对应点阵列号
	   
// 延时函数
void delay(u16 i) {
	while(i--);	
}

// 向74H595发送一个字节的数据
void Hc595SendByte(u8 dat) {
	u8 a;

	SRCLK = 1;
	RCLK = 1;

	for(a = 0; a < 8; a++) {	// 发送8位数
	  SER = dat >> 7;			// 从最高位开始发送
	  dat <<= 1;

	  SRCLK = 0;				// 发送时序
	  _nop_();					// 延时两个机器周期
	  _nop_();
	  SRCLK = 1;	
	}
	RCLK = 0;
	_nop_();					// 延时两个机器周期
	_nop_();
	RCLK = 1;
}

void main() {
	u8 ledNum = 0x01; 			// 使第一行为高电平
	LED = 0;					// 使第一列为低电平
	while(1) {
		Hc595SendByte(ledNum);
		ledNum = _crol_(ledNum, 1); // 循环左移
		delay(50000);
	}		
}
实例二
/*
实验现象:LED点阵显示爱心图像
*/
#include "reg51.h"			 //此文件中定义了单片机的一些特殊功能寄存器
#include "intrins.h"

typedef unsigned char u8;
typedef unsigned int u16;

// 定义使用的IO口
sbit SRCLK = P3^6;          // 移位寄存器时钟输入口
sbit RCLK  = P3^5;          // 存储寄存器时钟输入口
sbit SER   = P3^4;          // 串行数据输入

// 心形显示需要点亮的图形点阵数据
u8 ledduan[] = {0x38,0x44,0x42,0x21,0x42,0x44,0x38,0x00},
    ledwei[] = {0x7f,0xbf,0xdf,0xef,0xf7,0xfb,0xfd,0xfe};
	   
// 延时函数
void delay(u16 i) {
	while(i--);	
}

// 向74H595发送一个字节的数据
void Hc595SendByte(u8 dat) {
    u8 a;

    SRCLK = 0;
    RCLK = 0;

    for(a = 0; a < 8; a++) {    // 发送8位数
        SER = dat >> 7;         // 从最高位开始发送
        dat <<= 1;
 
        SRCLK = 1;              // 发送时序
        _nop_();                // 延时两个机器周期
        _nop_();
        SRCLK = 0;
    }
 
    RCLK = 1;
    _nop_();                    // 延时两个机器周期
    _nop_();
    RCLK = 0;
}
 
void main() {
    u8 i;
    while(1)
        for(i = 0; i < 8; i++) {
            P0 = ledwei[i];               //位选
            Hc595SendByte(ledduan[i]);    //发送段选数据
            delay(100);                   //延时
            Hc595SendByte(0x00);          //消隐
        }
}
实例三
/*
实验现象:点阵闪烁显示“洁”、心形图案
*/
#include "reg51.h"           //此文件中定义了单片机的一些特殊功能寄存器
#include <intrins.h>

typedef unsigned int u16;     //对数据类型进行声明定义
typedef unsigned char u8;

// 定义使用的IO口
sbit SRCLK = P3^6;          // 移位寄存器时钟输入口
sbit RCLK  = P3^5;          // 存储寄存器时钟输入口
sbit SER   = P3^4;          // 串行数据输入

// 点阵段选数据
u8 ledduan[][8] = {{0x40,0x57,0xF5,0x57,0x40,0x00,0x4A,0x91}, // 洁
				   {0x78,0xFC,0x3E,0x1F,0x1F,0x3E,0xFC,0x78}}, // 心
// 点阵位选数据
   ledwei[]  = {0xFE,0xFD,0xFB,0xF7,0xEF,0xDF,0xBF,0x7F};

void delay(u16 i) {
    while(i--); 
}

// 向74H595发送一个字节的数据
void Hc595SendByte(u8 dat) {
    u8 a;

    SRCLK = 0;
    RCLK = 0;

    for (a = 0; a < 8; a++) {    // 发送8位数
        SER = dat >> 7;         // 从最高位开始发送
        dat <<= 1;
 
        SRCLK = 1;              // 发送时序
        _nop_();                // 延时两个机器周期
        _nop_();
        SRCLK = 0;
    }
 
    RCLK = 1;
    _nop_();                    // 延时两个机器周期
    _nop_();
    RCLK = 0;
}

void main() {           
    u8 i, j = 0, n = 1;
    while(1) {
        for(i = 0; i < 8; i++) {
            P0 = ledwei[i];                 // 位选
            Hc595SendByte(ledduan[j][i]);   // 发送段选数据
            delay(100);                     // 延时
            Hc595SendByte(0x00);            // 消隐
        }
		if (n % 100 == 0) {                 // 间隔一段时间后切换显示的图形
		  n = 0;
		  j = !j;
		}
		n++;   
    }       
}

11、中断系统


中断的概念

CPU在处理某一事件A时,发生了另一事件B请求CPU迅速去处理(中断发生);

CPU暂时中断当前的工作,转去处理事件B(中断响应中断服务);

待CPU将事件B处理完毕后,再回到原来事件A被中断的地方继续处理事件A(中断返回),这一过程称为中断

51单片机的中断系统结构

中断系统结构

引起CPU中断的根源,称为中断源。中断源向CPU提出的中断请求。CPU暂时中断原来的事务A,转去处理事件B。对事件B处理完毕后,再回到原来被中断的地方(即断点),称为中断返回。实现上述中断功能的部件称为中断系统

随着计算机技术的应用,人们发现中断技术不仅解决了快速主机与慢速I/O设备的数据传送问题**,**而且还具有如下优点:

  • 分时操作:CPU可以分时为多个I/O设备服务,提高了计算机的利用率**;**
  • 实时响应:CPU能够及时处理应用系统的随机事件,系统的实时性大大增强;
  • 可靠性高:CPU具有处理设备故障及掉电等突发性事件能力,从而使系统可靠性提高。

89C51/52的中断系统有5个中断源2个优先级,可实现二级中断嵌套

中断系统原理图

  1. 可由IT0(TCON.0)选择其为低电平有效还是下降沿有效。当CPU检测到P3.2引脚上出现有效的中断信号时,中断标志IE0(TCON.1)置1,向CPU申请中断。
  2. 可由IT1(TCON.2)选择其为低电平有效还是下降沿有效。当CPU检测到P3.3引脚上出现有效的中断信号时,中断标志IE1(TCON.3)置1,向CPU申请中断。
  3. TF0(TCON.5),片内定时/计数器T0溢出中断请求标志。当定时/计数器T0发生溢出时,置位TF0,并向CPU申请中断。
  4. TF1(TCON.7),片内定时/计数器T1溢出中断请求标志。当定时/计数器T1发生溢出时,置位TF1,并向CPU申请中断。
  5. RI(SCON.0)或**TI**(SCON.1),串行口中断请求标志。当串行口接收完一帧串行数据时置位RI或当串行口发送完一帧串行数据时置位TI,向CPU申请中断。

中断允许控制

CPU对中断系统所有中断以及某个中断源的开放和屏蔽是由中断允许寄存器IE控制的。

76543210
字节地址:A8HEAESET1EX1ET0EX0IE
  • EX0(IE.0),外部中断0允许位;
  • ET0(IE.1),定时/计数器T0中断允许位;
  • EX1(IE.2),外部中断0允许位;
  • ET1(IE.3),定时/计数器T1中断允许位;
  • ES(IE.4),串行口中断允许位;
  • EA (IE.7), CPU中断允许(总允许)位。

中断请求标志

TCON的中断标志

76543210
字节地址:88HTF1TR1TF0TR0IE1IT1IE0IT0TCON
  • IT0(TCON.0),外部中断0触发方式控制位。

    • 当IT0 = 0时,为电平触发方式。

    • 当IT0 = 1时,为边沿触发方式(下降沿有效)。

  • IE0(TCON.1),外部中断0中断请求标志位。

  • IT1(TCON.2),外部中断1触发方式控制位。

  • IE1(TCON.3),外部中断1中断请求标志位。

  • TF0(TCON.5),定时/计数器T0溢出中断请求标志位。

  • TF1(TCON.7),定时/计数器T1溢出中断请求标志位。

同一优先级中的中断申请不止一个时,则有中断优先权排队问题。同一优先级的中断优先权排队,由中断系统硬件确定的自然优先级形成,其排列如所示:


各中断源响应优先级及中断服务程序入口表

中断源中断标志中断服务程序入口优先级入口
外部中断0(INT0)IE00003H
定时/计数器0(T0)TF0000BH
外部中断1(INT1)IE10013H
定时/计数器1(T1)TF1001BH
串行口RI或TI0023H

中断源

中断源符号名称中断引起原因中断号
INT0外部中断0P3.2引脚低电平或下降沿信号0
T0定时器0中断定时/计数器0计数回0溢出1
INT1外部中断1P3.3引脚低电平或下降沿信号2
T1定时器1中断定时/计数器1计数回0溢出3
TI / RI串行口中断串行通信完成一帧数据发送或接收引起中断4

51单片机中断优先级的三条原则

  • CPU同时接收到几个中断时,首先响应优先级别最高的中断请求
  • 正在进行的中断过程不能被新的同级或低优先级的中断请求所中断
  • 正在进行的低优先级中断服务,能被高优先级中断请求所中断

​ 为了实现上述后两条原则,中断系统内部设有两个用户不能寻址的优先级状态触发器。其中一个置1,表示正在响应高优先级的中断,它将阻断后来所有的中断请求;另一个置1,表示正在响应低优先级中断,它将阻断后来所有的低优先级中断请求。

中断处理过程

中断响应条件

  • 中断源有中断请求;

  • 此中断源的中断允许位为1;

  • CPU开中断(即EA=1)。

以上三条同时满足时,CPU才有可能响应中断。


使用中断,程序员需要做什么?

  • 你想使用的中断是哪个?选择相应的中断号;
  • 你所希望的触发条件是什么?
  • 你希望在中断之后干什么?

以外部中断0为例,主程序中需要有以下代码:

EA = 1;			//打开总中断开关
EX0 = 1;		//开外部中断0
IT0 = 0/1;		//设置外部中断的触发方式

中断服务函数:

void int0 () interrupt 0 using  1 {
	do anything that you want
}

驱动程序

外部中断0实例
/*
实验现象:K3按钮控制第一个LED的开关
*/
#include "reg52.h"

typedef unsigned int u16;
typedef unsigned char u8;

sbit LED = P2^0; // 定义P20口为LED
sbit K3  = P3^2; // 定义按钮K3
 
// 延时函数
void delay(u16 n) {
  while(n--);
}

// 初始化外部中断0的响应条件
void Int0Init() {
  IT0 = 1; // 触发方式设置为“边沿触发”方式(下降沿)
  EX0 = 1; // 打开INT0的中断允许。
  EA  = 1; // 打开总中断
}

void main() {
  Int0Init(); // 设置外部中断0
  while(1);
}

// 外部中断0的中断函数
void Int0() interrupt 0 {
  delay(1000); // 延时消抖
  if (K3 == 0)
    LED = ~LED;
}
外部中断1实例
/*
实验现象:K4按钮控制第一个LED的开关
*/
#include "reg52.h"

typedef unsigned char u8;
typedef unsigned int u16;

sbit LED = P2^0; // 定义P20口为LED
sbit K4  = P3^3; // 定义按钮K4
 
// 延时函数
void delay(u16 i) {
  while(i--);
}

// 初始化外部中断1的响应条件
void Int1Init() {
  IT1 = 1; // 触发方式设置为“边沿触发”方式(下降沿)
  EX1 = 1; // 打开INT1的中断允许。
  EA  = 1; // 打开总中断
}

void main() {
  Int1Init(); // 设置外部中断1
  while(1);
}

// 外部中断1的中断函数
void Int1() interrupt 2 {
  delay(1000); // 延时消抖
  if (K4 == 0)
    LED = ~LED;
}

12、定时器和计数器


CPU时序相关知识

  • 振荡周期:为单片机提供定时信号的振荡源的周期(晶振周期或外加振荡周期)
  • 状态周期:2个振荡周期为1个状态周期,用S表示。振荡周期又称S周期或时钟周期。
  • 机器周期:1个机器周期含6个状态周期,12个振荡周期。
  • 指令周期:完成1条指令所占用的全部时间,它以机器周期为单位。

例如:外接晶振为12MHz时,51单片机相关周期的具体值为:

  • 振荡周期 = 1/12us;
  • 状态周期 = 1/6us;
  • 机器周期 = 1us;
  • 指令周期 = 1~4us;

定时器/计数器基础知识

  • 51单片机有两组定时器/计数器,因为既可以定时,又可以计数,故称之为定时器/计数器
  • 定时器/计数器和单片机的CPU是相互独立的。定时器/计数器工作的过程是自动完成的,不需要CPU的参与。
  • 51单片机中的定时器/计数器是根据机器内部的时钟或者是外部的脉冲信号对寄存器中的数据加1
  • 有了定时器/计数器之后,可以增加单片机的效率,一些简单的重复加1的工作可以交给定时器/计数器处理。CPU转而处理一些复杂的事情。同时可以实现精确定时作用。

定时/计数器的工作原理

定时/计数器实质上是一个加1计数器。它随着计数器的输入脉冲进行自加1,也就是每来一个脉冲,计数器就自动加1,,当加到计数器为全1时,再输入一个脉冲就使计数器回零,且计数器的溢出使相应的中断标志位置1,向CPU发出中断请求(定时/计数器中断允许时)。如果定时/计数器工作于定时模式,则表示定时时间已到;如果工作于计数模式,则表示计数值已满。

可见,由溢出时计数器的值减去计数初值才是加1计数器的计数值

51单片机定时器结构

定时/计数器的实质是加1计数器(16位),由高8位和低8位两个寄存器THxTLx组成。TMOD是定时/计数器的工作方式寄存器,确定工作方式和功能;TCON是控制寄存器,控制T0T1的启动和停止及设置溢出标志。

标志位功能示意图

定时/计数器的控制

51单片机定时/计数器的工作由两个特殊功能寄存器控制。TMOD用于设置其工作方式;TCON用于控制其启动和中断申请。

1、工作方式寄存器TMOD

工作方式寄存器TMOD用于设置定时/计数器的工作方式,低四位用于T0,高四位用于T1。其格式如下:

76543210
字节地址:89HGATEC/TM1M0GATEC/TM1M0TMOD

GATE是门控位, GATE=0时,用于控制定时器的启动是否受外部中断源信号的影响。只要用软件使TCON中的TR0或TR1为1,就可以启动定时/计数器工作;GATA=1时,要用软件使TR0或TR1为1,同时外部中断引脚INT0/1也为高电平时,才能启动定时/计数器工作。即此时定时器的启动条件,加上了INT0/1引脚为高电平这一条件。

C/T:定时/计数模式选择位。C/T =0为定时模式;C/T = 1为计数模式。

M1M0:工作方式设置位。定时/计数器有四种工作方式。

定时/计数器工作方式设置表
M1M0工作方式说明
00方式013位定时/计数器
01方式116位定时/计数器
10方式28位自动重装定时/计数器
11方式3T0分成两个独立的8位定时/计数器;T1此方式停止计数
2、控制寄存器TCON

TCON的低4位用于控制外部中断,已在前面介绍。TCON的高4位用于控

制定时/计数器的启动和中断申请。其格式如下:

76543210
字节地址:88HTF1TR1TF0TR0TCON

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。所以,用软件可控制定时/计数器的启动与停止。

TF0(TCON.5):T0溢出中断请求标志位,其功能与TF1类同。

TR0(TCON.4):T0运行控制位,其功能与TR1类同。

定时/计数器的工作方式

方式0

方式0为13位计数,由TL0的低5位(高3位未用)和TH0的8位组成。TL0的低5位溢出时向TH0进位,TH0溢出时,置位TCON中的TF0标志,向CPU发出中断请求。

方式0

定时器模式时有:N=t / Tcy

计数初值计算的公式为:X = 213-N

定时器的初值还可以采用计数个数直接取补法获得。计数模式时,计数脉冲是T0引脚上的外部脉冲。

门控位GATE具有特殊的作用。当GATE=0时,经反相后使或门输出为1,此时仅由TR0控制与门的开启,与门输出1时,控制开关接通,计数开始;当GATE=1时,由外中断引脚信号控制或门的输出,此时控制与门的开启由外中断引脚信号和TR0共同控制。当TR0=1时,外中断引脚信号引脚的高电平启动计数,外中断引脚信号引脚的低电平停止计数。这种方式常用来测量外中断引脚上正脉冲的宽度。

方式1

方式1的计数位数是16位,由TL0作为低8位,TH0作为高8位,组成了16位加1计数器 。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kQ5JkjD9-1635008141075)(images/image-20211023194813224.png)]

计数个数与计数初值的关系为:X = 216-N

方式2

方式2为自动重装初值的8位计数方式。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-efF9sJMV-1635008141075)(images/image-20211023194836303.png)]

计数个数与计数初值的关系为:X = 28-N

工作方式2特别适合于用作较精确的脉冲信号发生器。

方式3

方式3只适用于定时/计数器T0,定时器T1处于方式3时相当于TR1=0,停止计数

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-X6iHbZXY-1635008141076)(images/image-20211023194855725.png)]

工作方式3将T0分成为两个独立的8位计数器TL0和TH0 。

使用定时器,该做哪些工作

初始化程序应完成如下工作:

  • 对TMOD赋值,以确定T0和T1的工作方式。
  • 计算初值,并将其写入TH0、TL0或TH1、TL1。
  • 中断方式时,则对EA赋值,开放定时器中断。
  • 使TR0或TR1置位,启动定时/计数器定时或计数。
中断源符号名称中断引起原因中断号
INT0外部中断0P3.2引脚低电平或下降沿信号0
T0定时器0中断定时/计数器0计数回0溢出1
INT1外部中断1P3.3引脚低电平或下降沿信号2
T1定时器1中断定时/计数器1计数回0溢出3
TI / RI串行口中断串行通信完成一帧数据发送或接收引起中断4

计数器初值的计算

  • 机器周期也就是CPU完成一个基本操作所需要的时间。
  • 机器周期 = 1 / 单片机的时钟频率。
  • 51单片机内部时钟频率是外部时钟的12分频。也就是说当外部晶振的频率输入到单片机里面的时候要进行12分频。比如说你用的是12MHZ的晶振,那么单片机内部的时钟频率就是12 / 12MHZ,当你使用12MHZ的外部晶振的时候。机器周期 = 1 / 1M = 1us。
  • 而我们定时1ms的初值是多少呢,1ms / 1us = 1000。也就是要计数1000个数,初值 = 65535 - 1000 + 1(因为实际上计数器计数到66636才溢出)= 64536 = FC18H

驱动程序

定时器0中断实例
/*
实验现象:第一个LED灯闪烁
*/
#include "reg52.h"
#include "intrins.h"

typedef unsigned int u16;
typedef unsigned char u8; 

sbit LED = P2^0; // 定义P20口为LED

// 定时器0初始化
void Timer0Init() {
  TMOD |= 0x01; // 选择为定时器0模式,工作方式1,仅用TR0打开启动。
  TH0   = 0xFC; // 给定时器赋初值,定时1ms
  TL0   = 0x18;
  ET0   = 1;    // 打开定时器0中断允许
  EA    = 1;    // 打开总中断
  TR0   = 1;    // 打开定时器
}

void main() {
  Timer0Init();
  while(1);
}

void Timer0() interrupt 1 {
  static u16 i;
  TH0   = 0xFC;     // 给定时器赋初值,定时1ms
  TL0   = 0x18;
  i++;
  if (i == 1000) {  // 1s
    i = 0;
    LED = ~LED;
  }
}
定时器1中断实例
/*
实验现象:数码管依次显示0、1、2、3、4、5、6、7、8、9、A、B、C、D、E、F
*/
#include "reg52.h"
#include "intrins.h"

typedef unsigned int u16;
typedef unsigned char u8; 

u8 code smgduan[17] = {0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,
					             0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71}; //显示0~F的值

// 定时器1初始化
void Timer1Init() {
  TMOD |= 0x01; // 选择为定时器1模式,工作方式1,仅用TR1打开启动。
  TH1   = 0xFC; // 给定时器赋初值,定时1ms
  TL1   = 0x18;
  ET1   = 1;    // 打开定时器1中断允许
  EA    = 1;    // 打开总中断
  TR1   = 1;    // 打开定时器
}

void main() {
  Timer1Init();
  while(1);
}

void Timer1() interrupt 3 {
  static u16 i, n;
  TH1 = 0xFC;         // 给定时器1赋初值,定时1ms
  TL1 = 0x18;
  if (++i == 5000) { // 1s
    P0 = smgduan[n++ % 16];
    i = 0;
  }
}

13、串口通信


计算机串行通信基础

  • 随着多微机系统的广泛应用和计算机网络技术的普及,计算机的通信功能愈来愈显得重要。计算机通信是指计算机与外部设备或计算机与计算机之间的信息交换

  • 通信有并行通信串行通信两种方式。在多微机系统以及现代测控系统中信息的交换多采用串行通信方式。

  • 计算机通信是将计算机技术和通信技术的相结合,完成计算机与外部设备或计算机与计算机之间的信息交换 。可以分为两大类:并行通信与串行通信。

  • 并行通信通常是将数据字节的各位用多条数据线同时进行传送 。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-j0ooLkml-1635008141077)(images/image-20211023203705306.png)]

    并行通信控制简单、传输速度快;由于传输线较多,长距离传送时成本高且接收方的各位同时接收存在困难。

串行通信的基本概念

  • 串行通信是将数据字节分成一位一位的形式在一条传输线上逐个地传送。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ziOJgVR9-1635008141078)(images/image-20211023203759195.png)]

串行通信的特点:传输线少,长距离传送时成本低,且可以利用电话网等现成的设备,但数据的传送控制比并行通信复杂。

异步通信与同步通信
1、异步通信

异步通信是指通信的发送与接收设备使用各自的时钟控制数据的发送和接收过程。为使双方的收发协调,要求发送和接收设备的时钟尽可能一致。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5IPyDWAZ-1635008141078)(images/image-20211023221833173.png)]

异步通信是以字符(构成的帧)为单位进行传输,字符与字符之间的间隙(时间间隔)是任意的,但每个字符中的各位是以固定的时间传送的,即字符之间不一定有“位间隔”的整数倍的关系,但同一字符内的各位之间的距离均为“位间隔”的整数倍。

异步通信的数据格式:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mecpYQY6-1635008141079)(images/image-20211023221953325.png)]

异步通信的特点:不要求收发双方时钟的严格一致,实现容易,设备开销较小,但每个字符要附加2~3位用于起止位,各帧之间还有间隔,因此传输效率不高。

2、同步通信

​ 同步通信时要建立发送方时钟对接收方时钟的直接控制,使双方达到完全同步。此时,传输数据的位之间的距离均为“位间隔”的整数倍,同时传送的字符间不留间隙,即保持位同步关系,也保持字符同步关系。发送方对接收方的同步可以通过两种方法实现。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7NekdvNM-1635008141080)(images/image-20211023222210090.png)]

串行通信的传输方向
  • 单工是指数据传输仅能沿一个方向,不能实现反向传输。
  • 半双工是指数据传输可以沿两个方向,但需要分时进行。
  • 全双工是指数据可以同时进行双向传输。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xCNJGRYY-1635008141081)(images/image-20211023222506955.png)]

串行通信常见的错误校验

1、奇偶校验

​ 在发送数据时,数据位尾随的1位为奇偶校验位(1或0)。奇校验时,数据中“1”的个数与校验位“1”的个数之和应为奇数;偶校验时,数据中“1”的个数与校验位“1”的个数之和应为偶数。接收字符时,对“1”的个数进行校验,若发现不一致,则说明传输数据过程中出现了差错。

2、代码和校验

​ 代码和校验是发送方将所发数据块求和(或各字节异或),产生一个字节的校验字符(校验和)附加到数据块末尾。接收方接收数据同时对数据块(除校验字节外)求和(或各字节异或),将所得的结果与发送方的“校验和”进行比较,相符则无差错,否则即认为传送过程中出现了差错。

3、循环冗余校验

​ 这种校验是通过某种数学运算实现有效信息与校验位之间的循环校验,常用于对磁盘信息的传输、存储区的完整性校验等。这种校验方法纠错能力强,广泛应用于同步通信中。

传输速率

比特率每秒钟传输二进制代码的位数,单位是:位/秒(bps)。如每秒钟传送240个字符,而每个字符格式包含10位(1个起始位、1个停止位、8个数据位),这时的比特率为:10位×240个/秒 = 2400 bps

传输距离与传输速率的关系

​ 串行接口或终端直接传送串行信息位流的最大距离与传输速率及传输线的电气特性有关。当传输线使用每0.3m(约1英尺)有50PF电容的非平衡屏蔽双绞线时,传输距离随传输速率的增加而减小。当比特率超过1000 bps 时,最大传输距离迅速下降,如9600 bps 时最大距离下降到只有76m(约250英尺)

串行通信接口标准

RS-232C接口

RS-232C是EIA(美国电子工业协会)1969年修订RS-232C标准。RS-232C定义了数据终端设备(DTE)与数据通信设备(DCE)之间的物理接口标准。

1、机械特性

​ RS-232C接口规定使用25针连接器,连接器的尺寸及每个插针的排 列位置都有明确的定义。(阳头)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7YqbKqGw-1635008141082)(images/image-20211023223346650.png)]

2、功能特性

​ RS-232C标准接口主要引脚定义

插针序号信号名称功能信号方向
1PGND保护接地
2(3)TXD发送数据(串行输出)DTE→DCE
3(2)RXD接收数据(串行输入)DTE←DCE
4(7)RTS请求数据DTE→DCE
5(8)CTS允许发送DTE←DCE
6(6)DSRDCE就绪(数据建立就绪)DTE←DCE
7(5)SGND信号接地
8(1)DCD载波检测DTE←DCE
20(4)DTRDTE就绪(数据中断准备就绪)DTE→DCE
22(9)RI振铃指示DTE←DCE
3、过程特性

​ 过程特性规定了信号之间的时序关系,以便正确地接收和发送数据 。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BGOhsP9N-1635008141082)(images/image-20211023224230745.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fjdy7sZZ-1635008141083)(images/image-20211023224444306.png)]

RS-232C电平与TTL电平转换驱动电路

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jHTBi4gT-1635008141084)(images/image-20211023225041365.png)]

采用RS-232C接口存在的问题
1、传输距离短,传输速率低

​ RS-232C总线标准受电容允许值的约束,使用时传输距离一般不要超过15米(线路条件好时也不超过几十米)。最高传送速率为20Kbps。

2、有电平偏移

​ RS-232C总线标准要求收发双方共地。通信距离较大时,收发双方的地电位差别较大,在信号地上将有比较大的地电流并产生压降。

3、抗干扰能力差

​ RS-232C在电平转换时采用单端输入输出,在传输过程中当干扰和噪声混在正常的信号中。为了提高信噪比,RS-232C总线标准不得不采用比较大的电压摆幅。

RS-422A接口

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YGntBjGA-1635008141085)(images/image-20211023225349799.png)]

​ RS-422A输出驱动器为双端平衡驱动器。如果其中一条线为逻辑“1”状态,另一条线就为逻辑“0”,比采用单端不平衡驱动对电压的放大倍数大一倍。差分电路能从地线干扰中拾取有效信号,差分接收器可以分辨200mV以上电位差。若传输过程中混入了干扰和噪声,由于差分放大器的作用,可使干扰和噪声相互抵消。因此可以避免或大大减弱地线干扰和电磁干扰的影响。RS-422A传输速率(90Kbps)时,传输距离可达1200米。

RS-485接口

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ppgI61bo-1635008141086)(images/image-20211023225531245.png)]

​ 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串行口的结构

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-J3qaJe8E-1635008141087)(images/image-20211023225724017.png)]

​ 有两个物理上独立的接收、发送缓冲器SBUF,它们占用同一地址99H ;接收器是双缓冲结构 ;发送缓冲器,因为发送时CPU是主动的,不会产生重叠错误。

80C51串行口的控制寄存器

SCON 是一个特殊功能寄存器,用以设定串行口的工作方式接收/发送控制以及设置状态标志

76543210
字节地址:98HSM0SM1SM2RENTB8RB8TIRISCON

SM0和SM1为工作方式选择位,可选择四种工作方式:

SM0SM1方式说明波特率
000移位寄存器 f o s c / 12 f_{osc}/12 fosc/12
01110位异步收发器(8位数据)可变
10211位异步收发器(9位数据) f o s c / 64 f_{osc}/64 fosc/64 f o s c / 32 f_{osc}/32 fosc/32
11311位异步收发器(9位数据)可变
  • 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与串行口工作有关 :

76543210
字节地址:97HSMODPCON

SMOD(PCON.7) 波特率倍增位。在串行口方式1、方式2、方式3时,波特率与SMOD有关,当SMOD=1时,波特率提高一倍。复位时,SMOD=0。

80C51串行口的工作方式
一、方式0

​ 方式0时,串行口为同步移位寄存器的输入输出方式。主要用于扩展并行输入或输出口。数据由RXD(P3.0)引脚输入或输出,同步移位脉冲由TXD(P3.1)引脚输出。发送和接收均为8位数据,低位在先,高位在后。波特率固定为fosc/12。

1、方式0输出:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-df06rY7W-1635008141088)(images/image-20211023233736982.png)]

2、方式0输入:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ebx19SVv-1635008141089)(images/image-20211023233759584.png)]

二、方式1

​ 方式1是10位数据的异步通信口。TXD为数据发送引脚,RXD为数据接收引脚,传送一帧数据的格式如图所示。其中1位起始位,8位数据位,1位停止位。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6UyrlL6i-1635008141090)(images/image-20211023233856078.png)]

1、方式1输出:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FNMg5b5d-1635008141090)(images/image-20211023233909506.png)]

2、方式1输入:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FmdshLrk-1635008141091)(images/image-20211023233931620.png)]

​ 用软件置REN为1时,接收器以所选择波特率的16倍速率采样RXD引脚电平,检测到RXD引脚输入电平发生负跳变时,则说明起始位有效,将其移入输入移位寄存器,并开始接收这一帧信息的其余位。接收过程中,数据从输入移位寄存器右边移入,起始位移至输入移位寄存器最左边时,控制电路进行最后一次移位。当RI=0,且SM2=0(或接收到的停止位为1)时,将接收到的9位数据的前8位数据装入接收SBUF,第9位(停止位)进入RB8,并置RI=1,向CPU请求中断。

三、方式2和方式3

​ 方式2或方式3时为11位数据的异步通信口。TXD为数据发送引脚,RXD为数据接收引脚 。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YuGz9IX4-1635008141092)(images/image-20211023234047526.png)]

​ 方式2和方式3时起始位1位,数据9位(含1位附加的第9位,发送时为SCON中的TB8,接收时为RB8),停止位1位,一帧数据为11位。方式2的波特率固定为晶振频率的1/64或1/32,方式3的波特率由定时器T1的溢出率决定。

1、方式2和方式3输出

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VXdXlyM1-1635008141092)(images/image-20211023234116175.png)]

​ 发送开始时,先把起始位0输出到TXD引脚,然后发送移位寄存器的输出位(D0)到TXD引脚。每一个移位脉冲都使输出移位寄存器的各位右移一位,并由TXD引脚输出。

​ 第一次移位时,停止位“1”移入输出移位寄存器的第9位上 ,以后每次移位,左边都移入0。当停止位移至输出位时,左边其余位全为0,检测电路检测到这一条件时,使控制电路进行最后一次移位,并置TI=1,向CPU请求中断。

2、方式2和方式3输入

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3gKwWnLa-1635008141093)(images/image-20211023234223797.png)]

​ 接收时,数据从右边移入输入移位寄存器,在起始位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 f_{osc}/12 fosc/12

  • 方式2的波特率 = ( 2 S M O D / 64 ) ⋅ f o s c (2^{SMOD}/64)·f_{osc} (2SMOD/64)fosc

  • 方式1的波特率 = ( 2 S M O D / 32 ) ⋅ ( T 1 溢 出 率 ) (2^{SMOD}/32)·(T1溢出率) (2SMOD/32)(T1)

  • 方式3的波特率 = ( 2 S M O D / 32 ) ⋅ ( T 1 溢 出 率 ) (2^{SMOD}/32)·(T1溢出率) (2SMOD/32)(T1)

​ 当T1作为波特率发生器时,最典型的用法是使T1工作在自动再装入的8位定时器方式(即方式2,且TCON的TR1=1,以启动定时器)。这时溢出率取决于TH1中的计数值。

T 1 溢 出 率 = f o s c / [ 12 ⋅ ( 256 − T H 1 ) ] T1溢出率 = f_{osc} / [12 · (256 - TH1)] T1=fosc/[12(256TH1)]

​ 在单片机的应用中,常用的晶振频率为:12MHz和11.0592MHz。所以,选用的波特率也相对固定。常用的串行口波特率以及各参数的关系如表所示。

常用波特率与定时器1的参数关系
串口工作方式波特率/(b/s)fosc(MHz)SMOD定时器T1
C / T工作方式初值
方式1、362.5k12102FFH
19.2k11.0592102FDH
960011.0592002FDH
480011.0592002FAH
240011.0592002F4H
120011.0592002E8H

串口如何使用

串行口工作之前,应对其进行初始化,主要是设置产生波特率的定时器1、串行口控制和中断控制。具体步骤如下:

  • 确定T1的工作方式(编程TMOD寄存器);
  • 计算T1的初值,装载TH1、TL1;
  • 启动T1(编程TCON中的TR1位);
  • 确定串行口控制(编程SCON寄存器);

串行口在中断方式工作时,要进行中断设置(编程IEIP寄存器)。

单片机与单片机的通信

一、点对点的通信

硬件连接

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UnSObQa8-1635008141094)(images/image-20211024000911905.png)]

二、多机通信

硬件连接

​ 单片机构成的多机系统常采用总线型主从式结构。所谓主从式,即在数个单片机中,有一个是主机,其余的是从机,从机要服从主机的调度、支配。80C51单片机的串行口方式2和方式3适于这种主从式的通信结构。当然采用不同的通信标准时,还需进行相应的电平转换,有时还要对信号进行光电隔离。在实际的多机应用系统中,常采用RS-485串行标准总线进行数据传输。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-y8GHS0dX-1635008141094)(images/image-20211024001028668.png)]

驱动程序

/*
实验现象:实现串口通信
注意事项:串口通信选择方式二、晶振频率为12,波特率为9600
*/
#include "reg52.h"

typedef unsigned char u8;

void UsartInit() {
  TMOD = 0x20;    // 选择为定时器1模式,工作方式2(8位自动重装定时/计数器),仅用TR1打开启动。
  TH1  = 0xF9;    // 设定串口波特率为9600
  TL1  = 0XF9;
  PCON = 0X80;    // 设定串口波特率倍增
  SCON = 0X50;    // 选择串口工作方式1(10位异步收发器(8位数据)),允许接收
  TR1  = 1;       // 启动定时器1
  ES   = 1;       // 打开串口中断
  EA   = 1;       // 打开总中断
}

void main(){
  UsartInit();
  while(1);
}

void Usart() interrupt 4 {
  u8 receiveData = SBUF;  // 接收数据
  RI = 0;                 // 关闭串口接收
  SBUF = receiveData;     // 发送数据
  while(!TI);
  TI = 0;                 // 停止发送
}
  • 83
    点赞
  • 473
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值