2014-11-03 01:17 AM
I have an STM32F429 connected via FMC to a 16-bit wide SRAM. I can read and write to the SRAM correctly using simple memory writes.
DMA works correctly too when I configure it with two streams; one to send internal memory words to the half-word memory, and a second stream to read half-words to back to the internal memory.Even though it works, I'm confused about whether I'm configuring my DMA correctly, and a couple of answers would help greatly:2014-11-03 01:54 AM
> Do I need to specify in the DMA that the memory transfers are from word to half-word, and
> vice versa? Or does the FMC automatically handle the data-width translation? i.e. can I use > DMA with word-transfers even though the SRAM is 16-bit? Read RM0090 rev.7 ch.37.3.1: ''AHB transaction size is greater than the memory size: In this case, the FMC splits the AHB transaction into smaller consecutive memory accesses to meet the external data width. '' > Is there any advantage to using FIFO mode with memory-to-memory transfers? You answered this in your next question.. >Memory-to-Memory transfers do not allow Direct Mode to be used. I can see the FIFO being enabled by hardware (DMDIS = 1). In other words, FIFO is used automatically when you use M-to-M. > But I'm not configuring BURST sizes and it seems to work fine. Is all this performed by hardware? No. Bursts are used only if you enable them (set to non-single-transfer), but they are not necessary. JW2014-11-03 08:40 PM
So I just changed my code to use word (32-bit) transactions. Previously I had word on internal memory and half-word on my SRAM external memory.
When I write the memory with one 128-word DMA transfer, and then read it back with another DMA transfer, the memory is not as expected.The data read back is correct for the most significant 16-bits, but the least significant 16-bits are read as the same as the most-significant.Here's my initialisation for the DMA:DMA_HandleTypeDef dmaHandleMem2Sram;DMA_HandleTypeDef dmaHandleSram2Mem;void dmaInit(void){ //Memory to SRAM (DMA2 Stream 0, high priority) //Memory = Source = ''Peripheral'' //SRAM = Destination = ''Memory'' dmaHandleMem2Sram.Init.Channel = DMA_CHANNEL_0; //Channel 0 dmaHandleMem2Sram.Init.Direction = DMA_MEMORY_TO_MEMORY; //Memory-to-memory mode dmaHandleMem2Sram.Init.PeriphInc = DMA_PINC_ENABLE; //Peripheral increment mode enabled dmaHandleMem2Sram.Init.MemInc = DMA_MINC_ENABLE; //Memory increment mode enabled dmaHandleMem2Sram.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD; //Peripheral data alignment: Word (source) dmaHandleMem2Sram.Init.MemDataAlignment = DMA_MDATAALIGN_WORD; //Memory data alignment: Word (destination) dmaHandleMem2Sram.Init.Mode = DMA_NORMAL; //Normal DMA mode (not circular or peripheral control modes) dmaHandleMem2Sram.Init.Priority = DMA_PRIORITY_HIGH; //Priority level: High dmaHandleMem2Sram.Init.FIFOMode = DMA_FIFOMODE_DISABLE; //FIFO mode disabled dmaHandleMem2Sram.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL; //FIFO threshold: Full dmaHandleMem2Sram.Init.MemBurst = DMA_MBURST_SINGLE; //Memory burst: Single dmaHandleMem2Sram.Init.PeriphBurst = DMA_PBURST_SINGLE; //Peripheral burst: Single //Select the DMA instance to be used for the transfer : DMA2_Stream0 dmaHandleMem2Sram.Instance = DMA2_Stream0; //Select Call-backs functions called after Transfer complete and Transfer error dmaHandleMem2Sram.XferCpltCallback = dmaComplete; dmaHandleMem2Sram.XferErrorCallback = dmaError; //Initialize the DMA stream if(HAL_DMA_Init(&dmaHandleMem2Sram) != HAL_OK) { Error_Handler(); //Initialization Error } //Configure NVIC for DMA transfer complete/error interrupts HAL_NVIC_SetPriority(DMA2_Stream0_IRQn, 0, 0); HAL_NVIC_EnableIRQ(DMA2_Stream0_IRQn); //SRAM to Memory (DMA2 Stream 1, medium priority) //SRAM = Source = Peripheral //Memory = Destination = Memory //Select the DMA functional Parameters dmaHandleSram2Mem.Init.Channel = DMA_CHANNEL_0; //Channel 0 dmaHandleSram2Mem.Init.Direction = DMA_MEMORY_TO_MEMORY; //Memory-to-memory mode dmaHandleSram2Mem.Init.PeriphInc = DMA_PINC_ENABLE; //Peripheral increment mode enabled dmaHandleSram2Mem.Init.MemInc = DMA_MINC_ENABLE; //Memory increment mode enabled dmaHandleSram2Mem.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD; //Peripheral data alignment: Word (source) dmaHandleSram2Mem.Init.MemDataAlignment = DMA_MDATAALIGN_WORD; //Memory data alignment: Word (destination) dmaHandleSram2Mem.Init.Mode = DMA_NORMAL; //Normal DMA mode (not circular or peripheral control modes) dmaHandleSram2Mem.Init.Priority = DMA_PRIORITY_MEDIUM; //Priority level: Medium ***NOTE: Lower Priority than Stream0*** dmaHandleSram2Mem.Init.FIFOMode = DMA_FIFOMODE_DISABLE; //FIFO mode disabled dmaHandleSram2Mem.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL; //FIFO threshold: Full dmaHandleSram2Mem.Init.MemBurst = DMA_MBURST_SINGLE; //Memory burst: Single dmaHandleSram2Mem.Init.PeriphBurst = DMA_PBURST_SINGLE; //Peripheral burst: Single //Select the DMA instance to be used for the transfer : DMA2_Stream1 dmaHandleSram2Mem.Instance = DMA2_Stream1; //Select Call-backs functions called after Transfer complete and Transfer error dmaHandleSram2Mem.XferCpltCallback = dmaComplete; dmaHandleSram2Mem.XferErrorCallback = dmaError; //Initialize the DMA stream if(HAL_DMA_Init(&dmaHandleSram2Mem) != HAL_OK) { Error_Handler(); //Initialization Error } //Configure NVIC for DMA transfer complete/error interrupts HAL_NVIC_SetPriority(DMA2_Stream1_IRQn, 1, 0); HAL_NVIC_EnableIRQ(DMA2_Stream1_IRQn);}void dmaTransferMem2Sram(uint32_t *src, uint32_t *dst, uint32_t size){ if(HAL_DMA_Start_IT(&dmaHandleMem2Sram, (uint32_t)src, (uint32_t)dst, size) != HAL_OK) { Error_Handler(); //Initialization Error }}void dmaTransferSram2Mem(uint32_t *src, uint32_t *dst, uint32_t size){ if(HAL_DMA_Start_IT(&dmaHandleSram2Mem, (uint32_t)src, (uint32_t)dst, size) != HAL_OK) { Error_Handler(); //Initialization Error }}And here's the FMC.void fmcSramInit(void){ SRAM_HandleTypeDef hsram; FMC_NORSRAM_TimingTypeDef SRAM_Timing; //SRAM Configuration hsram.Instance = FMC_NORSRAM_DEVICE; hsram.Extended = FMC_NORSRAM_EXTENDED_DEVICE; SRAM_Timing.AddressSetupTime = 1; //Don't care SRAM_Timing.AddressHoldTime = 1; //Don't care SRAM_Timing.DataSetupTime = 1; //Don't care SRAM_Timing.BusTurnAroundDuration = 0; SRAM_Timing.CLKDivision = 2; //Must be greater than 1 SRAM_Timing.DataLatency = 0; //Must be '0' for CRAM (PSRAM) SRAM_Timing.AccessMode = FMC_ACCESS_MODE_A; //Not used since we are synchronous hsram.Init.NSBank = FMC_NORSRAM_BANK1; //Use bank 1 since we only have access to the NE1 signal hsram.Init.DataAddressMux = FMC_DATA_ADDRESS_MUX_DISABLE; //Disable multiplex hsram.Init.MemoryType = FMC_MEMORY_TYPE_PSRAM; //PSRAM allowed CLK hsram.Init.MemoryDataWidth = FMC_NORSRAM_MEM_BUS_WIDTH_16; hsram.Init.BurstAccessMode = FMC_BURST_ACCESS_MODE_ENABLE; hsram.Init.WaitSignalPolarity = FMC_WAIT_SIGNAL_POLARITY_HIGH; //NOTE: With synchronous mode and NWAIT disabled, make this HIGH with NWAIT disconnected from AF12 GPIO hsram.Init.WrapMode = FMC_WRAP_MODE_DISABLE; hsram.Init.WaitSignalActive = FMC_WAIT_TIMING_BEFORE_WS; hsram.Init.WriteOperation = FMC_WRITE_OPERATION_ENABLE; hsram.Init.WaitSignal = FMC_WAIT_SIGNAL_DISABLE; hsram.Init.ExtendedMode = FMC_EXTENDED_MODE_DISABLE; //Disable extended mode hsram.Init.AsynchronousWait = FMC_ASYNCHRONOUS_WAIT_DISABLE; hsram.Init.WriteBurst = FMC_WRITE_BURST_ENABLE; hsram.Init.ContinuousClock = FMC_CONTINUOUS_CLOCK_SYNC_ONLY; //Unsure which this should be, but will leave it as only synchronous, 24/09/2014 //Enable FMC clock __FMC_CLK_ENABLE(); //Initialize the SRAM controller if(HAL_SRAM_Init(&hsram, &SRAM_Timing, &SRAM_Timing) != HAL_OK) { //Initialization Error Error_Handler(); }}And these are the calls to DMA:#define SRAM_BANK_ADDR ((uint32_t)0x60000000)#define BUF_SZ ((uint32_t)0x80)
__attribute__((aligned(64))) uint32_t randArray[BUF_SZ];
dmaTransferMem2Sram(randArray, (uint32_t *)SRAM_BANK_ADDR, BUF_SZ);**CODE INBETWEEN TO WAIT FOR PREVIOUS TRANSFER TO FINISH**dmaTransferSram2Mem((uint32_t *)SRAM_BANK_ADDR, randArray, BUF_SZ);I've attached an image of the memory I see, before writing (on the left) and after reading back from the memory (on the right)2014-11-04 12:18 AM
Could you read out the external memory after the first transfer?
Could you clear the internal memory between the transfers? Any change in the readback values? Can you perform the transfer using the processor, first 16-bit, then 32-bit? JW2014-11-05 08:20 PM
Hi waclawek.jan,
Last night I did some simple read/write tests without DMA. I can only get them to work when performing 16-bit transactions. 32-bit transactions have the same problem the DMA had, so I don't think it's so much a DMA problem as it is a problem with the FMC or a related area.Hopefully I'll have some time tonight and I'll try to compare the STM32F429I-Discovery's 16-bit external memory software example with my interface. It looks like their example should work, the only difference being the type of external memory.2014-11-20 04:51 PM
So I've solved the problem, and it turned out to be a combination of things:
Everything is working fine now with the FMC and DMA configuration!