SSC SPI master configuration help needed

Tip / Sign in to post questions, reply, level up, and achieve exciting badges. Know more

cross mob
Not applicable
Hello all!

I am trying to configure the SPI connection of the XMC4800 as a master to link it with an Arduino as a slave (as example).

The aim of the test program is to give a number to the slave, the slave will make a simple addition (given number + 15) and send back the new number.
The slave is working with interrupt and I monitor it with the serial monitor, but nothing seems happening. I precise it is working with another arduino as a master.

The SPI connection of the Arduino is 8 bits (8 bits data register) and working between 250 kHz and 16 mhz.

I configure the Baud Rate Generator Register BRG to have a SCLK of 250 kHz, with PDIV only. I assume the Peripheral Bus Clock is as the same frequency as the CPU
clock (e.g. 144 Mhz for the Cortex M4), because I didn't find where to check it inside the reference manual (RM).

I set up the word and frame length at 8 bits with MSB first. Is that mean the XMC4800 will send the bits [15-8] ?

I am using pin P3.10, which is connected to USIC2_CH0.SELO0. If TCSR.SELMD is deactivated, which TBUF I am supposed to use? TBUF[0] ?

Here my code, could you check my configuration settings ?

Thank you,
Nicolas



* main.c
*
* Created on: 2016 May 03 09:26:46
* Author: nicolas
*/




#include //Declarations from DAVE Code Generation (includes SFR declaration)

/**

* @brief main() - Application entry point
*
* Details of function

* This routine is the application entry point. It is invoked by the device startup code. It is responsible for
* invoking the APP initialization dispatcher routine - DAVE_Init() and hosting the place-holder for user application
* code.
*/

/* ===================== Global variable declaration ===================== */
uint8_t debug_tcsr_sof;

uint32_t transferAndWait (const uint32_t what);

