2020-07-01 05:40 AM
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.
It seems that the Start bit is present in the 'calculated' buffer for DMA, but the timer overflow early...
Thank you,
Pierre
Solved! Go to Solution.
2020-07-01 06:33 PM
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
2020-07-01 08:26 AM
Show code.
Which STM32?
JW
2020-07-01 08:54 AM
This is a STM32F401.
I forgot to mention that I tried with different baudrates and different optimisations. The problem is the same.
2020-07-01 09:37 AM
And the code?
JW
2020-07-01 01:51 PM
/**
* @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
2020-07-01 05:17 PM
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.
2020-07-01 06:33 PM
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
2020-07-01 11:53 PM
Thank you TDK and Jan.
I will try this afternoon.
2020-07-08 04:53 AM
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
2020-07-08 05:52 AM
In 'F4, you can't use DMA1 with GPIO.
JW