第6章

定时器/计数器及串行口

深入学习单片机定时器/计数器的工作原理、编程方法以及串行口通信的基础知识和应用

01

6.1 定时器/计数器概述

6.1.1 基本概念

定时器/计数器是单片机的重要组成部分,可用于精确计时、产生定时中断和计数外部事件。8051单片机有两个16位定时器/计数器:T0和T1,每个定时器/计数器由两个8位寄存器组成。

定时器模式

计数器对内部机器周期计数,用于实现精确的定时功能

计数器模式

计数器对外部输入脉冲计数,用于测量外部事件的频率或次数

6.1.2 工作原理

  • 核心结构:定时器/计数器的核心是一个16位加1计数器,由THx和TLx两个8位寄存器组成
  • 定时器模式:计数脉冲来自内部时钟,频率为晶振频率的1/12
  • 计数器模式:计数脉冲来自外部输入引脚T0(P3.4)或T1(P3.5)
  • 溢出机制:当计数器计满溢出时,TFx标志位置1,可触发中断或作为查询标志

6.1.3 相关寄存器

TMOD寄存器(定时器模式控制寄存器)

TMOD用于设置定时器/计数器的工作模式,字节地址为89H,不可位寻址。

D7
D6
D5
D4
D3
D2
D1
D0
GATE
C/T
M1
M0
GATE
C/T
M1
M0
高4位:控制T1 | 低4位:控制T0

位功能详解:

  • GATE(门控位):
  • = 0:仅由TR位控制启停
  • = 1:由TR位和外部中断引脚共同控制
  • C/T(模式选择):
  • = 0:定时器模式(内部时钟)
  • = 1:计数器模式(外部引脚)

M1、M0(模式选择):

  • M1=0, M0=0:模式0(13位)
  • M1=0, M0=1:模式1(16位)
  • M1=1, M0=0:模式2(8位自动重装)
  • M1=1, M0=1:模式3(T0双8位)

常用配置案例:

; T0模式1定时, T1模式2计数 MOV TMOD, #61H ; T0模式2定时, T1模式1定时 MOV TMOD, #12H ; T0模式0定时(仅T0) MOV TMOD, #00H ; T1模式2作为波特率发生器 MOV TMOD, #20H

TCON寄存器(定时器控制寄存器)

TCON用于控制定时器的运行和溢出标志,字节地址为88H,可位寻址。

D7
D6
D5
D4
D3
D2
D1
D0
TF1
TR1
TF0
TR0
IE1
IT1
IE0
IT0

定时器相关位:

  • TF1/TF0(溢出标志):
  • 定时器溢出时由硬件置1
  • 触发中断后由硬件清零(模式2除外)
  • TR1/TR0(运行控制):
  • = 1:启动定时器
  • = 0:停止定时器

外部中断相关位:

  • IE1/IE0(中断标志):
  • 外部中断请求有效时置1
  • IT1/IT0(触发方式):
  • = 0:电平触发方式
  • = 1:边沿触发方式

常用配置案例:

; 启动T0定时器 SETB TR0 ; 停止T1定时器 CLR TR1 ; 查询T0溢出标志 JNB TF0, $ CLR TF0 ; 设置外部中断0为边沿触发 SETB IT0 ; 同时启动两个定时器 SETB TR0 SETB TR1
02

6.2 定时器/计数器工作模式

模式0:13位定时器/计数器

模式0是8051单片机的兼容模式,由THx的8位和TLx的低5位组成13位计数器,TLx的高3位无效。

计数器结构:

THx (8位)
+
TLx低5位
=
13位
  • 最大计数值:2^13 = 8192
  • 最大定时时间:8192 × 机器周期(12MHz晶振下约8.192ms)
  • 适用场景:兼容早期单片机,较少使用

初始化代码:

; T0模式0定时1ms(12MHz晶振) ; 初值 = 8192 - 1000 = 7192 = 1C18H ; 13位模式下 TH0 = 1CH(高8位), TL0 = 18H(低5位有效) MOV TMOD, #00H ; T0模式0,定时 MOV TH0, #1CH ; 定时1ms初值高8位 MOV TL0, #18H ; 低5位有效 SETB TR0 ; 启动T0 ; 查询方式等待溢出 LOOP: JNB TF0, LOOP CLR TF0 ; 溢出处理代码 SJMP LOOP

模式1:16位定时器/计数器

模式1是最常用的工作模式,由THx和TLx组成完整的16位计数器,功能完整,定时范围最大。

计数器结构:

THx (8位)
+
TLx (8位)
=
16位
  • 最大计数值:2^16 = 65536
  • 最大定时时间:65536 × 机器周期(12MHz晶振下约65.536ms)
  • 适用场景:需要较长定时的场合,如LED闪烁、按键防抖等

初始化代码(定时50ms):

