簡介
串口USART(Universal Synchronous Asynchronous Receiver and Transmitter)也叫通用同步異步收發器,是單片機與外部進行信息交互的重要通信接口,屬於單片機的一種外設,幾乎所有單片機都支持使用串口通訊,同時也是單片機程序調試的一種重要手段,對於STM32,串口資源非常豐富,功能也比較齊全,以STM32F103ZET6爲例,就提供了5路的串口,我們一般用把串口用來在電腦的串口調試工具上打印調試信息,從而瞭解程序運行是否正確、如果出錯的話也能知道是哪裏出了錯誤。
通訊的有關概念
並行通訊
並行就是採用多條數據線進行通訊
優點是傳輸速度快,缺點是佔用的引腳資源多
串行通訊
數據按位順序傳輸,需要的信號線相比於並行通信來說少了很多,最簡單的只需要三根線:RXD,TXD,GND,顯然這種通訊方式的優點是佔用的引腳資源少,缺點是傳輸速率不高
單工
數據傳輸只支持數據在【一個方向上】傳輸
半雙工
允許數據在兩個方向上傳輸,但是,在某一時刻,只允許數據在一個方向上傳輸,它實際上是【一種切換方向】的【單工通信】
全雙工
允許數據【同時在兩個方向上傳輸】,因此,全雙工通信是【兩個單工通信方式的結合】,它要求發送設備和接收設備都有獨立的接收和發送能力
同步通訊
帶【時鐘同步信號】傳輸
如IIC通訊需要兩根線,一根是SDA數據線,一根是SCL時鐘線
異步通訊
【不帶】【時鐘同步】信號
也就是發出的信號可以不受時鐘線的約束
USART的寄存器
- 狀態寄存器(USART_SR)
- 數據寄存器(USART_DR)
- 波特比率寄存器(USART_BRR)
- 控制寄存器 1(USART_CR1)
- 控制寄存器 2(USART_CR2)
- 控制寄存器 3(USART_CR3)
- 保護時間和預分頻寄存器(USART_GTPR)
USART的功能框圖
初始化結構體中的各種參數
typedef struct {
uint32_t USART_BaudRate; // 波特率
uint16_t USART_WordLength; //數據字長
uint16_t USART_StopBits; // 停止位
uint16_t USART_Parity; // 奇偶校驗位選擇
uint16_t USART_Mode; // USART 模式
uint16_t USART_HardwareFlowControl; // 硬件流控制
} USART_InitTypeDef;
- 波特率
波特率指數據信號對載波的調製速率,它用單位時間內載波調製狀態改變次數來表示,
單位爲波特,指的是一個設備在一秒鐘內發送(或接收)了多少碼元的數據(碼元:又叫碼率,單位爲波特,一個脈衝信號就是一個碼元),這裏簡單理解爲傳輸數據的速率,波特率的計算有一個固定的公式
這裏的fck是USART的時鐘頻率,一般是72MHz,USARTDIV是一個與波特率寄存器(USART_BRR)有關的數,波特率寄存器有16個位,前4個位用於配置USARTDIV的小數部分,後12個位用於配置整數部分,我們可以根據所需要配置的波特率從而算出USARTDIV的值,進而配置波特率寄存器的值 - 數據字長
可以選擇8 位或 9 位,具體選擇多少位要看後面檢驗位,如果開啓了奇偶校驗,那麼就選9位,如果沒有的話就選8位,也就是一個bit - 停止位
串口在傳輸完數據的時候會有停止信號,這裏設置的就是停止信號的長度,可選 0.5 個、 1 個、 1.5 個和 2 個停止位,一般選擇1位 - 奇偶校驗位選擇
假設傳輸的數據位:1 1 1 0
那麼偶校驗位會根據數據位中的 1 的個數是否爲偶數來補位(補成偶數個1),如果前面1是三個 那麼此時偶校驗位爲 1 加起來一共有四個 1 是偶數,這時候偶校驗位的值就爲1
奇校驗位的原理則與之相反 - USART 模式
模式分爲:接收模式,發送模式,如果不設置默認不能接收和發送,一般在設置的時候設置成兩種模式都開,也就是收發模式 - 硬件流控制
很少用到,一般設置成無硬件數據流控制
初始化USART的流程
其中串口的接收端模式配置成浮空輸入,輸出端配置成複用推輓輸出,爲什麼這樣配置呢?具體怎麼配置可以在參考手冊的這裏找到
使用串口在串口調試助手上輸出HELLO WORLD
#include "stm32f10x.h"
void NVIC_Config(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
void USART_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
// 打開串口 GPIO USART1 的時鐘
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
//GPIOA_9 USART1 TX 配置爲推輓複用模式
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
//GPIA_10 USART1_RX 配置爲浮空輸入模式
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_Init(GPIOA,&GPIO_InitStructure);
//配置USART1的參數
USART_InitStructure.USART_BaudRate = 115200;// 配置波特率
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;// 配置硬件流控制
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;// 配置工作模式,收發一起
USART_InitStructure.USART_Parity = USART_Parity_No;//配置校驗位
USART_InitStructure.USART_StopBits = USART_StopBits_1;//配置停止位
USART_InitStructure.USART_WordLength = USART_WordLength_8b;//配置數據字長
USART_Init(USART1, &USART_InitStructure);// 完成串口的初始化配置
NVIC_Config();//配置NVIC
USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);//使能接收中斷
USART_Cmd(USART1, ENABLE);// 使能串口
}
//發送一個Byte
void Usart_SendByte( USART_TypeDef * USARTx, uint8_t ch)
{
USART_SendData(USARTx,ch); //發送一個字節數據到 USART
while (USART_GetFlagStatus(USARTx, USART_FLAG_TXE) == RESET);//等待發送數據寄存器爲空
}
//發送一個字符串
void USART_SendStr(USART_TypeDef *USARTx,uint8_t *str)
{
uint8_t i = 0;
do
{
Usart_SendByte(USART1,*(str+i));
i++;
}while( *(str+i) != '\0');
//等待發送完成
while (USART_GetFlagStatus(USARTx, USART_FLAG_TC) == RESET);
}
int main(void)
{
USART_Config();
USART_SendStr(USART1,"HELLO WORLD!");
while(1)
{
}
}
void USART1_IRQHandler(void)
{
uint8_t ucTemp;
if (USART_GetITStatus(USART1,USART_IT_RXNE) != RESET)
{
ucTemp |= USART_ReceiveData( USART1 );
}
USART_SendStr(USART1,"\n收到!");
}
在STM32中使用printf,putchar,scanf,getchar等函數
我們知道這幾個函數是在C語言頭文件stdio.h中的,由於我們使用的是運行於STM32的C語言,所以這幾個函數並不能使用,但是現在我們會使用串口,而串口可以在串口調試工具中輸出調試信息,出於習慣我們如果想使用這幾個函數必須重定向這幾個函數,具體步驟如下:
- 聲明頭文件
#include “stdio.h”
- 使用以下函數對這四個函數進行重定向
//發送數據
int fputc(int ch, FILE *f)
{
USART_SendData(USART1, (unsigned char)ch);// USART1 可以換成 USART2 等其他串口
while( !USART_GetFlagStatus(USART1,USART_FLAG_TXE) ); //等待數據被轉移到移位寄存器。
return (ch);
}
// 接收數據
int GetKey (void)
{
while( !USART_GetFlagStatus(USART1,USART_FLAG_RXNE) ); //等待讀數據寄存器接收到數據
return ((int)(USART1->DR & 0x1FF)); //數據寄存器有9位,這裏取出9位
}
- 在工程屬性的 “Target" > “Code Generation” 選項中勾選 “Use MicroLIB”
如何在打印出數據後換行
在字符串後面加上\r\n即可換行,即
printf("字符串\r\n");