2023-06-25 01:49 PM - edited 2023-06-25 01:53 PM
I've been writing apps using the STM HAL drivers for a while. I'd like to move to the low level drivers in order to have a better understanding of what's happening in my app. Comms over a UART with DMA is a critical part of most of my apps so I'm starting with that in a simple loop back configuration (TX connected to RX). It seems like using CubeMX to generate the startup code is a good place to start. And I add the code required to do what I need. I've been able to get the TX part of simple UART app working. I've got a logic analyzer hooked up to TX and I see the message I'm sending show up when I expect it. In addition if I put breakpoints in the TX callbacks, I can see the program get to the callbacks. Unfortunately, nothing seems to be happening on the RX side. The app doesn't get to the breakpoints in the RX callbacks and the rxBuffer doesn't get any data. I'd appreciate any suggestions about what I'm doing wrong. The main.c file is attached and here's the relevant code:
ALIGN_32BYTES(const uint8_t txBuffer[]) = "$PUBX,00*33\r\n";
uint8_t txBufferSize = sizeof(txBuffer);
ALIGN_32BYTES(const uint8_t rxBuffer[32]) = { 0 };
uint8_t rxBufferSize = 8;
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_DMA_Init();
//MX_SDMMC1_SD_Init();
MX_USART1_UART_Init();
//MX_BDMA2_Init();
//MX_LPUART1_UART_Init();
//MX_FATFS_Init();
//MX_LPTIM1_Init();
//MX_TIM1_Init();
//MX_IWDG1_Init();
//MX_USART2_UART_Init();
//MX_RTC_Init();
//MX_WWDG1_Init();
//MX_UART4_Init();
//MX_I2C1_Init();
/* USER CODE BEGIN 2 */
//Blink LED just to make sure basics are working
for (int i = 0; i < 10; i++)
{
LL_GPIO_SetOutputPin(LEDGreen_GPIO_Port, LEDGreen_Pin);
LL_mDelay(100);
LL_GPIO_ResetOutputPin(LEDGreen_GPIO_Port, LEDGreen_Pin);
LL_mDelay(100);
}
/* USER CODE END 2 */
//Stream 0 = RX
LL_DMA_EnableIT_TC(DMA1, LL_DMA_STREAM_0);
LL_DMA_EnableIT_TE(DMA1, LL_DMA_STREAM_0);
LL_DMA_ConfigAddresses(DMA1,
LL_DMA_STREAM_0,
(uint32_t)rxBuffer,
LL_USART_DMA_GetRegAddr(USART1, LL_USART_DMA_REG_DATA_RECEIVE),
LL_DMA_GetDataTransferDirection(DMA1, LL_DMA_STREAM_0));
LL_DMA_SetDataLength(DMA1, LL_DMA_STREAM_0, rxBufferSize);
//Stream 1 = TX
LL_DMA_EnableIT_TC(DMA1, LL_DMA_STREAM_1);
LL_DMA_EnableIT_TE(DMA1, LL_DMA_STREAM_1);
LL_DMA_ConfigAddresses(DMA1,
LL_DMA_STREAM_1,
(uint32_t)txBuffer,
LL_USART_DMA_GetRegAddr(USART1, LL_USART_DMA_REG_DATA_TRANSMIT),
LL_DMA_GetDataTransferDirection(DMA1, LL_DMA_STREAM_1));
LL_DMA_SetDataLength(DMA1, LL_DMA_STREAM_1, txBufferSize);
LL_DMA_EnableStream(DMA1, LL_DMA_STREAM_0);
LL_USART_EnableDMAReq_RX(USART1);
LL_USART_EnableDirectionRx(USART1);
LL_DMA_EnableStream(DMA1, LL_DMA_STREAM_1);
LL_USART_EnableDMAReq_TX(USART1);
LL_USART_EnableDirectionTx(USART1);
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
LL_mDelay(1000);
LL_mDelay(10);
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
static void MX_USART1_UART_Init(void)
{
/* USER CODE BEGIN USART1_Init 0 */
/* USER CODE END USART1_Init 0 */
LL_USART_InitTypeDef USART_InitStruct = { 0 };
LL_GPIO_InitTypeDef GPIO_InitStruct = { 0 };
LL_RCC_SetUSARTClockSource(LL_RCC_USART16_CLKSOURCE_PCLK2);
/* Peripheral clock enable */
LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_USART1);
LL_AHB4_GRP1_EnableClock(LL_AHB4_GRP1_PERIPH_GPIOB);
/**USART1 GPIO Configuration
PB6 ------> USART1_TX
PB7 ------> USART1_RX
*/
GPIO_InitStruct.Pin = LL_GPIO_PIN_6 | LL_GPIO_PIN_7;
GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
GPIO_InitStruct.Alternate = LL_GPIO_AF_7;
LL_GPIO_Init(GPIOB, &GPIO_InitStruct);
/* USART1 DMA Init */
/* USART1_RX Init */
LL_DMA_SetPeriphRequest(DMA1, LL_DMA_STREAM_0, LL_DMAMUX1_REQ_USART1_RX);
LL_DMA_SetDataTransferDirection(DMA1, LL_DMA_STREAM_0, LL_DMA_DIRECTION_PERIPH_TO_MEMORY);
LL_DMA_SetStreamPriorityLevel(DMA1, LL_DMA_STREAM_0, LL_DMA_PRIORITY_LOW);
LL_DMA_SetMode(DMA1, LL_DMA_STREAM_0, LL_DMA_MODE_NORMAL);
LL_DMA_SetPeriphIncMode(DMA1, LL_DMA_STREAM_0, LL_DMA_PERIPH_NOINCREMENT);
LL_DMA_SetMemoryIncMode(DMA1, LL_DMA_STREAM_0, LL_DMA_MEMORY_INCREMENT);
LL_DMA_SetPeriphSize(DMA1, LL_DMA_STREAM_0, LL_DMA_PDATAALIGN_BYTE);
LL_DMA_SetMemorySize(DMA1, LL_DMA_STREAM_0, LL_DMA_MDATAALIGN_BYTE);
LL_DMA_DisableFifoMode(DMA1, LL_DMA_STREAM_0);
/* USART1_TX Init */
LL_DMA_SetPeriphRequest(DMA1, LL_DMA_STREAM_1, LL_DMAMUX1_REQ_USART1_TX);
LL_DMA_SetDataTransferDirection(DMA1, LL_DMA_STREAM_1, LL_DMA_DIRECTION_MEMORY_TO_PERIPH);
LL_DMA_SetStreamPriorityLevel(DMA1, LL_DMA_STREAM_1, LL_DMA_PRIORITY_LOW);
LL_DMA_SetMode(DMA1, LL_DMA_STREAM_1, LL_DMA_MODE_NORMAL);
LL_DMA_SetPeriphIncMode(DMA1, LL_DMA_STREAM_1, LL_DMA_PERIPH_NOINCREMENT);
LL_DMA_SetMemoryIncMode(DMA1, LL_DMA_STREAM_1, LL_DMA_MEMORY_INCREMENT);
LL_DMA_SetPeriphSize(DMA1, LL_DMA_STREAM_1, LL_DMA_PDATAALIGN_BYTE);
LL_DMA_SetMemorySize(DMA1, LL_DMA_STREAM_1, LL_DMA_MDATAALIGN_BYTE);
LL_DMA_DisableFifoMode(DMA1, LL_DMA_STREAM_1);
/* USART1 interrupt Init */
NVIC_SetPriority(USART1_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 0, 0));
NVIC_EnableIRQ(USART1_IRQn);
/* USER CODE BEGIN USART1_Init 1 */
/* USER CODE END USART1_Init 1 */
USART_InitStruct.PrescalerValue = LL_USART_PRESCALER_DIV1;
USART_InitStruct.BaudRate = 9600;
USART_InitStruct.DataWidth = LL_USART_DATAWIDTH_8B;
USART_InitStruct.StopBits = LL_USART_STOPBITS_1;
USART_InitStruct.Parity = LL_USART_PARITY_NONE;
USART_InitStruct.TransferDirection = LL_USART_DIRECTION_TX_RX;
USART_InitStruct.HardwareFlowControl = LL_USART_HWCONTROL_NONE;
USART_InitStruct.OverSampling = LL_USART_OVERSAMPLING_16;
LL_USART_Init(USART1, &USART_InitStruct);
LL_USART_SetTXFIFOThreshold(USART1, LL_USART_FIFOTHRESHOLD_1_8);
LL_USART_SetRXFIFOThreshold(USART1, LL_USART_FIFOTHRESHOLD_1_8);
LL_USART_DisableFIFO(USART1);
LL_USART_ConfigAsyncMode(USART1);
/* USER CODE BEGIN WKUPType USART1 */
/* USER CODE END WKUPType USART1 */
LL_USART_Enable(USART1);
/* Polling USART1 initialisation */
while ((!(LL_USART_IsActiveFlag_TEACK(USART1))) || (!(LL_USART_IsActiveFlag_REACK(USART1))))
{
}
/* USER CODE BEGIN USART1_Init 2 */
/* USER CODE END USART1_Init 2 */
}
static void MX_DMA_Init(void)
{
/* Init with LL driver */
/* DMA controller clock enable */
LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_DMA1);
/* DMA interrupt init */
/* DMA1_Stream0_IRQn interrupt configuration */
NVIC_SetPriority(DMA1_Stream0_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 0, 0));
NVIC_EnableIRQ(DMA1_Stream0_IRQn);
/* DMA1_Stream1_IRQn interrupt configuration */
NVIC_SetPriority(DMA1_Stream1_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 0, 0));
NVIC_EnableIRQ(DMA1_Stream1_IRQn);
/* DMA1_Stream2_IRQn interrupt configuration */
HAL_NVIC_SetPriority(DMA1_Stream2_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(DMA1_Stream2_IRQn);
/* DMA1_Stream3_IRQn interrupt configuration */
HAL_NVIC_SetPriority(DMA1_Stream3_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(DMA1_Stream3_IRQn);
/* DMA1_Stream4_IRQn interrupt configuration */
NVIC_SetPriority(DMA1_Stream4_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 0, 0));
NVIC_EnableIRQ(DMA1_Stream4_IRQn);
/* DMA1_Stream5_IRQn interrupt configuration */
NVIC_SetPriority(DMA1_Stream5_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 0, 0));
NVIC_EnableIRQ(DMA1_Stream5_IRQn);
/* DMA1_Stream6_IRQn interrupt configuration */
NVIC_SetPriority(DMA1_Stream6_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 0, 0));
NVIC_EnableIRQ(DMA1_Stream6_IRQn);
/* DMA1_Stream7_IRQn interrupt configuration */
NVIC_SetPriority(DMA1_Stream7_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 0, 0));
NVIC_EnableIRQ(DMA1_Stream7_IRQn);
}
void dma1TXDoneCallback(void)
{
uint8_t tx_cmplt = 1;
}
void dma1RXDoneCallback(void)
{
uint8_t rx_cmplt = 1;
}
void uart1DMAErrorCallback(void)
{
LL_DMA_DisableStream(DMA1, LL_DMA_STREAM_0);
LL_DMA_DisableStream(DMA1, LL_DMA_STREAM_1);
//led_blink(LED_BLINK_RATE_ERROR);
}
void DMA1_Stream0_IRQHandler(void)
{
if (LL_DMA_IsActiveFlag_TC0(DMA1))
{
LL_DMA_ClearFlag_TC0(DMA1);
dma1RXDoneCallback();
}
else if (LL_DMA_IsActiveFlag_TE0(DMA1))
{
LL_DMA_ClearFlag_TE0(DMA1);
uart1DMAErrorCallback();
}
else
{
//Do something
}
}
void DMA1_Stream1_IRQHandler(void)
{
if (LL_DMA_IsActiveFlag_TC1(DMA1))
{
LL_DMA_ClearFlag_TC1(DMA1);
dma1TXDoneCallback();
}
else if (LL_DMA_IsActiveFlag_TE1(DMA1))
{
LL_DMA_ClearFlag_TE1(DMA1);
uart1DMAErrorCallback();
}
else
{
//Do something...
}
}
Solved! Go to Solution.
2023-06-28 08:11 PM - edited 2023-06-28 09:40 PM
I discovered my first mistake, I switched the source and destination in the LL_DMA_ConfigAddresses line for the RX direction. It was this:
LL_DMA_ConfigAddresses(DMA1,
LL_DMA_STREAM_0,
(uint32_t)rxBuffer,
LL_USART_DMA_GetRegAddr(USART1, LL_USART_DMA_REG_DATA_RECEIVE),
LL_DMA_GetDataTransferDirection(DMA1, LL_DMA_STREAM_0));
and it should have been this:
LL_DMA_ConfigAddresses(DMA1,
LL_DMA_STREAM_0,
LL_USART_DMA_GetRegAddr(USART1, LL_USART_DMA_REG_DATA_RECEIVE),
(uint32_t)rxBuffer,
LL_DMA_GetDataTransferDirection(DMA1, LL_DMA_STREAM_0));
The logic analyzer tells me I'm sending my entire 14 byte txBuffer. I set the 8 byte rxBuffer to all 0 and set the RX transfer for 8 bytes. The first byte of the txBuffer winds up in the first byte of the rxBuffer but all the other rxBuffer bytes stay at 0. I've checked the DMA1_Stream0->NDTR register which is where I think the rx transfer length is set and it's 8 before the transfer and 0 after the transfer, which seems right. I'm still looking but if someone has a suggestion where else to look, I'd love to hear it. Thanks.
2023-06-25 02:02 PM - edited 2023-06-25 02:09 PM
Which STM32? What hardware?
Polled UART Rx works?
Read out and check content of UART, DMA/DMAMUX and relevant GPIO registers.
In case of Cortex-M7-based STM32, watch out for the caches.
JW
2023-06-25 02:44 PM - edited 2023-06-25 03:03 PM
One frequent culprit is receiver overrun. It can block RX. It can occur in a time window from enabling RX to starting the DMA.
LL_USART_DisableOverrunDetect(USART1);
LL_USART_ClearFlag_ORE(USART1);
It's good that your underwater things are unmanned )) Sorry but cannot help...
2023-06-25 04:28 PM - edited 2023-06-25 04:46 PM
@waclawek.jan I came across the article re: caches that you pointed me at. I was hoping that the last comment in that thread suggesting it had been fixed in the latest versions of CubeMX was true. And the fact I've been using the HAL Drivers without problem. Regardless I'll check to see if I can figure out the memory map.
I think I mentioned it's a STM32H7A3. And I'm using the Nucleo-H7A3ZI-Q board. I didn't include this in what I posted but this works:
uint8_t rxChar[16] = { 0 };
for (int i = 0; i < sizeof(txBuffer); i++)
{
while(!LL_USART_IsActiveFlag_TXE(USART1))
{}
LL_USART_TransmitData8(USART1, txBuffer[i]);
while (!LL_USART_IsActiveFlag_RXNE(USART1))
{}
rxChar[i] = LL_USART_ReceiveData8(USART1);
}
2023-06-25 04:45 PM - edited 2023-06-25 04:46 PM
@Pavel A. I added the code you suggested but it didn't fix the problem.
WRT our underwater things, if they were manned, nobody would be letting me anywhere near the code that runs them.
2023-06-25 10:05 PM
Read out and check/post content of UART and relevant DMA/DMAMUX registers, compare before and after receiving a certain amount of data (start with a single byte).
At the same time, avoid observing the UART registers in debugger *during* reception.
JW
2023-06-28 08:11 PM - edited 2023-06-28 09:40 PM
I discovered my first mistake, I switched the source and destination in the LL_DMA_ConfigAddresses line for the RX direction. It was this:
LL_DMA_ConfigAddresses(DMA1,
LL_DMA_STREAM_0,
(uint32_t)rxBuffer,
LL_USART_DMA_GetRegAddr(USART1, LL_USART_DMA_REG_DATA_RECEIVE),
LL_DMA_GetDataTransferDirection(DMA1, LL_DMA_STREAM_0));
and it should have been this:
LL_DMA_ConfigAddresses(DMA1,
LL_DMA_STREAM_0,
LL_USART_DMA_GetRegAddr(USART1, LL_USART_DMA_REG_DATA_RECEIVE),
(uint32_t)rxBuffer,
LL_DMA_GetDataTransferDirection(DMA1, LL_DMA_STREAM_0));
The logic analyzer tells me I'm sending my entire 14 byte txBuffer. I set the 8 byte rxBuffer to all 0 and set the RX transfer for 8 bytes. The first byte of the txBuffer winds up in the first byte of the rxBuffer but all the other rxBuffer bytes stay at 0. I've checked the DMA1_Stream0->NDTR register which is where I think the rx transfer length is set and it's 8 before the transfer and 0 after the transfer, which seems right. I'm still looking but if someone has a suggestion where else to look, I'd love to hear it. Thanks.
2023-06-29 01:02 AM
Switch off caches to take care of the most common problem, for testing.
> The first byte of the txBuffer winds up in the first byte of the rxBuffer but all the other rxBuffer bytes stay at 0.
Is it the *first* byte or the *last* byte?
And why are you observing 14 bytes to be transferred, yet receive 8? This is strange, but should have no further consequences except reformulating the above questions as "do you see in Rx buffer the *first* byte or the *8th*?
Read out and check/post content of UART and relevant DMA/DMAMUX registers, compare before and after receiving a certain amount of data (start with a single byte).
JW
2023-06-29 03:23 AM
2023-06-29 01:10 PM
@Pavel A. I have seen that, I'm using it another example for a different STM32 MCU as my examples. Unfortunately, I haven't been able to get Majerle's example working either. Still working on it though. It's a background task for me so so I don't get to work on it as much as I'd like. As always, thanks for all your help.