企業(yè)網(wǎng)站哪里可以做廣告外鏈購(gòu)買交易平臺(tái)
? ? ? ?上一節(jié)我們學(xué)習(xí)了串口通信的基本理論,串口通信是學(xué)習(xí)單片機(jī)的一個(gè)重要的一步,非常重要,這一節(jié)我們通過(guò)實(shí)驗(yàn)來(lái)學(xué)習(xí)串口通信的使用,以及串口的接收中斷的使用。
一、發(fā)送單個(gè)字節(jié)uint8_t數(shù)據(jù)或者字符型數(shù)據(jù)
實(shí)現(xiàn)的功能:
? ? ? ?STM32F4 通過(guò)串口和上位機(jī)通信,發(fā)送單個(gè)字節(jié)數(shù)據(jù)(0-255)或者字符給上位機(jī),然后顯示在電腦串口助手上。
my_usart.h文件內(nèi)容
#ifndef __MY_USART1_H__
#define __MY_USART1_H__
#include <stdint.h>void My_UsartInit(void); //串口初始化
void Usart_SendByte(uint8_t date); //發(fā)送一字節(jié)函數(shù)#endif
my_usart.c文件內(nèi)容
#include "stm32f4xx.h"
#include "myusart.h"
#include<stdio.h> //對(duì)printf()函數(shù)進(jìn)行重定向引入頭文件/******************************第一步:串口時(shí)鐘使能,GPIO 時(shí)鐘使能。第二步:設(shè)置引腳復(fù)用器映射:調(diào)用 GPIO_PinAFConfig 函數(shù)第三步:GPIO 初始化設(shè)置:要設(shè)置模式為復(fù)用功能第四步:串口初始化:設(shè)置波特率,字長(zhǎng),奇偶校驗(yàn)等參數(shù)。第五步:使能串口*/void My_UsartInit(void)
{//1.第一步:串口時(shí)鐘使能,GPIO 時(shí)鐘使能RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);//2.第二步:設(shè)置引腳復(fù)用器映射:調(diào)用 GPIO_PinAFConfig 函數(shù)GPIO_PinAFConfig(GPIOA,GPIO_PinSource9,GPIO_AF_USART1);GPIO_PinAFConfig(GPIOA,GPIO_PinSource10,GPIO_AF_USART1);//3.第三步:GPIO 初始化設(shè)置:要設(shè)置模式為復(fù)用功能GPIO_InitTypeDef Struct1;Struct1.GPIO_Mode=GPIO_Mode_AF;Struct1.GPIO_Pin=GPIO_Pin_9 |GPIO_Pin_10;Struct1.GPIO_Speed=GPIO_Speed_100MHz;GPIO_Init(GPIOA,&Struct1);//4.第四步:串口初始化:設(shè)置波特率,字長(zhǎng),奇偶校驗(yàn)等參數(shù)。USART_InitTypeDef Struct2;Struct2.USART_BaudRate=115200; //設(shè)置波特率Struct2.USART_WordLength=USART_WordLength_8b; //數(shù)據(jù)位8位Struct2.USART_Parity=USART_Parity_No; //無(wú)校驗(yàn)位Struct2.USART_StopBits=USART_StopBits_1; //1位停止位Struct2.USART_HardwareFlowControl=USART_HardwareFlowControl_None; //不需要硬件流控Struct2.USART_Mode= USART_Mode_Rx |USART_Mode_Tx; //設(shè)置usart1既可以接收也可以發(fā)送USART_Init(USART1,&Struct2);//5.第五步:使能串口USART_Cmd(USART1,ENABLE);}//發(fā)送一字節(jié)(一個(gè)字符)函數(shù)
void Usart_SendByte(uint8_t data)
{USART_SendData(USART1,data); //這是一個(gè)庫(kù)函數(shù),用于將一個(gè)字節(jié)的數(shù)據(jù)寫入U(xiǎn)SART的數(shù)據(jù)寄存器 while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET); //等待發(fā)送完畢}//對(duì)printf()函數(shù)進(jìn)行重定向
int fputc(int ch, FILE *f)
{USART_SendData(USART1,ch); //通過(guò)串口發(fā)送數(shù)據(jù),每次發(fā)送一個(gè)字符while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET); //等待發(fā)送完畢return ch;
}
?main.c代碼
#include <stdio.h>
#include "myusart.h"int main(void)
{My_UsartInit();printf("發(fā)送一個(gè)字節(jié):\n");Usart_SendByte('a');printf("\n");Usart_SendByte(97);printf("\n");while(1){}}
關(guān)鍵部分解讀:
? ? ?發(fā)送過(guò)程:調(diào)用串口發(fā)送函數(shù)USART_SendData(),但是需要保證在發(fā)送下一個(gè)字節(jié)之前,必須確保當(dāng)前字節(jié)已被成功發(fā)送。否則可能會(huì)導(dǎo)致新數(shù)據(jù)寫入時(shí)覆蓋未發(fā)送完的數(shù)據(jù),造成通信錯(cuò)誤。因此,內(nèi)部使用死循環(huán)來(lái)控制,調(diào)用庫(kù)函數(shù)USART_GetFlagStatus()檢查標(biāo)志位來(lái)判斷發(fā)送的狀態(tài)!
- 這個(gè)
while
循環(huán)的作用是等待USART的數(shù)據(jù)寄存器空標(biāo)志(TXE)被置位。當(dāng)發(fā)送數(shù)據(jù)寄存器(TDR)中有數(shù)據(jù)時(shí),TXE標(biāo)志為RESET
。只有當(dāng)TDR中的數(shù)據(jù)已被移到移位寄存器中,TXE標(biāo)志才會(huì)被置位。當(dāng)TXE標(biāo)志為SET
時(shí),表示發(fā)送數(shù)據(jù)寄存器已空,可以發(fā)送下一個(gè)字節(jié)- 具體來(lái)說(shuō),發(fā)送數(shù)據(jù)過(guò)程包括:
- 將數(shù)據(jù)寫入TDR。
- 數(shù)據(jù)從TDR移到移位寄存器。
- 當(dāng)移位寄存器開(kāi)始發(fā)送數(shù)據(jù)時(shí),TDR變空,TXE標(biāo)志被置位。
while
循環(huán)確保在TDR變空之前不會(huì)發(fā)送新的數(shù)據(jù),從而避免數(shù)據(jù)丟失或覆蓋。
?
二、發(fā)送一個(gè)16位的數(shù)據(jù)uint16_t
實(shí)現(xiàn)的功能:
? ? ? ?STM32F4 通過(guò)串口和上位機(jī)通信,發(fā)送兩個(gè)字節(jié)數(shù)據(jù)(16位)給上位機(jī),然后顯示在電腦串口助手上。我們知道串口通信一次只能發(fā)送8位的數(shù)據(jù),那么如何實(shí)現(xiàn)一次發(fā)16位呢?
my_usart.h文件內(nèi)容
#ifndef __MY_USART1_H__
#define __MY_USART1_H__
#include <stdint.h>void My_UsartInit(void); //串口初始化
void USART_SendHalfWord(uint16_t data); //發(fā)送兩個(gè)字節(jié)函數(shù)#endif
my_usart.c
#include "stm32f4xx.h"
#include "myusart.h"
#include <stdio.h> //對(duì)printf()函數(shù)進(jìn)行重定向引入頭文件/******************************第一步:串口時(shí)鐘使能,GPIO 時(shí)鐘使能。第二步:設(shè)置引腳復(fù)用器映射:調(diào)用 GPIO_PinAFConfig 函數(shù)第三步:GPIO 初始化設(shè)置:要設(shè)置模式為復(fù)用功能第四步:串口初始化:設(shè)置波特率,字長(zhǎng),奇偶校驗(yàn)等參數(shù)。第五步:使能串口*/void My_UsartInit(void)
{//1.第一步:串口時(shí)鐘使能,GPIO 時(shí)鐘使能RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);//2.第二步:設(shè)置引腳復(fù)用器映射:調(diào)用 GPIO_PinAFConfig 函數(shù)GPIO_PinAFConfig(GPIOA,GPIO_PinSource9,GPIO_AF_USART1);GPIO_PinAFConfig(GPIOA,GPIO_PinSource10,GPIO_AF_USART1);//3.第三步:GPIO 初始化設(shè)置:要設(shè)置模式為復(fù)用功能GPIO_InitTypeDef Struct1;Struct1.GPIO_Mode=GPIO_Mode_AF;Struct1.GPIO_Pin=GPIO_Pin_9 |GPIO_Pin_10;Struct1.GPIO_Speed=GPIO_Speed_100MHz;GPIO_Init(GPIOA,&Struct1);//4.第四步:串口初始化:設(shè)置波特率,字長(zhǎng),奇偶校驗(yàn)等參數(shù)。USART_InitTypeDef Struct2;Struct2.USART_BaudRate=115200; //設(shè)置波特率Struct2.USART_WordLength=USART_WordLength_8b; //數(shù)據(jù)位8位Struct2.USART_Parity=USART_Parity_No; //無(wú)校驗(yàn)位Struct2.USART_StopBits=USART_StopBits_1; //1位停止位Struct2.USART_HardwareFlowControl=USART_HardwareFlowControl_None; //不需要硬件流控Struct2.USART_Mode= USART_Mode_Rx |USART_Mode_Tx; //設(shè)置usart1既可以接收也可以發(fā)送USART_Init(USART1,&Struct2);//5.第五步:使能串口USART_Cmd(USART1,ENABLE);}//發(fā)送兩個(gè)字節(jié)(16位)數(shù)據(jù)函數(shù)
void USART_SendHalfWord(uint16_t data)
{// 分離高8位和低8位uint8_t tmp_h = data >>0x08;//將date右移8位,取得高8位數(shù)據(jù)并賦值給tmp_h。右移8位相當(dāng)于將高8位移到低8位的位置,高8位的原位置被0填充。uint8_t tmp_l = data & 0xFF;//將date和0xff(255,二進(jìn)制為11111111)高八位為0,進(jìn)行按位與運(yùn)算,取得低8位數(shù)據(jù)并賦值給tmp_l。按位與運(yùn)算將高8位清零,僅保留低8位。// 發(fā)送高8位USART_SendData(USART1, tmp_h); while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);// 發(fā)送低8位USART_SendData(USART1, tmp_l);while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);}//對(duì)printf()函數(shù)進(jìn)行重定向
int fputc(int ch, FILE *f)
{USART_SendData(USART1,ch); //通過(guò)串口發(fā)送數(shù)據(jù),每次發(fā)送一個(gè)字符while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET); //等待發(fā)送完畢return ch;
}
main.c
#include <stdio.h>
#include "myusart.h"int main(void)
{My_UsartInit();//發(fā)送兩個(gè)字節(jié)的數(shù)據(jù)USART_SendHalfWord(0xffee);while(1){}}
關(guān)鍵部分解讀:
? ? ? ?串行通信接口通常一次只能處理8位數(shù)據(jù),那么,我們就可以通過(guò)位運(yùn)算拿到數(shù)據(jù)的高八位和低八位分別發(fā)送,
Usart_SendHalfWord
函數(shù)將16位數(shù)據(jù)分成兩個(gè)8位數(shù)據(jù)(高8位和低8位),然后分別通過(guò)Usart_SendByte
函數(shù)發(fā)送出去。這種方式在串行通信中很常見(jiàn)。使用示例
假設(shè)我們有一個(gè)要發(fā)送的16位數(shù)據(jù)
0x1234
:
tmp_h = 0x12
:0x1234
右移8位得到高8位0x12
。tmp_l = 0x34
:0x1234
和0xff
按位與得到低8位0x34
。- 調(diào)用
Usart_SendByte(pUSARTx, 0x12)
發(fā)送高8位。- 調(diào)用
Usart_SendByte(pUSARTx, 0x34)
發(fā)送低8位。這確保16位數(shù)據(jù)能夠通過(guò)支持8位傳輸?shù)腢SART接口完整發(fā)送。
三、發(fā)送8位的數(shù)組(uint8_t)
實(shí)現(xiàn)的功能:
? ? ? ?STM32F4 通過(guò)串口和上位機(jī)通信,發(fā)送一個(gè)數(shù)組(每個(gè)元素都是uint8_t類型)或者字符數(shù)組給上位機(jī),然后顯示在電腦串口助手上。
my_usart.h
#ifndef __MY_USART1_H__
#define __MY_USART1_H__
#include <stdint.h>void My_UsartInit(void); //串口初始化
void Usart_SendByte(uint8_t date); //發(fā)送一字節(jié)函數(shù)
void USART1_SendArray(uint8_t *array, uint16_t length);//發(fā)送一個(gè)數(shù)組
#endif
my_usart.c
#include "stm32f4xx.h"
#include "myusart.h"
#include<stdio.h> //對(duì)printf()函數(shù)進(jìn)行重定向引入頭文件/******************************第一步:串口時(shí)鐘使能,GPIO 時(shí)鐘使能。第二步:設(shè)置引腳復(fù)用器映射:調(diào)用 GPIO_PinAFConfig 函數(shù)第三步:GPIO 初始化設(shè)置:要設(shè)置模式為復(fù)用功能第四步:串口初始化:設(shè)置波特率,字長(zhǎng),奇偶校驗(yàn)等參數(shù)。第五步:使能串口*/void My_UsartInit(void)
{//1.第一步:串口時(shí)鐘使能,GPIO 時(shí)鐘使能RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);//2.第二步:設(shè)置引腳復(fù)用器映射:調(diào)用 GPIO_PinAFConfig 函數(shù)GPIO_PinAFConfig(GPIOA,GPIO_PinSource9,GPIO_AF_USART1);GPIO_PinAFConfig(GPIOA,GPIO_PinSource10,GPIO_AF_USART1);//3.第三步:GPIO 初始化設(shè)置:要設(shè)置模式為復(fù)用功能GPIO_InitTypeDef Struct1;Struct1.GPIO_Mode=GPIO_Mode_AF;Struct1.GPIO_Pin=GPIO_Pin_9 |GPIO_Pin_10;Struct1.GPIO_Speed=GPIO_Speed_100MHz;GPIO_Init(GPIOA,&Struct1);//4.第四步:串口初始化:設(shè)置波特率,字長(zhǎng),奇偶校驗(yàn)等參數(shù)。USART_InitTypeDef Struct2;Struct2.USART_BaudRate=115200; //設(shè)置波特率Struct2.USART_WordLength=USART_WordLength_8b; //數(shù)據(jù)位8位Struct2.USART_Parity=USART_Parity_No; //無(wú)校驗(yàn)位Struct2.USART_StopBits=USART_StopBits_1; //1位停止位Struct2.USART_HardwareFlowControl=USART_HardwareFlowControl_None; //不需要硬件流控Struct2.USART_Mode= USART_Mode_Rx |USART_Mode_Tx; //設(shè)置usart1既可以接收也可以發(fā)送USART_Init(USART1,&Struct2);//5.第五步:使能串口USART_Cmd(USART1,ENABLE);}//發(fā)送一字節(jié)函數(shù)
void Usart_SendByte(uint8_t data)
{USART_SendData(USART1,data); while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET); //等待發(fā)送完畢}//發(fā)送一個(gè)數(shù)組
void USART1_SendArray(uint8_t *array, uint16_t len)
{for (uint16_t i = 0; i < len; i++){Usart_SendByte(array[i]);}
}//對(duì)printf()函數(shù)進(jìn)行重定向
int fputc(int ch, FILE *f)
{USART_SendData(USART1,ch); //通過(guò)串口發(fā)送數(shù)據(jù),每次發(fā)送一個(gè)字符while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET); //等待發(fā)送完畢return ch;
}
main.c
#include <stdio.h>
#include "myusart.h"int main(void)
{My_UsartInit();// 要發(fā)送的字節(jié)數(shù)組uint8_t dataArray1[] = {1,2,3,4,5,6,7,8,9,10};int len =sizeof(dataArray1) / sizeof(dataArray1[0]);// 發(fā)送一個(gè)數(shù)組 USART1_SendArray(dataArray1, len);while (1){}}
四、發(fā)送字符串?dāng)?shù)據(jù)
實(shí)現(xiàn)的功能:
? ? ? ?STM32F4 通過(guò)串口和上位機(jī)通信,發(fā)送字符串給電腦,然后顯示在電腦串口助手上。
my_usart.h文件內(nèi)容
#ifndef __MY_USART1_H__
#define __MY_USART1_H__
#include <stdint.h>void My_UsartInit(void); //串口初始化
void USART1_SendString(char *str); //發(fā)送一個(gè)字符串
#endif
my_usart.c文件內(nèi)容
#include "stm32f4xx.h"
#include "myusart.h"
#include<stdio.h> //對(duì)printf()函數(shù)進(jìn)行重定向引入頭文件/******************************第一步:串口時(shí)鐘使能,GPIO 時(shí)鐘使能。第二步:設(shè)置引腳復(fù)用器映射:調(diào)用 GPIO_PinAFConfig 函數(shù)第三步:GPIO 初始化設(shè)置:要設(shè)置模式為復(fù)用功能第四步:串口初始化:設(shè)置波特率,字長(zhǎng),奇偶校驗(yàn)等參數(shù)。第五步:使能串口*/void My_UsartInit(void)
{//1.第一步:串口時(shí)鐘使能,GPIO 時(shí)鐘使能RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);//2.第二步:設(shè)置引腳復(fù)用器映射:調(diào)用 GPIO_PinAFConfig 函數(shù)GPIO_PinAFConfig(GPIOA,GPIO_PinSource9,GPIO_AF_USART1);GPIO_PinAFConfig(GPIOA,GPIO_PinSource10,GPIO_AF_USART1);//3.第三步:GPIO 初始化設(shè)置:要設(shè)置模式為復(fù)用功能GPIO_InitTypeDef Struct1;Struct1.GPIO_Mode=GPIO_Mode_AF;Struct1.GPIO_Pin=GPIO_Pin_9 |GPIO_Pin_10;Struct1.GPIO_Speed=GPIO_Speed_100MHz;GPIO_Init(GPIOA,&Struct1);//4.第四步:串口初始化:設(shè)置波特率,字長(zhǎng),奇偶校驗(yàn)等參數(shù)。USART_InitTypeDef Struct2;Struct2.USART_BaudRate=115200; //設(shè)置波特率Struct2.USART_WordLength=USART_WordLength_8b; //數(shù)據(jù)位8位Struct2.USART_Parity=USART_Parity_No; //無(wú)校驗(yàn)位Struct2.USART_StopBits=USART_StopBits_1; //1位停止位Struct2.USART_HardwareFlowControl=USART_HardwareFlowControl_None; //不需要硬件流控Struct2.USART_Mode= USART_Mode_Rx |USART_Mode_Tx; //設(shè)置usart1既可以接收也可以發(fā)送USART_Init(USART1,&Struct2);//5.第五步:使能串口USART_Cmd(USART1,ENABLE);}//發(fā)送一個(gè)字符串
void USART1_SendString(char *str)
{while (*str!='\0'){USART_SendData(USART1,*str);while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET); //等待發(fā)送完畢str++;}
}//對(duì)printf()函數(shù)進(jìn)行重定向
int fputc(int ch, FILE *f)
{USART_SendData(USART1,ch); //通過(guò)串口發(fā)送數(shù)據(jù),每次發(fā)送一個(gè)字符while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET); //等待發(fā)送完畢return ch;
}
main.c內(nèi)容:
#include <stdio.h>
#include "myusart.h"int main(void)
{My_UsartInit();// 要發(fā)送的字符串char *dataString = "Hello World!";// 發(fā)送字符串printf("發(fā)送一個(gè)字符串:\n");USART1_SendString(dataString);printf("\n");while (1){ }}
五、單片機(jī)接收電腦發(fā)送的數(shù)據(jù)顯示在串口助手上(接收中斷)
? ? ? ?使用串口接收中斷可以讓CPU在沒(méi)有數(shù)據(jù)到達(dá)時(shí)執(zhí)行其他任務(wù),而不需要浪費(fèi)時(shí)間輪詢接收寄存器。這提高了CPU利用率和系統(tǒng)效率,檢查標(biāo)志位判斷是否發(fā)生接收中斷(USART_IT_RXNE
),如果是,讀取接收到的數(shù)據(jù),然后通過(guò)清除接收中斷標(biāo)志以準(zhǔn)備接收下一個(gè)字節(jié)。
實(shí)現(xiàn)的功能:
? ? ? 將接收數(shù)據(jù)設(shè)置為接收中斷,當(dāng)上位機(jī)發(fā)送數(shù)據(jù)到STM32F407的USART1時(shí),STM32會(huì)接收這個(gè)數(shù)據(jù)并通過(guò)中斷處理函數(shù)將數(shù)據(jù)發(fā)送回上位機(jī)。上位機(jī)的串口助手會(huì)顯示發(fā)送和接收的數(shù)據(jù)。
my_usart.h
#ifndef __MY_USART1_H__
#define __MY_USART1_H__
#include <stdint.h>void USART1_Config(void); //串口配置
void USART1_SendByte(uint8_t data); //發(fā)送一個(gè)字節(jié)函數(shù)
void USART1_IRQHandler(void); //中斷服務(wù)函數(shù)
#endif
my_usart.c
#include "stm32f4xx.h"
#include "myusart.h"
#include<stdio.h> //對(duì)printf()函數(shù)進(jìn)行重定向引入頭文件/******************************第一步:串口時(shí)鐘使能,GPIO 時(shí)鐘使能。第二步:設(shè)置引腳復(fù)用器映射:調(diào)用 GPIO_PinAFConfig 函數(shù)。第三步:GPIO 初始化設(shè)置:要設(shè)置模式為復(fù)用功能。第四步:串口初始化:設(shè)置波特率,字長(zhǎng),奇偶校驗(yàn)等參數(shù)。第五步:開(kāi)啟中斷并且初始化 NVIC,使能中斷(如果需要開(kāi)啟中斷才需要這個(gè)步驟)。第六步:使能串口。第七步:編寫中斷處理函數(shù):函數(shù)名格式為 USARTxIRQHandler(x 對(duì)應(yīng)串口號(hào))。*/void USART1_Config(void)
{//第一步:串口時(shí)鐘使能,GPIO 時(shí)鐘使能RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);//第二步:設(shè)置引腳復(fù)用器映射:調(diào)用 GPIO_PinAFConfig 函數(shù)。GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_USART1);GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_USART1);// 第三步:GPIO 初始化設(shè)置:要設(shè)置模式為復(fù)用功能。GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9|GPIO_Pin_10;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;GPIO_Init(GPIOA, &GPIO_InitStructure);// 第四步:串口初始化:設(shè)置波特率,字長(zhǎng),奇偶校驗(yàn)等參數(shù)。USART_InitTypeDef USART_InitStructure;USART_InitStructure.USART_BaudRate = 115200;USART_InitStructure.USART_WordLength = USART_WordLength_8b;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_Tx | USART_Mode_Rx;USART_Init(USART1, &USART_InitStructure);// 第五步:開(kāi)啟中斷并且初始化 NVIC,使能USART1接收中斷USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);NVIC_InitTypeDef NVIC_InitStructure;NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;NVIC_Init(&NVIC_InitStructure);// 第六步:使能串口USART_Cmd(USART1, ENABLE);}//發(fā)送一字節(jié)函數(shù)
void USART1_SendByte(uint8_t data)
{USART_SendData(USART1,data); while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET); //等待發(fā)送完畢}// 第七步:編寫中斷處理函數(shù):函數(shù)名格式為 USARTxIRQHandler(x 對(duì)應(yīng)串口號(hào))
void USART1_IRQHandler(void)
{if (USART_GetITStatus(USART1, USART_IT_RXNE) != RESET){// 讀取接收到的數(shù)據(jù)uint8_t receivedData = USART_ReceiveData(USART1);// 將接收到的數(shù)據(jù)發(fā)送回電腦USART1_SendByte(receivedData);// 清除中斷標(biāo)志USART_ClearITPendingBit(USART1, USART_IT_RXNE);}
}//對(duì)printf()函數(shù)進(jìn)行重定向
int fputc(int ch, FILE *f)
{USART_SendData(USART1,ch); //通過(guò)串口發(fā)送數(shù)據(jù),每次發(fā)送一個(gè)字符while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET); //等待發(fā)送完畢return ch;
}
main.c
#include "myusart.h"int main(void)
{// 配置USART1USART1_Config();while (1){// 主循環(huán)中無(wú)需處理接收數(shù)據(jù),接收數(shù)據(jù)在中斷中處理}
}
六、向單片機(jī)發(fā)送指令點(diǎn)亮LED
實(shí)現(xiàn)的功能:
? ??STM32F4 通過(guò)串口和上位機(jī)通信,發(fā)送字符串(指令):?open(通過(guò)串口助手),然后單片機(jī)接收,點(diǎn)亮LED燈;
本實(shí)驗(yàn)主要是串口通信的控制功能,演示串口通信可以增強(qiáng)硬件的能力!
my_led..h
#ifndef __MYLED_H
#define __MYLED_Hvoid LED_Init(void);#endif
my_led.c
#include "stm32f4xx.h" // Device header
#include "myled.h"/*開(kāi)時(shí)鐘 打開(kāi)外設(shè)對(duì)應(yīng)的時(shí)鐘(查看參考手冊(cè),該外設(shè)掛在哪個(gè)數(shù)據(jù)總線上),對(duì)應(yīng)GPIO在哪條總線開(kāi)哪條GPIOF外設(shè) 掛在AHB1總線上,所以要打開(kāi)AHB1的時(shí)鐘,雙擊函數(shù),右鍵->go to definition*/void LED_Init(void)
{//第一步:使能GPIOF的時(shí)鐘 RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE);//使能 GPIOF 時(shí)鐘//第二步:GPIOF9,F10 初始化設(shè)置GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10;//LED0 和 LED1 對(duì)應(yīng) IO 口GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//普通輸出模式GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽輸出GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHzGPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉GPIO_Init(GPIOF, &GPIO_InitStructure);//初始化 GPIO//第三步:設(shè)置燈的初始狀態(tài)GPIO_SetBits(GPIOF,GPIO_Pin_9 | GPIO_Pin_10);//GPIOF9,F10 設(shè)置高電平,燈滅
}
my_usart.h
#ifndef __MY_USART1_H__
#define __MY_USART1_H__
#include <stdint.h>void USART1_Config(void); //串口配置
void USART1_IRQHandler(void);
#endif
my_usart.c
#include "stm32f4xx.h"
#include "myusart.h"
#include "myled.h"
#include "string.h"
#include <stdio.h> /******************************第一步:串口時(shí)鐘使能,GPIO 時(shí)鐘使能。第二步:設(shè)置引腳復(fù)用器映射:調(diào)用 GPIO_PinAFConfig 函數(shù)。第三步:GPIO 初始化設(shè)置:要設(shè)置模式為復(fù)用功能。第四步:串口初始化:設(shè)置波特率,字長(zhǎng),奇偶校驗(yàn)等參數(shù)。第五步:開(kāi)啟中斷并且初始化 NVIC,使能中斷(如果需要開(kāi)啟中斷才需要這個(gè)步驟)。第六步:使能串口。第七步:編寫中斷處理函數(shù):函數(shù)名格式為 USARTxIRQHandler(x 對(duì)應(yīng)串口號(hào))。*********************************/void USART1_Config(void)
{//第一步:串口時(shí)鐘使能,GPIO 時(shí)鐘使能RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);//第二步:設(shè)置引腳復(fù)用器映射:調(diào)用 GPIO_PinAFConfig 函數(shù)。GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_USART1);GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_USART1);// 第三步:GPIO 初始化設(shè)置:要設(shè)置模式為復(fù)用功能。GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9|GPIO_Pin_10;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;GPIO_Init(GPIOA, &GPIO_InitStructure);// 第四步:串口初始化:設(shè)置波特率,字長(zhǎng),奇偶校驗(yàn)等參數(shù)。USART_InitTypeDef USART_InitStructure;USART_InitStructure.USART_BaudRate = 115200;USART_InitStructure.USART_WordLength = USART_WordLength_8b;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_Tx | USART_Mode_Rx;USART_Init(USART1, &USART_InitStructure);// 第五步:開(kāi)啟中斷并且初始化 NVIC,使能USART1接收中斷USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);NVIC_InitTypeDef NVIC_InitStructure;NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;NVIC_Init(&NVIC_InitStructure);// 第六步:使能串口USART_Cmd(USART1, ENABLE);}// 第七步:編寫中斷處理函數(shù):函數(shù)名格式為 USARTxIRQHandler(x 對(duì)應(yīng)串口號(hào))
// USART1中斷服務(wù)程序
void USART1_IRQHandler(void)
{// 檢查USART1是否接收到數(shù)據(jù)if (USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) {// 讀取接收到的字符char received_char = USART_ReceiveData(USART1);// 存儲(chǔ)接收到的字符串的緩沖區(qū)static char buffer[10];static uint8_t index = 0;if (received_char != '\n' && received_char != '\r')//接收數(shù)據(jù)的結(jié)束標(biāo)志{// 將字符存儲(chǔ)到緩沖區(qū)buffer[index] = received_char;index++;} else{//將字符串末尾設(shè)置為'\0'buffer[index] = '\0';index = 0; //置0,為下一次存儲(chǔ)做準(zhǔn)備// 檢查接收到的字符串是否為"open"if (strcmp(buffer, "open") == 0) {// 點(diǎn)亮LED燈GPIO_ResetBits(GPIOF, GPIO_Pin_9| GPIO_Pin_10); }}}
}//對(duì)printf()函數(shù)進(jìn)行重定向
int fputc(int ch, FILE *f)
{USART_SendData(USART1,ch); //通過(guò)串口發(fā)送數(shù)據(jù),每次發(fā)送一個(gè)字符while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET); //等待發(fā)送完畢return ch;
}
? ? ? 注意:因?yàn)槲覀冊(cè)诔绦蛏厦嬖O(shè)置了必須輸入回車或者換行,串口才認(rèn)可接收到的數(shù)據(jù),所以必須在發(fā)送數(shù)據(jù)后再發(fā)送一個(gè)回車符, 這里 XCOM 提供的發(fā)送方法是通過(guò)勾選發(fā)送新行實(shí)現(xiàn),如圖,只要勾選了這個(gè)選項(xiàng),每次發(fā)送數(shù)據(jù)后,XCOM 都會(huì)自動(dòng)多發(fā)一個(gè)回車(0X0D+0X0A)。設(shè)置好了發(fā)送新行,我們?cè)僭诎l(fā)送區(qū)輸入發(fā)送的命令:open,然后單擊發(fā)送,這樣燈便會(huì)點(diǎn)亮!
?
main.c代碼:?
#include <stdio.h>
#include "myusart.h"
#include "myled.h"int main(void)
{LED_Init();USART1_Config();while (1){// 主循環(huán)中無(wú)需處理接收數(shù)據(jù),接收數(shù)據(jù)在中斷中處理}
}
七、主從機(jī)通信的兩種方式
至此,我們的本次的學(xué)習(xí)就結(jié)束了。通過(guò)以上幾個(gè)實(shí)驗(yàn),相信對(duì)串口通信有了深入的理解,這一節(jié)我們就講解到這里,希望能對(duì)大家的開(kāi)發(fā)有幫助。 如有興趣,感謝點(diǎn)贊、關(guān)注、收藏,若有不正地方,還請(qǐng)各位大佬多多指教!