Cobra II How to setup SPI from RLP

I’m struggling to setup SPI on Socket 9 from RLP on the CobraII. I’m using Simon’s EmBlock template but cannot seem to relate the header file to the manual where it’s talking about the ‘IOCON’ registers.

I’ve been through Codeshare looking for examples but could not find ant examples for the G120.

Could someone please point me in the right direction?

I somehow think IOCON==PINCON.

After spending the whole weekend trying to get this one simple step achieved I’m at the point of admitting defeat!

I’m using the following to configure the port on Socket 9

	LPC_SC->PCONP |= 1 <<21;					//enable POWER to SSP0 (redundant following reset)

	LPC_SC->PCLKSEL1 |= 1<<10;				//pclk = cclk  so should now be 120MHz on the G120
	LPC_SSP0->CPSR |= 30;							//Internal divider which should yield 4Mhz SCLK for 120Mhz pclk

	LPC_PINCON->PINSEL0 |= 0x02 << 30;		// Set Pin P0.15 to function 3  = SCLK
	LPC_PINCON->PINSEL1 |= 0x02<<2;			// Set Pin P0.17 to function 3  = MISO
	LPC_PINCON->PINSEL1 |= 0x02<<4;			// Set Pin P0.18 to function 3  = MOSI

	LPC_SSP0->CR0 |= 3<<6;						// CPOL = 1, CPHA = 1
	LPC_SSP0->CR0 |= 7<<0;						// 8 bit transfer
	LPC_SSP0->CR1 |= 1<<1;						//enable SSP

but it still not playing ball.

In desperation I resorted to peeking the contents of the IOCON register values (0x4002C03C, 0x4002C044 & 0x4002C048) for SCLK, MISO & MOSI from the managed side and can see that they do not change from their default values of 0xB0 when I try configuring the port but if I create the port on the managed side the values change to 0xA2.

All I can think is that I must be making a basic error somewhere but I feel like I’m just going around in circles now. :wall:

@ Sprigo - do you set the proper power enable bit?

@ RobvanSchelven - According to Table 16 (page 29) of the manual bit 21 is the power control bit for SSP0 with bit 10 being for SSP1.

@ Sprigo - you are right about the power bit . I have an example using spi1. maybe it helps


 #define SSP1CPSR (*(volatile unsigned long *)(0x40030010))	// SPI1 Clock Prescale Register
 #define SSP1CR0  (*(volatile unsigned long *)(0x40030000))	// SPI1 Control register 0
 #define SSP1CR1  (*(volatile unsigned long *)(0x40030004))	// SPI1 Control register 1
 #define SSP1DR   (*(volatile unsigned long *)(0x40030008))	// SPI1 data register
 #define SSP1IMSC (*(volatile unsigned long *)(0x40030014))	// SPI1 Interrupt Mask Set and Clear Register
 #define SSP1SR   (*(volatile unsigned long *)(0x4003000C))	// SPI1 Status Register


typedef struct
{
	union {
		unsigned long _u32;
		struct {
			unsigned long FUNC:3;
			unsigned long MODE:2;
			unsigned long HYS:1;
			unsigned long INV:1;
			unsigned long b7:1;	// must be set to 1 for normal operation
			unsigned long SLEW:1;
			unsigned long OD:1;
			unsigned long Res2:22;
		};
	};
} IOCON_W;

ypedef struct
{
	union {
		volatile unsigned long _u32;
		struct {
			volatile unsigned long LCD:1;
			volatile unsigned long TIM0:1;
			volatile unsigned long TIM1:1;
			volatile unsigned long UART0:1;
                        volatile unsigned long UART1:1;
			volatile unsigned long PWM0:1;
			volatile unsigned long PWM1:1;
			volatile unsigned long I2C0:1;
			volatile unsigned long UART4:1;
			volatile unsigned long RTC:1;
			volatile unsigned long SPI1:1;
			volatile unsigned long EMC:1;
			volatile unsigned long ADC:1;
			volatile unsigned long CAN1:1;
			volatile unsigned long CAN2:1;
			volatile unsigned long GPIO:1;
			volatile unsigned long RES:1;
			volatile unsigned long MCPWM:1;
			volatile unsigned long QE1:1;
			volatile unsigned long I2C1:1;
			volatile unsigned long SPI2:1;
			volatile unsigned long SPI0:1;
			volatile unsigned long TIM2:1;
			volatile unsigned long TIM3:1;
			volatile unsigned long UART2:1;
			volatile unsigned long UART3:1;
			volatile unsigned long I2C2:1;
			volatile unsigned long I2S:1;
			volatile unsigned long SDCard:1;
			volatile unsigned long GPDMA:1;
			volatile unsigned long ENET:1;
			volatile unsigned long USB:1;
		};
	};
} PCONP_CONTROL;

