Ok, a simple example I am trying to implement to find out what am I doing wrong: Each transaction starts by master pulling CS low and continues by master sending 10 bytes and slave responding with 10 bytes to master. The transaction ends with master pulling CS high. If the transaction is incomplete (e.g. not all bytes were sent), it should not affect the other transactions (this is why I have to detect the CS line - to reset incomplete transactions).
I have written following a simple demo to fulfill the task above using the advice to write SPI_CR1_SSI directly:
int rxTriggCount = 0;
int txTriggCount = 0;
int rxCounter = 0;
bool print = false;
char rxBuffer[21];
const char *txBuffer = "Hello world!";
int txCounter = 0;
extern "C" void SPI1_IRQHandler() {
if (LL_SPI_IsEnabledIT_RXNE( SPI1 ) && LL_SPI_IsActiveFlag_RXNE( SPI1 ) )
{
rxBuffer[ rxCounter++ ] = LL_SPI_ReceiveData8( SPI1 );
if ( rxCounter == 10 ) {
LL_SPI_SetTransferDirection( SPI1, LL_SPI_HALF_DUPLEX_TX );
LL_SPI_DisableIT_RXNE( SPI1 );
LL_SPI_EnableIT_TXE( SPI1 );
}
rxTriggCount++;
}
if ( LL_SPI_IsEnabledIT_TXE( SPI1 ) && LL_SPI_IsActiveFlag_TXE( SPI1 ) )
{
LL_SPI_TransmitData8( SPI1, txBuffer[ txCounter++ ] );
if ( txCounter == 10 )
LL_SPI_DisableIT_TXE( SPI1 );
txTriggCount++;
}
}
int main() {
HAL_Init();
LL_APB2_GRP1_EnableClock( LL_APB2_GRP1_PERIPH_SPI1 );
NVIC_SetPriority( SPI1_IRQn, 2 );
NVIC_EnableIRQ( SPI1_IRQn );
LL_GPIO_InitTypeDef GPIO_InitStruct{};
GPIO_InitStruct.Pin = LL_GPIO_PIN_1;
GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
GPIO_InitStruct.Alternate = LL_GPIO_AF_0;
LL_GPIO_Init( GPIOA, &GPIO_InitStruct );
GPIO_InitStruct.Pin = LL_GPIO_PIN_6;
GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
GPIO_InitStruct.Alternate = LL_GPIO_AF_0;
LL_GPIO_Init( GPIOA, &GPIO_InitStruct );
LL_SPI_InitTypeDef SPI_InitStruct{};
SPI_InitStruct.TransferDirection = LL_SPI_HALF_DUPLEX_RX;
SPI_InitStruct.Mode = LL_SPI_MODE_SLAVE;
SPI_InitStruct.DataWidth = LL_SPI_DATAWIDTH_8BIT;
SPI_InitStruct.ClockPolarity = LL_SPI_POLARITY_LOW;
SPI_InitStruct.ClockPhase = LL_SPI_PHASE_1EDGE;
SPI_InitStruct.NSS = LL_SPI_NSS_HARD_INPUT;
SPI_InitStruct.BitOrder = LL_SPI_MSB_FIRST;
SPI_InitStruct.CRCCalculation = LL_SPI_CRCCALCULATION_DISABLE;
SPI_InitStruct.CRCPoly = 7;
LL_SPI_Init( SPI1, &SPI_InitStruct );
LL_SPI_SetRxFIFOThreshold( SPI1, LL_SPI_RX_FIFO_TH_QUARTER );
LL_SPI_Enable( SPI1 );
LL_SPI_EnableIT_RXNE( SPI1 );
GpioA[ 4 ].setupInterrupt( LL_EXTI_TRIGGER_RISING_FALLING, []( bool rising ) {
if ( rising ) {
// Transaction ends
SPI1->CR1 &= ~SPI_CR1_SSI;
rxBuffer[ rxCounter + 1 ] = 0; // Terminal zero
print = true;
rxCounter = 0;
txCounter = 0;
LL_SPI_DisableIT_TXE( SPI1 );
LL_SPI_EnableIT_RXNE( SPI1 );
LL_SPI_SetTransferDirection( SPI1, LL_SPI_HALF_DUPLEX_RX );
// Preload data
LL_SPI_TransmitData8( SPI1, txBuffer[ txCounter++ ] );
}
else {
// Transaction begins
SPI1->CR1 |= SPI_CR1_SSI;
}
} );
while ( true ) {
if ( print ) {
counter++;
print = false;
Dbg::info("Received %d (%d, %d): %s", counter, rxTriggCount, txTriggCount, rxBuffer );
rxTriggCount = txTriggCount = 0;
}
}
}
The debug print shows expected values - I correctly receive 10 bytes from master and 1 + 9 bytes are pushed into the TX FIFO. However, the master receives no data. Am I missing something with chaning the direction of SPI?
Also: I grepped the sources of the LL and HAL library and there is no function which would change the direction of the SPI using SPI_CR1_SSI.