这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » 活动中心 » 合作大赛 » 一个外国人写的PortSwitch程序串口缓存处理的比较不错

共1条 1/1 1 跳转至

一个外国人写的PortSwitch程序串口缓存处理的比较不错

高工
2015-07-07 10:03:55     打赏
// Simple port switching controller
// A very light weight comms suite and command interpreter is implemented.
// Literal strings are used in-line - usually not an efficient practice.
//

#define NULL 0
#define FALSE 0
#define TRUE  1

// Buffer lengths are short to prevent the need for external RAM
// Only idata is used.
#define TX_BUFFER_LENGTH 16
#define RX_BUFFER_LENGTH 16

#define RX_CMD_TERMINATOR 0x0D  // <CR>
#define RX_CMD_BACKSPACE  0x08  // <CR>

#define NUMBER_OF_PORTS 4

#define CLK_FREQ     3U*11059200UL // about 33.178 MHz
#define BAUD_RATE    57600UL


// Save ram space by keeping all strings in ROM if
// possible
 #pragma romstring 

// Globals - resource limited processor and simple
// demonstration program. Globals are being used.

// version string
__rom char sVersion[] = "PrtSw Ver 1.3 ";
__rom char sDate[] = __DATE__" ";  // Date with appended space
__rom char sTime[] = __TIME__;

// RS-232 receive buffers
// These buffers are quite restricted in length and so
// overflow will have to be managed (it has to be managed
// regardless of length, but in this instance we will
// no doubt have plenty of instances of overflow).
// In the first instance overflow shall be managed by
// discarding characters up until the next command
// terminator (v. simple).
// Two buffers are provided to allow for ping-ponging
// between them as one is being dealt with at the the
// higher level and one is receiving characters from
// the receive ISR.
char rxBuffer1[RX_BUFFER_LENGTH];
char rxBuffer2[RX_BUFFER_LENGTH];
unsigned char rxBuffer1Length = 0;
unsigned char rxBuffer2Length = 0;

__bit rxBuffer1Ready = FALSE;
__bit rxBuffer2Ready = FALSE;

// RS-232 transmit buffers
// Very limited in length to conserve
// RAM space.  Note that ROM-based
// messages can be longer.
char txBuffer[TX_BUFFER_LENGTH];
unsigned char txBufferLength = 0;


// ISR variables
const char *isrTXBuf;
__rom char *isrTXBufROM;    
                      // Code space is more available than RAM
                      // so I use a separate pointer to dump
                      // ROM-based messages. Rather than copying
                      // the ROM messages into a RAM buffer.
unsigned char isrTXLength;
unsigned char isrTXIndex;
__bit isrTXFromRom;   // TRUE if transmitting a ROM-based message
char *isrRXBuf;
unsigned char isrRXLength;
__bit isrUsingRXBuffer1;


// ROM-based messaged - see also Version variables at the top
__rom const char txBufferPrompt[] = "cmd>"; 
#define PROMPT_LENGTH sizeof(txBufferPrompt)/sizeof(char)-1
__rom const char txBufferCR = 0x0d;

// Shadow registers
// Some of the I/O ports require shadow registers for correct operation
// as not all the pins are the same direction and the port out and
// in busses are not hardwired together.
// This demonstrates a possible difference between an embedded 8051
// and a traditional chip.
unsigned char P1_ShadowReg;


// Forward declarations of function prototypes
void ClearBuffer(char *cmdBuffer);
_Bool DoCommand(const char *cmdBuffer); // Simple command interpreter
void PrintHelp(void);                   // Print simple help
void PrintPrompt(void);                 // Print a small prompt
void PrintVersion(void);
_Bool Transmit(const char *msg, const unsigned char length);
_Bool TransmitRomMsg(__rom char * msg, unsigned char length);
_Bool TransmitCR(void);
_Bool TransmitPrompt(void);

void Init(void);
void SetupSerialPort(void);

_Bool Connect(unsigned char destPort, unsigned char srcPort);
_Bool SetPortState(unsigned char port, _Bool enable);
_Bool GetPortState(unsigned char port);
_Bool QueryPort(unsigned char port);
_Bool DumpPort(unsigned char port);
signed char QueryConnection(unsigned char port);
unsigned char ConvertPortToIndex(unsigned char port);


