第6 章 控制 电路 设计 与 仿真
控制电路被广泛地应用于电子设备的控制中,以单片机、嵌入式处理器为核心的控制电路成为主流的设计方案。针对单片机、嵌入式处理器的设计及仿真软件,可以在硬件设计前期进行高效的开发和验证,大大缩短了系统开发的时间,提高了设计效率。
本章主要介绍 Proteus VSM 单片机仿真,内容包括常用的 MCS-51 单片机以及 Atmel AVR 单片机的仿真。针对单片机及其外设与 Proteus 中的单片机仿真调试展开内容。针对 MCS-51 系列单片机,内容涉及 Proteus 中的仿真环境建立、Proteus 中的仿真调试以及与 Keil $ \mu $Vision 开发环境的联合仿真调试等内容。针对 Atmel AVR 单片机,内容涉及在 Proteus 中建立仿真环境、在 Proteus 中进行仿真调试以及与 IAR EWB for AVR 工具进行联合仿真调试等内容。
6.1 Proteus 系统仿真基础
Proteus VSM(Proteus virtual system modeling)软件是 Proteus 单片机仿真的核心。其提供了针对单片机同时进行高级语言和低级语言仿真的能力。同时,对单片机的仿真可以在对电路中的其他部分进行 Spice 仿真的同时进行。因此,Proteus VSM 是一种对数字系统和模拟系统进行混合仿真的开发环境。在仿真时,它将 Spice 混合仿真、具有动画效果的仿真元器件、微处理器系统模型综合在一起进行协同仿真。通过使用 Proteus VSM,可以在进行真实的原形系统开发前,在 Proteus VSM 软件中对设计进行初步的验证。设计者在设计时可以通过与仿真屏幕上的各种具有动画效果的仿真元器件(如 LED 元器件、LCD 显示元器件)以及各种可以在仿真时改变状态的元器件(如开关元器件、按钮元器件)进行交互。如果设计者使用的 PC 性能较好,则可以在仿真时得到近乎实时的仿真效果。例如,使用 Pentium III 1GHz 的处理器可以仿真一个以 12MHz 时钟频率运行的 51 单片机系统。Proteus VSM 还提供了各种调试的手段,例如在汇编语言调试和高级语言调试时都可以设置断点,单步运行,调试时观察各种变量值。这些手段极大方便了设计者进行数字原型设计的能力。进
行单片机系统的仿真,Proteus VSM 需要具有设计、仿真、分析与测量、调试以及诊断功能。下面分别进行介绍。
1. 设计
Proteus VSM 使用经过验证的原理图编辑工具 Proteus ISIS(如图 6-1 所示)进行设计输入和开发。Proteus ISIS 是一个久经考验的设计工具。它是一个结合了易用性和强大功能性的编辑工具。在 Proteus ISIS 中,同时提供了原理图编辑和仿真两个功能。因此,Proteus ISIS 是单片机仿真的基础和设计输入的工具。

Proteus ISIS 还提供了各种对原理图中对象外观的高级控制功能,如线宽、填充模式、字体等。这些功能在进行电路仿真动态呈现时是十分必要的。
2. 仿真
Proteus VSM 中最激动人心和最重要的特征是它可以仿真运行在单片机上的软件和任何单片机外部的模拟量或数字量之间的交互。图 6-2 所示是 Proteus VSM 中仿真一个国际象棋嵌入式软件的页面。在该图中,设计者可以用鼠标在 LCD 显示模块上进行操作,如移动棋子。在仿真前,设计者可以在原理图上绘制需要仿真的单片机仿真模型与用于产生所需要模拟量和数字量的其他电路模型。而在仿真时,它可以让代码像在物理元器件中运行一样在单片机系统中运行。如果程序代码向某个端口写入数据,那么端口上的逻辑电平将会做相应的改变;如果在仿真时出现在某个输入端口的状态被某个电路改变,那么这个改变也将会被单片机中的代码观察到。这一切就像在真实的物理原型系统上运行一样。
Proteus VSM 的 CPU 模型可以完整地仿真单片机的 I/O 端口、中断、计时器、通用同步/异步串行接收/发送器以及其他在 Proteus VSM 所支持的处理器中的外设。在 Proteus VSM 中,由于所有的一切都是软件模拟器,因此所有单片机、外设、原理图中的
其他外部电路模块(模拟的或者数字的)都在波形的级别上建模以进行统一的仿真,因此数字系统和模拟系统的交互与仿真可以同时进行。

在 Proteus VSM 中有超过 700 种单片机及其变种的仿真模型,以及几千种具有 Spice 模型,可以进行单片机仿真的外设库。这些仿真模型使 Proteus 成为最方便使用的数字系统以及数模混合系统的模拟工具,因此 Proteus VSM 仍然是目前最具潜力和使用最广泛的嵌入式系统模拟工具。
3. 分析与测量
在产品原型设计中,需要一系列的电子仪器和仪表。Proteus VSM 中包含各种常见的虚拟仪器,如图 6-3 所示,包括虚拟示波器、逻辑分析仪、函数信号发生器、模式信号发生器、虚拟终端等,还包括简单的仪表,如电压表、电流表。此外,还有主模式、从模式、监控模式的协议分析仪来对 $ I^{2}C $、SPI 等协议进行分析。这些虚拟仪器可以极大地方便数字系统的原型设计,使产品的软件代码在物理原型完成之前得到大部分的验证,极大地缩短了开发周期。而且因为各种仪器仪表相当昂贵,Proteus VSM 提供的这些虚拟仪器也极大地节约了开发成本。
4. 调试
调试在嵌入式软件开发中是一个重要的话题。由于嵌入式软件的特殊性,其调试也远比调试PC上的软件要复杂。尽管Proteus VSM提供了近乎实时的模拟运行环境,但

是真正释放 Proteus VSM 的能力是它的调试功能。Proteus VSM 提供了单步运行的功能,就像在 Visual Studio 中单步执行代码去寻找错误一样。在单步运行的过程中,设计者可以观察包括单片机以外的外部电路的整个系统的运行。这是在真实的物理原型系统上无法做到的。因为在真实的物理原型中,单片机可以单步运行或者停止,但是外部电路却不受调试器的限制,尤其是外部的模拟电路。
调试中的另一个重要功能是观察点(watchpoint)。观察点运行调试器监视寄存器、内存变量等值的变化,当被观察的变量或者寄存器取某个特殊的值或者使某个逻辑表达式为真时,调试器可以暂停程序代码的运行。使用观察点可以方便地调试类似算术溢出之类的错误,如图6-4所示。
5. 诊断
Proteus VSM 具有复杂的诊断和跟踪功能。这些功能允许设计者指定某些元器件或者处理器外设在任何时间接收关于活动和系统交互的详细文本报告,如图 6-5 所示。除了诊断和跟踪片内设备外,设计者还可以指定单片机的外设(如 SPI 控制器)或者片外的某个设备(如内存芯片、LCD 显示模块等)进行诊断和跟踪。
同时对片内和片外的设备进行诊断和跟踪对调试来说是非常有意义的功能,这将帮
电路设计、仿真与PCB设计


