2024-12-12 06:23 AM
Hello,
I am experiencing an interesting problem on the STM32F303VET:
UART1 is used to receive and transmit messages (460800 Baud), DMA1 Channel 4 is used for memory to UART TX buffer transfer, DMA1 Channel 5 for UART RX buffer to memory transfer.
Under situations of high load (both reception and transmission) UART1 will stop outputting messages after two bytes. The majority of transmissions work out fine, however the error happens in every test case, though the time to failure differes between approximately 5s and 120s. The problem seems to be caused by an unintended reset of the DMA enable transmitter (DMAT) Bit in UART 1 control register 3 (USART_CR3).
Above image shows the error:
Trace 1 (orange) shows the TX line of UART1, where only two bytes are transmitted.
Trace 2 (red) shows data on RX line. One can see that no RX takes place at the same time as TX.
Trace 3 shows a debug pin, it is triggered once when DMA transfer is started.
Trace 4 shows another debug pin. It is set HIGH when DMA transfer is started and is set LOW in DMA Transfer finished interrupt triggers (which never happens, because DMA never finishes its transfer, because UART stops emitting data).
A look at the peripheral registers shows the following:
UART 1 CR3 shows that DMAT is off. It was not set to off by software anywhere!
DMA1 Channel 4 Configuration register shows that DMA channel is enabled.
DMA1 Channel 4 count down register shows that there are still 74 (0x4a) bytes remaining to be transmitted.
The problem can be reproduced. Resetting the UART peripheral can reset the error case.
Same procedure was tested on STM32F405 and STM32F469 but could not be reproduced there.
May this be a hardware issue?
If any additional information is needed, let me know.
2024-12-12 11:31 AM
Check USART ISR registers for error codes. Probably a frame error or other noise error is occurring. You need to deal with it before the peripheral will start working again.
2024-12-12 12:11 PM
Show your relevant code
2024-12-12 11:38 PM
USART 1 interrupt and status register:
Overrun error (ORE) Flag is active, however this should only flag received data and should definitely not disable DMA for the transmit stage.
2024-12-13 01:47 AM
Here you go. Code is based on the old STM Standard peripheral library.
UART Initialization:
//Disable all interrupts
USART_ITConfig(USART1, USART_IT_CM, DISABLE);
USART_ITConfig(USART1, USART_IT_EOB, DISABLE);
USART_ITConfig(USART1, USART_IT_RTO, DISABLE);
USART_ITConfig(USART1, USART_IT_CTS, DISABLE);
USART_ITConfig(USART1, USART_IT_LBD, DISABLE);
USART_ITConfig(USART1, USART_IT_TXE, DISABLE);
USART_ITConfig(USART1, USART_IT_TC, DISABLE);
USART_ITConfig(USART1, USART_IT_RXNE, DISABLE);
USART_ITConfig(USART1, USART_IT_IDLE, DISABLE);
USART_ITConfig(USART1, USART_IT_PE, DISABLE);
USART_ITConfig(USART1, USART_IT_ERR, DISABLE);
USART_DeInit(USART1);
//Enable USART clock.
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
//Enable TX GPIO
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOC, ENABLE);
GPIO_PinAFConfig(GPIOC, GPIO_PinSource4, GPIO_AF_7);
/* Configure USART Tx as alternate function push-pull */
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_Init(GPIOC, &GPIO_InitStructure);
//Enable RX GPIO
GPIO_PinAFConfig(GPIOC, GPIO_PinSource5, GPIO_AF_7);
/* Configure USART Rx as input floating */
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOC, &GPIO_InitStructure);
/* USART configuration */
USART_InitTypeDef USART_InitStructure;
USART_InitStructure.USART_BaudRate = 460800;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;
USART_Init(USART1, &USART_InitStructure);
/* Enable USART */
USART_Cmd(USART1, ENABLE);
/*Enable IDLE interrupt*/
USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);
//Set interrupt with desired priority.
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 14;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
DMA Setup:
NVIC_InitTypeDef NVIC_InitStructure = {0};
/*Enable the DMA clock */
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
/*RX DMA*/
NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel5_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 14;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
/*TX DMA*/
NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel4_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 14;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
Transmit via DMA:
/*Transmit via DMA*/
DMA_InitTypeDef DMAInitStructure = {0};
//Disable USART DMA
USART_DMACmd(USART1, USART_DMAReq_Tx, DISABLE);
//Disable DMA Stream.
DMA_Cmd(DMA1_Channel4, DISABLE);
//Configure DMA Stream.
DMA_DeInit(DMA1_Channel4);
//Clear DMA flags.
DMA_ClearFlag(DMA_ISR_TCIF4 | DMA_ISR_HTIF4 | DMA_ISR_TEIF4);
//Configure DMA
strDMAInitStructure.DMA_PeripheralBaseAddr = (volatile uint32_t)&(USART1->TDR);
strDMAInitStructure.DMA_MemoryBaseAddr = (uint32_t)Buffer;
strDMAInitStructure.DMA_DIR = DMA_DIR_PeripheralDST; //DMA_DIR_MemoryToPeripheral;
strDMAInitStructure.DMA_BufferSize = BufferSize;
strDMAInitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
strDMAInitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
strDMAInitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
strDMAInitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
strDMAInitStructure.DMA_Mode = DMA_Mode_Normal;
strDMAInitStructure.DMA_Priority = DMA_Priority_VeryHigh;
strDMAInitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_Init(DMA1_Channel4, &strDMAInitStructure);
DMA_ITConfig(DMA1_Channel4, DMA_IT_TC | DMA_IT_TE , ENABLE);
//Clear TC Flag in UART
USART_ClearFlag(USART1, USART_FLAG_TC)
//Enable USART DMA
USART_DMACmd(USART1, USART_DMAReq_Tx, ENABLE);
//DMA Stream enable.
DMA_Cmd(DMA1_Channel4, ENABLE);
//------
//Here Program waits for DMA_TC Interrupt using OS
//------
DMA_ITConfig(DMA1_Channel4, DMA_IT_TC | DMA_IT_TE | DMA_IT_HT, DISABLE);
//Wait for TX to finish
while(USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET){}
Associated DMA ISR:
//DMA1CH4 ISR
//DMA Stream disable.
DMA_Cmd(DMA1_Channel4, DISABLE);
//Transfer complete?
if((DMA_GetITStatus(DMA_ISR_TCIF4) == SET)){
//Clear pending bits.
DMA_ClearITPendingBit(DMA_ISR_TCIF4 | DMA_ISR_HTIF4 | DMA_ISR_TEIF4);
//Clear DMA flags.
DMA_ClearFlag(DMA1_FLAG_TC4 | DMA1_FLAG_HT4 | DMA1_FLAG_TE4);
//Disable USART DMA
USART_DMACmd(USART1 USART_DMAReq_Tx, DISABLE);
usart_tx_status = USART_OK; //flag to indicate successful transmission
//---
//Resume TX function by OS
//---
}else{
//Clear pending bits.
DMA_ClearITPendingBit(DMA_ISR_TCIF4 | DMA_ISR_HTIF4 | DMA_ISR_TEIF4);
//Clear DMA flags.
DMA_ClearFlag(DMA1_FLAG_TC4 | DMA1_FLAG_HT4 | DMA1_FLAG_TE4);
//Disable USART DMA
USART_DMACmd(USART1 USART_DMAReq_Tx, DISABLE);
usart_tx_status = USART_ERROR; //flag to indicate error in transmission
//---
//Resume TX function by OS
//---
}
2024-12-13 11:42 AM
Relevant code means your Rx/Tx routine and IRQ handler.
2024-12-13 01:14 PM
If DMAT=0 it's not going to be sending anything out with DMA.
Set a hardware watchpoint to figure out what is modifying this memory. Probably it's in your code somewhere.
While ORE may not be the direct issue with transmission, it certainly indicates there is an issue with the code. Characters are being sent while the peripheral is not being read out quickly enough. Solving that may solve the other problem.
2024-12-16 11:01 PM
Using a hardware watchpoint on the register is not possible to my knowledge, because a storage position in the peripheral, not in RAM, has to be watched. Additionally, DMAT is set and reset for every outgoing transmission. Since the error case is not directly reproducable (only by letting the UART run under high load for some time), I don't see a way to make sure the watchpoint triggers on the wanted event.
Further tests have shown that the ORE bit is not set every time the error occurs. Since the UART is embedded into a bigger project, overruns can occur and are acceptable. An RTOS schedules the whole program, and the handling of incoming UART transmissions has a rather low priority.
2024-12-16 11:02 PM
Code is listed above