void delay(void)
{
    int i;
    for(i=0; i<0x1fff; i++)
    {
    }
}


void main(void)
{
    Init();
    SetupSerialPort();
    
    TransmitCR();
    PrintVersion();
    TransmitCR();
    TransmitPrompt();
    ClearBuffer(rxBuffer1);
    ClearBuffer(rxBuffer2);

    while(1)
    {
       
       // Simple Task Manager
       // Loop forever and check status of
       // signals from low level software.
       // Trigger high level tasks as required.

       if(rxBuffer1Ready)
       {
          DoCommand(rxBuffer1);
          ClearBuffer(rxBuffer1);
          rxBuffer1Ready = FALSE;  // allow ISR to use this buffer if it wishes
       }
       if(rxBuffer2Ready)
       {
          DoCommand(rxBuffer2);
          ClearBuffer(rxBuffer2);
          rxBuffer2Ready = FALSE;  // allow ISR to use this buffer if it wishes
       }
    }
}

void ClearBuffer(char *cmdBuffer)
{
    for(int i=0; i < RX_BUFFER_LENGTH; i++)
    {
        cmdBuffer[i] = 0x00;
    }
}

// DoCommand is the (simple) command interpreter.
// Command decoding and execution.
// Assumes fixed format commands - each command handler
// does its own parameter checking (though there is very
// limited error handling).
_Bool DoCommand(const char *cmdBuffer)
{
    _Bool bRetVal = FALSE;
    unsigned char i;
    unsigned char ch1, ch2;

    if(cmdBuffer != NULL)
    {
       TransmitCR();
       switch(cmdBuffer[0])
       {
          case '?':
          case 'h':
          case 'H':
             // Dump help
             PrintHelp();
             bRetVal = TRUE;
             break;

          case 'v':
          case 'V':
             // Dump version
             PrintVersion();
             bRetVal = TRUE;
             break;

          case 'e':
          case 'E':
             // port enable/disable command
             // we expect an integer (port)
             // followed by another integer (1 or 0)
             // Subtract '1' from port to get zero based port.
             // Subtract '0' from integer to get 0 for '0' and 1 for '1'.
             // No error checking for now
             ch1 = ConvertPortToIndex(cmdBuffer[2]);
             if( ch1 == 0xff)
             {
                // Parameter error
                TransmitRomMsg("Invalid port", 12);
             }
             else
             {
                 if( SetPortState( ch1, (cmdBuffer[4])-'0'))
                 {
                    TransmitRomMsg("OK", 2);
                 }
                 else
                 {
                    TransmitRomMsg("Fail", 4);
                 }
             }
             bRetVal = TRUE;
             break;


          case 'm':
          case 'M':
             // port mapping command
             // we expect an integer
             // followed by another integer
             // No error checking for now
             // See if we have alpha or numeric port references
             // Also deal with upper and lower case
             ch1 = ConvertPortToIndex(cmdBuffer[2]);
             ch2 = ConvertPortToIndex(cmdBuffer[4]);
             if( ch1 == 0xff)
             {
                // Parameter error
                TransmitRomMsg("Invalid destination port", 24);
             }
             if( ch2 == 0xff)
             {
                 if( ch1 == 0xff)
                 {
                    // add in <CR> if we have already printed an error message
                    TransmitCR();
                 }
                // Parameter error
                TransmitRomMsg("Invalid source port", 19);
             }

             if((ch1 != 0xff) && (ch2 != 0xff))
             {
                 if( Connect(ch1, ch2))
                 {
                    TransmitRomMsg("OK", 2);
                 }
                 else
                 {
                    TransmitRomMsg("Fail", 4);
                 }
             }
             bRetVal = TRUE;
             break;

          case 'q':
          case 'Q':
             // Query port mapping and enable state
             // We expect an integer giving the port to query
             ch1 = ConvertPortToIndex(cmdBuffer[2]);
             if( ch1 == 0xff)
             {
                // Parameter error
                TransmitRomMsg("Invalid port", 12);
             }
             else if( !DumpPort(ch1))
             {
                TransmitRomMsg("Fail", 4);
             }
             bRetVal = TRUE;
             break;

          case 'd':
          case 'D':
             // Query port mapping and enable state for all ports
             for(i=0; i<NUMBER_OF_PORTS; i++)
             {
                if( !DumpPort(i))
                {
                   TransmitRomMsg("Fail", 4);
                }
                TransmitCR();
             }
             bRetVal = TRUE;
             break;

          default:
             TransmitRomMsg("Huh?", 4);
             break;
       }
       TransmitCR();
       TransmitPrompt();
    }
    return bRetVal;
}