int main(void)
{
DAVE_STATUS_t status;

status = DAVE_Init(); /* Initialization of DAVE APPs */

if(status == DAVE_STATUS_FAILURE)
{
/* Placeholder for error handler code. The while loop below can be replaced with an user error handler. */
XMC_DEBUG("DAVE APPs initialization failed\n");

while(1U)
{

}
}


/* ===================== Variable declaration ===================== */
uint32_t i,j = 0;

uint8_t a, b, c, d;

/* ============================ SETUP ============================ */

// Choice of an USIC module registers and channel registers : prefix USICx_CHn

/* ----------------------------- Configuration of SSC SPI -----------------------------
* ====================================================================================
*/

/* Mode Control RM 18.2.2.2 */

/* Release reset of USIC module by writing a 1 to the USICxRS bit in SCU_PRCLR0 or SCU_PRCLR1 registers */
SCU_RESET->PRCLR1 |= (1<<8);
// Switch on the USIC Module (bit protection)
USIC2_CH0->KSCFG = 0x3; //MODEN=1, BPMODEN=1;

// Select SSC Mode for USIC Channel 0 : put at 0 for configuration
USIC2_CH0->CCR = 0x00000000;

/* ....... Pins configuration RM 26.8 & 26.10 ....... */

/* INPUT - MISO - P3.7 - USIC2_CH0.DX0C -> P3_IOCR4 -> PC7 (because P3.7) -> bits [31-27] */
PORT3->IOCR4 |= 0b00000000000000000000000000000000;

/* OUTPUT - MOSI - P3.8 -> P3_IOCR8 -> PC8 ( because P3.8) -> bits [7-3]
Alternate function 1 (cf RM p 2814 26.10 ) -> 10001b
*/
PORT3->IOCR8 |= 0b00000000000000000000000010001000;

/* CLK - P3.9 -> P3_IOCR9 -> PC9 ( because P3.9) -> bits [15-11]
Alternate function 1 (cf RM p 2814 26.10 ) -> 10001b
*/
PORT3->IOCR8 |= 0b00000000000000001000100000000000;

/* Slave Select SELO0 - P3.10, cf RM table 18-29 p1998
P3.10 -> P3_IOCR8 -> PC10 ( because P3.10) -> bits [23-19]
Alternate function 1(cf RM p 2814 26.10 ) -> 10001b
*/
PORT3->IOCR8 |= 0b00000000100010000000000000000000;


/* ....... Input Control Registers RM 18.11.5.1 ....... */
USIC2_CH0->DX0CR |= 0x00000052;
/* 0x2 = 0b0010 : DXnC selected */
/* 0x5 = 0b0101 :
- INSW = 1 : The input of the data shift unit is connected to the selected data input line. This setting is used
if the signals are directly derived from an input pin without treatment by the protocol preprocessor
- DSEN = 1 (The synchronized signal can be taken as input for the data shift unit.) cf RM p1925 and RM 18.4.3
*/

/* ....... SSC PCR Protocol Control Register RM 18.4.5.1 ....... */
// Main features :
//MSLS Enable
//Slave Mode Clock Phase Select
//MCLK Master Clock Enable
// Slave Select : SELO0 activated, direct select mode
USIC2_CH0->PCR |= 0b00000000000000010000000000000011;


/* ....... SCTR Shift Control Register RM 18.11.7.1 ....... */
/* Main features :
* SDIR Data Shift Direction MSB first
* DSM = 00 : Data Shift Mode : Receive and transmit data is shifted in and out one bit at a time through DX0 and DOUT0
* TRM = 01 : Transmission mode : This bit field describes how the shift control signal is interpreted by the DSU. Data transfers are only
possible while the shift control signal is active.
TRM = 01 :The shift control signal is considered active if it is at 1-level. This is the setting to be
programmed to allow data transfers.
* Frame Length = Word Length = 8 bits
* -> Aim : transmit 8 bits at the time and deactivate Slave Select automatically because of TCSR.EOF
*/

USIC2_CH0->SCTR |= 0b00001000000010000000000100000001;


/* ....... TCSR Transmission Control and Status Register RM 18.11.7.2 ....... */
/* Main features :
* WLEMD = 0 : WLE Mode means the TCSR.EOF bit is not automatically changed.
* SELMD = 0 : we have only one slave: Select Mode : automatically update bit field PCR.CTR[20:16] by the transmit control information TCI[4:0] and clear bit field
PCR.CTR[23:21] (see Page 18-33). If enabled, an automatic update takes place when new data is loaded to register TBUF, either by writing to one of the
transmit buffer input locations TBUFx or by an optional data buffer.
* WLE Mode : This bit enables the data handler to automatically update the bit field SCTR.WLE by the transmit control information TCI[3:0] and bit TCSR.EOF by TCI[4]
(see Page 18-33). If enabled, an automatic update takes place when new data is loaded to register TBUF, either by writing to one of the transmit buffer
input locations TBUFx or by an optional data buffer.
* SOF = 1 : Start of Frame : The data word in TBUF is considered as first word of a frame. A currently running frame is finished and MSLS becomes deactivated
(respecting the programmed delays).
So after have sending a frame, the PCR.MSLSEN (slave select) is automatically deactivated.
So here the Slave Select is deactivated after send 8 bits.
* EOF : End of Frame : If it is the last word, the MSLS signal becomes inactive after the transfer, respecting the programmed delays. This bit becomes
cleared when the TBUF data word is transferred to the transmit shift register.
EOF = 1 : The data word in TBUF is considered as last word of an SSC frame.
* TDSSM = 1 : TBUF Data Single Shot Mode: data in TBUf is considered inactive after being moved into the shift register, send data only once.
* TDEN = 01: TBUF Data Enable : A transmission of the data word in TBUF can be started if TDV = 1.
*/
USIC2_CH0->TCSR |= 0b00000000000000000000000001101000;


/* ....... Baud Rate Generator Register (BRG) RM 18.11.6.2 ....... */
// 250 kHz
// Main features :
// SCLKCFG = 10 : Shift Clock Output Configuration : The passive level is 0 and the delay is enabled
//Shift Clock Output Select
//PDIV = 287 : Divider factor to generate SCLK = 250khz
// SCLKOSEL = 0 : SCLK from the baud rate generator is selected as the SCLKOUT input source.
// CLKSEL = 00 : the fractional divider frequency Ffd is selected
USIC2_CH0->BRG |= 0b10000001000111110000000000000000;



/* ----------------------------- Activation of SSC SPI -----------------------------
* =================================================================================
*/

USIC2_CH0->CCR = 0x00000001; // Select SSC Mode for USIC Channel 0

/* ============================ LOOP ============================ */
/* Placeholder for user application code. The while loop below can be replaced with user application code. */
while(1U)
{
transferAndWait('a'); // add command
transferAndWait(10);
a = transferAndWait(17);
b = transferAndWait(33);
c = transferAndWait(42);
d = transferAndWait(0);

/* wait for 1 sec with time machine = 144 mhz */
for (i=0; i<143000000; i++)
{
j = i+1;
}
j = 0;
}
}

