While testing the SPI clock modes I measured the following graphs with my logic analyzer:
SPI clock mode 0:
SPI clock mode 1:
SPI clock mode 2:
SPI clock mode 3:
My SPI settings:
Master Mode
MSB first
Data Length: 8 Bit
I used no high level functions to start the transfer.
The code used is mainly from the periph_spi_sm_int example in the LPCopen lib.
#define LPC_SPIMASTERPORT LPC_SPI1
static void setupSPIMaster(void)
{
SPI_CFGSETUP_T spiSetup;
//SPIM_DELAY_CONFIG_T masterDelay;
/* Initialize SPI controller */
Chip_SPI_Init(LPC_SPIMASTERPORT);
/* Call to initialize first SPI controller for mode0, master mode,
MSB first */
Chip_SPI_Enable(LPC_SPIMASTERPORT);
spiSetup.master = 1;
spiSetup.lsbFirst = 0; //0=MSB first
spiSetup.mode = SPI_CLOCK_MODE3;
Chip_SPI_ConfigureSPI(LPC_SPIMASTERPORT, &spiSetup);
/* Setup master controller SSEL0 for active low select */
//Chip_SPI_SetCSPolLow(LPC_SPIMASTERPORT, 0);
/* Setup master clock rate, slave clock doesn't need to be setup */
Chip_SPIM_SetClockRate(LPC_SPIMASTERPORT, LPCMASTERCLOCKRATE);
//set SPI transmit length to 8 bit
Chip_SPI_SetXferSize(LPC_SPIMASTERPORT, 8);
}
To write a byte out by SPI I used the following code:
//write single byte to SPI1
while(!(Chip_SPI_GetStatus(LPC_SPI1) & (SPI_STAT_TXRDY) ) ); //wait until ready to transmit
Chip_SPI_WriteTXData(LPC_SPI1, 0x02); //write 0x02 to SPI1
//read single byte from SPI1
while((Chip_SPI_GetStatus(LPC_SPI1) & (SPI_STAT_RXRDY) ) ); //wait until one byte received
spiValue = Chip_SPI_ReadRXData(LPC_SPI1);
In my opinion the SPI clock Mode 1 and 3 are correct working.
However the Mode 0 and 2 are not working correct.
In Mode 0 the clock should stay low in idle state and in Mode 2 it should stay high.
User manual UM10850 (LPC54102)
I set the slave select (chip select) by my selfe. I didn't want to use the slave select automatic.
Did I configure something wrong?
解決済! 解決策の投稿を見る。
Thank you for your reply.
The SPI clock modes seem to work as expected. My posted pictures of the signals are correct.
Misleading was, that in clock mode 0 the clock stays high before the next byte starts. This happens due to "data stalls". The clock goes only low, if the next byte is ready to be transmitted.
I coded a minimal example without the use of the high-level callbacks for SPI data transfer.
Description of the SPI module of this microcontroller:
If the SSEL Assert is enabled, the SSEL is pulled active (low) if a data byte is written out on SPI. Additional bytes can be written the same way.
- If you know the when the last byte is sent, you can set the "end of transfer" bit (EOT in the TXCTL register). Then the SSEL is deactivated (high) after the transmission of the last byte.
-If you don't know when the last byte is sent, you can later force the SSEL to be pulled low by setting the "END TRANSFER" bit (END TRANSFER in STAT register).
If you force the "END TRANSFER" you have to wait until the SSEL is deassert (watch on "slave select deassert" bit in STAT register).
Result of the example:
The example project (LPCXpresso) is attached at the end of this post.
Source Code:
/*
* @brief SPI Example for LPC54102 (taken from LPCopen periph_spi_sm_int example)
*
* @note
* Copyright(C) NXP Semiconductors, 2014
* All rights reserved.
*
* @note
* Copyright(C) Simon Wyss, 2017
* Mail: blog (at) simonwyss.com
*
* @par
* Software that is described herein is for illustrative purposes only
* which provides customers with programming information regarding the
* LPC products. This software is supplied "AS IS" without any warranties of
* any kind, and NXP Semiconductors and its licensor disclaim any and
* all warranties, express or implied, including all implied warranties of
* merchantability, fitness for a particular purpose and non-infringement of
* intellectual property rights. NXP Semiconductors assumes no responsibility
* or liability for the use of the software, conveys no license or rights under any
* patent, copyright, mask work right, or any other intellectual property rights in
* or to any products. NXP Semiconductors reserves the right to make changes
* in the software without notification. NXP Semiconductors also makes no
* representation or warranty that such application will be suitable for the
* specified use without further testing or modification.
*
* @par
* Permission to use, copy, modify, and distribute this software and its
* documentation is hereby granted, under NXP Semiconductors' and its
* licensor's relevant copyrights in the software, without fee, provided that it
* is used in conjunction with NXP Semiconductors microcontrollers. This
* copyright, permission, and disclaimer notice must appear in all copies of
* this code.
*
*
*
*/
#include "board.h"
/*****************************************************************************
* Private types/enumerations/variables
****************************************************************************/
#define LOW 0
#define HIGH 1
#define OUTPUT 1
#define INPUT 0
/*****************************************************************************
* Public types/enumerations/variables
****************************************************************************/
/* Board specific example setup */
#define LPC_SPIMASTERPORT LPC_SPI1
#define LPCMASTERCLOCKRATE 10000 //10kHz for Debug (4000000)
/*****************************************************************************
* Private functions
****************************************************************************/
/* Initializes pin muxing for SPI1 interface - note that SystemInit() may
already setup your pin muxing at system startup */
static void Init_SPI_PinMux(void)
{
#if defined(BOARD_NXP_LPCXPRESSO_54102)
/* SPI1 (master) */
Chip_IOCON_PinMuxSet(LPC_IOCON, 1, 6, (IOCON_FUNC2 | IOCON_MODE_PULLUP | IOCON_DIGITAL_EN)); /* SPI1_SCK */
Chip_IOCON_PinMuxSet(LPC_IOCON, 1, 7, (IOCON_FUNC2 | IOCON_MODE_PULLUP | IOCON_DIGITAL_EN)); /* SPI1_MOSI */
Chip_IOCON_PinMuxSet(LPC_IOCON, 1, 14, (IOCON_FUNC4 | IOCON_MODE_PULLUP | IOCON_DIGITAL_EN)); /* SPI1_MISO */
Chip_IOCON_PinMuxSet(LPC_IOCON, 1, 15, (IOCON_FUNC4 | IOCON_MODE_PULLUP | IOCON_DIGITAL_EN)); // GPIO CS for RFM95, FUNC4 for SSL
#else
#warning "Board pin muxing not available!"
#endif
}
static void setupSPIMaster(void)
{
SPI_CFGSETUP_T spiSetup;
//SPIM_DELAY_CONFIG_T masterDelay;
/* Initialize SPI controller */
Chip_SPI_Init(LPC_SPIMASTERPORT);
/* Call to initialize first SPI controller for mode0, master mode,
MSB first */
Chip_SPI_Enable(LPC_SPIMASTERPORT);
spiSetup.master = 1;
spiSetup.lsbFirst = 0; //0=MSB first
spiSetup.mode = SPI_CLOCK_MODE0;
Chip_SPI_ConfigureSPI(LPC_SPIMASTERPORT, &spiSetup);
//pSPI->TXCTRL = SPI_TXDATCTL_DEASSERT_ALL; //configured in Chip_SPI_ConfigureSPI
/* Setup master controller SSEL0 for active low select */
Chip_SPI_SetCSPolLow(LPC_SPIMASTERPORT, 0);
/* Setup master clock rate, slave clock doesn't need to be setup */
Chip_SPIM_SetClockRate(LPC_SPIMASTERPORT, LPCMASTERCLOCKRATE);
//masterDelay.PreDelay = 0xD;
//masterDelay.PostDelay = 0xD;
//masterDelay.FrameDelay = 0xD;
//masterDelay.TransferDelay = 0xD;
//Chip_SPIM_DelayConfig(LPC_SPIMASTERPORT, &masterDelay); //delay unused when CS is forced low by the user
//set SPI transmit length to 8 bit
Chip_SPI_SetXferSize(LPC_SPIMASTERPORT, 8);
Chip_SPIM_AssertSSEL(LPC_SPIMASTERPORT, 0);
//SPI interrupts not used
/* For the SPI controller configured in master mode, enable SPI master interrupts
for interrupt service. Do not enable SPI_INTENSET_TXDYEN. */
//Chip_SPI_EnableInts(LPC_SPIMASTERPORT, (SPI_INTENSET_RXDYEN | SPI_INTENSET_RXOVEN | SPI_INTENSET_TXUREN | SPI_INTENSET_SSAEN |
// SPI_INTENSET_SSDEN));
//Chip_SPIM_AssertSSEL(LPC_SPIMASTERPORT, 0);
//Chip_SPIM_DeAssertSSEL(LPC_SPIMASTERPORT, 0);
}
/*****************************************************************************
* Public functions
****************************************************************************/
uint8_t SPI_write(uint8_t spi_tx){
//write single byte to SPI1
while(!(Chip_SPI_GetStatus(LPC_SPI1) & (SPI_STAT_TXRDY) ) ); //wait until TX buffer empty
Chip_SPI_WriteTXData(LPC_SPI1, spi_tx);
//read single byte from SPI1
while(!(Chip_SPI_GetStatus(LPC_SPI1) & (SPI_STAT_RXRDY) ) ); //wait until RX buffer full
return Chip_SPI_ReadRXData(LPC_SPI1);
}
/* main function (C entry point) */
int main(void)
{
/**** Define local variables for the application ***/
uint32_t Cnt;
int loop = 1; /* Used to avoid unreachable statement warning */
/* Generic Initialization */
SystemCoreClockUpdate();
/* Board_Init calls Chip_GPIO_Init and enables GPIO clock if needed,
Chip_GPIO_Init is not called again */
Board_Init(); //sets LED GPIO as output
//setup SPI PinMux
Init_SPI_PinMux();
/* Setup SPI controllers */
setupSPIMaster();
//SSEL0 is asserted automatically before first byte is clocked out with SPI
//SSEL0 goes LOW
SPI_write(0x02);
SPI_write(0x03);
SPI_write(0x04);
//force deassert of SSEL0
Chip_SPIM_ForceEndOfTransfer(LPC_SPIMASTERPORT); //force End of Transfer -> SSEL should go HIGH
while((Chip_SPI_GetStatus(LPC_SPIMASTERPORT) & SPI_STAT_SSD) == 0); //wait until SSEL is HIGH
Chip_SPI_ClearStatus(LPC_SPIMASTERPORT, SPI_STAT_SSD | SPI_STAT_SSA); //clear SSD and SSA state, that next SSD can be detected in this function
//clear of SSA Flag is not necessary for next "force deassert of SSEL0"
//SSEL0 is HIGH
//SSEL0 goes LOW
SPI_write(0x05);
SPI_write(0x06);
SPI_write(0x07);
Chip_SPIM_ForceEndOfTransfer(LPC_SPIMASTERPORT);
while((Chip_SPI_GetStatus(LPC_SPIMASTERPORT) & SPI_STAT_SSD) == 0);
Chip_SPI_ClearStatus(LPC_SPIMASTERPORT, SPI_STAT_SSD | SPI_STAT_SSA);
//SSEL0 is HIGH
//SSEL0 goes LOW ...
SPI_write(0x08);
SPI_write(0x09);
Chip_SPIM_ForceEndOfTransfer(LPC_SPIMASTERPORT);
while((Chip_SPI_GetStatus(LPC_SPIMASTERPORT) & SPI_STAT_SSD) == 0);
Chip_SPI_ClearStatus(LPC_SPIMASTERPORT, SPI_STAT_SSD | SPI_STAT_SSA);
//repeat SPI write
while (loop) {
Board_LED_Toggle(0); //blink LED
for (Cnt = 0; Cnt < (BOARD_MAINCLOCKRATE / 50); Cnt++) __NOP(); //wait some time (approximate 0.2 seconds)
//SPI write
SPI_write(0x0A);
SPI_write(0x0B);
//force deassert of SSEL0
Chip_SPIM_ForceEndOfTransfer(LPC_SPIMASTERPORT);
while((Chip_SPI_GetStatus(LPC_SPIMASTERPORT) & SPI_STAT_SSD) == 0);
Chip_SPI_ClearStatus(LPC_SPIMASTERPORT, SPI_STAT_SSD | SPI_STAT_SSA);
}
return 0;
}
Hi Simon Wyss,
What the code you are testing?
I have test this code:lpc5410x_xpresso54102_keil_iar_v3.03.000_16\lpc5410x\prj_xpresso54102\iar\periph_spi_int
Download link:
http://www.nxp.com/assets/downloads/data/en/software/lpc5410x_xpresso54102_keil_iar_v3.03.000_16.zip
board is lpcxpresso lpc54102
1. mode 0:
2.mode 1
3.mode 2:
wave:
4. mode 3
Compare with user manual:
It totally meets the demand.
You can test the same project as me on your side again.
Wish it helps you!
Have a great day,
Kerry
-----------------------------------------------------------------------------------------------------------------------
Note: If this post answers your question, please click the Correct Answer button. Thank you!
-----------------------------------------------------------------------------------------------------------------------
Thank you for your reply.
The SPI clock modes seem to work as expected. My posted pictures of the signals are correct.
Misleading was, that in clock mode 0 the clock stays high before the next byte starts. This happens due to "data stalls". The clock goes only low, if the next byte is ready to be transmitted.
I coded a minimal example without the use of the high-level callbacks for SPI data transfer.
Description of the SPI module of this microcontroller:
If the SSEL Assert is enabled, the SSEL is pulled active (low) if a data byte is written out on SPI. Additional bytes can be written the same way.
- If you know the when the last byte is sent, you can set the "end of transfer" bit (EOT in the TXCTL register). Then the SSEL is deactivated (high) after the transmission of the last byte.
-If you don't know when the last byte is sent, you can later force the SSEL to be pulled low by setting the "END TRANSFER" bit (END TRANSFER in STAT register).
If you force the "END TRANSFER" you have to wait until the SSEL is deassert (watch on "slave select deassert" bit in STAT register).
Result of the example:
The example project (LPCXpresso) is attached at the end of this post.
Source Code:
/*
* @brief SPI Example for LPC54102 (taken from LPCopen periph_spi_sm_int example)
*
* @note
* Copyright(C) NXP Semiconductors, 2014
* All rights reserved.
*
* @note
* Copyright(C) Simon Wyss, 2017
* Mail: blog (at) simonwyss.com
*
* @par
* Software that is described herein is for illustrative purposes only
* which provides customers with programming information regarding the
* LPC products. This software is supplied "AS IS" without any warranties of
* any kind, and NXP Semiconductors and its licensor disclaim any and
* all warranties, express or implied, including all implied warranties of
* merchantability, fitness for a particular purpose and non-infringement of
* intellectual property rights. NXP Semiconductors assumes no responsibility
* or liability for the use of the software, conveys no license or rights under any
* patent, copyright, mask work right, or any other intellectual property rights in
* or to any products. NXP Semiconductors reserves the right to make changes
* in the software without notification. NXP Semiconductors also makes no
* representation or warranty that such application will be suitable for the
* specified use without further testing or modification.
*
* @par
* Permission to use, copy, modify, and distribute this software and its
* documentation is hereby granted, under NXP Semiconductors' and its
* licensor's relevant copyrights in the software, without fee, provided that it
* is used in conjunction with NXP Semiconductors microcontrollers. This
* copyright, permission, and disclaimer notice must appear in all copies of
* this code.
*
*
*
*/
#include "board.h"
/*****************************************************************************
* Private types/enumerations/variables
****************************************************************************/
#define LOW 0
#define HIGH 1
#define OUTPUT 1
#define INPUT 0
/*****************************************************************************
* Public types/enumerations/variables
****************************************************************************/
/* Board specific example setup */
#define LPC_SPIMASTERPORT LPC_SPI1
#define LPCMASTERCLOCKRATE 10000 //10kHz for Debug (4000000)
/*****************************************************************************
* Private functions
****************************************************************************/
/* Initializes pin muxing for SPI1 interface - note that SystemInit() may
already setup your pin muxing at system startup */
static void Init_SPI_PinMux(void)
{
#if defined(BOARD_NXP_LPCXPRESSO_54102)
/* SPI1 (master) */
Chip_IOCON_PinMuxSet(LPC_IOCON, 1, 6, (IOCON_FUNC2 | IOCON_MODE_PULLUP | IOCON_DIGITAL_EN)); /* SPI1_SCK */
Chip_IOCON_PinMuxSet(LPC_IOCON, 1, 7, (IOCON_FUNC2 | IOCON_MODE_PULLUP | IOCON_DIGITAL_EN)); /* SPI1_MOSI */
Chip_IOCON_PinMuxSet(LPC_IOCON, 1, 14, (IOCON_FUNC4 | IOCON_MODE_PULLUP | IOCON_DIGITAL_EN)); /* SPI1_MISO */
Chip_IOCON_PinMuxSet(LPC_IOCON, 1, 15, (IOCON_FUNC4 | IOCON_MODE_PULLUP | IOCON_DIGITAL_EN)); // GPIO CS for RFM95, FUNC4 for SSL
#else
#warning "Board pin muxing not available!"
#endif
}
static void setupSPIMaster(void)
{
SPI_CFGSETUP_T spiSetup;
//SPIM_DELAY_CONFIG_T masterDelay;
/* Initialize SPI controller */
Chip_SPI_Init(LPC_SPIMASTERPORT);
/* Call to initialize first SPI controller for mode0, master mode,
MSB first */
Chip_SPI_Enable(LPC_SPIMASTERPORT);
spiSetup.master = 1;
spiSetup.lsbFirst = 0; //0=MSB first
spiSetup.mode = SPI_CLOCK_MODE0;
Chip_SPI_ConfigureSPI(LPC_SPIMASTERPORT, &spiSetup);
//pSPI->TXCTRL = SPI_TXDATCTL_DEASSERT_ALL; //configured in Chip_SPI_ConfigureSPI
/* Setup master controller SSEL0 for active low select */
Chip_SPI_SetCSPolLow(LPC_SPIMASTERPORT, 0);
/* Setup master clock rate, slave clock doesn't need to be setup */
Chip_SPIM_SetClockRate(LPC_SPIMASTERPORT, LPCMASTERCLOCKRATE);
//masterDelay.PreDelay = 0xD;
//masterDelay.PostDelay = 0xD;
//masterDelay.FrameDelay = 0xD;
//masterDelay.TransferDelay = 0xD;
//Chip_SPIM_DelayConfig(LPC_SPIMASTERPORT, &masterDelay); //delay unused when CS is forced low by the user
//set SPI transmit length to 8 bit
Chip_SPI_SetXferSize(LPC_SPIMASTERPORT, 8);
Chip_SPIM_AssertSSEL(LPC_SPIMASTERPORT, 0);
//SPI interrupts not used
/* For the SPI controller configured in master mode, enable SPI master interrupts
for interrupt service. Do not enable SPI_INTENSET_TXDYEN. */
//Chip_SPI_EnableInts(LPC_SPIMASTERPORT, (SPI_INTENSET_RXDYEN | SPI_INTENSET_RXOVEN | SPI_INTENSET_TXUREN | SPI_INTENSET_SSAEN |
// SPI_INTENSET_SSDEN));
//Chip_SPIM_AssertSSEL(LPC_SPIMASTERPORT, 0);
//Chip_SPIM_DeAssertSSEL(LPC_SPIMASTERPORT, 0);
}
/*****************************************************************************
* Public functions
****************************************************************************/
uint8_t SPI_write(uint8_t spi_tx){
//write single byte to SPI1
while(!(Chip_SPI_GetStatus(LPC_SPI1) & (SPI_STAT_TXRDY) ) ); //wait until TX buffer empty
Chip_SPI_WriteTXData(LPC_SPI1, spi_tx);
//read single byte from SPI1
while(!(Chip_SPI_GetStatus(LPC_SPI1) & (SPI_STAT_RXRDY) ) ); //wait until RX buffer full
return Chip_SPI_ReadRXData(LPC_SPI1);
}
/* main function (C entry point) */
int main(void)
{
/**** Define local variables for the application ***/
uint32_t Cnt;
int loop = 1; /* Used to avoid unreachable statement warning */
/* Generic Initialization */
SystemCoreClockUpdate();
/* Board_Init calls Chip_GPIO_Init and enables GPIO clock if needed,
Chip_GPIO_Init is not called again */
Board_Init(); //sets LED GPIO as output
//setup SPI PinMux
Init_SPI_PinMux();
/* Setup SPI controllers */
setupSPIMaster();
//SSEL0 is asserted automatically before first byte is clocked out with SPI
//SSEL0 goes LOW
SPI_write(0x02);
SPI_write(0x03);
SPI_write(0x04);
//force deassert of SSEL0
Chip_SPIM_ForceEndOfTransfer(LPC_SPIMASTERPORT); //force End of Transfer -> SSEL should go HIGH
while((Chip_SPI_GetStatus(LPC_SPIMASTERPORT) & SPI_STAT_SSD) == 0); //wait until SSEL is HIGH
Chip_SPI_ClearStatus(LPC_SPIMASTERPORT, SPI_STAT_SSD | SPI_STAT_SSA); //clear SSD and SSA state, that next SSD can be detected in this function
//clear of SSA Flag is not necessary for next "force deassert of SSEL0"
//SSEL0 is HIGH
//SSEL0 goes LOW
SPI_write(0x05);
SPI_write(0x06);
SPI_write(0x07);
Chip_SPIM_ForceEndOfTransfer(LPC_SPIMASTERPORT);
while((Chip_SPI_GetStatus(LPC_SPIMASTERPORT) & SPI_STAT_SSD) == 0);
Chip_SPI_ClearStatus(LPC_SPIMASTERPORT, SPI_STAT_SSD | SPI_STAT_SSA);
//SSEL0 is HIGH
//SSEL0 goes LOW ...
SPI_write(0x08);
SPI_write(0x09);
Chip_SPIM_ForceEndOfTransfer(LPC_SPIMASTERPORT);
while((Chip_SPI_GetStatus(LPC_SPIMASTERPORT) & SPI_STAT_SSD) == 0);
Chip_SPI_ClearStatus(LPC_SPIMASTERPORT, SPI_STAT_SSD | SPI_STAT_SSA);
//repeat SPI write
while (loop) {
Board_LED_Toggle(0); //blink LED
for (Cnt = 0; Cnt < (BOARD_MAINCLOCKRATE / 50); Cnt++) __NOP(); //wait some time (approximate 0.2 seconds)
//SPI write
SPI_write(0x0A);
SPI_write(0x0B);
//force deassert of SSEL0
Chip_SPIM_ForceEndOfTransfer(LPC_SPIMASTERPORT);
while((Chip_SPI_GetStatus(LPC_SPIMASTERPORT) & SPI_STAT_SSD) == 0);
Chip_SPI_ClearStatus(LPC_SPIMASTERPORT, SPI_STAT_SSD | SPI_STAT_SSA);
}
return 0;
}