Appendix A

Below you will find implementations of the Amulet UART protocol written in C, BASIC, and assembly. Please note that these code snippets are presented here to illustrate the workings of the protocol, not serve as a model implementation.

C source code

BASIC source code

Assembly source code

/********************************************************************
*MAIN ROUTINE - initializes RingBuffer and sets the baud to 9600, then
*stays in an infinite loop polling the serial line to see if anything
*has been received, and then handling the byte received.
*NOTE: the serIn() function simply checks the serial line to see if
*anything is there, if not it returns
***********************************************************************/

int main()
{
     rbInit(buffer);
     setbaud(BAUD9600);

     while(1)
     {
          serIn(&buffer);
     }

     return(0);
}

/**************************************************************************
*Checks to see if anything is waiting on the serial line to be handled,
*if so, it puts it at the end of the Ringbuffer, otherwise it does nothing
*and returns
**************************************************************************/
void serIn(RingBuf *buf)
{
     if ((SCSR & RDRF) != 0)
     {
          tail = buf->tail;
          buf->serData[tail++] = SCDR;
          buf->tail = (tail & RB_SIZE_MASK);
          parseSerial();
     }
}

/*************************************************************************
*This function acts as the byte handler to the bytes that serIn() puts
*in the Ringbuffer. Checks to see if valid request type has been
*received and then sets server response value. Using a standard state
*machine, the program proceeds to set variables to hold the values of
*the next byte received on the serial lines and when the correct number
*of bytes have been received (3 bytes for all request types except
*setByte which needs 5 bytes) later calls functions that put the
*variables back out on the serial line for output
*************************************************************************/
void parseSerial(void)
{
     static char caseType;
     newByte = byteFromBuf(&buffer);

     if((newByte >= 0xD0) && (newByte <= 0xD8))
     {
          serverResp = respMake(newByte);
          caseType = serverResp;
     }
     else if(state == 0)
          caseType = 0x00;

     if((state==0) && ((caseType >= 0xD0) && (caseType <= 0xD5)))
     {
          state++;
     }
     else if(state == 1)
     {
          hiNib = newByte;
          state++;
     }
     else if(state == 2)
     {
          loNib = newByte;

          if(caseType == 0xD5)
          {
               state++;
          }
          else
          {
               state = 0;
          }

          switch(caseType)
          {
               case 0xD0:
                    getByte();
                    break;
               case 0xD1:
                    getString();
                    break;
               case 0xD2:
                    getWord();
                    break;
               case 0xD8:
                    invokeRPC();
                    break;
          }
     }
     else if((state == 3) && (caseType == 0xD5))
     {
          setValHi = newByte;
          state++;
      }
     else if(state == 4)
     {
          setValLo = newByte;
          state = 0;
          setByte();
     }
}


/*********************************
*Hex to ascii conversion routine
**********************************/
char hex2ascii(char hex)
{
     return ((hex < 0x0A) ? (hex + '0') : (hex + ('A' - 0x0A)));
}

/*********************************
*Ascii to hex conversion routine
**********************************/
char ascii2hex(char ascii)
{
     return((ascii <= '9') ? (ascii - '0') : (ascii - ('A' - 0x0A)));
}

/*************************************************************************
*Handler for a getByte function request
* Format of request string is three bytes => 0xD0 vH vL, where v = variable being requested,
* vH is ASCII version of high nibble of v and vL is ASCII of low nibble of v.
* Returns five bytes => 0xE0 vH vL NH NL,
* where N = value of variable v (low byte),
* NH is ASCII version of high nibble of N and NL is ASCII of low nibble of N.
********************************************************************************/
void getByte(void)
{
     char index, byteValue, valueHiNib, valueLoNib;

     index = ascii2hex(hiNib) << 4;
     index |= ascii2hex(loNib);

     byteValue = byteData[index];

     valueHiNib = hex2ascii(byteValue >> 4);
     valueLoNib = hex2ascii(byteValue & 0x0f);

     putchar(serverResp);
     putchar(hiNib);
     putchar(loNib);
     putchar(valueHiNib);
     putchar(valueLoNib);
}