助设计者定位和修复软件缺陷和硬件设计上的错误。而且这个过程比在真实的物理原型上进行要快速和廉价得多。
6.2 Proteus 中的单片机模型
Proteus 中内置了各种单片机模型,包含 8051 单片机及其各种变种、8086 微处理器、基于 ARM7 内核的各种单片机、AVR 系列单片机、摩托罗拉的 HC11 系列、TI 的 MSP430 系列单片机、Microchip 的 PIC 系列单片机等。这些单片机模型具有完整的 CPU 内核仿真功能,以及内置于单片机的各种外设模块的仿真,例如输入/输出外设、中断控制器外设、用于同步串行通信的 $ I^{2}C $/SPI 控制器外设等。
在 Proteus VSM 中,所有的仿真模型都是以动态链接库的形式提供的。例如,MCS8051 单片机的仿真模型就由 MCS8051.DLL 提供。
1. MCS8051 单片机模型
单片微型计算机简称为单片机,又称为微型控制器,是微型计算机的一个重要分支。单片机是20世纪70年代中期发展起来的一种超大规模集成电路芯片,是将CPU、RAM、ROM、I/O接口和中断系统集成于同一芯片上的元器件。MCS8051是一种8位的单芯片微控制器,属于MCS-51单芯片的一种,由Intel公司于1981年制造。Intel公司将MCS8051的核心技术授权给很多其他公司,所以有很多公司在做以MCS8051为核心的单片机,如Atmel、飞利浦、深联华等公司,相继开发了功能更多、更强大的兼容产品。
MCS8051.DLL 提供了标准80C51和80C52单片机及其各种变种的仿真支持。将来也可能添加对其他51系列单片机的支持。MCS8051系列单片机仿真模型的属性如表6-1所示。
| 属性 | 默认值 | 描述 |
| PROGRAM | 无 | 这个属性指明了在仿真时要加载到仿真模型中的一个或多个程序文件。程序文件可以是 Intel HEX 格式,也可能是 OMF51 目标文件格式。如果需要指明有多个文件需要加载,那么用逗号分隔多个文件名 |
| CLOCK | 12MHz | 该属性指明仿真目标处理器运行的频率。因为效率的原因,时钟电路并没有被仿真,仿真时的处理器运行频率仅由这个属性值指出 |
| DBG_FETCH | FALSE | 如果这个属性设置为真,在仿真时模型将会从外部存储器取指令运行。这样取指令导致外部总线操作,而这些操作都需要进行仿真。所以会导致处理器运行异常缓慢,但这也是一种测试外部程序存储器的方式 |
| DATARAM | 无 | 该属性支持内存映射中对应外部数据存储器的位置。如果要使用外部数据存储器,那么必须指明该位置,这会加速仿真的运行 |
| CODERAM | 无 | 该属性指明内存映射中对应外部程序存储器的位置(如果是冯·诺依曼结构的单片机,那么该属性也是数据存储器的位置) |
2. AVR 单片机模型
1997 年,由 Atmel 公司挪威设计中心的 Alf-Egil Bogen 与 Vegard Wollan 利用 Atmel 公司的 Flash 新技术共同研发出 RISC 的高速 8 位单片机,简称 AVR。RISC(精简指令系统计算机)是相对于 CISC(复杂指令系统计算机)而言的。RISC 并非只是简单地减少了指令,而是通过使计算机的结构更加简单合理而提高了运算速度。RISC 优先选取使用频率最高的简单指令,避免复杂指令,并固定指令宽度,减少指令格式和寻址方式的种类,从而缩短指令周期,提高运行速度。由于 AVR 采用了 RISC 的结构,使 AVR 系列单片机都具备了 1MIPS/MHz 的高速处理能力。
Proteus VSM 所提供的 AVR 模型可以仿真一系列来自 Atmel 公司的 AVR 单片
机,包括 $ \text{ATTiny} $、 $ \text{AT90S} $、 $ \text{ATMEGA} $等。
AVR 单片机的一个特点就是其指令集为 C 语言优化,这在 8 位单片机中是较少见的。即使是只有 2KB 程序存储器的 AT Tiny 系列,也可以使用 C 语言进行开发。Proteus VSM 中的 AVR 模型支持三种 C 语言编译器:
(1)Imagecraft 和 CodeVision C 编译器。使用该编译器时支持 COFF 目标文件格式。
(2)WINAVR/GNU 编译器。使用该编译器时支持 ELF/DWARF 目标文件格式。
(3)IARC 编译器。使用该编译器时支持 UBROF 目标文件格式。
COFF 目标文件格式也是一个支持符号调试的目标文件格式,它也被 AVR Studio 支持。该格式的目标文件以“.COF”作为扩展名。要加载 COFF 格式的目标文件到仿真模型中,也是通过 PROGRAM 属性来声明。例如,在仿真模型中设置 PROGRAM = MYFILE.COF。需要注意的是,COFF 文件格式中包含编译时要引用的 C 语言程序文件的绝对路径。如果将整个工程复制或移动到其他文件夹,那么 COFF 文件需要重新编译生成。
ELF/DWARF 格式是一个由 AVR Studio 和开源的 GNU 工具链共同支持的目标文件格式。如果使用这种目标文件格式,那么 ELF 文件和 DWARF 文件必须和 ISIS 设计文件放置在同一个目录下。在仿真前,将要加载的目标文件通过 PROGRAM 属性指明,例如 PROGRAWNMYFILE.ELF。需要注意的是,Proteus VSM 支持的 DWARF 是 DWARF2 格式,如果该格式不是默认的输出格式,那么需要在编译时通过编译选项强制其生成 DWARF2 格式的文件。
UBROF 格式是 IAR 公司独有的目标文件格式。它也支持通过调试器进行符号调试。这种目标文件格式的文件以“.D90”作为扩展名。AVR 的 UBROF 格式包含完整的符号调试信息,包括源文件的位置、文件名、符号对应的源代码行的位置等信息。要将 UBROF 格式的目标文件加载进仿真模型,需要设置模型的 PROGRAM 属性,例如 PROGRAM=MYFILE.D90。与 COFF 格式类似,UBROF 格式中的符号调试信息包含的是绝对路径信息,因此在将工程文件移动或复制到其他文件夹后,需要重新编译整个工程。
这三种支持 AVR 单片机的编译器都得到了广泛的应用。在本章后面的内容中,将以 IAR C for AVR 编译器作为编译器设计 AVR 单片机中的固件程序。
6.351 系列单片机系统仿真
本节通过设计实例介绍基于8051系列单片机的设计和仿真。在本节中将通过Proteus原理图工具绘制仿真原理图,并在Proteus VSM中仿真运行,最后介绍Keil $ \mu $Vision与Proteus VSM进行联合调试的方法。
6.3.1 51系列单片机基础
MCS8051系列单片机中8051是基本型,包括8051、8751、8031、8951,这4个机种的区别仅在于片内程序储存器。8051为4KB ROM,8751为4KB EPROM,8031片内无程
序储存器,8951为4KB EEPROM。其他性能结构一样,有片内128BRAM、2个16位定时器/计数器和5个中断源。其中,8031由于片内无存储器,因而性价比更高,又易于开发,在过去应用广泛。但是8031系列需要外扩程序存储器,需要额外的ROM/Flash芯片存储程序。在20世纪90年代后,由于集成电路集成度的提高,多家厂商推出了价格便宜的内置Flash存储器的8051系列单片机,由于其性价比得到了提高,因此8031系列逐渐淡出市场。
MCS8051 系列在结构上具有以下特点:
(1) 8 位 CPU 内核。
(2) 片内带振荡器, 频率范围为 $ 1.2 \sim 12 \, MHz $。
(3)片内带 128B 的数据存储器。
(4)片内带 4KB 的程序存储器。
(5)程序存储器的寻址空间为 $ 64 \, KB $。
(6)片外数据存储器的寻址空间为 $ 64 \, KB $。
(7)128个用户位寻址空间。
(8)21B 的特殊功能寄存器。
(9)4个8位的 I/O 并行接口:P0、P1、P2、P3。
(10)两个16位定时/计数器。
(11)具有两个优先级别的 5 个中断源。
(12)一个全双工的串行 I/O 接口,可多机通信。
(13)111条指令,包含乘法指令和除法指令。
(14)片内采用单总线结构。
(15)有较强的位处理能力。
(16)采用单一的+5V电源。
除了 MCS8051 系列之外,还出现了 MCS8052 系列单片机。52 系列是增强型,有 8032、8052、8752、8952 等类型。例如,MCS8052 的 ROM 为 8KB,RAM 为 256B,片内 RAM 资源和 ROM 资源都比 MCS8051 多了一倍。此外,MCS8052 比 MCS8051 多了一个定时器/计数器,增加了一个中断源。
MCS8051 系列单片机的一个特点是具有位操作指令。位操作指令将其他指令集修改某个内存或寄存器位时需要进行的“读—修改—写”操作简化为一个位操作。这在 MCS8051 刚出现的时代是一个较大的进步,因为它不仅提高了位操作的速度,而且在这个特性的帮助下,能够将代码减少 30% 左右。这在存储器价格昂贵的时代是一个能够较好地提高性价比的特性。MCS8051 的另一个特点是将内部的寄存器分为 4 个组 (bank),这个特点使 MCS8051 在进行终端服务时可以不必保存当前的寄存器,而只是切换寄存器组即可。而切换寄存器组的操作仅需要一条指令,这使得中断可以得到快速响应。
MCS8051 单片机内部结构如图 6-6 所示。在开始以 MCS8051 为基础的电子设计进行编程之前,首先对其内存体系结构、内部/外部存储器、指令集等进行介绍。

