cancel
Showing results for 
Search instead for 
Did you mean: 

STM32G0 USART2 TXINV / DATAINV not having effect - line stays active LOW after DMA transfer

JU
Associate III

Per the title, I'm using USART2 with DMA transfer but for some reason the line stays LOW (active) before/after transmision.

Init code is pure HAL generated, I added a "DeInit" just to make sure the UART enable wasn't persisting between resets/debug sessions but it's made no difference.

No matter what I do with the control registers (UART_ADVFEATURE_TXINV / UART_ADVFEATURE_DATAINV), the data output & idle state don't seem to change.

EDIT: DATAINV and/or TXINV do seem to invert the data bits but the idle line is *always* LOW when there's no data no matter what.

Is this some edge case where using DMA transfer keeps the UART "active" all the time rather than releasing it to "idle" once transfer is complete?

EDIT #3: It appears HAL_HalfDuplex_Init means the UART behaves this way, using HAL_UART_Init seems to allow the TX line to idle high, although now the data appears to be wrong whichever way up I invert it. Ghaaa,

void MX_USART2_UART_Init(void)
{
 
  /* USER CODE BEGIN USART2_Init 0 */
	HAL_UART_DeInit(&huart2); // Ensure UART is disabled before making changes
  /* USER CODE END USART2_Init 0 */
 
  /* USER CODE BEGIN USART2_Init 1 */
 
  /* USER CODE END USART2_Init 1 */
  huart2.Instance = USART2;
  huart2.Init.BaudRate = 2400;
  huart2.Init.WordLength = UART_WORDLENGTH_8B;
  huart2.Init.StopBits = UART_STOPBITS_1;
  huart2.Init.Parity = UART_PARITY_NONE;
  huart2.Init.Mode = UART_MODE_TX;
  huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  huart2.Init.OverSampling = UART_OVERSAMPLING_16;
  huart2.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
  huart2.Init.ClockPrescaler = UART_PRESCALER_DIV1;
  huart2.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_TXINVERT_INIT|UART_ADVFEATURE_RXOVERRUNDISABLE_INIT;
  huart2.AdvancedInit.TxPinLevelInvert = UART_ADVFEATURE_TXINV_ENABLE;
  //huart2.AdvancedInit.OverrunDisable = UART_ADVFEATURE_OVERRUN_DISABLE;
  if (HAL_HalfDuplex_Init(&huart2) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN USART2_Init 2 */
 
  /* USER CODE END USART2_Init 2 */
 
}
 
void HAL_UART_MspInit(UART_HandleTypeDef* uartHandle)
{
 
  GPIO_InitTypeDef GPIO_InitStruct = {0};
  if(uartHandle->Instance==USART2)
  {
  /* USER CODE BEGIN USART2_MspInit 0 */
 
  /* USER CODE END USART2_MspInit 0 */
    /* USART2 clock enable */
    __HAL_RCC_USART2_CLK_ENABLE();
 
    __HAL_RCC_GPIOA_CLK_ENABLE();
    /**USART2 GPIO Configuration
    PA2     ------> USART2_TX
    */
    GPIO_InitStruct.Pin = LED_ORANGE_Pin;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_OD;
    GPIO_InitStruct.Pull = GPIO_PULLUP;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    GPIO_InitStruct.Alternate = GPIO_AF1_USART2;
    HAL_GPIO_Init(LED_ORANGE_GPIO_Port, &GPIO_InitStruct);
 
    /* USART2 DMA Init */
    /* USART2_TX Init */
    hdma_usart2_tx.Instance = DMA1_Channel2;
    hdma_usart2_tx.Init.Request = DMA_REQUEST_USART2_TX;
    hdma_usart2_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
    hdma_usart2_tx.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_usart2_tx.Init.MemInc = DMA_MINC_ENABLE;
    hdma_usart2_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
    hdma_usart2_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
    hdma_usart2_tx.Init.Mode = DMA_NORMAL;
    hdma_usart2_tx.Init.Priority = DMA_PRIORITY_LOW;
    if (HAL_DMA_Init(&hdma_usart2_tx) != HAL_OK)
    {
      Error_Handler();
    }
 
    __HAL_LINKDMA(uartHandle,hdmatx,hdma_usart2_tx);
 
    /* USART2 interrupt Init */
    HAL_NVIC_SetPriority(USART2_IRQn, 1, 0);
    HAL_NVIC_EnableIRQ(USART2_IRQn);
  /* USER CODE BEGIN USART2_MspInit 1 */
 
  /* USER CODE END USART2_MspInit 1 */
  }
}

EDIT: The pin is initialised as Push-Pull: It's used as GPIO when the UART is not needed, but the switch to alternate (UART) pin function is only done once at startup:

void SetAlternateFunction(uint8_t mode)
{
	GPIO_InitTypeDef GPIO_InitStruct;
 
	if(mode)
	{
		// UART TX on PA9 (Amber LED)
		GPIO_InitStruct.Pin = LED_ORANGE_Pin;           // PA2
		GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;    	// Push pull mode;
		GPIO_InitStruct.Pull = GPIO_NOPULL;           	// No Pull up
		GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
		GPIO_InitStruct.Alternate = GPIO_AF1_USART2;		// UART mode
		HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
	}
	else
	{
		// PA9 & PA10 to GPIO (Red and Amber LED)
		GPIO_InitStruct.Pin = LED_ORANGE_Pin|LED_RED_Pin;  // PA2 & PA1
		GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;	// Push pull mode with alternate function;
		GPIO_InitStruct.Pull = GPIO_NOPULL;               // No Pull up
		GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
		GPIO_InitStruct.Alternate = 0; 										// GPIO mode
		HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
	}
}

11 REPLIES 11
JU
Associate III

So how is the data line going up & down when I enable USART2 then? 😋

You were reading comments that are from ported code, the old unit used PA9, this one uses PA2 - I've updated the top post just in case it confuses anyone else.

LED_ORANGE_Pin maps to PA2 in the code.

JU
Associate III

Well I fixed it - half-duplex mode appears to cause the TX line to idle LOW (waiting to become RX) according to a random ST powerpoint presentation I found. Not the behaviour I expected for a configuration where we only want to TX and never RX.

Anyway, switching to TX_RX mode and most importantly calling HAL_UART_Init rather than HAL_HalfDuplex_Init seems to have fixed the problem.

Annoyingly, CubeMX will not allow you to configure UART in Asychronous mode if there's no RX pin available, so we have to put a workaround in now for TX-only case.:unamused_face:

/* USER CODE END USART2_Init 1 */
	huart2.Instance = USART2;
	huart2.Init.BaudRate = 2400;
	huart2.Init.WordLength = UART_WORDLENGTH_8B;
	huart2.Init.StopBits = UART_STOPBITS_1;
	huart2.Init.Parity = UART_PARITY_NONE;
	huart2.Init.Mode = UART_MODE_TX_RX; // Initialise TXRX even though we only want to TX
	huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
	huart2.Init.OverSampling = UART_OVERSAMPLING_16;
	huart2.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
	huart2.Init.ClockPrescaler = UART_PRESCALER_DIV1;
	huart2.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
 
	if (HAL_UART_Init(&huart2) != HAL_OK)
	{
		Error_Handler();
	}