1. 项目概述为Apple IIe打造一块复古扩展卡如果你和我一样对上世纪80年代那台米色方盒子——Apple IIe——有着特殊的情感那么你一定能理解亲手为它“续命”的冲动。这台机器不仅是个人电脑的里程碑更是无数极客和爱好者的启蒙导师。然而随着时代变迁为其寻找配件、扩展功能变得越来越困难。今天分享的这个项目就是一次纯粹的“情怀落地”设计并制作一块集成了多种经典芯片的扩展卡让这台老古董重新焕发活力甚至能与现代设备进行有限的“对话”。这块卡的核心思路很明确在尽可能保留原汁原味的“复古感”和“时代感”的前提下通过精心挑选的经典芯片组合为Apple IIe扩展出实用的并行I/O、串行通信、I2C总线控制以及一个直观的字符显示屏功能。我们不会使用任何现代的微控制器比如Arduino或STM32来“降维打击”而是坚持使用那个年代的“标准件”如8255、6551、PCF8584等。这种选择本身就是一种致敬它要求我们深入理解40年前的计算机总线协议、地址译码逻辑和编程方式整个过程充满了挑战与乐趣。最终这块卡将成为你Apple IIe主机上的一个“瑞士军刀”式工具。你可以用它连接老式打印机、通过串口与PC通信、挂载I2C传感器模块或者仅仅是用那两排闪烁的LED和液晶屏复现出当年那种“机器正在思考”的迷人光效。接下来我将从设计思路、硬件实现、软件驱动到调试心得完整拆解这个项目的每一个细节。2. 核心设计思路与芯片选型解析2.1 为什么选择“全经典芯片”方案在项目伊始最直接的选择其实是使用一颗CPLD或现代MCU来模拟所有功能。这无疑会更简单、更紧凑、成本也可能更低。但我最终放弃了这条“捷径”原因有三第一教学与理解价值。使用分立的标准芯片就像在阅读一本活生生的计算机接口教科书。你需要亲手用GAL进行地址译码理解8255的三种工作模式配置6551的波特率寄存器。每一步都能让你直观地触摸到早期微机系统的设计哲学。这对于学习计算机体系结构的人来说是无价的经验。第二纯粹的复古体验。Apple IIe的魅力就在于它的“可触摸性”。插上一块布满74系列逻辑芯片和DIP封装大芯片的扩展卡那种沉甸甸的质感和复杂的走线本身就是一种美学。用现代芯片“黑盒化”所有功能会丧失这种独特的仪式感。第三与原始生态的兼容性。使用经典芯片意味着你可以直接参考甚至复用上世纪80年代出版物上的代码和电路图。许多为Apple II编写的商用软件或自制程序其底层驱动就是为这些特定芯片编写的兼容性更有保障。2.2 核心功能芯片深度解读基于以上思路我们为扩展卡挑选了四位“老将”1. 可编程阵列逻辑GAL22V10系统的守门人GAL22V10在这块卡上扮演着至关重要的“地址译码器”角色。Apple IIe的扩展槽提供了16位地址线A0-A15、8位数据线D0-D7以及一系列控制信号如R/W、IO SELECT等。我们的卡上有多颗芯片每颗都需要在特定的地址范围内被访问。GAL22V10的任务就是监听总线当CPU发出的地址落在我们为这块卡分配的地址空间时例如我们选择从$C0F0到$C0FF这个范围它便产生相应的片选Chip Select信号激活对应的芯片。选择GAL22V10而非更简单的74系列逻辑门是因为它的可编程性。我们只需使用古老的CUPL或类似的硬件描述语言编写一个简单的译码逻辑烧录进去即可。后期如果想调整地址映射无需改动PCB重新烧录GAL即可灵活性极高。2. 并行接口适配器8255 PIA通用的数字端口8255是一颗极其经典且耐用的并行I/O芯片。它提供了24根可编程的I/O引脚分为A、B、C三个端口每端口8位。你可以通过软件将其配置为简单的输入、输出或者更复杂的握手模式。在这块扩展卡上它的用途非常广泛驱动那两块16字符x2行的LCD显示屏。通常使用4位或8位数据模式。连接外部设备如按钮、继电器、LED阵列或读取拨码开关的状态。作为其他芯片的辅助控制线例如与6551或PCF8584配合使用。3. 异步通信接口适配器6551 ACIA通往串行世界的桥梁6551是Motorola的经典UART芯片用于实现串行通信。Apple IIe本身没有标准的串口6551的加入弥补了这一缺憾。它负责将CPU的并行数据转换为串行比特流并反之亦然并处理起始位、停止位、奇偶校验等通信协议。我们为其搭配了一个MAX232之类的电平转换芯片就能输出标准的RS-232电平从而连接老式调制解调器、串口打印机或者通过一个USB转串口适配器连接到现代电脑。4. I2C总线控制器PCF8584连接现代传感器网络的钥匙I2C是一种在嵌入式领域经久不衰的双线串行总线协议。PCF8584这颗芯片的作用是作为Apple IIe系统总线与I2C总线之间的协议转换器。有了它你的Apple IIe就能轻松挂载海量的现代I2C设备如温度传感器BMP180、实时时钟DS3231、OLED显示屏等。这相当于为这台40岁的老电脑打开了一扇通往现代物联网世界的小窗其可玩性大大增加。3. 电路设计详解与PCB布局要点3.1 总线接口与地址译码电路设计Apple IIe的扩展槽是50Pin的双列直插式边缘连接器。设计的第一步也是最重要的一步就是正确连接总线信号并实现稳定的地址译码。关键信号连接地址线 A0-A15全部接入GAL22V10的输入端用于地址判断。数据线 D0-D7连接到8255、6551、PCF8584的数据端口。控制信号R/W读写信号至关重要必须接入GAL和所有可读写芯片。IO SELECT当CPU访问$C000-$CFFF的I/O地址空间时此信号有效。它是我们这块I/O卡工作的总开关。DEVICE SELECT通常由地址译码后的某一条片选线来模拟用于更精细的设备选择。5V,12V,-12V,GND电源和地线必须连接牢固特别是模拟电路部分如6551的RS-232电平转换可能需要±12V。GAL22V10译码逻辑设计我们为整块卡选择一个基础的I/O地址块例如$C0F0 - $C0FF。在GAL内部我们可以这样分配以下为逻辑描述非实际CUPL代码当A151, A141, A130, A120且A7-A4 1111时即地址高四位为$C0且A7-A4为$F表示CPU正在访问我们卡的地址范围。然后根据低4位地址线A3-A0生成不同的片选信号A3 A2 A1 A0 0000- 片选8255A3 A2 A1 A0 0001- 片选6551A3 A2 A1 A0 0010- 片选PCF8584A3 A2 A1 A0 0011- 片选预留或用于LCD直接控制A3 A211- 直接驱动两个LED如项目描述中地址49404$C0FC对应LED操作注意Apple IIe的I/O地址空间是$C000-$C0FF为保留空间但很多兼容卡也使用$C100-$C7FF的范围。选择$C0Fx区域时务必确认不与系统内其他卡如磁盘控制器卡冲突。最稳妥的方法是查阅Apple IIe技术参考手册或选择像$C800-$C8FF这类通常空闲的“扩展ROM”区域进行译码但这需要修改GAL逻辑和软件中的基地址。3.2 各功能模块电路设计要点1. 8255及其外围电路电源去耦每个芯片的VCC和GND之间必须紧贴引脚放置一个0.1uF的陶瓷电容这是稳定工作的基石。LCD连接如果驱动标准HD44780兼容的LCD通常将8255的某个端口如PA口接LCD的DB0-DB7另外几根引脚接RS寄存器选择、R/W读写、E使能信号。记得在LCD的背光引脚串联一个合适的限流电阻通常100-220欧姆。端口保护所有连接到外界的I/O引脚最好串联一个220-470欧姆的电阻以防短路或过流损坏宝贵的8255。2. 6551与RS-232电平转换时钟源6551需要外部时钟来生成波特率。通常使用1.8432MHz的晶振因为它可以被整除出所有标准波特率。将晶振连接到6551的XTAL1和XTAL2引脚。电平转换6551的串行输入输出是TTL电平0V/5V需要MAX232或类似芯片转换为RS-232电平±3V至±15V。MAX232需要外接4个1uF的电解电容来产生泵压电源。务必注意电容的极性。连接器使用标准的DB-9或DB-25母头作为串口连接器。虽然现在用得少但“味道”要对。3. PCF8584与I2C总线上拉电阻I2C总线的SDA数据线和SCL时钟线必须通过上拉电阻接到5V阻值通常在2.2kΩ到10kΩ之间取决于总线速度和设备数量。总线隔离考虑到可能会连接外部3.3V设备可以在总线上添加电平转换电路如TXS0102或者至少串联330欧姆的电阻以提供一定保护。地址选择PCF8584本身有一个I2C从机地址通常通过硬件引脚设置确保不与总线上其他设备冲突。4. LED指示电路项目中将两个LED直接由地址译码后的信号驱动。这是一个非常聪明的设计它用最少的元件实现了直观的调试功能。你需要计算LED的限流电阻。假设LED压降2V期望电流10mA则电阻 R (5V - 2V) / 0.01A 300欧姆。使用330欧姆的标准值即可。确保驱动信号有足够的电流输出能力。GAL22V10的输出引脚通常可以提供足够的电流驱动LED但最好查阅数据手册确认。3.3 PCB布局与布线经验谈设计这种混合数字、模拟信号的扩展卡布局布线至关重要1. 电源优先首先布置电源线和地线。采用“星型”或“主干分支”的拓扑结构确保从入口到每个芯片的路径尽可能短且粗。大面积铺地在PCB的底层或中间层进行大面积接地覆铜能极大地提高抗噪声能力。2. 分区布局数字区GAL、8255、PCF8584等核心数字芯片应集中放置靠近扩展槽接口。模拟/接口区6551、MAX232、串口连接器、I2C接口应放在板子的另一侧或边缘与数字区适当隔离。晶振1.8432MHz晶振必须紧贴6551的时钟引脚放置走线尽量短周围用接地覆铜包围避免其高频噪声干扰其他电路。3. 信号完整性总线信号地址线、数据线尽量走成一组长度大致相等避免过长的分支。控制信号R/W、片选等关键控制信号走线要短而直接。避免交叉通过双面板和合理的布局尽量减少信号线的交叉。电源和地线可以在任何层面“穿行”以解决交叉问题。4. 丝印与调试友好性在每个测试点、LED、跳线旁清晰标注其功能如“LED_PASS”, “JP1: I2C_EN”。将每个芯片的核心引脚如片选、中断引出到测试焊盘方便用示波器测量。务必在PCB上清晰标注卡片名称、版本号和你的名字这是作品的身份证。4. 固件开发与汇编语言编程实战硬件就绪后真正的乐趣——编程——就开始了。在Apple IIe上为这类硬件编程最直接高效的方式就是使用6502汇编语言。4.1 内存映射与寄存器定义首先我们需要根据硬件设计GAL译码逻辑确定每个芯片的寄存器地址。假设我们采用之前的译码方案基地址$C0F0; 硬件寄存器地址定义 PIA_BASE $C0F0 ; 8255 基地址 PIA_PORTA PIA_BASE 0 ; 端口A数据寄存器 PIA_PORTB PIA_BASE 1 ; 端口B数据寄存器 PIA_PORTC PIA_BASE 2 ; 端口C数据寄存器 PIA_CTRL PIA_BASE 3 ; 控制寄存器 ACIA_BASE $C0F1 ; 6551 基地址 ACIA_DATA ACIA_BASE 0 ; 数据收发寄存器 ACIA_STAT ACIA_BASE 1 ; 状态寄存器 ACIA_CMD ACIA_BASE 2 ; 命令寄存器 ACIA_CTRL ACIA_BASE 3 ; 控制寄存器波特率设置 I2C_BASE $C0F2 ; PCF8584 基地址 I2C_S0 I2C_BASE 0 ; 数据寄存器 I2C_S1 I2C_BASE 1 ; 控制/状态寄存器 I2C_S2 I2C_BASE 2 ; 时钟寄存器 I2C_S3 I2C_BASE 3 ; 中断向量寄存器 LED_REG $C0FC ; 直接控制LED的地址根据GAL逻辑4.2 核心驱动函数编写1. 8255初始化与LCD驱动首先需要配置8255的工作模式。假设我们将PA口和PC口高4位设为输出用于LCD数据和控制PB口设为输入。InitPIA: lda #%10000000 ; 控制字模式0PA出PB入PC高4位出PC低4位入可根据需要调整 sta PIA_CTRL rts驱动LCD需要严格的时序。下面是一个向LCD发送命令的子程序假设数据在A寄存器中通过PA口发送PC0RS, PC1R/W, PC2E; 发送命令到LCD (命令在A寄存器中) SendCmdToLCD: pha ; 保存命令 lda #%00000000 ; RS0 (命令), R/W0 (写) sta PIA_PORTC ; 设置控制线 pla ; 恢复命令 sta PIA_PORTA ; 命令数据放到数据端口 lda #%00000100 ; E1 (使能) sta PIA_PORTC nop ; 短暂延时满足LCD的使能脉冲宽度要求通常450ns nop lda #%00000000 ; E0 sta PIA_PORTC jsr Delay ; 调用延时子程序等待LCD执行命令37us rts ; 发送数据到LCD (字符数据在A寄存器中) SendDataToLCD: pha lda #%00000001 ; RS1 (数据), R/W0 (写) sta PIA_PORTC pla sta PIA_PORTA lda #%00000101 ; E1 sta PIA_PORTC nop nop lda #%00000001 ; E0, RS保持1 sta PIA_PORTC jsr Delay rts2. 6551串口通信初始化6551设置波特率、数据格式等。InitACIA: lda #%00011110 ; 控制寄存器设置波特率。例如 %0001时钟/16 %11109600波特率基于1.8432MHz晶振 sta ACIA_CTRL lda #%00001011 ; 命令寄存器无奇偶校验无中断RTS低允许发送DTR低准备好 sta ACIA_CMD rts ; 发送一个字符字符在A寄存器中 SendCharACIA: pha .TxBusy: lda ACIA_STAT ; 读取状态寄存器 and #%00010000 ; 检查“发送数据寄存器空”位 (TDRE) beq .TxBusy ; 如果忙循环等待 pla sta ACIA_DATA ; 将数据写入数据寄存器开始发送 rts ; 接收一个字符收到的字符返回到A寄存器如果超时则置进位标志 RecvCharACIA: ldx #100 ; 设置超时计数器 .RxWait: lda ACIA_STAT and #%00001000 ; 检查“接收数据寄存器满”位 (RDRF) bne .RxReady ; 有数据跳转 dex bne .RxWait ; 超时计数 sec ; 超时置进位标志表示失败 rts .RxReady: lda ACIA_DATA ; 读取数据 clc ; 成功清除进位标志 rts3. PCF8584 I2C控制器驱动I2C协议相对复杂PCF8584需要正确初始化总线时钟并遵循严格的读写序列。InitI2C: lda #%10000000 ; 进入I2C总线控制器模式 sta I2C_S1 ; 写入控制寄存器S1 lda #%01000000 ; 设置总线时钟频率例如约90kHz sta I2C_S2 ; 写入时钟寄存器S2 rts ; 向I2C从设备发送一个字节从机地址在X数据在A ; 注意这是一个极度简化的示例实际需要处理起始、停止、应答等完整序列 I2C_SendByte: ; 步骤1: 发送起始条件 lda #%10010001 ; 设置S1寄存器发送起始条件进入主发送模式 sta I2C_S1 jsr I2C_WaitBB ; 等待总线就绪 ; 步骤2: 发送从机地址写 txa ; 从机地址 - A ora #%00000000 ; 确保最低位是0写操作 sta I2C_S0 ; 写入数据寄存器 lda #%00010001 ; 设置S1清除STA位继续发送 sta I2C_S1 jsr I2C_WaitBB ; ... 检查ACK ... ; 步骤3: 发送数据字节 ; (这里的数据是调用者传入的A但已被覆盖需要提前保存) ; sta I2C_S0 ; ... 类似步骤2 ... ; 步骤4: 发送停止条件 lda #%01010001 ; 设置S1发送停止条件 sta I2C_S1 rts I2C_WaitBB: lda I2C_S1 and #%00000010 ; 检查BUSY (BB) 位 bne I2C_WaitBB ; 忙则等待 rts4. LED交替闪烁程序BASIC版本与汇编版本项目描述中给出了一个经典的BASIC程序用于交替点亮两个LED。我们将其转换为更高效的汇编程序; 汇编版本 - 高效无延迟函数调用开销 FlashLEDs: lda #1 sta LED_REG ; 点亮LED1 jsr LongDelay ; 调用长延时子程序 lda #2 sta LED_REG ; 点亮LED2 jsr LongDelay jmp FlashLEDs ; 循环 LongDelay: ldy #$FF .DelayOuter: ldx #$FF .DelayInner: dex bne .DelayInner dey bne .DelayOuter rts4.3 高级应用示例一个简单的系统监控器将以上驱动组合起来我们可以创建一个在LCD上显示系统信息并通过串口输出日志的小型监控程序。MonitorStart: jsr InitPIA jsr InitLCD ; 初始化LCD的另一个子程序包含一系列SendCmd jsr InitACIA ; 在LCD第一行显示欢迎信息 ldx #0 .PrintWelcome: lda WelcomeMsg, x beq .PrintDone ; 遇到0结束字符串 jsr SendDataToLCD inx jmp .PrintWelcome .PrintDone: ; 同时通过串口发送启动信息 ldx #0 .SendSerialMsg: lda SerialMsg, x beq .MainLoop jsr SendCharACIA inx jmp .SendSerialMsg .MainLoop: ; 这里可以添加读取开关状态、读取I2C传感器等代码 ; ... jmp .MainLoop WelcomeMsg: .asciiz Apple IIe Monitor SerialMsg: .asciiz Apple IIe Expansion Card Online.5. 调试、测试与故障排查实录硬件焊接完成程序也写好了但十有八九第一次上电不会完美工作。以下是基于我个人经验的调试流程和常见问题。5.1 上电前检查至关重要视觉检查用放大镜仔细检查所有焊点有无虚焊、桥接。重点检查芯片方向、电解电容极性、二极管/LED方向。电源短路测试在插入Apple IIe之前用万用表蜂鸣档测量扩展卡金手指上的5V与GND之间的电阻。应该有几百欧姆以上的阻值如果接近0欧姆说明存在严重短路必须排查。静态电流测试如果条件允许使用可调电源单独给扩展卡供电5V限制电流在500mA以内观察上电瞬间电流和稳定后电流是否正常通常应在100-200mA范围。5.2 分模块调试法不要试图让所有功能一次成功。采用“分而治之”的策略。第一步测试电源和基础逻辑插入Apple IIe开机。首先用万用表测量卡上各个芯片的VCC引脚确保都是稳定的5V。运行一个最简单的BASIC程序循环对LED控制地址进行POKE操作如原文的10-50行。用逻辑笔或示波器检测GAL22V10对应的输出引脚。如果LED能按预期闪烁说明地址译码和总线接口基本正确这是最大的胜利。第二步测试8255和LCD编写一个最小的汇编程序初始化8255并尝试通过它点亮一个接在端口上的LED不通过LCD。如果成功说明8255的读写正常。连接LCD。最常见的故障是初始化失败。HD44780控制器上电后需要一段较长的稳定时间通常40ms并且初始化指令序列必须严格遵守。如果屏幕显示乱码或黑块检查对比度调整LCD模块的电位器确保对比度合适。检查时序用示波器测量E使能信号的脉冲宽度是否足够450ns以及数据建立时间是否满足。检查接线再三确认RS、R/W、E和数据线是否与程序定义一致。尝试更长的延时在每条初始化指令后增加毫秒级的延时。第三步测试6551串口初始化6551后编写一个循环发送字符‘A’的程序。使用USB转串口适配器连接卡的RS-232口到电脑用串口调试助手如Putty、Tera Term接收波特率设置与程序一致。如果收不到数据用示波器测量6551的TxD引脚看是否有波形。如果没有检查6551的片选、读写线连接以及控制寄存器的配置特别是波特率设置和发送使能位。如果有波形但电脑收不到检查MAX232电路。测量MAX232的RS-232输出引脚通常接DB9的2脚TxD电压应该在±5V到±12V之间摆动。如果电压不对检查那4个1uF电容是否焊好、极性是否正确。检查串口线是否是“直连线”。通常需要一根“空调制解调器”线即2、3脚交叉或者使用带交叉功能的USB转串口线。第四步测试PCF8584 I2C这是最难调试的部分因为协议是双向的。先从最简单的开始不接任何从设备用逻辑分析仪或示波器观察SDA和SCL线。运行I2C初始化代码后再运行一个发送起始条件的代码。你应该能在总线上看到一个清晰的起始条件SCL高电平时SDA从高到低的跳变。如果总线始终为高检查PCF8584的片选、电源。检查SDA、SCL的上拉电阻是否连接。如果总线始终为低被拉死可能有芯片损坏或SDA/SCL对地短路。断开所有I2C从设备单独测试扩展卡。接上一个简单的I2C设备测试比如一个AT24C02 EEPROM。尝试读取它的厂商ID通常是0x50。逻辑分析仪是调试I2C的终极利器。5.3 常见问题速查表现象可能原因排查步骤插入卡后Apple IIe无法启动或死机1. 电源短路2. 数据总线冲突3. 地址译码错误卡占用了系统关键地址1. 测量5V对地电阻。2. 拔掉卡主机应正常。用逻辑分析仪在插槽上抓取总线活动看卡是否在不该响应的时候响应。3. 检查GAL译码逻辑确保地址范围是空闲的。LED测试程序不工作1. GAL未编程或编程错误2. LED或限流电阻损坏/接反3. 程序地址错误1. 用编程器验证GAL文件。2. 用万用表电压档测量LED两端电压POKE时应有变化。3. 在监控程序中直接查看/修改$C0FC内存看LED是否响应。LCD无任何显示1. 电源/背光问题2. 初始化序列错误或时序不对3. 对比度调节不当1. 检查LCD模块供电和背光电路。2. 在E使能信号上增加示波器确认初始化指令波形正确。3. 调节对比度电位器至最阻值最小和最大范围。串口发送电脑接收不到1. 6551波特率设置错误2. MAX232电路故障3. 串口线缆不对1. 用示波器测量6551的TxD引脚计算实际波特率。2. 测量MAX232的RS-232输出引脚电压是否为负电压如-5V到-10V。3. 尝试交换RxD和TxD线。I2C总线无活动1. PCF8584未正确初始化2. SDA/SCL上拉电阻缺失或开路3. 从设备地址错误1. 确认向控制寄存器S1写入了$80进入主模式。2. 测量SDA/SCL空闲时是否为高电平。3. 用逻辑分析仪解码确认发送的从机地址正确。5.4 调试心得与必备工具逻辑分析仪是你的最佳伙伴一个支持SPI、I2C、UART协议解码的廉价逻辑分析仪如Saleae Logic 8克隆版能极大提升调试效率。你可以清晰地看到总线上的每一个命令、每一个数据字节以及精确的时序。善用Apple IIe的监控程序Apple IIe自带的监控程序在ROM中是一个强大的调试工具。你可以用它来直接读写内存对应我们的I/O地址单步执行程序比单纯用BASIC的POKE/PEEK灵活得多。保持耐心做好记录每次改动硬件如飞线或软件都记录下来。遇到问题时回溯最近的改动。复杂的硬件问题往往是由多个小错误叠加引起的。社区是后盾遇到无法解决的问题时可以去像applefritter.com这样的复古计算社区提问。那里聚集了大量经验丰富的Apple II硬件高手他们很可能一眼就能看出问题所在。完成所有这些步骤后当你看到LCD上显示出自己设定的字符串口调试助手里滚动着来自40年前电脑的数据I2C温度传感器读数成功被读取那种跨越时空的成就感是任何现代项目都无法替代的。这块扩展卡不仅是一块功能板更是一座连接过去与现在的桥梁它让你以最亲密的方式理解了那个开创时代的计算机架构的精髓。