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);
	}
}

1 ACCEPTED SOLUTION

Accepted Solutions
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();
	}

View solution in original post

11 REPLIES 11
TDK
Guru

UART is normally idle high. You've flipped polarity with TXINV, so now the line is idle low. Are you expecting it to only invert data bits and not start/stop/idle as well?

0693W00000KbHtgQAF.png

If you feel a post has answered your question, please click "Accept as Solution".
JU
Associate III

Please check what I said - there is NO combination of TXINV and DATAINV that lets the TX idle high despite what the datasheet says.

This is my experience - to be clear right now I do not care which way up the data is but I need TX to idle HIGH:

0693W00000KbI2dQAF.png

TDK
Guru

Not sure. Are you sure the line isn't AC coupled somehow? What exactly is the hardware you're working on and where/how are you measuring the line? Do other UARTs behave the same way?

If you feel a post has answered your question, please click "Accept as Solution".
JU
Associate III

STM32G031 in SSOP20 package - UART TX pin is driving straight into a 1k resistor on the base of a simple transistor (driving an LED to blink data).

No decoupling, no pullups or down, pin configured push-pull AF_UART all using CubeMX generated initialisation & DMA transmit routines (HAL_UART_Transmit_DMA), the software is very basic.

Scope probe directly onto micro pin, the TX line is working OK sending correct data with sharp edges and no issues. Micro's running form 3.3v supply and hitting the rails cleanly.

> pin configured push-pull AF_UART

Code above initializes it as open-drain with pullup. Both should work at 2400 baud, of course.

> GPIO_InitStruct.Pin = LED_ORANGE_Pin;

> GPIO_InitStruct.Mode = GPIO_MODE_AF_OD;

> GPIO_InitStruct.Pull = GPIO_PULLUP;

It's also called LED_ORANGE. Not than the name matters, but sure you're not treating it as an LED somewhere else in the code?

Gotta be an answer somewhere, but I don't see it in the code provided. I do not know of a mode in which UART is idle low for no reason.

If you feel a post has answered your question, please click "Accept as Solution".
JU
Associate III

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;           // PA9
		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_USART1;		// 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;  // PA9 & PA10
		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);
	}
}

So the output register should then be ignoring any calls like:

HAL_GPIO_WritePin( LED_ORANGE_GPIO_Port, LED_ORANGE_Pin, GPIO_PIN_SET );

Because the GPIO output register is not connected to the pin.

Putting in calls to set the GPIO pin HIGH before switching to UART mode makes no difference, removing/disabling any GPIO_WritePin calls when the UART is enabled also makes no difference.

gbm
Lead III

What you observe is exactly what you ordered the uC to do. TX is set as open drain with ~50k pull-up, so it's never driven high. The pin is pulled down by 1k resistor in transistor's base. There is no way it could output anyhing above 0.8V. Set the pin to PUSH-PULL.

JU
Associate III

Please see edit / reply to 'TDK' above - the pin is initialised as push-pull at startup if the UART is going to be used and the TX pin is hitting 3v3 / 0v quite happily when sending data, it's purely the idle state that's incorrect.

void SetAlternateFunction(uint8_t mode)
{
	GPIO_InitTypeDef GPIO_InitStruct;
 
	if(mode)
	{
		// UART TX on PA9 (Amber LED)
		GPIO_InitStruct.Pin = LED_ORANGE_Pin;           // PA9
		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;  // PA9 & PA10
		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);
	}
}

gbm
Lead III

In STM32 USART2 is not connected to PA9/PA10. It's USART1 you should be controlling.