1. 8250芯片与串行通信基础
第一次接触8250芯片时,我和很多同学一样被密密麻麻的寄存器搞得头晕。这块看起来不起眼的芯片,其实是串行通信的核心大脑。简单来说,它就像个尽职的邮差,负责把并行数据打包成串行信号发送出去,再把接收到的串行信号拆包还原。
8250芯片最神奇的地方在于它的波特率发生器。通过设置除数寄存器,我们可以自由调整通信速率。记得当时做实验,我特意尝试了从110bps到9600bps的不同速率,看着屏幕上字符像打字机一样逐个跳出来,突然就理解了"波特率"这个抽象概念。这里有个小技巧:除数寄存器的高8位和低8位要分开写入,而且必须先设置DLAB位为1,这个细节我当初就漏掉过,调试了半天才发现问题。
芯片的线路控制寄存器(LCR)是配置通信格式的关键。我的实验记录本上还留着这样的配置:8位数据位、1位停止位、无校验位(03H)。这种配置兼容性最好,适合大多数ASCII字符传输。而Modem控制寄存器(MCR)的D4位特别有趣——把它置1就进入内环测试模式,这时发送端和接收端在芯片内部直接连通,非常适合调试。
2. 混合编程模式实战解析
查询发送和中断接收的混合模式,就像餐厅里点餐和上菜的分工。发送端(查询)像服务员主动询问:"厨房准备好新订单了吗?"(检查THR空标志),而接收端(中断)则像厨师长,菜做好了就主动敲铃通知(触发中断)。
在代码中,发送部分的核心循环是这样的:
SCANT: MOV DX,2FDH ; 线路状态寄存器 IN AL,DX TEST AL,20H ; 检查THR空标志位 JZ SCANT ; 未就绪则继续查询这个循环会持续检查bit5(THR空标志),直到8250准备好接收新数据。我实测发现,在1200bps下这个等待通常不超过1ms。
中断接收则复杂得多。首先要接管0BH中断向量,这就像在学校的公告板上把自己的联系方式覆盖到"快递通知"栏目。关键是要保存原向量,实验结束后必须恢复,否则系统会崩溃。有次我忘记恢复就直接退出程序,结果键盘突然失灵,只能重启电脑,这个教训至今记忆犹新。
3. 初始化流程的魔鬼细节
8250的初始化就像给新手机做设置,漏掉任何步骤都会导致通信失败。根据我的踩坑经验,必须严格按照这个顺序:
- 设置波特率:
MOV DX,2FBH ; LCR地址 MOV AL,80H ; 置DLAB=1 OUT DX,AL MOV DX,2F9H ; 除数高8位 MOV AL,0 OUT DX,AL MOV DX,2F8H ; 除数低8位(1200bps) MOV AL,60H OUT DX,AL- 配置通信格式:
MOV DX,2FBH MOV AL,03H ; 8N1格式 OUT DX,AL- 启用FIFO和中断:
MOV DX,2F9H MOV AL,01H ; 允许接收中断 OUT DX,AL MOV DX,2FCH ; Modem控制寄存器 MOV AL,00011000B ; 内环模式+中断使能 OUT DX,AL最容易出错的是最后一步的Modem控制寄存器。有次我把D3位(OUT2)设错了,导致中断根本无法传递到8259,调试了整整两小时才发现。建议用二进制直接写值,比十六进制更直观。
4. 中断服务程序的精妙设计
中断服务程序(ISR)就像个身手敏捷的消防员,必须快进快出。我的RECEIVE子程序最初没注意保护现场,导致系统随机崩溃。后来改成这样:
RECEIVE PROC PUSH AX ; 保护现场 PUSH DX PUSH DS MOV AX,DATA ; 设置数据段 MOV DS,AX MOV DX,2F8H ; 接收缓冲区 IN AL,DX ; 读取数据 AND AL,7FH ; 屏蔽最高位 CMP AL,0DH ; 检查回车键 JE NEXT MOV AH,2 ; 显示字符 MOV DL,AL INT 21H JMP EXIT NEXT: MOV FLAG,-1 ; 设置结束标志 EXIT: MOV AL,20H ; 发送EOI OUT 20H,AL POP DS ; 恢复现场 POP DX POP AX IRET ; 中断返回 RECEIVE ENDP这里有个关键技巧:中断返回前必须向8259发送EOI(中断结束命令),否则后续中断会被屏蔽。我曾在中断里加了延时想观察现象,结果导致系统卡死,这就是中断服务必须精简的原因。
5. 调试技巧与常见问题
调试串口程序时,我总结了几条实用经验:
内环测试先于外环:先用自发自收模式验证基本功能,再连接外部设备。有次我直接接串口线测试,结果因为电平问题折腾半天,其实芯片本身工作正常。
状态寄存器是救星:线路状态寄存器(LSR)的每个位都值得关注。当通信异常时,bit4(帧错误)、bit3(奇偶校验错)、bit1(溢出错误)能快速定位问题。建议在查询循环中加入错误检测:
MOV DX,2FDH IN AL,DX TEST AL,1EH ; 检查所有错误位 JNZ ERROR_HANDLER中断向量陷阱:在实模式下,中断向量表位于内存0000:0000处。我遇到过向量置换成功但中断不触发的情况,最后发现是DS寄存器没设置正确。建议在调试时用DEBUG查看中断向量是否真正修改。
波特率容错:当收发双方波特率偏差超过3%时,通信就会出错。我曾用示波器测量过,发现实验室某些计算机的基准时钟有约1.5%的偏差,这在长距离通信时需要特别注意。
6. 键盘与显示的实时交互
这个实验最有趣的部分是实时交互的实现。当你在键盘输入时,程序要同时处理三个任务:检测键盘输入、管理串口发送、响应接收中断。这就像边做饭边接电话,需要很好的多任务协调能力。
我的解决方案是用标志位轮询:
MAIN_LOOP: CMP FLAG,-1 ; 检查结束标志 JE EXIT_PROGRAM CALL CHECK_KEYBOARD CALL SEND_DATA JMP MAIN_LOOPCHECK_KEYBOARD子程序用INT 16H的非阻塞调用(AH=1)检查键盘缓冲区,避免程序卡住。而发送部分则要确保前一字符完全送出后才能发送下一个,这就需要检查LSR的bit6(发送移位寄存器空):
TWAIT: MOV DX,2FDH IN AL,DX TEST AL,40H ; 检查THRE JZ TWAIT7. 从实验室到工程实践
做完这个实验后,我在参加电子设计竞赛时真的用上了这些知识。当时需要让STM32和PC通信,我直接借鉴了8250的编程思路。虽然现代UART更先进,但基本框架惊人地相似。
几个值得记录的工程经验:
- 工业环境中电磁干扰大,建议在串口线上加磁环
- 长距离传输时,RS-232最多15米,而RS-485可达1200米
- Linux下的串口设备需要设置termios属性,比DOS复杂但原理相通
- 遇到通信乱码时,先检查地线连接,再确认波特率
记得有次调试一个二手设备,对方使用非标准的7位数据位+偶校验,就是通过不断修改LCR配置最终试出来的。这种实战经验,是单纯看理论永远学不到的。