; T0模式1定时50ms(12MHz晶振) ; 初值 = 65536 - 50000 = 15536 = 3CB0H MOV TMOD, #01H ; T0模式1,定时 MOV TH0, #3CH ; 定时50ms初值 MOV TL0, #0B0H SETB ET0 ; 使能T0中断 SETB EA ; 开总中断 SETB TR0 ; 启动T0 ; 主程序循环 MAIN: SJMP MAIN ; T0中断服务程序 TIMER0_ISR: PUSH ACC PUSH PSW MOV TH0, #3CH ; 重装初值 MOV TL0, #0B0H CPL P1.0 ; 切换LED POP PSW POP ACC RETI

模式2:自动重装初值

模式2是一种自动重装初值的8位定时器/计数器,TLx作为计数器,THx作为初值寄存器,溢出后THx自动装入TLx,无需软件重装。

工作原理:

THx(初值)
TLx(计数)
溢出
自动重装
  • 最大计数值:2^8 = 256
  • 最大定时时间:256 × 机器周期(12MHz晶振下约256μs)
  • 适用场景:需要精确定时且定时时间较短的场合,如波特率发生器

初始化代码(波特率发生器):

; T1模式2作为波特率发生器(2400bps,12MHz) ; 2400 = (1/32) × [12000000 / (12 × (256 - TH1))] ; 解得: TH1 = 243 = 0F3H MOV TMOD, #20H ; T1模式2,定时 MOV TH1, #0F3H ; 2400bps初值 MOV TL1, #0F3H SETB TR1 ; 启动T1 ; 串行口初始化 MOV SCON, #50H ; 方式1,允许接收 MOV PCON, #00H ; SMOD=0 SETB ES ; 使能串口中断 SETB EA ; 开总中断

模式3:T0双8位模式

模式3仅适用于T0,将T0分成两个独立的8位计数器TH0和TL0。TL0使用原T0的控制位(TR0、TF0),TH0占用T1的控制位(TR1、TF1),此时T1只能作为串行口波特率发生器使用。

计数器结构:

TL0
占用TR0, TF0
+
TH0
占用TR1, TF1
=
两个独立8位
  • TL0:8位定时器/计数器,使用T0中断
  • TH0:仅8位定时器,使用T1中断
  • T1:停止计数,只能用作波特率发生器
  • 适用场景:需要两个独立定时器的场合

初始化代码:

; T0模式3,TL0定时200μs,TH0定时100μs ; 8位定时器最大定时 256μs(12MHz晶振,机器周期1μs) MOV TMOD, #03H ; T0模式3 MOV TH0, #9CH ; TH0初值(256-100=156=9CH),定时100μs MOV TL0, #38H ; TL0初值(256-200=56=38H),定时200μs SETB ET0 ; 使能TL0中断 SETB ET1 ; 使能TH0中断(借用T1中断) SETB EA ; 开总中断 SETB TR0 ; 启动TL0 SETB TR1 ; 启动TH0 ; TL0中断服务(200μs) TIMER0_ISR: MOV TL0, #38H ; 重装TL0初值 ; 200μs定时处理 RETI ; TH0中断服务(100μs) TIMER1_ISR: MOV TH0, #9CH ; 重装TH0初值 ; 100μs定时处理 RETI

模式选择建议

1 模式1(最常用)

适用于需要较长定时的场合,如LED闪烁、按键防抖、定时采集等。

2 模式2(精确定时)

适用于需要精确定时且无需重装初值的场合,如波特率发生器、高频信号检测。

0 模式0(兼容模式)

与模式1类似,但较少使用,仅用于兼容早期8048单片机程序。

3 模式3(双定时器)

仅在需要两个独立8位定时器时使用,此时T1只能用作波特率发生器。

03

6.3 定时器/计数器编程

6.3.1 初值计算

初值计算公式: 初值 = 2^n - (定时时间 / 机器周期) 其中: - n为计数器位数(模式0为13,模式1为16,模式2为8) - 机器周期 = 12 / 晶振频率 示例1:12MHz晶振,定时1ms,模式1 机器周期 = 12 / 12MHz = 1μs 初值 = 65536 - (1000μs / 1μs) = 64536 = 0xFC18H 即:TH0 = 0xFCH,TL0 = 0x18H 示例2:12MHz晶振,定时50ms,模式1 机器周期 = 12 / 12MHz = 1μs 初值 = 65536 - (50000μs / 1μs) = 15536 = 0x3CB0H 即:TH0 = 0x3CH,TL0 = 0xB0H

6.3.2 编程步骤

1

设置TMOD寄存器

2

写入初值

3

设置中断寄存器

4

启动定时器

5

编写中断服务程序

6.3.3 编程实例

实例一:定时中断实现LED闪烁

项目功能:

使用定时器T0实现1秒延时,控制LED灯每秒闪烁一次,模拟警示灯效果。

硬件连接:

LED正极通过限流电阻连接至P1.0引脚,负极接地。