unsigned char ConvertPortToIndex(unsigned char port)
{
    unsigned char index = port;
    if( (port >= '1') && (port <= '4'))
    {
        index -= '1';
    }
    else if( (port >= 'a') && (port <= 'd'))
    {
        index -= 'a';
    }
    else if( (port >= 'A') && (port <= 'D'))
    {
        index -= 'A';
    }
    else
    {
        index = 0xff;  // signal error
    }

    return index;
}

void PrintVersion(void)
{
    // Dump the version number and date and time.
    // will have to wait around for the dump to occur
    // as our buffers are too small to take the
    // full dump in one go.
    //
    // This code is not all that robust.  If there is a
    // problem transmitting one part of the
    // message the rest will still be attempted - this
    // is probably not what is required.
    // Also, the wait for the TX system is infinite.
    // This should be a timed wait with error
    // recovery.
    TransmitRomMsg(sVersion, sizeof(sVersion)/sizeof(char)-1); 
    TransmitRomMsg(sDate,    sizeof(sDate)/sizeof(char)-1);
    TransmitRomMsg(sTime,    sizeof(sTime)/sizeof(char)-1); 
}


void PrintHelp(void)
{
    // Dump the help
    //
    // This code is not all that robust.  If there is a
    // problem transmitting one part of the
    // message the rest will still be attempted - this
    // is probably not what is required.
    // Also, the wait for the TX system is infinite.
    // This should be a timed wait with error
    // recovery.
    TransmitRomMsg("v         - print version", 25); TransmitCR();
    TransmitRomMsg("? or h    - print help", 22); TransmitCR();
    TransmitRomMsg("e prt 1|0 - enable/disable prt (1=enable)", 41); TransmitCR();
    TransmitRomMsg("m dst src - drive dst outputs from src inputs", 45); TransmitCR();
    TransmitRomMsg("q prt     - query prt connection and state" , 42); TransmitCR();
    TransmitRomMsg("d         - dump all port connections and states", 48); TransmitCR();
}


_Bool TransmitCR(void)
{
    return TransmitRomMsg(&txBufferCR, 1);
}

_Bool TransmitPrompt(void)
{
    return TransmitRomMsg(txBufferPrompt, PROMPT_LENGTH);
}

_Bool Transmit(const char * msg, unsigned char length)
{
    // This is a blocking function - it will
    // wait indefinitely until the serial port
    // is free.  Not ideal but simple for now.
    
    _Bool bRetVal = TRUE;

    // The following access of the isrTXLength variable
    // is atomic (byte variable) so we do not need to
    // disable interrupts.
    while(isrTXLength != 0) 
    {
       // Wait until the port is free.
       // We could put a timeout in here and return FALSE if
       // the TX fails.       
    }
    isrTXBuf = msg; // not sure this will work on a ROM string?
    isrTXLength = length;
    isrTXIndex = 1;  // we will send the 0'th character below
    isrTXFromRom = FALSE;

    SBUF = msg[0];  // start the transmission
       
    
    return bRetVal;
}

_Bool TransmitRomMsg(__rom char * msg, unsigned char length)
{
    // This is a blocking function - it will
    // wait indefinitely until the serial port
    // is free.  Not ideal but simple for now.
    
    _Bool bRetVal = TRUE;
    
    // The following access of the isrTXLength variable
    // is atomic (byte variable) so we do not need to
    // disable interrupts.
    while(isrTXLength != 0) 
    {
       // Wait until the port is free.
       // We could put a timeout in here and return FALSE if
       // the TX fails.
    }
    isrTXBufROM = msg; // not sure this will work on a ROM string?
    isrTXLength = length;
    isrTXIndex = 1;  // we will send the 0'th character below
    isrTXFromRom = TRUE;

    SBUF = msg[0];  // start the transmission
       
    return bRetVal;
}


