很多时候我们需要不止一个UART串行口的时候,可以用下面的程序直接“创造”一个的;
波特率是:9600bps@11.0592MHz
命名约定:
;前缀 含义
;on...事件程序入口
;f....位变量/标志
;c....常数
;p....端口定义
;
pTXD BIT P1.2 ; 软件UART的TXD口
; 软件UART的RXD口固定是INT0端口
;
fByteRx BIT 0 ; 硬件UART接收到一个字节
fByteRx1 BIT 1 ; 软件UART接收到一个字节
fByteTx1 BIT 2 ; 软件UART发送完一个字节(在停止位时刻有效)
fFirstBit BIT 3 ; 软件UART发送: 起始位
fStopBit BIT 4 ; 软件UART发送: 停止位
;
TxCount DATA 7FH ; 硬件UART发送字节数
TxPtr DATA 7EH ; 硬件UART发送缓冲区指针
RxBuff DATA 7DH ; 硬件UART接收缓存器
RxPtr DATA 7CH ; 硬件UART接收缓冲区指针(不使用接收缓冲区时置0)
;
portbuf DATA 7BH ; 软件UART接收: 采样保持缓存器
TxBuff1 DATA 7AH ; 软件UART发送移位寄存器
;
TxCount1 DATA 79H ; 软件UART发送字节数
TxPtr1 DATA 78H ; 软件UART发送缓冲区指针
RxBuff1 DATA 77H ; 软件UART接收缓存器
RxPtr1 DATA 76H ; 软件UART接收缓冲区指针(不使用接收缓冲区时置0)
;
ORG 0
NOP
AJMP onStart
NOP
MOV TL0,#0D4H ; 0.5 bit 加上跳入中断的两个周期和SETB的周期
SETB TR0
iRETN:
RETI
; ORG 0BH ; 写不写都是 0BH 的了
MOV portbuf,P3 ; 首先采样, 采样是高实时的!
ACALL iRETN ; 已经采样了,就释放中断(允许别的中断)
AJMP onRxBit
; ==================================================================
ORG 1BH ; 发送波特率发生器
XCH A,TxBuff1 ; A -> TxBuf
RRC A ; C -> ACC.7
MOV pTXD,C ; 送出数据
ACALL iRETN ; 实时部分完成,释放中断
SJMP $+1 ; 注意,这里的 $+X 不产生作用的,目标总在0034H!
; ; 因为后面的 JBC 机器码是 10 99 04, 其10 为 SJMP 的操作数!
; ==================================================================
ORG 23H ; 硬件串行口程序: 建立标志后在主程序处理!
JBC TI,onTxD ; 这句不得更改! (前面的原因)
JBC RI,onRxD
RETI
onTxD:
INC TxCount
DJNZ TxCount,onTxD1 ; 如果是零则忽略,(防止出错)
RETI
onTxD1:
DJNZ TxCount,onTxD2
RETI ; 已经发完
; ==================================================================
ORG 34H ; 连接在上面的 SJMP $+1 那里
onTxBit:
MOV C,ACC.7 ; 恢复 C
JBC fStopBit,onTxByte ; 已经是停止位, 一个字节发送完 ->
ORL TH0,#0A0H ; 加载波特率, 注意, 必须用 ORL 来加载!
JBC fFirstBit,onTxBit1 ; 发送的是第一位 ->
CLR ACC.7 ; 以 0 填充, 为后面的判断
JZ onTxBit2 ; 全 0, 8 位已送完! ->
XCH A,TxBuff1 ; 未完, 恢复现场
RET ; 不能用 RETI, 前面已经 RETI 过了
onTxBit1:
SETB ACC.7 ; 开始时给D7设置1, 让它一直移到右边出去
XCH A,TxBuff1 ; 恢复现场
RET
onTxBit2:
SETB fStopBit ; 8 位已送完, 下一个是停止位
MOV TH0,A ; 延长停止位
INC A ; 让 TxBuff1 = 1, Keep 住高电平
XCH A,TxBuff1 ; 恢复现场
SETB fByteTx1 ; 通知主程序一个字节已送出
RET
; ------------------------------------------------------------------
onTxByte:
MOV A,TxBuff1 ; 恢复 A
DJNZ TxCount1,TxBYTEA ; 还需发送, 接着发下一个字节
CLR TR1 ; 没有发送了, 停止波特率发生器
RET
; ==================================================================
; 上面的地址空间不足,写在这里
onTxD2:
XCH A,TxPtr ; 很简单的队列操作! 不解释啦
INC A
XCH A,R1
MOV SBUF,@R1
SJMP onUARTEnd
onRxD:
MOV RxBuff,SBUF
SETB fByteRx ; 通知主程序有字节被接收
XCH A,RxPtr
JZ onRxD1
INC A
XCH A,R1
MOV @R1,RxBuff
onUARTEnd:
XCH A,R1
onRxD1:
XCH A,RxPtr
RETI
; ==================================================================
TxBYTEA:
XCH A,TxPtr1
XCH A,R1
MOV TxBuff1,@R1
INC R1
XCH A,R1
XCH A,TxPtr1
; ------------------------------------------------------------------
TxBYTE:
MOV TH0,#0CFH ; 启动波特率发生器
SETB TR1
CLR pTXD ; 发送起始位
SETB fFirstBit ; 告知中断程序这是起始位
CLR fStopBit ; 当然不是停止位啦
RET
; ------------------------------------------------------------------
; 发送数据的使用方法:
; MOV TxPtr1,#<发送缓冲区首址>
; MOV TxCount1,#<发送的字节数>
; ACALL TxBYTEA
;
; 简单地发送一个字节:
; MOV TxBuff1,<待发送的数据>
; MOV TxCount1,#1
; ACALL TxBYTE
; ==================================================================
; 软件UART接收:
onRxBit:
ORL TL0,#0A0H ; 加载波特率
JBC EX0,onRxBit1st ; 起始的半个位, ->
XCH A,portbuf ; 保护 A -> portbuf
RLC A ; 保护 C -> ACC.0
MOV C,ACC.3 ; 取采样结果 RxD = P3.2(上面移了一位,所以取D3)
XCH A,RxBuff1 ; 移进 RxBuff1
RRC A
XCH A,RxBuff1
RRC A ; 恢复C(ACC.0->C->ACC.7)ACC.7为结束标志
JNB ACC.7,onRxEnd ; 未足一字节 ->
CLR TR0 ; 停止波特率发生器
MOV A,RxPtr1
JZ onRxByte1 ; 指针为空, 不作队列处理 ->
INC A ; 指针非空, 指针增量
MOV RxPtr1,A
XCH A,R1 ; 写入队列
MOV @R1,RxBuff1
MOV R1,A ; 恢复 R1
onRxByte1:
SETB EX0 ; 允许下一次接收
SETB fByteRx1 ; 通知主程序有字节被接收
onRxEnd:
MOV A,portbuf ; 恢复 A
RET
onRxBit1st:
MOV RxBuff1,#80H
RET
; ==================================================================
onStart: ; 程序开始的初始化
CLR A
MOV IE,A ; 禁止中断
MOV PSW,A
;...
MOV TH1,#0FDh ;<c9600bps>
MOV TMOD,#23H ; Timer1 运行(T0@模式3,上下8位各自给接收和发送)
MOV SCON,#50H ; 硬件UART
MOV PCON,#00H ; 硬件UART 波特率不倍增
MOV TCON,#01H ; EX0 边沿触发(用于捕获起始位)
MOV IP,#0BH ; UART 低优先级, EX0,ET0,ET1高优先级
MOV IE,#9BH ; 允许中断
; ...
end
|