typedef struct
{
	union {
		volatile unsigned long _u32;
		struct {
			unsigned long DSS:4;	// Data Size Select
			unsigned long FRF:2;	// Frame format
			unsigned long CPOL:1;	// Clock Out Polarity
			unsigned long CPHA:1;	// Clock Out Phase
			unsigned long SCR:1;	// Serial Clock Rate
			unsigned long res:27;	// not used
		};
	};
} SPI_CONTROL_REGISTER_0;



       // Set the functions of the used SPI pins
        IOCON_W iocon_w = {{ 0 }};
	iocon_w.FUNC = 2;
	iocon_w.b7 = 1;
	IOCON_P0_07 = iocon_w._u32; // SPI1 CLK -->
	IOCON_P0_08 = iocon_w._u32; // SPI1 MISO <-- in MCU
	IOCON_P0_09 = iocon_w._u32; // SPI1 MOSI --> out MCU

        PCONP_CONTROL powerControl = { {0} };
	powerControl.SPI1 = 1;
	PCONP |= powerControl._u32;

	// Set SPI1 mode
	SPI_CONTROL_REGISTER_0 spi1Ctrl0 = {{ 0 }};
	spi1Ctrl0.DSS = 7; // 8 bit data, SPI frame format, CPOL = 0, CPHA = 0
	SSP1CR0 = spi1Ctrl0._u32;

	SSP1CPSR = _spi_LowClock;
	SSP1IMSC = 0x00; // disable interrupts



@ Sprigo - I do not have a G120 based device so I cannot actually test this. Looking at the user manual, some of the code you have here does not look correct. Esp. the clock related stuff.

It also looks like you are using the <LPC17xx.h> header file, for this chip I think you should rather be using the <LPC177x_8x.h> header file, then it would give you IOCON structure which is (IMHO) easier to translate from the MCU’s user manual

Try the following.


  LPC_SC->PCONP = (LPC_SC->PCONP & 0xfffeffff) | (1UL <<21);

  LPC_SSP0->CPSR = 2; // Calculate the prescaler (2 - 254, even numbers only)

  // Based on <<LPC177x_8x.h>
  LPC_IOCON->P0_15 = 0x02; // SCK0 
  LPC_IOCON->P0_16 = 0x02; // SSEL0
  LPC_IOCON->P0_17 = 0x02; // MISO0 
  LPC_IOCON->P0_18 = 0x02; // MOSI0

  LPC_SSP0->CR1 = (LPC_SSP0->CR1 & 0x0f) | (1<<1);  // enable SSP

Check the logic for the CPSR in the user manual, this is the divider used to scale the clock, it must be an even value (2 - 254) , 30 is not valid.
http://www.nxp.com/documents/user_manual/UM10470.pdf

2 Likes

@ taylorza - you are a star! :clap: After getting hold of that header it was a breeze.

@ RobvanSchelven - many thanks but taylorza solution seemed a lot easier.

One thing I’m still not clear on is the prescaler values. I understand that they are clock dividers but what is the base clock frequency?

@ Sprigo - Good you made progress !

This is my first foray into RLP and after a whole weekend just going around in circles and being on the verge of scrapping the project (frustration had set in ) the relief at getting this simple step working is fantastic.

@ Sprigo - I am glad that helped.

The peripheral clock (PCLK) drives the peripheral bus (APB). To calculate your SPI frequency you set the Clock Prescale Register (CPSR) to a value that will divide the peripheral clock down to the desired clock frequency for the SPI peripheral, this is the clock that will be used to clock the peripheral, you then further divide this clock by the Serial Clock Rate (SCR) in the CR0 register to yield the final SPI frequency.

You can use the following formula to calculate the SPI frequency

freq = PCLK / (CPSR * (SCR + 1))

You will need to know the PCLK frequency to calculate the desired SPI frequency, I would not recommend you try change this as this has a system wide effect. Either GHI can tell you the frequency or if you have a logic analyzer you can plug in a few values for CPSR and SCR then measure the SPI frequency, you then have everything you need to transpose the formula and calculate what frequency the PCLK is running at. If I had a G120 I would happily do this for you, I just went and added a G120 to my cart, lived long enough without one now…