/******************************************************************************
*Handler for a getString function request
* Format of request string is three bytes => 0xD2 vH vL, where v = index of string variable,
* vH is ASCII version of high nibble of v and vL is ASCII of low nibble of v.
* Returns variable number of byte: 0xE2 vH vL String+Null to client
*
* This routine is only looking to respond with one of two strings. Therefore, it
* is only looking at the least significant nibble of string variable index.
*
* Uses putchar from ICC C library to put individual characters onto serial line
*******************************************************************************/
void getString(void)
{
     putchar(serverResp);
     putchar(hiNib);
     putchar(loNib);

     if(loNib == 0x30)
     {
          putstring(string1);
      }
     if(loNib == 0x31)
     {
          putstring(string2);
     }
}

/**********************************************************************
*Handler for a setByte function request
* Format of request string is five bytes => 0xD5 vH vL NH NL, where v = index of byte variable,
* vH is ASCII version of high nibble of v and vL is ASCII of low nibble of v,
* N = value of variable v,
* NH is ASCII version of high nibble of N and NL is ASCII of low nibble of N.
* Returns five bytes => 0xE5 vH vL NH NL,
* where P vH vL NH NL are all duplicates of the bytes that were in the request string.
***********************************************************************/
void setByte(void)
{
     char index, hexVal;

     index = ascii2hex(hiNib) << 4;
     index |= ascii2hex(loNib);

     hexVal = ascii2hex(setValHi) << 4;
     hexVal |= ascii2hex(setValLo);

     byteData[index] = hexVal;

     putchar(serverResp);
     putchar(hiNib);
     putchar(loNib);
     putchar(setValHi);
     putchar(setValLo);
}

/*******************************************************************************
*Handler for a getWord function request
* Format of request string is three bytes => 0xD1 vH vL, where v = index of word variable,
* vH is ASCII version of high nibble of v and vL is ASCII of low nibble of v.
* Returns seven bytes => 0xE1 vH vL PH PL NH NL,
* where P = value of variable v (high byte), N = value of variable v (low byte),
* PH is ASCII version of high nibble of P and PL is ASCII of low nibble of P,
* NH is ASCII version of high nibble of N and NL is ASCII of low nibble of N.
********************************************************************************/
void getWord(void)
{
     char index, valMSBhinib, valMSBlonib, valLSBhinib, valLSBlonib;
     unsigned int wordValue;

     index = ascii2hex(hiNib) << 4;
     index |= ascii2hex(loNib);

     wordValue = wordData[index];

     valMSBhinib = hex2ascii((char)((wordValue >> 12) & 0x0f));
     valMSBlonib = hex2ascii((char)((wordValue >> 8) & 0x0f));
     valLSBhinib = hex2ascii((char)((wordValue >> 4) & 0x0f));
     valLSBlonib = hex2ascii((char)(wordValue & 0x0f));

     putchar(serverResp);
     putchar(hiNib);
     putchar(loNib);
     putchar(valMSBhinib);
     putchar(valMSBlonib);
     putchar(valLSBhinib);
     putchar(valLSBlonib);
}

/*******************************************************************************
*Handler for an invokeRPC function
* Format of request string is three bytes => 0xD8 rH rL, where r = index of RPC,
* rH is ASCII version of high nibble of r and rL is ASCII of low nibble of r.
* Returns three bytes => 0xD8 rH rL,
* where rH rL are duplicates of the bytes that were in the request string.
********************************************************************************/
void invokeRPC(void)
{
     char rpc, valMSBhinib, valMSBlonib, valLSBhinib, valLSBlonib;
     unsigned int wordValue;

     rpc = ascii2hex(hiNib) << 4;
     rpc |= ascii2hex(loNib);

     performRPC[rpc];    // this code could do any number of things based upon
                         // which Remote Procedure Call is requested.

     putchar(serverResp);
     putchar(hiNib);
     putchar(loNib);
}

/*********************************************
*Function to take a byte out of the buffer
**********************************************/
char byteFromBuf(RingBuf *buf)
{
     char byte;
     head = buf->head;
     byte = buf->serData[head];
     buf->head = (head + 1) & RB_SIZE_MASK;

     return byte;
}

/*********************************************************************************
*Function to assign a serverResp value based on the byte taken out of the buffer
**********************************************************************************/
char respMake(char byte)
{
     // Response is always 0x10 greater than the command opcode
     return (byte + 0x10);
}

/**********************************************
*Function to put a string onto the serial line
**********************************************/
void putstring(char *str)
{
     char iloop;
     char index = 0;
     char value;

     for(iloop=0; iloop <= strlen(str); iloop++)
     {
          value = str[index];
          putchar(value);
          index++;
     }
}

------------------------------------------------------------------------------------------------------------------------------------------

 

