USART為通用同步/ 異步收發(fā)器。stm32F103RC內(nèi)置了3個通用同步/異步收發(fā)器(USART1、USART2和USART3)闷供,和2個通用異步收發(fā)器(UART4和UART5)烟央。
這5個接口提供異步通信、支持IrDA SIR ENDEC傳輸編解碼歪脏、多處理器通信模式疑俭、單線半雙工通信模式和LIN主/從功能。USART1接口通信速率可達4.5兆位/秒婿失,其他接口的通信速率可達2.25兆位/秒钞艇。USART1、USART2和USART3接口具有硬件的CTS和RTS信號管理豪硅、兼容ISO7816的智能卡模式和類SPI通信模式哩照,除了UART5之外所有其他接口都可以使用DMA操作婿脸。
串行通信工作原理
串行通信汇荐,即數(shù)據(jù)一位一位得按順序發(fā)送病梢。并行通信许蓖,即數(shù)據(jù)的各位同時發(fā)送概页。
串行通訊的分類
- 按通訊方式
同步通訊:帶時鐘同步信號傳輸粱栖。比如:SPI(全雙工)刻两,IIC(半雙工)通信接口外恕。
異步通訊:不帶時鐘同步信號赖草。比如:UART(全雙工)学少,單總線(半雙工)。 -
按數(shù)據(jù)傳送方向
單工:只支持數(shù)據(jù)在一個方向上傳輸秧骑;
半雙工:允許數(shù)據(jù)在兩個方向上傳輸版确,某一時刻只有一個傳輸方向;
全雙工:允許數(shù)據(jù)同時在兩個方向上傳輸乎折。
TTL RS232接口等指的是電平標準(不是硬件绒疗,硬件都是GND/RX/TX)(單片機的電平標準(TTL電平):+5V表示1,0V表示0骂澄;Rs232的電平標準:+15/+13 V表示0吓蘑,-15/-13表示1。)故如果需要在兩種不同接口之間實現(xiàn)串口通訊需要電平轉(zhuǎn)換電路坟冲。PL2303磨镶、CP2102、CH340芯片是 USB 轉(zhuǎn) TTL串口 的芯片健提,MAX232芯片是 TTL電平與RS232電平的專用雙向轉(zhuǎn)換芯片琳猫,可以TTL轉(zhuǎn)RS-232,也可以RS-232轉(zhuǎn)TTL私痹。
不同電平比較.PNG
UART串口通信的數(shù)據(jù)包以幀為單位脐嫂,常用的幀結(jié)構(gòu)為:1位起始位+8位數(shù)據(jù)位+1位奇偶校驗位(可選)+1位停止位统刮。奇校驗是指每幀數(shù)據(jù)中,包括數(shù)據(jù)位和奇偶校驗位在內(nèi)的全部9個位中1的個數(shù)必須是奇數(shù)账千,偶校驗同理网沾。
STM32F103串口
RX:接收數(shù)據(jù)串行輸。通過過采樣技術(shù)來區(qū)別數(shù)據(jù)和噪音蕊爵,從而恢復數(shù)據(jù)。
TX:發(fā)送數(shù)據(jù)輸出桦山。當發(fā)送器被禁止時攒射,輸出引腳恢復到它的I/O端口配置。當發(fā)送器被激活恒水,
并且不發(fā)送數(shù)據(jù)時会放,TX引腳處于高電平。在單線和智能卡模式里钉凌,此I/O口被同時用于數(shù)據(jù)的發(fā)
送和接收咧最。
● 總線在發(fā)送或接收前應處于空閑狀態(tài)
● 一個起始位
● 一個數(shù)據(jù)字(8或9位),最低有效位在前
● 0.5御雕,1.5矢沿,2個的停止位,由此表明數(shù)據(jù)幀的結(jié)束
● 使用分數(shù)波特率發(fā)生器 —— 12位整數(shù)和4位小數(shù)的表示方法酸纲。
● 一個狀態(tài)寄存器(USART_SR)
● 數(shù)據(jù)寄存器(USART_DR)
● 一個波特率寄存器(USART_BRR)捣鲸,12位的整數(shù)和4位小數(shù)
● 一個智能卡模式下的保護時間寄存器(USART_GTPR)
在同步模式中需要下列引腳:
● CK:發(fā)送器時鐘輸出。此引腳輸出用于同步傳輸?shù)?時鐘闽坡, (在Start位和Stop位上沒有時鐘
脈沖栽惶,軟件可選地,可以在最后一個數(shù)據(jù)位送出一個時鐘脈沖)疾嗅。數(shù)據(jù)可以在RX上同步被接
收外厂。這可以用來控制帶有移位寄存器的外部設備(例如LCD驅(qū)動器)。時鐘相位和極性都是軟
件可編程的代承。在智能卡模式里汁蝶,CK可以為智能卡提供時鐘。
在IrDA模式里需要下列引腳:
● IrDA_RDI: IrDA模式下的數(shù)據(jù)輸入论悴。
● IrDA_TDO: IrDA模式下的數(shù)據(jù)輸出穿仪。
下列引腳在硬件流控模式中需要:
● nCTS: 清除發(fā)送,若是高電平意荤,在當前數(shù)據(jù)傳輸結(jié)束時阻斷下一次的數(shù)據(jù)發(fā)送啊片。
● nRTS: 發(fā)送請求,若是低電平玖像,表明USART準備好接收數(shù)據(jù)
可配置的停止位
隨每個字符發(fā)送的停止位的位數(shù)可以通過控制寄存器2的位13紫谷、12進行編程齐饮。
- 1個停止位:停止位位數(shù)的默認值。
- 2個停止位:可用于常規(guī)USART模式笤昨、單線模式以及調(diào)制解調(diào)器模式祖驱。
- 0.5個停止位:在智能卡模式下接收數(shù)據(jù)時使用。
- 1.5個停止位:在智能卡模式下發(fā)送和接收數(shù)據(jù)時使用瞒窒。
配置步驟: - 通過在USART_CR1寄存器上置位UE位來激活USART
- 編程USART_CR1的M位來定義字長捺僻。
- 在USART_CR2中編程停止位的位數(shù)。
- 如果采用多緩沖器通信崇裁,配置USART_CR3中的DMA使能位(DMAT)匕坯。按多緩沖器通信中的描述配置DMA寄存器。
- 利用USART_BRR寄存器選擇要求的波特率拔稳。
- 設置USART_CR1中的TE位葛峻,發(fā)送一個空閑幀作為第一次數(shù)據(jù)發(fā)送。
- 把要發(fā)送的數(shù)據(jù)寫進USART_DR寄存器(此動作清除TXE位)巴比。在只有一個緩沖器的情況下术奖,對每個待發(fā)送的數(shù)據(jù)重復步驟7。
-
在USART_DR寄存器中寫入最后一個數(shù)據(jù)字后轻绞,要等待TC=1采记,它表示最后一個數(shù)據(jù)幀的傳輸結(jié)束。當需要關(guān)閉USART或需要進入停機模式之前政勃,需要確認傳輸結(jié)束挺庞,避免破壞最后一次傳輸。
USART寄存器介紹
1稼病、 狀態(tài)寄存器(USART_SR)
狀態(tài)寄存器.PNG
表1.PNG
表2.PNG
2选侨、 數(shù)據(jù)寄存器(USART_DR)
數(shù)據(jù)寄存器.PNG
USART_DR.PNG
3、 波特比率寄存器(USART_BRR)
波特比特率寄存器.PNG
波特率計算.PNG
上式中然走,fpclkx 是給串口的時鐘(PCLK1 用于 USART2援制、3、4芍瑞、5晨仑,PCLK2 用于 USART1);
USARTDIV 是一個無符號定點數(shù)拆檬。
4洪己、 控制寄存器 1(USART_CR1)
CR2和CR3主要用于同步串行控制和流控制。(不做介紹)其中USART_CR2的第[13:12]位為STOP位竟贯,為00b表示1位停止位(默認)答捕,01b表示0.5位停止位,10b表示2位停止位屑那,11b表示1.5位停止位拱镐。
開發(fā)板實驗及例程分析
開發(fā)板自帶的usart文件夾內(nèi)包含了 usart.c 和 usart.h 兩個文件艘款。這兩個文件用于串口的初始化和中斷接收。這里只是針對串口 1沃琅。usart.c里面包含了2個函數(shù)一個是void USART1_IRQHandler(void);另外一個是void uart_init(u32pclk2哗咆,u32 bound);里面還有一段對串口 printf 的支持代碼,如果去掉益眉,則會導致 printf 無法使用晌柬,雖然軟件編譯不會報錯,但是硬件上 STM32 是無法啟動的郭脂,這段代碼不能修改年碘。
當接收到從電腦發(fā)過來的數(shù)據(jù),把接收到的數(shù)據(jù)保存在 USART_RX_BUF 中朱庆,同時在接收狀態(tài)寄存器(USART_RX_STA)中計數(shù)接收到的有效數(shù)據(jù)個數(shù),當收到回車(回車的表示由 2個字節(jié)組成:0X0D 和 0X0A)的第一個字節(jié) 0X0D 時闷祥,計數(shù)器將不再增加娱颊,等待 0X0A 的到來,而如果 0X0A 沒有來到凯砍,則認為這次接收失敗箱硕,重新開始下一次接收。如果順利接收到 0X0A悟衩,則標記 USART_RX_STA 的第 15 位剧罩,這樣完成一次接收,并等待該位被其他程序清除座泳,從而開始下一次的接收惠昔,而如果遲遲沒有收到0X0D,那么在接收數(shù)據(jù)超過USART_REC_LEN的時候挑势,則會丟棄前面的數(shù)據(jù)镇防,重新接收。
寄存器
1潮饱、USART1_IRQHandler 函數(shù)
#if EN_USART1_RX //如果使能了接收
//串口 1 中斷服務程序
//注意,讀取 USARTx->SR 能避免莫名其妙的錯誤
u8 USART_RX_BUF[USART_REC_LEN]; //接收緩沖,最大 USART_REC_LEN 個字節(jié).
//接收狀態(tài)
//bit15来氧,接收完成標志
//bit14,接收到 0x0d
//bit13~0香拉,接收到的有效字節(jié)數(shù)目
u16 USART_RX_STA=0; //接收狀態(tài)標記
void USART1_IRQHandler(void)
{
u8 res;
#if SYSTEM_SUPPORT_OS //如果 SYSTEM_SUPPORT_OS 為真啦扬,則需要支持 OS.
OSIntEnter();
#endif
if(USART1->SR&(1<<5)) //接收到數(shù)據(jù)
{
res=USART1->DR;
if((USART_RX_STA&0x8000)==0)//接收未完成
{
if(USART_RX_STA&0x4000)//接收到了 0x0d
{
if(res!=0x0a)USART_RX_STA=0;//接收錯誤,重新開始
else USART_RX_STA|=0x8000; //接收完成了
}else //還沒收到 0X0D
{
if(res==0x0d)USART_RX_STA|=0x4000;
else
{
USART_RX_BUF[USART_RX_STA&0X3FFF]=res;
USART_RX_STA++;
if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;
//接收數(shù)據(jù)錯誤,重新開始接收
}
}
}
}
#if SYSTEM_SUPPORT_OS //如果 SYSTEM_SUPPORT_OS 為真,則需要支持 OS.
OSIntExit();
#endif
}
#endif
該部分為串口1的中斷服務函數(shù)凫碌。先判斷狀態(tài)寄存器是否接收到數(shù)據(jù)扑毡,接收到數(shù)據(jù)把數(shù)據(jù)放入一段緩存區(qū)中。每次結(jié)束后盛险,USART_RX_STA清零僚楞,再存數(shù)據(jù)時會覆蓋之前的存放位置勤晚。(USART_RX_STA類似于跟蹤每次存儲的數(shù)據(jù)(8位或9位的幀))
2、uart_init 函數(shù)
//初始化 IO 串口 1
//pclk2:PCLK2 時鐘頻率(Mhz)
//bound:波特率
void uart_init(u32 pclk2,u32 bound)
{
float temp;
u16 mantissa;
u16 fraction;
temp=(float)(pclk2*1000000)/(bound*16);//得到 USARTDIV
mantissa=temp; //得到整數(shù)部分
fraction=(temp-mantissa)*16; //得到小數(shù)部分
mantissa<<=4;
mantissa+=fraction;
RCC->APB2ENR|=1<<2; //使能 PORTA 口時鐘
RCC->APB2ENR|=1<<14; //使能串口時鐘
GPIOA->CRH&=0XFFFFF00F;//IO 狀態(tài)設置
GPIOA->CRH|=0X000008B0;//IO 狀態(tài)設置
RCC->APB2RSTR|=1<<14; //復位串口 1
RCC->APB2RSTR&=~(1<<14);//停止復位
//波特率設置
USART1->BRR=mantissa; // 波特率設置
USART1->CR1|=0X200C; //1 位停止,無校驗位.
#if EN_USART1_RX //如果使能了接收
//使能接收中斷
USART1->CR1|=1<<5; //接收緩沖區(qū)非空中斷使能
MY_NVIC_Init(3,3,USART1_IRQn,2);//組 2泉褐,最低優(yōu)先級
#endif
}
先配置PA9和PA10所在的IO口赐写,時鐘配置,分別為下拉輸入配置和復用開漏輸出配置膜赃。RCC->APB2RSTR為為時鐘APB2外設的復位寄存器挺邀,復位串口1,停止復位跳座。設置波特率(9600為234端铛,115200為271)。設置串口控制寄存器(第12位設置M位(0表示8疲眷,1表示9)禾蚕,13位表示開啟串口、5位表示接受數(shù)據(jù)開啟中斷狂丝,3位表示開啟發(fā)送單元换淆、2位表示接收有效位)0X200C=0010000000001100開啟串口,開啟發(fā)送和接收單元几颜。該程序在添加了條件判斷后倍试,使能了接收中斷。并調(diào)用正點原子的中斷管理函數(shù)蛋哭。
3县习、主函數(shù)
#include "sys.h"
#include "usart.h"
#include "delay.h"
#include "led.h"
int main(void)
{
u8 t; u8 len;
u16 times=0;
Stm32_Clock_Init(9); //系統(tǒng)時鐘設置
delay_init(72); //延時初始化
uart_init(72,9600); //串口初始化為 9600
LED_Init(); //初始化與 LED 連接的硬件接口
while(1)
{
if(USART_RX_STA&0x8000)
{
len=USART_RX_STA&0x3fff;//得到此次接收到的數(shù)據(jù)長度
printf("\r\n 您發(fā)送的消息為:\r\n");
for(t=0;t<len;t++)
{
USART1->DR=USART_RX_BUF[t];
while((USART1->SR&0X40)==0);//等待發(fā)送結(jié)束
}
printf("\r\n\r\n");//插入換行
USART_RX_STA=0;
}else
{
times++;
if(times%5000==0)
{
printf("\r\nALIENTEK MiniSTM32 開發(fā)板 串口實驗\r\n");
printf("正點原子@ALIENTEK\r\n\r\n\r\n");
}
if(times%200==0)printf("請輸入數(shù)據(jù),以回車鍵結(jié)束\r\n");
if(times%30==0)LED0=!LED0;//閃爍 LED,提示系統(tǒng)正在運行.
delay_ms(10);
}
}
}
主函數(shù)把接收到的數(shù)據(jù)發(fā)送出來(寫DR寄存器)。
庫函數(shù)
思路與寄存器類似谆趾。
1躁愿、 USART1_IRQHandler 函數(shù)
void USART1_IRQHandler(void) //串口 1 中斷服務程序
{
u8 Res;
#if SYSTEM_SUPPORT_OS //如果 SYSTEM_SUPPORT_OS 為真,則需要支持 OS
OSIntEnter();
#endif
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
//接收中斷(接收到的數(shù)據(jù)必須是 0x0d 0x0a 結(jié)尾)
{
Res =USART_ReceiveData(USART1);//(USART1->DR); //讀取接收到的數(shù)據(jù)
if((USART_RX_STA&0x8000)==0)//接收未完成
{
if(USART_RX_STA&0x4000)//接收到了 0x0d
{
if(Res!=0x0a)USART_RX_STA=0;//接收錯誤,重新開始
else USART_RX_STA|=0x8000; //接收完成了
}
else //還沒收到 0X0D
{
if(Res==0x0d)USART_RX_STA|=0x4000;
else
{
USART_RX_BUF[USART_RX_STA&0X3FFF]=Res ;
USART_RX_STA++;
if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;
//接收數(shù)據(jù)錯誤,重新開始接收
}
}
}
}
#if SYSTEM_SUPPORT_OS //如果 SYSTEM_SUPPORT_OS 為真沪蓬,則需要支持 OS
OSIntExit();
#endif
}
2攘已、 uart_init 函數(shù)
void uart_init(u32 bound){
//GPIO 端口設置
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA
|RCC_APB2Periph_AFIO, ENABLE); //使能 USART1,GPIOA 時鐘
//以及復用功能時鐘
//USART1_TX PA.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 發(fā)送端
//USART1_RX PA.10 浮空輸入
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
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 ;//搶占優(yōu)先級 3
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //子優(yōu)先級 3
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ 通道使能
NVIC_Init(&NVIC_InitStructure); //中斷優(yōu)先級配置
//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;//無硬件數(shù)據(jù)流控制
USART_InitStructure.USART_Mode = USART_Mode_Rx |USART_Mode_Tx;//收發(fā)模式
USART_Init(USART1, &USART_InitStructure); //初始化串口
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//開啟中斷
USART_Cmd(USART1, ENABLE); //使能串口
}
3怜跑、主函數(shù)
#include "sys.h"
#include "usart.h"
#include "delay.h"
#include "led.h"
int main(void)
{
u8 t;
u8 len;
u16 times=0;
delay_init(); //延時函數(shù)初始化
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //設置中斷分組
uart_init(9600); //串口初始化為 9600
LED_Init(); //初始化與 LED 連接的硬件接口
while(1)
{
if(USART_RX_STA&0x8000)
{
len=USART_RX_STA&0x3fff;//得到此次接收到的數(shù)據(jù)長度
printf("\r\n 您發(fā)送的消息為:\r\n");
for(t=0;t<len;t++)
{
USART1->DR=USART_RX_BUF[t];
while((USART1->SR&0X40)==0);//等待發(fā)送結(jié)束
}
printf("\r\n\r\n");//插入換行
USART_RX_STA=0;
}else
{
times++;
if(times%5000==0)
{
printf("\r\nALIENTEK MiniSTM32 開發(fā)板 串口實驗\r\n");
printf("正點原子@ALIENTEK\r\n\r\n\r\n");
}
if(times%200==0)printf("請輸入數(shù)據(jù),以回車鍵結(jié)束\r\n");
if(times%30==0)LED0=!LED0;//閃爍 LED,提示系統(tǒng)正在運行.
delay_ms(10);
}
}
}
分析
目標實現(xiàn):用PC上的串口助手和單片機串口1進行通訊样勃,雙方能夠接受發(fā)送數(shù)據(jù)。(單片機發(fā)送的數(shù)據(jù)位從PC處接受的數(shù)據(jù)性芬,故需要把接受到的數(shù)據(jù)存儲起來峡眶。發(fā)送停止標識為回車。)
步驟:
1植锉、串口初始化辫樱,配置串口通信的波特率、設置串口數(shù)據(jù)幀的格式開啟串口接受中斷俊庇、打開接收和發(fā)送單元狮暑。
2鸡挠、接收數(shù)據(jù)。接收到數(shù)據(jù)后搬男,開啟中斷服務函數(shù)拣展,把數(shù)據(jù)存儲起來,并判斷接收是否完成缔逛。(通過串口接收中斷實現(xiàn)备埃,需要開啟串口接收中斷)
3、發(fā)送數(shù)據(jù)褐奴。判斷前一個數(shù)據(jù)是否發(fā)送完成按脚,發(fā)送完成即可發(fā)送本次數(shù)據(jù)。
STM32Cube實例
1)printf函數(shù)重定向(printf內(nèi)部函數(shù)要調(diào)用fputc函數(shù)敦冬,故通過對庫函數(shù)fputc的重定義辅搬,即可實現(xiàn)。)
#include "stdio.h"
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
PUTCHAR_PROTOTYPE
{
HAL_UART_Transmit(&huart1, (uint8_t*)&ch, 1, 0xFFFF);
return ch;
}
2)使用中斷接收
1脖旱、開啟接收中斷(如HAL_UART_Receive_IT(&huart2, (uint8_t *)kRxBuffer, 10)堪遂,為開啟串口2的中斷,把接收到的數(shù)據(jù)放在kRxBuffer夯缺,當接收到10個數(shù)據(jù)后調(diào)用callback函數(shù)蚤氏。串口接收完數(shù)據(jù)后會關(guān)閉使能甘耿,故在回調(diào)函數(shù)中需要再寫一次該函數(shù)踊兜。)
2、在回調(diào)函數(shù)中實現(xiàn)收到數(shù)據(jù)之后的操作
3佳恬、發(fā)送中斷類似捏境。使用HAL_UART_Transmit_IT函數(shù)發(fā)送指定長度的數(shù)據(jù),并使能發(fā)送中斷毁葱,發(fā)送到一半和發(fā)送結(jié)束會觸發(fā)中斷(相關(guān)的回調(diào)函數(shù)是HAL_UART_TxHalfCpltCallback()HAL_UART_TxCpltCallback())中斷觸發(fā)后發(fā)送中斷使能會被清除垫言,然后調(diào)用回調(diào)函數(shù),回調(diào)函數(shù)執(zhí)行完成之后結(jié)束本次發(fā)送倾剿。
開啟接收中斷
HAL_UART_Receive_IT(&huart1,(uint8_t*)&aRxBuffer,1);
回調(diào)函數(shù)(部分代碼)
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart){
Uart1_RxBuff[Uart1_Rx_Cnt++] = aRxBuffer; //接收數(shù)據(jù)轉(zhuǎn)存
if((Uart1_RxBuff[Uart1_Rx_Cnt-1] == 0x0A)&&(Uart1_RxBuff[Uart1_Rx_Cnt-2] == 0x0D)) //判斷結(jié)束位
{
HAL_UART_Transmit(&huart1, (uint8_t *)&Uart1_RxBuff, Uart1_Rx_Cnt,0xFFFF); //將收到的信息發(fā)送出去
Uart1_Rx_Cnt = 0;
memset(Uart1_RxBuff,0x00,sizeof(Uart1_RxBuff)); //清空數(shù)組
}
}