Home/Support/Support Forum/SPI Read Problem
Welcome to Digi Forum, where you can ask questions and receive answers from other members of the community.

SPI Read Problem

0 votes
I am trying to get my SPI working but I got into several problems.

Here is the functions I am using to get the revision number of my SPI peripheral.

// Structure used in SPI registration
static NASpiDeviceConfigType SPI_CP2120 [] =
{
{
1, /*Serial Port, 0 based*/
NULL, /*Callback function to select the SEEPROM Chip*/
NULL, /*Callback function to deselect the SEEPROM Chip*/
NA_SPI_MODE0, /*SPI mode, only mode 0 is supported in NS7520*/
NA_SPI_MSB, /*most significant bit first*/
100000, /*speed*/
40, /*deselect time*/
"NWC_CP2120"/*unique name for this device*/
},
};

void TestSPI (void)
{
NaStatus ccode;
char Revision[2];

ccode = NASpiRegisterDevice(&SPI_CP2120[0]);
if (ccode != NASTATUS_SUCCESS)
{
printf("NASpiRegisterDevice() failed 0x%X\n", (unsigned int)ccode);
}
else
{
//Get version from chip
GetCP2120Revision(Revision);


//Deregister the SPI Port
NASpiDeregisterDevice(SPI_CP2120[0].name);

if (ccode != NASTATUS_SUCCESS)
{
printf("NASpiDeregisterDevice() failed 0x%X!\n", (unsigned int)ccode);
}
else
{
printf("NASpiDeregisterDevice() success!\n\n");
}
}

return;
}

//Command template is out:[command][don't care]
// in: [Rev Byte 1] [Rev Byte 2]
void GetCP2120Revision (char* Revision)
{
NaStatus Status;
char Buffer[128];
char Response[128];

memset(Buffer, 0xFF, 128);
memset(Response, 0xFF, 128);

Buffer[0] = 0x40;

//use bufferlength of 4 to include 2 bytes out and 2 bytes in from what I have read on the forum
Status = NASpiReadWrite(SPI_CP2120[0].name, Buffer, Response, 4);

if(NA_IS_SUCCESS(Status))
{
Revision[0] = Response[0];
Revision[1] = Response[1];
printf("SPI Success\n");
}
}

With this code I am using getting the error NASTATUS_SPI_MASTER_INVALID_BUFFER.

Anyone has any idea why this is happening? Am I wrong to assume that if my buffer has a length of 128 bytes that it should be 32-bits aligned?

Regards
asked Apr 6, 2010 in NET+OS by kpineau New to the Community (8 points)
recategorized Dec 4, 2013 by tuxembb

Please log in or register to answer this question.

4 Answers

0 votes
Hi,

The buffers need to be on a 32 Byte boundary for SPI. I believe this is required for the DMA to operate correctly.

I remember having some problems trying to get this alignment simply and developed the following routine to allocate SPI buffers.

It is a bit inefficient but ios seems to work OK for me.

/*********************************************************************
Definitions of SPI buffer structures used to transfer data
*********************************************************************/
struct str_spi_buffers {
unsigned char *rd_algn_ptr;
unsigned char *wr_algn_ptr;
};



/*****************************************************************************

SPI_AllocBuffers:

Description:
This routine is used to allocate buffers to be used by the SPI ReadWrite
routine. The routine first allocates a str_spi_buffers structure and then
fills this structure with pointers to the equal sized read and write buffers
that are correctly aligned on a 32 byte boundary

Parameters
size Required buffer size

Returns
Pointer to allocated structue (NOTE: use SPI_FreeBuffers to free this)
NULL error allocating buffers

*****************************************************************************/
struct str_spi_buffers * SPI_AllocBuffers(int size)
{
UINT status;
int rqd_size;
unsigned int temp;
struct str_spi_buffers *ptr = NULL;
const char *ERR_FMT = "ERR:[%08ld] SPI_InitModule: %s() failed with status %d.\n";

/* Determine the total size required */
rqd_size = ((size + BYTE_ALIGN - 1) * 2) + sizeof(struct str_spi_buffers);

/* Allocate memory for both structure and buffers */
status = tx_byte_allocate(&GBL_SPI_pool, (VOID *)&ptr, rqd_size, TX_NO_WAIT);
if (status != TX_SUCCESS) {
printf(ERR_FMT, tx_time_get(), "tx_byte_allocate", status);
return(NULL);
}

/* Determine aligned pointer for rd buffer */
temp = (unsigned int)ptr + sizeof(struct str_spi_buffers);
if (temp % BYTE_ALIGN) {
temp += BYTE_ALIGN - (temp % BYTE_ALIGN);
}
ptr->rd_algn_ptr = (unsigned char *)temp;

/* Determine aligned pointer for wr buffer */
temp += size;
if (temp % BYTE_ALIGN) {
temp += BYTE_ALIGN - (temp % BYTE_ALIGN);
}
ptr->wr_algn_ptr = (unsigned char *)temp;

return (ptr);
}

