Main Page | Index

Axis Software User Manual


Serial Peripheral Interface

The Axis hardware includes a three channel Serial Peripheral Interface (SPI). There is one set of SPI hardware which can be switched to the selected channel. One channel (0) is used to communicate with the power down monitoring processor, communications with this processor use custom protocol and Heber provides a library that implements a simple API to allow applications to query the power down processor. The other two channels are available for user applications.

SPI is a simple synchronous serial protocol, the master device (in this case the Axis hardware) contains two shift registers and a clock source. The slave device one shift register with taps at either end. The three shift registers are effectively connected in series to form one long 24 bit register. An SPI operation can be though of as the master clocking data from its transmit register into the slave's SPI data register and concurrently transferring the old slave's SPI data register contents into the master receive register.

The I/O model used by the Axis driver divides SPI operations into reads and writes. It should be noted that a write by the Axis driver involves throwing away the old slave data register values and a read involves clocking dummy data into the slave data register.

The Axis driver allows several SPI parameters to be modified.
An application can control:

The driver also allows an application to specify a minimum intercharacter time, this can be a useful way of stopping the Axis processor overwhelming a slower device with data.

In a multi-processing environment the driver has to deal with the possibility of multiple applications attempting concurrent SPI operations. The driver allows access to each SPI channel to only one application at-a-time on a first come, first served basis. If a second attempt is made to open a SPI channel while an application already has the channel open, then the open operation will fail. The driver does allow applications to issue operations against different SPI channels concurrently.

Before using the SPI hardware both the Axis kernel driver module needs loaded and a set of special character device files need creating.

mknod -m666 /dev/axis_0/spi_0 c 231 96
mknod -m666 /dev/axis_0/spi_1 c 231 97
mknod -m666 /dev/axis_0/spi_2 c 231 98

The /dev/axis_0/spi_0 entry is needed for the door monitoring library to work. The spi_1 and spi_2 devices are available.

Once these device files have been created then the SPI hardware can used like a normal device.

This application spi_tee reads data from its STDINPUT and sends it to the selected SPI device. It then optionally reads data back and writes the returned data to STDOUT.

The application also demonstrates how to change other SPI parameters such as clock speed phase and polarity from their default values.

// //////////////////////////////////////////////////////////////////////////////
// Simple utility to send data /receive data to or from  an Axis SPI device
//
// //////////////////////////////////////////////////////////////////////////////

#include <stdio.h>
#include <popt.h>
#include <sys/types.h>
#include <sys/stat.h>

#include <fcntl.h>
#include "../PLX_Driver/axis.h"

// //////////////////////////////////////////////////////////////////////////////
// Text to display if help is requested
// //////////////////////////////////////////////////////////////////////////////
const char *Help =
	"-p --port device path default = /dev/axis_0/spi_0\n"

	"-d --delay n intercharacter delay(usec)\n"
	"-s --silent do not echo input to STDOUT\n"
	"-h --help display this message\n"
	"-t --phase use alternate clock phase\n"
	"-t --invert use inverted clock\n"
	"-l --lsb send lsb first\n"

	"-r --read  n read n bytes from back from SPI after transmit completed\n"
	"-c --clock n set bit clock divisor, valid values = 2 4 8 16 32 64 256 \n"
	"-w --wait n useconds between finishing transmission and attempting receive\n";

// //////////////////////////////////////////////////////////////////////////////
// Default values for user options
// //////////////////////////////////////////////////////////////////////////////
char *PortName = "/dev/axis_0/spi_0";

unsigned long inter_character_delay = 0;
int help = 0;
int silent = 0;
unsigned long phase = 0;
unsigned long invert = 0;

unsigned long lsb = 0;
unsigned long clock = 256;
unsigned long read_count = 0;
unsigned long post_tx_delay = 0;


