一个适用于8088单板机的PC16550 UART数据接收程序 1.这个程序使用汇编语言编写考虑了8088处理器的特性和单板机环境。;************************************************************* ; PC16550 UART 接收程序 for 8088 Single Board Computer ; 适用于: 8088 CPU, PC16550 UART, 汇编语言 ; 地址定义: COM1基地址 3F8h ;************************************************************* ; ; 常量定义 ; BASE_ADDR EQU 3F8H ; COM1基地址 UART_RBR EQU 0 ; 接收缓冲寄存器 UART_THR EQU 0 ; 发送保持寄存器 UART_DLL EQU 0 ; 除数锁存器低字节 UART_DLM EQU 1 ; 除数锁存器高字节 UART_IER EQU 1 ; 中断使能寄存器 UART_IIR EQU 2 ; 中断识别寄存器 UART_FCR EQU 2 ; FIFO控制寄存器 UART_LCR EQU 3 ; 线路控制寄存器 UART_MCR EQU 4 ; 调制解调器控制寄存器 UART_LSR EQU 5 ; 线路状态寄存器 UART_MSR EQU 6 ; 调制解调器状态寄存器 ; 线路状态寄存器标志位 LSR_DR EQU 01H ; 数据就绪 LSR_OE EQU 02H ; 溢出错 LSR_PE EQU 04H ; 奇偶错 LSR_FE EQU 08H ; 帧错误 LSR_BI EQU 10H ; 中断标志 LSR_THRE EQU 20H ; 发送保持寄存器空 LSR_TEMT EQU 40H ; 发送移位寄存器空 ; 中断使能标志 IER_RX_INT EQU 01H ; 接收数据中断使能 IER_TX_INT EQU 02H ; 发送保持寄存器空中断使能 IER_LS_INT EQU 04H ; 线路状态中断使能 IER_MS_INT EQU 08H ; 调制解调器状态中断使能 ; FIFO控制寄存器标志 FCR_ENABLE EQU 01H ; 使能FIFO FCR_CLEAR_RX EQU 02H ; 清除接收FIFO FCR_CLEAR_TX EQU 04H ; 清除发送FIFO FCR_DMA EQU 08H ; DMA模式选择 FCR_RX_TRIG1 EQU 00H ; 接收触发点1字节 FCR_RX_TRIG4 EQU 40H ; 接收触发点4字节 FCR_RX_TRIG8 EQU 80H ; 接收触发点8字节 FCR_RX_TRIG14 EQU 0C0H ; 接收触发点14字节 ; 线路控制寄存器标志 LCR_WL5 EQU 00H ; 5位数据 LCR_WL6 EQU 01H ; 6位数据 LCR_WL7 EQU 02H ; 7位数据 LCR_WL8 EQU 03H ; 8位数据 LCR_STOP EQU 04H ; 2位停止位 LCR_PEN EQU 08H ; 使能奇偶校验 LCR_EPS EQU 10H ; 偶校验0为奇校验 LCR_SP EQU 20H ; 强制奇偶校验 LCR_DLAB EQU 80H ; 除数锁存器访问使能 ; ; 数据段 ; DATA SEGMENT MSG_INIT DB UART Initializing..., 0DH, 0AH, $ MSG_READY DB UART Ready, waiting for data..., 0DH, 0AH, $ MSG_RX DB Received: $ MSG_ERROR DB Error: $ MSG_OVER DB Overrun Error!, 0DH, 0AH, $ MSG_PARITY DB Parity Error!, 0DH, 0AH, $ MSG_FRAME DB Frame Error!, 0DH, 0AH, $ MSG_NODATA DB No data received, 0DH, 0AH, $ MSG_BYE DB Program terminated, 0DH, 0AH, $ RX_BUFFER DB 256 DUP(?) ; 接收缓冲区 BUFFER_PTR DW OFFSET RX_BUFFER ; 缓冲区指针 RX_COUNT DW 0 ; 接收字节计数 TIMEOUT DW 5000 ; 超时计数约5秒 FLAG_EXIT DB 0 ; 退出标志 DATA ENDS ; ; 代码段 ; CODE SEGMENT ASSUME CS:CODE, DS:DATA START: ; 初始化数据段 MOV AX, DATA MOV DS, AX ; 显示初始化信息 MOV DX, OFFSET MSG_INIT CALL DISPLAY_STRING ; 初始化UART CALL INIT_UART JC INIT_ERROR ; 显示就绪信息 MOV DX, OFFSET MSG_READY CALL DISPLAY_STRING ; 主循环 MAIN_LOOP: ; 检查键盘输入用于退出 CALL CHECK_KEYBOARD ; 检查UART是否有数据 CALL CHECK_RX_DATA JC NO_DATA ; 有数据接收数据 CALL RECEIVE_DATA JMP MAIN_LOOP NO_DATA: ; 没有数据延时等待 CALL DELAY_SHORT JMP MAIN_LOOP INIT_ERROR: MOV DX, OFFSET MSG_NODATA CALL DISPLAY_STRING JMP EXIT_PROGRAM ; ; 子程序: 初始化UART ; 输入: 无 ; 输出: CF0成功, CF1失败 ; INIT_UART PROC NEAR PUSH AX PUSH DX ; 1. 设置DLAB1访问除数锁存器 MOV DX, BASE_ADDR UART_LCR MOV AL, LCR_DLAB OUT DX, AL CALL DELAY_SHORT ; 2. 设置波特率9600 (1843200/16/960012) MOV DX, BASE_ADDR UART_DLL MOV AL, 12 ; 9600波特率 OUT DX, AL INC DX ; DLM寄存器 MOV AL, 0 OUT DX, AL ; 3. 设置数据格式: 8位数据, 1位停止位, 无校验 MOV DX, BASE_ADDR UART_LCR MOV AL, LCR_WL8 ; 8位数据1停止位无校验 OUT DX, AL CALL DELAY_SHORT ; 4. 使能FIFO清除FIFO MOV DX, BASE_ADDR UART_FCR MOV AL, FCR_ENABLE OR FCR_CLEAR_RX OR FCR_CLEAR_TX OR FCR_RX_TRIG14 OUT DX, AL ; 5. 使能调制解调器控制 (DTR, RTS) MOV DX, BASE_ADDR UART_MCR MOV AL, 03H ; DTR1, RTS1 OUT DX, AL ; 6. 清除中断使能 (使用查询方式) MOV DX, BASE_ADDR UART_IER MOV AL, 0 OUT DX, AL ; 7. 测试UART响应读LSR MOV DX, BASE_ADDR UART_LSR IN AL, DX CMP AL, 0 JE INIT_FAIL CLC ; 成功清除进位标志 JMP INIT_DONE INIT_FAIL: STC ; 失败设置进位标志 INIT_DONE: POP DX POP AX RET INIT_UART ENDP ; ; 子程序: 检查接收数据 ; 输出: CF0有数据, CF1无数据 ; CHECK_RX_DATA PROC NEAR PUSH AX PUSH DX MOV DX, BASE_ADDR UART_LSR IN AL, DX AND AL, LSR_DR ; 检查数据就绪位 JZ NO_DATA_AVAIL CLC ; 有数据 JMP CHECK_DONE NO_DATA_AVAIL: STC ; 无数据 CHECK_DONE: POP DX POP AX RET CHECK_RX_DATA ENDP ; ; 子程序: 接收数据 ; 输入: 无 ; 输出: AL接收数据 ; RECEIVE_DATA PROC NEAR PUSH BX PUSH CX PUSH DX ; 读取线路状态 MOV DX, BASE_ADDR UART_LSR IN AL, DX MOV BL, AL ; 保存状态 ; 检查错误 TEST AL, LSR_OE JZ NO_OVERRUN CALL DISPLAY_OVERRUN_ERROR NO_OVERRUN: TEST AL, LSR_PE JZ NO_PARITY CALL DISPLAY_PARITY_ERROR NO_PARITY: TEST AL, LSR_FE JZ NO_FRAME CALL DISPLAY_FRAME_ERROR NO_FRAME: ; 读取数据 MOV DX, BASE_ADDR UART_RBR IN AL, DX ; 保存数据到缓冲区 MOV BX, BUFFER_PTR MOV [BX], AL INC BUFFER_PTR INC RX_COUNT ; 显示接收到的字符 PUSH AX MOV DX, OFFSET MSG_RX CALL DISPLAY_STRING POP AX CALL DISPLAY_CHAR CALL DISPLAY_NEWLINE ; 检查缓冲区是否满 CMP RX_COUNT, 256 JL BUFFER_NOT_FULL ; 缓冲区满处理或清空 CALL PROCESS_BUFFER BUFFER_NOT_FULL: POP DX POP CX POP BX RET RECEIVE_DATA ENDP ; ; 子程序: 带超时的数据接收 ; 输入: 无 ; 输出: AL接收数据, CF0成功, CF1超时 ; RECEIVE_WITH_TIMEOUT PROC NEAR PUSH CX PUSH DX MOV CX, TIMEOUT ; 超时计数 TIMEOUT_LOOP: MOV DX, BASE_ADDR UART_LSR IN AL, DX AND AL, LSR_DR JNZ DATA_READY CALL DELAY_SHORT LOOP TIMEOUT_LOOP ; 超时 STC JMP TIMEOUT_DONE DATA_READY: MOV DX, BASE_ADDR UART_RBR IN AL, DX CLC ; 成功 TIMEOUT_DONE: POP DX POP CX RET RECEIVE_WITH_TIMEOUT ENDP ; ; 子程序: 批量接收数据 ; 输入: CX要接收的字节数, ES:DI缓冲区地址 ; 输出: CX实际接收的字节数 ; RECEIVE_BLOCK PROC NEAR PUSH AX PUSH SI MOV SI, CX ; 保存要接收的字节数 XOR CX, CX ; 清零计数 BLOCK_LOOP: PUSH CX CALL CHECK_RX_DATA JC BLOCK_TIMEOUT CALL RECEIVE_DATA POP CX MOV [DI], AL INC DI INC CX CMP CX, SI JB BLOCK_LOOP BLOCK_TIMEOUT: POP CX POP AX RET RECEIVE_BLOCK ENDP ; ; 子程序: 处理接收缓冲区 ; PROCESS_BUFFER PROC NEAR PUSH AX PUSH CX PUSH SI PUSH DI ; 显示缓冲区内容 MOV DX, OFFSET MSG_RX CALL DISPLAY_STRING MOV CX, RX_COUNT MOV SI, OFFSET RX_BUFFER DISPLAY_LOOP: LODSB CALL DISPLAY_CHAR LOOP DISPLAY_LOOP CALL DISPLAY_NEWLINE ; 清空缓冲区 MOV BUFFER_PTR, OFFSET RX_BUFFER MOV RX_COUNT, 0 POP DI POP SI POP CX POP AX RET PROCESS_BUFFER ENDP ; ; 子程序: 显示字符串 (DOS中断) ; 输入: DS:DX 字符串地址 ; DISPLAY_STRING PROC NEAR PUSH AX MOV AH, 09H ; DOS显示字符串功能 INT 21H POP AX RET DISPLAY_STRING ENDP ; ; 子程序: 显示字符 ; 输入: AL 字符 ; DISPLAY_CHAR PROC NEAR PUSH AX PUSH DX MOV DL, AL MOV AH, 02H ; DOS显示字符功能 INT 21H POP DX POP AX RET DISPLAY_CHAR ENDP ; ; 子程序: 显示换行 ; DISPLAY_NEWLINE PROC NEAR PUSH AX MOV AL, 0DH CALL DISPLAY_CHAR MOV AL, 0AH CALL DISPLAY_CHAR POP AX RET DISPLAY_NEWLINE ENDP ; ; 子程序: 显示错误信息 ; DISPLAY_OVERRUN_ERROR PROC NEAR PUSH AX PUSH DX MOV DX, OFFSET MSG_OVER CALL DISPLAY_STRING POP DX POP AX RET DISPLAY_OVERRUN_ERROR ENDP DISPLAY_PARITY_ERROR PROC NEAR PUSH AX PUSH DX MOV DX, OFFSET MSG_PARITY CALL DISPLAY_STRING POP DX POP AX RET DISPLAY_PARITY_ERROR ENDP DISPLAY_FRAME_ERROR PROC NEAR PUSH AX PUSH DX MOV DX, OFFSET MSG_FRAME CALL DISPLAY_STRING POP DX POP AX RET DISPLAY_FRAME_ERROR ENDP ; ; 子程序: 检查键盘输入 (用于退出) ; CHECK_KEYBOARD PROC NEAR PUSH AX MOV AH, 01H ; 检查键盘状态 INT 16H JZ NO_KEY MOV AH, 00H ; 读取键盘 INT 16H CMP AL, Q ; 按Q退出 JE EXIT_PROGRAM CMP AL, q ; 按q退出 JE EXIT_PROGRAM NO_KEY: POP AX RET CHECK_KEYBOARD ENDP ; ; 子程序: 短延时 (约1ms) ; DELAY_SHORT PROC NEAR PUSH CX MOV CX, 100 ; 根据CPU速度调整 DELAY_LOOP: NOP LOOP DELAY_LOOP POP CX RET DELAY_SHORT ENDP ; ; 子程序: 长延时 (约10ms) ; DELAY_LONG PROC NEAR PUSH CX MOV CX, 1000 DELAY_LOOP2: CALL DELAY_SHORT LOOP DELAY_LOOP2 POP CX RET DELAY_LONG ENDP ; ; 子程序: 读取UART状态 ; 输出: AL线路状态寄存器值 ; GET_UART_STATUS PROC NEAR PUSH DX MOV DX, BASE_ADDR UART_LSR IN AL, DX POP DX RET GET_UART_STATUS ENDP ; ; 子程序: 清空接收FIFO ; CLEAR_RX_FIFO PROC NEAR PUSH AX PUSH DX MOV DX, BASE_ADDR UART_FCR MOV AL, FCR_ENABLE OR FCR_CLEAR_RX OUT DX, AL POP DX POP AX RET CLEAR_RX_FIFO ENDP ; ; 退出程序 ; EXIT_PROGRAM: MOV DX, OFFSET MSG_BYE CALL DISPLAY_STRING MOV AH, 4CH ; DOS退出功能 INT 21H CODE ENDS END START2.编译链接; 使用MASM或TASM编译 masm uart_recv.asm; link uart_recv.obj; ; 或使用A86编译器 a86 uart_recv.asm ; 运行 uart_recv.exe3.测试