6.3.2 在 Proteus 中进行源程序设计与编译
本节在最小系统上学习 Proteus VSM 中的单片机仿真调试。首先在 Proteus ISIS 中建立如图 6-7 所示的 AT89C51 最小系统。其中元器件 X1 为振荡晶体,其在 Proteus ISIS 中的元器件名为 XTAL,C1 和 C2 的容值为 22pF,C3 的容值为 $ 10\mu F $,R1 的阻值为 $ 1.0k\Omega $。
首先在记事本中输入如下的源程序:
ORG OH
MAIN: MOV A, #10 ;加载立即数#10到累加器A
MOV R0, #5 ;加载立即数#5到寄存器R0
ADD A, R0 ;寄存器相加,结果保存在累加器A中
JMP MAIN ;跳转到MAIN,MAIN为标号,在此程序中其代表的地址为#OH
END
将其命名为 proteus_exam1.ASM,并保存在与该设计相同的目录下。

在 Proteus 中按照下面的步骤编译并调试该程序:
(1)选择工具栏中的 Source Code 按钮 ,在新标签 Source Code 中,选择 Project→Create Project 命令。如图 6-8 所示进行设置,不要勾选 Create Quick Start Files。如图 6-9 所示,选择 Project→Add Files 命令,为刚才新建的 Project 添加文件 proteus_exam1.asm。

(2)选择主菜单中的 Build→Build Project 命令。Proteus 将会调用 51 汇编器将源文件编译为 HEX 格式的目标文件。成功执行后,将会弹出如图 6-10 所示的窗口显示编译日志。
(3)双击原理图中的 U1 元器件,打开如图 6-11 所示的元器件属性。确认其中 Program File 属性的值为 DEBUG.HEX。如果不是,则单击其旁边的 ☑ 图标,选择正确的 HEX 文件。
(4)单击 ▶ 按钮开始仿真,然后单击 ☐ 按钮暂停。
(5)选择菜单栏中的 Debug 菜单,在列表底部选择 8051CPU,其中有 4 个命令,如图 6-12 所示。
① 选择 Source Code-U1 菜单项将显示源代码调试窗口,如图 6-13 所示。
电路设计、仿真与PCB设计





② 选择 Registers-U1 菜单项将显示 8051 处理器的寄存器窗口,如图 6-14 所示。
③ 选择 Internal(IDATA) Memory-U1 菜单项将显示内部 RAM 窗口,如图 6-15 所示。

④ 选择 SFR Memory-U1 菜单项将会显示 8051 内部 SFR 寄存器窗口,如图 6-16 所示。
(6)源代码调试窗口中,其工具栏中的各功能如下:
① 代表运行,单击该按钮将继续程序运行。
② 代表 Step Over Source Code,单击该按钮将单步执行当前行的程序。
③ 代表 Step Into Source Code,单击该按钮将单步执行当前行的程序,如果当前行代码是函数调用,则执行调用指令,进入被调用函数的第一行,并暂停。
④ $ \underline{ 📐 } $代表 Step Out From Source Code,单击该按钮将从当前调用栈中退出,返回到上一层调用该函数的那一行程序。

⑤ 代表 Run To Source Line,单击该按钮,则调试器会让程序运行,直到运行到当前选择的那一行停止,这相当于在当前选择的程序行设置了临时断点。
⑥ 代表 Toggle Breakpoint,单击该按钮将在当前已选择的程序行中设置或者取消断点。这里单击 ☑ 按钮,一次执行一行代码,并观察 8051 CPU 寄存器窗口的变化。该程序将立即数 #10 加载到累加器 A 中,将立即数 #5 加载到寄存器 R0 中,然后将 A 与 R0 相加,结果存放在累加器 A 中。因此在执行程序的过程中将会看到累加器 A 中的值的变化过程。
经过这个实验可以看到,Proteus VSM 具有和其他开发环境几乎一样的编程调试环境,具有加载程序到单片机、运行、暂停、单步执行、设置断点等功能。这些功能配合它的模拟及数字电路仿真功能,为设计者提供了一个相对完整的虚拟嵌入式系统实验室。
6.3.3 在 Keil $ \mu $Vision 中进行源程序设计与编译
上一节中的汇编语言程序也可以在 Keil $ \mu $Vision 中编译和调试。在学习 Proteus 与 Keil $ \mu $Vision 联合调试之前,先介绍如何在 Keil $ \mu $Vision 中建立工程。本书选择使用 Keil C51 V9.5 来介绍如何使用 Keil $ \mu $Vision。
Keil C51 是美国 Keil Software 公司开发的 51 系列兼容单片机 C 语言软件开发系统,与汇编语言相比,C 语言在功能、结构性、可读性、可维护性上有明显的优势,因而易学易用。Keil 提供了包括 C 编译器、宏汇编、链接器、库管理和一个功能强大的仿真调试器等在内的完整开发方案,通过一个集成开发环境 ( $ \mu $Vision) 将这些部分组合在一起。其方便易用的集成环境、强大的软件仿真调试工具会使开发设计工作事半功倍。
Keil C51 使用 Keil $ \mu $Vision 5 作为编辑器和集成开发环境。它的主窗口如图 6-17 所示。首先需要使用的是主菜单中的 Project 菜单。按照如下步骤进行 Keil $ \mu $Vision 项目的创建、编译和调试。

(1)在 Keil $ \mu $Vision 中创建工程。在主菜单中选择 Project $ \rightarrow $New $ \mu $Vision Project 命令,弹出如图 6-18 所示的对话框。在该对话框中选择项目路径,并在“文件名”栏中填写项目名称。本例中使用 proteus_exam1 作为项目名称,然后单击“保存”按钮保存。
(2)只有 Keil $ \mu $Vision 会弹出如图 6-19 所示的对话框要求选择该项目所使用的单片机型号。本例中选用的是 Atmel 公司的 AT89C51 单片机,然后单击 OK 按钮。
(3)Keil $ \mu $Vision 弹出如图 6-20 所示的提示框。在该提示框中单击“否”按钮。STARTUP. A51 是 Keil $ \mu $Vision 提供的启动文件。因为本例中的汇编语言程序过于简单,所以并不需要使用 Keil $ \mu $Vision 提供的启动文件进行初始化的工作。