uint32_t transferAndWait(const uint32_t what)
{
/* Start transmission */
/* slave selected and clock output */
USIC2_CH0->PCR |= 0b00000000000000000000000000000011;

/* Writing into the transmit buffer */
USIC2_CH0->TBUF[0] = what;

/* Wait for transmission complete */
while ( ((USIC2_CH0->TCSR) & (1<<7)) && (!( (USIC2_CH0->TCSR) & (1<<26) ) ) ) // TDV bit number 7, TV bit number 26
{

/*New data should not be written to a TBUFx input location while TDV = 1.
Wait for validation transmission of the latest data word while TV = 0.
*/
};
uint32_t temp = USIC2_CH0->RBUF ;

/* MSLEN is supposed to be automatically deactivated - TCSR.SOF = 1 - */
if ( ( USIC2_CH0->PCR & (1<<5) ) && ( USIC2_CH0->PCR & (0<<0) ) )
debug_tcsr_sof = 1;

return temp;



}

0 Likes
3 Replies
chismo
Employee
Employee
First like received
Hi Nicholas,

Are you using the DAVE APP to generate a base code and then overwriting it?
If you are using the APP, you should be able to configure directly the APP to fit most of your requirements.

Or if you prefer to use direct register coding, then I would suggest to refer to the following SPI code example and adapt it to your requirements.


#include

/**

* @brief main() - Application entry point
*
* Details of function

* This routine is the application entry point. It is invoked by the device startup code.
*/
#define SPI_CH USIC2_CH0

void SPI_CH_MasterInit(void)
{
/// ------------------------------------------------------------
/// 1.Enable USIC0 channel 1
/// ------------------------------------------------------------
// BPMODEN MODEN
SPI_CH->KSCFG|= (1 << 1)| (1 << 0);
/// ------------------------------------------------------------
/// 2.Configure baud rate generator
/// - Normal divider mode
/// - Baud rate = 250 kbit/s
/// - SCLKCFG = 10b
/// ------------------------------------------------------------
// DM STEP
SPI_CH->FDR = (1 << 14) | (1023 << 0);
// SCLKCFG PDIV
SPI_CH->BRG = (2 << 30) | (287 << 16);
/// ------------------------------------------------------------
/// 3.Configure input stages
/// - Select input DX0C
/// - Derive input of data shift unit directly from input pin
/// - DSEN = 1
/// ------------------------------------------------------------
// DSEN INSW DSEL
SPI_CH->DX0CR =(1 << 6) | (1 << 4) |(2 << 0);
/// ------------------------------------------------------------
/// 4.Configure data format
/// - Data word = data frame = 8 bits
/// - Data transfer allowed with passive level = 0 and MSB first
/// ------------------------------------------------------------
// WLE FLE TRM SDIR
SPI_CH->SCTR =(7<<24)|(7<<16)|(1<<8)|(1<<0);
/// ------------------------------------------------------------
/// 5.Configure data transfer parameters
/// - Single shot transmission of data word when a valid word
/// is available
/// ------------------------------------------------------------
// TDEN TDSSM
SPI_CH->TCSR =(1 << 10) |(1 << 8);
/// ------------------------------------------------------------
/// 6.Configure SSC protocol-specific parameters
/// - Slave select generation is enabled
/// - Direct slave select mode with inversion is selected
/// - End of frame condition is required for the frame to be
/// considered as finished
/// - SELO0 is selected as the active select signal
/// ------------------------------------------------------------
// SELO SELCTR MSLSEN
SPI_CH->PCR = (1<<16)|(1<<1)|(1<<0);
/// ------------------------------------------------------------
/// 7.Enable SSC protocol
/// ------------------------------------------------------------
// MODE
SPI_CH->CCR =(1 << 0);
}