; T0定时中断实现1秒延时LED闪烁 ; 晶振频率:12MHz,机器周期:1μs ; 定时50ms,软件计数20次实现1秒 ORG 0000H ; 程序入口地址 LJMP MAIN ; 跳转到主程序 ORG 000BH ; T0中断向量地址 LJMP TIMER0_ISR ; 跳转到T0中断服务程序 ORG 0030H ; 主程序起始地址 MAIN: MOV SP, #60H ; 设置堆栈指针,避免覆盖重要数据 MOV TMOD, #01H ; T0模式1(16位定时器),定时模式 MOV TH0, #3CH ; 定时50ms初值高8位 MOV TL0, #0B0H ; 定时50ms初值低8位 SETB ET0 ; 使能T0中断 SETB EA ; 开总中断 MOV R7, #20 ; 软件计数器,50ms*20=1秒 SETB TR0 ; 启动T0定时器 MAIN_LOOP: SJMP MAIN_LOOP ; 主程序循环等待 TIMER0_ISR: PUSH ACC ; 保护累加器 PUSH PSW ; 保护程序状态字 MOV TH0, #3CH ; 重装初值高8位 MOV TL0, #0B0H ; 重装初值低8位 DJNZ R7, EXIT ; 软件计数减1,不为0则退出 MOV R7, #20 ; 重置软件计数器 CPL P1.0 ; 切换LED状态(亮→灭,灭→亮) EXIT: POP PSW ; 恢复程序状态字 POP ACC ; 恢复累加器 RETI ; 中断返回 END ; 程序结束

实例二:计数器模式脉冲计数

项目功能:

使用T0计数器模式对外部脉冲计数,每计满100个脉冲触发中断,切换LED状态。

硬件连接:

脉冲信号输入至T0引脚(P3.4),LED连接至P1.0引脚。

; T0计数器模式对外部脉冲计数 ; 每计数100个脉冲触发中断,控制LED闪烁 ORG 0000H ; 程序入口地址 LJMP MAIN ; 跳转到主程序 ORG 000BH ; T0中断向量地址 LJMP TIMER0_ISR ; 跳转到T0中断服务程序 ORG 0030H ; 主程序起始地址 MAIN: MOV SP, #60H ; 设置堆栈指针 MOV TMOD, #05H ; T0模式1(16位计数器),计数模式 MOV TH0, #0FFH ; 计数初值高8位(65536-100=65436=FF9CH) MOV TL0, #9CH ; 计数初值低8位 SETB ET0 ; 使能T0中断 SETB EA ; 开总中断 SETB TR0 ; 启动T0计数器 MAIN_LOOP: SJMP MAIN_LOOP ; 主程序循环等待 TIMER0_ISR: PUSH ACC ; 保护累加器 PUSH PSW ; 保护程序状态字 MOV TH0, #0FFH ; 重装计数初值高8位 MOV TL0, #9CH ; 重装计数初值低8位 INC R0 ; 累计总脉冲数 CPL P1.0 ; 切换LED状态 EXIT: POP PSW ; 恢复程序状态字 POP ACC ; 恢复累加器 RETI ; 中断返回 END ; 程序结束

实例三:简易电子秒表

项目功能:

使用T0定时器实现秒表功能,P1口控制8个LED显示秒数(二进制),每1秒加1。

硬件连接:

8个LED分别连接至P1.0~P1.7引脚,LED正极通过限流电阻接电源。

; 简易电子秒表程序 ; 使用T0定时1秒,P1口8个LED显示0-255秒(二进制) ORG 0000H ; 程序入口地址 LJMP MAIN ; 跳转到主程序 ORG 000BH ; T0中断向量地址 LJMP TIMER0_ISR ; 跳转到T0中断服务程序 ORG 0030H ; 主程序起始地址 MAIN: MOV SP, #60H ; 设置堆栈指针 MOV TMOD, #01H ; T0模式1(16位定时器) MOV TH0, #3CH ; 定时50ms初值高8位 MOV TL0, #0B0H ; 定时50ms初值低8位 SETB ET0 ; 使能T0中断 SETB EA ; 开总中断 MOV R7, #20 ; 软件计数器,50ms*20=1秒 MOV R0, #0 ; 秒表计数值(0-255) MOV P1, #0 ; LED初始全灭 SETB TR0 ; 启动T0定时器 MAIN_LOOP: SJMP MAIN_LOOP ; 主程序循环等待 TIMER0_ISR: PUSH ACC ; 保护累加器 PUSH PSW ; 保护程序状态字 MOV TH0, #3CH ; 重装初值高8位 MOV TL0, #0B0H ; 重装初值低8位 DJNZ R7, EXIT ; 软件计数减1,不为0则退出 MOV R7, #20 ; 重置软件计数器 INC R0 ; 秒表加1 MOV A, R0 ; 计数值送累加器 MOV P1, A ; 输出到LED显示 EXIT: POP PSW ; 恢复程序状态字 POP ACC ; 恢复累加器 RETI ; 中断返回 END ; 程序结束

实例四:LED流水灯控制器

项目功能:

使用T0定时器实现LED流水灯效果,8个LED依次点亮,每个LED点亮500ms。

硬件连接:

8个LED分别连接至P1.0~P1.7引脚,采用共阳极接法。