(4)下一步工程文件就创建完毕了。在主窗口的 Project 窗格中可以看到,目前还没有源文件被添加到工程中。因此接下来要创建源文件。在图 6-21 所示的窗格中,右击 Source Group 1,选择 Add New Item to 'Source Group 1' 将会弹出源文件向导,如图 6-22 所示。在该对话框左侧选择 Asm File(s),并在 Name 输入框中输入 proteus_exam1 作为文件名,在 Location 输入框中输入工程所在的路径,最后单击 OK 按钮。


(5)将程序代码输入刚创建的文件中,如图6-23所示。可以看到,Keil $ \mu $Vision 的源代码编辑器具有语法高亮的功能,这样可以清晰地区分各个语法单元,以减少输入错误的可能。
(6)选择主菜单中的 Project→Build Target 命令,对工程文件进行编译。Keil $ \mu $Vision 将会在窗口下方的 Build Output 窗口中显示出编译日志,如果源

程序有语法错误或者工程设置有不正确的地方,那么编译器就会给出提示,并在编译日志中显示出来。从如图 6-24 所示的编译日志可见,工程编译正确,生成了代码,并给出了编译后的代码所使用的各种存储器的大小。这些代码编译后有 7 字节长。

(7)如果需要在 Keil $ \mu $Vision 中调试该程序,首先在 Keil $ \mu $Vision 中设置调试选项。在 Project 窗格中的 Target 1 上右击,在弹出的快捷菜单中选择 Option 命令。在弹出的对话框中选择 Debug 标签,确认 Use Simulator 被选中,如图 6-25 所示。然后单击 OK 按钮确认修改。

(8)在主菜单中选择 Debug→Start/Stop Debug Session 命令。Keil $ \mu $Vision 将切换窗口布局,如图 6-26 所示。其中 Disassembly 窗口显示的是从编译生成的目标文件中反汇编得到的机器指令,其下方是被调试的源代码。在进行调试时,反汇编窗口和源代码窗口将同步更新。执行到某一行时,反汇编窗口将同步显示当前源代码对应的汇编语言。Register 窗口显示的是当前被调试的 CPU 中的寄存器。从图中可以看到,当前 PSW 为 0,因此该窗口中的 R0~R7 这 8 个寄存器是 Bank0 的寄存器。

Keil $ \mu $Vision 对所支持的单片机的外设也具有部分调试功能。但其与 Proteus 相比功能过于简单。它可以以图形化的形式显示 AT89C51 中各外设的状态,包括外设寄存器以及外设所对应的端口。但是这些信息在仿真时是基于对于外设寄存器的状态读取值来确定的,而并不是对外设进行仿真得来的。从这一点来说,Keil $ \mu $Vision 并不能对外设进行仿真。它所仿真的仅仅是 AT89C51 的处理器内核。
选择 Keil $ \mu $Vision 主菜单的 Peripherals $ \rightarrow $ I/O Ports $ \rightarrow $ Port 0 命令,可以打开 AT89C51 的 I/O 端口外设窗口,如图 6-27 所示。此时端口的状态以图形化的方式呈现。

(9)在调试布局下,工具栏中与调试运行有关的按钮有8个。其中, $ \uwave{\text{RST}} $ 代表将仿真器(被调试的处理器)复位,被调试程序将从复位向量处开始取指并执行; $ \underline{\text{圆}} $ 代表从当前程序指针(PC)所指向的位置开始执行; $ \uwave{\text{7}} $ 代表单步进入
被调用的子程序,如果下一步并不是子程序调用,则直接单步执行;⑦代表单步当前的代码行,不论当前的代码调用子程序与否,都执行到下一行代码并暂停。此处单击⑦图标进行单步运行,并观察寄存器窗口中寄存器值的变化。
本节介绍了使用 Keil $ \mu $Vision 建立工程、输入代码并进行简单调试的过程。通过与 Proteus 对比,可以看出两者各有长处。Keil $ \mu $Vision 是专门的软件开发工具,虽然它提供了对单片机的仿真功能,但是其仅仅能够仿真单片机的内核,也就是说只能仿真一个单片机最小系统。而 Proteus VSM 则能仿真一个完整的基于单片机的嵌入式电路板上的所有元器件,进行数字原型的开发。
6.3.4 Proteus 和 Keil $ \mu $Vision 联合调试
Keil $ \mu $Vision 与 Proteus 各有所长,Keil $ \mu $Vision 擅长调试,并提供了各种工程模板,具有出色的代码编辑功能,这些都是 Proteus 所不具备的。Proteus 具有强大的仿真能力,能够对一个完整的嵌入式设计的片内和片外的各种元器件进行仿真。如果将 Proteus 的硬件仿真能力与 Keil $ \mu $Vision 的源代码编写、软件调试功能结合起来,将很大程度上减轻设计者的负担,得到一个相对完美的虚拟嵌入式实验室。Proteus 提供了用外部软件对原理图中的微处理器进行调试的接口。本节将介绍如何配置 Proteus 和 Keil $ \mu $Vision 进行联合调试。
为了进行联合调试,首先需要设置 Keil $ \mu $Vision 环境,然后在 Proteus VSM 中设置使用远程调试器。
(1)将 VDM51.DLL 复制到 Keil C51 安装目录的 BIN 目录下(采用默认安装时,该目录是 C:\KEIL\C51\BIN)。VDM51.DLL 是 Proteus 提供的针对 MCS8051 系列单片机和 Keil C51 的远程调试代理。该动态链接库是需要加载进 Keil $ \mu $Vision 被调试器调用的,因此需要安装到 Keil C51 的安装目录中。
(2)修改 Keil C51 的 TOOLS-OLD.INI 文件,注册 VDM51.DLL。TOOLS.INI 文件如图 6-28 所示。从图中可以看到,所有 Keil $ \mu $Vision 支持的调试代理都在该文件中注册。
在该文件的最后添加如下一行内容:

添加后得到如图 6-29 所示的 TOOLS.INI 文件。

(3)关闭 Keil $ \mu $Vision,再重新启动 Keil $ \mu $Vision,加载之前创建的工程。
(4)在 Project 窗格中右击 Target1,在弹出的快捷菜单中选择 Option 命令,弹出如

(5)单击右边的 Settings 按钮,打开如图 6-31 所示的对话框。在对话框中需要设置的是 Host 和 Port 这两个属性。其中 Host 是指运行 Proteus VSM 的 PC 的 IP 地址。如果 Keil $ \mu $Vision 与 Proteus 不在同一个 PC 上运行,则要将 Host 修改为运行 Proteus 的 PC 的 IP 地址。Port 端口默认为 8000,不用修改。单击 OK 按钮结束设置。

在 Options for Target 对话框中同样单击 OK 按钮结束设置。
(6)回到 Proteus 软件中,选择主菜单中的 Debug → Use Remote Debug Monitor 命令,如图 6-32 所示,确保该子菜单选项被选择。
(7)回到 Keil $ \mu $Vision 中,选择主菜单中的 Debug $ \rightarrow $ Start/Stop Debug Session 命令开始调试。可以看到 Keil $ \mu $Vision 进入调试状态,窗口布局切换到调试模式,如图 6-26 所示。同时 Proteus VSM 也进入到如图 6-13 所示的仿真状态。
(8)如图6-33所示,在Keil $ \mu $Vision单步运行程序,可以发现Proteus中的源代码调试窗口也在同步更新,A寄存器的值和PC的值也在变化。
(9)在 Keil $ \mu $Vision 中结束调试,则 Proteus VSM 也退出仿真状态。