int main(void)
{

/* Placeholder for user application code. The while loop below can be replaced with user application code. */
/* De-assert USIC2 reset */
SCU_RESET->PRCLR1 |= (1<<8);
/* Initialize USIC channel */
SPI_CH_MasterInit();
/* Initialize USIC output pin functions */
PORT3->IOCR8 |= 0x888888;

while(1U)
{
/* Poll TCSR.TDV bit */
while((SPI_CH->TCSR & 0x80)>>7);
/* Transmit data to slave */
SPI_CH->TBUF[0] = 0x55;
}
}


From your use case description, the master will need to send 2 SPI frames.
The first is to provide the slave with the number (slave transmit data is dummy and can be discarded).
The second is to provide the clock (master transmit data is dummy) to receive the response from the slave.

Hope this helps.

Regards,
Min Wei
0 Likes
Not applicable
Thank you Min Wei for your help.

Yes I am not using Dave apps; I want to learn how to use the registers.
I modified your sample code, since the Arduino is active LOW (change PCR.SELINV = 1b).
It is working, however I don’t understand exactly why;


// MSLSEN - Select Slave
SPI_CH->PCR |= (1<<0);

/* Poll TCSR.TDV bit */
while((SPI_CH->TCSR & 0x80)>>7);
SPI_CH->TBUF[0] = 0x00A0;
while((SPI_CH->TCSR & 0x80)>>7);
SPI_CH->TBUF[0] = 0x00A1;
// MSLSEN – Deselect Slave
SPI_CH->PCR &= (0<<0);


I understand the while loop means do nothing as long as TCSR.TDV = 1. So the program is waiting the end of the transmission and the reset of TCSR.TDV to write another word/frame.
What do you mean by Poll TCSR.TDV bit (sorry for the English, I am not an English native speaker) ?
After the transmission is finished, how the TCSR.TSV bit is reset? If automatically, on which criteria?

Here my code :

/*
* main.c
*/

#include

/* ===================== Global variable declaration ===================== */
uint8_t debug_tcsr_sof;
uint8_t debug_TDV = 0;
uint8_t debug_RBUF01, debug_RBUF10;

