STM32串口的使用(原理、结构体、库函数、串口发送字符(串)、重定向printf串口发送、串口中断接收控制灯)
參考:串口的結構體 重定向printf串口發送stm32等博文
作者:點燈小哥
發布時間: 2021-03-06 21:46:33
網址:https://blog.csdn.net/weixin_46016743/article/details/114458698
目錄
- 串口相關知識
- 定義
- 通信概念
- 1.通訊結構
- 2.電平標準
- 3.同步異步傳輸方式(USART與UART)
- 4.串口數據包組成
- 5.速率類型
- 6.通信類型(串行、并行)
- 7.通信方式(單工、半雙工、全雙工)
- 8.概念補充
- 串口的引腳
- 串口的結構體
- 串口的初始化函數(串口狀態標志位)
- 串口的發送配置
- 串口發送字符(STM32發送到上位機)
- 串口發送字符串
- 重定向printf串口發送
- 串口的中斷接收控制燈
- 方法:在usart.c上增加配置NVIC中斷控制器,中斷源為串口1
- 串口中斷不屬于EXTI外部中斷,所以不用配置EXTI結構體
- usart.c
- main.c -- USART1_IRQHandler(void)
串口相關知識
定義
串口通訊(Serial Communication)是一種設備間非常常用的串行通訊方式,因為它簡單便捷,因此大部分電子設備都支持該通訊方式,其通訊協議可分層為協議層和物理層。
- 物理層規定通信協議中具有機械、電子功能的特性,從而確保原始數據在物理媒體的傳播
- 協議層主要規定通訊邏輯,統一雙方的數據打包、解包標準。通俗的講物理層規定我們用嘴巴還是肢體交流,協議層規定我們用中文還是英文交流。
通信概念
1.通訊結構
串口通訊的物理層的主要標準是RS-232標準,其規定了信號的用途、通訊接口及信號的電平標準,其通訊結構如下:
在設備內部信號是以TTL電平標準傳輸的,設備之間是通過RS-232電平標準傳輸的,而且TTL電平需要經過電平轉換芯片才能轉化為RS-232電平,RS-232電平轉TTL電平也是如此。
2.電平標準
根據使用的電平標準不同,串口通訊可分為 RS-232標準及TTL標準,具體標準如下:
在電子電路中常使用TTL的電平標準,但其抗干擾能力較弱,為了增加串口的通訊距離及抗干擾能力,使用RS-232電平標準在設備之間傳輸信息,經常使用MAX232芯片對TTL電平及RS-232電平進行相互轉換。
3.同步異步傳輸方式(USART與UART)
- A同步:
傳輸以數據塊為核心,在一個數據塊內,字符間無間隔,接受發送同步,有sclk時鐘,雙方sclk(串行時鐘)連在一起,提供同步
特點:效率高,無間隔 - B異步:
以字符為傳輸單位,每發一個字符,都得發送一個起始位,(告訴對方我開始發了)結束發送停止位。(我發完了)
特點:效率低,間隔任意
-
USART(通用同步異步收發器)是一個串行通信設備,可以靈活地與外部設備進行全雙工數據交換。
-
UART,它是在 USART 基礎上裁剪掉了同步通信功能,只有異步通信。簡單區分同步和異步就是看通信時需不需要對外提供時鐘輸出,我們平時用的串口通信基本都是 UART。
USART 在 STM32 應用最多莫過于“打印”程序信息,一般在硬件設計時都會預留一USART 通信接口連接電腦,用于在調試程序是可以把一些調試信息“打印”在電腦端的串口調試助手工具上,從而了解程序運行是否正確、如果出錯哪具體哪里出錯等等。
4.串口數據包組成
起始位、數據位(8位或者9位)、奇偶校驗位(第9位)、起始停止位(1,15,2位)、波特率設置
校驗方式 :
奇偶校驗需要一位校驗位,即使用串口通信的方式2或方式3(8位數據位+1位校驗位)。
奇校驗(odd parity):讓傳輸的數據(包含校驗位)中1的個數為奇數。
即:如果傳輸字節中1的個數是偶數(不包含校驗位),則校驗位為“1”,奇數相反。
5.速率類型
比特(bit):每秒傳輸的二進制位
波特(byte):每秒傳輸的碼源個數(串口常用),一個二進制位表示一個碼源(0V——0;3.3V——1)
注:這倆本質上其實是一樣的
6.通信類型(串行、并行)
- 串行:一個一個傳輸 如:fsmc
- 特點:占用資源多,速度慢,看干擾強
- 并行:多個一起傳輸 如:spi usart
- 特點:占用資源少,速度快。抗干擾能力弱,距離近
7.通信方式(單工、半雙工、全雙工)
-
單工:數據傳輸只支持數據在一個方向上傳輸;如:打印機
-
半雙工:允許數據在兩個方向上傳輸。但是,在某一時刻,只允許數據在一個方向上傳輸,它實際上是一種切換方向的單工通信;它不需要獨立的接收端和發送端,兩者可以合并一起使用一個端口。如:對講機,spi
-
全雙工:允許數據同時在兩個方向上傳輸。因此,全雙工通信是兩個單工通信方式的結合,需要獨立的接收端和發送端。如:spi,usart
8.概念補充
-
1.數據包
串口通訊的數據包由發送設備通過自身的TXD接口傳輸到接收設備得RXD接口,在協議層中規定了數據包的內容,具體包括起始位、主體數據(8位或9位)、校驗位以及停止位,通訊的雙方必須將數據包的格式約定一致才能正常收發數據。 -
2.波特率
由于異步通信中沒有時鐘信號,所以接收雙方要約定好波特率,即每秒傳輸的碼元個數,以便對信號進行解碼,常見的波特率有4800、9600、115200等。STM32中波特率的設置通過串口初始化結構體來實現。 -
3.起始和停止信號
數據包的首尾分別是起始位和停止位,數據包的起始信號由一個邏輯0的數據位表示,停止位信號可由0.5、1、1.5、2個邏輯1的數據位表示,雙方需約定一致。STM32中起始和停止信號的設置也是通過串口初始化結構體來實現。 -
4.有效數據
有效數據規定了主題數據的長度,一般為8或9位,其在STM32中也是通過串口初始化結構體來實現的。 -
5.數據校驗
在有效數據之后,有一個可選的數據校驗位。由于數據通信相對更容易受到外部干擾導致傳輸數據出現偏差,可以在傳輸過程加上校驗位來解決這個問題。校驗方法有奇校驗(odd)、偶校驗(even)、 0 校驗(space)、 1 校驗(mark)以及無(noparity)。這些也都可以在串口初始化結構體中實現的。
串口的引腳
下面是最小板原理圖,UART4和UART5并沒有引出來。
串口的結構體
串口的初始化函數(串口狀態標志位)
串口標志位,用于說明串口發送接收狀態,是否都發/收完了
串口的發送配置
串口發送字符(STM32發送到上位機)
新建一個usart文件夾,里面新建usart.c和usart.h兩個文件,將usart.c添加到工程里面(user一欄下),編譯一下usart.h會自動添加進工程。
usart.h
usart.c
#include "stm32f10x.h" // Device header #include "usart.h"void Usart_Init(void) {//2. 配置GPIO的結構體GPIO_InitTypeDef GpioInitStructure; //初始化GPIO結構體命名//3. 配置USART的結構體(另外一個結構體是帶時鐘的串口)USART_InitTypeDef UsartInitStructure;//初始化USART結構體命名//1. 時鐘使能: GPIOA的時鐘,引腳復用(成串口)的時鐘,串口的時鐘RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);//開啟APB2總線復用時鐘RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);//2.1 配置PA9 TX(輸出)GpioInitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//復用推挽輸出(因為復用成串口)GpioInitStructure.GPIO_Pin = GPIO_Pin_9;GpioInitStructure.GPIO_Speed = GPIO_Speed_50MHz;//任意選擇,影響不大GPIO_Init(GPIOA,&GpioInitStructure);//2.2 配置PA10 RX(接收)GpioInitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空輸入GpioInitStructure.GPIO_Pin = GPIO_Pin_10;GPIO_Init(GPIOA,&GpioInitStructure); //3.配置串口結構體 這是不帶時鐘的結構體 還有一個是帶時鐘的結構體UsartInitStructure.USART_BaudRate = 115200; //波特率UsartInitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; //硬件流(沒有使用)UsartInitStructure.USART_Mode = USART_Mode_Rx| USART_Mode_Tx;//模式(輸入輸出都選擇)UsartInitStructure.USART_Parity = USART_Parity_No; //校驗位(不用)UsartInitStructure.USART_StopBits = USART_StopBits_1; //停止位(1位)UsartInitStructure.USART_WordLength = USART_WordLength_8b;//有效字節長度(8位)//串口1USART_Init(USART1, &UsartInitStructure);USART_Cmd(USART1, ENABLE);//打開串口 比配置GPIO多這一步 }main.c
#include "stm32f10x.h" #include "usart.h"//頭文件是單獨創建的文件 main函數找不到 要去手工添加路徑(點擊魔術棒...)void delay(uint16_t time) {uint16_t i = 0;while(time--){i=12000;while(i--);} }int main(void) {Usart_Init(); while(1){ USART_SendData(USART1, 'O'); //發送一個字符 //數據寄存器空標志位 RESET狀態說明上面字符發送成功了!while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET); //USART_GetFlagStatus是判斷標志位 USART_SendData(USART1, 'K'); while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET); USART_SendData(USART1, '\n');while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET); //右鍵USART_GetFlagStatus去goto找到RESETdelay(1000); } }串口發送字符串
上一節串口只能一個一個發送字符,可以自己寫一個串口發送字符串函數,然后main函數調用即可。
usart.c
//發送字符函數 (自己定義的 注意形參要和固件庫里面串口發送字符的形參要一樣) //調用這個函數不用每次都去判斷標志位 void USARTSendByte(USART_TypeDef* USARTx, uint16_t Data)//main函數調用這個函數的方法:USARTSendByte(USART1, 'A'); {USART_SendData( USARTx, Data);//固件庫本身的串口發送函數//判斷數據寄存器是否為空 字符標志位while( USART_GetFlagStatus( USARTx, USART_FLAG_TXE) == RESET);//USART_GetFlagStatus是判斷標志位 USART_FLAG_TXE 去usart.h FLAG找 }//發送字符串函數(自己定義的) void USARTSendString( USART_TypeDef* USARTx, char *str) {uint16_t i = 0;do{USARTSendByte(USART1,*(str+i));i++;}while(*(str+i) != '\0');//字符串結束標識//字符串標志位 上面是字符標志位while( USART_GetFlagStatus( USARTx, USART_FLAG_TC) == RESET);//USART_GetFlagStatus是判斷標志位 USART_FLAG_TC(這是判斷字符串) 去usart.h FLAG找//main函數里這樣調用USARTSendByte( USART1, 'O');USARTSendByte( USART1, 'K');USARTSendString( USART1, "你好STM32"); }重定向printf串口發送
在C語言標準庫中,printf()擁有十分強大的輸出能力,可以輸出各種類型的數據,整型、浮點型、8進制、16進制、換行符,縮進符等等。
printf()是把數據輸出到屏幕,但是ARM芯片中沒有屏幕,我們設想將printf()打印到串口,這樣我們就可以通過printf()和串口實時的觀察ARM芯片內部的工作情況,運行結果。
先來認識一個關鍵字:__weak
weak的字面意思就是“微弱”的意思,其主要作用就是可以重新定義重名函數或變量而編譯時不報錯。筆者最開始注意到這個關鍵字是在使用 STM32 HAL 庫的時候注意到的,比如這張圖片所示:
在上圖我們可以看到左邊的 HAL_MspInit 函數前面用 __weak 進行修飾,而圖片右邊又定義了 HAL_MspInit函數,這時整個工程就定義了兩個 HAL_MspInit 函數,聲明可以有多個,但是定義只能存在一個,因為 __weak的存在,所以不會報錯,并且真正起作用的函數是沒有用 __weak 修飾的函數。
printf函數其實就是調用了fputc,我們來重寫fputc達到重定向printf的目的。
實現過程:在usart.c 的后面增加兩個函數
usart.c
int fputc(int ch, FILE *f)//(串口發送)重寫fputc,供printf調用 { USARTSendByte( USART1, (uint8_t)ch);while( USART_GetFlagStatus( USART1, USART_FLAG_TXE) == RESET);return (ch); }int fgetc(FILE *f)//(串口接收)重寫fgetc { //標志位選擇接收while( USART_GetFlagStatus( USART1, USART_FLAG_RXNE) == RESET);return (int) USART_ReceiveData(USART1); }usart.h
#include "stm32f10x.h" #include <stdio.h>//標準輸入輸出printfvoid Usart_Init(void); void USARTSendByte(USART_TypeDef* USARTx, uint16_t Data); void USARTSendString( USART_TypeDef* USARTx, char *str); int fputc(int ch, FILE *f);注意:在使用printf等C語言標準庫函數要包含頭文件stdio.h,并且勾選Target中的use MicroLIB使用標準庫。
main.c
#include "stm32f10x.h" // Device header #include "usart.h"void delay(uint16_t time) {uint16_t i = 0;while(time--){i=12000;while(i--);} }int main(void) { Usart_Init(); printf("你好STM32");//printf調用重寫的fputc 這樣單片機就可以通過串口發送字符串"你好STM32" putchar('X'); //putchar調用重寫的fputc 這樣單片機就可以通過串口發送字符'X' while(1){} }串口的中斷接收控制燈
參考:串口 stm32 實現中斷接收 打開板子上的led燈
作者:點燈小哥
發布時間: 2021-03-07 11:55:35
網址:https://blog.csdn.net/weixin_46016743/article/details/114481125
注:板子上的LED燈看電路圖連接的是PC13引腳,前面博文led.c文件里配置好了。
方法:在usart.c上增加配置NVIC中斷控制器,中斷源為串口1
串口中斷不屬于EXTI外部中斷,所以不用配置EXTI結構體
NVIC_InitTypeDef Nvic_init; //misc.hNVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //配置NVIC中斷組//3.配置NVIC中斷控制器 中斷源(通道)選擇串口1Nvic_init.NVIC_IRQChannel = USART1_IRQn; Nvic_init.NVIC_IRQChannelCmd = ENABLE; //使能 找到FunctionalState字眼 右鍵gotoNvic_init.NVIC_IRQChannelPreemptionPriority = 1; //因為只配置了一個中斷 不考慮優先級 所以只有1個Nvic_init.NVIC_IRQChannelSubPriority = 1;//搶占優先級與子優先級NVIC_Init(&NvicInitStructure);usart.c
#include "stm32f10x.h" // Device header #include "usart.h" //#include "stdio.h"void Usart_Init(void) {//2. 配置GPIO的結構體GPIO_InitTypeDef GpioInitStructure; //初始化GPIO結構體命名USART_InitTypeDef UsartInitStructure;//初始化USART結構體命名 NVIC_InitTypeDef NvicInitStructure; //加入NVICNVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//配置NVIC中斷分組 目前一個中斷隨便配置就好了 //1. 配置時鐘:GPIO的時鐘,引腳復用的時鐘,串口的時鐘RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);//2.1 配置PA9 TXGpioInitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//復用推挽輸出GpioInitStructure.GPIO_Pin = GPIO_Pin_9;GpioInitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA,&GpioInitStructure);//2.2 配置PA10 RXGpioInitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空輸入GpioInitStructure.GPIO_Pin = GPIO_Pin_10;GPIO_Init(GPIOA,&GpioInitStructure);//3.配置串口結構體UsartInitStructure.USART_BaudRate = 115200; //波特率UsartInitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; //硬件流UsartInitStructure.USART_Mode = USART_Mode_Rx| USART_Mode_Tx; //模式UsartInitStructure.USART_Parity = USART_Parity_No; //校驗位UsartInitStructure.USART_StopBits = USART_StopBits_1; //停止位UsartInitStructure.USART_WordLength = USART_WordLength_8b; //字節長度USART_Init(USART1, &UsartInitStructure); //串口中斷配置函數 //接收數據寄存器非空標志位 作為串口發生中斷的標志USART_ITConfig( USART1, USART_IT_RXNE, ENABLE ); USART_Cmd(USART1, ENABLE);//打開串口 比配置GPIO多這一步//配置NVIC中斷控制器 中斷源(通道)選擇串口1NvicInitStructure.NVIC_IRQChannel = USART1_IRQn;NvicInitStructure.NVIC_IRQChannelPreemptionPriority = 1;//搶占優先級NvicInitStructure.NVIC_IRQChannelSubPriority = 1;//子優先級NvicInitStructure.NVIC_IRQChannelCmd = ENABLE;NVIC_Init(&NvicInitStructure); }//發送字符 void USARTSendByte(USART_TypeDef* USARTx, uint16_t Data) {USART_SendData( USARTx, Data);while( USART_GetFlagStatus( USARTx, USART_FLAG_TXE) == RESET);//USART_GetFlagStatus是判斷標志位 USART_FLAG_TXE 去usart.h FLAG找 } //發送字符串 void USARTSendString( USART_TypeDef* USARTx, char *str) {uint16_t i = 0;do{USARTSendByte(USART1,*(str+i));i++;}while(*(str+i) != '\0');while( USART_GetFlagStatus( USARTx, USART_FLAG_TC) == RESET);//USART_GetFlagStatus是判斷標志位 USART_FLAG_TC(這是判斷字符串) 去usart.h FLAG找 }main.c – USART1_IRQHandler(void)
#include "stm32f10x.h" // Device header #include "usart.h" #include "led.h" #include "exti.h"void delay(uint16_t time) {uint16_t i = 0;while(time--){i=12000;while(i--);} }int main(void) { Usart_Init();LED_Init();GPIO_SetBits( GPIOC, GPIO_Pin_13);//初始化C13電平為高電平 燈不亮while(1){} }//4. 中斷服務函數(在啟動頭文件里 有weak標志 屬于重定向函數) void USART1_IRQHandler(void) {char temp;//獲得串口中斷標志位 接收數據寄存器非空標志位if( USART_GetITStatus( USART1, USART_IT_RXNE) != RESET) //發生了中斷 開始接收數據{ temp = USART_ReceiveData( USART1);if(temp == 'O'){GPIO_ResetBits( GPIOC, GPIO_Pin_13);//串口助手發送過來字符'O' 開燈USARTSendString( USART1, "LED IS OK");//調用自己寫的串口發送字符串函數 }if(temp == 'C'){GPIO_SetBits( GPIOC, GPIO_Pin_13);//關燈USARTSendString( USART1, "LED IS DOWN"); } } }總結
以上是生活随笔為你收集整理的STM32串口的使用(原理、结构体、库函数、串口发送字符(串)、重定向printf串口发送、串口中断接收控制灯)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: a标签的href与onclick中使用j
- 下一篇: matlab中的取整函数(ceil、fl