Keil $ \mu $Vision 与 Proteus 的结合使得 Proteus VSM 的软件开发与调试能力得到了提高。实际上,Proteus VSM 不仅能与 Keil $ \mu $Vision 进行联合调试,还可以与 IAR Workbench 进行联合调试。由于 Proteus VSM 具有内建的符号调试能力,且其支持多种在单片机开发中使用的目标文件格式。因此其还可以与 SDCC、WIN AVR、WIN ARM 等开源编译器协同工作,为嵌入式数字原型提供了便利的开发手段。
6.4 用 51 单片机实现电子秒表设计实例
本节以一个七段数码管显示的电子秒表为示例讲授使用 C51 开发语言与 Proteus 进行联合开发调试的方法和技巧。
如图 6-34 所示的电路以 AT89C51 为核心驱动 4 个七段数码管。由于 4 个七段数码管至少需要 28 个引脚,因此对 AT89C51 来说,资源非常紧张。为了节约使用 AT89C51 的引脚资源,该电路图中将 4 个七段数码管的 $ a \sim g $ 引脚复用,连接在 P0 端口上,再将 4 个七段数码管的公共端分别控制。这样,分时地驱动每个七段数码管。由于人眼的限制,如果每个七段数码管每秒更新 25 次以上,则人眼无法看到其闪烁。所以,需要在程序中分别控制 Q3~Q0 这 4 个公共端。

