cancel
Showing results for 
Search instead for 
Did you mean: 

Emul usart AN4457 issue

PCu1
Senior

Hello,

Has anyone ever used the emulated usart provided by ST (AN4457) based on timer and DMA?

I need it for transmit, not for reception.

In my test, most of the time TX characters are corrupted, sometimes it works.

I checked with scope and it appears that the startBit is missing, but the rest of the byte is correct.

Here example with the 'a', white trace is the correct one, yellow is the measured trace.

0693W000001rvWYQAY.bmp

It seems that the Start bit is present in the 'calculated' buffer for DMA, but the timer overflow early...

Thank you,

Pierre

1 ACCEPTED SOLUTION

Accepted Solutions

Try to clear TIMx_DIER.CC1DE (__HAL_TIM_DISABLE_DMA(&TimHandle, TIM_DMA_CC1) ? I don't know, I don't speak the Cube/HAL gibberish) as the first thing in UART_Emul_DMATransmitCplt().

(It's equivalent to what TDK suggested with clearing the TIMx_SR register, except that with the DMA trigger signals rather than the interrupt trigger signals

https://community.st.com/s/question/0D50X00009XkaAtSAJ/how-to-clear-pending-dma-request

).

JW

View solution in original post

14 REPLIES 14

Show code.

Which STM32?

JW

PCu1
Senior

This is a STM32F401.

I forgot to mention that I tried with different baudrates and different optimisations. The problem is the same.

And the code?

JW

