如果你看過《STM32的中文手冊》,你會發(fā)現(xiàn)STM32的串口是非常強大的,不僅支持最基本的通用串口同步,異步通訊,還具有LAN總線的功能(局域互聯(lián)網(wǎng)),IRDA功能(紅外通訊),SmartCard功能
異步串口通訊協(xié)議:
這里介紹的是串口最基本,最常用的方式,全雙工,異步通訊方式.
通過串口的通訊協(xié)議,我們知道要配置串口通訊,至少要配置幾個參數(shù):
字長(一次傳送的數(shù)據(jù)長度);
波特率(每秒傳輸?shù)臄?shù)據(jù)位數(shù));
奇偶校驗位;
還有停止位;
當然,在我們的ST庫里面,肯定有一個串口初始化結構體啦,這個結構體肯定有幾個成員是來存儲這些控制參數(shù)的!!!!!
*串口線:*
要實現(xiàn)基本的全雙工異步通訊,只要3條線,分別為Rx、Tx、和GND.
串口線主要分兩種,直通線(平行線)和交叉線.
交叉線:
假如PC與板子之間要實現(xiàn)全雙工串口通訊,必然是PC的Tx針腳要連接到板子的Rx針腳,而PC的Rx針腳則要連接至板子的Tx針腳了.由于板子和pc的串口接法是相同的,就要使用交叉線來連接了.
直通線(平行線):
如果有的開發(fā)板是 Tx 連接至
DB9的第2針腳,而Rx連接至第3針腳,其實就是與PC接法是相反的,這樣的板子與 PC 通訊就需要使用直通線了.
推薦使用PC接法(交叉線):
假如使用非 PC 接法,由于板子與 PC 的接法相反,通訊就要使用直通線,但兩個板子之間想要進行串口通訊時,由于接法相同,就要使用交叉線.如果使用PC接法,板子與PC之間接法相同,通訊使用交叉線,兩個相同板子之間接法也相同,通訊也是使用交叉線.
串口工作工程:
這個架構圖圖看起來好像很復雜,但其實我們做軟件的只要大概了解串口發(fā)送過程就行!!!
從下到上,串口外設主要是由三部分組成,分別是波特率控制部分,收發(fā)控制部分,數(shù)據(jù)存儲轉移部分.
波特率控制:
波特率,即每秒傳輸?shù)亩M制位數(shù), 用 b/s (bps)表示,通過對時鐘的控制可以改變波特率.在配置波特率時,我們向波特比率寄存器 USART_BRR 寫入?yún)?shù),修改了串口時鐘的分頻值 USARTDIV. USART_BRR 寄存器包括兩部分,分別是 DIV_Mantissa(USARTDIV 的整數(shù)部分)和 DIVFraction(USARTDIV的小數(shù))部分,
最終,計算公式為USARTDIV=DIV_Mantissa+(DIVFraction/16).
USARTDIV 是對串口外設的時鐘源進行分頻的,對于 USART1,由于它是掛載在 APB2 總線上的,所以它的時鐘源為 fPCLK2;而 USART2、 3 掛載在APB1 上,時鐘源則為 fPCLK1,串口的時鐘源經(jīng)過 USARTDIV 分頻后分別輸出作為發(fā)送器時鐘及接收器時鐘,控制發(fā)送和接收的時序.
收發(fā)控制:
圍繞著發(fā)送器和接收器控制部分,有好多個寄存器: CR1、 CR2、 CR3、SR,即 USART 的三個控制寄存器(Control Register)及一個狀態(tài)寄存器(StatusRegister).通過向寄存器寫入各種控制參數(shù),來控制發(fā)送和接收,如奇偶校驗位,停止位等,還包括對 USART 中斷的控制;串口的狀態(tài)在任何時候都可以從狀態(tài)寄存器中查詢得到.
(具體的控制和狀態(tài)檢查,都是使用庫函數(shù)來實現(xiàn)的,所以就不具體分析這些寄存器位啦.)
數(shù)據(jù)存儲轉移部分:
收發(fā)控制器根據(jù)我們的寄存器配置,對數(shù)據(jù)存儲轉移部分的移位寄存器進行控制.
當我們需要發(fā)送數(shù)據(jù)時,內核或 DMA 外設把數(shù)據(jù)從內存(變量)寫入到發(fā)送數(shù)據(jù)寄存器 TDR 后, 發(fā)送控制器將適時地自動把數(shù)據(jù)從 TDR 加載到發(fā)送移位寄存器,然后通過串口線 Tx,把數(shù)據(jù)一位一位地發(fā)送出去,
在數(shù)據(jù)從 TDR 轉移到移位寄存器時,會產(chǎn)生發(fā)送寄存器TDR 已空事件 TXE,當數(shù)據(jù)從移位寄存器全部發(fā)送出去時,會產(chǎn)生數(shù)據(jù)發(fā)送完成事件 TC,這些事件可以在狀態(tài)寄存器中查詢到.
(而接收數(shù)據(jù)則是一個逆過程,數(shù)據(jù)從串口線 Rx 一位一位地輸入到接收移位寄存器,然后自動地轉移到接收數(shù)據(jù)寄存器 RDR,最后用內核指令或 DMA讀取到內存(變量)中.)
例程解析:
main函數(shù):
int main(void)
{
/* USART1 config 115200 8-N-1 */
USART1_Config();
printf(USART1, “\r\n This is a USART1 test \r\n”);
printf(USART1, “\r\n(“DATE ” -” TIME “) \r\n”);
while(1)
{}
}
這里main函數(shù)首先調用了USART1_Config(),接著就打印了幾條信息.
USART1_Config()函數(shù):
void USART1_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIO //注意這里是GPIO復用
A, ENABLE);
//TX腳
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
//RX腳
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
//配置UART
USART_InitStructure.USART_BaudRate = 115200; //設置波特率
USART_InitStructure.USART_WordLength = USART_WordLength_8b; //字節(jié)長
USART_InitStructure.USART_StopBits = USART_StopBits_1; //停止位
USART_InitStructure.USART_Parity = USART_Parity_No ; //無校驗位
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlow //不采用硬件流控制
Control_None;
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //配置串口模式 全雙工
USART_Init(USART1, &USART_InitStructure);
USART_Cmd(USART1, ENABLE);
}
這個函數(shù)做了以下的工作:
1. 使能了串口 1 的時鐘.
2. 配置好了 usart1 的 I/O.
3. 配置好了 usart1 的工作模式,具體為波特率為 115200 、 8 個數(shù)據(jù)位、 1個停止位、無硬件流控制.
(即 115200 8-N-1)
RCC_APB2PeriphClockCmd()
打開了GPIOA和USART1的時鐘
這是因為使用了 GPIOA 的 PA9 和 PA10 的默認復用USART1 的功能,在使用復用功能的時候,要開啟相應的功能時鐘USART1.
GPIO_InitStructure配置
GPIO的初始化
通過《 STM32F103CDE 增強型系列數(shù)據(jù)手冊》的引腳功能定義中查詢到為什么是PA9和PA10用作串口的Tx和Rx,而不是其它GPIO引腳
選定了這兩個引腳,PA9作為TX,PA10作為RX.
Tx 為發(fā)送端, 輸出引腳,而且現(xiàn)在 GPIO 是使用復用功能,所以要把它配置為復用推挽輸出(GPIO_Mode_AF_PP)
Rx 引腳為接收端, 輸入引腳,所以配置為浮空輸入模式 GPIO_Mode_IN_FLOATING.
(在使用復用功能的時候如果對GPIO的模式不太確定的話,可以從《STM32參考手冊》的GPIO章節(jié)查詢)
USART的初始化:(根據(jù)通訊協(xié)議)
USART_InitStructure.USART_BaudRate = 115200;
波特率設置,利用庫函數(shù),我們可以直接這樣配置波特率,而不需要自行計算 USARTDIV 的分頻因子.在這里把串口的波特率設置為 115200,也可以設置為 9600 等常用的波特率,在《 STM32 參考手冊》中列舉了一些常用的
波特率設置及其誤差.如果配置成 9600, 那么在和 PC 通訊的時候,也應把 PC 的串口傳輸波特率設置為相同的 9600.通訊協(xié)議要求兩個通訊器件之間的波特率、字長、停止位奇偶校驗位都相同.
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
配置串口傳輸?shù)淖娱L,也可以設置為9位.
USART_InitStructure.USART_StopBits = USART_StopBits_1;
配置停止位.把通訊協(xié)議中的停止位設置為 1 位.
USART_InitStructure.USART_Parity = USART_Parity_No ;
由于是個例程,所以就不弄校驗位了.
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlow Control_None;
配置硬件控制流,不采用硬件流
硬件流:
在 STM32 的很多外設都具有硬件流的功能,其功能表現(xiàn)為:當外設硬件處于準備好的狀態(tài)時,硬件啟動自動控制,而不需要軟件再進行干預.
*在串口這個外設的硬件流具體表現(xiàn)為:
使用串口的 RTS (Request toSend) 和 CTS(Clear to Send) 針腳,當串口已經(jīng)準備好接收新數(shù)據(jù)時,由硬件流自動把 RTS 針拉低(向外表示可接收數(shù)據(jù));在發(fā)送數(shù)據(jù)前,由硬件流自動檢查 CTS 針是否為低(表示是否可以發(fā)送數(shù)據(jù)),再進行發(fā)送.
(例程中沒有使用到 CTS 和 RTS,所以不采用硬件流控制.)
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
配置串口的模式.為了配置雙線全雙工通訊,需要把Rx和Tx模式都開啟.
printf()函數(shù)重定向:
在main函數(shù)中,配置好USART1后,就往終端打印信息.
要使printf()函數(shù)工作的話,我們需要把printf()重新定向到串口中.
重定向:是指用戶可以自己重寫C的庫函數(shù),當連接器檢查到用戶編寫了與C庫函數(shù)相同名字的函數(shù)時,優(yōu)先采用用戶編寫的函數(shù),這樣用戶就可以實現(xiàn)對庫的修改了.
(因為printf()在C標準庫函數(shù)中實質是一個宏,最終是調用了fputc()這個函數(shù)的.)
int fputc(int ch,FILE *f)
{
USART_SendData(USART1,(unsigned char)ch);
while( USART_GetFlagStatus(USART1,USART_FLAF_TC)!=RESET);
return(ch);
}
這里是調用了兩個庫函數(shù)USART_SendData()和USART_GetFlagStatus()
(具體可以在keil環(huán)境下跟蹤查看函數(shù)實現(xiàn))
重定向時,把fputc()的形參ch作為串口要發(fā)送的數(shù)據(jù),也就是說,當使用當使用printf(),它調用這個 fputc()函數(shù)時,然后使用 ST 庫的串口發(fā)送函數(shù) USART_SendData(),把數(shù)據(jù)轉移到發(fā)送數(shù)據(jù)寄存器 TDR,觸發(fā)串口向 PC 發(fā)送一個相應的數(shù)據(jù).
調用完USART_SendData()后,要使用while( USART_GetFlagStatus(USART1,USART_FLAG_TC)!= SET)語句不停地檢查串口發(fā)送是否完成的標志位TC,一直檢測到標志為完成,才進入一下步的操作,避免出錯.
(在這段 while 的循環(huán)檢測的延時中,串口外設已經(jīng)由發(fā)送控制器根據(jù)我們的配置把數(shù)據(jù)從移位寄存器一位一位地通過串口線 Tx 發(fā)送出去了)
fgetc()用于接收的重定向(scanf())
int fgetc(FILE *f)
{
/* 等待串口1輸入數(shù)據(jù) */
while (USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == RESET);
return (int)USART_ReceiveData(USART1);
}
這里就是在循環(huán)檢測狀態(tài),一旦有數(shù)據(jù)就調用USART_ReceiveData()庫函數(shù)
PS:在使用C標準輸出庫函數(shù)時,切記要把stdio.h這個頭文件添加進來,如果是在keil環(huán)境下,還需要設置中選擇Target選項,勾選Use MicroLIB(微庫)
(這個微庫是keil MDK為嵌入式應用定做的C庫)
PS:關于printf()
受緩沖區(qū)大小的影響,有時候在用它打印的時候程序會發(fā)生莫名奇妙的錯誤,而實際上就是由于使用printf()這個函數(shù)引起的,其優(yōu)點就是這種情況很少見且支持的格式較多.
上一篇:STM32之DMA(直接存儲器存儲)
下一篇:STM32之SysTick(系統(tǒng)定時器)
推薦閱讀
史海拾趣