为此,在程序中使用定时器及其中断来完成七段数码管的刷新工作。AT89C51有3个定时器。本例中使用其Timer1作为刷新七段数码管的时间控制单元。在Timed的中断服务程序中进行数码显示。由于有4个七段数码管,则Timer1的中断服务程序需要每 $ 1000/(4\times25)=10ms $运行一次。在每次进入中断服务程序后,让某一个七段数码管显示字符。
由于该例子还是一个电子秒表,则在 Timer1 的中断服务程序中还应当对中断次数进行计数。每 100 次中断代表时间过去 1s,则数码管上显示的分秒值更新。图中的 M1M0 代表分钟数,S1S0 代表过去的秒数。
AT89C51 有 3 个定时器。本例中使用其中的定时器 Timer1。在 AT89C51 中,定时器使用时钟频率的 6 分频作为计数器工作频率。因此,当 AT89C51 工作在 12MHz 时钟时,要获得 10ms 的定时,计数器的计数值为 20000。因此在定时器模式下,Timer1 的重新装载值为 65535-20000=0xB1DF。因此必须使用 16 位的定时器。
在 AT89C51 中,每个定时器有 4 种模式,其中只有模式 1 使用 16 位计数器,因此本例中要将定时器 1 设置为模式 1。因此 TMOD 寄存器的高 4 位要设置为 1,TH1 要设置为 0xB1,TL1 要设置为 0xDF。由于计时器工作在模式 1,因此需要在定时器中断发生后重新加载 TH1 和 TL1 的值。
下面在 Keil $ \mu $Vision 和 Proteus VSM 中联合调试。为了运行这个例子,并在 Keil $ \mu $Vision 中调试,请按下列步骤进行。
(1)打开 Keil $ \mu $Vision,新建项目。
(2)选择 CPU 类型,此例选择 Atmel AT89C51。
(3)Keil $ \mu $Vision 提示是否将 STARTUP. A51 添加进项目中,单击 Yes 按钮确定。
(4)打开新建的项目,在 Project 窗格中右击 Target 1,选择添加新文件。在弹出的对话框中选择新建 C 文件。
(5)将源代码输入新建的 C 语言文件中。源代码请参考后文中的代码。
(6)在 Project 窗格中右击 Target 1,在弹出的快捷菜单中选择 Option for Target 1 命令,在弹出的对话框中选择 Output 标签。在此标签中选中 Create HEX File。
(7)选择 Debug 选项卡,在窗口的右侧选择 Use Proteus VSM 51 In-System Debugger,单击 OK 按钮保存修改。
(8)在 Project 菜单中选择 Build target 命令,编译此项目。
(9)在 Proteus 中按照图 6-34 所示建立仿真原理图。
(10)在 Proteus 中,选择主菜单中的 Debug→Enable remote debug monitor 命令,并确认 Enable remote debug monitor 已被选中。
(11)在 Keil $ \mu $Vision 中,选择主菜单中的 Debug $ \rightarrow $Start/Stop Debug Session 命令开始仿真调试。
该例子程序源代码如下:
#include
static const char num7seg[] = {0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};
volatile int minute;
volatile int second;
void init_timer1(void)
{
TMOD &= 0x0F; //设置 Timer 1 工作模式
TMOD |= 0x10;
TH1 = 0xB1; //计数器计数值,高 8 位
TL1 = 0xDF; //计数器计数值,低 8 位
ET1 = 1; //Timer1 中断
}
电路设计、仿真与PCB设计
TR1 = 1; //开启计时器
void led_disp(void)
{
char m1, m0, s1, s0;
char pattern;
static char dcnt = 0;
m1 = minute / 10;
m0 = minute % 10;
s1 = second / 10;
s0 = second % 10;
dcnt = (dcnt + 1) % 4;
switch (dcnt) {
case 0: pattern = num7seg[s0]; break;
case 1: pattern = num7seg[s1]; break;
case 2: pattern = num7seg[m0]; break;
case 3: pattern = num7seg[m1]; break;
}
P0 = ~pattern; //输出七段数码管段位值(a~g)
P1 = 0x1 << dcnt; //输出LED数码管公共端控制信
}
void time_count(void)
{
static int count = 0;
count++;
if (count == 100)
{
second++;
count = 0;
}
if (second == 60)
{
minute++;
second = 0;
}
if (minute == 60)
{
minute = 0;
second = 0;
}
}
void timer1_isr() interrupt 3
{
TF1 = 0; //清除中断标志
TH1 = 0xB1; //重新加载计数值,高8位
TL1 = 0xDF; //重新加载计数值,低8位
time_count(); //计时
led_disp(); //LED分时显示
}
void main(void)
{
minute = 0;
second = 0;
init_timer1(); //初始化定时器1
EA = 1; //开中断
while (1)
{
}
仿真开始后,可以看到4个七段数码管开始从00:00显示时间,如图6-35所示。

由于4个七段数码管是分时显示的,因此在仿真时会有闪烁,且由于任何时刻只有一个数码管显示字符,在截图中只能看到一个数码管是点亮的。
6.5 AVR 系列单片机仿真
AVR 单片机是 Atmel 公司 1997 年推出的 RISC 单片机。RISC(降低复杂性指令集计算机)是相对于 CISC(复杂指令集计算机)而言的。RISC 并非只是简单地减少指令,而是通过使计算机的结构更加简单合理而提高运算速度。RISC 优先选取使用频率最高的简单指令,避免复杂指令,并固定指令宽度,减少指令格式和寻址方式的种类,从而缩短指令周期,提高运行速度。由于 AVR 采用了 RISC 这种结构,使 AVR 系列单片机都
具备了 1MIPS/MHz 的高速处理能力。高速度、为高级语言开发优化以及高可靠性是 AVR 单片机优于之前的各种单片机的主要特点。
6.5.1 AVR 系列单片机基础
AVR 单片机采用了一种修改过的哈佛结构,在该结构中,指令和代码存储在不同的物理存储器中,而且指令存储器和数据存储器也分别位于不同的地址空间(程序空间和数据空间)。因此与其他哈佛结构的微处理器类似,AVR 有两条总线:数据总线和指令总线。但是在 AVR 指令集中也提供了某些特殊的指令用于从程序空间读取数据。
AVR 单片机主要分为以下几个系列:
(1)tinyAVR。0.5~16KB程序存储器,6~32个引脚,有限的外设。
(2)megaAVR。4~256KB程序存储器,2~100个引脚,扩展的指令集(主要包含乘法指令和用于处理大内存的指令)。
(3)XMEGA。4~256KB的程序存储器,44~100个引脚,用于提高性能的外设(主要包含DMA、事件处理外设、加解密设备等)。
(4)AVR32。2006年,AVR推出了32位的AVR体系结构以及采用该体系结构的AVR单片机。AVR32与之前的AVR具有完全不同的体系结构。它具有32位宽度的数据总线、SIMD和DSP指令集,以及其他用于音频和视频处理的特点。AVR32的指令集与8位AVR有相似之处,但不完全兼容。
在嵌入式系统开发中,AVR单片机是很有特点的一种。它的特点如下:
(1)多功能的 GPIO 控制器。
(2)多种内部振荡器。
(3)集成在芯片内部的。
(4)可通过 JTAG 或 debugWIRE 进行在线调试。
(6)大容量的内部 RAM。
(5)内部集成 EEPROM。
(7)具有外部总线(不分型号)。
(8)多个定时器(8位或16位)。
(9)内部集成的模拟比较器。
(10)内部集成的 A/D 转换器(8 位精度或 12 位精度)。
(11)内建的支持多种串行通信协议的串行通信控制器(支持 $ I^{2}C $、SPI、RS232协议)。
(12)内建的看门狗电路。
(13)内建的 CAN 总线控制器。
(14)内建的 USB 控制器。
(15)内建的以太网控制器。
(16)内建的 LCD 控制器。
(17)内建的 AES 和 DES 模块(部分型号)。
(18) DSP 指令(32 位 AVR)。
AVR 单片机的特性十分丰富,这里不能一一列举。这些特点让其具有广泛的应用价值,其内建的设备降低了最终的 BOM 成本以及开发难度。例如,内建上拉和下拉控制的多功能的 GPIO 控制器可以减少外部电阻的数量,且使用更灵活;集成的 ADC、EEPROM、看门狗电路等降低了设计难度并使电路板更简单可靠;在对时钟要求不高时可以使用内部振荡器作为时钟源;可以通过指令编程的 Flash 存储器使得现场更新固件成为可能;在内部丰富的外设仍不能满足设计要求时,可以通过外部总线扩展外部设备。再加上 AVR 单片机本身的高性能、高可靠性以及低成本,使得 AVR 单片机在各种应用场合得到了广泛的应用。
6.5.2 Proteus 和 IAR EWB for AVR 联合开发
IAR EWB(embedded work bench)是业界流行的开发工具之一,是具有完整的编辑、编译、调试、烧写功能的工具。其中,IAR EWB for AVR是针对Atmel AVR系列单片机开发的,它支持目前所有的AVR单片机的嵌入式软件开发工作。
1. 在 IAR EWB for AVR 上建立新工程
可以将 IAR EWB 中的 Workspace 看作工程的容器。一个 Workspace 中可以包含多个工程,这些工程之间可能存在依赖关系。
(1)在 IAR EWB 中开始一个新工程首先要新建一个工作空间。
选择菜单栏中的 File→Workspace 命令建立工作空间,如图 6-36 所示。
注意,在 IAR 创建的项目的路径名不能包含中文,否则会在和 Proteus 的联合调试中出现乱码的错误。所以工作空间不要存放在中文路径下。
(2)选择主菜单中的 Project→Create New Project 命令新建工程,如图 6-37 所示。


(3)之后会弹出如图 6-38 所示的对话框,在该对话框中依次选择模板类型为 C 和 main 模板。IAR EWB 将使用该模板创建一个新的工程,工程中包含名为 main.c 的 C 语言源程序。

(4)单击 OK 按钮确认之后,IAR EWB 会提示用户保存新建工程,这里将其命名为 minimal-avr,如图 6-39 所示。之后 IAR EWB 会创建此工程,并且在此工程中生成一个名为 main.c 的源文件,如图 6-40 所示。

(5)配置该工程。在如图 6-40 所示的窗口中,左侧是 Workspace 窗格,该窗格里列出了所有包含的工程。这时只有唯一的一个名为 minimal-avr 的工程。在 minimal-avr 上右击,在弹出的快捷菜单中选择 Options 命令,如图 6-41 所示。IAR EWB 会弹出如图 6-42 所示的对话框。



在该对话框中,依次选择 General Options、Target、Processor configuration、AT90、AT90S8515。设置该 Project 使用的处理器是 AT90S8515,这是一款与 MCS8051 引脚兼容的 AVR 单片机,如图 6-43 所示。单击 OK 按钮确认修改。其他的所有选项目前保持默认值即可。

(6)由于 IAR EWB 已经为工程创建了一个 main.c,此时可以通过菜单 Project→Make 命令尝试编译该 Project。IAR EWB 在编译前会提示工作空间尚未保存,并弹出如图 6-44 所示的对话框要求保存工作空间。

(7)在对话框的“文件名”输入框中输入文件名,本例中使用 minimal-avr 作为工作空间的名字,然后,单击“保存”按钮保存。
编译后 IAR EWB 将在窗口下方的 Build 窗格中输出编译信息,如图 6-45 所示,表明新创建的工程编译成功。

2. 在 Proteus 中建立 AT90S8515 最小系统
AT90S8515 是 Atmel 公司兼容 MCS8051 引脚的单片机,它相对于 MCS8051 大幅提高了性能,具有更多的片内 RAM,集成了可由片内擦写的 FLASH 工艺的程序存储器,集成了增强型的外设,具有相同的抗干扰能力,因此 AT90S8515 是替代 MCS8051 系列单片机的理想选择。由于它与 MCS8051 引脚相互兼容,在不修改电路设计的情况下即可替换 MCS8051,因此也是老旧系统升级的理想选择。
本节中的最小系统使用 AT90S8515 为主要元器件。从图 6-46 中可以看到,它与图 6-7 中的 AT89C51 除了芯片型号之外其他都是相同的。该最小系统中除了 AT90S8515 外,只有几个阻容元器件,但是这样的最小系统足够在本节用于说明 IAR EWB 和 Proteus VSM 联合调试的过程。

3. 设计最小系统测试程序
本节使用 AT90S8515 定时器的 PWM 工作模式来作为例子。实验程序如下:
#include
void pwm_start()
{
OCR1AL = 0x40; /* 装载:PWM 宽度 */
OCR1AH = 0;
}
电路设计、仿真与PCB设计
DDRD | = (1 << 5); /* 设置 PortD.5 为输出 */
TCCR1A = 0x81; /* 8 位非翻转 PWM */
TCCR1B = 1; /* 启动 PWM */
}
int main(void)
{
pwm_start();
while (1);
}
打开前面创建的 IAR EWB 工程,将 main.c 中的代码替换为上面的测试程序,然后保存。
4. 在 IAR EWB for AVR 中编译并生成目标代码
接下来按 F7 键或者通过主菜单中的 Project→Make 命令编译工程。编译成功后,在主窗口的下方 Build 窗格中将显示如图 6-47 所示信息,代表编译成功。

