STM32:未对齐的循环DMA UART缓冲区

时间:2019-05-06 13:30:04

标签: stm32 uart dma cortex-m hal

对于我的STM32L053微控制器应用程序,我需要一个稳定的UART RX缓冲区,并用于来自github的DMA实现,该实现基于ST HAL:https://github.com/akospasztor/stm32-dma-uart

如果RX输入数据根据相应的缓冲区大小对齐,则此实现会非常稳定。例如,如果缓冲区大小为24个字节,并且所有传入的数据请求都具有此缓冲区长度的倍数,例如每个请求8个字节,则缓冲区溢出可以正常工作而不会出现问题。

我的应用程序使用不同的消息长度,因此我发现此实现对于未对齐的缓冲区溢出有一个弱点。例如,如果缓冲区长度设置为23个字节,则前两个8字节消息均正确传递,但随后的8个字节消息均未正确传输。

为此,我扩展了unit untObjectHelper; interface uses SysUtils; type TObjectHelper = class(TInterfacedObject) public class procedure Clone(const objOrigem: TObject; const objDestino: TObject); end; implementation uses System.Rtti; { TObjectHelper } class procedure TObjectHelper.Clone(const objOrigem, objDestino: TObject); begin if not Assigned(objOrigem) then Exit; if not Assigned(objDestino) then Exit; if objOrigem.ClassType <> objDestino.ClassType then Exit; var contexto := TRttiContext.Create; try var tipo := contexto.GetType(objOrigem.ClassType); var campos: TArray<TRttiField> := tipo.GetFields(); finally contexto.Free; end; end; end. 例程,以使用HAL_UART_RxCpltCallback DMA寄存器并记住其在变量CNDTR中的最后一个值来处理未对齐的缓冲区溢出:

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个字节,尽管没有其他变量分配?!

enter image description here

有人可以帮我弄清楚我在做什么错吗?

2 个答案:

答案 0 :(得分:0)

对齐没有共同之处。您需要处理两个事件-DMA传输结束-它发生在CNDR达到零且USART处于IDLE时,发现usart传输结束。逻辑上是NTDR会低于在后台进行传输并且触发断点需要一些时间的情况。

答案 1 :(得分:0)

因为在 AMBA 总线 DMA pcore 上有至少 4 个地址线的 ARM 处理器的整个 Mx 系列(又名 Thumb)被跳过,所有与 DMA 相关的缓冲区都必须对齐32 个字节。

gcc 编译器示例:

uint8_t dumpBuffer[2][DUMP_LIMIT] __attribute__ ((aligned(32)));

此外,在处理 DMA 时,请始终记住至少双缓冲区 - 一个由 DMA 使用,另一个由应用程序使用。