Appendix D

Below you will find an implementation of the Amulet SPI protocol written in C. The code found below is a working model of the protocol, meant for demonstration use only and does not run on reset.

#define _SPI
#include <hc11.h>
#include <stdio.h>
#include <string.h>

//mapping of LED and IRQ pin (used in setting and clearing of IRQ)
#define LED_ON() PORTA = 0x40
#define LED_OFF() PORTA = 0x00
#define PULSE_BIT bit(4)

//Client Start of Message (CSOM) values
#define GETBYTE_REQUEST 0x11
#define STRING_REQUEST 0x12
#define SETBYTE_REQUEST 0x13
#define INVOKE_REQUEST 0x14
#define GETWORD_REQUEST 0x15

//Server Start of Message (SSOM) values
#define GETBYTE_RESPONSE 0x21
#define STRING_RESPONSE 0x22
#define SETBYTE_RESPONSE 0x23
#define INVOKE_RESPONSE 0x24
#define GETWORD_RESPONSE 0x25

//States
#define SSOM 1
#define VARIABLE_HINIB 2
#define VARIABLE_LONIB 3

#define GETBYTE_VARIABLE_HINIB 11
#define GETBYTE_VARIABLE_LONIB 12
#define GETBYTE_VALUE_HINIB 13
#define GETBYTE_VALUE_LONIB 14
#define GETBYTE_LAST_BYTE 15

#define STRING_VARIABLE_HINIB 21
#define STRING_VARIABLE_LONIB 22
#define OUTPUT_STRING 23
#define STRING_LAST_BYTE 24

#define SETBYTE_VARIABLE_HINIB 31
#define SETBYTE_VARIABLE_LONIB 32
#define SETBYTE_VALUE_HINIB 33
#define SETBYTE_VALUE_LONIB 34
#define SETBYTE_LAST_BYTE 35

#define INVOKE_VARIABLE_HINIB 41
#define INVOKE_VARIABLE_LONIB 42
#define INVOKE_LED_PULSE 43

#define GETWORD_VARIABLE_HINIB 51
#define GETWORD_VARIABLE_LONIB 52
#define GETWORD_MSB_VALUE_HINIB 53
#define GETWORD_MSB_VALUE_LONIB 54
#define GETWORD_LSB_VALUE_HINIB 55
#define GETWORD_LSB_VALUE_LONIB 56
#define GETWORD_LAST_BYTE 57

/***********************************
*GLOBAL VARIABLE ALLOCATIONS *
************************************/
unsigned char serverResp; //Server Start of Message (SSOM) variable
unsigned char hiNib; //high nibble of variable being requested
unsigned char loNib; //low nibble of variable being requested
unsigned char valueHiNib; //high nibble of value in getByte request
unsigned char valueLoNib; //low nibble of value in getByte request
unsigned char setValHi; //high nibble of set value in setByte //request
unsigned char setValLo; //low nibble of set value in setByte //request
unsigned char valMSBhinib; //MSB high nibble in getWord request
unsigned char valMSBlonib; //MSB low nibble in getWord request
unsigned char valLSBhinib; //LSB high nibble in getWord request
unsigned char valLSBlonib; //LSB low nibble in getWord request
unsigned char newByte; //byte read from spi buffer
unsigned char state; //state that the state table is in
unsigned char stringIndex; //index into the string that is being //outputted
unsigned char firstByteResp; //byte sent when SSOM is received

/**************************
*FUNCTION PROTOTYPES *
***************************/
void spiIn();
void parseSPI(void);
void getByte(void);
void getString(void);
void setByte(void);
void invoke(void);
void getWord(void);
void putstring(unsigned char *str);
void initialize(void);
void reinitialize(void);
void PULSE_ON(void);
void PULSE_OFF(void);
unsigned char hex2ascii(unsigned char hex);
unsigned char ascii2hex(unsigned char ascii);

/***************************
*ARRAY VALUES FOR TESTING *
****************************/
unsigned char byteData[5] = {0x30, 0x31, 0x32, 0x33, 0x34};
unsigned int wordData[5] = {0x100, 0x101, 0x102, 0x103, 0x104};
unsigned char string1[7] = {'S','T','R','I','N','G',0x00};
unsigned char string2[6] = {'T','R','I','N','G',0x00};
unsigned char string3[5] = {'R','I','N','G',0x00};
unsigned char string4[4] = {'I','N','G',0x00};