PCu1
Senior
/**
 * @brief  Sends an amount of data
 * @param  huart: UART Emulation handle
 * @param  pData: Pointer to data buffer
 * @param  Size: Amount of data to be sent
 * @retval HAL status
*/
HAL_StatusTypeDef HAL_UART_Emul_Transmit_DMA(UART_Emul_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
{
  uint32_t tmp = 0;
 
  tmp = huart->State;
  if ((tmp == HAL_UART_EMUL_STATE_READY) || (tmp == HAL_UART_EMUL_STATE_BUSY_RX))
  {
    if ((pData == NULL ) || (Size == 0))
    {
      return HAL_ERROR;
    }
 
    huart->TxXferSize = Size;
    huart->pTxBuffPtr = pData;
    huart->TxXferCount = 1;
    huart->ErrorCode = HAL_UART_EMUL_ERROR_NONE;
 
    /* Check if a receive process is ongoing or not */
    if (huart->State == HAL_UART_EMUL_STATE_BUSY_RX)
    {
      huart->State = HAL_UART_EMUL_STATE_BUSY_TX_RX;
    }
    else
    {
      huart->State = HAL_UART_EMUL_STATE_BUSY_TX;
    }
 
    /* Set the UART Emulation DMA transfer complete callback */
    TimHandle.hdma[TIM_DMA_ID_CC1]->XferCpltCallback = UART_Emul_DMATransmitCplt;
 
    /* Set the DMA error callback */
    TimHandle.hdma[TIM_DMA_ID_CC1]->XferErrorCallback = UART_Emul_DMAError;
 
    /* Format first Frame to be sent */
    if (huart->TxXferCount == FIRST_BYTE)
    {
      /* Format Frame to be sent */
      UART_Emul_TransmitFormatFrame(huart, *(pData), (uint32_t*)pFirstBuffer_Tx);
 
      /* Enable the Capture compare channel */
      //TODO
      TIM_CCxChannelCmd(TIM1, TIM_CHANNEL_1, TIM_CCx_ENABLE);
      //TIM_CCxChannelCmd(TIM5, TIM_CHANNEL_1, TIM_CCx_ENABLE);
 
      /* Send Frames */
      UART_Emul_TransmitFrame(huart);
    }
 
    if ((huart->TxXferCount == FIRST_BYTE) && (huart->TxXferCount < Size))
    { 
      /* Format Second Frame to be sent */
      UART_Emul_TransmitFormatFrame(huart, *(pData + huart->TxXferCount), (uint32_t*)pSecondBuffer_Tx);
    }
 
    return HAL_OK;
  }
  else
  {
    return HAL_BUSY;
  }
}
 
/**
  * @brief  This function formats one Frame
  * @param  UART Emulation Handle
  * @param  pdata pinteur in data
  * @retval None
  */
static void UART_Emul_TransmitFormatFrame(UART_Emul_HandleTypeDef *huart , uint8_t Data, uint32_t *pBuffer_Tx)
{
uint32_t counter = 0;
uint32_t bitmask = 0;
uint32_t length = 0;
uint32_t cntparity = 0;
 
 
  length = huart->Init.WordLength;
 
  /* Get the Pin Number */
  bitmask = (uint32_t)huart->Init.TxPinNumber;
 
/* with no parity */
if(huart->Init.Parity == 0x00)
{		
  for (counter = 0; counter < length; counter++)
  {
    if (((Data >> counter)&BitMask) != 0)
    {
      pBuffer_Tx[counter+1] = bitmask;
    }
    else
    {
      pBuffer_Tx[counter+1] = (bitmask << 16);
    }
  }
}
/* with parity */
else
{
  for (counter = 0; counter < length-1; counter++)
  {
    if (((Data >> counter)&BitMask) != 0)
    {
      pBuffer_Tx[counter+1] = bitmask;
      cntparity ++;
    }
    else
    {
      pBuffer_Tx[counter+1] = (bitmask << 16);
    }
  }	
}	
	
  switch  (huart->Init.Parity)
  {
    case UART_EMUL_PARITY_ODD:
    {
      /* Initialize Parity Bit */
      if ((cntparity % 2) != SET)
      {
        pBuffer_Tx[length] = bitmask;
      }
      else
      {
        pBuffer_Tx[length] = (bitmask << 16);
      }
 
    }
    break;
    case UART_EMUL_PARITY_EVEN:
    {
      /* Initialize Parity Bit */
      if ((cntparity % 2) != SET)
      {
        pBuffer_Tx[length] = (bitmask << 16);
      }
      else
      {
        pBuffer_Tx[length] = bitmask;
      }
    }
    break;
    default:
      break;
  }
  /* Initialize Bit Start */
  pBuffer_Tx[0] = (bitmask << 16);
 
  if (huart->Init.StopBits == UART_EMUL_STOPBITS_1)
  {
    /* Initialize Bit Stop  */
    pBuffer_Tx[length+1] = bitmask;
  }
  else
  {
    /* Initialize Bit Stop  */
    pBuffer_Tx[length+1] = bitmask;
    pBuffer_Tx[length+2] = bitmask;
  }
  /* Reset counter parity */
  cntparity = 0;
}
 
 
/**
  * @brief  Sends an amount of Frames
  * @param  huart: UART Emulation handle
  * @param  pData: Frame to be sent
  * @retval None
  */
static void UART_Emul_TransmitFrame(UART_Emul_HandleTypeDef *huart)
{
  uint32_t tmp_sr = 0;
  uint32_t tmp_ds = 0;
  uint32_t tmp_size = 0;
 
	
  if ((huart_emul->TxXferCount % 2 ) != 0)
  {
    tmp_sr = (uint32_t)pFirstBuffer_Tx;
  }
  else
  {
    tmp_sr = (uint32_t)pSecondBuffer_Tx;
  }
 
	tmp_ds = (uint32_t) & ((huart->TxPortName)->BSRR);
	
  tmp_size = __HAL_UART_EMUL_FRAME_LENGTH(huart);
 
  /* Configure DMA Stream data length */
  hdma_tx.Instance->NDTR = tmp_size;
 
  /* Configure DMA Stream destination address */
  hdma_tx.Instance->PAR = tmp_ds;
 
  /* Configure DMA Stream source address */
  hdma_tx.Instance->M0AR = tmp_sr;
 
  /* Enable the transfer complete interrupt */
  __HAL_DMA_ENABLE_IT(&hdma_tx, DMA_IT_TC);
 
  /* Enable the transfer Error interrupt */
  __HAL_DMA_ENABLE_IT(&hdma_tx, DMA_IT_TE);
 
  /* Enable the Peripheral */
  __HAL_DMA_ENABLE(&hdma_tx);
 
  /* Enable the TIM Update DMA request */
  __HAL_TIM_ENABLE_DMA(&TimHandle, TIM_DMA_CC1);
 
  /* Enable the Peripheral */
  __HAL_TIM_ENABLE(&TimHandle);
 
}
 
/**
  * @brief  This function is executed in case of Transfer Complete of a Frame.
  * @param  None
  * @retval None
  */
static void UART_Emul_DMATransmitCplt(DMA_HandleTypeDef *hdma)
{
  uint32_t tmpbuffer = 0;
 
  /* Incremente Counter of frame */
  huart_emul->TxXferCount++;
 
  if (huart_emul->TxXferCount <= huart_emul->TxXferSize)
  { 
 
    /* Call UART Emulation Transmit frame for next Frame */
    UART_Emul_TransmitFrame(huart_emul);
 
    if ((huart_emul->TxXferCount % 2 ) != 0)
    {
      tmpbuffer = (uint32_t)pSecondBuffer_Tx;
    }
    else
    {
      tmpbuffer = (uint32_t)pFirstBuffer_Tx;
    }
    /* Format second Data to be sent */
    UART_Emul_TransmitFormatFrame(huart_emul, *(huart_emul->pTxBuffPtr + huart_emul->TxXferCount), (uint32_t*)tmpbuffer);
  }
  else
  {
    /* Disable the transfer complete interrupt */
    __HAL_DMA_DISABLE_IT(TimHandle.hdma[TIM_DMA_ID_CC1], DMA_IT_TC);
 
    /* Set TC flag in the status register software */
    __HAL_UART_EMUL_SET_FLAG(huart_emul, UART_EMUL_FLAG_TC);
 
    /* De_Initialize counter frame for Tx */
    huart_emul->TxXferCount = 0;
 
    /* Initialize the UART Emulation state */
    huart_emul->ErrorCode = HAL_UART_EMUL_ERROR_NONE;
 
    /* Check if a receive process is ongoing or not */
    if (huart_emul->State == HAL_UART_EMUL_STATE_BUSY_TX_RX)
    {
      huart_emul->State = HAL_UART_EMUL_STATE_BUSY_RX;
    }
    else
    {
      huart_emul->State = HAL_UART_EMUL_STATE_READY;
    }
    /* Handle for UART Emulation Transfer Complete */
    HAL_UART_Emul_TxCpltCallback(huart_emul);
  }
}

I did a copy/paste with transmit code

TDK
Guru

Just prior to starting the timer, set CNT to 0 and clear the TIMx->SR flags. Sounds like an update event is pending. Just a guess.

You could also just have it set an extra start bit on the first byte for a quick hack.

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

Try to clear TIMx_DIER.CC1DE (__HAL_TIM_DISABLE_DMA(&TimHandle, TIM_DMA_CC1) ? I don't know, I don't speak the Cube/HAL gibberish) as the first thing in UART_Emul_DMATransmitCplt().

(It's equivalent to what TDK suggested with clearing the TIMx_SR register, except that with the DMA trigger signals rather than the interrupt trigger signals

https://community.st.com/s/question/0D50X00009XkaAtSAJ/how-to-clear-pending-dma-request

).

JW

PCu1
Senior

Thank you TDK and Jan.

I will try this afternoon.

PCu1
Senior

Hello ,

I added the clearing of TIM_DIER_CC1DE and it works better. I inserted it just before UART_Emul_TransmitFrame()

Jan, me too I prefer working with Low level instead of HAL.

Now I'm trying to work with TIM5 instead of TIM1(already used in my project) but no signal leaves from TX pin. (stays high)

DMA, bus clk and stream have been adapted, configuration registers have the same value.

And the end of DMA (in the interrupt), I get the TE(transmit error) flag.

It seems that NDTR is not at zero when the interrupt is generated (0x9 instead of 0x12).

And it does not leave the interrupt.

Pierre

In 'F4, you can't use DMA1 with GPIO.

JW