; LED流水灯控制器 ; 使用T0定时500ms,LED依次从P1.0到P1.7点亮 ORG 0000H ; 程序入口地址 LJMP MAIN ; 跳转到主程序 ORG 000BH ; T0中断向量地址 LJMP TIMER0_ISR ; 跳转到T0中断服务程序 ORG 0030H ; 主程序起始地址 MAIN: MOV SP, #60H ; 设置堆栈指针 MOV TMOD, #01H ; T0模式1(16位定时器) MOV TH0, #3CH ; 定时50ms初值高8位 MOV TL0, #0B0H ; 定时50ms初值低8位 SETB ET0 ; 使能T0中断 SETB EA ; 开总中断 MOV R7, #10 ; 软件计数器,50ms*10=500ms MOV A, #01H ; 初始点亮P1.0 MOV P1, A ; 输出到LED SETB TR0 ; 启动T0定时器 MAIN_LOOP: SJMP MAIN_LOOP ; 主程序循环等待 TIMER0_ISR: PUSH ACC ; 保护累加器 PUSH PSW ; 保护程序状态字 MOV TH0, #3CH ; 重装初值高8位 MOV TL0, #0B0H ; 重装初值低8位 DJNZ R7, EXIT ; 软件计数减1,不为0则退出 MOV R7, #10 ; 重置软件计数器 RL A ; 循环左移,点亮下一个LED MOV P1, A ; 输出到LED显示 CJNE A, #80H, EXIT ; 判断是否到最后一个LED MOV A, #01H ; 回到第一个LED EXIT: POP PSW ; 恢复程序状态字 POP ACC ; 恢复累加器 RETI ; 中断返回 END ; 程序结束

实例五:按键防抖处理

项目功能:

使用T0定时器实现按键防抖,检测按键按下后延时10ms再判断,避免抖动干扰。

硬件连接:

按键一端接P3.2(INT0),另一端接地;LED连接至P1.0引脚。

; 按键防抖处理程序 ; 使用T0定时10ms检测按键,按键按下后LED状态翻转 ORG 0000H ; 程序入口地址 LJMP MAIN ; 跳转到主程序 ORG 000BH ; T0中断向量地址 LJMP TIMER0_ISR ; 跳转到T0中断服务程序 ORG 0030H ; 主程序起始地址 MAIN: MOV SP, #60H ; 设置堆栈指针 MOV TMOD, #01H ; T0模式1(16位定时器) MOV TH0, #0FCH ; 定时10ms初值高8位(65536-10000=55536=0FC18H) MOV TL0, #18H ; 定时10ms初值低8位 SETB ET0 ; 使能T0中断 SETB EA ; 开总中断 MOV R7, #0 ; 按键检测计数器 CLR P1.0 ; LED初始熄灭 SETB TR0 ; 启动T0定时器 MAIN_LOOP: SJMP MAIN_LOOP ; 主程序循环等待 TIMER0_ISR: PUSH ACC ; 保护累加器 PUSH PSW ; 保护程序状态字 MOV TH0, #0FCH ; 重装初值高8位 MOV TL0, #18H ; 重装初值低8位 JB P3.2, RESET_KEY ; 按键未按下,重置计数器 INC R7 ; 按键按下,计数器加1 CJNE R7, #2, EXIT ; 未达到20ms(10ms*2),退出 CPL P1.0 ; 稳定按下20ms,翻转LED状态 RESET_KEY: MOV R7, #0 ; 重置按键检测计数器 EXIT: POP PSW ; 恢复程序状态字 POP ACC ; 恢复累加器 RETI ; 中断返回 END ; 程序结束

实例六:定时数据采集

项目功能:

使用T0定时器实现每隔500ms采集一次ADC数据,模拟温度采集场景。

硬件连接:

ADC输出接至P0口,LED连接至P1口显示采集状态。

; 定时数据采集程序 ; 使用T0定时500ms,模拟ADC数据采集 ORG 0000H ; 程序入口地址 LJMP MAIN ; 跳转到主程序 ORG 000BH ; T0中断向量地址 LJMP TIMER0_ISR ; 跳转到T0中断服务程序 ORG 0030H ; 主程序起始地址 MAIN: MOV SP, #60H ; 设置堆栈指针 MOV TMOD, #01H ; T0模式1(16位定时器) MOV TH0, #3CH ; 定时50ms初值高8位 MOV TL0, #0B0H ; 定时50ms初值低8位 SETB ET0 ; 使能T0中断 SETB EA ; 开总中断 MOV R7, #10 ; 软件计数器,50ms*10=500ms MOV R1, #30H ; 数据存储地址指针 SETB TR0 ; 启动T0定时器 MAIN_LOOP: SJMP MAIN_LOOP ; 主程序循环等待 TIMER0_ISR: PUSH ACC ; 保护累加器 PUSH PSW ; 保护程序状态字 MOV TH0, #3CH ; 重装初值高8位 MOV TL0, #0B0H ; 重装初值低8位 DJNZ R7, EXIT ; 软件计数减1,不为0则退出 MOV R7, #10 ; 重置软件计数器 ; 模拟ADC采集(实际应用中调用ADC读取) MOV A, P0 ; 读取P0口数据(模拟ADC输入) MOV @R1, A ; 存储到数据缓冲区 INC R1 ; 指向下一个存储单元 CJNE R1, #40H, NEXT ; 判断是否存满16个数据 MOV R1, #30H ; 回到起始地址 NEXT: CPL P1.0 ; 闪烁LED表示采集完成 EXIT: POP PSW ; 恢复程序状态字 POP ACC ; 恢复累加器 RETI ; 中断返回 END ; 程序结束
04