The following BASIC source code was taken from actual server implementation based on a Basic Stamp interfaced to an Analog Devices ADXL202EB accelerometer.

'*********************************************************************************************************************
'* The main loop of this code waits for valid Client Start of Message (CSOM) characters then jumps to the appropriate
'* service routine
'*********************************************************************************************************************
incoming VAR byte(6)  ' Incoming buffer

'********************************************************************************************************************
' serin Rpin, Baudmode, [STR incoming\L\E]
' serin = receive asynchronous serial data
' Rpin = Rx (instruct BASIC Stamp to use dedicated serial-input pin)
' Baudmode = N9600 (9600 baud, 8-bit data, no-parity, true polarity)
' [STR amuletMsg\L\E] = receive string of length L or until end character E is received into amuletMsg array
'
' The command serin Rx, N9600, [STR incoming\L\E]
' will poll the serial line looking for serial data up to length L or until end character E is encountered.
' Incoming data will be stored in the amuletMsg array.
'********************************************************************************************************************
serial_in:

SERIN 16, 84, [RxType, VarType1, VarType2, SetVar1, SetVar2]
SERIN 16, 84, [STR incoming\6\0]      '* Read a max of 6 bytes or stop when received a null termination character
RxType = incoming(0)
VarType1 = incoming(1)
VarType2 = incoming(2)

IF RxType = $D5 THEN setByte     'asking for a setByte
IF RxType = $D0 THEN getByte     'asking for a getByte
IF RxType = $D2 THEN getString   'asking for a getString
IF RxType = $D1 THEN getWord     'asking for a getWord
IF RxType > $D5 THEN serial_in   'value on line is not a function, go back to wait for
                                 'another command

'******************************************************************************
'* Handler for a getByte function request
'* Format of request string = 0xD0xx, where xx = variable being requested
'* Returns 0xE0xxNN, where NN equals the HEX data of the variable xx
'******************************************************************************
getByte:
     ServerResp1 = $E0

     IF VarType2 = "5" THEN RateReturn
     IF VarType2 = "6" THEN TimeReturn
     IF VarType2 = "7" THEN VariableReturn

     'Variable 5 is being requested so return value stored for rate (in register B20)
     RateReturn:
          SEROUT 16,84,[ServerResp1, VarType1, VarType2, HEX2 B20]
          GOTO serial_in

     'Variable 6 is being requested so return value stored for time (in register B22)
     TimeReturn:
          SEROUT 16,84,[ServerResp1, VarType1, VarType2, HEX2 B22]
          GOTO serial_in

     'Variable 7 is being requested so return value stored for variable (in register B21)
     VariableReturn:
          SEROUT 16,84,[ServerResp1, VarType1, VarType2, HEX2 B21]
          GOTO serial_in

'****************************************************************************
* Handler for a getString function request
'* Format of request string = 0xD2xx, where xx = index of string variable
'* Returns 0xE2xxString+Null to client
'* Returns requested data back to the screen
'****************************************************************************
getString:
     ServerResp1 = $E2

     IF VarType2 = "2" THEN Author_string
     IF VarType2 = "3" THEN Company_string

     Author_string:
          SEROUT 16,84,[ServerResp1, VarType1, VarType2, "Jacob Horn", NULL]
          GOTO serial_in

     Company_string:
          SEROUT 16,84,[ServerResp1, VarType1, VarType2, "Amulet Technologies",
               NULL]
          GOTO serial_in


'***********************************************************************
'* Handler for a setByte function request
'* Format of request string = 0xD5xxNN, where xx = variable to be set
'* and NN = HEX data
'* Returns 0xE5xxNN to client
'***********************************************************************
setByte:
     ServerResp1 = $E5

     IF VarType2 = "5" THEN Rate
     IF VarType2 = "6" THEN Time

     'Variable 5 has been called to be set
     Rate:
          'Choose which parameter associated with rate is to be set, using NN values
          IF incoming(4) = "0" THEN one
          IF incoming(4) = "1" THEN two
          IF incoming(4) = "2" THEN five
          IF incoming(4) = "3" THEN ten

          one:
               B20 = 1
               GOTO setByteResp
          two:
               B20 = 2
               GOTO setByteResp
          five:
               B20 = 5
               GOTO setByteResp
          ten:
               B20 = 10
               GOTO setByteResp

     'Variable 6 has been called to be set
     Time:
          'Once again, choose which parameter is to be set using NN values
          IF incoming(4) = "0" THEN tenSeconds
          IF incoming(4) = "1" THEN thirtySeconds
          IF incoming(4) = "2" THEN fortyfiveSeconds
          IF incoming(4) = "3" THEN sixtySeconds

          tenSeconds:
               B22 = 10
               GOTO setByteResp
          thirtySeconds:
               B22 = 30
               GOTO setByteResp
          fortyfiveSeconds:
               B22 = 45
               GOTO setByteResp
          sixtySeconds:
               B22 = 60
               GOTO setByteResp

     setByteResp:
          SEROUT 16,84,[ServerResp1, VarType1, VarType2, incoming(3), incoming(4)]
          GOTO serial_in

 

