cancel
Showing results for 
Search instead for 
Did you mean: 

DMA I2C locking up STM32F4

denix8
Associate II
Posted on August 29, 2016 at 20:10

Hi!

Im trying to change a library for STM32F4 to include DMA transfers when using I2C

http://stm32f4-discovery.net/2015/05/library-61-ssd1306-oled-i2c-lcd-for-stm32f4xx/

. 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.


// TM_STM32F4_I2C.h

typedef
struct
DMA_Data

{

DMA_Stream_TypeDef* DMAy_Streamx;

uint32_t feif;

uint32_t dmeif;

uint32_t teif;

uint32_t htif;

uint32_t tcif;

} DMA_Data;


//...


// TM_STM32F4_I2C.c


int16_t TM_I2C_WriteMultiDMA(DMA_Data* dmaData, I2C_TypeDef* I2Cx, uint8_t address, uint8_t reg, uint16_t len)

{

int16_t ok = 0;

// If DMA is already enabled, wait for it to complete first.

// Interrupt will disable this after transmission is complete.

TM_I2C_Timeout = 10000000;

// TODO: Is this I2C check ok?

while
(I2C_GetFlagStatus(I2Cx, I2C_FLAG_BUSY) && !I2C_GetFlagStatus(I2Cx, I2C_FLAG_TXE) && DMA_GetCmdStatus(dmaData->DMAy_Streamx) && TM_I2C_Timeout)

{

if
(--TM_I2C_Timeout == 0)

{

return
-1;

}

}

//Set amount of bytes to transfer

DMA_Cmd(dmaData->DMAy_Streamx, DISABLE); 
//should already be disabled at this point

DMA_SetCurrDataCounter(dmaData->DMAy_Streamx, len);

DMA_ClearFlag(dmaData->DMAy_Streamx, dmaData->feif | dmaData->dmeif | dmaData->teif | dmaData->htif | dmaData->tcif); 
// Clear dma flags

DMA_Cmd(dmaData->DMAy_Streamx, ENABLE); 
// enable DMA

//Send I2C start

ok = TM_I2C_Start(I2Cx, address, I2C_TRANSMITTER_MODE, I2C_ACK_DISABLE);

//Send register to write to

TM_I2C_WriteData(I2Cx, reg);

//Start DMA transmission, interrupt will handle transmit complete.

I2C_DMACmd(I2Cx, ENABLE);

return
ok;

}


//...


// TM_STM32F4_SSD1h


#define SSD1306_I2C I2C3

#define SSD1306_I2Cx 3

#define SSD1306_DMA_STREAM DMA1_Stream4

#define SSD1306_DMA_FEIF DMA_FLAG_FEIF4

#define SSD1306_DMA_DMEIF DMA_FLAG_DMEIF4

#define SSD1306_DMA_TEIF DMA_FLAG_TEIF4

#define SSD1306_DMA_HTIF DMA_FLAG_HTIF4

#define SSD1306_DMA_TCIF DMA_FLAG_TCIF4


static
DMA_Data ssd1306_dma_data = { SSD1306_DMA_STREAM, SSD1306_DMA_FEIF, SSD1306_DMA_DMEIF, SSD1306_DMA_TEIF, SSD1306_DMA_HTIF, SSD1306_DMA_TCIF };


#define SSD1306_I2C_ADDR 0x78


//...


// TM_STM32F4_SSD1c


void
TM_SSD1306_initDMA(
void
)

{

DMA_InitTypeDef DMA_InitStructure;

NVIC_InitTypeDef NVIC_InitStructure;


RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1, ENABLE);

DMA_DeInit(DMA1_Stream4);

DMA_Cmd(DMA1_Stream4, DISABLE);


//Configure DMA controller channel 3, I2C TX channel.

DMA_StructInit(&DMA_InitStructure); 
// Load defaults

DMA_InitStructure.DMA_Channel = DMA_Channel_3;

DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)(&(I2C3->DR)); 
// I2C3 data register address

DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)SSD1306_Buffer; 
// Display buffer address

DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral; 
// DMA from mem to periph

DMA_InitStructure.DMA_BufferSize = 1024; 
// Is set later in transmit function

DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; 
// Do not increment peripheral address

DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; 
// Do increment memory address

DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;

DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;

DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; 
// DMA one shot, no circular.

DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; 
// Tweak if interfering with other dma actions

DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;

DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull;

DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;

DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;

DMA_Init(DMA1_Stream4, &DMA_InitStructure);

DMA_ITConfig(DMA1_Stream4, DMA_IT_TC, ENABLE); 
// Enable transmit complete interrupt

DMA_ClearITPendingBit(DMA1_Stream4, DMA_IT_TC);


// Set interrupt controller for DMA

NVIC_InitStructure.NVIC_IRQChannel = DMA1_Stream4_IRQn; 
// I2C3 TX connect to stream 4 of DMA1

NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x05;

NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x05;

NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;

NVIC_Init(&NVIC_InitStructure);


// Set interrupt controller for I2C

NVIC_InitStructure.NVIC_IRQChannel = I2C3_EV_IRQn;

NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;

NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;

NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;

NVIC_Init(&NVIC_InitStructure);

I2C_ITConfig(I2C3, I2C_IT_BTF, ENABLE);

}


extern
void
DMA1_Channel3_IRQHandler(
void
)

{

//I2C3 DMA transmit completed

if
(DMA_GetITStatus(DMA1_Stream4, DMA_IT_TC) != RESET)

{

// Stop DMA, clear interrupt

DMA_Cmd(DMA1_Stream4, DISABLE);

DMA_ClearITPendingBit(DMA1_Stream4, DMA_IT_TC);

I2C_DMACmd(SSD1306_I2C, DISABLE);

}

}

// Sending stop condition to I2C in separate handler necessary

// because DMA can finish before I2C finishes

// transmitting and last byte is not sent

extern
void
I2C3_EV_IRQHandler(
void
)

{

if
(I2C_GetITStatus(I2C3, I2C_IT_BTF) != RESET)

{

TM_I2C_Stop(SSD1306_I2C); //send i2c stop

I2C_ClearITPendingBit(I2C3, I2C_IT_BTF);

}

}


// ...


void
TM_SSD1306_UpdateScreen(
void
) {

TM_I2C_WriteMultiDMA(&ssd1306_dma_data, SSD1306_I2C, SSD1306_I2C_ADDR, 0x40, 1024); 
// Use DMA

}

0 REPLIES 0