6.4 串行口通信

6.4.1 基本概念

串行通信是指数据一位一位地按顺序传送的通信方式,具有传输线少、成本低等优点。8051单片机内置一个全双工串行通信接口,可实现单片机之间或单片机与其他设备之间的通信。

异步通信

8051单片机采用异步通信方式,以帧为单位进行数据传输

全双工

发送和接收可同时进行,有独立的发送和接收缓冲器

6.4.2 串行口结构

  • 发送缓冲器SBUF:用于存放待发送的数据:指令:MOV SBUF,A
  • 接收缓冲器SBUF:用于存放接收到的数据:指令:MOV A,SBUF
  • SCON寄存器:用于设置串行口的工作方式、接收/发送控制和状态标志
  • 波特率发生器:通常由定时器T1或T2构成,用于控制串行通信的速率

6.4.3 工作方式

方式0:同步移位寄存器

波特率固定为fosc/12,主要用于扩展I/O口

方式1:10位异步通信

波特率可变,由定时器T1或T2控制,最常用的通信方式

方式2:11位异步通信

波特率固定为fosc/64或fosc/32

方式3:11位异步通信

波特率可变,由定时器T1或T2控制

6.4.4 波特率计算

方式1和方式3的波特率计算公式: 波特率 = (2^SMOD / 32) × (定时器T1溢出率) 其中: - SMOD是PCON寄存器的最高位 - 定时器T1溢出率 = fosc / [12 × (256 - TH1)] 示例:12MHz晶振,2400bps,SMOD=0 2400 = (1 / 32) × [12000000 / (12 × (256 - TH1))] 解得:TH1 = 243 = 0xF3H 常用波特率初值表(12MHz,存在微小误差): - 2400bps: TH1 = 0xF3H (SMOD=0,误差约 0.16%) - 1200bps: TH1 = 0xE6H (SMOD=0,误差约 0.16%)

6.4.5 串行口编程

案例一:串口发送字符串

项目功能:

单片机通过串口向PC发送"Hello, 8051!"字符串,可用于调试通信是否正常。

硬件连接:

单片机TXD(P3.1)引脚接USB转TTL模块的RXD,GND共地。

; 串口发送字符串"Hello, 8051!" ; 波特率:2400bps,晶振:12MHz ORG 0000H ; 程序入口地址 LJMP MAIN ; 跳转到主程序 ORG 0030H ; 主程序起始地址 MAIN: MOV SP, #60H ; 设置堆栈指针 MOV SCON, #40H ; 串行口方式1,不允许接收 MOV PCON, #00H ; SMOD=0 MOV TMOD, #20H ; T1模式2(波特率发生器) MOV TH1, #0F3H ; 2400bps初值 MOV TL1, #0F3H ; 初值装入TL1 SETB TR1 ; 启动T1定时器 SEND_LOOP: MOV DPTR, #STRING ; 字符串首地址送数据指针 SEND_CHAR: CLR A ; 累加器清零 MOVC A, @A+DPTR ; 查表取字符 JZ SEND_END ; 遇到0结束发送 ACALL SEND_BYTE ; 调用发送字节子程序 INC DPTR ; 指向下一个字符 SJMP SEND_CHAR ; 继续发送 SEND_END: ACALL DELAY ; 发送完成后延时 SJMP SEND_LOOP ; 循环发送 ; 发送单字节子程序 SEND_BYTE: MOV SBUF, A ; 数据送发送缓冲器 WAIT_SEND: JNB TI, WAIT_SEND ; 等待发送完成 CLR TI ; 清除发送标志 RET ; 返回 ; 延时子程序(约1秒) DELAY: MOV R5, #20 D1: MOV R6, #200 D2: MOV R7, #250 DJNZ R7, $ DJNZ R6, D2 DJNZ R5, D1 RET ; 字符串数据 STRING: DB 'Hello, 8051!', 0DH, 0AH, 0 ; 0DH=回车, 0AH=换行, 0=结束标志 END ; 程序结束

案例二:串口回显程序

项目功能:

单片机接收PC发送的数据,然后将接收到的数据原样发送回去,实现回显功能。

硬件连接:

单片机TXD(P3.1)接USB转TTL的RXD,RXD(P3.0)接USB转TTL的TXD。