/**********************************************************************
*MAIN ROUTINE - stays in an infinite loop polling the spi line to see
*if anything has been received, and then handling the byte received.
*NOTE: the spiIn() function simply checks the SPI line to see if
*anything is there, if not it returns.
**********************************************************************/
int main()
{
   initialize();

   while(1)
   {
      spiIn();
   }

   return(0);
}

/**********************************************************************
*Checks to see if anything is waiting on the spi line to be handled,
*if so, it is read into the newByte variable, otherwise it does
*nothing and returns. It also turns off the interrupt pulse that was
*sent out to single that we are ready to receive another byte.
**********************************************************************/
void spiIn()
{
   PULSE_OFF();

   if((SPSR & SPIF) != 0)
   {
      newByte = read_spi();
      parseSPI();
   }
}

/**********************************************************************
*This function acts as the byte parser to the bytes that spiIn()
*assigns to newByte. Its state machine is made up of case statements
*for all possible states in the processing and parsing of the request
*for a function. The first 3 states (SSOM, VARIABLE_HINIB, and
*VARIABLE_LONIB) are used by every function type (getByte, getString,
*setByte, invokeRPC, and getWord), and the remainder of the states
*are only called if their corresponding function is being requested.
**********************************************************************/
void parseSPI(void)
{
   unsigned char stringVal;

   switch(state)
   {
      //Determines which type of function is being requested (CSOM) and
      //puts the corresponding Server Start of Message (SSOM) into
      //the SPDR to be outputted on the next time a byte is received
      //on the SPI line. Sends out pulse on the IRQ line to tell
      //Amulet that we are ready for another byte
      case SSOM:
      switch(newByte)
      {
         case GETBYTE_REQUEST:
            serverResp = GETBYTE_RESPONSE;
            break;
         case STRING_REQUEST:
            serverResp = STRING_RESPONSE;
            break;
         case SETBYTE_REQUEST:
            serverResp = SETBYTE_RESPONSE;
            break;
         case INVOKE_REQUEST:
            serverResp = INVOKE_RESPONSE;
            break;
         case GETWORD_REQUEST:
            serverResp = GETWORD_RESPONSE;
            break;
      }
      SPDR = serverResp;
      state = VARIABLE_HINIB;
      PULSE_ON();
      break;

      //Stores the High Nibble of the variable being requested and puts
      //value in SPDR sends IRQ pulse
      case VARIABLE_HINIB:
         hiNib = newByte;
         SPDR = hiNib;
         state = VARIABLE_LONIB;
         PULSE_ON();
         break;

      //Stores the Low Nibble of the variable being requested and puts
      //value in SPDR, sends IRQ pulse. Also parses serverResp to
      //determine which type of function is being requested. sends IRQ
      //pulse
      case VARIABLE_LONIB:
         loNib = newByte;
         SPDR = loNib;
         switch(serverResp)
         {
            case GETBYTE_RESPONSE:
               state = GETBYTE_VALUE_HINIB;
               break;
            case STRING_RESPONSE:
               state = OUTPUT_STRING;
               break;
            case SETBYTE_RESPONSE:
               state = SETBYTE_VALUE_HINIB;
               break;
            case INVOKE_RESPONSE:
               state = INVOKE_LED_PULSE;
               break;
            case GETWORD_RESPONSE:
               state = GETWORD_MSB_VALUE_HINIB;
               break;
         }
         PULSE_ON();
         break;

      //Calls getByte() function which uses hiNib and loNib to
      //determine which value from the byteData array is being
      //requested. Loads high nibble of that value into SPDR for
      //output on the next transmission. Sends out IRQ pulse
      case GETBYTE_VALUE_HINIB:
         getByte();
         SPDR = valueHiNib;
         state = GETBYTE_VALUE_LONIB;
         PULSE_ON();
         break;

      //Loads low nibble of value retrieved from the byteData array in
      //SPDR
      case GETBYTE_VALUE_LONIB:
         SPDR = valueLoNib;
         state = GETBYTE_LAST_BYTE;
         PULSE_ON();
         break;

      //Outputs string based on which variable is being requested
      //(determined from high nibble and low nibble, only low nibble in
      //the implementation for simplicity's sake. Stays in this state
      //until null termination is encountered. sends IRQ pulse.
      case OUTPUT_STRING:
      if(loNib == 0x30)
      {
         stringVal = string1[stringIndex++];
      }
      else if(loNib == 0x31)
      {
         stringVal = string2[stringIndex++];
      }
      else if(loNib == 0x32)
      {
         stringVal = string3[stringIndex++];
      }
      else if(loNib == 0x33)
      {
         stringVal = string4[stringIndex++];
      }
      if(stringVal == 0)
      {
         state = STRING_LAST_BYTE;
      }
      SPDR = stringVal;
      PULSE_ON();
      break;

      //Loads high nibble of value that variable is being set to in
      //SPDR, sends IRQ pulse
      case SETBYTE_VALUE_HINIB:
         setValHi = newByte;
         SPDR = setValHi;
         state = SETBYTE_VALUE_LONIB;
         PULSE_ON();
         break;

      //Loads low nibble of value that variable is being set to in
      //SPDR, calls setByte() which uses hiNib and loNib to determine
      //which variable is being set, and setValHi and setValLo to
      //determine what value to set that variable to, send IRQ pulse
      case SETBYTE_VALUE_LONIB:
         setValLo = newByte;
         SPDR = setValLo;
         state = SETBYTE_LAST_BYTE;
         setByte();
         PULSE_ON();
         break;

      //turns LED on or off depending on what variable is used. Also
      //calls reinitialize, which resets state machine and loads the
      //firstByteResp (0xFE) back into the SPDR
      //NOTE: a pulse is NOT needed at the end of a byte request
      case INVOKE_LED_PULSE:
         if(loNib == 0x30)
         {
            LED_ON();
            reinitialize();
         }
         else if(loNib == 0x31)
         {
            LED_OFF();
            reinitialize();
         }
         break;

      //Calls getWord(), which uses hiNib and loNib to determine which
      //value from the wordData array is wanted. Loads the MSB high
      //nibble into the SPDR and sends an IRQ pulse
      case GETWORD_MSB_VALUE_HINIB:
         getWord();
         SPDR = valMSBhinib;
         state = GETWORD_MSB_VALUE_LONIB;
         PULSE_ON();
         break;

      //next 3 states load the remaining nibbles of the value
      //calculated in getWord() into SPDR, and send IRQ pulses when
      //ready to process another byte
      case GETWORD_MSB_VALUE_LONIB:
         SPDR = valMSBlonib;
         state = GETWORD_LSB_VALUE_HINIB;
         PULSE_ON();
         break;

      case GETWORD_LSB_VALUE_HINIB:
         SPDR = valLSBhinib;
         state = GETWORD_LSB_VALUE_LONIB;
         PULSE_ON();
         break;

      case GETWORD_LSB_VALUE_LONIB:
         SPDR = valLSBlonib;
         state = GETWORD_LAST_BYTE;
         PULSE_ON();
         break;

      //last state of getByte, getWord, and getString. Resets state
      //machine and loads firstByteResp into SPDR
      case GETBYTE_LAST_BYTE:
      case GETWORD_LAST_BYTE:
      case STRING_LAST_BYTE:
      case SETBYTE_LAST_BYTE:
         reinitialize();
         break;
   }
}

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

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

