當時有兩個思路:
1、使用結尾標誌,如 "\r\n" 什麼的
2、定時判斷接收數據的長度,如果在規定的時間內長度沒有發生變化,證明已經接收完了任意長度的字符
因爲思路 1 比較好實現,而且網上也有很多例程,所以着重講思路 2
宏定義:
usart.h 文件
/****************************************************************************************************************/
#ifndef USART_H
#define USART_H
#include "stm32f4xx.h"
#define MAX_LENGTH 1024
#define USARTX USART3
#define USART_CLK RCC_APB1Periph_USART3
#define USART_BAUD_TATE 115200
#define USART_GPIO_PORT GPIOB
#define USART_GPIO_CLK RCC_AHB1Periph_GPIOB
#define USART_RX_PIN GPIO_Pin_11
#define USART_TX_PIN GPIO_Pin_10
#define USART_RX_PINSOURCE GPIO_PinSource11
#define USART_TX_PINSOURCE GPIO_PinSource10
#define GPIO_AF_USART GPIO_AF_USART3
#define USART_IRQN USART3_IRQn
#define USART_IRQ_HANDLDER USART3_IRQHandler
void USART_Init(void);
char* USART_GetString(void);
注意:
本文中使用的是 STM32F405 系列的單片機,使用的是 USART3 ,請讀者根據自己的單片機型號和 USART 做出相應更改
usart.c 文件
/****************************************************************************************************************/
#include "./XXX/usart.h" //XXX 代表存放 usart.h 文件的路徑
volatitle char receiveBuffer[MAX_LENGTH];
volatitle uint16_t receiveLength = 0;
volatitle uint8_t rxFlag = 0;
static char str[MAX_LENGTH + 1];
static void GPIO_Config(void)
{
GIPO_InitTypeDef GPIO_InitStructure;
RCC_AHB1PeriphClockCmd(USART_GPIO_PORT, ENABLE);
GPIO_PinAFConfig(USART_GPIO_PORT, USART_RX_PINSOURCE, GPIO_AF_USART);
GPIO_PinAFConfig(USART_GPIO_PORT, USART_TX_PINSOURCE, GPIO_AF_USART);
GPIO_InitStructure.GPIO_Pin = USART_RX_PIN;
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_UP;
GPIO_Init(USART_GPIO_PORT, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = USART_TX_PIN;
GPIO_Init(USART_GPIO_PORT, &GPIO_InitStructure);
}
static void NVIC_Config(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
NVIC_InitStructure.NVIC_IRQChannel = USART_IRQN;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
static void delay_us(uint16_t time)
{
uint8_t count;
while(time--)
{
count = 10;
while(count--);
}
}
void USART_Init(void)
{
USART_InitTypeDef USART_InitStructure;
RCC_APB1PeriphClockCmd(USART_CLK, ENABLE);
GPIO_Config();
NVIC_Config();
USART_InitStructure.USART_BaudRate = USART_BAUD_TATE;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
USART_Init(USARTX, &USART_InitStructure);
USART_ITConfig(USARTX, USART_IT_RXNE, ENABLE);
USART_Cmd(USARTX, ENABLE);
}
char* USART_GetString(void)
{
uint16_t temp;
while(!rxFlag); //等待接收數據,在串口接收到一幀數據時 rxFlag 將會被置 1 (USART3 中斷服務函數中)
while(rxFlag)
{
temp = receiveLength;
delay_us(500); //等待 500us
if(temp == receiveLength) //判斷 receiveLength 是否發生變化(USART3 中斷函數中 receiveLength 會有變化),如果沒有,證明已經收完所有的數據,否則等待接收完成
{
rxFlag = 0;
}
}
for(temp = 0; temp < receiveLength; temp++)
{
str[temp] = receiveBuffer[temp];
}
receiveLength = 0;
str[temp] = "\0";
return str;
}
/****************************************************************************************************************/
#include "./XXX/usart.h" //XXX 代表存放 usart.h 文件的路徑
extern volatitle char receiveBuffer[MAX_LENGTH];
extern volatitle uint16_t receiveLength;
extern volatitle uint8_t rxFlag;
/*USART3 中斷服務函數*/
void USART_IRQ_HANDLDER(void)
{
char temp;
if(USART_GetITStatus(USARTX, USART_IT_RXNE))
{
USART_ClearITPendingBit(USARTX, USART_IT_RXNE);
temp = USART_ReceiveData(USARTX)
if(receiveLength == MAX_LENGTH)
{
return;
}
if(!rxFlag)
{
rxFlag = 1;
}
receiveBuffer[receiveLength++] = temp;
}
}
/****************************************************************************************************************/
main.c 文件
/****************************************************************************************************************/
#include "./XXX/usart.h" //XXX 代表存放 usart.h 文件的路徑
int main()
{
char* temp = NULL;
USART_Init();
temp = USART_GetString();
while(1)
{
}
}
/****************************************************************************************************************/
總結:
當 USART 接收到一個字符時進入中斷服務函數,在中斷服務函數中會將 rxFlag 標誌置 1 ,然後判斷接收緩衝區 receiveBuff 是否溢出。若沒有溢出,則將字符存儲到接收緩衝區中,接收長度 receiveLength 做自加運算。
接收函數 GetString() 通過 500us 的間隔檢測 receiveLength 是否發生改變,若沒有發生改變,意味着 USART 已經接受完所有數據,則從接收緩衝區中取出接收到的數據。若發生改變,則意味着 USART 尚未接收完所有數據,所以等待其接收完所有數據。
本程序中存在一些不足,首先是如果接收緩衝區溢出了並不會報錯,不過只能接收和緩衝區長度一致的字符。還有就是 receiveLength 的檢測時間,理論上來說只需要設定爲 3 * 接收一位數據的時間,即 3 / Baud 秒,但是通過逐一調試,發現只有大於 500us 時才能出現正確的結果。