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