在嵌入式系统的奇妙世界中,汇编语言就像是一位神秘的魔法师,而硬件则是它最忠实的伙伴。今天,我将带你走进一场充满乐趣的实验:如何用汇编语言在单片机上点亮跑马灯,开启一段奇幻之旅!
实验背景
跑马灯,这个名字听起来就充满了动感和趣味。它是一种经典的硬件实验项目,通过控制 LED 灯的顺序点亮,形成一种动态的视觉效果。在本次实验中,我选择了经典的 AT89C51 单片机,搭配 12MHz 的晶振频率,通过定时器中断实现精确的时间控制,让 LED 灯以 1 秒的时间间隔依次点亮。
实验工具
Keil uVision4:用于编写汇编代码并验证是否成功
PZ-ISP普中自动下载软件:用于将汇编代码烧录至开发板上
STC89Txx:STC89系列单片机,该系列单片机是8位CMOS微控制器,具有高性能、低功耗、丰富的指令集等特点
实验设计
整体思路
要实现跑马灯效果,我们需要让汇编语言与硬件伙伴紧密配合。首先,初始化定时器和中断系统,设置好中断的触发条件和频率。然后,通过循环和中断服务程序,控制 LED 灯的点亮顺序。具体来说:
- 初始化模块:配置定时器模式,设置初始值,启动定时器。
- 点亮模块:通过寄存器操作,控制 LED 灯的点亮状态。
- 中断响应模块:在中断服务程序中,实现 LED 状态的更新。
流程图
为了更直观地展示程序的运行逻辑,我绘制了两种模式的流程图:
模式一:定时器模式 1
在模式 1 中,定时器以 16 位计数器的方式运行,通过计算得到的初值实现 1 秒的定时。
模式二:定时器模式 2
在模式 2 中,定时器以自动重装载的方式运行,通过两个嵌套的计数器实现 1 秒的定时。
实现细节
(1)初始化模块
初始化模块是整个程序的基础,它负责配置定时器的模式、设置初值,并启动定时器。以下是模式一的初始化代码:
PT0M0:
MOV TL0, #0F0H ; 将定时器0的低位TL0设置为0F0H
MOV TH0, #0D8H ; 将定时器0的高位TH0设置为0D8H,一次中断10000ms
SETB ET0 ; 设置定时器0的中断允许位,允许定时器0中断
SETB EA ; 设置全局中断允许位,允许中断
SETB TR0 ; 启动定时器0
RET
(2)点亮模块
点亮模块通过操作寄存器 `A` 和端口 `P2`,控制 LED 灯的点亮状态。以下是代码片段:
MOV P2, A ; 将累加器A的值传送到端口P2
RL A ; 将寄存器A的值左移,实现LED灯的循环点亮
(3)中断响应模块
中断响应模块是跑马灯效果的核心。每当定时器中断触发时,程序会进入中断服务程序,更新 LED 灯的状态。以下是模式一的中断服务程序:
ITOP:
MOV TL0, #0F0H ; 重新加载定时器初值
MOV TH0, #0D8H
DJNZ R1, CONTINUE ; 减少计数器R1的值,如果R1不为0,则跳转到CONTINUE
MOV R1, #65H ; 重新设置计数器R1的值
MOV P2, A ; 更新LED灯的状态
RL A ; 左移寄存器A的值
RETI ; 从中断返回
(4)汇编代码实现
以下是实现上述功能的汇编代码:
模式一:
ORG 0000H
MOV A, #11111110B ; 将累加器A设置为11111110
MOV R1, #65H ; 设置寄存器R1值
RESET:
AJMP MAIN ; 跳转到MAIN标签
ORG 000BH
AJMP ITOP
; 主程序
ORG 0100H
MAIN:
MOV TMOD, #01H ; 设置定时器模式寄存器TMOD为01H,配置定时器0为模式1
ACALL PT0M0 ; 调用PT0M0子程序
HERE:
SJMP HERE ; 创建了一个无限循环,程序会一直跳转到HERE标签
; 定时器0初始化子程序
PT0M0:
MOV TL0, #0F0H ; 将定时器0的低位TL0设置为0F0H
MOV TH0, #0D8H ; 将定时器0的高位TH0设置为0D8H,一次中断10000ms
SETB ET0 ; 设置定时器0的中断允许位,允许定时器0中断
SETB EA ; 设置全局中断允许位,允许中断
SETB TR0 ; 启动定时器0
RET
ITOP:
MOV TL0, #0F0H ; 将定时器0的低位TL0设置为0F0H
MOV TH0, #0D8H ; 将定时器0的高位TH0设置为0D8H
DJNZ R1, CONTINUE ; 减少R1的值,如果R1不为0,则跳转到CONTINUE
MOV R1, #65H ; 将R1重新设置为65H
MOV P2, A ; 将累加器A的值传送到端口P2
RL A
RETI ; 从中断返回
CONTINUE: ; 继续执行的标签
RETI ; 从中断返回
END
模式二:
ORG 0000H
MOV A, #11111110B ; 将累加器A设置为11111110
MOV R1, #65H ; 设置寄存器R1值为101
MOV R2, #33H ; 设置寄存器R2值为51
RESET:
AJMP MAIN ; 跳转到MAIN标签
ORG 000BH
AJMP ITOP
; 主程序
ORG 0100H
MAIN:
MOV TMOD, #02H ; 设置定时器模式寄存器TMOD为02H,配置定时器0为模式2
ACALL PT0M0 ; 调用PT0M0子程序
HERE:
SJMP HERE ; 创建了一个无限循环,程序会一直跳转到HERE标签
; 定时器0初始化子程序
PT0M0:
MOV TL0, #038H ; 将定时器0的低位TL0设置为038H,一次中断为200ms
MOV TH0, #038H ; 将定时器0的高位TH0设置为038H
SETB ET0 ; 设置定时器0的中断允许位,允许定时器0中断
SETB EA ; 设置全局中断允许位,允许中断
SETB TR0 ; 启动定时器0
RET
ITOP:
DJNZ R1, CONTINUE_1 ; 减少R1的值,如果R1不为0,则跳转到CONTINUE
DJNZ R2, CONTINUE_2
MOV R1, #65H ; 将R1重新设置为65H
MOV R2, #33H ; 将R2重新设置为33H
MOV P2, A ; 将累加器A的值传送到端口P2
RL A
RETI ; 从中断返回
CONTINUE_1: ; 继续执行的标签
RETI ; 从中断返回
CONTINUE_2:
MOV R1, #65H
RETI
END
实验结果
通过调试和验证,跑马灯效果成功实现!LED 灯按照设定的顺序依次点亮,形成了一种动态的视觉效果。无论是模式一还是模式二,都能实现精准的 1 秒间隔控制,让跑马灯的效果流畅而稳定。
跑马灯效果图
总结与思考
通过这次实验,我收获颇丰:
1. 汇编语言基础:进一步掌握了 8051 微控制器的汇编语言,包括指令集、寄存器操作和程序结构。
2. 定时器与中断:学会了如何配置和使用 8051 的定时器,以及如何编写和处理中断服务程序。
3. 硬件控制:掌握了如何通过编程控制微控制器的 I/O 端口,这对于嵌入式系统开发至关重要。
4. 调试与测试:学会了如何调试和测试汇编程序,包括识别和修复代码中的错误。
这次实验不仅让我在技术上有了新的突破,更让我对嵌入式系统开发充满了兴趣。汇编语言虽然古老,但它依然有着强大的生命力,能够帮助我们实现各种奇妙的功能。
如果你对汇编语言或嵌入式开发感兴趣,欢迎一起交流探讨!让我们在技术的道路上共同进步,继续探索更多神奇的魔法!✨