void Init(void)
{
       // Initialise ports for loopback and disabled
       unsigned char i;
       for(i=0; i<NUMBER_OF_PORTS; i++)
       {
          SetPortState(i, FALSE);
          Connect(i, i);
       }
}

void SetupSerialPort(void)
{
    isrTXBuf = NULL;
    isrTXLength = 0;
    isrRXBuf = rxBuffer1;
    isrRXLength = 0;
    isrUsingRXBuffer1 = TRUE;

    SM0 = 0;    
    SM1 = 1;    // Mode 1 - 8-bit UART
    SM2 = 0;    // Disable multiprocessor feature
    TB8 = 0;    // 9th bit (not used)
    RB8 = 0;    // This is the rx stop bit.
    TI  = 0;    // Clr TX int
    RI  = 0;    // Clr RX int
    
    //Need to set up timer 1 for baud rate CLK_FREQ_MHZ
    
    TR1 = 0; // stop timer 1
    
    PCON_bit(7) = 1;  // SMOD = 1 -> high speed baud rate
  
    // baud rate divisor
    TL1 = 0;  // initialise to zero
    TH1 = 256U - (2 * CLK_FREQ/(12U*32U*BAUD_RATE));
    
    TMOD_bit(7) = 0;    // disable GATE
    TMOD_bit(6) = 0;    // operate as timer
    TMOD_bit(5) = 1;
    TMOD_bit(4) = 0;    // 8-bit timer auto-reload mode

    IT0 = 0;  
    IE0 = 0; 
    IT1 = 0;
    IE1 = 0;
    TR0 = 0; // timer 0 stopped
    TF0 = 0;
    TR1 = 1; // timer 1 running
    TF1 = 0;  
    
    ES = 1;
    IE_bit(7) = 1;

    REN = 1;  // enable reception
}

// Connect - the destination port outputs are driven from the
//           the inputs of the source port.
//           __putbit and __getbit could be used in this function.
//           Returns TRUE if both ports are within range, FALSE
//           otherwise (and no mapping change is done in this case).

_Bool Connect(unsigned char destPort, unsigned char srcPort)
{
    _Bool bRetVal;
    // Are ports within range?
    if(destPort >= NUMBER_OF_PORTS || srcPort >= NUMBER_OF_PORTS)
    {
       bRetVal = FALSE;
    }
    else
    {
       // first set save the current state of the dest port
       _Bool bSave = GetPortState(destPort);
       P0 &= ~(0x03 << (2*destPort));  // Clear the existing mapping for this port
                                   // This little hack only works when
                                   // NUMBER_OF_PORTS == 4!
                                   // Generally a more elaborate mechanism would
                                   // be required that cleared all the bits of
                                   // the port that control this destination port.
       P0 |= (srcPort << (2*destPort));
       SetPortState(destPort, bSave);  // Restore port state
       bRetVal = TRUE;
    }
    return bRetVal;
}

// SetPortState - returns FALSE (0) if port is out of range.
//                returns TRUE (1) if port is within range
//                This function will only work correctly when
//                NUMBER_OF_PORTS is 8 or less.
_Bool SetPortState(unsigned char port, _Bool enable)
{
    _Bool bRetVal;
    // Do some parameter checking
    if(port >= NUMBER_OF_PORTS)
    {
       bRetVal = FALSE;
    }
    else
    {
       if(enable)
       {
          P1_ShadowReg |= (1 << port);  // OR in the correct port
       }
       else
       {
          P1_ShadowReg &= ~(1 << port); // AND out the correct port
       
       }
       P1 = P1_ShadowReg;
       bRetVal = TRUE;
    }
    return bRetVal;
}

// GetPortState - return FALSE (0) if port enable bit is not set or
//                if port is out of range (TRUE otherwise).
_Bool GetPortState(unsigned char port)
{
    if(port >= NUMBER_OF_PORTS)
    {
       return FALSE;  // Warning - unstructured error return.
    }

    return (P1_ShadowReg & (1 << port));
}

