2022-09-05 05:50 AM
I am using FMC on STM32F765NIH7. Using the HAL library for FMC, which copies the data byte by byte into the NAND register, what happens is that almost any interruption during the data exchange can lead to incorrect receipt of them. So that interrupts do not interfere with the exchange process, I decided to use DMA. But since there are no functions for DMA in the HAL for FMC, I had to slightly change the HAL code of the driver, replacing the command for receiving data from the NAND register to the buffer (function HAL_NAND_Read_Page_8b):
/* Get Data into Buffer */
for(; index < size; index++)
{
*(uint8_t *)pBuffer++ = *(uint8_t *)deviceAddress;
}
on DMA2 mem-to-mem function:
/* Get Data into Buffer */
HAL_DMA_Start_IT(_hdmaRx, deviceAddress, (uint32_t)pBuffer, size);
DMA2 mem-to-mem receive has the following settings:
hdma_memtomem_dma2_stream6.Instance = DMA2_Stream6;
hdma_memtomem_dma2_stream6.Init.Channel = DMA_CHANNEL_0;
hdma_memtomem_dma2_stream6.Init.Direction = DMA_MEMORY_TO_MEMORY;
hdma_memtomem_dma2_stream6.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_memtomem_dma2_stream6.Init.MemInc = DMA_MINC_ENABLE;
hdma_memtomem_dma2_stream6.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
hdma_memtomem_dma2_stream6.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
hdma_memtomem_dma2_stream6.Init.Mode = DMA_NORMAL;
hdma_memtomem_dma2_stream6.Init.Priority = DMA_PRIORITY_LOW;
hdma_memtomem_dma2_stream6.Init.FIFOMode = DMA_FIFOMODE_ENABLE;
hdma_memtomem_dma2_stream6.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_HALFFULL;
hdma_memtomem_dma2_stream6.Init.MemBurst = DMA_MBURST_SINGLE;
hdma_memtomem_dma2_stream6.Init.PeriphBurst = DMA_PBURST_SINGLE;
I did the same for the send command HAL_NAND_Write_Page_8b:
/* Write data to memory */
for(; index < size; index++)
{
*(__IO uint8_t *)deviceAddress = *(uint8_t *)pBuffer++;
__DSB();
}
Replaced with DMA:
/* Write data to memory */
HAL_DMA_Start_IT(_hdmaTx, (uint32_t)pBuffer, deviceAddress, size);
DMA2 mem-to-mem to send has the following settings:
hdma_memtomem_dma2_stream5.Instance = DMA2_Stream5;
hdma_memtomem_dma2_stream5.Init.Channel = DMA_CHANNEL_0;
hdma_memtomem_dma2_stream5.Init.Direction = DMA_MEMORY_TO_MEMORY;
hdma_memtomem_dma2_stream5.Init.PeriphInc = DMA_PINC_ENABLE;
hdma_memtomem_dma2_stream5.Init.MemInc = DMA_MINC_DISABLE;
hdma_memtomem_dma2_stream5.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
hdma_memtomem_dma2_stream5.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
hdma_memtomem_dma2_stream5.Init.Mode = DMA_NORMAL;
hdma_memtomem_dma2_stream5.Init.Priority = DMA_PRIORITY_LOW;
hdma_memtomem_dma2_stream5.Init.FIFOMode = DMA_FIFOMODE_ENABLE;
hdma_memtomem_dma2_stream5.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_HALFFULL;
hdma_memtomem_dma2_stream5.Init.MemBurst = DMA_MBURST_SINGLE;
hdma_memtomem_dma2_stream5.Init.PeriphBurst = DMA_PBURST_SINGLE;
The use of DMA made it possible to exchange data without loss.
Also on this DMA2 I have 2 ADCs and an SPI running in parallel with the FMC. And as soon as mem-to-mem starts, the ADCs and SPI streams fall and go to ErrorCallback. At the same time, data is received in FMC without problems. To try to fix this, I changed the DMA settings (including changing streams and channels) in all peripherals and their priorities, but this did not help.
DMA settings for ADC:
hadc->DMA_Handle->Instance = DMA2_Stream0;
hadc->DMA_Handle->Init.Channel = DMA_CHANNEL_0;
hadc->DMA_Handle->Init.Direction = DMA_PERIPH_TO_MEMORY;
hadc->DMA_Handle->Init.PeriphInc = DMA_PINC_DISABLE;
hadc->DMA_Handle->Init.MemInc = DMA_MINC_ENABLE;
hadc->DMA_Handle->Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
hadc->DMA_Handle->Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
hadc->DMA_Handle->Init.Mode = DMA_CIRCULAR;
hadc->DMA_Handle->Init.Priority = DMA_PRIORITY_HIGH;
hadc->DMA_Handle->Init.FIFOMode = DMA_FIFOMODE_DISABLE;
Moreover, if you use mem-to-mem between two arrays in RAM, then there are no problems. Also, sending data from a static constexpr array does not cause other threads to crash.
Apparently this is somehow related to the deviceAddress variable (the address of the NAND register) and the fact that the DMA Arbiter does not allow other streams to work until the exchange in the FMC is completed.
Maybe someone knows how to use DMA for FMC without breaking streams for other peripherals?
2022-09-05 07:40 PM
Not sure..
Now NAND is funny in that the data read/write are not addressable, at the bus level at least, its more of a FIFO action, where the NAND has an expectation of data in a specific sector form. You'll need some mutex/semaphore as the NAND as the STM32 can't maintain multiple concurrent interactions. The F7 might be problematic if it generate speculative read/write. Check MPU settings.
Does your NAND device have a READY/WAIT type of signal which will stall the interface?
What's failing on the ADC/SPI ? Are you getting overflow/underflow errors?
2022-09-06 02:30 AM
In my case, using mutex/semaphore is not the best option.
There are no READY/WAIT commands in NAND.
ADC/SPI fall into DMA IrqHandler and go into overflow.
Does it really block the entire channel when working with NAND with DMA?
I also tried setting ADC to FIFO mode and it also went to ErrorHandler