/* ===================== Initialisation function ===================== */
void SPI_CH_MasterInit(void)
{

/// ------------------------------------------------------------
/// 1.Enable USICx channel n
/// ------------------------------------------------------------
// Mode Control RM 18.2.2.2, Kernel State Configuration Register RM 18.11.3.3 :
// BPMODEN MODEN
SPI_CH->KSCFG |= (1 << 1)| (1 << 0);
// Run mode 0 : Channel operation as specified, no impact on data transfer

// Channel Control Register RM 18.11.3.1
// Select SSC Mode for USIC Channel 0 : put at 0 for configuration
//SPI_CH->CCR &= (0 << 3)| (0 << 2)| (0 << 1)| (0 << 0);
/// ------------------------------------------------------------


/// ------------------------------------------------------------
/// 2.Configure baud rate generator
/// - Normal divider mode
/// - Baud rate = 250 kbit/s
/// - SCLKCFG = 10b
/// ------------------------------------------------------------
/// ------------------------------------------------------------
// DM = 01b STEP = 1023d = 3FFh = 001111111111b
SPI_CH->FDR = (1 << 14) | ( 2013 << 0);
// SCLKCFG PDIV = 287
SPI_CH->BRG = (2 << 30) | (287 << 16);
/// ------------------------------------------------------------

/// ------------------------------------------------------------
/// 3.Configure input stages
/// - Select input DX0C
/// - Derive input of data shift unit directly from input pin
/// - DSEN = 1
/// ------------------------------------------------------------
// DSEN INSW DSEL = 010b = 2d
SPI_CH->DX0CR = (1 << 6) | (1 << 4) |(2 << 0);
/// ------------------------------------------------------------

/// ------------------------------------------------------------
/// 4.Configure data format
/// - Data word = data frame = 8 bits
/// - Data transfer allowed with passive level = 0 and MSB first
/// ------------------------------------------------------------
// WLE FLE TRM SDIR
SPI_CH->SCTR = (7<<24)|(7<<16)|(1<<8)|(1<<0);
// Not Frame settle
// WLE TRM SDIR
// SPI_CH->SCTR = (7<<24)|(1<<8)|(1<<0);
/// ------------------------------------------------------------


/// ------------------------------------------------------------
/// 5.Configure data transfer parameters
/// - Single shot transmission of data word when a valid word
/// is available
/// ------------------------------------------------------------
// TDEN TDSSM
SPI_CH->TCSR = (1 << 10) |(1 << 8);
/// ------------------------------------------------------------


/// 6.Configure SSC protocol-specific parameters
/// - Slave select generation is enabled
/// - Direct slave select mode with inversion (SELINV = 1) is selected
/// - End of frame condition is required for the frame to be
/// considered as finished
/// - SELO0 is selected as the active select signal
/// ------------------------------------------------------------
// SELO SELINV SELCTR MSLSEN
SPI_CH->PCR = (1<<16)|(1<<2)|(1<<1)|(1<<0);
/// ------------------------------------------------------------


/// 7.Enable SSC protocol
/// ------------------------------------------------------------
// MODE
SPI_CH->CCR =(1 << 0);
}


