一、什么是串口通信
串口通信是指外部設備與主控芯片之間,通過數據信號線、地線等,按位進行數據傳輸的一種通信方式,屬于串行通信方式。串行通信是指使用一條數據線依次逐位傳輸數據,每一位數據占據固定長度的時間。可以看一下簡單的串行通信示意圖。
串口通信示意圖
二、串口通信有什么用
這里簡單列舉一下串口通信的用途
? 下載程序
? 打印信息 比如將ADC采集到的電壓發送給上位機的串口調試助手,或者實時監測某一個變量的變化。
三、STM32的串口通信
普中核心板上使用的STM32F103ZET6有三個USART,兩個UART,他們都支持串口通信功能。USART(通用同步異步收發器)與UART(通用異步收發器)相比,多了一個同步功能,可以認為USART是UART的增強型。
四、串口通信相關概念
44.1 波特率
引用專業的說法,波特率表示單位時間內傳送的碼元符號的個數,它是對符號傳輸速率的一種度量。其實意思就是波特率表示1s內傳輸碼元的個數。在單片機中數字都是二進制的01表示的,所以波特率可以說是1s內傳輸01的個數。常見的波特率有38400、9600和115200等。
波特率通常由波特率發生器產生,串口要想實現收發首先要有波特率發生器,網上介紹波特率發生器的作用是輸入時鐘轉換出需要的波特率CLK。個人理解,波特率發生器就是提供一個時鐘,這樣才能發送出正確波特率的信息,比如1和0需要多久的高/低電平表示。
在串口通信時如果收發雙方波特率不相同會導致通信失敗,要么是接收不到,要么是接收到的是亂碼。
4.2 全雙工和半雙工
- ? 全雙工可以簡單解釋為,我在接收消息的同時,你也可以發送消息。
- ? 半雙工可以簡單解釋為,我在接收消息時,沒辦法發送消息。類似于對講機,你說話時占用了信道,對方無法跟你講話,只有當你說完了,他才可以對你講話。
4.3 同步通信和異步通信
同步通信和異步通信的區別在于通信雙方是否需要時鐘同步。同步通信的接收雙方之間除了需要數據線之外,還需要一根時鐘線,而異步通信不需要。關于二者的詳細定義與區別,請大家自行搜索。
五、硬件連接
串口通信只需幾條線即可在兩個系統間交換信息,特別適用于計算機與計算機、計算機與外設之間的通信,常用的串口通信接口標準有很多,比如RS-232C、RS-232、RS-485等。但是放在單片機開發里,最簡單的串口通信就是用四根線VCC、GND、TXD和RXD實現通信。
串口通信硬件連接示意圖
普中核心板上常用的是USART1,其引腳對應如下
? TXD——PA9
? RXD——PA10
六、串口通信程序配置
下面以配置USART1為例,來簡單展示一下USART的配置方法。
66.1 使能串口時鐘和GPIO時鐘
// 使能USART1,GPIOA時鐘
RCC_APB2PeriphClockCmd (RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE);
6.2 初始化GPIO
初始化USART1用到的GPIO。TXD引腳設置為復用推挽式輸出,RXD引腳設置為輸入浮空。
// USART1_TX GPIOA.9
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; // PA.9
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; // 復用推挽輸出
GPIO_Init(GPIOA, &GPIO_InitStructure); // 初始化GPIOA.9
// USART1_RX GPIOA.10初始化
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; // PA10
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; // 輸入浮空
GPIO_Init(GPIOA, &GPIO_InitStructure); // 初始化GPIOA.10
6.3 初始化串口參數
庫函數提供了一個結構體,用于初始化串口。其中包括
USART_InitTypeDef USART_InitStructure;
// USART 初始化設置
USART_InitStructure.USART_BaudRate = bound; // 串口波特率
USART_InitStructure.USART_WordLength = USART_WordLength_8b; // 字長為8位數據格式
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(USART1, &USART_InitStructure); // 初始化串口1
6.4 使能串口
USART_Cmd(USART1, ENABLE); // 使能串口1
6.5 串口接收中斷
平時開發過程中經常需要開啟串口接收中斷,配置串口接收中斷的方法與上一篇的外部中斷有些類似,主要包括以下步驟
- ? 配置中斷分組(通常在main函數中初始化中配置)
- ? 設置中斷優先級
- ? 使能中斷
配置中斷優先級
NVIC_InitTypeDef NVIC_InitStructure;
// Usart1 NVIC 配置
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ; // 搶占優先級3
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; // 子優先級3
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; // IRQ通道使能
NVIC_Init(&NVIC_InitStructure); // 根據指定的參數初始化VIC寄存器
使能串口接收中斷
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); // 開啟串口接收中斷
6.6 串口接收中斷服務函數
通常接收到的數據會是一幀,很少是一個單獨的字符,這里給出一個接收一幀數據的串口中斷服務函數。需要注意的是,在初始化串口時,需要使能空閑中斷。
使能空閑中斷的程序如下
USART_ITConfig(USART1, USART_IT_IDLE, ENABLE); // 使能空閑中斷
/*
*==============================================================================
*函數名稱:USART1_IRQHandler
*函數功能:USART1中斷服務函數
*輸入參數:無
*返回值:無
*備 注:無
*==============================================================================
*/
u32 gReceCount = 0; // 接收計數變量
u32 gClearCount = 0; // 清空接收數組計數變量
u8 gReceFifo[1500]; // 接收數組
u8 gReceEndFlag = 0; // 接收完成標志位
void USART1_IRQHandler(void)
{
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //接收到一個字節
{
gReceFifo[gReceCount++] = USART_ReceiveData(USART1);
}
else if(USART_GetITStatus(USART1,USART_IT_IDLE) != RESET) //接收到一幀數據
{
USART1- >SR; // 先讀SR
USART1- >DR; // 再讀DR
gReceEndFlag = 1; // 接收完成標志置1
}
}
接收完成后,接收完成標志位會置1。此時,對接收到的幀進行解析處理。解析完成后需要清除接收數組,同時,不要忘記清除接收完成標志位。
/*
*==============================================================================
*函數名稱:Uart_Rece_Pares
*函數功能:解析串口接收內容
*輸入參數:無
*返回值:無
*備 注:無
*==============================================================================
*/
void Uart_Rece_Pares(void) // 串口接收內容解析函數
{
if (gReceEndFlag == 1) // 如果接收完成
{
// 解析接收內容
// 清空接收數組
for (gClearCount = 0;gClearCount < gReceCount;gClearCount ++)
{
gReceFifo[gClearCount] = ' ';
}
gReceEndFlag = 0; // 清除接收完成標志位
gReceCount = 0; // 清零接收計數變量
}
}
6.7 串口發送函數
//串口發送函數
void USART1_Send(u8*str)
{
u8 index=0;
do
{
USART_SendData(USART1,str[index++]);
while(USART1,str[index++]);
while(USART_GetFlagStatus(USART1,USART_FLAG_TXE) == RESET);
}
while(str[index]!=0);
}
其實這里最根本的USART_SendData()本質就是將數據搬運到串口發送的寄存器。當然除了直接用發送函數發送,也可以直接重定向之后用printf發送,這里就不詳細介紹了,有需要的友友可以直接去看普中或者正點的教程視頻。
七、拓展
7.1 printf重定向
關于重定向的概念這里就不再做介紹了,重定向之后就可以在程序中使用printf
直接打印或者發送字符串,不再需要串口發送函數。重定向的方法就是在串口的.c文件中添加下面的程序
// 加入以下函數可以使用printf
#pragma import(__use_no_semihosting)
// 標準庫需要的支持函數
struct __FILE
{
int handle;
};
FILE __stdout;
//定義_sys_exit()以避免使用半主機模式
void _sys_exit(int x)
{
x = x;
}
//重定義fputc函數
int fputc(int ch, FILE *f)
{
while((USART1- >SR&0X40)==0);//循環發送,直到發送完畢
USART1- >DR = (u8) ch;
return ch;
}
7.2 接收幀解析
這里的接收幀解析比較簡單,比如有些項目要求接收到某些特定字符執行某些操作。這時需要根據接收幀的長度和固定位置的字符來解析命令。
比如項目要求上位機(電腦)發送“BEEP ON”時,蜂鳴器響。這時在解析時只要接收到長度為6,第5和第6個字符分別為“O”,“N”時,開啟蜂鳴器即可。
// 解析接收內容
if (gReceCount == 6 && gReceFifo[5] == 'O' && gReceFifo[6] == 'N')
{
// 開啟蜂鳴器
}
當然上面的只是粗略的卡命令,也可以寫的更詳細。
八、實戰項目
8.1 前期準備
- ? CH340驅動
- ? USB轉TTL,用于單片機與電腦的通信
- ? 串口調試助手
剛買來的普中核心板,不拔短接片的話可以直接通過USB下載程序,或者與電腦進行串口通信,串口為USART1。注意一定要插圖中標出來的USB接口,另一個只能用來供電。
串口通信跳線帽連接
8.2 項目要求
? 單片機上電發送“Sys Ready!”
? 電腦串口助手發送“LED1 ON”(帶回車換行),LED1點亮,同時單片機回復“OK!”
? 電腦串口助手發送“LED1 OFF”(帶回車換行),LED2熄滅,同時單片機回復“OK!”
88.3 串口程序
8.3.1 初始化串口
首先是串口初始化程序,需要開啟接收中斷和空閑中斷。
/*
*==============================================================================
*函數名稱:uart_init
*函數功能:初始化USART1
*輸入參數:bound:波特率
*返回值:無
*備 注:可以修改成輸入初始化哪個USART
*==============================================================================
*/
void uart_init(u32 bound)
{
// 相關結構體定義
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
// 使能USART1,GPIOA時鐘
RCC_APB2PeriphClockCmd (RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE);
// USART1_TX GPIOA.9
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; // PA.9
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; // 復用推挽輸出
GPIO_Init(GPIOA, &GPIO_InitStructure); // 初始化GPIOA.9
// USART1_RX GPIOA.10初始化
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; // PA10
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; // 浮空輸入
GPIO_Init(GPIOA, &GPIO_InitStructure); // 初始化GPIOA.10
// Usart1 NVIC 配置
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ; // 搶占優先級3
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; // 子優先級3
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; // IRQ通道使能
NVIC_Init(&NVIC_InitStructure); // 根據指定的參數初始化VIC寄存器
// USART 初始化設置
USART_InitStructure.USART_BaudRate = bound; // 串口波特率
USART_InitStructure.USART_WordLength = USART_WordLength_8b; // 字長為8位數據格式
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(USART1, &USART_InitStructure); // 初始化串口1
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); // 開啟串口接收中斷
USART_ITConfig(USART1, USART_IT_IDLE, ENABLE); // 使能空閑中斷
USART_Cmd(USART1, ENABLE); // 使能串口1
}
其次需要加上重定向函數,直接復制上面的即可。
8.3.2 串口接收中斷服務函數
/*
*==============================================================================
*函數名稱:USART1_IRQHandler
*函數功能:USART1中斷服務函數
*輸入參數:無
*返回值:無
*備 注:無
*==============================================================================
*/
u32 gReceCount = 0; // 接收計數變量
u32 gClearCount = 0; // 清空接收數組計數變量
u8 gReceFifo[1500]; // 接收數組
u8 gReceEndFlag = 0; // 接收完成標志位
void USART1_IRQHandler(void)
{
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //接收到一個字節
{
gReceFifo[gReceCount++] = USART_ReceiveData(USART1);
}
else if(USART_GetITStatus(USART1,USART_IT_IDLE) != RESET) //接收到一幀數據
{
USART1- >SR; // 先讀SR
USART1- >DR; // 再讀DR
gReceEndFlag = 1; // 接收完成標志置1
}
}
8.3.3 接收幀解析函數
/*
*==============================================================================
*函數名稱:Uart_Rece_Pares
*函數功能:解析串口接收內容
*輸入參數:無
*返回值:無
*備 注:無
*==============================================================================
*/
void Uart_Rece_Pares(void) // 串口接收內容解析函數
{
if (gReceEndFlag == 1) // 如果接收完成
{
// 解析接收內容
if (gReceFifo[6] == 'N')
{
Med_Led_StateCtrl (LED1,LED_ON); // 點亮LED1
printf ("OK!rn");
}
if (gReceFifo[6] == 'F' && gReceFifo[7] == 'F')
{
Med_Led_StateCtrl (LED1,LED_OFF); // 熄滅LED1
printf ("OK!rn");
}
// 清空接收數組
for (gClearCount = 0;gClearCount < gReceCount;gClearCount ++)
{
gReceFifo[gClearCount] = ' ';
}
gReceEndFlag = 0; // 清除接收完成標志位
gReceCount = 0; // 清零接收計數變量
}
}
8.3.3 main函數
int main(void)
{
Med_Mcu_Iint(); // 系統初始化
printf ("Sys Ready!rn");
while(1)
{
Uart_Rece_Pares (); // 接收幀解析
}
}
-
串口通信
+關注
關注
34文章
1624瀏覽量
55508 -
異步收發器
+關注
關注
0文章
36瀏覽量
10847 -
中斷優先級
+關注
關注
0文章
22瀏覽量
9368 -
USART串口
+關注
關注
0文章
32瀏覽量
6818 -
波特率發生器
+關注
關注
0文章
11瀏覽量
4028
發布評論請先 登錄
相關推薦
評論