* Then on each character match interrupt, you can enable a DMA transfer for the maximum allowable packet size (ex. for COBS it is 254 bytes).
* If each frame is sandwiched between the escape character (ex. 0x0 for COBS), then on the next character match interrupt, you stop the DMA transfer and notify the user.
You can do really high speed UART comms with framing with very little processor intervention ..
Attached is some code I refactored a bit from working, tested code (this may or may not work - since i just modified it for the sake of demonstrating the concept).
Code: Select all
static uint8_t sCobsUsartRxDmaBuf[COBS_USART_RX_DMA_BUF_LEN];
void character_match_cb(void)
{
// wait for the current DMA transaction to finish
int32_t t = 0;
while ((USART1->ISR & USART_ISR_RXNE) & (t++ < USART_DMA_WAIT_TRANSFER_CYCLES));
// Check how many bytes the DMA has already transfered
// The DMA counter is reset to the configured transaction length
// if it completed the transaction
uint32_t dma_count = DMA1_Channel5->CNDTR & 0xffff;
// If the dma_count is == to the transaction length (COBS_USART_RX_DMA_BUF_LEN)
// then that either means it already transfered an entire packet, or it received
// a character match, and the CM interrupt fired before the DMA had a chance to
// transfer the incoming byte.
dma_count = (dma_count >= COBS_USART_RX_DMA_BUF_LEN) ? 0 : dma_count;
// search for cobs frame delimiter character (0x00)
// This is to account for a race condition between CM interrupt and
// DMA transfer complete interrupt. Start from the end of the DMA transfer
// buffer. On a reliable channel, and a well behaving actor on the other side
// there should only be 1 iteration of this.
do
{
t = COBS_USART_RX_DMA_BUF_LEN - dma_count - 1; // current dma buf index to inspect
if ((sCobsUsartRxDmaBuf[t] == 0x00) && (t > 0))
{
// perform callback here
got_cobs_frame_cb(sCobsUsartRxDmaBuf, t);
break;
}
} while (++dma_count < COBS_USART_RX_DMA_BUF_LEN);
// stop the current DMA transfer (the frame length could be less than the maximum)
HAL_UART_AbortReceive(&huart1);
// configure a new DMA transfer for upto the maximum frame length
HAL_UART_Receive_DMA(&huart1, sCobsUsartRxDmaBuf, COBS_USART_RX_DMA_BUF_LEN);
// This is just a hack to work with the lame STM32 driver library
DMA1_Channel5->CCR &= ~DMA_CCR_HTIE;
return;
}
void USART1_IRQHandler(void)
{
uint32_t isrflags = USART1->ISR;
if (isrflags & USART_ISR_CMF)
{
character_match_cb();
USART1->ICR = USART_ICR_CMCF;
}
HAL_UART_IRQHandler(&huart1);
}
void DMA1_Channel5_IRQHandler(void)
{
uint32_t isrflags = DMA1->ISR;
if (isrflags & DMA_FLAG_TC5)
{
DMA1->IFCR = DMA_IFCR_CTCIF5;
HAL_UART_AbortReceive(&huart1);
HAL_UART_Receive_DMA(&huart1, sCobsUsartRxDmaBuf, COBS_USART_RX_DMA_BUF_LEN);
DMA1_Channel5->CCR &= ~DMA_CCR_HTIE;
}
if (isrflags & DMA_FLAG_HT5)
{
DMA1->IFCR = DMA_IFCR_CHTIF5;
}
}