All the above was gleaned from the user manual, any errors are probably in my interpretation of that information. I referenced the following sections
3.3.22 Peripheral Clock Selection Register
21.6.1 SSPn Control Register 0 (See bits 15:8 SCR it includes the formula above)
21.6.5 SSPn Clock Prescale Register

I hope that helps!

1 Like

Thanks for the information @ taylorza. It was the frequency of PCLK that I couldn’t find anywhere but I didn’t realise that it was set by GHI. Unfortunately I don’t have a logic analyzer so took the rather dubious route of increasing the prescaler until I could communicate and then added a bit more. :whistle:

I must have a trawl of fleaBay and see if I can’t find a cheap logic analyzer.

@ Sprigo - It is a pleasure.

Since you do not have a logic analyzer, you can also read the registers to determine the configuration.

CCLKSEL[4:0] will give you the divide used for the CPU clock, you can use this info to determine the sysclk frequency. I suspect this might be 1, giving a sysclk freq of 120Mhz, but I would double check.

PCLKSEL[4:0] will give you the PCLK divide value that is used to divide the sysclk frequency to give you PCLK.

Btw. I would be interested to know what your findings are if you do the above.

Hope this helps.

@ taylorza - Once again, many thanks.

I get the following values for those registers.

CCLKSEL[4:0] = 1
PCLKSEL[4:0] = 2

So PCLK = 60Mhz

Simples :smiley:

I have already trawled eBay and was surprised at the price of the logic analyzers - no where near as expensive as I was expecting.

1 Like

@ Sprigo - Great, thanks for sharing!

@ Sprigo - Have you any working RLP C code you could share to setup SPI and Read/Write some bytes?
All I need to do is set it up, and then write/read 2 bytes with an G120 based board.

@ Reinhard - I think I’ve stripped it down to all that you require.


 #ifndef DRIVER_H_INCLUDED
 #define DRIVER_H_INCLUDED

// Driver.h file

