星期一, 9月 12, 2005

雙UART Program - 1 hardware, 1 software : Impmenet Soft UART in PIC.

一個UART是hardware peripherial, 另一個是software。
Software UART的pin是PortC,因為PIC的IO interrupt只有PORTB有IO interrupt,所以代表這個Software UART沒有(辦法)使用interrupt,所以應該是用polling的方式。或是用Timer 定時Sampling。
猜測RX用定時Sampling,TX用polling。因為都要用timer,所以是half-duplex。

follow microchip的AN555 software implement UART : sampling freq = 4 x Baudrate.

RX StartBit的偵測是用Counter/Capture 來作:
將Timer 設定和外部RX Pin腳同步counting (下緣)。
設定Timer Initial 值FF,這樣只要count 1就會overflow引發中斷。
但是只有Timer0可以Synch到Falling edge,Timer1只能Synch Rising Edge.

所以只能使用Timer作,以4x freq作Sampling。
4800 => 1/4800 : 0.208mS => 0.208mS/4 : 0.052mS => 52uS
20MHz => 20MHz/4 : 5MHz => 1/5MHz : 0.2uS

52/0.2 = 260. > 255,所以用prescale = 2, count 130. TMR0 Preload=125


結果我用code :

bit RcvStartBit;
bit RcvOk;
char sampleCount;
char biti;
char RcvByte;

#define TMR0PRELOAD (201)
unsigned int err=0;
void interrupt inthandler(void)
{
if(TMR0IF){
TMR0IF=0;
TMR0=TMR0PRELOAD;
if(!RcvStartBit){
if(!RC1){
sampleCount=5;
RcvStartBit=1;
biti=0;
RcvByte=0;
}
}else if(sampleCount>0){ // StartBit received
sampleCount--;
}else{
sampleCount=3;
if(biti<8){>>= 1;
if(RC1)
RcvByte |= 0x80;
biti++;
}else{
if(RC1){
RcvOk=1;
}else{
err++;
}
RcvStartBit=0;
}
}
}
}

startbit wait 6
interbit wait 4
receive '0' (0x30). 收到的卻是0xFA.
所以把125改成200才OK. --- 查,這是9600 baudrate用的PRELOAD SETTING


可見效率很差,要把4x sampling改成2x sample (start-bit 要wait 1.5 bittime)
或是採用wait startbit 和interbit 不同的freq來作。


奇怪,今天試,是113 - 123是OK的區域
( 255 - 113) ~ ( 255 - 123)
也就是142 ~ 132. --- 這是4800的PRELOAD SETTING,和計算值125比較接近。
計算 -->實際
65 --> 55
125 --> 110
所以猜測interrupt 的overhead是10


