для моего приложения микроконтроллера STM32L053 мне нужен стабильный буфер UART RX и для этого используется реализация DMA от github, основанная на ST HAL: https://github.com/akospasztor/stm32-dma-uart.
Эта реализация работает достаточно стабильно, если RXвходные данные выровнены согласно соответствующему размеру буфера.Например, если размер буфера составляет 24 байта, а все входящие запросы данных имеют кратный размер этой длины буфера, скажем, например, 8 байтов на запросы, переполнение буфера работает без проблем.
MyПриложение использует сообщения различной длины, поэтому я обнаружил, что в этой реализации есть недостаток переполнения буфера без выравнивания.Например, если длина буфера установлена в 23 байта, первые оба 8-байтовых сообщения передаются корректно, но следующие 8-байтовые сообщения передаются некорректно.
Для этого я расширил процедуру HAL_UART_RxCpltCallback
для обработкипереполнение буфера без выравнивания с использованием регистра CNDTR
DMA и запоминание его последнего значения в переменной dma_uart_rx.prevCNDTR
:
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *uartHandle)
{
uint16_t i, pos, start, length;
uint32_t currCNDTR = huart.hdmarx->Instance->CNDTR;
/* Ignore IDLE Timeout when the received characters exactly filled up the DMA buffer and DMA Rx Complete IT is generated, but there is no new character during timeout */
if((dma_uart_rx.flag && currCNDTR == DMA_BUF_SIZE) || error_flag)
{
error_flag = RESET;
dma_uart_rx.flag = 0;
return;
}
/* Determine start position in DMA buffer based on previous CNDTR value */
start = (dma_uart_rx.prevCNDTR < DMA_BUF_SIZE) ? (DMA_BUF_SIZE - dma_uart_rx.prevCNDTR) : 0;
if (dma_uart_rx.flag) /* Timeout event */
{
/* Determine new data length based on previous DMA_CNDTR value:
* If previous CNDTR is less than DMA buffer size: there is old data in DMA buffer (from previous timeout) that has to be ignored.
* If CNDTR == DMA buffer size: entire buffer content is new and has to be processed.
*/
length = (dma_uart_rx.prevCNDTR < DMA_BUF_SIZE) ? (dma_uart_rx.prevCNDTR - currCNDTR) : (DMA_BUF_SIZE - currCNDTR);
dma_uart_rx.prevCNDTR = currCNDTR;
dma_uart_rx.flag = 0;
}
else /* DMA Rx Complete event */
{
// My buffer overrun handling
if (currCNDTR > dma_uart_rx.prevCNDTR)
{
length = dma_uart_rx.prevCNDTR + DMA_BUF_SIZE - currCNDTR;
// 1st rx data part
for (i=0, pos=DMA_BUF_SIZE - dma_uart_rx.prevCNDTR; pos < DMA_BUF_SIZE; ++i,++pos)
{
data[i] = dma_rx_buf[pos];
}
// 2nd rx data part
for (pos=0; pos < DMA_BUF_SIZE - currCNDTR; ++i,++pos)
{
data[i] = dma_rx_buf[pos];
}
receivedBytes = length;
dma_uart_rx.prevCNDTR = currCNDTR;
return;
}
length = DMA_BUF_SIZE - start;
dma_uart_rx.prevCNDTR = DMA_BUF_SIZE;
}
/* Copy and Process new data */
for (i=0,pos=start; i<length; ++i,++pos)
{
data[i] = dma_rx_buf[pos];
}
receivedBytes = length;
}
До этого момента все работало без проблем, но затем я выяснил странное поведениерегистр CNDTR
при переполнении буфера: если я остановился на точке останова после присвоения значения регистра CNDTR
переменной currCNDTR
, а затем сравнил текущее значение регистра регистра CNDTR
с упомянутой переменной в отладчикепеременная всегда на 1 байт выше значения регистра CNDTR
, хотя других назначений переменной нет!!
Можеткто-нибудь, пожалуйста, помогите мне разобраться, что я здесь не так делаю?