//                          CobraII X9
// READY     Pin 03            P0.12
// RESET    Pin 04             P1.31
//
// CSEL      Pin 06            P1.05
//
// SCK      Pin 09             P0.15
// MISO     Pin 08             P0.17
// MOSI     Pin 07             P0.18
//


    #include "LPC177x_8x.h"     // This is a better header file for the G120

    // Pin numbers for the Socket in use (X9) - can be obtained by using GHI's Pins and ToString()
    #define READY_PIN    12
    #define RESET_PIN   63
    #define CSEL_PIN     37
    #define SCK_PIN     15
    #define MISO_PIN    17
    #define MOSI_PIN    18

    // Pin 03 - READY
    #define READY_GPIO_SET   RLP->GPIO.EnableInputMode(READY_PIN, RLP_GPIO_RESISTOR_PULLUP)
    // Pin 04 - RESET
    #define RESET_GPIO_SET  RLP->GPIO.EnableOutputMode(RESET_PIN, RLP_TRUE)
    // Pin 06 - CSEL
    #define CSEL_GPIO_SET    RLP->GPIO.EnableOutputMode(CSEL_PIN, RLP_FALSE)
    // Pin 07 - MOSI
    #define MOSI_FUNC_SET   LPC_IOCON->P0_18 = 0x2    // set P0.09 as MOSI
    // Pin 08 - MISO
    #define MISO_FUNC_SET   LPC_IOCON->P0_17 = 0x2    // set P0.08 as MISO
    // Pin 09 - SCK
    #define SCK_FUNC_SET    LPC_IOCON->P0_15 = 0x2   // set P0.07 as SCK

    #define SSEL_FUNC_SET   LPC_IOCON->P0_16 = 0x2  // set PX.XX as SSEL <--- Not using at the moment

    //-----------------------------------------------------------------------------
    //  SSP0 (SPI1) config and control
    //-----------------------------------------------------------------------------
    //
    #define SSP_PWR_ON          (LPC_SC->PCONP |= (1UL << 21))   // power on
    #define SSP_CONTROL0        (LPC_SSP0->CR0)
    #define SSP_CONTROL1        (LPC_SSP0->CR1)
    #define SSP_INTERRUPTMASK   (LPC_SSP0->IMSC)
    #define SSP_CLOCKRATE       (LPC_SSP0->CPSR)
    #define SSP_STATUS          (LPC_SSP0->SR)
    #define SSP_DATAREGISTER    (LPC_SSP0->DR)

    #define SSP_INTERRUPT        14     // TFE interrupt
    #define SSP_INT_OFF    0x00         // disable interrupts
    #define SSP_INT_ON     0x08         // enable TFE interrupt


    #define SSP_CR0        0x07     // set DSS data to 8-bit, Frame format SPI, CPOL = 0, CPHA = 0
    #define SSP_CR1        0x02     // enable in master mode, normal operation

    // PCLK = 60Mhz
    #define SSP_CLKP_RD    15     // clk prescalar for reads
    #define SSP_CLKP_WR    8     // clk prescalar for writes
    #define SSP_CLKP_RST   60     // clk prescalar for reset



    #define SCI_CLOCKF_CFG  0x8800                      
    #define SCI_MODE_CFG    0x0802                      

    #define READY (RLP->GPIO.Readpin(READY_PIN))       //P0.12 is READY high?

     //-----------------------------------------------------------------------------
    //  SSP0 (SPI1) config and control
    //-----------------------------------------------------------------------------
    //

    #define SSP_TFE        (1 << 0) // transmit fifo empty
    #define SSP_TNF        (1 << 1) // transmit fifo not full
    #define SSP_RNE        (1 << 2) // receive fifo empty
    #define SSP_RFF        (1 << 3) // receive fifo full
    #define SSP_BSY        (1 << 4) // SSP0 busy

    #define READY        (RLP->GPIO.Readpin(READY_PIN))       // Is READY high?
    #define RESET_SET   (RLP->GPIO.Writepin(RESET_PIN, RLP_TRUE))
    #define RESET_CLR   (RLP->GPIO.Writepin(RESET_PIN, RLP_FALSE))
    #define CSEL_SET     (RLP->GPIO.Writepin(CSEL_PIN, RLP_TRUE))
    #define CSEL_CLR     (RLP->GPIO.Writepin(CSEL_PIN, RLP_FALSE))

    #define SSP_STATUS_BUSY         (SSP_STATUS & SSP_BSY)
    #define SSP_STATUS_TXNOTFULL     (SSP_STATUS & SSP_TNF)
    #define SSP_STATUS_RXNOTEMPTY   (SSP_STATUS & SSP_RNE)

 #define TASK_INTERVAL	10000	// (uSec)



 #endif /* DRIVER_H_INCLUDED */


And the source file


 #include "RLP.h"

 #include "Driver.h"

RLP_Task bufTask, cancelTask;
int bufCount;
unsigned char bufPass;
unsigned char *buf;
unsigned char DataEnd;


/********************************************************************************************************
 PRIVATE FUNCTIONS
********************************************************************************************************/
static void initPorts()
{
	READY_GPIO_SET;		// set as input
	RESET_GPIO_SET;		// set as output
	CSEL_GPIO_SET;		// set as output

	SCK_FUNC_SET;		// set as SCK
	MISO_FUNC_SET;		// set as MISO
	MOSI_FUNC_SET;		// set as MOSI
	//SSEL_FUNC_SET;      // set as SSEL
}

static void clearRecieveBuffer()
{
 #pragma GCC diagnostic ignored "-Wunused-but-set-variable"
	volatile unsigned char junk;
	unsigned char i;
	for (i = 0 ;  i < 8 ;  i++)
		junk = SSP_DATAREGISTER;
 #pragma GCC diagnostic pop
}

void ssp_Interrupt(void *arg);

static void initSSP1()
{
    SSP_PWR_ON;

	//Set DSS data to 8-bit, Frame format SPI, CPOL = 0, CPHA = 0
    SSP_CONTROL0 = SSP_CR0;
    SSP_CLOCKRATE = SSP_CLKP_RST; // Clock prescaler for reset
    SSP_CONTROL1 |= SSP_CR1;    // Enable SSP
	SSP_INTERRUPTMASK = SSP_INT_OFF;	// disable interrupts

	clearRecieveBuffer();

	// install SSP1 interrupt
	RLP->Interrupt.Install(SSP_INTERRUPT, ssp_Interrupt, (void*)0);

	// enable interrupt
	RLP->Interrupt.Enable(SSP_INTERRUPT);
}