; 串口回显程序 ; 接收PC数据并原样发送回去,波特率2400bps ORG 0000H ; 程序入口地址 LJMP MAIN ; 跳转到主程序 ORG 0023H ; 串口中断向量地址 LJMP UART_ISR ; 跳转到串口中断服务程序 ORG 0030H ; 主程序起始地址 MAIN: MOV SP, #60H ; 设置堆栈指针 MOV SCON, #50H ; 串行口方式1,允许接收(REN=1) MOV PCON, #00H ; SMOD=0 MOV TMOD, #20H ; T1模式2(波特率发生器) MOV TH1, #0F3H ; 2400bps初值 MOV TL1, #0F3H ; 初值装入TL1 SETB TR1 ; 启动T1定时器 SETB ES ; 使能串口中断 SETB EA ; 开总中断 MAIN_LOOP: SJMP MAIN_LOOP ; 主程序循环等待 ; 串口中断服务程序 UART_ISR: PUSH ACC ; 保护累加器 PUSH PSW ; 保护程序状态字 JNB RI, UART_EXIT ; 不是接收中断,退出 CLR RI ; 清除接收标志 MOV A, SBUF ; 读取接收到的数据 ; 回显:将接收到的数据发送回去 MOV SBUF, A ; 数据送发送缓冲器 WAIT_ECHO: JNB TI, WAIT_ECHO ; 等待发送完成 CLR TI ; 清除发送标志 UART_EXIT: POP PSW ; 恢复程序状态字 POP ACC ; 恢复累加器 RETI ; 中断返回 END ; 程序结束

案例三:串口控制LED

项目功能:

PC通过串口发送指令控制LED:发送'1'点亮LED,发送'0'熄灭LED,发送'2'闪烁LED。

硬件连接:

LED正极通过限流电阻接P1.0,负极接地;串口连接同案例二。

; 串口控制LED程序 ; 指令格式:'1'=点亮, '0'=熄灭, '2'=闪烁 ORG 0000H ; 程序入口地址 LJMP MAIN ; 跳转到主程序 ORG 000BH ; T0中断向量地址 LJMP TIMER0_ISR ; 跳转到T0中断服务 ORG 0023H ; 串口中断向量地址 LJMP UART_ISR ; 跳转到串口中断服务 ORG 0030H ; 主程序起始地址 MAIN: MOV SP, #60H ; 设置堆栈指针 ; 定时器初始化(用于LED闪烁) MOV TMOD, #21H ; T1模式2(波特率发生器),T0模式1(16位定时器) MOV TH0, #3CH ; 定时50ms初值 MOV TL0, #0B0H SETB ET0 ; 使能T0中断 ; 串口初始化 MOV SCON, #50H ; 串行口方式1,允许接收 MOV PCON, #00H ; SMOD=0 MOV TH1, #0F3H ; 2400bps初值 MOV TL1, #0F3H SETB TR1 ; 启动T1 SETB ES ; 使能串口中断 SETB EA ; 开总中断 CLR P1.0 ; LED初始熄灭 MOV R2, #0 ; 控制标志:0=熄灭,1=点亮,2=闪烁 MAIN_LOOP: SJMP MAIN_LOOP ; 主程序循环等待 ; T0中断服务(LED闪烁) TIMER0_ISR: PUSH ACC ; 保护累加器 PUSH PSW ; 保护程序状态字 MOV TH0, #3CH ; 重装初值 MOV TL0, #0B0H CJNE R2, #2, TIMER_EXIT ; 不是闪烁模式,退出 CPL P1.0 ; 闪烁模式:翻转LED状态 TIMER_EXIT: POP PSW ; 恢复程序状态字 POP ACC ; 恢复累加器 RETI ; 中断返回 ; 串口中断服务程序 UART_ISR: PUSH ACC ; 保护累加器 PUSH PSW ; 保护程序状态字 JNB RI, UART_EXIT ; 不是接收中断,退出 CLR RI ; 清除接收标志 MOV A, SBUF ; 读取接收到的数据 CJNE A, #31H, CHECK_OFF ; 判断是否为'1'(ASCII=31H) MOV R2, #1 ; 设置控制标志为点亮 SETB P1.0 ; 点亮LED CLR TR0 ; 停止定时器 SJMP UART_EXIT CHECK_OFF: CJNE A, #30H, CHECK_BLINK ; 判断是否为'0'(ASCII=30H) MOV R2, #0 ; 设置控制标志为熄灭 CLR P1.0 ; 熄灭LED CLR TR0 ; 停止定时器 SJMP UART_EXIT CHECK_BLINK: CJNE A, #32H, UART_EXIT ; 判断是否为'2'(ASCII=32H) MOV R2, #2 ; 设置控制标志为闪烁 SETB TR0 ; 启动定时器 UART_EXIT: POP PSW ; 恢复程序状态字 POP ACC ; 恢复累加器 RETI ; 中断返回 END ; 程序结束

案例四:串口方式0驱动74LS164实现8位左移流水灯

项目功能:

利用串口方式0驱动74LS164芯片,实现8位LED从右向左依次点亮的流水灯效果,循环往复。

硬件连接:

74LS164的DS接P3.0(RXD),CP接P3.1(TXD),MR接VCC;Q0-Q7输出端接8个LED。

