PC16550 FIFO接收方式研究
1.两种工作模式下的行为
1. FIFO 禁用模式 (兼容8250/16450模式)
行为:接收到一个字节就产生一次中断。
过程:
当UART的接收移位寄存器成功接收并组装完一个完整的字节后,会将其放入接收缓冲寄存器。
此时,UART会立即置位线路状态寄存器(LSR)的"数据就绪"(DR)位,并产生一个接收数据可用中断。
CPU响应中断,从接收缓冲寄存器中读取该字节。
读取操作会清除LSR的DR位,并撤销中断请求。
特点:中断频率高,CPU负担重,效率低。
2. FIFO 启用模式 (PC16550D的核心特性)
行为:只有当接收FIFO中的数据量达到预设的"触发水平"时,才产生一次中断。
过程:
通过FIFO控制寄存器(FCR) 启用FIFO,并设置接收FIFO的触发水平(例如8字节)。
UART接收到的字节会被连续存入16字节的接收FIFO中。
当FIFO中累积的字节数达到或超过设定的触发水平(例如8个)时,UART才产生一次接收数据可用中断。
CPU响应中断后,可以一次性从FIFO中读取多个字节(最多16个)。
当CPU从FIFO中读取数据,使得FIFO中的字节数低于触发水平后,该中断条件被清除。
特点:中断频率大幅降低,CPU可以批量处理数据,效率高。
2.FIFO工作模式设置
INIT_UART:; 设置波特率除数 (9600 @ 18.432MHz)MOV DX, LCRMOV AL, 80H ; 设置DLAB=1OUT DX, ALMOV DX, DLL ; 除数锁存低字节MOV AL, 78H ; 120 = 78H (18.432MHz / (16 * 9600))OUT DX, ALMOV DX, DLM ; 除数锁存高字节MOV AL, 00HOUT DX, AL; 设置线路参数: 8位数据, 1停止位, 无校验MOV DX, LCRMOV AL, 03H ; 8N1, DLAB=0OUT DX, AL; 启用并复位FIFOMOV DX, IIR_FCRMOV AL, 0C7H ; 启用FIFO, 14字节触发点, 清除接收FIFOOUT DX, AL; 设置调制解调器控制MOV DX, MCRMOV AL, 0BH ; 启用OUT2(中断使能), RTS和DTROUT DX, AL; 启用接收数据中断MOV DX, IERMOV AL, 01H ; 仅启用接收数据中断OUT DX, ALRET
3.中断一次性读出FIFO中所有的数据
MOV DX, RBR_THRIN AL, DX ; 清除接收数据中断 MOV [recv_char], AL ; 保存字符 CALL SEND_CHAR MOV DX, LSRIN AL, DX ; 清除线路状态中断TEST AL,01H ; 判断FIFO中是否数据为空,0--为空 1为 不空 JNZ READ_AGAIN ;如果不空,继续读取数据并回送
4.完整程序
; =============================================
; PC16550 UART接收中断处理示例
; 功能: 接收字符并回显,显示中断触发与释放状态
; 硬件配置:
; - UART基地址: 100H
; - 8259 PIC端口: 400H(命令), 402H(数据)
; - 中断请求线: IRQ1 (8259 IR1)
; - 中断向量号: 21H
; - 时钟频率: 18.432MHz
; - 波特率: 9600 bps (除数78H)
; =============================================ORG 100H
JMP INITIALIZATION ; 跳过数据区到初始化代码; 数据段定义
int_triggered DB 0 ; 中断触发标志
int_released DB 0 ; 中断释放标志
recv_char DB 'A' ; 接收的字符; 状态消息
trigger_msg DB 0Dh, 0Ah, '>>> Interrupt TRIGGERED', 0
release_msg DB 0Dh, 0Ah, '<<< Interrupt RELEASED (EOI sent)', 0
recv_msg DB 0Dh, 0Ah, 'Received char: ', 0
start_msg DB 0Dh, 0Ah, 'UART Interrupt Demo Started', 0Dh, 0AhDB 'Send characters to test interrupt...', 0Dh, 0Ah, 0
end_msg DB 0Dh, 0Ah, 'Program terminated.', 0Dh, 0Ah, 0; 16550寄存器偏移
UART_BASE EQU 100H
RBR_THR EQU UART_BASE + 0 ; 接收缓冲/发送保持寄存器
IER EQU UART_BASE + 1 ; 中断使能寄存器
IIR_FCR EQU UART_BASE + 2 ; 中断标识/FIFO控制寄存器
LCR EQU UART_BASE + 3 ; 线路控制寄存器
MCR EQU UART_BASE + 4 ; 调制解调器控制寄存器
LSR EQU UART_BASE + 5 ; 线路状态寄存器
DLL EQU UART_BASE + 0 ; 除数锁存低字节 (DLAB=1)
DLM EQU UART_BASE + 1 ; 除数锁存高字节 (DLAB=1); 8259 PIC端口
PIC_CMD EQU 400H
PIC_DATA EQU 402H; 中断向量号
UART_IRQ EQU 21H ; IRQ1对应中断21H; =============================================
; UART初始化子程序
; =============================================
INIT_UART:; 设置波特率除数 (9600 @ 18.432MHz)MOV DX, LCRMOV AL, 80H ; 设置DLAB=1OUT DX, ALMOV DX, DLL ; 除数锁存低字节MOV AL, 78H ; 120 = 78H (18.432MHz / (16 * 9600))OUT DX, ALMOV DX, DLM ; 除数锁存高字节MOV AL, 00HOUT DX, AL; 设置线路参数: 8位数据, 1停止位, 无校验MOV DX, LCRMOV AL, 03H ; 8N1, DLAB=0OUT DX, AL; 启用并复位FIFOMOV DX, IIR_FCRMOV AL, 0C7H ; 启用FIFO, 14字节触发点, 清除接收FIFOOUT DX, AL; 设置调制解调器控制MOV DX, MCRMOV AL, 0BH ; 启用OUT2(中断使能), RTS和DTROUT DX, AL; 启用接收数据中断MOV DX, IERMOV AL, 01H ; 仅启用接收数据中断OUT DX, ALRET; =============================================
; 8259 PIC初始化
; =============================================
INIT_PIC:; 初始化8259MOV DX, PIC_CMDMOV AL, 17H ; ICW1: 边沿触发, 级联, 需要ICW4OUT DX, ALMOV DX, PIC_DATAMOV AL, UART_IRQ-1 ; ICW2: 中断向量基值OUT DX, ALMOV AL, 01H ; ICW4: 8086模式, 非缓冲, 正常EOIOUT DX, AL; 允许IRQ1中断IN AL, DXAND AL, 0FDH ; 清除IRQ1屏蔽位(11111101)OUT DX, ALRET; =============================================
; 设置中断向量
; =============================================
SET_INTERRUPT_VECTOR:CLI ; 关中断XOR AX, AXMOV ES, AX ; ES = 0 (中断向量表段地址); 计算中断向量位置 (中断号 * 4)MOV AX, UART_IRQSHL AX, 2 ; 乘以4; 设置中断向量MOV DI, AXMOV AX, OFFSET UART_ISRCLDSTOSW ; 存储偏移地址MOV AX, CSSTOSW ; 存储段地址STI ; 开中断RET; =============================================
; UART中断服务程序 (IRQ1)
; 演示中断触发和释放过程
; =============================================
UART_ISR PROC FAR; 保存寄存器PUSH AXPUSH DXPUSH DS; 设置DS为当前数据段MOV AX, CSMOV DS, AX; 设置中断触发标志MOV [int_triggered], 1; 发送中断触发消息MOV SI, OFFSET trigger_msgCALL SEND_STRING;ISR_LOOP:
; ; 检查中断源
; MOV DX, IIR_FCR
; IN AL, DX
; TEST AL, 01H ; 检查是否有待处理中断 (bit0=1表示无中断)
; JNZ ISR_EXIT ; 无中断则退出
;
; ; 检查是否为接收数据中断
; TEST AL, 04H ; 检查中断类型位 (bit1-2)
; JNZ CHECK_OTHER ; 不是接收中断则检查其他
;
; ; 处理接收数据中断
; MOV DX, RBR_THR
; IN AL, DX ; 读取接收到的字符
; MOV [recv_char], AL ; 保存字符
;
; ; 发送接收消息
; MOV SI, OFFSET recv_msg
; CALL SEND_STRING
; MOV AL, [recv_char]
; CALL SEND_CHAR
;
; ; 继续检查其他中断
; JMP ISR_LOOP
;
;CHECK_OTHER:
; ; 可以添加其他中断类型的处理
; ; ...
;
ISR_EXIT:; 发送中断释放消息MOV SI, OFFSET release_msgCALL SEND_STRING; ; 发送EOI到8259
; MOV AL, 20H
; MOV DX, PIC_CMD
; OUT DX, AL
;
; ; 设置中断释放标志
; MOV [int_released], 1CALL CLEAR_ALL_INTERRUPTS; 恢复寄存器POP DSPOP DXPOP AXIRET
UART_ISR ENDP; =============================================
; 串口发送字符子程序
; 输入: AL = 要发送的字符
; =============================================
SEND_CHAR:PUSH AXPUSH DX; 保存字符MOV AH, ALSEND_WAIT:; 检查发送保持寄存器是否为空MOV DX, LSRIN AL, DXTEST AL, 20H ; 检查THRE位(bit5)JZ SEND_WAIT ; 不为空则等待; 发送字符MOV DX, RBR_THRMOV AL, AHOUT DX, ALPOP DXPOP AXRET; =============================================
; 串口发送字符串
; 输入: SI = 字符串偏移地址
; =============================================
SEND_STRING:PUSH AXPUSH SISEND_STR_LOOP:LODSB ; 加载字符到ALOR AL, AL ; 检查是否结束(0)JZ SEND_STR_DONE ; 是则结束CALL SEND_CHAR ; 发送字符JMP SEND_STR_LOOP ; 继续发送SEND_STR_DONE:POP SIPOP AXRET; =============================================
; 强制清除所有中断状态
; =============================================
CLEAR_ALL_INTERRUPTS:; 读取所有状态寄存器以清除中断; MOV DX, LSR
; IN AL, DX ; 清除线路状态中断MOV DX, MCRIN AL, DX ; 清除调制解调器状态中断 READ_AGAIN:MOV DX, RBR_THRIN AL, DX ; 清除接收数据中断 MOV [recv_char], AL ; 保存字符 CALL SEND_CHAR MOV DX, LSRIN AL, DX ; 清除线路状态中断TEST AL,01H ; JNZ READ_AGAIN
;
; MOV DX, IIR_FCR
; IN AL, DX ; 清除中断标识
; ; 发送EOI到8259MOV AL, 20HMOV DX, PIC_CMDOUT DX, ALRET; =============================================
; 主初始化程序
; =============================================
INITIALIZATION:; 初始化标志 CLIMOV AX,01F0H ;老版BIOS重新设置CS寄存器地址为01F0H MOV DS,AXMOV SS,AX MOV ES,AXMOV AX,5FFFHMOV SP,AX ;MOV AX,0000H ;CS 老版BIOS重新设置CS寄存器地址为01F0H ;PUSH AX;MOV AX,OFFSET START123 ;IP;PUSH AX; IRET;MOV SS,AX;MOV DS,AX;MOV AX, 01F0H
START123:NOPNOPMOV [int_triggered], 0MOV [int_released], 0; 初始化UARTCALL INIT_UART; 初始化8259 PICCALL INIT_PIC; 设置中断向量CALL SET_INTERRUPT_VECTOR; 通过串口发送启动消息MOV SI, OFFSET start_msgCALL SEND_STRING STI; =============================================
; 主程序循环
; =============================================
MAIN_LOOP:; 可以在这里添加其他任务; 例如: 检查中断标志状态等; 简单的延时循环MOV CX, 0FFFFH
DELAY_LOOP:LOOP DELAY_LOOP; 检查退出条件(可选); ... ;CALL CLEAR_ALL_INTERRUPTS ; MOV AL, [recv_char]
; CALL SEND_CHARJMP MAIN_LOOP DB 55H,55H,55H,55H,55H; =============================================
; 退出程序(可选)
; =============================================
; 此示例程序将无限运行
; 在实际应用中可添加退出逻辑