Archive for the UART Category

Note: A Remote Programable Digital Input/Ouput module

Posted in 8051, MCU, UART on 2011 年 05 月 23 日 by Kun-Yi

這是一個用 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

Posted in 8051, MCU, UART on 2007 年 07 月 16 日 by Kun-Yi

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

Posted in 8051, MCU, UART on 2007 年 07 月 16 日 by Kun-Yi

there context is file "cm.c "

Hardware:

 MCS-51

4KROM SIZE

4KRAM SIZE

communication physical interface:

9600bps, N, 8, 1, RS485 tranceiver 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

Posted in 8051, MCU, UART on 2006 年 02 月 13 日 by Kun-Yi
這版本的是當初退伍後,工作幾個月後寫的,靈感來自於 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);
        }
    }
}