/*****************************************************************************

SPI_FreeBuffers:

Description:
This routine is used to free buffers that were provided by SPI_AllocBuffers

Parameters
spi_buffers Pointer to the allocated bufferrs

Returns
status SPI_OK (0) = Success
SPI_ERR_MEMORY = Failed to release memory

*****************************************************************************/
int SPI_FreeBuffers(struct str_spi_buffers *spi_buffers)
{
UINT status;
const char *ERR_FMT = "ERR:[%08ld] SPI_FreeBuffers: %s() failed with status %d.\n";

/* Allocate memory for controlling structure */
status = tx_byte_release((VOID *)spi_buffers);
if (status != TX_SUCCESS) {
printf(ERR_FMT, tx_time_get(), "tx_byte_release", status);
return(SPI_ERR_MEMORY);
}

return(SPI_ERR_OK);
}
answered Apr 7, 2010 by rdeabill Community Contributor (142 points)
0 votes
Thank you very much for your routines concerning the allocation of thebuffers.

I have used it and now I am getting a different error NASTATUS_SPI_MASTER_DMA_FAILED . I have noticed that the length passed in NASpiReadWrite() is confusing also, what should I put as length, the TX length or the TX + RX length ? Maybe that may cause my problem...

If this is not working with a good length in parameter I will recheck every signal on the module to make sure everything is ok.
answered Apr 8, 2010 by kpineau New to the Community (8 points)
0 votes
Hi,

The length passed to NASpiReadWrite() is used for both the Tx Length and the Rx length. Effectively the data from the Tx buffer is clocked out and the data in the Rx buffer is clocked in using the same clock, atr the same time. The way to interpret this does depend a bit on the exact way the peripheral works. If youare ony reading then you can ignore the data in the Tx buffer, but it still must be there to provide data even though it is ignored. Similarly when writing data will be read into the Rx buffer so that must be there also.

The Initialisation I use to set up the GPIO is:
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ConfigureGPIO:

Description:
This routine is used to configure the GPIO pins that will be used for
the SPI interface. It uses the pin definitions from spi.h and is
caled by the main Module Initialisation routine.

Parameters
None

Return
None

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
static void ConfigureGPIO(void)
{
#if (PROCESSOR == ns9210 || PROCESSOR == ns9215)
NAconfigureGPIOpin(APP_SPI_MASTER_GPIO_CS, NA_GPIO_FUNC4_STATE, 0);
NAconfigureGPIOpin(APP_SPI_MASTER_GPIO_CLK, NA_GPIO_FUNC4_STATE, 0);
NAconfigureGPIOpin(APP_SPI_MASTER_GPIO_RXD, NA_GPIO_FUNC4_STATE, 0);
NAconfigureGPIOpin(APP_SPI_MASTER_GPIO_TXD, NA_GPIO_FUNC4_STATE, 0);

#elif (PROCESSOR == ns9360 || PROCESSOR == ns7520)
NAconfigureGPIOpin(APP_SPI_MASTER_GPIO_CS, NA_GPIO_OUTPUT_STATE, 1); /*enable*/
NAsetGPIOpin (APP_SPI_MASTER_GPIO_CS, 1);

NAconfigureGPIOpin(APP_SPI_MASTER_GPIO_CLK, NA_GPIO_OUTPUT_STATE, 0); /*clk*/
NAsetGPIOpin (APP_SPI_MASTER_GPIO_CLK, 0);

NAconfigureGPIOpin(APP_SPI_MASTER_GPIO_CS, NA_GPIO_FUNC0_STATE, 0);
NAconfigureGPIOpin(APP_SPI_MASTER_GPIO_CLK, NA_GPIO_FUNC0_STATE, 0);
#else
#error "set up the GPIO pins here.\nThen, comment this line out to build your platform."
#endif
}



Then the device registration is done by:
/*****************************************************************************

SPI_OpenModule:

Description:
This routine is used to initialise the SPI module.
The required GPIO signals asr set up and a device description is added
to the NetOS driver table

NOTE: This currently supports a master interface optimised for CML C-BUS

Parameters
None

Returns
status SPI_OK (0) = Success
SPI_ERR_MEMORY = Failed to generate memory pool
SPI_ERR_REGISTER = Failed to register device

*****************************************************************************/
int SPI_OpenModule(void)
{
UINT status;
NaStatus reg_stat;
const char *ERR_FMT = "ERR:[%08ld] SPI_InitModule: %s() failed with status %d.\n";

/* Configure the hardware signal we will use */
ConfigureGPIO();

/* Create the memory pool to be used for SPI buffers */
status = CreateBufferPool();
if (status) {
return(SPI_ERR_MEMORY);
}

reg_stat = NASpiRegisterDevice(&GBL_CMX_Device);
if (NA_IS_ERROR(reg_stat))
{
printf(ERR_FMT, tx_time_get(), "NASpiRegisterDevice", reg_stat);
return(SPI_ERR_REGISTER);
}

return (SPI_ERR_OK);
}



And the actual transfer is:

/*****************************************************************************

SPI_ReadWrite:

Description:
This routine is used to transfer data on the SPI bus. It should passed a
buffer structure that was allocated by SPI_AllocBuffers, and with data to
be written set up at the wr_algn_ptr. The routine reads and writes at the
same time. When the routine returns read data will be at the rd_align_ptr
location.

Parameters
device Name of device for data transfer (SPI_CMX7143)
buffs pointer to structure containing read and write buffers
length Number of bytes to be transferred

Returns
Pointer to allocated structue (NOTE: use SPI_FreeBuffers to free this)
NULL error allocating buffers

*****************************************************************************/
int SPI_ReadWrite(char * device, struct str_spi_buffers *buffs, int length)
{
NaStatus status;
const char *ERR_FMT = "ERR:[%08ld] SPI_ReadWrite: %s() failed with status %d.\n";
char *wr_ptr = (char *)(buffs->wr_algn_ptr);
char *rd_ptr = (char *)(buffs->rd_algn_ptr);

ShowBuff("SPT", wr_ptr, length);
status = NASpiReadWrite(device, wr_ptr, rd_ptr, length);
ShowBuff("SPR", rd_ptr, length);
if (NA_IS_ERROR(status))
{
printf(ERR_FMT, tx_time_get(), "NASpiReadWrite", status);
return(SPI_ERR_REGISTER);
}

return (SPI_ERR_OK);
}

The actual GPIO pins used are defined by

/*********************************************************************
Definition of the Port and GPIO Signals to be used
*********************************************************************/
#define PORTA 0
#define PORTB 1
#define PORTC 2
#define PORTD 3

#if (PROCESSOR == ns9215)
#define APP_SPI_MASTER_PORT PORTA
#define APP_SPI_SLAVE_PORT PORTA
#define APP_SPI_MASTER_GPIO_CS 0
#define APP_SPI_MASTER_GPIO_CLK 5
#define APP_SPI_MASTER_GPIO_RXD 3
#define APP_SPI_MASTER_GPIO_TXD 7
#define APP_SPI_SLAVE_GPIO_CS 0
#define APP_SPI_SLAVE_GPIO_CLK 5
#define APP_SPI_SLAVE_GPIO_RXD 3
#define APP_SPI_SLAVE_GPIO_TXD 7
#elif (PROCESSOR == ns9210)
#define APP_SPI_SLAVE_PORT PORTA
#define APP_SPI_MASTER_PORT PORTA
#define APP_SPI_MASTER_GPIO_CS 0
#define APP_SPI_MASTER_GPIO_CLK 5
#define APP_SPI_MASTER_GPIO_RXD 3
#define APP_SPI_MASTER_GPIO_TXD 7
#define APP_SPI_SLAVE_GPIO_CS 0
#define APP_SPI_SLAVE_GPIO_CLK 5
#define APP_SPI_SLAVE_GPIO_RXD 3
#define APP_SPI_SLAVE_GPIO_TXD 7
#elif (PROCESSOR == ns9360)
#define APP_SPI_MASTER_PORT PORTB
#define APP_SPI_SLAVE_PORT PORTB
#if (APP_SPI_MASTER_PORT == PORTB)
#define APP_SPI_MASTER_GPIO_CS 7
#define APP_SPI_MASTER_GPIO_CLK 6
#elif (APP_SPI_MASTER_PORT == PORTC)
#define APP_SPI_MASTER_GPIO_CS 23
#define APP_SPI_MASTER_GPIO_CLK 22
#elif (APP_SPI_MASTER_PORT == PORTD)
#define APP_SPI_MASTER_GPIO_CS 27
#define APP_SPI_MASTER_GPIO_CLK 26
#endif
#if (APP_SPI_SLAVE_PORT == PORTB)
#define APP_SPI_SLAVE_GPIO_CS 7
#define APP_SPI_SLAVE_GPIO_CLK 6
#elif (APP_SPI_SLAVE_PORT == PORTC)
#define APP_SPI_SLAVE_GPIO_CS 23
#define APP_SPI_SLAVE_GPIO_CLK 22
#elif (APP_SPI_SLAVE_PORT == PORTD)
#define APP_SPI_SLAVE_GPIO_CS 27
#define APP_SPI_SLAVE_GPIO_CLK 26
#endif
#elif (PROCESSOR == ns7520)
#define APP_SPI_MASTER_PORT PORTB
#define APP_SPI_MASTER_GPIO_CS 0
#define APP_SPI_MASTER_GPIO_CLK 4
#else
#error "SPI support not available on this processor or platform."
#endif

I have only used this with a ConnectCore9P_9215 to communicate with a SPI compatiable device and it works very well apart from being rather slow.

Hope this helps
answered Apr 9, 2010 by rdeabill Community Contributor (142 points)
0 votes
Thank you very much rdeabill it helped out a lot. I am not able to send correctly over the SPI bus now but I have found out that my SPI device chip requires a mode 3 which is not available for the NS7520. I was wondering if some code is now available for the NS7520 in mode 1. Currently, we only have mode 0. If mode 1 was available I could try to inverse the clock with hardware and use mode 1 and see if it works.

Regards,
answered May 17, 2010 by kpineau New to the Community (8 points)
...