串口输出版UART接收中断程序 (8259端口400H/402H)

发布于:2025-06-14 ⋅ 阅读:(14) ⋅ 点赞:(0)

1.硬件原理图

 

 

2.测试程序

; =============================================
; PC16550 UART接收中断程序 + LED闪烁 + 串口输出
; 硬件配置:
;   - UART基地址: 100H
;   - LED端口: 800H
;   - 8259 PIC端口: 400H(命令), 402H(数据)
;   - 中断请求线: IRQ1 (8259 IR1)
;   - 中断向量号: 21H
;   - 时钟频率: 18.432MHz
;   - 波特率: 9600 bps (除数78H)
; =============================================

ORG 100H
JMP INITIALIZATION      ; 跳过数据区到初始化代码

; 数据段定义
BUFFER_SIZE EQU 256     ; 接收缓冲区大小
recv_buffer DB BUFFER_SIZE DUP(0)  ; 接收缓冲区
buffer_head DW 0        ; 缓冲区头指针
buffer_tail DW 0        ; 缓冲区尾指针
buffer_count DW 0       ; 缓冲区中字符计数

; LED控制变量
led_state DB 0FFH       ; LED状态: FFH=亮, 00H=灭
flash_counter DW 0      ; 闪烁计数器
FLASH_INTERVAL EQU 500  ; 闪烁间隔(约0.5秒)

; 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端口 (修改为400H/402H)
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初始化 (适配400H/402H端口)
; =============================================
INIT_PIC:
    ; 保存原始中断屏蔽字
    MOV DX, PIC_DATA
    IN AL, DX
    MOV [original_mask], AL
    
    ; 初始化8259
    MOV DX, PIC_CMD
    MOV AL, 11H         ; ICW1: 边沿触发, 级联, 需要ICW4
    OUT DX, AL
    
    MOV DX, PIC_DATA
    MOV AL, UART_IRQ    ; 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 BX
    PUSH DX
    PUSH DS
    
    ; 设置DS为当前数据段
    MOV AX, CS
    MOV DS, AX
    
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           ; 读取接收到的字符
    
    ; 将字符存入缓冲区
    CALL BUFFER_STORE
    
    ; 继续检查其他中断
    JMP ISR_LOOP
    
CHECK_OTHER:
    ; 处理其他中断类型 (可选)
    ; 这里可以添加发送中断或错误中断的处理
    ; ...
    
ISR_EXIT:
    ; 发送EOI到8259 (使用新端口)
    MOV AL, 20H
    MOV DX, PIC_CMD     ; PIC_CMD = 400H
    OUT DX, AL
    
    POP DS
    POP DX
    POP BX
    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

; =============================================
; LED控制子程序
; =============================================
UPDATE_LED:
    PUSH AX
    PUSH DX
    
    ; 更新闪烁计数器
    INC [flash_counter]
    CMP [flash_counter], FLASH_INTERVAL
    JB LED_DONE         ; 未达到间隔
    
    ; 重置计数器
    MOV [flash_counter], 0
    
    ; 切换LED状态
    XOR [led_state], 0FFH
    
    ; 输出到LED端口
    MOV DX, 800H
    MOV AL, [led_state]
    OUT DX, AL
    
LED_DONE:
    POP DX
    POP AX
    RET

; =============================================
; 将字符存入缓冲区
; =============================================
BUFFER_STORE:
    PUSH BX
    
    ; 检查缓冲区是否已满
    MOV BX, buffer_count
    CMP BX, BUFFER_SIZE
    JAE BUFFER_FULL     ; 缓冲区已满,丢弃字符
    
    ; 存储字符
    MOV BX, buffer_tail
    MOV [recv_buffer + BX], AL
    
    ; 更新尾指针
    INC BX
    CMP BX, BUFFER_SIZE
    JB NO_WRAP_TAIL
    XOR BX, BX          ; 回绕到缓冲区开头
    
