目錄
一、解決的問題
二、串口通訊協(xié)議和RS-232的介紹以及USB/TTL轉(zhuǎn)232模塊的工作原理?
?1、?串口協(xié)議和RS-232標(biāo)準(zhǔn):
?(1)串口協(xié)議:
(2)RS-232 標(biāo)準(zhǔn):?
?2、RS232電平與TTL電平的區(qū)別?
?3、USB/TTL轉(zhuǎn)232“模塊(CH340芯片為例)?
(1)基本原理:?
(2)CH340模塊介紹:
?三、搭建STM32開發(fā)環(huán)境(HAL庫環(huán)境)
?四、利用HAL庫新建一個(gè)中斷控制串口通信的工程?
?五、完善通過中斷方式控制串口通信的keil5工程
?(1)本工程中幾個(gè)函數(shù)簡介:
(2)編寫代碼思路:?
?(3)完善keil5工程代碼:
六、基于中斷控制串口通信的電路連接與燒錄運(yùn)行
?1、電路連接:
?2、 USB轉(zhuǎn)TTL環(huán)境配置:
?3、下載燒錄軟件與串口通信軟件:?
?4、keil5工程里面對于USB轉(zhuǎn)TTL的配置:
?5、編譯生產(chǎn)hex文件,用于后面的燒錄步驟:
?6、燒錄:
7、配置XCOM,打開XCOM軟件,按下圖所示進(jìn)行配置:
?8、運(yùn)行結(jié)果演示:
七、基于中斷控制串口通信的keil5仿真調(diào)試
?1、進(jìn)入keil5仿真:
?2、開始仿真:
八、利用HAL庫新建一個(gè)DMA控制串口通信的工程?
九、完善通過DMA方式控制串口通信的keil5工程
1、 本工程中的幾個(gè)函數(shù)簡介:
2、代碼編寫思路
3、完善keil5工程代碼:
十、 基于DMA方式控制串口通信的電路連接與燒錄運(yùn)行
1、電路的連接、軟件的下載以及環(huán)境的配置:
2、運(yùn)行結(jié)果演示:?
十一、基于DMA控制串口通信的keil5仿真調(diào)試
?1、進(jìn)入keil5仿真與開始仿真:
2、仿真結(jié)果演示:?
十二、總結(jié)
十三、參考資料
一、解決的問題
? ? ?1、使用HAL庫(或標(biāo)準(zhǔn)庫)方式,設(shè)置USART1 波特率為115200,1位停止位,無校驗(yàn)位,分別采用中斷方式、DMA方式完成下列任務(wù):
? ? ?STM32系統(tǒng)給上位機(jī)(win10)連續(xù)發(fā)送“hello windows!”;當(dāng)上位機(jī)給stm32發(fā)送字符“stop”后,stm32暫停發(fā)送“hello windows!”;發(fā)送一個(gè)字符“start”后,stm32繼續(xù)發(fā)送;
? ? 2、?在沒有示波器條件下,可以使用Keil的軟件仿真邏輯分析儀功能觀察串口輸出波形,并分析時(shí)序狀態(tài)正確與否,計(jì)算波特率實(shí)際為多少。
二、串口通訊協(xié)議和RS-232的介紹以及USB/TTL轉(zhuǎn)232模塊的工作原理?
?1、?串口協(xié)議和RS-232標(biāo)準(zhǔn):
?(1)串口協(xié)議:
? ? ? ? 串口通訊(Serial Communication)是一種設(shè)備間非常常用的串行通訊方式,因?yàn)樗唵?、便捷,因此大部分電子設(shè)備都支持該通訊方式,電子工程師在調(diào)試設(shè)備時(shí)也經(jīng)常使用該通 訊方式輸出調(diào)試信息。
? ? ? ?在計(jì)算機(jī)科學(xué)里,大部分復(fù)雜的問題都可以通過分層來簡化。如芯片被分為內(nèi)核層和片上外設(shè);STM32標(biāo)準(zhǔn)庫則是在寄存器與用戶代碼之間的軟件層。對于通訊協(xié)議,我們也以分層的方式來理解,最基本的是把它分為物理層和協(xié)議層。
名稱 | 組成作用 |
---|---|
物理層 | 具有機(jī)械、電子功能部分的特性,確保原始數(shù)據(jù)在物理媒體的傳輸 |
協(xié)議層 | 規(guī)定通訊邏輯,統(tǒng)一收發(fā)雙方的數(shù)據(jù)打包、解包標(biāo)準(zhǔn)。 |
?在串口通訊的物理層有很多標(biāo)準(zhǔn)及變種,下面主要講解 RS-232 標(biāo)準(zhǔn)!
(2)RS-232 標(biāo)準(zhǔn):?
?RS-232 標(biāo)準(zhǔn)主要規(guī)定了信號的用途,通訊接口以及信號的電平標(biāo)準(zhǔn)。?
? ? ? ?在上面的通訊方式中,兩個(gè)通訊設(shè)備的“DB9接口”之間通過串口信號線建立起連接,串口信號線中使用“RS-232標(biāo)準(zhǔn)”傳輸數(shù)據(jù)信號。由于RS-232電平標(biāo)準(zhǔn)的信號不能直接被控制器直接識別,所以這些信號會經(jīng)過一個(gè)“電平轉(zhuǎn)換芯片”轉(zhuǎn)換成控制器能識別的“TTL校準(zhǔn)”的電平信號,才能實(shí)現(xiàn)通訊。?
?2、RS232電平與TTL電平的區(qū)別?
?根據(jù)通訊使用的電平標(biāo)準(zhǔn)不同,串口通訊可分為 TTL標(biāo)準(zhǔn)和 RS-232標(biāo)準(zhǔn):
標(biāo)準(zhǔn)名稱 | 邏輯1 | 邏輯0 |
---|---|---|
TLL | 2.4V~5V | 0~0.5V |
RS-232 | -15V~3V | +3V~+15V |
? ? ? ?從表格中不難看出,兩種標(biāo)準(zhǔn)劃分的邏輯電壓不同。在電子電路中常使用 TTL 的電平標(biāo)準(zhǔn),理想狀態(tài)下,使用 5V 表示二進(jìn)制邏輯 1,使用 0V 表示邏輯 0;而為了增加串口通訊的遠(yuǎn)距離傳輸及抗干擾能力,它使用-15V表示邏輯 1,+15V 表示邏輯 0。
?下圖為用RS232與TTL電平校準(zhǔn)表示同一個(gè)信號時(shí)的對比:?
?3、USB/TTL轉(zhuǎn)232“模塊(CH340芯片為例)?
(1)基本原理:?
? ? ? ?USB轉(zhuǎn)串口即實(shí)現(xiàn)計(jì)算機(jī)USB接口到物理串口之間的轉(zhuǎn)換。可以為沒有串口的計(jì)算機(jī)或其他USB主機(jī)增加串口,使用USB轉(zhuǎn)串口設(shè)備等于將傳統(tǒng)的串口設(shè)備變成了即插即用的USB設(shè)備。
? ? ? USB主機(jī)檢測到USB轉(zhuǎn)串口設(shè)備插入后,首先會對設(shè)備復(fù)位,然后開始USB枚舉過程。USB枚舉時(shí)過程會獲取設(shè)備描述符、配置描述符、接口描述符等。描述符中會包含USB設(shè)備的廠商ID,設(shè)備ID和Class類別等信息。操作系統(tǒng)會根據(jù)該信息為設(shè)備匹配相應(yīng)的USB設(shè)備驅(qū)動(dòng)。
? ? ? USB虛擬串口的實(shí)現(xiàn)在系統(tǒng)上依賴于USB轉(zhuǎn)串口驅(qū)動(dòng),一般由廠家直接提供,也可以使用操作系統(tǒng)自帶的CDC類串口驅(qū)動(dòng)等。驅(qū)動(dòng)主要分為2個(gè)功能,其一注冊USB設(shè)備驅(qū)動(dòng),完成對USB設(shè)備的控制與數(shù)據(jù)通訊,其二注冊串口驅(qū)動(dòng),為串口應(yīng)用層提供相應(yīng)的實(shí)現(xiàn)方法。
串口收發(fā)對應(yīng)的驅(qū)動(dòng)數(shù)據(jù)流向一覽表:
發(fā)送or接收 | 數(shù)據(jù)流向 |
串口發(fā)送 | 串口應(yīng)用發(fā)送數(shù)據(jù)→USB串口驅(qū)動(dòng)獲取數(shù)據(jù)→驅(qū)動(dòng)將數(shù)據(jù)經(jīng)過USB通道發(fā)送給USB串口設(shè)備→USB串口設(shè)備接收到數(shù)據(jù)通過串口發(fā)送 |
串口接收 | USB串口設(shè)備接收串口數(shù)據(jù)→將串口數(shù)據(jù)經(jīng)過USB打包后上傳給USB主機(jī)→USB串口驅(qū)動(dòng)獲取到通過USB上傳的串口數(shù)據(jù)→驅(qū)動(dòng)將數(shù)據(jù)保存在串口緩沖區(qū)提供給串口應(yīng)用讀取 |
??(2)CH340模塊介紹:
CH340電路與實(shí)物圖:?
TXD:發(fā)送端,一般表示為自己的發(fā)送端,正常通信必須接另一個(gè)設(shè)備的RXD。
RXD:接收端,一般表示為自己的接收端,正常通信必須接另一個(gè)設(shè)備的TXD。
正常通信的時(shí)候本身的TXD永遠(yuǎn)接設(shè)備的RXD。
??USB轉(zhuǎn)TTL串口模塊與單片機(jī)連接電路圖如下所示:
??三、搭建STM32開發(fā)環(huán)境(HAL庫環(huán)境)
??請參考我的這篇博客:STM32使用HAL庫點(diǎn)亮流水燈-CSDN博客
?四、利用HAL庫新建一個(gè)中斷控制串口通信的工程?
????(1)打開STM32CubeMX,在主界面點(diǎn)擊:ACCESS TO MCU SELECTOR:
?(2)選擇的單片機(jī)型號以及點(diǎn)擊開始工程項(xiàng)目:?
?(3)配置GPIO:PA0。如果僅僅是完成串口通信的話,這一步可以跳過。但是根據(jù)實(shí)驗(yàn)要求,為了區(qū)分串口通信的開啟與關(guān)閉,要使用一個(gè)LED燈來顯示。當(dāng)串口通信開啟(STM32向電腦發(fā)送信息)的時(shí)候,LED燈亮,當(dāng)串口通信關(guān)閉(STM32停止向電腦發(fā)送消息)的時(shí)候,LED燈滅。?
?(4)配置USART1,我們使用USART1進(jìn)行數(shù)據(jù)傳輸。在這個(gè)界面按下圖進(jìn)行配置。我們對USART1的配置要做的只有兩件事:一是選擇串口工作模式為異步,二是開啟USART1全局中斷
?
(5)進(jìn)入Project Manager(工程管理),進(jìn)行工程設(shè)置點(diǎn)擊生成工程與代碼:
注意:路徑不能包含中文和空格,不然生成的工程文件無法在Keil中打開;
?五、完善通過中斷方式控制串口通信的keil5工程
?(1)本工程中幾個(gè)函數(shù)簡介:
?HAL_UART_Receive_IT:
HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_int *data, uint16_t Size)
/*
huart:使用哪個(gè)串口進(jìn)行通信
data: 一個(gè)地址,用于保存接受到的數(shù)據(jù)
Size: 接收的數(shù)據(jù)個(gè)數(shù)
*/
? ? ? 在調(diào)用此函數(shù)后,程序會將對應(yīng)串口的接收中斷開啟,當(dāng)我們向單片機(jī)發(fā)送數(shù)據(jù)時(shí)會觸發(fā)這個(gè)中斷。在觸發(fā)這個(gè)中斷后,程序會接收數(shù)據(jù)到你傳入的地址中,會讀取Size個(gè)數(shù)據(jù)。讀取完成后,關(guān)閉接收中斷使能。
? ? ? 由于程序在接收完數(shù)據(jù)后會關(guān)閉接收中斷。因此這個(gè)函數(shù)我們要寫在main的死循環(huán)中,保證接收中斷可以一直開啟。
?HAL_UART_Transmit_IT:
HAL_UART_Transmit_IT(UART_HandleTypeDef *huart, uint8_int *data, uint16_t Size)
/*
huart:使用哪個(gè)串口進(jìn)行通信
data: 一個(gè)地址,里面是要發(fā)送的數(shù)據(jù)通常是數(shù)組
Size: 發(fā)送的數(shù)據(jù)個(gè)數(shù)
*/
? ? ? ?使用這個(gè)函數(shù)開啟發(fā)送中斷,發(fā)送寄存器為空時(shí)觸發(fā)中斷,將要發(fā)送的數(shù)據(jù)送入發(fā)送寄存器并發(fā)送。發(fā)送完成后關(guān)閉中斷。在此實(shí)驗(yàn)中,我們把它當(dāng)做普通的發(fā)送函數(shù)即可。
HAL_GPIO_WritePin:?
HAL_GPIO_WritePin(GPIOX,GPIO_PIN_X,GPIO_PIN_STATUS)
/*
GPIOX:目標(biāo)GPIO的組號
GPIO_PIN_X: 目標(biāo)GPIO的引腳編號
GPIO_PIN_STATUS: 引腳狀態(tài)
*/
?使用這個(gè)函數(shù)修改GPIO_ODR寄存器,將非復(fù)用輸出的GPIO引腳輸出電平設(shè)置成自己想要的。?
?HAL_Delay(uint ms):?
HAL_Delay(uint ms)
延時(shí)ms函數(shù)。?
(2)編寫代碼思路:?
main函數(shù)外用一個(gè)char類型的數(shù)組:rcData,接收發(fā)過來的連續(xù)字符串,默認(rèn)為:start
main函數(shù)中進(jìn)入死循環(huán),調(diào)用HAL_UART_Receive_IT使能接收中斷
如果電腦發(fā)送了字符串,接收變量flag的值會變
如果接收變量為:start,led陰極置低電平,led亮,向電腦發(fā)數(shù)據(jù)“hello windows!”
如果接收變量為:stop!,led陰極置高電平,led滅,不向電腦發(fā)送數(shù)據(jù)
?(3)完善keil5工程代碼:
?首先,點(diǎn)擊剛剛生成的keil5工程文件,雙擊main.c文件,然后再main.c中找到圖示框住的函數(shù),?接著右擊此函數(shù),進(jìn)入其定義的地方處:
(2)將圖中框住的部分改為SET即可:此步驟是將這個(gè)GPIO口設(shè)置為高電平,初始時(shí)不亮!
?(3)回到main.c文件中,詳細(xì)編寫主要代碼:
(1:設(shè)置接收中斷,函數(shù)原型簡介:
HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
UART_HandleTypeDef *huart UATR的別名
huart1 *pData 接收到的數(shù)據(jù)存放地址
Size 接收的字節(jié)數(shù)
功能:串口中斷接收,以中斷方式接收指定長度數(shù)據(jù)。
大致過程是,設(shè)置數(shù)據(jù)存放位置,接收數(shù)據(jù)長度,然后使能串口接收中斷。
接收到數(shù)據(jù)時(shí),會觸發(fā)串口中斷。
再然后,串口中斷函數(shù)處理,直到接收到指定長度數(shù)據(jù)
而后關(guān)閉中斷,進(jìn)入中斷接收回調(diào)函數(shù),不再觸發(fā)接收中斷。(只觸發(fā)一次中斷)
?具體化運(yùn)用在本項(xiàng)目中代碼為:
HAL_UART_Receive_IT(&huart1,(uint8_t*)rcData,5);
?(2:用字符串進(jìn)行判斷,因此接受變量用數(shù)組來儲存,Size要改成數(shù)組的大小為:6。單片機(jī)收到串口助手發(fā)的信息后,與"stop2!"和"start"進(jìn)行匹配。根據(jù)匹配結(jié)果執(zhí)行不同的代碼?!皊top!”,"start"與收到的數(shù)據(jù)都用uint8_t數(shù)組保存。為執(zhí)行匹配操作,我們需要寫一個(gè)函數(shù)對每一位進(jìn)行判斷與匹配:
int strEqual(char rcData[15],char rcData2[15]){
for(uint8_t i = 0 ; i < 15 ; i++){
if (rcData[i] != rcData2[i]) return 0;
}
return 1;
}
?(3:在main函數(shù)前面添加上如下代碼(接收信息儲存數(shù)組,接收信息匹配處理函數(shù),信息標(biāo)志flag):
int strEqual(char rcData[6],char rcData2[6]){
for(uint8_t i = 0 ; i < 6 ; i++)
{
if (rcData[i] != rcData2[i]) return 0;
}
return 1;
}
char rcData[6] = "start";
uint8_t flag=1;
?(4:main里面的while(1)替換為如下信息接收與發(fā)送處理代碼:
if(flag==0)
{
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_0,GPIO_PIN_SET);
}
else if(flag==1)
{
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_0,GPIO_PIN_RESET);
uint8_t hello[20]="hello windows!\n";
HAL_UART_Transmit_IT(&huart1,hello,20);
HAL_Delay(600);
}
?(5:在main函數(shù)下面重寫中斷處理函數(shù)
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
//當(dāng)輸入的指令為“stop!"時(shí),發(fā)送提示并改變flag=0
if(strEqual(rcData,"stop!"))
{
flag=0;
}
//當(dāng)輸入的指令為"start"時(shí),發(fā)送提示并改變flag=1
else if(strEqual(rcData,"start"))
{
flag=1;
}
//重新設(shè)置中斷
HAL_UART_Receive_IT(&huart1,(uint8_t*)rcData,5);
}
?(6:完善之后的main.c的全部代碼編寫如下所示:
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : main.c
* @brief : Main program body
******************************************************************************
* @attention
*
* Copyright (c) 2023 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "usart.h"
#include "gpio.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
int strEqual(char rcData[6],char rcData2[6]){
for(uint8_t i = 0 ; i < 6 ; i++){
if (rcData[i] != rcData2[i]) return 0;
}
return 1;
}
char rcData[6] = "start";
uint8_t flag=1;
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_USART1_UART_Init();
HAL_UART_Receive_IT(&huart1,(uint8_t*)rcData,5);
while (1)
{
if(flag==0)
{
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_0,GPIO_PIN_SET);
}
else if(flag==1)
{
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_0,GPIO_PIN_RESET);
uint8_t hello[20]="hello windows!\n";
HAL_UART_Transmit_IT(&huart1,hello,20);
HAL_Delay(600);
}
}
}
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
//當(dāng)輸入的指令為“stop!"時(shí),發(fā)送提示并改變flag=0
if(strEqual(rcData,"stop!"))
{
flag=0;
}
//當(dāng)輸入的指令為"start"時(shí),發(fā)送提示并改變flag=1
else if(strEqual(rcData,"start"))
{
flag=1;
}
//重新設(shè)置中斷
HAL_UART_Receive_IT(&huart1,(uint8_t*)rcData,5);
}
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK)
{
Error_Handler();
}
}
/* USER CODE BEGIN 4 */
/* USER CODE END 4 */
/**
* @brief This function is executed in case of error occurrence.
* @retval None
*/
void Error_Handler(void)
{
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
__disable_irq();
while (1)
{
}
/* USER CODE END Error_Handler_Debug */
}
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t *file, uint32_t line)
{
/* USER CODE BEGIN 6 */
/* User can add his own implementation to report the file name and line number,
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
六、基于中斷控制串口通信的電路連接與燒錄運(yùn)行
?1、電路連接:
USB轉(zhuǎn)TTL與STM32的連接,參考下圖:?
?
?PA0——黃燈;
連接好的實(shí)物圖如下所示:
?2、 USB轉(zhuǎn)TTL環(huán)境配置:
需要在電腦上安裝CH340驅(qū)動(dòng)(USB串口驅(qū)動(dòng))或者CH341驅(qū)動(dòng)(USB串口驅(qū)動(dòng)):
在網(wǎng)上下載好所需的CH340驅(qū)動(dòng)(USB串口驅(qū)動(dòng))或者CH341驅(qū)動(dòng)(USB串口驅(qū)動(dòng)),如下圖所示:
?點(diǎn)擊CH341SER_2.EXE進(jìn)行安裝CH341驅(qū)動(dòng),會彈出一下彈窗,點(diǎn)擊安裝,等待安裝成功即可:
?3、下載燒錄軟件與串口通信軟件:?
燒錄軟件推薦使用:FLYMCU,如下圖所示圖樣:?
串口通信軟件推薦使用:XCOM,如下圖所示圖樣:
請自行在網(wǎng)上把這兩個(gè)軟件下載好,為后面的燒錄運(yùn)行做好準(zhǔn)備!?
?4、keil5工程里面對于USB轉(zhuǎn)TTL的配置:
?5、編譯生產(chǎn)hex文件,用于后面的燒錄步驟:
?6、燒錄:
(1)將USB轉(zhuǎn)TTL插上電腦的USB接口上去,打開剛剛下載好的FLYMCU軟件,按照下圖所示進(jìn)行相關(guān)配置,其中的第二步就是選擇剛剛上一步編譯生成的hex文件:
(2)改變STM32最小系統(tǒng)板子的跳線帽連接方式:BOOTO的跳線帽連接方式由0——>1:
(3)點(diǎn)擊FLYMCU的開始編程(P),接著馬上點(diǎn)擊STM32最小系統(tǒng)板子的復(fù)位鍵即可完成燒錄:
第一步:
第二步:
燒錄成功示意圖:
燒錄完成之后還需要下面的關(guān)鍵一步,把STM32最小系統(tǒng)板子的跳線帽連接方式還原:BOOTO的跳線帽連接方式由1——>0:
??7、配置XCOM,打開XCOM軟件,按下圖所示進(jìn)行配置:
??8、運(yùn)行結(jié)果演示:
?打開串口,并且同時(shí)點(diǎn)擊STM32最小系統(tǒng)板子的復(fù)位鍵即可開始運(yùn)行:
注:輸入“start”:讓STM32單片機(jī)繼續(xù)向電腦發(fā)送信息;
輸入“stop!”:讓STM32單片機(jī)停止向電腦發(fā)送消息 ;
七、基于中斷控制串口通信的keil5仿真調(diào)試
?1、進(jìn)入keil5仿真:
(1)點(diǎn)擊第一步,Target界面中,選擇跟正確的晶振大小,使用8MHz的外部晶振:
(2)接著進(jìn)行Debug頁的設(shè)置:??
(3)點(diǎn)擊圖示圈住的地方進(jìn)入仿真調(diào)試界面:?
??(4)選擇邏輯分析儀:?
(5)點(diǎn)擊Setup設(shè)置添加要進(jìn)行觀察的引腳波形信息:
?波形信息配置:添加波形信息的時(shí)候,如果自己選擇的串口配置的是USART1,就在添加信息里面輸入:USART1_RS;同理可得,如果自己選擇的串口配置的是USART2,就在添加信息里面輸入:USART2_RS;我項(xiàng)目里配置的是USART1,所以我在添加信息時(shí)就輸入:USART1_RS。
我這里選擇的USART1對應(yīng)的波形顏色是綠色?。?!
?2、開始仿真:
(1)點(diǎn)擊圖示圈住的部分,進(jìn)行仿真???
可以調(diào)節(jié)下圖所示幾個(gè)比較重要與常用的按鈕來進(jìn)行對圖像的總體放大與縮小等等操作?:
??(2)仿真結(jié)果:
放大處理之后波形圖像:
八、利用HAL庫新建一個(gè)DMA控制串口通信的工程?
(1)打開STM32CubeMX,在主界面點(diǎn)擊:ACCESS TO MCU SELECTOR:
(2)選擇的單片機(jī)型號以及點(diǎn)擊開始工程項(xiàng)目:?
?(3)按照下圖所示進(jìn)行配置RCC:
?(4)設(shè)置USART1:選擇異步通信、參數(shù)選擇默認(rèn)和使能串口:
(5)添加兩個(gè)通道:
?(6)進(jìn)入Project Manager(工程管理),進(jìn)行工程設(shè)置點(diǎn)擊生成工程與代碼:
注意:路徑不能包含中文和空格,不然生成的工程文件無法在Keil中打開;
九、完善通過DMA方式控制串口通信的keil5工程
1、 本工程中的幾個(gè)函數(shù)簡介:
- HAL_UART_Transmit_DMA():串口DMA模式發(fā)送
HAL_UART_Transmit_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
功能:串口通過DMA發(fā)送指定長度的數(shù)據(jù)。
參數(shù):
UART_HandleTypeDef *huart UATR的別名 如 : UART_HandleTypeDef huart1; 別名就是huart1
*pData 需要發(fā)送的數(shù)據(jù)
Size 發(fā)送的字節(jié)數(shù)
?本文運(yùn)用舉例:
HAL_UART_Transmit_DMA(&huart1, (uint8_t *)message, sizeof(message));
- HAL_UART_Receive_DMA():串口DMA模式接收
HAL_UART_Receive_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
功能:串口通過DMA接受指定長度的數(shù)據(jù)。
參數(shù):
UART_HandleTypeDef *huart UATR的別名 如 : UART_HandleTypeDef huart1; 別名就是huart1
*pData 需要存放接收數(shù)據(jù)的數(shù)組
Size 接受的字節(jié)數(shù)
?本文運(yùn)用舉例:
HAL_UART_Receive_DMA(&huart1,(uint8_t*)rx_buf,5);//設(shè)置DMA接收到的數(shù)據(jù)存放在rx_buf中
- ?HAL_Delay(uint ms):?
HAL_Delay(uint ms)
延時(shí)ms函數(shù)。??
2、代碼編寫思路
main函數(shù)外用一個(gè)uint8_t類型的數(shù)組:rcData,接收發(fā)過來的連續(xù)字符串,默認(rèn)為:start
main函數(shù)中進(jìn)入死循環(huán),調(diào)用HAL_UART_Receive_DMA()
如果電腦發(fā)送了字符串,接收變量flag的值會變
如果接收變量為:start,led陰極置低電平,向電腦發(fā)數(shù)據(jù)“hello windows!”
如果接收變量為:stop!,led陰極置高電平,不向電腦發(fā)送數(shù)據(jù)
3、完善keil5工程代碼:
?(1)在main.c文件中,詳細(xì)編寫主要代碼:
(1:重新定義串口接收完成回調(diào)函數(shù):
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
//當(dāng)輸入的指令為“stop!"時(shí),發(fā)送提示并改變flag=0
if(strEqual(rx_buf,"stop!"))
{
flag=0;
}
//當(dāng)輸入的指令為"start"時(shí),發(fā)送提示并改變flag=1
else if(strEqual(rx_buf,"start"))
{
flag=1;
}
HAL_UART_Receive_DMA(&huart1,(uint8_t*)rx_buf,5);
}
??(2:用字符串進(jìn)行判斷,因此接受變量用數(shù)組來儲存,Size要改成數(shù)組的大小為:6。單片機(jī)收到串口助手發(fā)的信息后,與"stop2!"和"start"進(jìn)行匹配。根據(jù)匹配結(jié)果執(zhí)行不同的代碼?!皊top!”,"start"與收到的數(shù)據(jù)都用uint8_t數(shù)組保存。為執(zhí)行匹配操作,我們需要寫一個(gè)函數(shù)對每一位進(jìn)行判斷與匹配:
int strEqual(char rcData[6],char rcData2[6])
{
for(uint8_t i = 0 ; i < 6 ; i++){
if (rcData[i] != rcData2[i]) return 0;
}
return 1;
}
??(3:在main函數(shù)前面添加上如下代碼(接收信息儲存數(shù)組,接收信息匹配處理函數(shù),信息標(biāo)志flag):
uint8_t flag=1;
uint8_t rx_buf[6];//接收串口數(shù)據(jù)存放的數(shù)組
int strEqual(char rcData[6],char rcData2[6])
{
for(uint8_t i = 0 ; i < 6 ; i++){
if (rcData[i] != rcData2[i]) return 0;
}
return 1;
}
?(4:main里面的while(1)替換為如下信息接收與發(fā)送處理代碼:
if(flag==1)
{
HAL_UART_Transmit_DMA(&huart1, (uint8_t *)message, sizeof(message));
HAL_Delay(600);
}
?5:在main函數(shù)下面重寫串口接收完成回調(diào)函數(shù):
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
//當(dāng)輸入的指令為“stop!"時(shí),發(fā)送提示并改變flag=0
if(strEqual(rx_buf,"stop!"))
{
flag=0;
}
//當(dāng)輸入的指令為"start"時(shí),發(fā)送提示并改變flag=1
else if(strEqual(rx_buf,"start"))
{
flag=1;
}
HAL_UART_Receive_DMA(&huart1,(uint8_t*)rx_buf,5);
}
??(6:完善之后的main.c的全部代碼編寫如下所示:
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : main.c
* @brief : Main program body
******************************************************************************
* @attention
*
* Copyright (c) 2023 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "dma.h"
#include "usart.h"
#include "gpio.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
uint8_t flag=1;
uint8_t rx_buf[6];//接收串口數(shù)據(jù)存放的數(shù)組
int strEqual(char rcData[6],char rcData2[6])
{
for(uint8_t i = 0 ; i < 6 ; i++){
if (rcData[i] != rcData2[i]) return 0;
}
return 1;
}
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
//當(dāng)輸入的指令為“stop!"時(shí),發(fā)送提示并改變flag=0
if(strEqual(rx_buf,"stop!"))
{
flag=0;
}
//當(dāng)輸入的指令為"start"時(shí),發(fā)送提示并改變flag=1
else if(strEqual(rx_buf,"start"))
{
flag=1;
}
HAL_UART_Receive_DMA(&huart1,(uint8_t*)rx_buf,5);
}
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
HAL_Init();
uint8_t message[] = "hello windows!\n"; //定義數(shù)據(jù)發(fā)送數(shù)組
SystemClock_Config();
MX_GPIO_Init();
MX_DMA_Init();
MX_USART1_UART_Init();
HAL_UART_Receive_DMA(&huart1,(uint8_t*)rx_buf,5);//設(shè)置DMA接收到的數(shù)據(jù)存放在rx_buf中
while (1)
{
if(flag==1)
{
HAL_UART_Transmit_DMA(&huart1, (uint8_t *)message, sizeof(message));
HAL_Delay(600);
}
}
}
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK)
{
Error_Handler();
}
}
/* USER CODE BEGIN 4 */
/* USER CODE END 4 */
/**
* @brief This function is executed in case of error occurrence.
* @retval None
*/
void Error_Handler(void)
{
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
__disable_irq();
while (1)
{
}
/* USER CODE END Error_Handler_Debug */
}
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t *file, uint32_t line)
{
/* USER CODE BEGIN 6 */
/* User can add his own implementation to report the file name and line number,
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
十、 基于DMA方式控制串口通信的電路連接與燒錄運(yùn)行
1、電路的連接、軟件的下載以及環(huán)境的配置:
參考本文前面第六大點(diǎn):基于中斷方式控制串口通信的電路連接與燒錄與運(yùn)行!基本上一致?。?!
2、運(yùn)行結(jié)果演示:?
?打開串口,并且同時(shí)點(diǎn)擊STM32最小系統(tǒng)板子的復(fù)位鍵即可開始運(yùn)行:
注:輸入“start”:讓STM32單片機(jī)繼續(xù)向電腦發(fā)送信息;
輸入“stop!”:讓STM32單片機(jī)停止向電腦發(fā)送消息 ;
十一、基于DMA控制串口通信的keil5仿真調(diào)試
?1、進(jìn)入keil5仿真與開始仿真:
參考本文前面第七大點(diǎn):基于中斷控制串口通信的keil5仿真調(diào)試!里面有詳細(xì)介紹了關(guān)于如何進(jìn)入keil5仿真、環(huán)境設(shè)置和詳細(xì)操作等等?。?!
2、仿真結(jié)果演示:
?放大處理之后波形圖像:
十二、總結(jié)
? ? ? 本人在這篇blog:STM32使用HAL庫中斷控制串口通信-CSDN博客,已經(jīng)提前接觸并且完成了有關(guān)中斷控制串口通信:向單片機(jī)發(fā)送單個(gè)字符的實(shí)驗(yàn);本次又在這個(gè)的實(shí)驗(yàn)的基礎(chǔ)上完成了對于利用中斷控制串口通信:向單片機(jī)發(fā)送連續(xù)字符串的實(shí)驗(yàn)??偟膩碚f,是溫習(xí)前面已經(jīng)會了的中斷控制串口通信;從局部來說,也是進(jìn)步:學(xué)習(xí)并且嘗試了新的內(nèi)容:向單片機(jī)發(fā)送連續(xù)字符串的實(shí)驗(yàn)而不是單個(gè)字符?。?!
? ? ?對于DMA控制串口通信方式,本人之前既沒有聽說過,也沒有接觸過。在本實(shí)驗(yàn)中,本人學(xué)習(xí)了有關(guān)DMA的知識,并且同時(shí)付出行動(dòng)去實(shí)踐實(shí)驗(yàn),最終成功利用DMA方式也實(shí)現(xiàn)了串口通信:向單片機(jī)發(fā)送單個(gè)字符的實(shí)驗(yàn)?。?!
? ? 謝謝你的觀看,希望你能有所收獲!完!??!
十三、參考資料
1、stm32使用hal庫中斷控制串口通信_stm32 hal庫串口中斷接收_終極末影龍的博客-CSDN博客?
2、HAL庫中斷方式進(jìn)行串口通信_醉意丶千層夢的博客-CSDN博客?
3、STM32使用HAL庫中斷控制串口通信-CSDN博客?
4、基于HAL庫實(shí)現(xiàn)DMA串口通信_hal_dma_start_it_醉意丶千層夢的博客-CSDN博客
5、STM32F0x HAL庫學(xué)習(xí)筆記(7)DMA數(shù)據(jù)的傳輸配置:串口數(shù)據(jù)的DMA發(fā)送與接收_hal_dma_start-CSDN博客文章來源:http://www.zghlxwxcb.cn/news/detail-788330.html
6、【STM32】HAL庫 STM32CubeMX教程十一---DMA (串口DMA發(fā)送接收)_hal庫dma串口接收-CSDN博客文章來源地址http://www.zghlxwxcb.cn/news/detail-788330.html
到了這里,關(guān)于STM32—HAL庫中斷/DMA控制和完成串口通信的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!