8051 software UART


之前有其他的 CPU 種類和應用, 這次是 8051 的 MCU

其實原理不變, 但是需要配合不同的 MCU 指令, 計算的方法都一致

首先要了解 UART 通訊的規則, 看圖就最簡單了

Software UART and clock pattern.jpg
Software UART and clock pattern.jpg

先不管電壓的高低如何, 只關心訊號的特徵

UART 通訊, 通常有 N81這種格式, 就是 START BIT + 8 DATA BIT + STOP BIT

START BIT 永遠是 0

STOP BIT 永遠是 1

所以, 如果要傳送 0xAA 這個資料, 它的 BIN = 10101010

串起來, START BIT + 10101010 + STOP BIT 就會變成

0, 10101010, 1 = 0101010101

總共10個BIT, 是一個 0101 (或 1010) 重複的特徵, 也就是所謂的 時鐘訊號 或叫作 CLOCK PATTERN.

因此, 結論是, 用 UART, N81格式, 不斷地送出資料 0xAA, 就會出現CLOCK PATTERN.

那麼, 現在來關心這個 “時鐘訊號" 的頻率是多少. UART 另外一個要素就是 BAUD RATE, 每秒鐘送多少個 BIT, 就叫作 BAUD RATE. 常用的有 9600 BAUD, 或她的整倍數或半倍數, 例如 x0.5, x1, x1.5 等, 常用的速度最高 115200 BAUD.

第二個結論, 用 UART, N81格式, 9600 BAUD, 不斷地送出資料 0xAA, 就會出現CLOCK PATTERN, 頻率是 9600/2 = 4800Hz. 為什麼要BAUD RATE 除以二, 因為2個BIT, 組成01, 是一個週期, 9600 BIT / 2 = 4800 週期, 每秒變化4800個週期, 就是 4800Hz.

如果用自製的軟件, 是不是一樣可以產生這個 UART 的生成的時鐘訊號呢? 答案是肯定的, 而且, 看完解說, 了解了原理, 比想像中還要簡單.

首先, 發送的資料是 0xAA = 10101010, 用人類的語言, 這樣描述她的操作順序, 這樣作的話, 就能夠產生一個 0101010101 的訊號

START_BIT = send_0
STOP_BIT = send_1
DATA_BYTE = 0xAA = 10101010

put_char:
  START_BIT
  send 1
  send 0
  send 1
  send 0
  send 1
  send 0
  send 1
  send 0
  STOP_BIT
done:
.
.
.
或者把它更模組化的編寫, 可以這樣描述,
START_BIT = send_0
STOP_BIT = send_1
DATA_BYTE = {
 send 0
 send 1
 send 0
 send 1
 send 0
 send 1
 send 0}

put_char:
  START_BIT
  DATA_BYTE
  STOP_BIT
done:

.
.
.
.
上面的作法, 一看就知道, 當作完了一次, 它就會到達 DONE 的位置, 不會再執行了, 所以我們希望她可以不斷執行, 才有機會不斷地產生時鐘訊號, 是的, 就不斷重複執行就好了

;不斷重複執行, 可以這樣描述,
START_BIT = send_0
STOP_BIT = send_1
DATA_BYTE = {
 send 0
 send 1
 send 0
 send 1
 send 0
 send 1
 send 0}

put_char:
  START_BIT
  DATA_BYTE
  STOP_BIT
repeat:
  jump to put_char

.
.
.
這裡假設了, 每個send_0 或 send_1 的動作, 過程中都是緊湊的, 沒有任何考慮. 所以, 現在開始要加入對每個BIT的發送在時間上的要求了.
回顧一下, UART 的特點就是在固定的時間間隔內發送 0 或 1 而達成目標.

例如, 9600 BAUD, 每個BIT佔用的時間大約是 1/9600 = 104.167us
所以, 實際操作時, 俺們可以這樣寫

;不斷重複執行, 可以這樣描述,
  START_BIT = send_0, delay_104us
  STOP_BIT = send_1, delay_104us
  DATA_BYTE = {
  send 1, delay_104us
  send 0, delay_104us
  send 1, delay_104us
  send 0, delay_104us
  send 1, delay_104us
  send 0, delay_104us
  send 1, delay_104us
  send 0, delay_104us
  }

put_char:
  START_BIT
  DATA_BYTE
  STOP_BIT
repeat:
 jump to put_char

.
.
.
原理大致就這樣.
接收是發送逆向操作, 當然, 其中還有比較多的技巧, 暫不描述. 直接套用人家的

編譯環境

不知道怎樣設定使 LISTING 出現 CPU MACHINE CYCLE [], 源碼中的 MACHINE CYCLE 是 查這裡 然後自行加上去, 方便核算 BIT DELAY 計算的

KEIL C U4

.
.
main.c

#include "softuart.h" //include the header file

//This is an echo program using software uart
void main(){
unsigned char x;
	while(1){
		x = getc();
		putc(x);
	}
}

/*
//This is an put_char program using software uart
void main(){
unsigned char x;

	x = 0xAA;
	while(1){
		putc(x);
	}
}
*/

.
.
softwareUART.h

#ifndef __SOFTUART

#define __SOFTUART

void putc(unsigned char);
unsigned char getc(void);

#endif

.
.

softwareUART.asm

;copy http://www.8051projects.net/serial-communication/software-uart-8051.php
;2014-02-28
;modified

?SU?PUTC SEGMENT CODE
?SU?GETC SEGMENT CODE

PUBLIC _putc
PUBLIC getc

;txd_pin EQU	P3.1	;Transmit on this pin
;rxd_pin EQU	P3.0	;Receive on this pin