NO_WRAP_TAIL:
    MOV buffer_tail, BX
    
    ; 更新字符计数
    INC buffer_count
    
BUFFER_FULL:
    POP BX
    RET

; =============================================
; 从缓冲区读取字符
; =============================================
BUFFER_READ:
    PUSH BX
    
    ; 检查缓冲区是否为空
    CMP buffer_count, 0
    JE BUFFER_EMPTY
    
    ; 读取字符
    MOV BX, buffer_head
    MOV AL, [recv_buffer + BX]
    
    ; 更新头指针
    INC BX
    CMP BX, BUFFER_SIZE
    JB NO_WRAP_HEAD
    XOR BX, BX          ; 回绕到缓冲区开头
    
NO_WRAP_HEAD:
    MOV buffer_head, BX
    
    ; 更新字符计数
    DEC buffer_count
    
    ; 设置成功标志
    STC
    JMP READ_DONE
    
BUFFER_EMPTY:
    XOR AL, AL          ; 返回0
    CLC                 ; 清除进位标志 (失败)
    
READ_DONE:
    POP BX
    RET

; =============================================
; 初始化缓冲区和LED
; =============================================
INIT_SYSTEM:
    ; 初始化缓冲区
    MOV buffer_head, 0
    MOV buffer_tail, 0
    MOV buffer_count, 0
    
    ; 初始化LED状态
    MOV [led_state], 0FFH  ; 初始状态: 亮
    MOV [flash_counter], 0
    
    ; 初始化LED端口
    MOV DX, 800H
    MOV AL, [led_state]
    OUT DX, AL
    
    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:
    ; 初始化系统
    CALL INIT_SYSTEM
    
    ; 初始化UART
    CALL INIT_UART
    
    ; 初始化8259 PIC
    CALL INIT_PIC
    
    ; 设置中断向量
    CALL SET_INTERRUPT_VECTOR
    
    ; 通过串口发送启动消息
    MOV SI, OFFSET startup_msg
    CALL SEND_STRING

; =============================================
; 主程序循环
; =============================================
MAIN_LOOP:
    ; 更新LED状态
    CALL UPDATE_LED
    
    ; 检查是否有接收到的字符
    CALL BUFFER_READ
    JNC NO_DATA         ; 无数据则继续等待
    
    ; 通过串口回显接收到的字符
    CALL SEND_CHAR
    
    ; 检查是否为退出命令
    CMP AL, 1BH         ; ESC键
    JE EXIT_PROGRAM
    
NO_DATA:
    ; 短延时以控制闪烁频率
    MOV CX, 3000        ; 延时参数
DELAY_SHORT:
    LOOP DELAY_SHORT
    
    JMP MAIN_LOOP

; =============================================
; 退出程序
; =============================================
EXIT_PROGRAM:
    ; 恢复原始中断屏蔽字
    MOV DX, PIC_DATA
    MOV AL, [original_mask]
    OUT DX, AL
    
    ; 禁用UART中断
    MOV DX, IER
    MOV AL, 00H
    OUT DX, AL
    
    ; 关闭LED
    MOV DX, 800H
    MOV AL, 00H
    OUT DX, AL
    
    ; 通过串口发送退出消息
    MOV SI, OFFSET exit_msg
    CALL SEND_STRING
    
    ; 返回DOS
    MOV AH, 4CH
    INT 21H

; =============================================
; 数据区
; =============================================
original_mask DB ?     ; 原始8259中断屏蔽字
startup_msg DB 0Dh, 0Ah, 'UART Interrupt Program (8259@400H) Started', 0Dh, 0Ah
            DB 'Press ESC to exit...', 0Dh, 0Ah, 0
exit_msg    DB 0Dh, 0Ah, 'Program terminated.', 0Dh, 0Ah, 0

END INITIALIZATION

 


网站公告

今日签到

点亮在社区的每一天
去签到