In most cases I'm not able to get LPSPI to work properly on RT1024.
Update: Problem was due to a nasty LPSPI driver bug explained below.
Best Regards, Dave
Background
4 different sensors on LPSPI bus 4. Trying to validate operation by reading chip IDs from each sensor.
One sensor (ND120) I can read from properly, so the pin configuration, clocks, hardware seem all OK.
The other 3 sensors I can only operate using "continuous mode" which is not what I need in this case.
Code
Here's the ND120 code that operates AOK:
typedef struct {
// normal read is just 32-bit pressure and temperature
int16_t pressure;
int16_t temperature;
// extended data read (beyond 4 bytes/32 bits) should get the following:
char model[8];
uint32_t serialNumber;
char softwareVersion[6];
char fill[2]; // because LPSPI API requires multiple of 4 bytes; don't know why...
} ND120_output_T;
typedef struct {
uint8_t mode;
uint8_t rate;
// ToDo: ND120: ND120_control_T ctor to assemble fields of mode and rate
} ND120_control_T;
void test_ND120() {
lpspi_master_config_t ND120_LPSPI_MasterConfig =
{
.baudRate = 150000U, /*!< Baud Rate for LPSPI. 6uS cycle = 166,666Hz */
.bitsPerFrame = 8 * sizeof(ND120_output_T),/*!< Bits per frame, minimum 8, maximum 4096.*/
// Clock SPI mode '00' (CPOL = CPHA = 0)
.cpol = kLPSPI_ClockPolarityActiveLow, /*!< Clock polarity. */
.cpha = kLPSPI_ClockPhaseSecondEdge, /*!< Clock phase. */
.direction = kLPSPI_MsbFirst, /*!< MSB or LSB data shift direction. */
.pcsToSckDelayInNanoSec = 110000, /*!< PCS to SCK delay time in nanoseconds, setting to 0 sets the minimum delay.
It sets the boundary value if out of range.
spec: 100us */
.lastSckToPcsDelayInNanoSec = 20000, /*!< Last SCK to PCS delay time in nanoseconds, setting to 0 sets the minimum
delay. It sets the boundary value if out of range.
spec: 20us */
.betweenTransferDelayInNanoSec = 0, /*!< After the SCK delay time with nanoseconds, setting to 0 sets the
minimum delay. It sets the boundary value if out of range.*/
.whichPcs = kLPSPI_Pcs0, /*!< Desired Peripheral Chip Select (PCS) - over-ridden in transfer API */
.pcsActiveHighOrLow = kLPSPI_PcsActiveLow, /*!< Desired PCS active high or low */
.pinCfg = kLPSPI_SdiInSdoOut, /*!< Configures which pins are used for input and output data
during single bit transfers.*/
.dataOutConfig = kLpspiDataOutRetained, /*!< Configures if the output data is tristated
* between accesses (LPSPI_PCS is negated). */
/*guess*/.enableInputDelay = false, /*!< Enable master to sample the input data on a delayed SCK. This can help improve slave
setup time. Refer to device data sheet for specific time length. */
};
LPSPI_MasterInit(LPSPI4, &ND120_LPSPI_MasterConfig, BOARD_BOOTCLOCKRUN_LPSPI_CLK_ROOT); // enables clock and LPSI module
ND120_control_T ctrl = {
.mode = 0,
.rate = 0,
};
ND120_output_T result;
memset(&result,0,sizeof(result));
lpspi_transfer_t t1 =
{
.txData = (uint8_t*)&ctrl, /*!< Send buffer. */
.rxData = (uint8_t*)&result, /*!< Receive buffer. */
.dataSize = sizeof(result), /*!< Transfer bytes. */
/*!< Transfer transfer configuration flags. Set from _lpspi_transfer_config_flag_for_master if
the transfer is used for master or _lpspi_transfer_config_flag_for_slave enumeration if the
transfer is used for slave.*/
.configFlags = kLPSPI_MasterPcs0 /*| kLPSPI_MasterPcsContinuous */,
};
// Reset ND120
GPIO_PinWrite(BOARD_INITPINS_ND120_RESET_GPIO, BOARD_INITPINS_ND120_RESET_PIN, 0);
vTaskDelay(pdMS_TO_TICKS(10));
GPIO_PinWrite(BOARD_INITPINS_ND120_RESET_GPIO, BOARD_INITPINS_ND120_RESET_PIN, 1);
vTaskDelay(pdMS_TO_TICKS(100)); // wait for reset to actually complete
// ND120 Returns:
// {pressure = 5988, temperature = 1, model = "21DN\0\0\00", serialNumber = 1163278597, softwareVersion = "7410\0", fill = "\0A"}
status_t s1 = LPSPI_MasterTransferBlocking(LPSPI4, &t1);
assert(s1 == kStatus_Success);
(void)s1;
bool connected = memcmp(result.model,"21DN",5)==0;
assert(connected);
(void)connected;
// Can we do it twice?
status_t s2 = LPSPI_MasterTransferBlocking(LPSPI4, &t1);
assert(s2 == kStatus_Success);
(void)s2;
bool connected2 = memcmp(result.model,"21DN",5)==0;
assert(connected2);
(void)connected2;
}
Using 'continuous mode', I can read the device ID from the 3 BMP581 sensors:
void test_bmp581() {
// first try, using 'continuous CS', works before or after ND120 test
uint8_t t1_ReadChipID[2] = {
0x80 /* read operation */ | /* register */ BMP5_REG_CHIP_ID,
0 // dummy, time for data reply
};
uint8_t t1_result[sizeof(t1_ReadChipID)] = {55,66};
lpspi_master_config_t masterConfig =
{
.baudRate = 10000000U, /*!< Baud Rate for LPSPI. 10MHz (12.5MHz spec) */
.bitsPerFrame = 8, /*!< Bits per frame, minimum 8, maximum 4096.*/
// Clock SPI mode '11' (CPOL = CPHA = 1)
.cpol = kLPSPI_ClockPolarityActiveLow, /*!< Clock polarity. */
.cpha = kLPSPI_ClockPhaseSecondEdge, /*!< Clock phase. */
.direction = kLPSPI_MsbFirst, /*!< MSB or LSB data shift direction. */
/*guess*/.pcsToSckDelayInNanoSec = 50, /*!< PCS to SCK delay time in nanoseconds, setting to 0 sets the minimum delay.
It sets the boundary value if out of range.
ToDo: T_setup_csb MISSING FROM SPECIFICATION. */
.lastSckToPcsDelayInNanoSec = 50, /*!< Last SCK to PCS delay time in nanoseconds, setting to 0 sets the minimum
delay. It sets the boundary value if out of range.
T_hold_csb = 40ns spec.*/
.betweenTransferDelayInNanoSec = 0, /*!< After the SCK delay time with nanoseconds, setting to 0 sets the
minimum delay. It sets the boundary value if out of range.*/
.whichPcs = kLPSPI_Pcs1, /*!< Desired Peripheral Chip Select (PCS) - over-ridden in transfer API */
.pcsActiveHighOrLow = kLPSPI_PcsActiveLow, /*!< Desired PCS active high or low */
.pinCfg = kLPSPI_SdiInSdoOut, /*!< Configures which pins are used for input and output data
during single bit transfers.*/
.dataOutConfig = kLpspiDataOutRetained, /*!< Configures if the output data is tristated
* between accesses (LPSPI_PCS is negated). */
/*guess*/.enableInputDelay = false, /*!< Enable master to sample the input data on a delayed SCK. This can help improve slave
setup time. Refer to device data sheet for specific time length. */
};
LPSPI_MasterInit(LPSPI4, &masterConfig, BOARD_BOOTCLOCKRUN_LPSPI_CLK_ROOT); // enables clock and LPSI module
lpspi_transfer_t t1 =
{
.txData = &t1_ReadChipID[0], /*!< Send buffer. */
.rxData = &t1_result[0], /*!< Receive buffer. */
.dataSize = sizeof(t1_result), /*!< Transfer bytes. */
/*!< Transfer transfer configuration flags. Set from _lpspi_transfer_config_flag_for_master if
the transfer is used for master or _lpspi_transfer_config_flag_for_slave enumeration if the
transfer is used for slave.*/
.configFlags = kLPSPI_MasterPcs1 /* Overridden below anyway: | kLPSPI_MasterPcsContinuous */ ,
};
// trying to read multiple devices fails, probably because we've got CS confusion from 'continuous' mode
// =================== PCS #1 =======================
t1.configFlags = kLPSPI_MasterPcs1 | kLPSPI_MasterPcsContinuous ;
status_t s1 = LPSPI_MasterTransferBlocking(LPSPI4, &t1);
assert(s1 == kStatus_Success);
printf("BMP581-1 id (should be x50): x%x\n", t1_result[1]);
assert(t1_result[1] == 0x50);
}
If I try without continuous mode (which is what's needed here), I get blank results:
void test_bmp581() {
uint8_t t1_ReadChipID[2] = {
0x80 /* read operation */ | /* register */ BMP5_REG_CHIP_ID,
0 // dummy, time for data reply
};
uint8_t t1_result[sizeof(t1_ReadChipID)] = {55,66};
lpspi_master_config_t masterConfig =
{
.baudRate = 10000000U, /*!< Baud Rate for LPSPI. 10MHz (12.5MHz spec) */
.bitsPerFrame = 8 * sizeof(t1_result), /*!< Bits per frame, minimum 8, maximum 4096.*/
// Clock SPI mode '11' (CPOL = CPHA = 1)
.cpol = kLPSPI_ClockPolarityActiveLow, /*!< Clock polarity. */
.cpha = kLPSPI_ClockPhaseSecondEdge, /*!< Clock phase. */
.direction = kLPSPI_MsbFirst, /*!< MSB or LSB data shift direction. */
/*guess*/.pcsToSckDelayInNanoSec = 50, /*!< PCS to SCK delay time in nanoseconds, setting to 0 sets the minimum delay.
It sets the boundary value if out of range.
ToDo: T_setup_csb MISSING FROM BOSCH SPECIFICATION. */
.lastSckToPcsDelayInNanoSec = 50, /*!< Last SCK to PCS delay time in nanoseconds, setting to 0 sets the minimum
delay. It sets the boundary value if out of range.
T_hold_csb = 40ns spec.*/
.betweenTransferDelayInNanoSec = 0, /*!< After the SCK delay time with nanoseconds, setting to 0 sets the
minimum delay. It sets the boundary value if out of range.*/
.whichPcs = kLPSPI_Pcs1, /*!< Desired Peripheral Chip Select (PCS) - over-ridden in transfer API */
.pcsActiveHighOrLow = kLPSPI_PcsActiveLow, /*!< Desired PCS active high or low */
.pinCfg = kLPSPI_SdiInSdoOut, /*!< Configures which pins are used for input and output data
during single bit transfers.*/
.dataOutConfig = kLpspiDataOutRetained, /*!< Configures if the output data is tristated
* between accesses (LPSPI_PCS is negated). */
/*guess*/.enableInputDelay = false, /*!< Enable master to sample the input data on a delayed SCK. This can help improve slave
setup time. Refer to device data sheet for specific time length. */
};
LPSPI_MasterInit(LPSPI4, &masterConfig, BOARD_BOOTCLOCKRUN_LPSPI_CLK_ROOT); // enables clock and LPSI module
lpspi_transfer_t t1 =
{
.txData = &t1_ReadChipID[0], /*!< Send buffer. */
.rxData = &t1_result[0], /*!< Receive buffer. */
.dataSize = sizeof(t1_result), /*!< Transfer bytes. */
/*!< Transfer transfer configuration flags. Set from _lpspi_transfer_config_flag_for_master if
the transfer is used for master or _lpspi_transfer_config_flag_for_slave enumeration if the
transfer is used for slave.*/
.configFlags = kLPSPI_MasterPcs1 /* Overridden below anyway: | kLPSPI_MasterPcsContinuous */ ,
};
// ToDo: BMP581: Bug: reads 0x00 0x00
// =================== PCS #1 =======================
t1.configFlags = kLPSPI_MasterPcs1 /*| kLPSPI_MasterPcsContinuous */;
status_t s1 = LPSPI_MasterTransferBlocking(LPSPI4, &t1);
assert(s1 == kStatus_Success);
assert(t1_result[1] == 0x50); // <<< Fails here
}
Solved! Go to Solution.
Had to break out the logic analyzer to find this one.
Here's an image of the correctly ordered bytes, as was shown in the "continuous CS" example
(though here I've de-asserted CS after the transfer):
Using the normal (no continuous CS) mode, the LPSPI driver sends the bytes in the wrong order:
We know this is a driver bug in the normal (non-continuous CS mode) short transmission, because the driver:
It would be great if someone from NXP ( @EdwinHz ?) can:
I believe the bug is caused by incorrect interpretation of the isByteSwap parameter in the routine LPSPI_CombineWriteData.
If NXP has a repository accepting pull-requests, please let me know and I'll create a pull-request for the fix.
Thanks,
Best Regards, Dave
Had to break out the logic analyzer to find this one.
Here's an image of the correctly ordered bytes, as was shown in the "continuous CS" example
(though here I've de-asserted CS after the transfer):
Using the normal (no continuous CS) mode, the LPSPI driver sends the bytes in the wrong order:
We know this is a driver bug in the normal (non-continuous CS mode) short transmission, because the driver:
It would be great if someone from NXP ( @EdwinHz ?) can:
I believe the bug is caused by incorrect interpretation of the isByteSwap parameter in the routine LPSPI_CombineWriteData.
If NXP has a repository accepting pull-requests, please let me know and I'll create a pull-request for the fix.
Thanks,
Best Regards, Dave
This is getting a bit silly.
See: https://github.com/nxp-mcuxpresso/mcux-sdk/issues/113
Hi We have a repo in github https://github.com/nxp-mcuxpresso/mcux-sdk
You can create a PR , Thanks .
https://github.com/nxp-mcuxpresso/mcux-sdk/blob/main/drivers/lpspi/fsl_lpspi.c
Hello, I recommend using the examples to analyze if you miss any configuration.
The examples can download in the next URL Select Board | MCUXpresso SDK Builder (nxp.com)
Best regards,
Pavel
@Pavel_Hernandez- I have already done that (I used examples to create this code).
Can you find the error in my configuration?
Thanks!
Best Regards, Dave