static unsigned char readWriteByte(unsigned char data)
{
	while (!SSP_STATUS_TXNOTFULL);
	SSP_DATAREGISTER = data;

	while (!SSP_STATUS_RXNOTEMPTY);
	return (SSP_DATAREGISTER);
}

static void writeRegister(unsigned char reg, unsigned short data)
{

	CSEL_CLR;
	while (!(READY));

	readWriteByte(0x02);
	readWriteByte(reg);
	readWriteByte(data >> 8);
	readWriteByte(data);

	CSEL_SET;
}

static unsigned short readRegister(unsigned char reg)
{
	while (SSP_STATUS_BUSY);

	SSP_CLOCKRATE = SSP_CLKP_RD;	//clock prescaler 10
	clearRecieveBuffer();

	CSEL_CLR;
	while (!(READY));

	readWriteByte(0x03);
	readWriteByte(reg);

	unsigned short result = readWriteByte(0x00) << 8;
	result |= readWriteByte(0x00);

	CSEL_SET;

	while (SSP_STATUS_BUSY);

    SSP_CLOCKRATE = SSP_CLKP_WR;

	return result;
}

/*******************************************************************************************************************
 SSP  FIFO-HALF-EMPTY ISR
*******************************************************************************************************************/
void ssp_Interrupt(void *arg)
{
	if (bufCount == 0)
	{
		// disable interrupt
		SSP_INTERRUPTMASK = SSP_INT_OFF;

		// notify that we're ready for more data
		RLP->PostManagedEvent(0X01);
		return;
	}

	if (bufPass == 0 && !(READY))
	{   
		SSP_INTERRUPTMASK = SSP_INT_OFF;                                    // disable interrupt
		RLP->Task.ScheduleTimeOffset(&bufTask, TASK_INTERVAL);   // check back later
		return;
	}

	// send data
	switch (bufCount)
	{
		case 1:
			SSP_DATAREGISTER = *buf++;
			bufCount--;
			break;
		case 2:
			SSP_DATAREGISTER = *buf++;
			SSP_DATAREGISTER = *buf++;
			bufCount -= 2;
			break;
		case 3:
			SSP_DATAREGISTER = *buf++;
			SSP_DATAREGISTER = *buf++;
			SSP_DATAREGISTER = *buf++;
			bufCount -= 3;
			break;
		default:
			SSP_DATAREGISTER = *buf++;
			SSP_DATAREGISTER = *buf++;
			SSP_DATAREGISTER = *buf++;
			SSP_DATAREGISTER = *buf++;
			bufCount -= 4;
			break;
	}
		bufPass = (bufPass + 1) & 0x07;

}

/********************************************************************************************************
 TASK FUNCTIONS
********************************************************************************************************/
void G120SPI_CancelTask(void *arg)
{
	//no more data coming?
	if (DataEnd && bufCount == 0)
	{
		if (G120SPI_CancelDataStream())
			//notify that the streaming could not be canceled ok
			RLP->PostManagedEvent(0x02);
	}
	else
		//recheck later
		RLP->Task.ScheduleTimeOffset(&cancelTask, TASK_INTERVAL);
}

void G120SPI_Task(void *arg)
{
	if (READY)
		SSP_INTERRUPTMASK = 0x08; //enable TX fifo half empty interrupt
	else
		//recheck later
		RLP->Task.ScheduleTimeOffset(&bufTask, TASK_INTERVAL);
}


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

/********************************************************************************************************
 PUBLIC FUNCTIONS
********************************************************************************************************/


int G120SPI_DataEnd(void **args)
{
	DataEnd = 1;
	RLP->Task.ScheduleTimeOffset(&cancelTask, TASK_INTERVAL); //recheck later

	return 0;
}


int G120SPI_Init(void **args)
{
    unsigned short value;

	if (RLP->magic != RLP_EXT_MAGIC) return 0X29;

	bufCount = 0;

	initPorts();
	initSSP1();

	// do hard reset
	RESET_CLR;
	RLP->Delay(5000); //wait 5ms
	RESET_SET;


	RLP->Task.Initialize(&cancelTask, G120SPI_CancelTask, 0, RLP_FALSE);
	RLP->Task.Initialize(&bufTask, G120SPI_Task, 0, RLP_FALSE);

	return 0;
}


4 Likes

@ Sprigo - Than you a lot, I think this will save me a lot of try and error.
I’ll report back how it went.