Note: A Remote Programable Digital Input/Ouput module

這是一個用 8051實作使用 UART/RS485通訊的Keil C code的案例, 目前這個簡單的模組已經在某個大眾運輸系統中, 還算穩定的運行超過 10 years. 這個code 比較值得一提的是利用切換UART mode, 來避免當使用8051在RS485使用時需要去 delay 1bits的時間才能切換 Direction Pin. (將模式從 N, 8, 1 變成 N, 9, 1 , 因為8051的 TX Interrupt 是在stop bit 發出時同時 trigger 這時 stop bit 並未完成, 直接切 RS485的direction pin 的話, 會造成該 byte的 frame error!)

硬體是簡單的 8031 + ROM + TTL (最早的是 8031+ROM+PIC 8255, 但是應用上8255常發生latch up 導致損毀, 成本也高), 後來改用簡單的 GAL + TTL去做8255的基本功能模擬

基本的細節都在 CM.H這個檔案內

click here to download the project source code

or get from github

因為之前有寫過 8051 UART C Version 有些人光看該code, 好像還是不懂怎樣用, 所以現在放出一個簡單又完整的專案, 但是這個專案因為是比較早期寫的所以某些 code style會多少不一致, 但是思路是一致的.

廣告

a simple DIO/RS485 firmware Part II

Update: the source put on Github now
there context is file “cm.h"

#ifndef CM_KunI
#define CM_KunI 1
/* ———————————————— *
* This is new version of Controller Module         *
* File Name   : CM.H                               *
* Author      : K’un I Chen                        *
* Version     : 1.00                               *
* Create Date : 2002/ 4/ 15                        *
* Modification:                                    *
* Developer Platform : Keil C v6.2x & Intel 8031   *
* ———————————————— */

/* include all header file                          */
#include <REG51.H>
#include <ABSACC.H>
/* Peripheral Address                               */

/* ———————————————— *
* External RAM Address                             *
* 0xF000 ~ 0xFFFF                                  *
* use 62128 or 62256 or 61128 or 61256             *
* have 16K or 32 Byte                              *
* ———————————————— */

/* System constance                                 */
#define  XTAL                             (11059200)
#define  TIMER0H                  ((65535-9207)/256)
#define  TIMER0L                  ((65535-9207)%256)

/* Programable Interface Device 8255                */
#define  PID_BASE        0x2000
#define  PID_A           (XBYTE[PID_BASE])
#define  PID_B           (XBYTE[PID_BASE+1])
#define  PID_C           (XBYTE[PID_BASE+2])
#define  PID_CTRL        (XBYTE[PID_BASE+3])
/* ———————————————— *
* Pin assign                                       *
* DIP_SW   use Port1                               *
* MUX_SEL  use P1.4                                *
* UART_DIR use P3.4                                *
* WatchDOg use P3.5                                *
*                                                  *
* ———————————————— */
#define  DIP_SW  P1
sbit MUXSEL = P1^4;
sbit UART_DIR = P3^4;
sbit WATCH_DOG = P3^5;

/* PID 8255 CTL(controll) register attrib           */
#define  PID_AIN        (0x90)    /* 10010000B bit4 */
#define  PID_AOUT       (0x80)    /* 10000000B      */
#define  PID_BIN        (0x82)    /* 10000010B bit1 */
#define  PID_BOUT       (0X80)    /* 10000000B      */
#define  PID_CHIN       (0X88)    /* 10001000B bit3 */
#define  PID_CHOUT      (0X80)    /* 10000000B      */
#define  PID_CLIN       (0x81)    /* 10000001B bit0 */
#define  PID_CLOUT      (0x80)    /* 10000000B      */
/* Directional mode                                 */
#define  DIRWITHIN         1      /* Dir with in    */
#define  DIRWITHOUT        0      /* Dir with out   */

/* Serial communication packet                      */
/* ———————————————— *
* Packet format defined                            *
* <SOH> : Start Of Header, it’s always 0x01        *
* <NUM> : CM Number                                *
* <CMD> : Command                                  *
* <VAL> : Command parameter                        *
* <OPT> : <CMD> Optional parameter                 *
* <CHK> : CheckSum, from <NUM> field XOR each all  *
*         date field                               *
* ———————————————— */
#define     SOH            (0x01)
#define     MASTERID       (0x00)

/* defined element                                  */
/* ———————————————— *
* Command define                                   *
* character ‘D’: direction setting of PID Port     *
* character ‘R’: read port value of PID            *
* character ‘W’: write port value of PID           *
* character ‘P’: generation a pulse                *
* character ‘T’: pulse width parameter             *
* character ‘S’: send a 9HZ, CM self produce,      *
* ———————————————— */
/* <CMD> defined                                   */
#define CMD_DIRECTION        (‘D’)
#define CMD_READ             (‘R’)
#define CMD_WRITE            (‘W’)
#define CMD_PULSE            (‘P’)
#define CMD_TERM             (‘T’)
#define CMD_AIOWATCHDOG      (‘S’)

/* <VAL> defined                                    */
#define PORT_A               (‘A’)
#define PORT_B               (‘B’)
#define PORT_CH              (‘H’)
#define PORT_CL              (‘L’)
#define DIRECTION_STATE      (‘d’)
#define CLOSEAIO             (‘N’)

/* <OPT> defined                                    */
#define SET_INPUT            (‘I’)
#define SET_OUTPUT           (‘O’)