// //////////////////////////////////////////////////////////////////////////////
// Use the popt library for lazy command line parsing
// need to add -lpopt to the gcc command line
// //////////////////////////////////////////////////////////////////////////////
static const struct poptOption commandline_options[] = {
	{"phase", 't', POPT_ARG_NONE, &phase, 0},
	{"invert", 'i', POPT_ARG_NONE, &invert, 0},
	{"lsb", 'l', POPT_ARG_NONE, &lsb, 0},
	{"clock", 'c', POPT_ARG_LONG, &clock, 0},
	{"read", 'r', POPT_ARG_LONG, &read_count, 0},
	{"port", 'p', POPT_ARG_STRING, &PortName, 0},
	{"delay", 'd', POPT_ARG_LONG, &inter_character_delay, 0},
	{"wait", 'w', POPT_ARG_LONG, &post_tx_delay, 0},
	{"silent", 's', POPT_ARG_NONE, &silent, 0},
	{"help", 'h', POPT_ARG_NONE, &help, 0},
	{NULL, '\0', 0, NULL, 0},
};



// /////////////////////////////////////////////////////////////////////////
//
// Main program
//
// //////////////////////////////////////////////////////////////////////////
int main( int argc, const char **argv )
{
	int handle;
	int n;
	int number_this_time;
	int number_to_do;
	char buffer[512];
	int ReturnValue = 0;
	unsigned char spi_setting = SPI_CLK_DIV_256;
	poptContext pop_context;
	pop_context = poptGetContext( NULL, argc, argv, commandline_options, 0 );
	while ( poptGetNextOpt( pop_context ) != -1 ) ;
	poptFreeContext( pop_context );
	if ( help )
	{
		fprintf( stderr, Help );
		return 0;
	}
	handle = open( PortName, O_RDWR );
	if ( handle < 0 )
	{
		fprintf( stderr, "Failed to open %s\n", PortName );
		return -1;
	}
	if ( inter_character_delay )
	{

		ioctl( handle, AXIS_SPI_SET_MIN_INTERCHARACTER_TIME, inter_character_delay );
	}
	switch ( clock )
	{
	  case 2:
		  spi_setting = SPI_CLK_DIV_2;
		  break;
	  case 4:
		  spi_setting = SPI_CLK_DIV_4;
		  break;
	  case 8:
		  spi_setting = SPI_CLK_DIV_8;
		  break;
	  case 16:
		  spi_setting = SPI_CLK_DIV_16;
		  break;
	  case 32:
		  spi_setting = SPI_CLK_DIV_32;
		  break;
	  case 64:
		  spi_setting = SPI_CLK_DIV_64;
		  break;
	  case 128:
		  spi_setting = SPI_CLK_DIV_128;
		  break;
	  case 256:
		  spi_setting = SPI_CLK_DIV_256;
		  break;
	  default:
		  {
			  ReturnValue = -1;
			  fprintf( stderr, "Valid clock divisor\n %s", Help );
			  goto end;
		  }
	}
	spi_setting |= phase ? SPI_CLOCK_PHASE : 0;
	spi_setting |= invert ? SPI_CLOCK_POLARITY : 0;
	spi_setting |= lsb ? SPI_LSB_FIRST : 0;
	ioctl( handle, AXIS_SPI_SET, spi_setting );
	while ( ( n = read( 0, buffer, sizeof( buffer ) ) ) > 0 )
	{
		if ( !silent )
			write( 1, buffer, n );
		write( handle, buffer, n );
	}
	if ( read_count )
	{
		if ( post_tx_delay )
			usleep( post_tx_delay );
		number_to_do = read_count;
		while ( number_to_do )
		{
			number_this_time =
				( number_to_do > sizeof( buffer ) ) ? sizeof( buffer ) : number_to_do;
			n = read( handle, buffer, number_this_time );
			if ( n > 0 )
				write( 1, buffer, n );
			number_to_do -= number_this_time;
		}
	}
  end:
	close( handle );
	return ReturnValue;
}


© HEBER LTD. 2005. This document and the information contained therein is the intellectual property of Heber Ltd. and must not be disclosed to a third party without consent. Copies may be made only if they are in full and unmodified. The information contained in this documentation is believed to be accurate and reliable. However, Heber Ltd. assumes no responsibility for its use, and reserves the right to revise the documentation without notice.
Document No: 80-17794, Issue 4r1    Release Date: 01.12.05     Email: support@heber.co.uk    www.heber.co.uk