5. 设置生成的目标文件格式
但是此时产生的目标文件还不能为 Proteus VSM 所用,还需要依下面的步骤设置 IAR EWB 生成 Proteus VSM 支持的目标文件格式。
(1)如图 6-48 所示,在 Project 窗格中单击 Release 或者 Debug 下拉列表,确认当前编译选项为 Debug。
(2)在工程名上右击,在弹出的快捷菜单中选择 Options 命令,如图 6-49 所示。
(3)在弹出的对话框中选择 Linker,在右侧的窗格中选择 Output 选项卡。
(4)选中 Override default,将输出文件命名为 minimal-avr.d90。
(5)在 Format 中选择 Other,然后在 Output format 中选择 ubrof8(forced)作为输出格式。
(6)重新编译工程。


6. 在 Proteus VSM 中调试
回到 Proteus VSM,在仿真原理图中双击 AT90S8515 元器件,将 Program File 属性设置为 IAR EWB 输出的目标文件,文件名为 minimal-avr.d90,如图 6-50 所示。

此时,单击 ▶ 按钮开始仿真。为了进行源代码调试,在开始仿真后单击 ∏ 按钮暂停,然后按如下步骤操作。如果该窗口显示空白,则单击窗口上方的文件名下拉列表框,选择文件 main.c。
(1)选择主菜单中的 Debug→AVR→Source Code-U1 命令,如图 6-51 所示。

(2)显示如图 6-52 所示的源代码调试窗口。

(3)在源代码窗口中右击,在弹出的快捷菜单中选择 Display Line Numbers 命令。
(4)在第14行代码处右击,在弹出的快捷菜单中选择 Toggle(Set/Clear)Breakpoint 命令,如图6-53所示。

单击 ▶ 按钮恢复程序运行,或者单击 ⬛ 按钮停止仿真,再单击 ▶ 按钮开始仿真。
这时可以看到程序停止在断点处。然后可以通过单步调试手段一步步地执行程序。此时,该示例程序已经正常运行。如果在端口 PortD.5 上连接虚拟示波器,则可以看到 PortD.5 上输出的 PWM 波形,如图 6-54 所示。

本节介绍了建立 AT90S8515 最小系统,并使用 IAR EWB for AVR 为 AVR 单片机编写程序,以及在 Proteus VSM 中进行 AVR 单片机符号调试的知识。在本节内容的基础上,下一节将通过一个相对复杂和完整的实例进一步介绍 IAR EWB for AVR 与 Proteus VSM 进行联合调试的技巧。
6.6 用 AVR 单片机实现数字电压表设计实例
本节以一个完整的基于 AT90S8515 的数字电压表为例来说明 IAR EWB fro AVR 和 Proteus VSM 联合调试的方法。在该示例中将使用 TLC549 作为 A/D 转换器,使用 LM016L 作为显示元器件,核心 CPU 仍然使用 AT90S8515。开发工具采用 IAR EWB for AVR。
TLC549 是 TI 公司生产的一种低价位、高性能的 8 位 A/D 转换器,采用了 CMOS 工艺,它以 8 位开关电容逐次逼近的方法实现 A/D 转换,其转换速度小于 $ 17\mu s $,最大转换速率为 40000 Hz,典型值为 4 MHz 的内部系统时钟,电源为 3~6 V。它能方便地采用 SPI 接口方式与各种微处理器连接,构成测控应用系统。其操作时序如图 6-55 所示。
从 TLC549 的操作时序图中可以看到,当片选信号有效时 TLC549 开始输出模数转换的结果,其结果是 8 位二进制数。当片选信号有效时(低电平),前一次模数转换结果的最高位(MSB)就出现在 DATAOUT 引脚上,也就是 SDO 引脚。当片选信号无效时,SDO 引脚上出现的数据没有意义,也不会被 SPI 主机读取。当片选信号再次有效时,TLC549 再次用同样的方式输出最近一次模数转换的结果。

TLC549 作为 SPI 从设备只有 SDO、CS、SCK 三个数据引脚,这说明该元器件是只读元器件。SPI 主设备无法向 TLC549 写入数据或命令。因此,模数转换只能由其内部逻辑控制。从图 6-55 中可以看出, $ t_{conv} $ 是 TLC549 进行模数转换的时间。根据其数据手册,这个时间是 36 个内部时钟周期(注意,不是 36 个 SPI 时钟周期),即大约 $ 17\mu s $。而触发 TLC549 进行一次模数转换的事件是 SCK 和 SS 上的信号。当片选信号有效时,连续 8 个 SCK 信号的下降沿将触发一次模数转换。因此,在两次读取 TLC549 之间,至少需要 $ 17\mu s $ 的时间间隔,否则中间读取的数据将是重复的。
AT90S8515 的端口 B 中的第 4、5、6、7 脚的第二个功能可以作为 SPI 总线控制器使用。其中,PB7 是主机模式的时钟输出(SCK),PB6 是 SPI 主机数据输入、从机数据输出端(MISO),PB5 是 SPI 主机数据输出、从机数据输入端(MOSI),PB4 是从机片选信号(SS)。在 AT90S8515 读取了 TLC549 中输出的模数转换结果后,将其显示在 LM016L 显示模块上。LM016L 是一个与 LCD1602 兼容的字符型点阵液晶显示模块,其主控芯片是 HD44780。它具有集成的字符发生器和 32 个字符点阵。可以以 16 个一行的形式同时显示 32 个字符。LM016L 与单片机接口的信号有 D7~D0、RS、R/W 和 E。其中,RS(register select)信号是作为读写 LM016L 内部寄存器的选择信号。当 RS 有效时,MCU 读写的是 LM016L 的寄存器;当 RS 无效时,读写的是字符缓存。R/W 信号控制对 LM016L 的读写。E 信号为数据允许信号。当 EN 信号有效时,D7~D0 为 LM016L 读取信号。
LM016L 的写时序如图 6-56 所示。显然在写操作开始前,首先要设置正确的 RS 信号,选择读写寄存器或字符缓存,然后设置正确的 R/W 信号。如果 RS 和 R/W 信号属于同一个 I/O 端口,则 RS 和 R/W 信号可以在一条指令中同时设置。在向 LM016L 写数据前,先输出 E 信号。接下来在下一条指令中输出 D7~D0。一般为了方便,将 D7~D0 连接在一个 I/O 端口上。这样可以使用一条指令写入 8 位数据。LM016L 的读时序如图 6-57 所示。与写操作时序类似,LM016L 在 E 信号有效后,才在 D7~D0 上输出有效数据。
为了仿真数字电压表,还需要准备一个被测信号。图6-58中的RV1为滑动变阻器。滑动变阻器相当于两个电阻的串联,在变阻器控制端滑动时将同时改变R1和R2,但是R1与R2的阻值之和为固定值。这样将控制端连接到TLC549的模拟输入端,即可在仿真时改变被测信号的电压值。