/* ===================== Main ===================== */
int main(void)
{

/* ===================== Variable declaration ===================== */
uint32_t i,j = 0;

uint8_t a, b, c, d;

uint16_t data_received[9];
uint16_t dummy;
uint8_t cpt = 0;

/* De-assert USIC2 reset */
/* Release reset of USIC module by writing a 1 to the USICxRS bit in SCU_PRCLR0 or SCU_PRCLR1 registers */
SCU_RESET->PRCLR1 |= (1<<8);
/* Initialize USIC channel */
SPI_CH_MasterInit();
/* Initialize USIC output pin functions */
PORT3->IOCR8 |= 0x888888;


/* ============================ LOOP ============================ */
while(1U)
{
/* Slave Select */
// MSLSEN - activate slave
SPI_CH->PCR |= (1<<0);

// counter set at 0 :
cpt = 0;

while((SPI_CH->TCSR & 0x80)>>7);
/* transmit dummy frame to the slave */
SPI_CH->TBUF[0] = 0x0000;
dummy = SPI_CH->RBUF;

/* Poll TCSR.TDV bit */
while((SPI_CH->TCSR & 0x80)>>7);
/* transmit data 10 to the slave */
SPI_CH->TBUF[0] = 0x000A;
while( !( ((SPI_CH->RBUF01SR & 0x4000)>>14) | ((SPI_CH->TCSR & 0x2000)>>13) ) );
data_received[cpt] = SPI_CH->RBUF;
cpt++;

while((SPI_CH->TCSR & 0x80)>>7);
/* transmit dummy frame to the slave */
SPI_CH->TBUF[0] = 0x0000;
while( !( ((SPI_CH->RBUF01SR & 0x4000)>>14) | ((SPI_CH->TCSR & 0x2000)>>13) ) );
data_received[cpt] = SPI_CH->RBUF;
cpt++;


while((SPI_CH->TCSR & 0x0080)>>7);
/* transmit data 17 to the slave */
SPI_CH->TBUF[0] = 0x0011;
/* received previous data sent, so 10 + 15 */
while( !( ((SPI_CH->RBUF01SR & 0x4000)>>14) | ((SPI_CH->TCSR & 0x2000)>>13) ) );
data_received[cpt] = SPI_CH->RBUF;
cpt++;

while((SPI_CH->TCSR & 0x80)>>7);
/* transmit dummy frame to the slave */
SPI_CH->TBUF[0] = 0x0000;
while( !( ((SPI_CH->RBUF01SR & 0x4000)>>14) | ((SPI_CH->TCSR & 0x2000)>>13) ) );
data_received[cpt] = SPI_CH->RBUF;
cpt++;


while((SPI_CH->TCSR & 0x80)>>7);
/* transmit data 33 to the slave */
SPI_CH->TBUF[0] = 0x0021;
while( !( ((SPI_CH->RBUF01SR & 0x4000)>>14) | ((SPI_CH->TCSR & 0x2000)>>13) ) );
data_received[cpt] = SPI_CH->RBUF;
cpt++;

while((SPI_CH->TCSR & 0x80)>>7);
/* transmit dummy frame to the slave */
SPI_CH->TBUF[0] = 0x0000;
while( !( ((SPI_CH->RBUF01SR & 0x4000)>>14) | ((SPI_CH->TCSR & 0x2000)>>13) ) );
data_received[cpt] = SPI_CH->RBUF;
cpt++;


while((SPI_CH->TCSR & 0x80)>>7);
/* transmit data 42 to the slave */
SPI_CH->TBUF[0] = 0x002A;
while( !( ((SPI_CH->RBUF01SR & 0x4000)>>14) | ((SPI_CH->TCSR & 0x2000)>>13) ) );
data_received[cpt] = SPI_CH->RBUF;
cpt++;

while((SPI_CH->TCSR & 0x80)>>7);
/* transmit dummy frame to the slave */
SPI_CH->TBUF[0] = 0x0000;
while( !( ((SPI_CH->RBUF01SR & 0x4000)>>14) | ((SPI_CH->TCSR & 0x2000)>>13) ) );
data_received[cpt] = SPI_CH->RBUF;
cpt++;

while((SPI_CH->TCSR & 0x80)>>7);
/* transmit dummy frame to the slave */
SPI_CH->TBUF[0] = 0x0000;
while( !( ((SPI_CH->RBUF01SR & 0x4000)>>14) | ((SPI_CH->TCSR & 0x2000)>>13) ) );
data_received[cpt] = SPI_CH->RBUF;

dummy = SPI_CH->RBUF;
/* Close SPI connection */
// MSLSEN - deactivate slave
SPI_CH->PCR &= (0<<0);


// wait for 0.5 sec with time machine = 144 MHz
for (i=0; i<73000000; i++)
{
j = i+1;
}
j = 0;
debug_TDV++;
if ( debug_TDV >= 200)
debug_TDV = 0;


/* Close SPI connection */
// MSLSEN - deactivate slave
SPI_CH->PCR &= (0<<0);
}
}




Thank you in advance,
Nicolas
0 Likes
chismo
Employee
Employee
First like received
Hello,

TCSR.TDV flag is used to indicate if there is a valid data in the transmit buffer TBUF, waiting to be transmitted.
Effectively it also indicates when is the next earliest time that you can write the transmit data to the buffer input location TBUF.

The flag:
- is set when the transmit data is written to TBUF and gets loaded into TBUF.
- is cleared when the same data gets further loaded into the transmit shift register TSR.

By polling, it means the code is checking the flag for a specific value.
In this case, the while loop can be exited only when TDV = 0.

Hope this helps.

Regards,
Min Wei
0 Likes