2019-07-29 01:00 PM
I am working with the STM32F7. I am implementing a DMA communications handler, preferably using the HAL (but LL is also an option that I have tried). I have successfully implemented the TX side of the equation but, am unable to get the RX portion to put data into the buffer. Below I have detailed the HW configuration and provided the HAL based code to match. I can also provide the LL based code should anyone want to see that. Can someone help me to see the error of my ways?
I am hoping to use the idle line detection to allow variable packet size, but cannot get the RX to work even with a fixed packet size.
I have used the following references:
Example code for Idle Line with f4: https://github.com/MaJerle/STM32_USART_DMA_RX
DMA Controller Guide: https://www.st.com/content/ccc/resource/technical/document/application_note/27/46/7c/ea/2d/91/40/a9/DM00046011.pdf/files/DM00046011.pdf/jcr:content/translations/en.DM00046011.pdf
Platform: NUCLEO-F767ZI
RTOS: uC/OS-III
UART: UART4 (RX: PC11, TX: PC10)
UART Config:
#define MAIN_BOARD_UART_BAUD 115200
#define MAIN_BOARD_UART_WORDLENGTH UART_WORDLENGTH_8B
#define MAIN_BOARD_UART_STOPBITS UART_STOPBITS_1
#define MAIN_BOARD_UART_PARITY UART_PARITY_NONE
#define MAIN_BOARD_UART_HW_FLOW_CTL UART_HWCONTROL_NONE
#define MAIN_BOARD_UART_MODE UART_MODE_TX_RX
#define MAIN_BOARD_UART_OVERSAMPLING UART_OVERSAMPLING_16
DMA, Channel, Stream: DMA1, Channel 4, Stream 4
DMA Config: See Code below
UART Init Code:
__HAL_RCC_UART4_CLK_ENABLE();
__HAL_RCC_GPIOC_CLK_ENABLE();
GPIO_InitStruct.Pin = USARTx_RX_PIN | USARTx_TX_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FAST;
GPIO_InitStruct.Alternate = USARTx_RX_AF | USARTx_TX_AF;
//Init the UART - Configured thorugh macros in .h for portability
HAL_UART_MspInit(&CommUartHandle);
CommUartHandle.Instance = MAIN_BOARD_UART_INSTANCE;
CommUartHandle.Init.BaudRate = MAIN_BOARD_UART_BAUD;
CommUartHandle.Init.WordLength = MAIN_BOARD_UART_WORDLENGTH;
CommUartHandle.Init.StopBits = MAIN_BOARD_UART_STOPBITS;
CommUartHandle.Init.Parity = MAIN_BOARD_UART_PARITY;
CommUartHandle.Init.HwFlowCtl = MAIN_BOARD_UART_HW_FLOW_CTL;
CommUartHandle.Init.Mode = MAIN_BOARD_UART_MODE;
CommUartHandle.Init.OverSampling = MAIN_BOARD_UART_OVERSAMPLING;
if(HAL_UART_Init(&CommUartHandle) != HAL_OK)
{
BSP_LED_On(USER_LD2);
while(1) {;}
}
HAL_NVIC_SetPriority(UART4_IRQn, 5, 0);
HAL_NVIC_EnableIRQ(UART4_IRQn);
DMA Init Code:
__HAL_RCC_DMA1_CLK_ENABLE();
__HAL_DMA_CLEAR_FLAG(&dma_comm_rx, DMA_FLAG_TCIF2_6 | DMA_FLAG_HTIF2_6 | DMA_FLAG_TEIF2_6 | DMA_FLAG_DMEIF2_6 | DMA_FLAG_FEIF2_6);
dma_comm_rx.Instance = DMA1_Stream2;
dma_comm_rx.Init.Channel = DMA_CHANNEL_4;
dma_comm_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
dma_comm_rx.Init.PeriphInc = DMA_PINC_DISABLE;
dma_comm_rx.Init.MemInc = DMA_MINC_ENABLE;
dma_comm_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
dma_comm_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
dma_comm_rx.Init.Mode = DMA_CIRCULAR ;
dma_comm_rx.Init.Priority= DMA_PRIORITY_VERY_HIGH;
dma_comm_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
dma_comm_rx.Init.PeriphBurst = DMA_PBURST_SINGLE;
if(HAL_DMA_Init(&dma_comm_rx) != HAL_OK){
while(1);
}
__HAL_LINKDMA(huart,hdmarx,dma_comm_rx);
__HAL_DMA_ENABLE(&dma_comm_rx);
__HAL_DMA_ENABLE_IT(huart->hdmarx, DMA_IT_TC);
__HAL_DMA_ENABLE_IT(huart->hdmarx, DMA_IT_HT);
HAL_NVIC_SetPriority(DMA1_Stream2_IRQn, 5, 0);
HAL_NVIC_EnableIRQ(DMA1_Stream2_IRQn);
Interrupt Code:
void DMA1_Stream2_IRQHandler(void){
HAL_DMA_IRQHandler(&dma_comm_rx);
}
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart){
OS_ERR os_err;
huart->gState = HAL_UART_STATE_READY;
OSSemPost(&CommRxSem,OS_OPT_POST_1,&os_err);
HAL_UART_Receive_DMA(&CommUartHandle, rxDmaBuffer, 10);
BSP_LED_Off(USER_LD3);
}
void HAL_UART_RxHalfCpltCallback(UART_HandleTypeDef *huart){
OS_ERR os_err;
huart->gState = HAL_UART_STATE_READY;
OSSemPost(&CommRxSem,OS_OPT_POST_1,&os_err);
BSP_LED_Off(USER_LD3);
}
void UART4_IRQHandler(void){
OS_ERR os_err;
if (LL_USART_IsEnabledIT_IDLE(UART4) && LL_USART_IsActiveFlag_IDLE(UART4)) {
LL_USART_ClearFlag_IDLE(UART4); // Clear IDLE line flag
//HAL_UART_Receive_DMA(&CommUartHandle, rxDmaBuffer, 100);
CommUartHandle.gState = HAL_UART_STATE_READY;
OSSemPost(&CommRxSem,OS_OPT_POST_1,&os_err);
BSP_LED_Off(USER_LD3);
}
HAL_UART_IRQHandler(&CommUartHandle);
}
Receive Code:
status = HAL_UART_Receive_DMA(&CommUartHandle, rxDmaBuffer, 100);
if(status !=HAL_OK){
while(1){;}
}
while(DEF_TRUE){
BSP_LED_On(USER_LD3);
OSSemPend(&CommRxSem, 0, OS_OPT_PEND_BLOCKING, DEF_NULL, &os_err);
BSP_LED_Off(USER_LD3);
printf("end%s\n",rxDmaBuffer );
}
Solved! Go to Solution.
2019-07-30 02:38 PM
Well, it appears that you've captured the registers after Cube succeeded to clean up (it even cleared the peripheral- and memory-address registers and also NDTR of the DMA stream, which is weird, but then not everything in Cube makes sense), so there's no evidence of any significant error there. The only flag set in DMA_LISR is TCIF2 (ie. transfer complete interrupt for Stream 2), which is explained by the fact that it is set when software disables the channel forcibly (by clearing DMA_SxCR.EN) and Cube probably has disabled the DMA stream interrupt by that time.
Try to set a breakpoint in whatever error handlers Cube uses in both the UART and DMA ISR, and observe the registers there.
JW
2019-07-29 02:03 PM
Polled implementation works?
Read out and check content of UART and DMA registers (carefully, readig UART status register clears its bits). Is Rx DMA enabled in UART? Does NDTR in DMA change? Is there no error signaled in DMA's status register?
JW
2019-07-29 02:17 PM
JW,
Thanks for the response, all great questions! I can make the interrupt and blocking implementations work, but not any variety of the DMA operation. I have carefully watched the UART and DMA registers. The RX DMA is enabled, but NDTR does not change at any point - If i tweak a few lines of code I can get it in a situation where I get a DMA_TRANSFER_ERROR from the UART, but I can also avoid that - Not sure which is a better scenario.
Thanks!
2019-07-30 12:50 AM
Post the relevant UART ad DMA registers content.
JW
2019-07-30 07:51 AM
JW the Registers are below in text and screenshot from IAR as well as attached in a log file. These values are captured when the DMA1_Stream2_IRQHandler fires after a transmission of 5 bytes. Please note the UART shows Error 8 (0verrun) in this scenario, not sure why that is.
Thanks!
Expression Value Location Type
CommUartHandle <struct> 0x20003A78 UART_HandleTypeDef
Instance 0x40004C00 0x20003A78 USART_TypeDef *
Init <struct> 0x20003A7C UART_InitTypeDef
AdvancedInit <struct> 0x20003A9C struct <Unnamed 23>
pTxBuffPtr 0x8007DA8 "Beat\r\n" 0x20003AC4 uint8_t *
TxXferSize 6 0x20003AC8 uint16_t
TxXferCount 0 0x20003ACA uint16_t
pRxBuffPtr 0x20003AE8 "" 0x20003ACC uint8_t *
RxXferSize 10 0x20003AD0 uint16_t
RxXferCount 0 0x20003AD2 uint16_t
Mask 0 0x20003AD4 uint16_t
hdmatx dma_comm_tx (0x20003B4C) 0x20003AD8 DMA_HandleTypeDef *
hdmarx dma_comm_rx (0x20003BAC) 0x20003ADC DMA_HandleTypeDef *
Instance 0x40026040 0x20003BAC DMA_Stream_TypeDef *
Init <struct> 0x20003BB0 DMA_InitTypeDef
Lock HAL_LOCKED 0x20003BE0 HAL_LockTypeDef
State HAL_DMA_STATE_ABORT 0x20003BE1 enum <Unnamed 16>
Parent CommUartHandle (0x20003A78) 0x20003BE4 void *
XferCpltCallback UART_DMAReceiveCplt (0x8006061) 0x20003BE8 void (*)(struct __DMA_HandleTypeDef *)
XferHalfCpltCallback UART_DMARxHalfCplt (0x80060A9) 0x20003BEC void (*)(struct __DMA_HandleTypeDef *)
XferM1CpltCallback 0x00000000 0x20003BF0 void (*)(struct __DMA_HandleTypeDef *)
XferM1HalfCpltCallback 0x00000000 0x20003BF4 void (*)(struct __DMA_HandleTypeDef *)
XferErrorCallback UART_DMAError (0x80060B9) 0x20003BF8 void (*)(struct __DMA_HandleTypeDef *)
XferAbortCallback UART_DMAAbortOnError (0x8006109) 0x20003BFC void (*)(struct __DMA_HandleTypeDef *)
ErrorCode 0 0x20003C00 uint32_t
StreamBaseAddress 1073897472 0x20003C04 uint32_t
StreamIndex 16 0x20003C08 uint32_t
Lock HAL_UNLOCKED 0x20003AE0 HAL_LockTypeDef
gState HAL_UART_STATE_READY 0x20003AE1 enum <Unnamed 17>
RxState HAL_UART_STATE_READY 0x20003AE2 enum <Unnamed 17>
ErrorCode 8 0x20003AE4 uint32_t
rxDmaBuffer <array>"" 0x20003AE8 uint8_t[100]
2019-07-30 08:22 AM
I mean the hardware registers of UART and DMA, not HAL/Cube's intestinal content.
JW
2019-07-30 08:30 AM
> These values are captured when the DMA1_Stream2_IRQHandler fires after a transmission of 5 bytes.
DMA1_Stream2 (Channel 4) is UART4_RX, so it's certainly not the consequence of *transmitting* bytes.
> UART shows Error 8 (0verrun) in this scenario, not sure why that is.
STM32Cube_FW_F7_V1.7.0\Drivers\STM32F7xx_HAL_Driver\Inc\stm32f7xx_hal_usart.h:
#define HAL_USART_ERROR_ORE ((uint32_t)0x00000008U) /*!< Overrun error */
Overrun may be consequence of bytes arriving at UART while not being picked by a stuck DMA.
Observing the relevant DMA registers (i.e. the givern stream's registers plus the status registers) should shed more light on the problem.
In case it wasn't obvious, I don't use Cube/HAL and I also don't use 'F7 so if this is related to some memory mapping issue I am out.
JW
2019-07-30 08:53 AM
JW - sorry for the confusing wording - I meant transmitting 5 bytes to the device, not from the device. Using a USB-UART cable to send data to remove any uncertainty.
The DMA Registers are:
Name Value Access
LISR 0x00200000 ReadOnly
HISR 0x00000000 ReadOnly
LIFCR 0x00000000 ReadWrite
HIFCR 0x00000000 ReadWrite
S0CR 0x00000000 ReadWrite
S0NDTR 0x00000000 ReadWrite
S0PAR 0x00000000 ReadWrite
S0M0AR 0x00000000 ReadWrite
S0M1AR 0x00000000 ReadWrite
S0FCR 0x00000021 ReadWrite
S1CR 0x00000000 ReadWrite
S1NDTR 0x00000000 ReadWrite
S1PAR 0x00000000 ReadWrite
S1M0AR 0x00000000 ReadWrite
S1M1AR 0x00000000 ReadWrite
S1FCR 0x00000021 ReadWrite
S2CR 0x0803051E ReadWrite
S2NDTR 0x00000000 ReadWrite
S2PAR 0x00000000 ReadWrite
S2M0AR 0x00000000 ReadWrite
S2M1AR 0x00000000 ReadWrite
S2FCR 0x000000A0 ReadWrite
S3CR 0x00000000 ReadWrite
S3NDTR 0x00000000 ReadWrite
S3PAR 0x00000000 ReadWrite
S3M0AR 0x00000000 ReadWrite
S3M1AR 0x00000000 ReadWrite
S3FCR 0x00000021 ReadWrite
S4CR 0x08020446 ReadWrite
S4NDTR 0x00000000 ReadWrite
S4PAR 0x40004C28 ReadWrite
S4M0AR 0x08007DA8 ReadWrite
S4M1AR 0x00000000 ReadWrite
S4FCR 0x000000A0 ReadWrite
S5CR 0x00000000 ReadWrite
S5NDTR 0x00000000 ReadWrite
S5PAR 0x00000000 ReadWrite
S5M0AR 0x00000000 ReadWrite
S5M1AR 0x00000000 ReadWrite
S5FCR 0x00000021 ReadWrite
S6CR 0x00000000 ReadWrite
S6NDTR 0x00000000 ReadWrite
S6PAR 0x00000000 ReadWrite
S6M0AR 0x00000000 ReadWrite
S6M1AR 0x00000000 ReadWrite
S6FCR 0x00000021 ReadWrite
S7CR 0x00000000 ReadWrite
S7NDTR 0x00000000 ReadWrite
S7PAR 0x00000000 ReadWrite
S7M0AR 0x00000000 ReadWrite
S7M1AR 0x00000000 ReadWrite
S7FCR 0x00000021 ReadWrite
UART Registers are:
Name Value Access
CR1 0x0000000D ReadWrite
CR2 0x00000000 ReadWrite
CR3 0x00000000 ReadWrite
BRR 0x000001D5 ReadWrite
GTPR 0x00000000 ReadWrite
RTOR 0x00000000 ReadWrite
RQR WWWWWWWW WriteOnly
ISR 0x006000F8 ReadOnly
ICR WWWWWWWW WriteOnly
RDR 0x00000048 ReadOnly
TDR 0x0000000A ReadWrite
2019-07-30 02:38 PM
Well, it appears that you've captured the registers after Cube succeeded to clean up (it even cleared the peripheral- and memory-address registers and also NDTR of the DMA stream, which is weird, but then not everything in Cube makes sense), so there's no evidence of any significant error there. The only flag set in DMA_LISR is TCIF2 (ie. transfer complete interrupt for Stream 2), which is explained by the fact that it is set when software disables the channel forcibly (by clearing DMA_SxCR.EN) and Cube probably has disabled the DMA stream interrupt by that time.
Try to set a breakpoint in whatever error handlers Cube uses in both the UART and DMA ISR, and observe the registers there.
JW
2019-07-30 03:34 PM
Thank you very much for your help - This helped me to find the issue. Turns out the HAL had not cleaned up the registers yet, but rather had never set them. The __HAL_DMA_ENABLE() had set the S2SCR->EN bit high, preventing the call to HAL_UART_Receive_DMA() (and then HAL_DMA_Start_IT()>DMA_SetConfig() )from changing the peripheral and memory address registers - Good lesson in making sure I know how ALL the registers relate to teach other. If you ever find yourself in Colorado, beers on me!