2023-11-18 10:59 AM
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!
Solved! Go to Solution.
2023-11-22 08:59 AM
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.
2023-11-18 02:20 PM - edited 2023-11-18 02:20 PM
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
2023-11-18 04:05 PM
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.
2023-11-20 10:39 AM
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.
2023-11-20 10:41 AM
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.
2023-11-20 11:18 AM - edited 2023-11-20 11:45 AM
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
2023-11-22 08:59 AM
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.