txd_pin EQU	P3.5	;Transmit on this pin, T1 pin
rxd_pin EQU	P3.3	;Receive on this pin, EXT INT1 pin

;Formula to calculate the bit time delay constant
;This constant is calculated as: (((crystal/baud)/12) - 5) / 2
;crystal is the frequency of crystal in Hz
;baud is required baudrate
;Please try to keep baudrate below 9600
;to get best results

;BITTIM EQU	45; (((11059200/9600)/12) - 5) / 2

; xiaolaba, 2014-02-28
; AT89S51
; Xtal / 12T / 9600 BAUD = 96 machine cycle / UART bit
; 96 - 6 = 90
; 90 / 2 (DJNZ loop cycle) = 45
BITTIM EQU 45; (((11059200/9600)/12) - 6) / 2

;--------------------------------------------
;To send data serially
;For C programs
;Protype definition:
; void putc(unsigned char);
;Usage:
; putc(data);
;Return:
; This function returns nothing
;
;For Assembly Programs:
;
;Usage:
; data to be send has to be moved to R7
; for example:
; mov R7,#'a'
; lcall _putc
;--------------------------------------------
RSEG ?SU?PUTC
_putc:
	push ACC
	Push PSW

	mov	a,r7
	CLR	txd_pin		;[ ] Drop line for start bit
	MOV R0,#BITTIM	;[1] Wait full bit-time
	DJNZ R0,$		;[2] For START bit

	MOV R1,#8		;[1] Send 8 bits
putc1:
	RRC A			;[1] Move next bit into carry
	MOV txd_pin,C	;[2] Write next bit
	MOV R0,#BITTIM	;[1] Wait full bit-time
		DJNZ R0,$		;[2] For DATA bit

	DJNZ R1,putc1	;[2] write 8 bits

stop_bit_putc:
	SETB txd_pin	;[1] Set line high
	RRC A			;[1] Restore ACC contents
	MOV R0,#BITTIM	;[1] Wait full bit-time
		DJNZ R0,$		;[2] For STOP bit

	POP PSW
	pop ACC
	RET

;--------------------------------------------
;To receive data Serially
;If you want to use this routine in your
;C program then define function prototype
; as:
; unsigned char getc(void);
;
; Usage:
; data = getc();
; Return value:
; Returns data received
;
;
;If you are using it in assembly program
; Usage:
; lcall getc
; Return:
; data received is stored in R7
;--------------------------------------------

getc:
	Push ACC
	Push PSW

	JB rxd_pin,$		;[2] Wait for start bit
	MOV R0,#BITTIM/2	;[1] Wait 1/2 bit-time
		DJNZ R0,$		;[2] To sample in middle
	JB rxd_pin,getc 	;[2] Insure valid, this jump has
						;	 problem as possible re-entry
						;    stack may overflow

	MOV R1,#8			;[1] Read 8 bits
getc1:
	MOV R0,#BITTIM 		;[1] Wait full bit-time
		DJNZ R0,$		;[2] For DATA bit
	MOV C,rxd_pin 		;[1] Read bit
	RRC A				;[1] Shift it into ACC
		DJNZ R1,getc1 	;[2] read 8 bits

	mov r7,a

	POP PSW
	pop ACC
	RET					;go home

END

.
.
.
.

8051 Mnemonic Cycles Operand
ACALL 2
ADD 1
ADDC 1
AJMP 2
ANL 1 A, #const8
A, @Ri
A, direct
A, Rn
direct, A
2 C, /bit
C, bit
direct, #const8
CJNE 2
CLR 1
CPL 1
DA 1
DEC 1
DIV 4
DJNZ 2
INC 1 @Ri
A
direct
Rn
2 DPTR
JB 2
JBC 2
JC 2
JMP 2
JNB 2
JNC 2
JNZ 2
JZ 2
LCALL 2
LJMP 2
MOV 1 @Ri, #const8
@Ri, A
A, #const8
A, @Ri
A, direct
A, Rn
C, bit
direct, A
Rn, #const8
Rn, A
2 @Ri, direct
bit, C
direct, #const8
direct, @Ri
direct, direct
direct, Rn
DPTR, #const16
Rn, direct
MOVC 2
MOVX 2
MUL 4
NOP 1
ORL 1 A, #const8
A, @Ri
A, direct
A, Rn
direct, A
2 C, /bit
C, bit
direct, #const8
POP 2
PUSH 2
RET 2
RETI 2
RL 1
RLC 1
RR 1
RRC 1
SETB 1
SJMP 2
SUBB 1
SWAP 1
XCH 1
XCHD 1
XRL 1 A, #const8
A, @Ri
A, direct
A, Rn
direct, A
2 direct, #const8

.
.
.

REF:
http://www.8051projects.net/serial-communication/software-uart-8051.php

要顯示 ADC 的結果到 PC 的螢幕, 不是新問題, 也不是難題, 不過就是要特別給這個 ATTINY26 量身訂造一個. 不是最好的寫法, 不過最直接.接好線, DEMO 會在 PC 螢幕顯示一個字母 A 實際上, 只要計算好…

http://www.keil.com/support/man/docs/is51/is51_instructions.htm

http://plit.de/asem-51/append_j.htm

http://plit.de/asem-51/bintro.htm

http://plit.de/asem-51/bdesign.htm

廣告

發表迴響

在下方填入你的資料或按右方圖示以社群網站登入:

WordPress.com Logo

您的留言將使用 WordPress.com 帳號。 登出 / 變更 )

Twitter picture

您的留言將使用 Twitter 帳號。 登出 / 變更 )

Facebook照片

您的留言將使用 Facebook 帳號。 登出 / 變更 )

Google+ photo

您的留言將使用 Google+ 帳號。 登出 / 變更 )

連結到 %s