STM32f091rc UART 接收函数只返回数据包的最后一个字节而不是完整的数据包
STM32f091rc UART Receive function returning only the last byte of the packet instead of the full data packet
我一直在研究 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));
}
我做错了什么?谁能指出我正确的方向?谢谢你的时间。
这里:
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
寄存器)。
UART->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
然后设置串口,使能DMA接收USART1->CR3
。传输结束在 DMA1->ISR
寄存器中发出信号,您可以在主程序中定期检查它,或者在 DMA1_Channel3->CCR
(和 NVIC
)中启用中断。你应该通过DMA1->IFCR
清除中断标志,否则你会在启用时得到无限的中断。要开始另一次传输,请再次设置 CNDTR
寄存器。将 DMA 或中断处理程序接触的所有变量声明为 volatile
.
你的逻辑完全错误。
for(j=0; j<8; j++)
{
while(USART_GetFlagStatus(USART1, USART_FLAG_RXNE) != SET); // wait for the data
rxd[i] = (0xFF & (USART1->RDR));
}
我一直在研究 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));
}
我做错了什么?谁能指出我正确的方向?谢谢你的时间。
这里:
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
寄存器)。
UART->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
然后设置串口,使能DMA接收USART1->CR3
。传输结束在 DMA1->ISR
寄存器中发出信号,您可以在主程序中定期检查它,或者在 DMA1_Channel3->CCR
(和 NVIC
)中启用中断。你应该通过DMA1->IFCR
清除中断标志,否则你会在启用时得到无限的中断。要开始另一次传输,请再次设置 CNDTR
寄存器。将 DMA 或中断处理程序接触的所有变量声明为 volatile
.
你的逻辑完全错误。
for(j=0; j<8; j++)
{
while(USART_GetFlagStatus(USART1, USART_FLAG_RXNE) != SET); // wait for the data
rxd[i] = (0xFF & (USART1->RDR));
}