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;

 

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.