// Program Description:
//
// This program configures a C8051F02x as a 4-wire SPI Single Master.
//
// The SPI clock in this example is limited to 320 kHz when used with the
// SPI0_Slave code example. During a SPI_Read, the slave needs some time to
// interpret the command and write the appropriate data to the SPI0DAT
// register, and the slave no longer has enough time to complete the
// SPI_READ_BUFFER command with a clock greater than 320 kHz. For faster SPI
// clocks, a dummy byte between the command and the first byte of Read data
// will be required.
//
// This example is intended to be used with the SPI0_Slave example.
//
// Pinout:
//
// P0.0 - SPI SCK (digital output, push-pull)
// P0.1 - SPI MISO (digital input, open-drain)
// P0.2 - SPI MOSI (digital output, push-pull)
// P0.3 - SPI NSS (digital input, open-drain)
//
// P0.7 - SLAVE_SEL (digital output, push-pull) This pin should be routed
// to NSS on the slave
//
// P1.6 - LED (digital output, push-pull)
//
// all other port pins unused.
//
//
// How To Test:
//
// 1) Download the code to a F020-TB that is connected as above to
// another device running the SPI0_Slave code.
// 2) Verify that the J3 jumper is populated.
// 3) Run the code.
// 4) If the communication passes, the LEDs on both the Master and Slave
// boards will blink slowly. If it fails, the LEDs will be OFF.
//
//
// Target: C8051F02x
// Tool chain: Keil C51 7.50 / Keil EVAL C51
// Command Line: None
//
// Release 1.0
// -Initial Revision (TP)
// -14 DEC 2006
//
//-----------------------------------------------------------------------------
// Includes
//-----------------------------------------------------------------------------
#include <C8051F040.h> // SFR declarations
//-----------------------------------------------------------------------------
// Global Constants
//-----------------------------------------------------------------------------
#define SYSCLK 16000000 // Internal oscillator frequency in Hz
#define SPI_CLOCK 320000 // Maximum SPI clock
// The SPI clock is a maximum of 320 kHz
// when this example is used with
// the SPI0_Slave code example.
#define MAX_BUFFER_SIZE 8 // Maximum buffer Master will send
// Instruction Set
#define SLAVE_LED_ON 0x01 // Turn the Slave LED on
#define SLAVE_LED_OFF 0x02 // Turn the Slave LED off
#define SPI_WRITE 0x04 // Send a byte from the Master to the
// Slave
#define SPI_READ 0x08 // Send a byte from the Slave to the
// Master
#define SPI_WRITE_BUFFER 0x10 // Send a series of bytes from the
// Master to the Slave
#define SPI_READ_BUFFER 0x20 // Send a series of bytes from the Slave
// to the Master
#define ERROR_OCCURRED 0x40 // Indicator for the Slave to tell the
// Master an error occurred
sbit LED = P1^6; // LED='1' means ON
sbit SLAVE_SEL = P0^7; // Slave select pin (NSS)
//-----------------------------------------------------------------------------
// Global Variables
//-----------------------------------------------------------------------------
unsigned char SPI_Data = 0xA5;
unsigned char SPI_Data_Array[MAX_BUFFER_SIZE] = {0};
bit Error_Flag = 0;
unsigned char Command = 0x00;
bit Transfer_In_Progress = 0;
//-----------------------------------------------------------------------------
// Function Prototypes
//-----------------------------------------------------------------------------
void Watchdog_Init (void);
void Oscillator_Init (void);
void Port_Init (void);
void SPI0_Init (void);
void Init_Device (void);
void SPI_LED_On (void);
void SPI_LED_Off (void);
void SPI_Byte_Write (void);
void SPI_Byte_Read (void);
void SPI_Array_Write (void);
void SPI_Array_Read (void);
void Delay (void);
void Short_Delay (void);
//-----------------------------------------------------------------------------
// main() Routine
//-----------------------------------------------------------------------------
void main (void)
{
unsigned char test_value = 0x55;
unsigned char test_array[MAX_BUFFER_SIZE] = {1,2,3,4,5,6,7,8};
unsigned char i;
Init_Device (); // Initializes hardware peripherals
EA = 1; // Enable global interrupts
LED = 0;
// TEST BEGIN --------------------------------------------------------------
SPI_Data = test_value;
// Write a value
SPI_Byte_Write ();
while (Transfer_In_Progress); // Wait until the Write transfer has
// finished
// Read the same value back
SPI_Data = 0x00;
SPI_Byte_Read ();
while (Transfer_In_Progress); // Wait until the Write transfer has
// finished
// Check if the sent value and returned value match
if (SPI_Data != test_value)
{
Error_Flag = 1;
}
// Copy test_array into SPI_Data_Array
for (i = 0; i < MAX_BUFFER_SIZE; i++)
{
SPI_Data_Array[i] = test_array[i];
}
// Send the array to the slave
SPI_Array_Write ();
while (Transfer_In_Progress); // Wait until the Write transfer has
// finished
// Clear SPI_Data_Array for the SPI_Buffer_Read function
for (i = 0; i < MAX_BUFFER_SIZE; i++)
{
SPI_Data_Array[i] = 0;
}
// Read the array back from the slave
SPI_Array_Read ();
while (Transfer_In_Progress); // Wait until the Write transfer has
// finished
// Check if the received array matches the sent array
for (i = 0; i < MAX_BUFFER_SIZE; i++)
{
if (SPI_Data_Array[i] != test_array[i])
{
Error_Flag = 1;
}
}
// END OF TEST -------------------------------------------------------------
while (1)
{
// If no error has occurred, blink the LEDs on the Master and Slave
// boards
if (Error_Flag == 0)
{
LED = 1;
SPI_LED_On ();
while (Transfer_In_Progress); // Wait until the Write transfer has
// finished
Delay ();
SPI_LED_Off ();
LED = 0;
while (Transfer_In_Progress); // Wait until the Write transfer has
// finished
Delay ();
}
};
}
//-----------------------------------------------------------------------------
// Initialization Subroutines
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Watchdog_Init
//-----------------------------------------------------------------------------
//
// Return Value : None
// Parameters : None
//
// This function disables the watchdog timer.
//
//-----------------------------------------------------------------------------
void Watchdog_Init (void)
{
WDTCN = 0xDE; // Disable the Watchdog Timer
WDTCN = 0xAD;
}
//-----------------------------------------------------------------------------
// Oscillator_Init
//-----------------------------------------------------------------------------
//
// Return Value : None
// Parameters : None
//
// This function initializes the system clock to use the internal oscillator
// at 16 MHz.
//
//-----------------------------------------------------------------------------
void Oscillator_Init (void)
{
OSCICN = 0x07; // Set the internal oscillator to
// 16 MHz
}
//-----------------------------------------------------------------------------
// Port_Init
//-----------------------------------------------------------------------------
//
// Return Value : None
// Parameters : None
//
// This function configures the crossbar and GPIO ports.
//
// P0.0 - SCK (SPI0), Push-Pull, Digital
// P0.1 - MISO (SPI0), Open-Drain, Digital
// P0.2 - MOSI (SPI0), Push-Pull, Digital
// P0.3 - NSS (SPI0), Open-Drain, Digital
//
// P0.7 - SLAVE_SEL, Push-Pull, Digital
//
// P1.6 - LED, Push-Pull, Digital (LED D3 on Target Board)
//
//-----------------------------------------------------------------------------
void PORT_Init (void)
{
P0MDOUT = 0x85; // Make SCK, MOSI, and SLAVE_SEL
// push-pull
P1MDOUT = 0x40; // Make the LED push-pull
XBR0 = 0x02; // Enable the SPI on the XBAR
XBR2 = 0x40; // Enable the XBAR and weak pull-ups
}
//-----------------------------------------------------------------------------
// SPI0_Init
//-----------------------------------------------------------------------------
//
// Return Value : None
// Parameters : None
//
// Configures SPI0 to use 4-wire Single Master mode. The SPI timing is
// configured for Mode 0,0 (data centered on first edge of clock phase and
// SCK line low in idle state).
//
//-----------------------------------------------------------------------------
void SPI0_Init()
{
SPI0CFG = 0x07; // CKPHA = '0', CKPOL = '0'
// SPI transmits 8 bits
SPI0CN = 0x03; // Master, SPI enabled
// SPI clock frequency equation from the datasheet
SPI0CKR = (SYSCLK/(2*SPI_CLOCK))-1;
EIE1 |= 0x01; // Enable SPI interrupts
SLAVE_SEL = 1; // De-select the slave
}
//-----------------------------------------------------------------------------
// Init_Device
//-----------------------------------------------------------------------------
//
// Return Value : None
// Parameters : None
//
// Calls all device initialization functions.
//
//-----------------------------------------------------------------------------
void Init_Device (void)
{
Watchdog_Init (); // Disable the Watchdog Timer first
Oscillator_Init ();
Port_Init ();
SPI0_Init ();
}
//-----------------------------------------------------------------------------
// Interrupt Service Routines
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// SPI_ISR
//-----------------------------------------------------------------------------
//
// Handles all error checks and single-byte writes.
//
// Note: SPI_WRITE_ARRAY is not handled by this ISR in order to take
// advantage of double-buffering (checking the TXBMT flag) using polling.
//
//
// Typical Write:
//
// | 1st sent | 2nd sent | 3rd sent | ... | last sent |
// ---------------------------------------------------------
// Master NSSv | Command | Data1 | Data2 | ... | DataN | NSS^
// Slave | N/A | N/A | N/A | ... | N/A |
//
// Typical Read:
//
// | 1st sent | 2nd sent | 3rd sent | ... | last sent |
// ---------------------------------------------------------
// Master NSSv | Command | dummy | dummy | ... | dummy | NSS^
// Slave | N/A | Data1 | Data2 | ... | DataN |
//-----------------------------------------------------------------------------
void SPI_ISR (void) interrupt 6
{
static unsigned char array_index = 0;
static char state = 0;
if (WCOL == 1)
{
// Write collision occurred
WCOL = 0; // Clear the write collision flag
Error_Flag = 1;
}
else
{
// SPIF is set when the Master has finished the transfer, but the Slave
// is not necessarily ready to continue the sequence.
SLAVE_SEL = 1; // De-select the slave so it can
// process the last transfer
Short_Delay(); // All the slave a little time to
// process the last transfer
if (SPI0DAT == ERROR_OCCURRED)
{
// This example recognizes when an error occurs, but does not include
// any error handling. The transfer can be aborted or rescheduled,
// if desired.
Error_Flag = 1;
}
// When the Master first enters the ISR, the SPIF flag should be set from
// sending the Command byte. This ISR handles the remaining steps of the
// SPI transfer process.
// <state> == 0: writing or reading 1 byte of data
// <state> == 1: for READ commands (first time, only a dummy byte is
// sent but the second time, the data must be read from
// SPI0DAT)
// <state> == 2: NSS = 1 to end the transfer, final byte read
if (state == 0)
{
switch (Command)
{
case SLAVE_LED_ON:
case SLAVE_LED_OFF:
// Not expecting any data back
Transfer_In_Progress = 0; // Allow a new transfer to begin
break;
case SPI_WRITE:
SLAVE_SEL = 0; // Reselect the slave
SPI0DAT = SPI_Data;
state = 2; // Advance to the final state (only
// writing one byte)
break;
case SPI_READ:
SLAVE_SEL = 0; // Reselect the slave
SPI0DAT = 0xFF; // Send a dummy byte so the Slave can
// send the data
state = 2; // Advance to the final state (only
// reading one byte)
break;
case SPI_WRITE_BUFFER:
array_index = 0; // Clear the data counter
SLAVE_SEL = 0; // Reselect the slave
SPI0DAT = SPI_Data_Array[array_index]; // Write the bytes in
// the array to the Slave
array_index++;
state = 1;
break;
case SPI_READ_BUFFER:
array_index = 0; // Clear the data counter
SLAVE_SEL = 0; // Reselect the slave
SPI0DAT = 0xFF; // Send a dummy byte so the Slave can
// start sending the data
state = 1; // Advance to the next state where the
// data can be received
// The data from the slave is not
// available until after the second
// transfer is completed.
// The dummy byte allows the slave to
// send data, since the Master controls
// SCK.
break;
default:
state = 2; // Any errors in the Command parsing
// should go to state 2 where NSSMD0
// is de-asserted
}
}
else if (state == 1) // This state is for ARRAY
{ // commands.
// For READ_ARRAY, the data must be read
// after the first dummy byte is sent.
// For WRITE_ARRAY, the data counter
// must be cleared before sending the
// first byte.
switch (Command)
{
case SPI_WRITE_BUFFER:
SLAVE_SEL = 0; // Reselect the slave
SPI0DAT = SPI_Data_Array[array_index]; // Write the bytes in
// the array to the Slave
array_index++;
if (array_index == MAX_BUFFER_SIZE)
{
state = 2; // Advance to the final state when
// all bytes are written
}
break;
case SPI_READ_BUFFER:
SPI_Data_Array[array_index] = SPI0DAT;
SLAVE_SEL = 0; // Reselect the slave
SPI0DAT = 0xFF;
array_index++;
if (array_index == (MAX_BUFFER_SIZE-1))
{
state = 2;
}
break;
default:
state = 2; // Any errors in the Command parsing
// should go to state 2 where NSSMD0
// is de-asserted
}
}
else if (state == 2)
{
switch (Command)
{
case SPI_READ:
SPI_Data = SPI0DAT; // Read the data from the slave
break;
case SPI_READ_BUFFER:
SPI_Data_Array[array_index] = SPI0DAT; // Read the last data
// without sending a
// dummy byte
break;
}
Transfer_In_Progress = 0; // Allow a new transfer to begin
state = 0; // Reset the state
}
SPIF = 0; // Clear the SPIF flag
}
}
//-----------------------------------------------------------------------------
// Support Routines
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// SPI_LED_On
//-----------------------------------------------------------------------------
//
// Return Value : None
// Parameters : None
//
// Turns the LED on the SPI Slave on. The slave does not respond to this
// command, so the command consists of:
//
// Command = SLAVE_LED_ON
// Length = 1 byte (the command itself)
//
//-----------------------------------------------------------------------------
void SPI_LED_On (void)
{
while (Transfer_In_Progress); // Wait until the SPI is free, in case
// it's already busy
Transfer_In_Progress = 1; // Indicate a transfer is ongoing
SLAVE_SEL = 0; // Select the slave
Command = SLAVE_LED_ON;
SPI0DAT = Command;
// The rest of this command will be handled by the SPI ISR, which will
// trigger when SPIF is set from sending the Command
}
//-----------------------------------------------------------------------------
// SPI_LED_Off
//-----------------------------------------------------------------------------
//
// Return Value : None
// Parameters : None
//
// Turns the LED on the SPI Slave off. The slave does not respond to this
// command, so the command consists of:
//
// Command = SLAVE_LED_OFF
// Length = 1 byte (the command itself)
//
//-----------------------------------------------------------------------------
void SPI_LED_Off (void)
{
while (Transfer_In_Progress); // Wait until the SPI is free, in case
// it's already busy
Transfer_In_Progress = 1; // Indicate a transfer is ongoing
SLAVE_SEL = 0; // Select the slave
Command = SLAVE_LED_OFF;
SPI0DAT = Command;
// The rest of this command will be handled by the SPI ISR, which will
// trigger when SPIF is set from sending the Command
}
//-----------------------------------------------------------------------------
// SPI_Byte_Write
//-----------------------------------------------------------------------------
//
// Return Value : None
// Parameters : None
//
// Note: SPI_Data must contain the data to be sent before calling this
// function.
//
// Writes a single byte to the SPI Slave. The slave does not respond to this
// command, so the command consists of:
//
// Command = SPI_WRITE
// Length = 1 byte of command, 1 byte of data
//
//-----------------------------------------------------------------------------
void SPI_Byte_Write (void)
{
while (Transfer_In_Progress); // Wait until the SPI is free, in case
// it's already busy
Transfer_In_Progress = 1; // Indicate a transfer is ongoing
SLAVE_SEL = 0; // Select the slave
Command = SPI_WRITE;
SPI0DAT = Command;
// The rest of this command will be handled by the SPI ISR, which will
// trigger when SPIF is set from sending the Command
}
//-----------------------------------------------------------------------------
// SPI_Byte_Read
//-----------------------------------------------------------------------------
//
// Return Value : None
// Parameters : None
//
// Note: SPI_Data will contain the data received after calling this function.
//
// Reads a single byte from the SPI Slave. The command consists of:
//
// Command = SPI_READ
// Length = 1 byte of command, 1 byte of data
//
//-----------------------------------------------------------------------------
void SPI_Byte_Read (void)
{
while (Transfer_In_Progress); // Wait until the SPI is free, in case
// it's already busy
Transfer_In_Progress = 1; // Indicate a transfer is ongoing
SLAVE_SEL = 0; // Select the slave
Command = SPI_READ;
SPI0DAT = Command;
// The rest of this command will be handled by the SPI ISR, which will
// trigger when SPIF is set from sending the Command
}
//-----------------------------------------------------------------------------
// SPI_Array_Write
//-----------------------------------------------------------------------------
//
// Return Value : None
// Parameters : None
//
// Note: SPI_Data_Array must contain the data to be sent before calling this
// function.
//
// Writes an array of values of size MAX_BUFFER_SIZE to the SPI Slave. The
// command consists of:
//
// Command = SPI_WRITE_BUFFER
// Length = 1 byte of command, MAX_BUFFER_SIZE bytes of data
//
//-----------------------------------------------------------------------------
void SPI_Array_Write (void)
{
while (Transfer_In_Progress); // Wait until the SPI is free, in case
// it's already busy
Transfer_In_Progress = 1; // Indicate a transfer is ongoing
SLAVE_SEL = 0; // Select the slave
Command = SPI_WRITE_BUFFER;
SPI0DAT = Command;
// The rest of this command will be handled by the SPI ISR, which will
// trigger when SPIF is set from sending the Command
}
//-----------------------------------------------------------------------------
// SPI_Array_Read
//-----------------------------------------------------------------------------
//
// Return Value : None
// Parameters : None
//
// Note: SPI_Data_Array will contain the data received after calling this
// function.
//
// Reads a single byte from the SPI Slave. The command consists of:
//
// Command = SPI_READ_BUFFER
// Length = 1 byte of command, MAX_BUFFER_SIZE bytes of data
//
//-----------------------------------------------------------------------------
void SPI_Array_Read (void)
{
while (Transfer_In_Progress); // Wait until the SPI is free, in case
// it's already busy
Transfer_In_Progress = 1; // Indicate a transfer is ongoing
SLAVE_SEL = 0; // Select the slave
Command = SPI_READ_BUFFER;
SPI0DAT = Command;
// The rest of this command will be handled by the SPI ISR, which will
// trigger when SPIF is set from sending the Command
}
//-----------------------------------------------------------------------------
// Delay
//-----------------------------------------------------------------------------
//
// Return Value : None
// Parameters : None
//
// Delay for little while (used for blinking the LEDs)
//
//-----------------------------------------------------------------------------
void Delay (void)
{
unsigned long count;
for (count = 200000; count > 0; count--);
}
//-----------------------------------------------------------------------------
// Short_Delay
//-----------------------------------------------------------------------------
//
// Return Value : None
// Parameters : None
//
// Delay for little while (used between transfers with the Slave)
//
//-----------------------------------------------------------------------------
void Short_Delay (void)
{
unsigned char count;
for (count = 200; count > 0; count--);
}
//-----------------------------------------------------------------------------
// End Of File
//-----------------------------------------------------------------------------
|