/**********************************************************************
*getByte() - calculates the high and low nibble of value of the
*variable being requested
**********************************************************************/
void getByte(void)
{
   unsigned char index, byteValue;

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

   byteValue = byteData[index];

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

/**********************************************************************
*setByte() - sets the value of the variable being set in the byteData
*array
**********************************************************************/
void setByte(void)
{
   unsigned char index, hexVal;

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

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

   byteData[index] = hexVal;
}

/**********************************************************************
*getWord() - calculates the 4 nibbles (word) of the value of the
*variable being requested
**********************************************************************/
void getWord(void)
{
   unsigned char index;
   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));
}
/********************************************
*Initialization
*********************************************/
void initialize(void)
{
   firstByteResp = 0xFE;
   stringIndex = 0;
   SPCR = 0x40;
   DDRD = 0x04;
   SPDR = firstByteResp;
   state = SSOM;
}

/******************************************************************
*Reinitialize state machine after function has been performed
******************************************************************/
void reinitialize(void)
{
   SPDR = firstByteResp;
   state = SSOM;
   stringIndex = 0;
}
/*******************************
*Send IRQ pulse to Amulet
********************************/
void PULSE_ON(void)
{
   PORTA &= ~PULSE_BIT;
}

/*******************************
*Turn off IRQ pulse
********************************/
void PULSE_OFF(void)
{
   PORTA |= PULSE_BIT;
}

 



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

Back to Welcome - Contact Amulet - Amulet Home