; 串口方式0驱动74LS164实现8位左移流水灯 ; 串口方式0: 同步移位寄存器方式, 波特率=fosc/12 ; 74LS164: 8位串行输入/并行输出移位寄存器 ORG 0000H ; 程序入口 LJMP MAIN ; 跳主程序 ORG 0030H ; 主程序起始地址 MAIN: MOV SCON, #00H ; 串口方式0 MOV A, #01H ; 初始: Q0=1, 点亮第一个LED ACALL SEND_74LS164 ; 发送初始数据 LOOP: ACALL DELAY ; 延时(控制流水灯速度) RL A ; 循环左移(00000001→00000010→...) ACALL SEND_74LS164 ; 发送数据到74LS164 SJMP LOOP ; 循环执行 ; 发送数据到74LS164子程序 SEND_74LS164: MOV SBUF, A ; 数据送串口发送缓冲器 JNB TI, $ ; 等待发送完成(TI=1表示8位发送完毕) CLR TI ; 清除发送标志 RET ; 返回 ; 延时子程序(约500ms) DELAY: MOV R5, #50 D1: MOV R6, #200 D2: MOV R7, #250 DJNZ R7, $ DJNZ R6, D2 DJNZ R5, D1 RET END ; 程序结束
05

6.5 综合案例

案例一:LED闪烁控制

使用定时器T0产生500ms的定时中断,在中断服务程序中切换LED的状态,实现LED以1秒为周期闪烁。

; 定时器T0控制LED闪烁(1秒周期) ORG 0000H LJMP MAIN ORG 000BH LJMP TIMER0_ISR ORG 0030H MAIN: MOV SP, #60H MOV TMOD, #01H ; T0模式1,定时 MOV TH0, #3CH ; 50ms初值 MOV TL0, #0B0H SETB ET0 ; 使能T0中断 SETB EA ; 开总中断 SETB TR0 ; 启动T0 MOV R7, #10 ; 50ms*10=500ms MAIN_LOOP: SJMP MAIN_LOOP TIMER0_ISR: PUSH ACC PUSH PSW MOV TH0, #3CH ; 重装初值 MOV TL0, #0B0H DJNZ R7, EXIT MOV R7, #10 ; 重置计数 CPL P1.0 ; 切换LED状态 EXIT: POP PSW POP ACC RETI END

案例二:数码管动态显示

使用定时器T1产生2ms的定时中断,在中断服务程序中进行数码管的动态扫描,实现4位数码管显示0-9999的计数。

; 定时器T1实现数码管动态显示 ORG 0000H LJMP MAIN ORG 001BH LJMP TIMER1_ISR ORG 0030H MAIN: MOV SP, #60H MOV P0, #0FFH MOV TMOD, #10H ; T1模式1,16位定时器 MOV TH1, #0F8H ; 2ms初值高字节(65536-2000=63536=F830H) MOV TL1, #30H ; 2ms初值低字节 SETB ET1 SETB EA SETB TR1 MOV R0, #0 ; 扫描位指针 MOV R1, #0 ; 计数值 MAIN_LOOP: INC R1 MOV A, R1 CJNE A, #100, SKIP MOV R1, #0 SKIP: MOV A, R1 MOV B, #100 DIV AB MOV 30H, A ; 百位 MOV A, B MOV B, #10 DIV AB MOV 31H, A ; 十位 MOV 32H, B ; 个位 SJMP MAIN_LOOP TIMER1_ISR: PUSH ACC PUSH PSW PUSH DPH PUSH DPL MOV TH1, #0F8H ; 重装2ms初值高字节 MOV TL1, #30H ; 重装2ms初值低字节 MOV P2, #00H MOV A, R0 MOV DPTR, #SEG_TAB MOVC A, @A+DPTR MOV P0, A MOV A, R0 MOV P2, A INC R0 MOV A, R0 CJNE A, #4, NEXT MOV R0, #0 NEXT: POP DPL POP DPH POP PSW POP ACC RETI SEG_TAB: DB 3FH,06H,5BH,4FH,66H,6DH,7DH,07H DB 7FH,6FH,77H,7CH,39H,5EH,79H,71H END

案例三:串口通信测试

实现单片机与PC之间的串口通信,单片机接收PC发送的数据,并将其原样返回。

; 串口通信测试(2400bps,12MHz) ORG 0000H LJMP MAIN ORG 0023H LJMP UART_ISR ORG 0030H MAIN: MOV SP, #60H MOV SCON, #50H ; 方式1,允许接收 MOV PCON, #00H ; SMOD=0 MOV TMOD, #20H ; T1模式2 MOV TH1, #0F3H ; 2400bps MOV TL1, #0F3H SETB TR1 SETB ES SETB EA ; 发送欢迎信息 MOV A, #'H' ACALL SEND_CHAR MOV A, #'e' ACALL SEND_CHAR MOV A, #'l' ACALL SEND_CHAR MOV A, #'l' ACALL SEND_CHAR MOV A, #'o' ACALL SEND_CHAR MAIN_LOOP: SJMP MAIN_LOOP SEND_CHAR: MOV SBUF, A WAIT_TX: JNB TI, WAIT_TX CLR TI RET UART_ISR: PUSH ACC PUSH PSW JNB RI, EXIT CLR RI MOV A, SBUF ACALL SEND_CHAR ; 回显 EXIT: POP PSW POP ACC RETI END

