在整理串口庫函數時發現有幾個以前沒有注意到的問題
問題一:
如果使能了接收中斷,即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不同協議和串口的應用(待補充)