Firstly, use the scope to check the data is set up correctly relative to the clock edge specified in the data sheet.
Then, all that really matters is the quality of the clock (the other signals can have poor edges etc) and you can tweak that with a series R to get it clean
for that particular cable run.
If you have the above wrong, it will never be reliable.
I've played a lot with SPI and all kinds of different chips, all running off the same SPI controller (32F417) and one finds surprising things. One of them is that chips whose data sheets require the clock parked high actually do not care if the clock is parked low. In terms of digital design this is fairly obvious which makes one wonder why the hell does anybody want the clock parked high.
The following code shows some of the fun, for the devices listed
/**
* @brief Set the mode of the SPI for the required peripheral
* This must be called before using each peripheral in turn
* @param mode: SPI_MODE_STLED316, SPI_MODE_ADS1118 or SPI_MODE_MCP3550 or SPI_MODE_HI3593 etc
*
* There were some weird problems related to different SPI devices not playing together, and the SPI clock
* mode of one affecting another. One was in the form of STLED316 display artefacts with the HI3593.
* One solution was to run all slaves with the clock parked LOW, which was incorrect per data sheet for some.
* Most devices should not actually care anyway i.e. they should sample data only on the clock *edge*. But
* What obviously does matter is which clock edge is used to sample the data and that must be set right.
* The ADS1118 is the only one on which the data is sampled on the *falling* clock edge.
* This is handy: https://visualgdb.com/tutorials/arm/stm32/spi/
* and https://peter-ftp.co.uk/screenshots/202102233910802613.jpg
*
* One explanation for this which somebody found: Depending on SPI phase/polarity, the initial clock/data levels
* after setup might be wrong. Since that bus has a whole bunch of SPI-slaves, I have to control the NCS-pin by
* firmware anyways. So simple solution: after changing config, keep all NCS idle/high and send a single packet.
* After that the idle polarity of the clock/data lines correspond to the configuration.
* This was attempted and made some difference but also broke some other stuff.
*
* STLED316 clock modes marked **** work but brightness control fails and display occassionally flickers, which
* suggests that the data coming out of the SPI is being read incorrectly. However this could be due to other
* SPI device activity before or after the STLED316.
*
* SPI3 runs off APB1 which is 42MHz.
*
* This function works regardless of whether the SPI transfer is done with HAL_SPI_TransmitReceive() or with
* SPI3_DMA_TransmitReceive().
*
*/
void spi3_set_mode(uint8_t mode)
{
// SPI configuration
switch (mode) {
case SPI_MODE_STLED316: // options *** below work but should not
m_spi.Init.Direction = SPI_DIRECTION_2LINES;
m_spi.Init.DataSize = SPI_DATASIZE_8BIT;
// m_spi.Init.CLKPolarity = SPI_POLARITY_HIGH; // ****
m_spi.Init.CLKPolarity = SPI_POLARITY_LOW; // ***
// m_spi.Init.CLKPhase = SPI_PHASE_2EDGE; // ****
m_spi.Init.CLKPhase = SPI_PHASE_1EDGE; // ***
m_spi.Init.NSS = SPI_NSS_SOFT;
m_spi.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_64; // 650kHz (max 1MHz)
m_spi.Init.FirstBit = SPI_FIRSTBIT_LSB; // LSB first - unusual
m_spi.Init.CRCPolynomial = 7;
m_spi.Init.Mode = SPI_MODE_MASTER;
m_spi.Instance = STLED316S_SPI;
break;
case SPI_MODE_ADS1118:
m_spi.Init.Direction = SPI_DIRECTION_2LINES;
m_spi.Init.DataSize = SPI_DATASIZE_16BIT;
m_spi.Init.CLKPolarity = SPI_POLARITY_LOW; // correct per data sheet
m_spi.Init.CLKPhase = SPI_PHASE_2EDGE; // likewise - sample on -ve ck edge
m_spi.Init.NSS = SPI_NSS_SOFT;
m_spi.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_16; // 2.6MHz (max 4MHz for ADS1118)
m_spi.Init.FirstBit = SPI_FIRSTBIT_MSB;
m_spi.Init.CRCPolynomial = 7;
m_spi.Init.Mode = SPI_MODE_MASTER;
m_spi.Instance = ADS1118_SPI;
break;
case SPI_MODE_MCP3550:
m_spi.Init.Direction = SPI_DIRECTION_2LINES_RXONLY;
m_spi.Init.DataSize = SPI_DATASIZE_8BIT;
// m_spi.Init.CLKPolarity = SPI_POLARITY_HIGH;
m_spi.Init.CLKPolarity = SPI_POLARITY_LOW; // SPI mode 0,0, uses 4 bytes
// m_spi.Init.CLKPhase = SPI_PHASE_2EDGE;
m_spi.Init.CLKPhase = SPI_PHASE_1EDGE;
m_spi.Init.NSS = SPI_NSS_SOFT;
m_spi.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_16; // 2.6MHz (max 5MHz for MCP3550)
m_spi.Init.FirstBit = SPI_FIRSTBIT_MSB;
m_spi.Init.CRCPolynomial = 7;
m_spi.Init.Mode = SPI_MODE_MASTER;
m_spi.Instance = MCP3550_SPI;
break;
case SPI_MODE_HI3593:
m_spi.Init.Direction = SPI_DIRECTION_2LINES;
m_spi.Init.DataSize = SPI_DATASIZE_8BIT;
m_spi.Init.CLKPolarity = SPI_POLARITY_LOW; // This is actually correct per data sheet
m_spi.Init.CLKPhase = SPI_PHASE_1EDGE; // and so is this
m_spi.Init.NSS = SPI_NSS_SOFT;
m_spi.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_8; // 5.2MHz (max 10MHz for HI3593)
// m_spi.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_4; // 10.5MHz (max 10MHz for HI3593)
m_spi.Init.FirstBit = SPI_FIRSTBIT_MSB;
m_spi.Init.CRCPolynomial = 7;
m_spi.Init.Mode = SPI_MODE_MASTER;
m_spi.Instance = HI3593_SPI;
break;
case SPI_MODE_NEO_M9N:
m_spi.Init.Direction = SPI_DIRECTION_2LINES;
m_spi.Init.DataSize = SPI_DATASIZE_8BIT;
m_spi.Init.CLKPolarity = SPI_POLARITY_LOW;
m_spi.Init.CLKPhase = SPI_PHASE_1EDGE;
m_spi.Init.NSS = SPI_NSS_SOFT;
m_spi.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_64; // 64: 650kHz (max 1MHz for 125kbyte/sec max data rate)
m_spi.Init.FirstBit = SPI_FIRSTBIT_MSB;
m_spi.Init.CRCPolynomial = 7;
m_spi.Init.Mode = SPI_MODE_MASTER;
m_spi.Instance = NEO_M9N_SPI;
break;
case SPI_MODE_LY68L6400:
m_spi.Init.Direction = SPI_DIRECTION_2LINES;
m_spi.Init.DataSize = SPI_DATASIZE_8BIT;
m_spi.Init.CLKPolarity = SPI_POLARITY_LOW;
m_spi.Init.CLKPhase = SPI_PHASE_1EDGE;
m_spi.Init.NSS = SPI_NSS_SOFT;
m_spi.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2; // 2: 21MHz - max possible on this box
m_spi.Init.FirstBit = SPI_FIRSTBIT_MSB;
m_spi.Init.CRCPolynomial = 7;
m_spi.Init.Mode = SPI_MODE_MASTER;
m_spi.Instance = LY68L6400_SPI;
break;
default:
break;
}
HAL_SPI_Init(&m_spi);
}
/code]
My next project is, wait for it, driving LCDs
