一个完整的PC16550 UART接收中断处理示例代码,包含中断触发和中断释放(EOI)的实现
cpp
; =============================================
; 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 0 ; 接收的字符
; 状态消息
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, 0Ah
DB '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, LCR
MOV AL, 80H ; 设置DLAB=1
OUT DX, AL
MOV DX, DLL ; 除数锁存低字节
MOV AL, 78H ; 120 = 78H (18.432MHz / (16 * 9600))
OUT DX, AL
MOV DX, DLM ; 除数锁存高字节
MOV AL, 00H
OUT DX, AL
; 设置线路参数: 8位数据, 1停止位, 无校验
MOV DX, LCR
MOV AL, 03H ; 8N1, DLAB=0
OUT DX, AL
; 启用并复位FIFO
MOV DX, IIR_FCR
MOV AL, 0C7H ; 启用FIFO, 14字节触发点, 清除接收FIFO
OUT DX, AL
; 设置调制解调器控制
MOV DX, MCR
MOV AL, 0BH ; 启用OUT2(中断使能), RTS和DTR
OUT DX, AL
; 启用接收数据中断
MOV DX, IER
MOV AL, 01H ; 仅启用接收数据中断
OUT DX, AL
RET
; =============================================
; 8259 PIC初始化
; =============================================
INIT_PIC:
; 初始化8259
MOV DX, PIC_CMD
MOV AL, 17H ; ICW1: 边沿触发, 级联, 需要ICW4
OUT DX, AL
MOV DX, PIC_DATA
MOV AL, UART_IRQ-1 ; ICW2: 中断向量基值
OUT DX, AL
MOV AL, 01H ; ICW4: 8086模式, 非缓冲, 正常EOI
OUT DX, AL
; 允许IRQ1中断
IN AL, DX
AND AL, 0FDH ; 清除IRQ1屏蔽位(11111101)
OUT DX, AL
RET
; =============================================
; 设置中断向量
; =============================================
SET_INTERRUPT_VECTOR:
CLI ; 关中断
XOR AX, AX
MOV ES, AX ; ES = 0 (中断向量表段地址)
; 计算中断向量位置 (中断号 * 4)
MOV AX, UART_IRQ
SHL AX, 2 ; 乘以4
; 设置中断向量
MOV DI, AX
MOV AX, OFFSET UART_ISR
CLD
STOSW ; 存储偏移地址
MOV AX, CS
STOSW ; 存储段地址
STI ; 开中断
RET
; =============================================
; UART中断服务程序 (IRQ1)
; 演示中断触发和释放过程
; =============================================
UART_ISR PROC FAR
; 保存寄存器
PUSH AX
PUSH DX
PUSH DS
; 设置DS为当前数据段
MOV AX, CS
MOV DS, AX
; 设置中断触发标志
MOV [int_triggered], 1
; 发送中断触发消息
MOV SI, OFFSET trigger_msg
CALL 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_msg
CALL SEND_STRING
; 发送EOI到8259
MOV AL, 20H
MOV DX, PIC_CMD
OUT DX, AL
; 设置中断释放标志
MOV [int_released], 1
; 恢复寄存器
POP DS
POP DX
POP AX
IRET
UART_ISR ENDP
; =============================================
; 串口发送字符子程序
; 输入: AL = 要发送的字符
; =============================================
SEND_CHAR:
PUSH AX
PUSH DX
; 保存字符
MOV AH, AL
SEND_WAIT:
; 检查发送保持寄存器是否为空
MOV DX, LSR
IN AL, DX
TEST AL, 20H ; 检查THRE位(bit5)
JZ SEND_WAIT ; 不为空则等待
; 发送字符
MOV DX, RBR_THR
MOV AL, AH
OUT DX, AL
POP DX
POP AX
RET
; =============================================
; 串口发送字符串
; 输入: SI = 字符串偏移地址
; =============================================
SEND_STRING:
PUSH AX
PUSH SI
SEND_STR_LOOP:
LODSB ; 加载字符到AL
OR AL, AL ; 检查是否结束(0)
JZ SEND_STR_DONE ; 是则结束
CALL SEND_CHAR ; 发送字符
JMP SEND_STR_LOOP ; 继续发送
SEND_STR_DONE:
POP SI
POP AX
RET
; =============================================
; 主初始化程序
; =============================================
INITIALIZATION:
; 初始化标志
MOV [int_triggered], 0
MOV [int_released], 0
; 初始化UART
CALL INIT_UART
; 初始化8259 PIC
CALL INIT_PIC
; 设置中断向量
CALL SET_INTERRUPT_VECTOR
; 通过串口发送启动消息
MOV SI, OFFSET start_msg
CALL SEND_STRING
; =============================================
; 主程序循环
; =============================================
MAIN_LOOP:
; 可以在这里添加其他任务
; 例如: 检查中断标志状态等
; 简单的延时循环
MOV CX, 0FFFFH
DELAY_LOOP:
LOOP DELAY_LOOP
; 检查退出条件(可选)
; ...
JMP MAIN_LOOP
DB 55H,55H,55H,55H,55H
; =============================================
; 退出程序(可选)
; =============================================
; 此示例程序将无限运行
; 在实际应用中可添加退出逻辑