首先请按照图 6-58 所示准备仿真调试的原理图。在该图中,输入到 TLC549 的模拟量输入端跨接了一个虚拟电压表。这么做是为了在仿真时将 LCD 显示模块中显示的电压和虚拟电压表中的电压做比较,以验证硬件和程序设计的正确性。
(1)为了进行仿真,接下来在 IAR EWB for AVR 中建立数字电压表的软件工程,并将工程命名为 digital. voltage. meter。建立的过程请参考上节中的示例。在 main.c 中输入下面的程序并保存。
#include
#include
#include
#define F_CPU(12000000)
#define delay_us(us) __delay_cycles((F_CPU / 1000000) * us)
#define delay_ms(ms) __delay_cycles((F_CPU / 1000) * ms)
#define LCD_EN_PORTPORTC
#define LCD_EN_DDRDDRC
#define LCD_EN_MSK(1 << 2)
#define LCD_RW_PORT PORTC
#define LCD_RW_DDR DDR
#define LCD_RW_MSK(1 << 1)
#define LCD_RS_PORT PORTC
#define LCD_RS_DDR DDR
#define LCD_RS_MSK(1 << 0)
#define LCD_DATA_PORT PORTA
#define LCD_DATA_DDR DDR
#define EN_0() (LCD_EN_PORT & = ~LCD_EN_MSK)
#define RW_0() (LCD_RW_PORT & = ~LCD_RW_MSK)
#define RW_1() (LCD_RW_PORT | = LCD_RW_MSK)
#define RS_0() (LCD_RS_PORT & = ~LCD_RS_MSK)
#define RS_1() (LCD_RS_PORT | = LCD_RS_MSK)
void lcd_busy_wait()
{
LCD_DATA_PORT = 0x00;
LCD_DATA_DDR = 0x00;
do {
EN_0();
RS_0();
RW_1();
EN_1();
asm("nop");
} while (LCD_DATA_PORT & 0x80);
EN_0();
}
void lcd_write_command(char command)
{
lcd_busy_wait();
LCD_DATA_DDR = 0xFF;
EN_0();
RS_0();
RW_0();
LCD_DATA_PORT = command;
}
EN_1();
asm("nop");
EN_0();
delay_us(100);
}
void lcd_write_data(char data)
{
lcd_busy_wait();
LCD_DATA_DDR = 0xFF;
EN_0();
RS_1();
RW_0();
LCD_DATA_PORT = data;
EN_1();
asm("nop");
EN_0();
delay_us(100);
}
void lcd_set_xy(char x, char y)
{
unsigned char address;
if (y == 0)
address = 0x80 + x;
else
address = 0xc0 + x;
lcd_write_command(address);
}
void lcd_write_string(char x, char y, const char * s)
{
lcd_set_xy(x, y);
while (* s)
{
lcd_write_data(* s);
s++;
delay_us(100);
}
}
void lcd_write_char(char x, char y, char c)
{
lcd_set_xy(x, y);
lcd_write_data(c);
}
void lcd_init()
{
LCD_EN_DDR | = LCD_EN_MSK;
LCD_RW_DDR | = LCD_RW_MSK;
LCD_RS_DDR | = LCD_RS_MSK;
EN_0();
lcd_write_command(0x38); //8位模式,两行
delay_ms(1);
lcd_write_command(0x0C); //取消光标,取消
}
电路设计、仿真与PCB设计
delay_ms(1);
lcd_write_command(0x06); //打开自增模式
delay_ms(1);
lcd_write_command(0x01); //清屏
delay_ms(1);
void init_spi()
{
DDRB = 0x90; //SCK & $SS 引脚为输入, SDO 引脚为输出
SPCR = 0x53; //SPI 使能, SPI 主模式, SPI 时钟频率为 Fosc/
SPSR & = 0xFE; //正常 SPI 速度
}
double read_voltage()
{
char data;
PORTB & = ~(1 << 4);
SPDR = 0;
while!(SPSR & (1 << 7))); //等待接收完成
data = SPDR; //数据寄存器
PORTB | = (1 << 4); //转换成电压值, 参考电压 5.0V
return (double)5.0 * data/256;
}
void main(void)
{
char buf[17] = "Volt(s) : 0.00";
int d1, d2, d3;
double voltage;
lcd_init();
delay_ms(10);
lcd_write_string(0, 0, "Voltage Meter!");
delay_ms(1);
init_spi();
for (;;)
{
voltage = read_voltage();
//计算电压值的个位
d1 = (int)voltage;
//计算电压值的十分位
d2 = (int)(voltage * 10 - 10.0 * d1);
//计算电压值的百分位
d3 = (int)(voltage * 100 - 100.0 * d1 - 10.0 * d2);
buf[10] = '0'+d1;
buf[12] = '0'+d2;
buf[13] = '0'+d3;
lcd_write_string(0, 1, buf);
delay_ms(10);
}
}
打开前面创建的 IAR EWB 工程,将 main.c 中的代码替换为上面的测试程序,然后保存。
(2)工程建立好之后,按如图 6-49 所示设置目标文件格式为 ubrof 8,并将输出的目标文件设置为 digital.voltage.meter.d90。
(3)在 IAR EWB for AVR 中按 F7 键编译代码,并生成目标文件。如果工程配置正确,且源代码输入正确,将会显示如图 6-59 所示的编译日志。如果编译日志中显示错误信息,则参考错误信息进行修改。
(4)在 Proteus VSM 中参考图 6-58 建立仿真原理图。

(5)单击原理图中的 U1(AT90S8515) 打开处理器设置对话框。将 Clock Frequency 设置为 12MHz,按如图 6-60 所示设置处理器运行频率。

(6)在图 6-60 中,单击 Program File 输入框右侧的 ☑ 图标,选择 IAR EWB for AVR 生成的 digital. voltage. meter. d90 文件,并单击 OK 按钮保存修改。
(7)单击 ▶ 按钮开始仿真。在仿真开始后按 Esc 键或者单击 ∏ 按钮暂停仿真。此时 Proteus 将会自动打开源代码调试窗口和变量观察窗口,如图 6-61 所示。如果调试窗口未打开,则参考图 6-51 所示,通过菜单打开源代码调试窗口。
(8)单击 ▶ 按钮继续程序仿真。如果前面的工作无误,则会显示如图 6-62 所示的界面。
在该界面中单击滑动变阻器旁的上下箭头,可以改变滑动变阻器中间触点的位置,从而改变分压值。如图6-63所示,此时TLC549模拟输入端的电压值为3.48V。
如图 6-64 所示,在 LM016L 上将显示测量到的电压值为 3.47V。考虑到数模转换的精度和在程序中将结果从 8 位二进制数转换到浮点数时可能会损失精度,这样的误差可以忽略不计。这验证了数字电压表电路和程序的正确性。




本节通过设计一个完整的基于 AT90S8515 和 TLC549 的数字电压表,演示了 Proteus VSM 与 IAR EWB for AVR 的联合设计与调试能力,介绍了通过 Proteus VSM 进行嵌入式系统的虚拟数字原型设计简单易用且功能强大。
第二部分 电路原理图及PCB设计
本篇介绍基于 AD 工具绘制原理图和 PCB 的原理和方法,内容主要包括电路原理图和 PCB 设计的流程、原理图和 PCB 绘制方法、以及 PCB 设计中的布局、布线的规则。
在印制电路板基础部分,主要介绍了印制电路板的发展、印制电路板制板流程及工艺、常用电子元器件参数及封装、集成电路芯片的各种封装形式和自定义元器件的设计流程。
在原理图参数设置与绘制部分,主要介绍了以下内容:原理图绘制流程、原理图设计规则、原理图绘制环境参数设置、所需元器件库的安装和绘制原理图。
在 PCB 绘制部分,主要介绍了以下内容:PCB 设计流程、PCB 层标签、PCB 视图查看命令、PCB 绘图对象、PCB 绘图环境参数设置、PCB 形状和边界设置、PCB 叠层设置、PCB 面板的使用、PCB 设计规则、PCB 高级绘图对象和运行设计规则检查等内容。
在实际电路设计中,完成电路原理图设计和电路仿真后,最终需要将电路中的实际元器件安装在印制电路板(printed circuit board, PCB)上。原理图的绘制解决了电路的逻辑连接,而电路元器件的物理连接是靠PCB上的铜箔实现。