AnsweredAssumed Answered

DMA I2C locking up STM32F4

Question asked by Drulko.Malko on Aug 29, 2016
Hi!

Im trying to change a library for STM32F4 to include DMA transfers when using I2C link.
Im using it do drive an OLED screen. In the comments, somebody already added DMA, but also ported it to STM32F10 and I'm trying to port it back to F4. My problem is, after enabling DMA transfer (at exactly that line), debugger stops working (LED stops flashing and debugger stays at next statement). After some more testing (blinking a led at certain events) I found out that code actually continues to certain point (specifically, next time when DMA transfer is needed/called). After that, cpu doesn't continue. The weird thing is, I know the transfer it working because the screen gets a few characters written on it. That only happens if I don't debug step by step because CPU writes new data to screen buffer in the mean time and changes content of it before it is entierly sent to the screen by DMA (I will figure out how to fix that later - probably dual buffer, but it shouldnt interfere with DMA transfer anyway). However if I debug step by step, DMA finishes before CPU writes new content to screen buffer and screen is black (as it should be). I tried blinking a led in interrupt handler of DMA but it never blinks, that means it is never fired. I would appreciate any help as Im at a loss (been debugging for a few days now).
Thank you!

Here is relevant part of code (I have omitted rest of the code because there is a lot of it, but if required I can post). The code works without DMA (with ordinary I2C transfers), it only breaks with DMA.