下圖是RS232的't'字波形
RS232Sample
(from : PUMPKIN's AN-8, Implemeting Quard 1200 Full-dulpex Software UART)
從Start bit後,每一點間隔都是一個bit -time (1/baudra)。

但是如果Start bit的Sample 點在bit period的後段時,間隔一個bit time後,可能因為傳送端和接收段的Timming 誤差而導致sample到下一個bit period。

所 以為了增加timming的torance,要儘早的sample到Start bit,一般Start bit都是以baudrate的4倍取樣,這樣可以確保Start bit同步在前1/4 bit period中,之後再1+1/2 bit time後作為下一個bit的sample點。之後每個bit 間隔1 bit time。

上面的code就是用Timer0產生4x baudrate的中斷,在中斷中sample各bit,所以start bit後的delay count = 5 (+1),之後每個bit的delay count是 3 (+1)


其實這樣很愚蠢,因為PIC的Timer0不是Auto Reload,而是每次要自己Reload,所以可以設計成不同Sampling Freq :
Sample Start bit 時,Sampling Freq = 4x baudrate.
Sample data bit時,Sampling Freq = baudrate.
這樣可以減少不必要的中斷。
* 從這裡也可以看出,如果當初設計hardware的人,將RX assign到有interrupt功能的pin腳上,也不用浪費這4x Sampling Freq的中斷時間。....我也覺得莫名其妙。為什麼放著可以中斷的PORTB不用(接到LED),反而把RX拉到RC。-----難道是要證明他們的 功力?


試作none-even sampling freq:
一個bit time需要delay的timer count:
1/4800 = 2.08x10-4
2.08x10-4/2x10-7 = 1040.
所以prescale 要用8, Delay Counting = 1040/8=130.
Startbit counting = 130/4=32.5, 從上一個code test,約120,所以用30.
Interbit counting = 130, 從上一個code test, 修正10/4,所以用128.
TEST CODE:

#include <pic.h>

bit RcvStart;
bit RcvOk;
char biti;
char RcvData;
char RxData;
char RcvErr=0; // for debug

#define TMR0PRELOAD_START (255-30)
#define TMR0PRELOAD_BIT (255-131)

void interrupt inthandler(void)
{
if(TMR0IF) {
TMR0IF=0;
if(!RcvStart){
if(!RC1) {
TMR0=TMR0PRELOAD_BIT;
RcvStart=1;
biti=0;
RcvData=0;
}else{
TMR0=TMR0PRELOAD_START;
}
}else{
if(biti<8){ tmr0="TMR0PRELOAD_BIT;">>= 1;
if(RC1)
RcvData |= 0x80;
biti++;
}else{
TMR0=TMR0PRELOAD_START;
if(RC1){
RxData=RcvData;
RcvOk=1;
}else{
RcvErr++;
}
RcvStart=0;
}
}
}
}

char data[20];
void main(void)
{
char ri;

OPTION=0x02; // prescale use 8
TMR0=TMR0PRELOAD_START;
TRISC=0x0F;
TMR0IE=1;
GIE=1;

ri=0;
while(1) {
if(RcvOk){
RcvOk=0;
data[ri]=RxData;
ri++;
if(ri>=20)
ri=0;
}
}
}

結果STARTBIT在255-30的情況下,INTERBIT可以使用255-(126~137)的range.所以用131


Tx也用同樣的方法,以下是Tx/Rx半雙工的Code.

// Software UART, half-duplex
// call RcvStart to start the Rx session. (and stop the Tx session)
// call RcvStop befor sending Data out.

#include

#define TXPIN RC0 // remember to modify the TRISC config
#define RXPIN RC1

#define TMR0PRELOAD_START (255-16) // 4800 : 30 9600: 16
#define TMR0PRELOAD_BIT (255-63) // 4800 : 130 9600: 63

bit TxProcess=0; // Tx In Process
bit RxProcess=0; // Rx In Process
bit RxStart=0; // Receive a Start bit
bit RxError=0; // Receive a Error condition
char TRBiti; // bit index, used in rx/tx bit
bit RxOk; // receive 1 char in TRData
char TRData; // sending, receiving data

char RxData; // receiving data : shift register - workspace


// INTERRUPT
//==================
void interrupt inthandler(void)
{
if(TMR0IF) {
TMR0IF=0;

if(TxProcess){ // Process TX
TMR0=TMR0PRELOAD_BIT;
if(TRBiti==10){ // start bit
TXPIN=0;
}else if(TRBiti==1){ // stop bit
TXPIN=1;
}else if(TRBiti==0){ // all complete
TMR0IE=0;
TxProcess=0;
}else{
if(TRData&0x01){
TXPIN=1;
}else{
TXPIN=0;
}
TRData = TRData>>1;
}
TRBiti--;
}else if(RxProcess){ // Process RX
if(!RxStart){
if(!RXPIN){
TMR0=TMR0PRELOAD_BIT;
RxStart=1;
TRBiti=0;
RxData=0;
}else{
TMR0=TMR0PRELOAD_START;
}
}else{
if(TRBiti<8){
TMR0=TMR0PRELOAD_BIT;
RxData = RxData >> 1;
if(RXPIN)
RxData = RxData | 0x80;
TRBiti++;
}else{
TMR0=TMR0PRELOAD_START;
if(RXPIN){
TRData = RxData;
RxOk=1;
}else{
RxError=1;
}
RxStart=0;
}
}
}else{
TMR0IE=0;
}
}
}

// Sending
//=========================
void SendChar(char data)
{
while(TxProcess);
TxProcess=1;
TRBiti=10;
TRData=data;
TMR0=TMR0PRELOAD_BIT;
TMR0IE=1;
}

void SendCString(const char *str)
{
char data;
char i;

i=0;
data=str[i];
while(data!=0){
SendChar(data);
i++;
data=str[i];
}
}

//==============================================
void RcvStart(void)
{
while(TxProcess); // wait Tx Complete
TMR0IE=0;
RxProcess=1;
TRBiti=0;
RxStart=0;
RxOk=0;
RxError=0;
TMR0=TMR0PRELOAD_START;
TMR0IE=1;
}

void RcvStop(void)
{
TMR0IE=0;
RxProcess=0;
}

//==============================================
char rcvstr[40];
char rcvlen=0;

void main(void)
{
int i;

OPTION=0x02; // prescale 8
TRISC=0x02;
GIE=1;

SendCString("Hello!\r\n");
RcvStart();
for(i=0;i<sizeof(rcvstr);i++){
while(!RxOk);
rcvstr[i]=TRData;
RxOk=0;
}
RcvStop();
SendCString("Receiveing OK\r\n");

while(1); // end
}

沒有留言:

網誌存檔