_Bool DumpPort(unsigned char port)
{
    _Bool bRetVal = FALSE;
    signed char tmp;
    
    if(port < NUMBER_OF_PORTS)
    {
       tmp = port + 'A';
       Transmit((const char *)&tmp, 1);
       TransmitRomMsg(" <- ", 4);
       // Extract the mapping
       tmp = QueryConnection(port);
       if(tmp >= 0 && tmp < NUMBER_OF_PORTS)
       {
          tmp += 'A';
          Transmit((const char *)&tmp, 1);
       }
       else
       {
          // If we received an error display a question mark
          TransmitRomMsg("?", 1);
       }
       if(GetPortState(port))
       {
          TransmitRomMsg(" enabled", 8);
       }
       else
       {                                                 
          TransmitRomMsg(" disabled", 9);
       }
       bRetVal = TRUE;
    }
    return bRetVal;
}

signed char QueryConnection(unsigned char port)
{
    signed char retVal = -1;

    if(port < NUMBER_OF_PORTS)
    {
       // Extract the mapping
       retVal = P0;
       retVal >>= (2*port);
       retVal &= 0x03; // Only works for NUMBER_OF_PORTS = 4!
    }
    return retVal;
}



__interrupt(0x23) void SerialISR( void )
{
    char tempChar; // temporary variable to reduce the
                   // amount of array dereferencing.
    // Is there a TX interrupt?
    if(TI)
    {
       TI = 0;  // clear int source as soon as possible to
                // minimise chances of missing an interrupt
       // send the next character (if any) out to the port
       if(isrTXIndex < isrTXLength)
       {
          // Another char to send - determine what sort of pointer we should
          // use. When we are sending a ROM-based message use a
          // __rom char *.  This saves having to copy the message into RAM.
          // RAM is very limited in this application - only idata.
          if(isrTXFromRom)
          {
             // We are sending a ROM message so use the ROM pointer
             SBUF = isrTXBufROM[isrTXIndex++];
          }
          else
          {
              SBUF = isrTXBuf[isrTXIndex++];
          }
       }
       else
       {
          // signal that we have finished transmitting
          isrTXLength = 0;
       }
    }   // end of if(TI)  - transmit character handling
    
    // Is there an RX interrupt?
    if(RI)
    {
       RI = 0;  // clear int source as soon as possible to
                // minimise chances of missing an RX interrupt 

       tempChar = SBUF;
       // save this character away - if there is space
       if(isrRXLength < RX_BUFFER_LENGTH-1)
       {
          // OK save it
          isrRXBuf[isrRXLength++] = tempChar;
       }
       else
       {
          // Lets signal something
          // I know! We can put the count of received chars onto
          // Port 2. But we will do this outside the test so it
          // is a useful count at all times.
       }

       // Deal with back spaces - watch for underflow of the
       // buffer.
       if(tempChar == RX_CMD_BACKSPACE)
       {
          if(isrRXLength > 1)
          {
            isrRXLength -= 2; // take off two to account for the just-added
                              // backspace character
          }
          else
          {
            isrRXLength = 0; // only really useful for a signed variable
          }
          // Could print a space and then do the backspace again
          // but I don't want to have to deal with potential recursion in
          // calling the TX routines, or to possibly stuff up some outgoing
          // transmission by forceably inserting a character into the transmit
          // buffer.
       }

       // Debug ouput - Upper four bits of port 2 contain lower four bits of
       // tempChar and lower four bits of port 2 contains lower four bits of
       // isrRXLength
       P2 = ((tempChar << 4) & 0xf0)|(0x0f & isrRXLength);
       //P2 = isrRXLength; // signal the number of received characters onto port 2


       // If the character was a command terminator then we need to
       // pass it up to the high level.
       if(tempChar == RX_CMD_TERMINATOR)
       {
          // new command buffer
          if(isrUsingRXBuffer1)
          {
             rxBuffer1Length = isrRXLength;

             // set up for further reception in the other RX buffer
             isrRXBuf = rxBuffer2;
             isrUsingRXBuffer1 = FALSE;

             rxBuffer1Ready = TRUE;  // signal high level code 
          }
          else
          {
             rxBuffer2Length = isrRXLength;
             isrRXBuf = rxBuffer1;
             isrUsingRXBuffer1 = TRUE;
             rxBuffer2Ready = TRUE;
          }
          isrRXLength = 0;
       }   // end of if(tempChar == RX_CMD_TERMINATOR )
    }      // end of if(RI)  - receive character handling
}


共1条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]