2019-01-16 04:15 AM
I need to transfer data between two micro controllers - both short commands and large blocks of data.
The STM will receive and so will be the SPI slave. I intend to use DMA.
The SPI master will transmit either short or long packets and so need a way to receive variable lengths using DMA - I could not find an example & wanted a quick start.
My guess is that if I should use hdma_tx.Init.Mode=DMA_PFCTRL & hope that means that the slave transaction will complete once master completes brings CS high? Anyone tried this? Other than adding a length field to the packet, how may I get the number of bytes transferred?
Perhaps a easier route is to capture the SPI complete event?
In either case I wonder what the meaning of 'SIze' in the HAL_SPI_*** calls becomes.
2019-01-16 06:15 AM
You didn't care to tell us the STM32 model you are using, but read the Flow controller subchapter of the DMA chapter in the RM, you might find something like this there:
The peripheral indicates by hardware to the DMA controller
when the last data are being transferred. This feature is only supported for peripherals
that are able to signal the end of the transfer, that is: SDIO.
If you have a CS line between master and slave, then the easiest thing to do is to interrupt upon CS change (use it as an EXTI) and then you know the number of bytes transferred from the DMA's NDTR.
JW
2019-01-16 07:14 AM
Are there any other SPI slaves on the bus?
2019-01-16 07:17 AM
Indeed. I was just answering the first response. Sharing the bus is the tricky part...
Thanks. STM32F401CB
My RM is not as explicit and states:
'The last external burst or single request has been generated from the peripheral...'
re: Using CS as EXTI. So I just call HAL_SPI_DMAStop() (which then chains through the completion handlers)?
Can I use CS as an EXTI when that pin is being used as slave hardware NSS?
My slave will be full duplex and return status information lodged in the minimum packet size.
I would therefore need to enable/disable my MISO depending on CS or upset the other slaves.
On a shared SPI bus using software NSS (if I am to use EXTI) then this would have too much latency.
Do back to DMA events I think.
I did not find a CS state change INT.
2019-01-16 07:52 AM
EXTI is independent of chosen alternate function, however you'll need to workaround the HAL as it has yet to enable this scheme.
Personally, used NSS SW+EXTI on both edges, with SPI with DMA in cyclic mode for slaves (no DMA interrupt). First transaction exchange the desired block transfer size (in and out bound on 4 wire), then second transaction is the DMA block transfer which size if the biggest block size.
Allow minimal pause when NSS toggles for slaves to get ready for the transaction or process the incoming data message.
2019-01-16 08:24 AM
Thanks,
Could you reference how to use EXTI in AF mode to trigger on CS plz?
I can only see TX/RX/ERROR SPI events, not the needed CS change, in the RM.
The rest is difficult to follow as the HAL/RM is so ambiguous.
Circular mode reloads length after each transfer. Fine. But when is transfer end? If you are SW NSS then transfer end is when Size has been reached(?). So seems to me that Circular is only for regular (e.g. ADC) outputs as described and not for variable packet lengths which I need
I have an ESP on the other end & ESP latency is crippling.
2019-01-16 10:54 AM
ok, got EXTI on SCK, you can probably adapt to NSS (HW) pin:
void SPIP_EnableSCK_EXTI(SPIP_t* pSPIP) { // STM32L4R5
// PD1 = SCK
SYSCFG->EXTICR[0] &= ~(0xF<<4);
SYSCFG->EXTICR[0] |= (3<<4);
EXTI->IMR1 |= GPIO_PIN_1; // interrupt enable
EXTI->RTSR1 |= GPIO_PIN_1; // rising sense
EXTI->FTSR1 |= GPIO_PIN_1; // falling sense
// Above is the simplest way to have EXTI on AF pin.
__HAL_GPIO_EXTI_CLEAR_IT(pSPIP->pSCK->Init.Pin);
__HAL_GPIO_EXTI_CLEAR_FLAG(pSPIP->pSCK->Init.Pin);
//EXTI->IMR1 |= (1<<1);
HAL_NVIC_SetPriority(EXTI1_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(EXTI1_IRQn);
}
void SPIP_DisableSCK_EXTI(SPIP_t* pSPIP) {
HAL_NVIC_DisableIRQ(EXTI1_IRQn);
}
void EXTI1_IRQHandler(void)
{
/* USER CODE BEGIN EXTI0_IRQn 0 */
/* USER CODE END EXTI0_IRQn 0 */
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_1);
/* USER CODE BEGIN EXTI0_IRQn 1 */
/* USER CODE END EXTI0_IRQn 1 */
}
DMA needs to know the block size going to be transffered:
Of course, if planning to exchange data full duplex MISO and MOSI, there are two directions, two blocks, two sizes sharing the same SCK clock pulses...
One implemented solution if there are only MCU on the SPI bus is to send data at each edge of NSS, rising edge, I send a small "header" which contains the coming next data size and simultaneously, I received the slave intended block size to receive. Then on NSS falling edge, the DMA do a pure block transfer uninterrupted. In this particular case, NSS is SW controlled and MISO/MOSI at slave are dynamically configured from the NSS dual edge EXTI interrupt. All is handled in a ISR based state machine to hide the details and minimize core load.
2019-01-16 12:27 PM
I don't Cube.
JW
2019-01-17 04:20 AM
Thanks all. Fairly straight forwards once you ditch the HAL straight jacket.
Shame they messed up SPI SLAVE, stop/start DMA on NSS - how hard is that?
I am going to play & double check. If I set SPI transfer to 65535 then I can terminate SPI on NSS EXTI.
I would still have to enforce bus gap between frames but better (on ESP) than programming SPI length following first frame.