'*********************************************************************************************
'* Handler for a getWord function request
'* Format of request string = 0xD1xx, where xx = variable being requested
'* Returns 0xE1xxPPNN, where PP = high byte of variable xx, and NN = low byte of variable xx
'*********************************************************************************************
getWord:
     ServerResp1 = $E1

     IF VarType2 = "1" THEN YWORDvariable

     'Pulse X port pin, read, and save out x acceleration values
     PULSIN 4,1,xWord

     ServerResp2 = xWord.HIGHBYTE
     ServerResp3 = xWord.LOWBYTE

     'server data sent out over RS232 to client
     SEROUT 16,84,[ServerResp1, VarType1, VarType2, HEX2 ServerResp2,
          HEX2 ServerResp3]
     GOTO serial_in

     YWORDvariable:
          PULSIN 2,1,yWord


          ServerResp2 = yWord.HIGHBYTE
          ServerResp3 = yWord.LOWBYTE

          SEROUT 16,84,[ServerResp1, VarType1, VarType2, HEX2 ServerResp2,
               HEX2 ServerResp3]

          GOTO serial_in

END

 

-------------------------------------------------------------------------------------------------------------------------------------------

The following assembly code snippets were taken from an actual server implementation based on Atmel's AVR 8-bit RISC microcontroller (Part# AT90LS4433).

 
;********************************************************************
;Main loop of program. Waits for valid Client Start Of Message (CSOM)
;characters, then vectors to the appropriate service routine.
;******************************************************************** 
 
.equ GetCommand     = 0xD0
.equ GetResponse    = 0xE0
.equ StringCommand  = 0xD2
.equ StringResponse = 0xE2
.equ SetCommand     = 0xD5
.equ SetResponse    = 0xE5
.equ InvokeCommand  = 0xD8
.equ InvokeResponse = 0xE8
try_again:
    rcall   getch               ;Go and wait until a character is present in the buffer
    cpi     buffer,GetCommand   ;Is it a get command?
    brne    is_it_S             ;Not get command, so check others
    rjmp    handleG
is_it_String:
    cpi     buffer,StringCommand;Is it a string command?
    brne    is_it_S             ;Not string command, so check for set
    rjmp    handleString 
is_it_S:
    cpi     buffer,SetCommand   ;Is it a set command?
    brne    is_it_I             ;Not set command, so check for I
    rjmp    handleS
is_it_I:
    cpi buffer,InvokeCommand    ;Is it an invoke command?
    brne    try_again           ;Not a valid request so start over and wait for next character
    rjmp    handleI
    
;********************************************************************
;Handle Get Byte command to get variable data 
; Format of request string = 0xD0xx
; where xx = variable requested
; Returns 0xE0xxNN to client where NN = HEX data of variable (xx)
;********************************************************************
handleG:
    rcall   getByte             ;Read both nibbles of xx and assemble into a byte. Return in buffer. 
    brcc    try_again           ;If carry cleared, then invalid value, so start over
    mov     which,buffer
    ldi     buffer,GetResponse
    rcall   putch               ;Acknowledge valid command
    mov     buffer,which
    swap    buffer              ;Rotate MSNibble into LSNibble position
    rcall   nib2ascii           ;Convert LSNibble of buffer to an ASCII character
    rcall   putch               ;Echo back MSNibble of variable (xx) 
    mov     buffer,which
    rcall   nib2ascii           ;Convert LSNibble of buffer to an ASCII character
    rcall   putch               ;Echo back LSNibble of variable (xx)
    rcall   get_varH            ;Go get data (msn) for variable (xx)
    rcall   putch               ;Transfer data to the client
    rcall   get_varL            ;Go get data (lsn) for variable (xx)
    rcall   putch               ;Transfer data to the client
    rjmp    try_again           ;Done with Get command so start over
 