001.// TM_STM32F4_I2C.h
002.    typedef struct DMA_Data
003.    {
004.        DMA_Stream_TypeDef* DMAy_Streamx;
005.        uint32_t feif;
006.        uint32_t dmeif;
007.        uint32_t teif;
008.        uint32_t htif;
009.        uint32_t tcif;
010.    } DMA_Data;
011.     
012.    //...
013.     
014.// TM_STM32F4_I2C.c
015. 
016.    int16_t TM_I2C_WriteMultiDMA(DMA_Data* dmaData, I2C_TypeDef* I2Cx, uint8_t address, uint8_t reg, uint16_t len)
017.    {
018.        int16_t ok = 0;
019.        // If DMA is already enabled, wait for it to complete first.
020.        // Interrupt will disable this after transmission is complete.
021.        TM_I2C_Timeout = 10000000;
022.        // TODO: Is this I2C check ok?
023.        while (I2C_GetFlagStatus(I2Cx, I2C_FLAG_BUSY) && !I2C_GetFlagStatus(I2Cx, I2C_FLAG_TXE) && DMA_GetCmdStatus(dmaData->DMAy_Streamx) && TM_I2C_Timeout)
024.        {
025.            if (--TM_I2C_Timeout == 0)
026.            {
027.                return -1;
028.            }
029.        }
030.        //Set amount of bytes to transfer
031.        DMA_Cmd(dmaData->DMAy_Streamx, DISABLE); //should already be disabled at this point
032.        DMA_SetCurrDataCounter(dmaData->DMAy_Streamx, len);
033.        DMA_ClearFlag(dmaData->DMAy_Streamx, dmaData->feif | dmaData->dmeif | dmaData->teif | dmaData->htif | dmaData->tcif);   // Clear dma flags
034.        DMA_Cmd(dmaData->DMAy_Streamx, ENABLE); // enable DMA
035.        //Send I2C start
036.        ok = TM_I2C_Start(I2Cx, address, I2C_TRANSMITTER_MODE, I2C_ACK_DISABLE);
037.        //Send register to write to
038.        TM_I2C_WriteData(I2Cx, reg);
039.        //Start DMA transmission, interrupt will handle transmit complete.
040.        I2C_DMACmd(I2Cx, ENABLE);
041.        return ok;
042.    }
043.     
044.    //...
045. 
046.// TM_STM32F4_SSD1306.h
047. 
048.    #define SSD1306_I2C             I2C3
049.    #define SSD1306_I2Cx            3
050.    #define SSD1306_DMA_STREAM      DMA1_Stream4
051.    #define SSD1306_DMA_FEIF        DMA_FLAG_FEIF4
052.    #define SSD1306_DMA_DMEIF       DMA_FLAG_DMEIF4
053.    #define SSD1306_DMA_TEIF        DMA_FLAG_TEIF4
054.    #define SSD1306_DMA_HTIF        DMA_FLAG_HTIF4
055.    #define SSD1306_DMA_TCIF        DMA_FLAG_TCIF4
056.     
057.    static DMA_Data ssd1306_dma_data = { SSD1306_DMA_STREAM, SSD1306_DMA_FEIF, SSD1306_DMA_DMEIF, SSD1306_DMA_TEIF, SSD1306_DMA_HTIF, SSD1306_DMA_TCIF };
058.     
059.    #define SSD1306_I2C_ADDR         0x78
060.     
061.    //...
062. 
063.// TM_STM32F4_SSD1306.c
064. 
065.    void TM_SSD1306_initDMA(void)
066.    {
067.        DMA_InitTypeDef DMA_InitStructure;
068.        NVIC_InitTypeDef NVIC_InitStructure;
069. 
070.        RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1, ENABLE);
071.        DMA_DeInit(DMA1_Stream4);
072.        DMA_Cmd(DMA1_Stream4, DISABLE);
073. 
074.        //Configure DMA controller channel 3, I2C TX channel.
075.        DMA_StructInit(&DMA_InitStructure);                                 // Load defaults
076.        DMA_InitStructure.DMA_Channel = DMA_Channel_3;
077.        DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)(&(I2C3->DR)); // I2C3 data register address
078.        DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)SSD1306_Buffer;   // Display buffer address
079.        DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;             // DMA from mem to periph
080.        DMA_InitStructure.DMA_BufferSize = 1024;                            // Is set later in transmit function
081.        DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;    // Do not increment peripheral address
082.        DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;             // Do increment memory address
083.        DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
084.        DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
085.        DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;                       // DMA one shot, no circular.
086.        DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;               // Tweak if interfering with other dma actions
087.        DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;
088.        DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull;
089.        DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
090.        DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
091.        DMA_Init(DMA1_Stream4, &DMA_InitStructure);
092.        DMA_ITConfig(DMA1_Stream4, DMA_IT_TC, ENABLE);                      // Enable transmit complete interrupt
093.        DMA_ClearITPendingBit(DMA1_Stream4, DMA_IT_TC);
094. 
095.        // Set interrupt controller for DMA
096.        NVIC_InitStructure.NVIC_IRQChannel = DMA1_Stream4_IRQn;             // I2C3 TX connect to stream 4 of DMA1
097.        NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x05;
098.        NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x05;
099.        NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
100.        NVIC_Init(&NVIC_InitStructure);
101. 
102.        // Set interrupt controller for I2C
103.        NVIC_InitStructure.NVIC_IRQChannel = I2C3_EV_IRQn;
104.        NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
105.        NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
106.        NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
107.        NVIC_Init(&NVIC_InitStructure);
108.        I2C_ITConfig(I2C3, I2C_IT_BTF, ENABLE);
109.    }
110. 
111.    extern void DMA1_Channel3_IRQHandler(void)
112.    {
113.        //I2C3 DMA transmit completed
114.        if (DMA_GetITStatus(DMA1_Stream4, DMA_IT_TC) != RESET)
115.        {
116.            // Stop DMA, clear interrupt
117.            DMA_Cmd(DMA1_Stream4, DISABLE);
118.            DMA_ClearITPendingBit(DMA1_Stream4, DMA_IT_TC);
119.            I2C_DMACmd(SSD1306_I2C, DISABLE);
120.        }
121.    }
122.    // Sending stop condition to I2C in separate handler necessary
123.    // because DMA can finish before I2C finishes
124.    // transmitting and last byte is not sent
125.    extern void I2C3_EV_IRQHandler(void)
126.    {
127.        if (I2C_GetITStatus(I2C3, I2C_IT_BTF) != RESET)
128.        {
129.            TM_I2C_Stop(SSD1306_I2C); // send i2c stop
130.            I2C_ClearITPendingBit(I2C3, I2C_IT_BTF);
131.        }
132.    }
133. 
134.    // ...
135. 
136.    void TM_SSD1306_UpdateScreen(void) {
137.        TM_I2C_WriteMultiDMA(&ssd1306_dma_data, SSD1306_I2C, SSD1306_I2C_ADDR, 0x40, 1024); // Use DMA
138.    }

Outcomes