案例四:串口控制LED

通过串口发送指令控制LED的亮灭。发送'A'点亮LED,发送'B'熄灭LED,发送'C'切换LED状态。

; 串口控制LED ORG 0000H LJMP MAIN ORG 0023H LJMP UART_ISR ORG 0030H MAIN: MOV SP, #60H MOV SCON, #50H MOV PCON, #00H MOV TMOD, #20H MOV TH1, #0F3H ; 2400bps MOV TL1, #0F3H SETB TR1 SETB ES SETB EA CLR P1.0 ; LED初始熄灭 MAIN_LOOP: SJMP MAIN_LOOP UART_ISR: PUSH ACC PUSH PSW JNB RI, EXIT CLR RI MOV A, SBUF CJNE A, #'A', CHECK_B SETB P1.0 ; 点亮LED SJMP EXIT CHECK_B: CJNE A, #'B', CHECK_C CLR P1.0 ; 熄灭LED SJMP EXIT CHECK_C: CJNE A, #'C', EXIT CPL P1.0 ; 切换LED EXIT: POP PSW POP ACC RETI END
06

6.6 本章总结

核心要点

  • 掌握定时器/计数器的4种工作模式及其应用场景
  • 熟练计算定时器初值,理解定时时间与初值的关系
  • 掌握串行口的4种工作方式,重点掌握方式1
  • 熟练计算串行口波特率,理解定时器T1作为波特率发生器的配置方法
  • 能够编写定时器中断和串行口中断的应用程序

学习建议

  • 通过实际实验加深对定时器工作原理的理解
  • 多练习定时器初值和波特率的计算
  • 结合Proteus仿真软件验证程序功能
  • 尝试将定时器和串行口结合使用,实现更复杂的应用
  • 注意中断服务程序中现场保护的重要性
07

6.7 课后练习题

1 填空题

1. 8051单片机有____个16位定时器/计数器,它们分别是____和____。

2. 定时器模式下,计数脉冲来自____;计数器模式下,计数脉冲来自____引脚。

3. TMOD寄存器中,GATE位为____时,定时器由TR位控制启动;GATE位为____时,定时器由外部中断引脚控制启动。

4. 定时器模式2具有____功能,适用于____场合。

5. 8051串行口有____种工作方式,其中方式____是最常用的异步通信方式。

6. 串行口方式1的波特率由____决定,通常使用____作为波特率发生器。

7. SCON寄存器中,REN位的作用是____,TI位表示____。

8. 定时器T0工作在模式3时,被分成两个独立的____位计数器,此时____停止工作。

9. 方式1通信的帧格式为:1位____、____位数据、1位____,共10位。

10. 12MHz晶振,波特率2400bps,SMOD=0时,定时器T1的初值应为____。

2 单选题

1. 以下哪种工作模式具有自动重装初值功能?

A. 模式0   B. 模式1   C. 模式2   D. 模式3

2. 定时器T0工作在模式3时,TH0的启停由哪个位控制?

A. TR0   B. TR1   C. ET0   D. EA

3. 串行口方式1的波特率是?

A. 固定的   B. 可变的   C. fosc/12   D. fosc/64

4. TMOD寄存器中,C/T位为1时,定时器工作在?

A. 定时模式   B. 计数模式   C. 中断模式   D. 串行模式

5. 12MHz晶振下,定时器T0模式1的最大定时时间约为?

A. 65ms   B. 8ms   C. 256ms   D. 1ms

6. 串行口发送数据时,以下哪个标志位需要软件清零?

A. RI   B. TI   C. SCON   D. SMOD

7. 定时器中断向量地址中,T0中断的入口地址是?

A. 000BH   B. 001BH   C. 0023H   D. 0003H

8. 以下哪种方式常用于波特率发生器?

A. T0模式0   B. T1模式1   C. T1模式2   D. T0模式3

9. 串行口方式0的波特率是?

A. fosc/12   B. fosc/64   C. 可变   D. 9600bps

10. PCON寄存器中SMOD位为1时,波特率会?

A. 不变   B. 减半   C. 加倍   D. 清零

3 编程题

1. 使用定时器T0模式1,编写程序实现P1.0引脚输出周期为2秒的方波(晶振频率为12MHz)。

2. 使用定时器T1模式2,编写程序实现P1口8个LED依次循环点亮,每个LED点亮时间为500ms(晶振频率为12MHz)。

3. 编写串口通信程序,实现单片机向PC发送字符串"Hello, 8051!"(波特率2400bps,12MHz晶振)。

4. 编写程序,通过串口接收PC发送的数据,并根据接收到的数据控制LED:接收到'1'点亮LED,接收到'0'熄灭LED。

5. 使用定时器T0模式0,编写程序实现4位数码管动态显示,显示0-9999的循环计数(定时时间为1ms)。