;Handle String command to tx a string back.
; Format of request string = 0xD2xx
; where xx = index of string variable 
; Returns 0xE2xxString+Null to host.
;******************************************************
handleString:
    rcall   getByte             ;Read both nibbles of xx and assemble into a byte. Return in buffer. 
    brcc    try_again           ;If carry cleared, then invalid value and start over
    mov     which,buffer
    ldi     buffer,StringResponse
    rcall   putch               ;Acknowledge valid command
    mov     buffer,which
    swap    buffer              ;Rotate MSNibble into LSNibble position
    rcall   nib2ascii           ;Convert LSNibble of buffer to an ASCII character
    rcall   putch               ;Echo back MSNibble of variable (xx) 
    mov     buffer,which
    rcall   nib2ascii           ;Convert LSNibble of buffer to an ASCII character
    rcall   putch               ;Echo back LSNibble of variable (xx)
 
    ...                         ;To simplify this snippet, all the code which looks up the string(xx) 
    ...                         ;in a look-up table was left out
 
    icall                       ;Time to go pound the null terminated string out
    rjmp    try_again           ;Done with string command so start over
 
;****************************************
;Handle S command to Set variable data
; Format of request string = 0xD5xxNN
; where xx = variable to set
; NN = HEX data for variable (xx)
; Returns 0xE5xxNN to client
;****************************************
handleS:
    rcall   getByte             ;Read both nibbles of xx and assemble into a byte. Return in buffer. 
    brcc    try_again           ;If carry cleared, then invalid value, so start over
    mov     which,buffer
    rcall   getByte             ;Read both nibbles of NN and assemble into a byte. Return in buffer. 
    brcc    try_again           ;If carry cleared, then invalid value, so start over
    mov     what,buffer
 
    ;This is where we would use the value of xx, now stored in 'which', to determine
    ;where to store the value of NN, now stored in 'what'. For the sake of brevity,
    ;this example only handles xx=00, while all other xx are ignored.
    
    cpi     which,0             ;If offset is zero, then VAR0 is the variable to set
    brne    try_again
    sts     VAR0,what           ;Set VAR0 
    ldi     buffer,SetResponse
    rcall   putch               ;Acknowledge valid command
    mov     buffer,which
    swap    buffer              ;Rotate MSNibble into LSNibble position
    rcall   nib2ascii           ;Convert LSNibble of buffer to an ASCII character
    rcall   putch               ;Echo back MSNibble of variable (xx) 
    mov     buffer,which
    rcall   nib2ascii           ;Convert LSNibble of buffer to an ASCII character
    rcall   putch               ;Echo back LSNibble of variable (xx)
    mov     buffer,what
    swap    buffer              ;Rotate MSNibble into LSNibble position
    rcall   nib2ascii           ;Convert LSNibble of buffer to an ASCII character
    rcall   putch               ;Echo back MSNibble of variable (NN) 
    mov     buffer,what
    rcall   nib2ascii           ;Convert LSNibble of buffer to an ASCII character
    rcall   putch               ;Echo back LSNibble of variable (NN)
    rjmp    try_again           ;Done with Set command so start over
    
;**************************************
;Handle I command to Invoke a function
; Format of request string = 0xD8xx
; where xx = function to invoke
; Returns 0xE8xx to client
;**************************************
handleI: 
    rcall   getByte             ;Read both nibbles of xx and assemble into a byte. Return in buffer. 
    brcc    try_again           ;If carry cleared, then invalid value, so start over
    mov     which,buffer
 
    ...                         ;To simplify this snippet, all the code which looks up the function(xx) 
    ...                         ;in a look-up table was left out
 
    icall                       ;Time to go execute the function (xx)
    ldi     buffer,InvokeResponse
    rcall   putch               ;Acknowledge valid command
    mov     buffer,which        
    swap    buffer              ;Rotate MSNibble into LSNibble position
    rcall   nib2ascii           ;Convert LSNibble of buffer to an ASCII character
    rcall   putch               ;Echo back MSNibble of variable (xx) 
    mov     buffer,which        
    rcall   nib2ascii           ;Convert LSNibble of buffer to an ASCII character
    rcall   putch               ;Echo back LSNibble of variable (xx)
    rjmp    try_again           ;Done with Invoke command so start over 
 
 


Amulet HTMLCompiler,
Copyright © 2000-2004 by
Amulet Technologies, LLC

Back to Welcome - Contact Amulet - Amulet Home