【问题标题】:STM32f091rc UART Receive function returning only the last byte of the packet instead of the full data packetSTM32f091rc UART接收函数只返回数据包的最后一个字节而不是完整的数据包
【发布时间】:2018-09-08 10:17:14
【问题描述】:

我一直在 STM32f091rc 板上工作,试图让 UART1 和 UART2 工作。我尝试从控制器向 STM 板发送 8 个字节的数据包。由于某些原因,我的功能只是显示数据包的最后一个字节。我的接收功能如下:-

uint8_t rxd[10];
void getChar (void) {

while(USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == SET) { // Check RXNE to 
//see if there is data
    for(j=0; j<8; j++) { 
        rxd[i] = (0xFF & (USART1->RDR));
    }

我做错了什么?谁能指出我正确的方向?感谢您的宝贵时间。

【问题讨论】:

    标签: arm stm32 uart cmsis stm32f0


    【解决方案1】:

    UART-&gt;RDR 寄存器没有缓冲区,它只保存最后一个完全接收到的字节。如果接收到另一个字节,它将被覆盖。

    您应该确保每次在一个字节到达之后和接收到下一个字节之前读取RDR 中的值。有 3 种基本方法。

    • 轮询

    定期检查RXNE 标志,并在设置RDR 只读一次。重复直到你有整个数据包。从RDR 读取一个字节会清除RXNE 标志,等到它再次设置后再读取下一个字节。

    • 中断

    设置CR1中的RXNEIE位,开启NVIC中UART对应的中断。每次接收到一个字节时都会调用中断处理程序。处理程序一开始可能非常简单,只需读取RDR 并将其存储在缓冲区中。稍后您可以添加错误检查和恢复。不要忘记将中断处理程序接触的每个变量声明为volatile

    • DMA

    先设置DMA通道(USART1默认映射到DMA1_Channel3,可以重新映射,其他查看参考手册):

    DMA1_Channel3->CPAR = (uint32_t)&USART1->RDR;
    DMA1_Channel3->CMAR = (uint32_t)rxd;            // start of receive array
    DMA1_Channel3->CNDTR = 8;                       // 8 bytes to receive
    DMA1_Channel3->CCR = DMA_CCR_MINC | DMA_CCR_EN; // Memory increment, enable
    

    然后设置串口,并在USART1-&gt;CR3 中启用DMA 接收。传输结束在DMA1-&gt;ISR 寄存器中发出信号,您可以在主程序中定期检查它,或者在DMA1_Channel3-&gt;CCR(和NVIC)中启用中断。您应该通过DMA1-&gt;IFCR 清除中断标志,否则启用时您将获得无休止的中断。要开始另一次传输,请再次设置CNDTR 寄存器。将 DMA 或中断处理程序触及的所有变量声明为 volatile

    【讨论】:

    • 感谢您提供宝贵的信息。所以现在我得出结论,只有在接收到一个字节并且读取 RDR 寄存器会清除该标志时,才会设置 RXNE 标志。现在我已将逻辑更改为:- uint8_t getChar (void) { if(USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == SET) { // 检查 RXNE 是否有数据返回 ((USART1->RDR) & (0xFF) )); } 其他 { 返回 0xFF; } } -- 这是我的函数 getchar(),如果我在 while(1) 中连续运行它,在返回值时,它应该清除标志 RXNE 对吗?
    • 那我想我必须在返回值之前存储字节?
    • 要么将值存储在函数中并返回错误/成功代码,要么在函数中使用整数返回类型,当没有接收到时返回负值。后者类似于getchar() 库函数。
    • 根据您的解释,我打算使用轮询方法来接收 8 个字节,这是我的逻辑似乎不起作用:- char rxdata[2]; for (k=0; kRDR)&(0x00FF)); // 将 RDR 值放入 rxdata[0],只读取一次 } else{ //如果未设置标志 UartTransmitPacket("flag reset", sizeof("flag reset")); //打印标志被重置 } }
    • 我尝试在每次迭代后打印 rxdata[0] 的值。还是没听懂。
    【解决方案2】:

    这里:

    for(j=0; j<8; j++) { 
      rxd[i] = (0xFF & (USART1->RDR));
    }
    

    您使用j 作为循环计数器,但您在索引i 下写入rxd,而不是j。你覆盖同一个字节 8 次。

    另一件事是您等待设置USART_FLAG_RXNE 标志,然后从RDR 寄存器读取8 次。当接收到第一个字节时设置此标志,然后您读取它 8 次 - 您很可能读取速度比发送数据的速度快得多。如果您想通过轮询来执行此操作 - 这似乎是您的意图 - 您应该在分别读取每个字节后等待设置 USART_FLAG_RXNE 标志,因为此 MCU 中的 USART 外设没有 FIFO,只能保存一个接收到的字节供您阅读(提及RDR 注册)。

    【讨论】:

    • 是的,我写错了'i'而不是'j'。谢谢。我会尽量让逻辑正确:)
    【解决方案3】:

    你的逻辑完全错误。

    for(j=0; j<8; j++) 
    {
        while(USART_GetFlagStatus(USART1, USART_FLAG_RXNE) != SET); // wait for the data
        rxd[i] = (0xFF & (USART1->RDR));
    }
    

    【讨论】:

      猜你喜欢
      • 2021-10-24
      • 2014-03-25
      • 2015-02-20
      • 2018-05-18
      • 2012-08-05
      • 1970-01-01
      • 1970-01-01
      • 2017-03-04
      • 2011-03-30
      相关资源
      最近更新 更多