/* defined customer data type                       */
typedef enum { WAIT_SOH,     /* wait SOH      */
RCV_ID,       /* get module ID */
RCV_CMD,      /* get command   */
RCV_VAL,      /* get value     */
RCV_OPT,      /* get optional  */
RCV_CHK       /* get checksum  */
} RcvPacketState;

typedef enum { FALSE = 0, TRUE = 1 } Boolean;
typedef enum { RX_DIR = 0, TX_DIR  = 1 } UartDir;
typedef enum { ID_SEL = 0, DIR_SEL = 1} MuxSel;
typedef unsigned char   Byte;
typedef Byte *   PByte;
typedef unsigned short  Word;
typedef Word *   PWord;

typedef struct {  Byte QueH;
Byte QueT;
Byte QueLen; } QueCtrl;

/* functions declarative                            */
void Initiation(void);
void GeneraltionPulse(void);
Byte GetParameterLen(Byte cmd);
void DeCommand(void);
void ReplyCMD(Byte typed);
void DispatchCMD(void);
void SettingPID(void);

void GetID(void);
void GetDirSW(void);

/* interrupt vector function                        */
void UART_INT(void);
void TIMER0_INT(void);

/* data struct operation macro define               */
#define MaxQueLen  (128)

#define CreateQue(x)        Byte xdata x[MaxQueLen];
QueCtrl data x##C;
Boolean _PutTo##x(Byte Val);
Boolean _GetFrom##x(PByte Val);
#define ImplementQue(x)     Boolean _PutTo##x(Byte Val)
{
ES = FALSE;
if (IsFullOfQue(x))
{ ES = TRUE; return FALSE; }
x##C.QueLen++;
x[x##C.QueT++] = Val;

if(x##C.QueT >= MaxQueLen) x##C.QueT = 0;
ES = TRUE;
return TRUE;
}

Boolean _GetFrom##x(PByte Val)
{
ES = FALSE;
if (IsEmptyOfQue(x))
{ ES = TRUE; return FALSE; }
*Val = x[x##C.QueH++];
x##C.QueLen–;
if(x##C.QueH >= MaxQueLen) x##C.QueH = 0;
ES = TRUE;
return TRUE;
}
#define InitialQue(x)   x##C.QueH = x##C.QueT = x##C.QueLen = 0
#define IsEmptyOfQue(x) (x##C.QueLen == 0) ? TRUE:FALSE
#define IsFullOfQue(x)  (x##C.QueLen >= MaxQueLen) ? TRUE:FALSE
#define GetQueLen(x)    x##C.QueLen
#define PutToQue(x, value) _PutTo##x((value))
#define GetFromQue(x, value) _GetFrom##x(value)

#define CONFIRMOK        (0)
#define MAINCMDERR       (CONFIRMOK+1)
#define DIRSUBCMDERR     (CONFIRMOK+2)
#define RDDIRERR         (CONFIRMOK+3)
#define RDSUBCMDERR      (CONFIRMOK+4)
#define WRDIRERR         (CONFIRMOK+5)
#define WRSUBCMDERR      (CONFIRMOK+6)
#define FAILPULSECMD     (CONFIRMOK+7)
#define PULSEDIRERR      (CONFIRMOK+8)
#define PULSESUBCMDERR   (CONFIRMOK+9)
#define RETURNVALUE      (CONFIRMOK+10)

#endif

a simple DIO/RS485 firmware Part I

Update: the source put on Github now
there context is file “cm.c "

Hardware:

 MCS-51

4KROM SIZE

4KRAM SIZE

communication physical interface:

9600bps, N, 8, 1, RS485 transceiver or RS232

#include “cm.h"

/* ———————————————— *
* File Name   : CM.C                               *
* Author      : K’un I Chen                        *
* Version     : 1.00                               *
* Create Date : 2002/ 4/ 15                        *
* Modification:                                    *
* Developer Platform : Keil C v6.2x                *
* ———————————————— */
Byte bdata   _BitsArray;
Byte bdata   _PulsePortMask;
/* Direction of PID port                    */
sbit PORTA_DIR      = _BitsArray^3;
sbit PORTB_DIR      = _BitsArray^2;
sbit PORTCH_DIR     = _BitsArray^1;
sbit PORTCL_DIR     = _BitsArray^0;
/* Recvice packet OK                        */
sbit _bRcvOK        = _BitsArray^4;
sbit _bNowPulse     = _BitsArray^5;
sbit _bAIO_WatchDog = _BitsArray^6;
/* Pulse of PID port                        */
sbit _bPulseOfPA    = _PulsePortMask ^ 3;
sbit _bPulseOfPB    = _PulsePortMask ^ 2;
sbit _bPulseOfPCH   = _PulsePortMask ^ 1;
sbit _bPulseOfPCL   = _PulsePortMask ^ 0;
/* receive command buffer                   */
Byte data  _RcvID, _RcvCMD, _RcvVAL, _RcvOPT;
/* Phase packet state                       */
Byte data  _RcvState;
/* Transmit command buffer                  */
Byte data  _TxVAL;
/* Control module ID                        */
Byte data  _SelfID;
/* AIO Reset count                          */
Byte data  _TimerCount;
Word data  _10SCount;
Byte data  _AIOCount;
/* 9Hz = 11.1ms                             */
#define AIOOneCycle      11
#define AIOWDLength    1000
/* PID output bufffer                       */
Byte data  _PortABuff, _PortBBuff, _PortCBuff;
/* Generaltion pulse mask                   */
Byte data  _PortAMask, _PortBMask, _PortCMask;
/* Current pulse lenght count               */
Byte data  _PulseCount;
/* Pulse width                              */
Byte data  _PulseWidth;

/* declear network fifo(circle quene)       */
CreateQue(_RxQue)
CreateQue(_TxQue)
/* Implement opration function              */
ImplementQue(_RxQue)
ImplementQue(_TxQue)

void main(void)
{
Initiation();

while(1)
{
/* Reset watchdog timer  */
WATCH_DOG = ~WATCH_DOG;
/* get controller module ID */
GetID();
/* recvice packet        */
if (GetQueLen(_RxQue) > 0)
DeCommand();

/* Process host command  */
if (_bRcvOK)
{
_bRcvOK = FALSE;
DispatchCMD();
}
/* Process pulse        */
if (_bNowPulse)
{
if ((Byte)(_PulseWidth + _PulseCount) == _TimerCount)
{
GeneraltionPulse();
_bNowPulse = FALSE;
}
}
/* Process AIO pulse       */
if (_bAIO_WatchDog)
{
if ((Byte)(AIOOneCycle + _AIOCount) == _TimerCount)
{
/* should check PID_C directional
but old CM no check            */
_AIOCount = _TimerCount;

// NOT ( bit 7 of _PortCBuff)
if ((_PortCBuff & 0x80) == 0x80)
_PortCBuff &= 0x7F;
else
_PortCBuff |= 0x80;
}

if (_10SCount >= AIOWDLength)
{
_bAIO_WatchDog = FALSE;
/* disable AIO watch dog then
must be clear PID_C bit 7 */
_PortCBuff &= 0x7F;
}
PID_C = _PortCBuff;
} /* end of if(_bAIO_WatchDog) */
} /* main loop */
}

/* ————————————————— *
* Function : Initial                                  *
* Parameter: void                                     *
* result   : void                                     *
* descript : initial system                           *
* ————————————————— */
void Initiation(void)
{
WATCH_DOG = ~WATCH_DOG;

_bRcvOK = _bNowPulse = FALSE;
_RcvCMD = _RcvVAL = _RcvOPT = 0;
_10SCount = _AIOCount = _TimerCount = 0;
_PortABuff = _PortBBuff = _PortCBuff = 0;
// default Port out;
P1 = 0x0F;
P3 = 0xCF;
_bAIO_WatchDog = TRUE;
_RcvState = WAIT_SOH;

WATCH_DOG = ~WATCH_DOG;
/* getting control module ID and PID port directional */
GetDirSW();
SettingPID();
UART_DIR = RX_DIR;

WATCH_DOG = ~WATCH_DOG;

/* Initial circuit         */
InitialQue(_RxQue);
InitialQue(_TxQue);

WATCH_DOG = ~WATCH_DOG;

/* setting timer 0 with mode 1
timer 1 with mode 2 */
TMOD = 0x21;

TH0 = TIMER0H;
TL0 = TIMER0L;
TH1 = 253;   /* 9600 of baudrate   */
SCON = 0x50; /* 8bits of char      */

TCON = 0x50; /* TR0, TR1 enable    */
IE = 0x92;   /* EA, ES, ET0 enable */
IP = 0x10;   /* PS enable          */

TR1 = 1;
TR0 = 1;
ES = 1;
ET0 = 1;
EA = 1;

WATCH_DOG = ~WATCH_DOG;

}

/* ————————————————— *
* Function : GetID                                    *
* Parameter: void                                     *
* result   : void                                     *
* descript : get DIP switch setting for CM ID         *
* ————————————————— */
void GetID(void)
{
MUXSEL = ID_SEL;
_SelfID = (DIP_SW & 0x0f);
}

/* ————————————————— *
* Function : GetDirSw                                 *
* Parameter: void                                     *
* result   : void                                     *
* descript : get DIP switch setting for PID port      *
*            directional                              *
* ————————————————— */
void GetDirSW(void)
{
MUXSEL = DIR_SEL;
_BitsArray &= 0xF0;
_BitsArray |= (DIP_SW & 0x0F);
}

/* ————————————————— *
* Function : SettingPID                               *
* Parameter: void                                     *
* result   : void                                     *
* descript : Setting PID port directional             *
* ————————————————— */
void SettingPID(void)
{
Byte Ctrl = 0x80;
if (PORTA_DIR == DIRWITHIN) Ctrl |= 0x10;
if (PORTB_DIR == DIRWITHIN) Ctrl |= 0x02;
if (PORTCH_DIR == DIRWITHIN) Ctrl |= 0x08;
if (PORTCL_DIR == DIRWITHIN) Ctrl |= 0x01;
PID_CTRL = Ctrl;
}

/* ————————————————— *
* Function : DispatchCMD                              *
* Parameter: cmd, val, opt                            *
* result   : void                                     *
* descript : process cmd and dispatch                 *
* ————————————————— */
void DispatchCMD(void)
{
Byte ReCode = CONFIRMOK;

switch (_RcvCMD)
{
case CMD_DIRECTION:
/* Setting PID port direction */
switch (_RcvVAL)
{
case PORT_A:
switch (_RcvOPT)
{
case SET_INPUT:
PORTA_DIR = DIRWITHIN;
break;
case SET_OUTPUT:
PORTA_DIR = DIRWITHOUT;
break;
default:
goto DIRSUBERR;
break;
}
break;
case PORT_B:
switch (_RcvOPT)
{
case SET_INPUT:
PORTB_DIR = DIRWITHIN;
break;
case SET_OUTPUT:
PORTB_DIR = DIRWITHOUT;
break;
default:
goto DIRSUBERR;
break;
}
break;
case PORT_CH:
switch (_RcvOPT)
{
case SET_INPUT:
PORTCH_DIR = DIRWITHIN;
break;
case SET_OUTPUT:
PORTCH_DIR = DIRWITHOUT;
break;
default:
goto DIRSUBERR;
break;
}
break;
case PORT_CL:
switch (_RcvOPT)
{
case SET_INPUT:
PORTCL_DIR = DIRWITHIN;
break;
case SET_OUTPUT:
PORTCL_DIR = DIRWITHOUT;
break;
default:
goto DIRSUBERR;
break;
}
break;
default:
DIRSUBERR:
ReCode = DIRSUBCMDERR;
break;
}

if (ReCode == CONFIRMOK)
SettingPID();
break;
/* End CMD_DIRECTION case     */
case CMD_READ:
/* Reading PID port value     */
switch (_RcvVAL)
{
case PORT_A:
if (PORTA_DIR == DIRWITHIN)
{
_TxVAL = PID_A;
ReCode = RETURNVALUE;
}
else
ReCode = RDDIRERR;
break;
case PORT_B:
if (PORTB_DIR == DIRWITHIN)
{
_TxVAL = PID_B;
ReCode = RETURNVALUE;
}
else
ReCode = RDDIRERR;
break;
case PORT_CH:
if (PORTCH_DIR == DIRWITHIN)
{
_TxVAL = (PID_C >> 4);
ReCode = RETURNVALUE;
}
else
ReCode = RDDIRERR;
break;
case PORT_CL:
if (PORTCL_DIR == DIRWITHIN)
{
_TxVAL = PID_C & 0x0F;
ReCode = RETURNVALUE;
}
else
ReCode = RDDIRERR;
break;
case DIRECTION_STATE:
/* Read directional with all port */
_TxVAL = _BitsArray & 0x0F;
ReCode = RETURNVALUE;
break;
default:
ReCode = RDSUBCMDERR;
break;
}
break;
/* End CMD_READ case          */
case CMD_WRITE:
/* Reading PID port value     */
switch (_RcvVAL)
{
case PORT_A:
if (PORTA_DIR == DIRWITHOUT)
{
_PortABuff = _RcvOPT;
PID_A = _RcvOPT;
}
else
ReCode = WRDIRERR;
break;
case PORT_B:
if (PORTB_DIR == DIRWITHOUT)
{
_PortBBuff = _RcvOPT;
PID_B = _RcvOPT;
}
else
ReCode = WRDIRERR;
break;
case PORT_CH:
if (PORTCH_DIR == DIRWITHOUT)
{
_PortCBuff &= 0x0F;
_PortCBuff = (_RcvOPT << 4);
PID_C = _PortCBuff;
}
else
ReCode = WRDIRERR;
break;
case PORT_CL:
if (PORTCL_DIR == DIRWITHOUT)
{
_PortCBuff &= 0xF0;
_PortCBuff |= (0x0F & _RcvOPT);
PID_C = _PortCBuff;
}
else
ReCode = WRDIRERR;
break;
case DIRECTION_STATE:
/* Setting directional of all port */
_BitsArray &= 0xF0;
_BitsArray |= (0x0F & _RcvOPT );
SettingPID();
break;
default:
ReCode = WRSUBCMDERR;
break;
}
break;
/* End CMD_WRITE case         */
case CMD_PULSE:
/* Port ‘val’ generation pulse
with (opt*10ms) width */
if (!_bNowPulse)
{
switch(_RcvVAL)
{
Byte temp;

case PORT_A:
if (PORTA_DIR == DIRWITHOUT)
{
_bNowPulse   = TRUE;
_bPulseOfPA  = TRUE;
_PortAMask   = _RcvOPT;
_PortABuff  ^= _PortAMask;
PID_A        = _PortABuff;
}
else
ReCode = PULSEDIRERR;
break;
case PORT_B:
if (PORTB_DIR == DIRWITHOUT)
{
_bNowPulse   = TRUE;
_bPulseOfPB  = TRUE;
_PortBMask   = _RcvOPT;
_PortBBuff  ^= _PortBMask;
PID_B        = _PortBBuff;
}
else
ReCode = PULSEDIRERR;
break;
case PORT_CH:
if (PORTCH_DIR == DIRWITHOUT)
{
_bNowPulse   = TRUE;
_bPulseOfPCH = TRUE;
_PortCMask  &= 0x0F;
_PortCMask  |= (_RcvOPT << 4);
temp  = _PortCBuff ^ _PortCMask;
temp &= 0xF0;
_PortCBuff |= temp;
PID_C = _PortCBuff;
}
else
ReCode = PULSEDIRERR;
break;
case PORT_CL:
if (PORTCL_DIR == DIRWITHOUT)
{
_bNowPulse    = TRUE;
_bPulseOfPCL  = TRUE;
_PortCMask   &= 0xF0;
_PortCMask   |= (_RcvOPT & 0x0F);
temp  = _PortCBuff ^ _PortCMask;
temp &= 0x0F;
_PortCBuff |= temp;
PID_C = _PortCBuff;
}
else
ReCode = PULSEDIRERR;
break;
default:
ReCode = PULSESUBCMDERR;
} /* end switch(_RcvVAL) */
}
else { ReCode = FAILPULSECMD; }

if (ReCode == CONFIRMOK)
_PulseCount = _TimerCount;
break;
/* End CMD_PULSE case         */
case CMD_TERM:
/* setting a pulse width      */

/* Is generaltion pulse       */
if (!_bNowPulse)
_PulseWidth = _RcvVAL;
else
ReCode = FAILPULSECMD ;
break;
/* End CMD_TEST case          */

case CMD_AIOWATCHDOG:
/* CM use bit 7 of PID port C
generation 10Hz signal and
length 10sec               */
if (_RcvVAL == ‘N’) _AIOCount = _TimerCount;
_bAIO_WatchDog = TRUE;
_10SCount = 0;
break;
/* End CMD_SEND case          */

default:
ReCode = MAINCMDERR;
break;
}
ReplyCMD(ReCode);
}

void GeneraltionPulse(void)
{
Byte temp;

if (_bPulseOfPA)
{
_bPulseOfPA = FALSE;
if (DIRWITHOUT == PORTA_DIR)
{
_PortABuff ^= _PortAMask;
PID_A = _PortABuff;
}
}

if (_bPulseOfPB)
{
_bPulseOfPB = FALSE;
if (DIRWITHOUT == PORTB_DIR)
{
_PortBBuff ^= _PortBMask;
PID_B = _PortBBuff;
}
}

if (_bPulseOfPCH)
{
_bPulseOfPCH = FALSE;
if (DIRWITHOUT == PORTCH_DIR)
{
temp  = _PortCMask ^ _PortCBuff;
temp &= 0xF0;
_PortCBuff |= temp;
PID_C = _PortCBuff;
}
}

if (_bPulseOfPCL)
{
_bPulseOfPCL = FALSE;
if (DIRWITHOUT == PORTCL_DIR)
{
temp  = _PortCMask ^ _PortCBuff;
temp &= 0x0F;
_PortCBuff |= temp;
PID_C = _PortCBuff;
}
}
}

/* ————————————————— *
* Function : GetParameterLen                          *
* Parameter: cmd                                      *
* result   : parameter number                         *
* descript : get CMD parameter number                 *
* ————————————————— */
Byte GetParameterLen(Byte cmd)
{
register Byte result = 0;
switch (cmd) {
case CMD_DIRECTION:
case CMD_WRITE:
case CMD_PULSE:
result = 2;
break;
case CMD_READ:
case CMD_TERM:
case CMD_AIOWATCHDOG:
result = 1;
break;
}
return result;
/*
Byte code CMD_TBL[] =
{ CMD_DIRECTION, CMD_READ, CMD_WRITE,
CMD_PULSE, CMD_TERM, CMD_AIOWATCHDOG };
Byte code CMD_LEN[(sizeof(CMD_TBL)/sizeof(Byte))] =
{ 2, 1, 2, 2, 1, 1 };
Byte i;
for( i = 0; i < (sizeof(CMD_TBL)/sizeof(Byte)); i++)
if (CMD_TBL[i] == cmd) return CMD_LEN[i];
return 0;
*/
}

/* ————————————————— *
* Function : ReplyCMD                                 *
* Parameter: typed                                    *
* result   : null                                     *
* descript : reply master command                     *
* ————————————————— */
void ReplyCMD(Byte typed)
{
Byte Cmd, Val;
Byte Chk;

switch(typed)
{
case CONFIRMOK:
Cmd = ‘O’;
Val = ‘K’;
break;
case RETURNVALUE:
Cmd = ‘V’;
Val = _TxVAL;
break;
case MAINCMDERR:
case DIRSUBCMDERR:
case RDDIRERR:
case RDSUBCMDERR:
case WRDIRERR:
case WRSUBCMDERR:
case    FAILPULSECMD:
case PULSEDIRERR:
case PULSESUBCMDERR:
Cmd = ‘X’;
Val = ‘1’ + (typed – 1);
break;
default:
/* CM error */
Cmd = ‘E’;
Val = ‘R’;
break;
}
Chk = MASTERID;
Chk ^= Val;
Chk ^= Cmd;

PutToQue(_TxQue, SOH);
PutToQue(_TxQue, MASTERID);
PutToQue(_TxQue, Cmd);
PutToQue(_TxQue, Val);
PutToQue(_TxQue, Chk);
UART_DIR = TX_DIR;
TI = 1;
}

/* ————————————————— *
* Function : UART_INT                                 *
* Parameter: null                                     *
* result   : null                                     *
* descript : UART event interrupter, it using 2 of    *
*            Register Bank                            *
* ————————————————— */
void UART_INT(void) interrupt 4 using 2
{
Byte tmp;

if (RI)
{
RI = 0;
_bAIO_WatchDog = TRUE;
ET0 = 0;
_10SCount = 0;
ET0 = 1;
PutToQue(_RxQue,SBUF);
}

if (TI)
{
TI = 0;
if (GetFromQue(_TxQue,&tmp) == TRUE)
{
if (IsEmptyOfQue(_TxQue))
{
// using RS485 network interface
// then the last of transmit stream
// switch to 9bits format with UART registers
// corrected problem with cutoff with stop bit
TB8 = 1;     /* dummy stop bit       */
SCON = 0xF0; /* switch to 9bits mode */
}
SBUF = tmp;
}
else
{
// Tx queue is empty
// re-switch 8bits format and chage to receiver
SCON = 0x50;
UART_DIR = RX_DIR;
}
}
}

/* ————————————————— *
* Function : TIMER0_INT                               *
* Parameter: null                                     *
* result   : null                                     *
* descript : timer0 event interrupter, it using 2 of  *
*            Register Bank,Timer 0 is Mode 1, adjust  *
*            10ms length                              *
* ————————————————— */
void TIMER0_INT(void) interrupt 1 using 2
{
TH0 = TIMER0H;
TL0 = TIMER0L;
_10SCount++;
_TimerCount++;
}

/* ————————————————— *
* Function : DeCommand                                *
* Parameter: null                                     *
* result   : null                                     *
* descript : check correct packet of recvice stream   *
*            and decode it to command type            *
* ————————————————— */
void DeCommand(void)
{
static Byte RcvCHK;
Byte tmp;

while (!IsEmptyOfQue(_RxQue))
{
if (GetFromQue(_RxQue,&tmp)== FALSE)
return;

switch (_RcvState)
{
case WAIT_SOH:
if (tmp == SOH)
{
_RcvState = RCV_ID;
_RcvCMD = 0;
}
break;

case RCV_ID:
RcvCHK = tmp;
_RcvID = tmp;
_RcvState = RCV_CMD;
break;

case RCV_CMD:
RcvCHK ^= tmp;
_RcvCMD = tmp;
_RcvState = RCV_VAL;
break;

case RCV_VAL:
RcvCHK ^= tmp;
_RcvVAL = tmp;
switch (GetParameterLen(_RcvCMD))
{
case 1:
_RcvState = RCV_CHK;
break;
case 2:
_RcvState = RCV_OPT;
break;
default:
/* otherwise length not defined   */
_RcvState = WAIT_SOH;
break;
}
break;

case RCV_OPT:
RcvCHK ^= tmp;
_RcvOPT = tmp;
_RcvState = RCV_CHK;
break;

case RCV_CHK:
/* _RcvID equal zero nothing */
/* because it be MasterID    */
if ((tmp == RcvCHK) && (_SelfID == _RcvID) && (_RcvID != 0))
_bRcvOK = TRUE;

default:
_RcvState = WAIT_SOH;
break;
}
}
}

8051 UART C Version

這版本的是當初退伍後,工作幾個月後寫的,靈感來自於 Paul’s 8051 Free Tools 的ASM版本,前一兩年在一個案子中還經過一次修訂,改進了使用 RS-485時不需使用 Loop Delay 來產生最後一個位元。這個版本有針對C語言(這裡指的是 Franklin/Keil 5.x or later, 並未在其他Compiler 使用過)的最佳化編寫,因此相較於使用 Assemble 來說增加的程式碼與RAM的使用並不會增加太多成本。程式碼如下

現在提供一個類似, 但是更完整而且實際運行的source code 請參考這 Note: A Remote Programable Digital Input/Ouput module


in uart.h

#if !defined ( _UART_H_ )
#define _UART_H_
#define MaxLength 16
#define UART0_R_Length   RLength0
#define UART0_T_Length   TLength0
#define UART0_R_IsEmpty  ((RLength0 == 0 ) ? TRUE : FLASE)
#define UART0_R_IsFull   ((RLength0 == MaxLength) ? TRUE : FLASE )
#define UART0_T_IsEmpty  ((TLength0 == 0 ) ? TRUE : FLASE)
#define UART0_T_IsFull   ((TLength0 == MaxLength) ? TRUE : FLASE )
#define UART1_R_Length   RLength1
#define UART1_T_Length   TLength1
#define UART1_R_IsEmpty  ((RLength1 == 0 ) ? TRUE : FLASE)
#define UART1_R_IsFull   ((RLength1 == MaxLength) ? TRUE : FLASE )
#define UART1_T_IsEmpty  ((TLength1 == 0 ) ? TRUE : FLASE)
#define UART1_T_IsFull   ((TLength1 == MaxLength) ? TRUE : FLASE )

#if ! defined ( _PYHSICAL_UART_ )
extern BYTE RLength0;
extern BYTE TLength0;
extern BYTE RLength1;
extern BYTE TLength1;
#endif 

void UART0_Interrupt(void);
void UART1_Interrupt(void);
void InitUART0(void);
void InitUART1(void);
BOOLEAN UART0_Put(BYTE);
BOOLEAN UART0_Get(BYTE *);
void UART0Drive(void);
void UART1Drive(void);
BOOLEAN UART1_Put(BYTE);
BOOLEAN UART1_Get(BYTE *);

#endif /* UART.H FILE END */ 

in UART.C implement function
#define OSC 22118400L
#define UART0_BaudRate 9600L
#define UART1_BaudRate 1200L
#define UART0_RS485_Adapter
#define UART0_RS485_DelayTime 5
#define UART1_RS485_Adapter
#define UART1_RS485_DelayTime 40

#if defined ( UART0_RS485_Adapter )
sbit DIR0 = P1 ^ 0;
   #if !defined( TRANSMIT )
      #define TRANSMIT 1
      #define RECEIVE  0
   #endif
#endif 
#if defined ( UART1_RS485_Adapter )
sbit DIR1 = P1 ^ 1;
    #if !defined( TRANSMIT )
        #define TRANSMIT 1
        #define RECEIVE  0
    #endif
#endif 

BYTE TLength0 = 0;
BYTE RLength0 = 0;

BYTE TLength1 = 0;
BYTE RLength1 = 0;

static EXTERN_DATA BYTE UART0_RFIFO[MaxLength];
static EXTERN_DATA BYTE UART0_TFIFO[MaxLength];
static EXTERN_DATA BYTE UART1_RFIFO[MaxLength];
static EXTERN_DATA BYTE UART1_TFIFO[MaxLength];

static P_INTERNAL_DATA BYTE R_Tail0;
static P_INTERNAL_DATA BYTE R_Head0;
static P_INTERNAL_DATA BYTE T_Tail0;
static P_INTERNAL_DATA BYTE T_Head0;

static P_INTERNAL_DATA BYTE R_Tail1;
static P_INTERNAL_DATA BYTE R_Head1;
static P_INTERNAL_DATA BYTE T_Tail1;
static P_INTERNAL_DATA BYTE T_Head1;

static bit uart0;
static bit uart1;

void UART0_Interrupt(void) interrupt 4 using 2
{
   int i;
   register BYTE temp;

   if (TI)                                        /* Is TI flag set                          */
     {  RESET_REGISTER(TI);                       /* Clear TI Flage                          */
        if (TLength0 != 0 )                       /* UART0 Tx FIFO is empty                  */
          { SBUF = UART0_TFIFO[T_Tail0];          /* Put a byte to SBUF                      */
            if (++T_Tail0 == MaxLength)           /* Is T_Tail0 point to UART0 Tx FIFO tail  */
              { T_Tail0 = 0; }                    /* Set T_Tail0 point to UART0 Tx FIFO head */
            TLength0--;
          }
        else
          {
            uart0 = OFF;
       #if defined( UART0_RS485_Adapter )              /* Is UART0 use RS485 Adapter        */
                                                       /* UART0 Tx FIFO is empty            */
            for ( i = 0; i < UART0_RS485_DelayTime;)   /* then delay one bit time           */
               { i++; }
            DIR0 = RECEIVE;                            /* change R/T Direction              */
       #endif
          }
     }
   else
     {
      RESET_REGISTER(RI);
      temp = SBUF;                                     /* Get a byte from SBUF                    */
      if (!(RLength0 == MaxLength))                    /* Is UART0 Rx FIFO not full               */
        { UART0_RFIFO[R_Head0] = temp;                 /* Put a byte to UART0 Rx FIFO             */
          if (++R_Head0 == MaxLength)                  /* Is R_Head0 point to UART0 Rx FIFO tail  */
            { R_Head0 = 0; }                           /* Set R_Head0 point to UART0 Rx FIFO head */
          RLength0++;
        }
     }
}

void UART1_Interrupt(void) interrupt 7  using 2
{
   int i;
   register BYTE temp;
   if (TI1)                                             /* Is TI flag set                          */
     {  RESET_REGISTER(TI1);                            /* Clear TI Flage                          */
        if (TLength1 != 0 )                             /* UART1 Tx FIFO is empty                  */
          {
          /* generation last bit*/
            for ( i = 0; i < UART1_RS485_DelayTime;)    /* then delay one bit time           */
               { i++; }

            SBUF1 = UART1_TFIFO[T_Tail1];               /* Put a byte to SBUF1                     */
            if (++T_Tail1 == MaxLength)                 /* Is T_Tail1 point to UART1 Tx FIFO tail  */
              { T_Tail1 = 0; }                          /* Set T_Tail1 point to UART1 Tx FIFO head */
            TLength1--;
          }
        else                                            /* UART1 Tx FIFO is empty            */
          {
            uart1 = OFF;
       #if defined( UART1_RS485_Adapter )               /* Is UART1 use RS485 Adapter        */
            for ( i = 0; i < UART1_RS485_DelayTime;)    /* then delay one bit time           */
               { i++; }
            DIR1 = RECEIVE;                             /* change R/T Direction              */
       #endif
          }
     }
   else
     {
      RESET_REGISTER(RI1);
      temp = SBUF1;                                    /* Get a byte from SBUF                    */
      if (!(RLength1 == MaxLength))                    /* Is UART1 Rx FIFO not full               */
        { UART1_RFIFO[R_Head1] = temp;                 /* Put a byte to UART1 Rx FIFO             */
          if (++R_Head1 == MaxLength)                  /* Is R_Head1 point to UART1 Rx FIFO tail  */
            { R_Head1 = 0; }                           /* Set R_Head1 point to UART1 Rx FIFO head */
          RLength1++;
        }
     }
}

BOOLEAN UART0_Put(const BYTE Value)
{
  RESET_REGISTER(EA);                         /* Disable Interrupt                         */
  if (TLength0 == MaxLength)                  /* Is UART0 Tx FIFO full                     */
    { SET_REGISTER(EA);                       /* Enable Interrupt                          */
      return FALSE; }                         /* Return FALSE                              */

  UART0_TFIFO[T_Head0] = Value;               /* Put a byte to UART0 Tx FIFO               */
  TLength0++;
  if (++T_Head0 == MaxLength)                 /* Is T_Head0 point to UART0 Tx FIFO tail    */
    { T_Head0 = 0; }                          /* Set T_Head0 point to UART0 Tx FIFO head   */
  SET_REGISTER(EA);                           /* Enable Interrupt                          */
  return TRUE;
}

BOOLEAN UART0_Get(BYTE *Value)
{
  RESET_REGISTER(EA);                           /* Disable Interrupt                       */
    if (RLength0 == 0 )                         /* Is UART0 Rx FIFO empty                  */
      { SET_REGISTER(EA);                       /* Enable Interrupt                        */
        return FALSE; }                         /* Return FALSE                            */
    *Value = UART0_RFIFO[R_Tail0];              /* Get a Byte from UART0 Rx FIFO           */
    if (++R_Tail0 == MaxLength)                 /* Is R_Tail0 point to UART0 Rx FIFO tail  */
      { R_Tail0 = 0; }                          /* Set R_Tail0 point to UART0 Rx FIFO head */
    RLength0--;                                 /*                                         */
    SET_REGISTER(EA);                           /* Enable Interrupt                        */
    return TRUE;
}

BOOLEAN UART1_Put(BYTE Value)
{
  RESET_REGISTER(EA);                           /* Disable Interrupt                       */
  if (TLength1 == MaxLength)                    /* Is UART1 Tx FIFO full                   */
  { SET_REGISTER(EA);                           /* Enable Interrupt                        */
    return FALSE; }                             /* Return FALSE                            */

  UART1_TFIFO[T_Head1] = Value;                 /* Put a byte to UART1 Tx FIFO             */
  if (++T_Head1 == MaxLength)                   /* Is T_Head0 point to UART0 Tx FIFO tail  */
    { T_Head1 = 0; }                            /* Set T_Head0 point to UART0 Tx FIFO head */
  TLength1++;
  SET_REGISTER(EA);                             /* Enable Interrupt                        */
  return TRUE;
}

BOOLEAN UART1_Get(BYTE *Value)
{
  RESET_REGISTER(EA);                           /* Disable Interrupt                       */
    if (RLength1 == 0 )                         /* Is UART0 Rx FIFO empty                  */
      { SET_REGISTER(EA);                       /* Enable Interrupt                        */
        return FALSE; }                         /* Return FALSE                            */
    *Value = UART1_RFIFO[R_Tail1];              /* Get a Byte from UART0 Rx FIFO           */
    if (++R_Tail1 == MaxLength)                 /* Is R_Tail0 point to UART0 Rx FIFO tail  */
      { R_Tail1 = 0; }                          /* Set R_Tail0 point to UART0 Rx FIFO head */
    RLength1--;                                 /*                                         */
    SET_REGISTER(EA);                           /* Enable Interrupt                        */
    return TRUE;
}

void InitUART0(void)
{
/* =================================================== UART 0 Use Time 2 Baud General 9600bps @ 22.118MHZ RCAP2H,RCAP2L = 65535 - (Osc) / (32 * Baud Rate) =================================================== CKCON Register (clock control) MSB Bit Function . 7 WD1 . 6 WD0 . 5 T2M . 4 T1M . 3 T0M . 2 MD2 . 1 MD1 LSB 0 MD0 Set Time Clk ;Osc/4 or Osc/12 SCON Register (serial port control) MSB Bit Function . 7 SM0 MODE SM0 SM1 . 6 SM1 0 0 0 . 5 SM2 1 0 1 . 4 REN 2 1 0 . 3 TB8 3 1 1 . 2 RB8 . 1 TI LSB 0 RI */
   R_Head0 = 0;
   R_Tail0 = 0;
   T_Head0 = 0;
   T_Tail0 = 0;
   RLength0 = 0;
   TLength0 = 0;

   CKCON  = 0x82;
   SCON   = 0x50;
   RCAP2H = (65535 - (OSC / (32 * UART0_BaudRate))/256);
   RCAP2L = (65535 - (OSC / (32 * UART0_BaudRate))%256)+1;
   TH2 = RCAP2H;
   TL2 = RCAP2L;
   T2CON  = 0x34;
   uart0 = 0;
   #if defined( UART0_RS485_Adapter )              /* Is UART0 use RS485 Adapter */       DIR0 = RECEIVE;                              /* change R/T Direction */
   #endif    SET_REGISTER(PS);
   ES0    = ENABLE; /* Enable UART 0 Interrupt */
}
void InitUART1(void)
{
// UART 1 Use Time 1 Baud General 1200bps @ 22.118MHz // SoftTime Use Time 0, Mode 1 // SMOD = 0, then K = 1 else K = 2 // W77E5X or DS80C3X // T1M = 1, then C = 3, else C = 1 // TH1 = 256 - (osc * K(1 or 2) * C(1 or 3) /32*12*Baud Rate)
   R_Head1 = 0;
   R_Tail1 = 0;
   T_Head1 = 0;
   T_Tail1 = 0;
   RLength1 = 0;
   TLength1 = 0;

   SCON1 = 0x50;
   TMOD  = 0x21;
   TH1 = (256 - ((OSC)/(32*12*UART1_BaudRate)));
   TR1 = ENABLE;
   uart1 = 0;
   SET_REGISTER(PS1);
   ES1 = ENABLE; /* Enable UART1 Interrupt */
/*   TMOD |= 0x21;
   TH0 = 0xFD;       */
      DIR1 = RECEIVE;                              /* change R/T Direction              */
}

void UART0Drive(void)
{
  if (!IsReceiveExtendUnit())
    {
      if ((TLength0 > 0) && (uart0 == OFF))
        {
          uart0 = ON;
          DIR0 = TRANSMIT;
          SET_REGISTER(TI);
        }
    }
}

void UART1Drive(void)
{
  if (!IsReceiveCallStation())
    {
      if ((TLength1 > 0) && (uart1 == OFF))
        {
          uart1 = ON;
          DIR1 = TRANSMIT;
          SET_REGISTER(TI1);
        }
    }
}