I am writing a SPI driver for the LPC84x line of micros. I may have a misunderstanding of the how the peripheral works in one aspect and need some clarification. First the SPI function in question:
static void LCD_Madctl(unsigned value)
{
// wait for any previous commands to finish - this WORKS!
while ((LPC_SPI1->STAT & STAT_MSTIDLE) == 0);
// set command/data gpio to command
LCD_SetCommand();
// send the MADCTL command byte
LPC_SPI1->TXDATCTL = TXCTL_TXSSEL0 | TXCTL_TXSSEL1_N | TXCTL_TXSSEL2_N
| TXCTL_TXSSEL3_N | TXCTL_RXIGNORE| TXCTL_LEN8 | SITRONIX_CMD_MADCTL;
// wait for command byte to finish transferring - this HANGS!
while ((LPC_SPI1->STAT & STAT_MSTIDLE) == 0);
// set command/data gpio to data
LCD_SetData();
// send the data byte
LPC_SPI1->TXDATCTL = TXCTL_TXSSEL0 | TXCTL_TXSSEL1_N | TXCTL_TXSSEL2_N
| TXCTL_TXSSEL3_N | TXCTL_EOT | TXCTL_RXIGNORE | TXCTL_LEN8
| (value & 0xff);
}
This code is trying to do the following sequence:
However, after sending the command byte the code waiting for the MSTIDLE flag (step 4) hangs.
The documentation for this bit reads as follows:
MSTIDLE - Master idle status flag. This bit is 1 whenever the SPI master function is fully
idle. This means that the transmit holding register is empty and the transmitter
is not in the process of sending data.
So, is my code working as one would expect? I think not - someone please correct if I'm missing something.
From test code MSTIDLE seems to only get set after chip select is released. Is this correct? It would seem one of two things is incorrect: Either the documentation should mention the chip select must be released prior to this bit being set or maybe there a bug in the silicon of the LPC84x?!?
As a work around I was able to not ignore the rx of the command byte and trigger off the RXRDY flag to know when its safe to change the command/data line.
SIDE NOTE: It doesn't seem likely that chip select should be part of MSTIDLE. its not mentioned in the documentation 2. why mention transmit holding register and transmitter being idle since these obviously has to happen prior to releasing the chip select. 3. There is already a SSD flag that would seem to provide that functionality (except it has to be cleared in software).
Hi Michael boyko,
Do you use the GPIO to control the slave SSEL instead of the SPI hardware SSEL?
If yes, I recommend you refer to our the official LPC845 SPI code, use the TXCTL, TXDAT to transfer the data.
The official code you can refer to the code bundle which can be downloaded from this link:
LPC84x 30MHz|Arm® Cortex®-M0+|32-bit Microcontrollers (MCUs)|NXP
Under Lab and test software.
The idle should relate to the Slave Select function, please check this description in the user manual:
Please refer to the official code:
int main(void) {
unsigned char temp;
// Configure the debug uart (see Serial.c)
setup_debug_uart();
// Enable clocks to relevant peripherals
LPC_SYSCON->SYSAHBCLKCTRL[0] |= (SPI0|SWM);
// Configure the SWM (see peripherals_lib and swm.h)
ConfigSWM(SPI0_SCK, SCK_PIN);
ConfigSWM(SPI0_MOSI, MOSI_PIN);
ConfigSWM(SPI0_SSEL0, SSEL_PIN);
// Give SPI0 a reset
LPC_SYSCON->PRESETCTRL[0] &= (SPI0_RST_N);
LPC_SYSCON->PRESETCTRL[0] |= ~(SPI0_RST_N);
// Enable main_clk as function clock to SPI
LPC_SYSCON->SPI0CLKSEL = FCLKSEL_MAIN_CLK;
// Get main_clk frequency
SystemCoreClockUpdate();
// Configure the SPI master's clock divider (value written to DIV divides by value+1)
LPC_SPI0->DIV = (main_clk/SPIBAUD) - 1;
// Configure the CFG register:
// Enable=true, master, no LSB first, CPHA=0, CPOL=0, no loop-back, SSEL active low
LPC_SPI0->CFG = CFG_ENABLE | CFG_MASTER;
// Configure the SPI delay register (DLY)
// Pre-delay = 0 clocks, post-delay = 0 clocks, frame-delay = 0 clocks, transfer-delay = 0 clocks
LPC_SPI0->DLY = 0x0000;
// Configure the SPI control register
// Master: End-of-frame true, End-of-transfer true, RXIGNORE true, LEN 8 bits.
LPC_SPI0->TXCTL = CTL_EOF | CTL_EOT | CTL_RXIGNORE | CTL_LEN(8);
Config_LED(LED_GREEN);
while(1) {
LED_On(LED_GREEN);
// Prompt user to select a low-power mode for the slave
temp = GetConsoleCharacter((const char *)&promptstring);
// Wait for TXRDY on SPI0
while (!(LPC_SPI0->STAT & STAT_TXRDY));
// Accept '0' '1' or '2' only
if (temp >= 0x30 && temp <= 0x32) {
LPC_SPI0->TXDAT = temp; // Transmit the '0', '1', or '2' character to put the slave board to sleep
}
else {
LPC_SPI0->TXDAT = 0x30; // Default is Sleep mode if garbage was entered
}
LED_Off(LED_GREEN);
// Prompt user, wait for 'Esc' before proceeding
DebugWaitEsc();
// Wait for TXRDY on SPI0
while (!(LPC_SPI0->STAT & STAT_TXRDY));
// Send a wake-up byte (value is irrelevant)
LPC_SPI0->TXDAT = 0x69;
} // end of while(1)
} // end of main
Wish it helps you!
If you still have question about it, please kindly let me know.
Have a great day,
Kerry
-----------------------------------------------------------------------------------------------------------------------
Note: If this post answers your question, please click the Correct Answer button. Thank you!
-----------------------------------------------------------------------------------------------------------------------
Kerry,
Thanks for replying. I am using the SSEL pin controlled by the SPI peripheral. It probably wasn't clear from my original post but the particular SPI device I am talking with needs another digital pin that determines if the byte transmitted is a command or data for previously transmitted command. Therefore it is necessary to transmit the command byte and wait until it has finished transmitting prior to changing the GPIO to indicate data. In particular I'm interested in the behavior of the MSTIDLE flag which your example code does not use at all. I am still having this issue.
Thanks,
Michael