在整理串口库函数时发现有几个以前没有注意到的问题
问题一:
如果使能了接收中断,即USART_ITConfig(USART1,USART_IT_RXNE,ENABLE),则默认ORE溢出中断也开启,且此时溢出中断标志USART_IT_ORE不能通过USART_GetITStatus()来检测到,而只能通过USART_GetFlagStatus()检测到,且此时USART_ClearITPendingBit(USART1, USART_IT_ORE)也不起作用。这种情况下,如果在中断处理函数中有if(USART_GetITStatus(USART1,USART_IT_ORE) != RESET)判断,则程序每接收一个字符都会进入到溢出中断(不过暂时检测到对接收数据的读取没有很大影响)
解决办法:
在串口初始化时使能溢出中断USART_ITConfig(USART1,USART_IT_ORE,ENABLE);只有使能了这个USART_ClearITPendingBit(USART1, USART_IT_ORE)才可以起作用
在串口中断处理函数中检测溢出标志,如果产生溢出中断则清除标志,且读取串口RDR寄存器清空接收缓存器
问题二:
在使用串口助手向单片机串口发送数据时,如果不勾选“发送新行”,则接收到的字符串将丢失最后一个字符;而只有勾选“发送新行”后,接收到的字符串才是完整的
解决办法:
经过排查,查到了真正原因,和原子那边的“串口接收定义收到换行符才判断为接收结束”没有任何关系,真正原因在于我在串口中断处理函数中检测中断标志用的USART_GetFlagStatus()而不是USART_GetITStatus(),前者的返回值是中断标志位状态(读ISR寄存器),后者的返回值是中断发生与否(读CR寄存器)。
从CR寄存器中读取的是RXNEIE,发生接收中断时该位就被置位,且该标志位的置位和清除都是通过软件来完成的
从ISR寄存器中读取的是RXNE,这个位则是当RDR移位寄存器向USART_RDR寄存器移数据时,由硬件自动置位,它的清除可以通过读取RDR寄存器内的数据清除或者软件置位RXFRQ来完成
所以我这里的问题在于,我判断RXNE Flag Status等于RESET后才进行数据的读取,如果我发送的字符串是“helloworld”,那么当我发完d后,因为没有新的数据发过来,所以就不存在有“RDRRDR移位寄存器向USART_RDR寄存器移数据”这个动作,因此ISR中的RXNE就不会被置位。所以最后一个"d"字母就没有被存储处理。这个时候其实还是进了中断,只是不是USART_GetFlagStatus(USART1,USART_IT_RXNE)这一项,因而也就没有对最后一位数据进行存储处理。事实证明,当我再接着发送新的字符串时,之前的"d"字符会重新出现在新的字符串的第一位,同理这个时候新字符串的最后一位也没有被存储。
同样的,如果使用USART_GetFlagStatus(USART1,USART_IT_RXNE) != RESET判断,即便我在串口助手中勾选了“发送新行”,根据得到的数据也可以发现串口接收buffer里面丢了换行符(两个ASCII值)的一个ASCII码值。
细节三:
在官方给的串口中断处理函数中,读取接收字符的代码为:RxBuffer[RxCount++] = (USART_ReceiveData(USART1) & 0x7F);这里为什么与的不是0xFF而是0x7F,查看手册了解到无论串口配置时选的数据宽度为8bit还是9bit,其最高位一般保留为奇偶检验的结果位,因此如果读取实际数据的话应该省掉最高位
细节四:
开启串口的DMA中断传输后,每次串口接收到一个字节后DMA就会自动去串口的RDR寄存器中读取数据,这时串口接收中断不会产生
DMA_DeInit(DMA1_Channel3);
DMA_InitTypeDef DMA_InitStructure;
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)(&USART1->RDR);
DMA_InitStructure.DMA_BufferSize = (uint16_t)10;
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)AckBuffer;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
DMA_InitStructure.DMA_Priority = DMA_Priority_Low;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
DMA_Init(DMA1_Channel3,&DMA_InitStructure);
//USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);
//USART_ITConfig(USART1,USART_IT_ORE,ENABLE);
USART_Cmd(USART1,ENABLE);
USART_DMACmd(USART1,USART_DMAReq_Rx,ENABLE);
/**************************************
* 查询传输用 while(DMA_GetFlagStatus(DMA1_IT_TC3) == RESET)
***************************************/
DMA_ITConfig(DMA1_Channel3, DMA_IT_TC, ENABLE);
DMA_Cmd(DMA1_Channel3,ENABLE);
细节五:重定向printf函数,记得#include <stdio.h>
#ifdef __GNUC__
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch,FILE *f)
#endif
PUTCHAR_PROTOTYPE
{
USART_SendData(USART1, (uint16_t) ch);
while(USART_GetFlagStatus(USART1,USART_FLAG_TC) == RESET)
{}
return ch;
}
串口通信的硬件流控制
流控制的相关解释:https://blog.csdn.net/zeroboundary/article/details/8966586
串口设备使用硬件流控制的连接方式:
TX ————> RX
CTS <———— RTS
RX <———— TX
RTS ————> CTS
因为手上也没有足够的板子做测试,只能用串口助手做个简单的测试,首先是配置CTS/RTS两个复用GPIO口,然后配置硬件流控制USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_RTS_CTS;
/******USART1 CTS/RTS GPIO_Pins Configuration*******/
GPIO_PinAFConfig(GPIOA,GPIO_PinSource11,GPIO_AF_1); //CTS
GPIO_PinAFConfig(GPIOA,GPIO_PinSource12,GPIO_AF_1); //RTS
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11 | GPIO_Pin_12;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOA,&GPIO_InitStructure);
我用的是串口USB转接口,只有TX和RX,测试CTS,我在主程序中循环打印“hello world”,可以发现如果将PA11接高电平或者悬空,字符不能正常打印,只有接低电平时字符串才能正常打印。即CTS低电平有效
RS232 RS485不同协议和串口的应用(待补充)
————————————————
原文链接:https://blog.csdn.net/yhl_sophia/article/details/88695434