cancel
Showing results for 
Search instead for 
Did you mean: 

Strange interference between I2C1 DMA and USART1 interrupt (BARE METAL)

Nickelgrass
Senior

Hello,

first of I must state that this is a bare metal project. If bare metal is not your thing then please do not read on. I only use parts of the CMSIS but no HAL and no LL.

I have a strange effect when using I2C1 bus with DMA and USART1 with receiver interrupt. Maybe someone can shine some light on this. 

So my USART1 is set up correctly for 115200 8E1 communication. Transmitting and receiving works in polled mode and with receive interrupt. Also the I2C bus is set up correct and transmitts as it should.

When I now initialize the I2C1 DMA to transmit BEFORE initializing the USART, the USART receive interrupt works. If the I2C1 DMA is setup (and fired) AFTER the USART is initialized, the USART interrupt does not work. As if the I2C DMA somehow disables the USART interrupt. I have tracked the problem down to the starting the DMA. Just initializing the I2C does not disable the interrupt. 

The defines for bitx should be self explanatory. 

This is the I2C init code:

 

RCC->APB1ENR1 |= bit21; // enable i2c1 clock RCC->AHB1ENR |= (bit1|bit2); // enable DMA2 and DMAMUX RCC->APB1RSTR1 |= bit21; // reset I2C1 GPIOA->MODER &= ~(0b11<<30); GPIOA->MODER |= (0b10<<30); // PA15 alternate function GPIOA->OTYPER |= bit15; // PA15 open drain GPIOA->AFRH |= (4<<28); // PA15 alternate funcion i2c1 scl GPIOB->MODER &= ~(0b11<<14); GPIOB->MODER |= (0b10<<14); // PB7 alternate function GPIOB->OTYPER |= bit7; // PB7 open drain GPIOB->AFRL |= (4<<28); // PB7 alternate funcion i2c1 sda RCC->APB1RSTR1 &= ~bit21; // reset I2C1 I2C1->TIMINGR = (0x1050537C & 0xF0FFFFFFU); // set i2x timing I2C1->OAR1 = 0; I2C1->OAR2 = 0; I2C1->CR1 |= bit17|bit14|bit7|bit4; // no clock stretch, generate start, error interrupt, NACK interrupt I2C1->CR2 |= sizeof(comseq)<<16|0x78; // set nbytes and saddr I2C1->CR1 |= bit0; // enable i2c int_enable(I2C1_ER_IRQn, 10); int_enable(I2C1_EV_IRQn, 10); DMA2_Channel2->CCR = 0; // disable DMA DMA2_Channel2->CPAR = (uint32_t)&I2C1->TXDR; // peripheral i2c_tx DMA2_Channel2->CMAR = (uint32_t)&comseq; // memory array DMA2_Channel2->CCR = (bit4|bit7); // write to peripheral, memory increment, 8 bit, low priority DMAMUX1_Channel7->CCR = 17; // DMAMUX I2C1_TX DMA2_Channel2->CNDTR = sizeof(comseq); // reload amount DMA2_Channel2->CCR |= bit0; // enable DMA // send comseq I2C1->CR2 |= bit13; uint32_t to = 10000; while((DMA2->ISR&bit5) == 0) if(error || (--to == 0)) break; if(to < 2) error = 1; DMA2->IFCR |= bit4|bit5; to = 10000; while((I2C1->ISR&bit6)==0) if(error || (--to == 0)) break; if(to < 2) error = 1; I2C1->CR2 |= bit14; to = 10000; while((I2C1->ISR&bit15)==bit15) if(error || (--to == 0)) break; if(to < 2) error = 1;
View more

 

The problem is when line 37 is executed. The error variable is set if an I2C error triggers one of the interrupts. 

This is the USART init code:

 

RCC->APB2ENR |= bit14; // enable USART1 clock GPIOA->MODER &= ~(0b11<<18|0b11<<20); GPIOA->MODER |= (0b10<<18|0b10<<20); // PA9 and PA10 alternate function GPIOA->AFRH |= ((7<<4)|(7<<8)); // PA9 and PA10 AF7 -> USART1 uart_set_baudrate(USART1, 115200, 106000000); // 115200 baudrate USART1->CR1 = (bit2|bit3|bit10|bit5|bit12|bit8);// enable RX, RXIE, TX and parity even (like bootloader), PEIE USART1->CR3 |= bit0; USART1->CR1 |= bit0; // enable UART1 int_enable(USART1_IRQn, 3); // enable UART1 interrupt

 

As metioned everything works if the USART1 is initialized after the I2C1. What could cause this strange interference?

Thanks!

1 ACCEPTED SOLUTION

Accepted Solutions
Nickelgrass
Senior

Thanks for all the replies. I finally (after days ripping my hair out) found the error. It was something totally *** and unrelated. I botched up the GPIO's in an other init function. 

View solution in original post

6 REPLIES 6

Which STM32?

> USART interrupt does not work

What does that mean, exactly? What is the expected behaviour and how is the observed one different?

In debugger, read out and check relevant registers. Isn't the program "hanging" in some of the ISR  e.g. due to not handling all the interrupt sources?

JW

If the source of the interrupt is not cleared or serviced it will keep re-entering.

Show the IRQ Handler too.

Be aware that doing multiple RMW on registers is very inefficient.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..

Thanks for the reply. It means that once the I2C bus has started the DMA the USART1 receive interrupt does not work abymore. The corresponding flag is still set but the interrupt does not fire. The interrupt has the highestpriority and there is no other interrupt fireing. I would expect that the USART1 and I2C1 would work independent and that the USART receive interrupt still works after the I2C DMA has run. I put a pintoggle in the main while(1) loop and it constantly toggles, so there is not interrupt fireing constantly.

Thanks for the reply. I put a pin toggle in the main while(1) loop and that constantly toggles. So the processor does not hang in an interrupt. 

I know that RMW is inefficient. In some cases where it does not matter I use it anyway if it makes the code a bit more clear. Also some bits have to be written when a peripheral is disabled.

Sounds like an NVIC side issue.

State STM32 you're talking about

Show register dump of RCC, UART, I2C, DMA, GPIO and NVIC in failing case

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
Nickelgrass
Senior

Thanks for all the replies. I finally (after days ripping my hair out) found the error. It was something totally *** and unrelated. I botched up the GPIO's in an other init function.