Explain the SPI peripheral's MSTIDLE flag for LPC84x

cancel
Showing results for 
Search instead for 
Did you mean: 

Explain the SPI peripheral's MSTIDLE flag for LPC84x

616 Views
houe
Contributor I

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:

  1. wait for any previous commands to finish
  2. set gpio for command
  3. transmit a command byte
  4. wait for the command byte to finish
  5. set gpio for data
  6. send the data byte

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).

Labels (2)
0 Kudos
2 Replies

386 Views
kerryzhou
NXP TechSupport
NXP TechSupport

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:

pastedImage_2.png

  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!
-----------------------------------------------------------------------------------------------------------------------

0 Kudos

386 Views
houe
Contributor I

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

0 Kudos