目錄
一、stm32串口通信
??????? 1.1 硬件流控
???????? 1.2 軟件流控
??????? 1.3 串口通信參數(shù)
二、新建RS485通信工程
??????? 2.1 項目實現(xiàn)背景信息
??????? 2.2 項目配置
??????? 2.3 代碼實現(xiàn)
?? 三、RS485驅(qū)動調(diào)用及測試
??????? 3.1 接口調(diào)用
?????? 3.2 編譯及下載及測試
一、stm32串口通信
?????????stm32串口通信一般是指通過UART(Universal Asynchronous Receiver/Transmitter)通用異步收發(fā)傳輸器傳輸數(shù)據(jù),UART 作為異步串口通信協(xié)議的一種,工作原理是將傳輸數(shù)據(jù)的每個字符一位接一位地傳輸,其在應(yīng)用程序開發(fā)過程中使用頻率最高的數(shù)據(jù)總線。而MCU的UART 通信實際上是基于GPIO引腳電平信號實現(xiàn)的,因此,在預(yù)制UART外設(shè)不夠時,也會可以采用GPIO引腳自行模擬UART串口通信。
??????? 1.1 硬件流控
????????UART 串口的特點是將數(shù)據(jù)一位一位地順序傳送,只要 2 根傳輸線就可以實現(xiàn)雙向通信,一根線發(fā)送數(shù)據(jù)的同時用另一根線接收數(shù)據(jù)。但是,數(shù)據(jù)在兩個串口之間進行通訊的時候常常會出現(xiàn)丟失數(shù)據(jù)的現(xiàn)象,主要原因是數(shù)據(jù)處理及數(shù)據(jù)接收效率不一致引起的沖突,為了解決這種問題,引入了流控概念。流控的概念源于 RS232 這個標準,用來解決這個速度匹配的問題,通過新增傳輸線來控制數(shù)據(jù)傳輸數(shù)據(jù)線的流向、流速等,這就是簡單的三線串口的通訊方式,如下圖:
???????? 在兩條數(shù)據(jù)線基礎(chǔ)上,增加兩根控制線,一根叫 CTS(Clear To Send 為輸入信號,一根叫 RTS(Require To Send 為輸出信號),一個是接收控制,一個是發(fā)送控制??梢钥吹?,數(shù)據(jù)線方向與流控線數(shù)據(jù)方向相反,因為流控的主要是協(xié)調(diào)接收數(shù)據(jù)和處理數(shù)據(jù)一致性,所以需要讓發(fā)送端等待,接收端發(fā)出來的信號叫 RTS 信號,發(fā)送端檢測管腳叫 CTS。
??????? 數(shù)據(jù)接收與 RTS 信號標記如下圖,接收端緩存沒數(shù)據(jù)時,RTS是低電平狀態(tài),發(fā)送端可以發(fā)送數(shù)據(jù);當接收端接收到數(shù)據(jù)后,RTS切換為高電平,就是告訴發(fā)送端,數(shù)據(jù)還沒有被拿走,請發(fā)送端等待;RTS 信號在數(shù)據(jù)沒有被讀取之前都是保持在高電平狀態(tài),一旦數(shù)據(jù)被 DMA 或者 CPU 從 DR 寄存器讀取之后,RTS 就釋放高電平,變?yōu)榈碗娖?,這時候發(fā)送端又可以發(fā)送數(shù)據(jù)的了。另外,如果 USART 是FIFO 模式,即緩沖模式開啟的時候,在 FIFO 滿的時候才會去拉高 RTS 信號。
???????? 相應(yīng)的,發(fā)送數(shù)據(jù)和CTS信號類似,發(fā)送端在發(fā)送數(shù)據(jù)時,要實時監(jiān)測 CTS 的電平狀態(tài),如果發(fā)現(xiàn)是高電平,就不會再發(fā)送新的數(shù)據(jù),直到 CTS 檢測發(fā)現(xiàn)已經(jīng)沒有高電平信號才會再次發(fā)送。
??????? 我們在cubeIDE中,對應(yīng)上述的就是勾選RS232流控制支持,就可以選擇CTS/RTS引腳配置。
???????? 如上面所述,由設(shè)想的兩根數(shù)據(jù)線傳輸多出兩根流控制線,這無形中增加硬件成本,因此,RS485出現(xiàn)后,RS232的CTS/RT合二為一DE ,STM32 上有一個 DE 管腳和 RS485 的接收器芯片直接相連,控制數(shù)據(jù)的收發(fā),主要就是數(shù)據(jù)的方向的控制。因為 RS485 是一個半雙工的通訊模式,它的數(shù)據(jù)收的時候就不能發(fā),發(fā)的時候不能收。
???????? 在cubeMX中,需要支持DE引腳開始,就需要勾選RS485流控制功能
???????? 1.2 軟件流控
????????上述的增加流控制線的做法都是屬于硬件流控的實現(xiàn),在實際項目開發(fā)過程,我們很多時候為了方便以及節(jié)省引腳,往往采用軟流控。軟件流控是以特殊的字符來代表從機已經(jīng)不能再接收新的數(shù)據(jù)了,基本的流程就是從機在接收數(shù)據(jù)很多的時候或主動給發(fā)送端發(fā)送一個特殊字符,當發(fā)送端接收到這個特殊字符后就不能再發(fā)送數(shù)據(jù)了。
軟件流控很方便,不需要增加新的硬件,還是以前的TX、RX兩根數(shù)據(jù)線實現(xiàn),但是使用了軟件流控,它本身的字符也是數(shù)據(jù),這個數(shù)據(jù)只不過是說在軟件里把它設(shè)置了一個特殊的含義。如果它是一個全雙工的通訊,在給另一個串口發(fā)送數(shù)據(jù)的時候如果也包含了這樣一個特殊字符,對方就會誤以為我讓它不要再發(fā)送數(shù)據(jù)了,會有一定的概率出現(xiàn)錯誤,而硬件流控就不需要考慮這方面,只需要使用 CTS 和 RTS,所有的數(shù)據(jù)都是由硬件來操作的,因此硬件流控在穩(wěn)定性上更可靠。
?????? 1.3 串口通信參數(shù)
???????? UART 串口通信有幾個重要的參數(shù),分別是起始位、波特率、數(shù)據(jù)位、停止位和奇偶檢驗位(流控,按需),對于兩個使用 UART 串口通信的端口,這些參數(shù)必須匹配,否則通信將無法正常完成。
????????起始位:表示數(shù)據(jù)傳輸?shù)拈_始,默認電平邏輯為 “0” 。
????????波特率:串口通信時的速率,它用單位時間內(nèi)傳輸?shù)亩M制代碼的有效位(bit)數(shù)來表示,其單位為每秒比特數(shù) bit/s(bps)。常見的波特率值有 4800、9600、14400、38400、115200等,數(shù)值越大數(shù)據(jù)傳輸?shù)脑娇欤ㄌ芈蕿?115200 表示每秒鐘傳輸 115200 位數(shù)據(jù)。
????????數(shù)據(jù)位:可能值有 5、6、7、8、9,表示傳輸這幾個 bit 位數(shù)據(jù)。一般取值為 8,因為一個 ASCII 字符值為 8 位。
????????停止位: 表示一幀數(shù)據(jù)的結(jié)束。默認電平邏輯為 “1”。
????????奇偶校驗位:用于接收方對接收到的數(shù)據(jù)進行校驗,校驗 “1” 的位數(shù)為偶數(shù)(偶校驗)或奇數(shù)(奇校驗),以此來校驗數(shù)據(jù)傳送的正確性,使用時不需要此位也可以。
二、新建RS485通信工程
??????? 2.1 項目實現(xiàn)背景信息
????????本文采用的STM32L496VGTX-ali開發(fā)板,該開發(fā)板預(yù)留了擴展接口支持USART4通信,本文將USART4與USB轉(zhuǎn)RS485(CH340)連接,進而與電腦通信。
?????????實物圖:
???????? 擴展接口原理框圖如下:
???????? 另外本人開發(fā)板標注背后擴展口標注的是PA0、PA1,查看其他描述,確定就是PA0、PA1引腳。
????? 現(xiàn)在基于本專欄前面已近實現(xiàn)的串口lpusart通信和及l(fā)cd亮屏工程為基礎(chǔ)創(chuàng)建新工程,并移植了相關(guān)代碼。
?? cubeIDE開發(fā), stm32調(diào)試信息串口通信輸出顯示_py_free的博客-CSDN博客
?? cubeIDE開發(fā), stm32的OLED點亮及字符顯示設(shè)計(基于SPI通信)_py_free的博客-CSDN博客
????????現(xiàn)將在該工程基礎(chǔ)上,實現(xiàn)串口Usart4和lpusart通信,并lcd屏打印其發(fā)送數(shù)據(jù);Usart4發(fā)送'A'/'B'可以點亮及熄滅LED0。
??????? 2.2 項目配置
??????? 雙擊(.ioc)打開cubeMX配置界面,開啟USART4串口功能,配置串口參數(shù)
??????? 并開啟usart4的中斷支持
???????? 點擊保存生成輸出代碼。
??????? 2.3 代碼實現(xiàn)
????????在ICore目錄下,新建rs485文件夾,并在該文件目錄下,創(chuàng)建rs485.h和rs485.c源文件,其實現(xiàn)代碼如下:
??????? rs485.h
#ifndef RS485_RS485_H_
#define RS485_RS485_H_
#include "stm32l4xx_hal.h" //HAL庫文件聲明
extern UART_HandleTypeDef huart4;//聲明USART4的HAL庫結(jié)構(gòu)體
void RS485_printf (char *fmt, ...); //RS485發(fā)送
#endif /* RS485_RS485_H_ */
??????? rs485.c
#include "../usart/usart.h"
#include "main.h"
#include <stdarg.h>
/*
RS485總線通信,使用UART4,這是RS485專用的printf函數(shù)
*/
void RS485_printf (char *fmt, ...)
{
char buff[USART4_REC_LEN+1]; //用于存放轉(zhuǎn)換后的數(shù)據(jù) [長度]
uint16_t i=0;
va_list arg_ptr;
va_start(arg_ptr,fmt);
vsnprintf(buff, USART4_REC_LEN+1,fmt,arg_ptr);//數(shù)據(jù)轉(zhuǎn)換
i=strlen(buff);//得出數(shù)據(jù)長度
if(strlen(buff)>USART4_REC_LEN)i=USART4_REC_LEN;//如果長度大于最大值,則長度等于最大值(多出部分忽略)
HAL_UART_Transmit(&huart4,(uint8_t *)buff,i,0Xffff);//串口發(fā)送函數(shù)(串口號,內(nèi)容,數(shù)量,溢出時間)
va_end(arg_ptr);
}
//所有USART串口的中斷回調(diào)函數(shù)HAL_UART_RxCpltCallback,統(tǒng)一存放在【USART.C】文件中。
??????? 調(diào)整usart.h和usart.c文件,重新實現(xiàn)串口USART4的中斷回調(diào)函數(shù)
??????? usart.h
#ifndef _USART_H_
#define _USART_H_
#include "stm32l4xx_hal.h" //HAL庫文件聲明
#include <string.h>//用于字符串處理的庫
#include "../print/print.h"http://用于printf函數(shù)串口重映射
extern UART_HandleTypeDef hlpuart1;//聲明LPUSART的HAL庫結(jié)構(gòu)體
extern UART_HandleTypeDef huart4;//聲明USART4的HAL庫結(jié)構(gòu)體
#define HLPUSART_REC_LEN 256//定義LPUSART最大接收字節(jié)數(shù)
#define USART4_REC_LEN 256
extern uint8_t HLPUSART_RX_BUF[HLPUSART_REC_LEN];//接收緩沖,最大HLPUSART_REC_LEN個字節(jié).末字節(jié)為換行符
extern uint16_t HLPUSART_RX_STA;//接收狀態(tài)標記
extern uint8_t HLPUSART_NewData;//當前串口中斷接收的1個字節(jié)數(shù)據(jù)的緩存
extern uint8_t USART4_RX_BUF[USART4_REC_LEN];
extern uint16_t USART4_RX_STA;
extern uint8_t USART4_NewData;
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart);//串口中斷回調(diào)函數(shù)聲明
#endif /* _USART_H_ */
??????? usart.c
#include "usart.h"
uint8_t HLPUSART_RX_BUF[HLPUSART_REC_LEN];//接收緩沖,最大HLPUSART_REC_LEN個字節(jié).末字節(jié)為換行符
/*
* bit15:接收到回車(0x0d)時設(shè)置HLPUSART_RX_STA|=0x8000;
* bit14:接收溢出標志,數(shù)據(jù)超出緩存長度時,設(shè)置HLPUSART_RX_STA|=0x4000;
* bit13:預(yù)留
* bit12:預(yù)留
* bit11~0:接收到的有效字節(jié)數(shù)目(0~4095)
*/
uint16_t HLPUSART_RX_STA=0;接收狀態(tài)標記//bit15:接收完成標志,bit14:接收到回車(0x0d),bit13~0:接收到的有效字節(jié)數(shù)目
uint8_t HLPUSART_NewData;//當前串口中斷接收的1個字節(jié)數(shù)據(jù)的緩存
uint8_t USART4_RX_BUF[USART4_REC_LEN];
uint16_t USART4_RX_STA=0;
uint8_t USART4_NewData;
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)//串口中斷回調(diào)函數(shù)
{
if(huart ==&hlpuart1)//判斷中斷來源(串口1:USB轉(zhuǎn)串口)
{
if(HLPUSART_NewData==0x0d){//回車標記
HLPUSART_RX_STA|=0x8000;//標記接到回車
}else{
if((HLPUSART_RX_STA&0X0FFF)<HLPUSART_REC_LEN){
HLPUSART_RX_BUF[HLPUSART_RX_STA&0X0FFF]=HLPUSART_NewData; //將收到的數(shù)據(jù)放入數(shù)組
HLPUSART_RX_STA++; //數(shù)據(jù)長度計數(shù)加1
}else{
HLPUSART_RX_STA|=0x4000;//數(shù)據(jù)超出緩存長度,標記溢出
}
}
HAL_UART_Receive_IT(&hlpuart1,(uint8_t *)&HLPUSART_NewData,1); //再開啟接收中斷
}
if(huart ==&huart4)//判斷中斷來源(串口4:USB轉(zhuǎn)串口)
{
// printf("%c",USART4_NewData);
if(USART4_NewData==0x0d){//回車標記
USART4_RX_STA|=0x8000;//標記接到回車
}else{
if((USART4_RX_STA&0X0FFF)<USART4_REC_LEN){
USART4_RX_BUF[USART4_RX_STA&0X0FFF]=USART4_NewData; //將收到的數(shù)據(jù)放入數(shù)組
USART4_RX_STA++; //數(shù)據(jù)長度計數(shù)加1
}else{
USART4_RX_STA|=0x4000;//數(shù)據(jù)超出緩存長度,標記溢出
}
}
HAL_UART_Receive_IT(&huart4,(uint8_t *)&USART4_NewData,1); //再開啟接收中斷
}
}
?? 三、RS485驅(qū)動調(diào)用及測試
??????? 3.1 接口調(diào)用
??????? 在main.c文件中加入rs485.h頭文件支持
/* USER CODE BEGIN Includes */
#include "../../ICore/key/key.h"
#include "../../ICore/led/led.h"
#include "../../ICore/print/print.h"
#include "../../ICore/usart/usart.h"
#include "../../ICore/rs485/rs485.h"
#include "../../ICore/oled/oled.h"
/* USER CODE END Includes */
??????? 在主函數(shù)初始化中調(diào)整加入usart4初始化設(shè)定
/* USER CODE BEGIN 2 */
ResetPrintInit(&hlpuart1);
HAL_UART_Receive_IT(&hlpuart1,(uint8_t *)&HLPUSART_NewData, 1); //再開啟接收中斷
HLPUSART_RX_STA = 0;
HAL_UART_Receive_IT(&huart4,(uint8_t *)&USART4_NewData,1); //開啟串口4接收中斷
USART4_RX_STA = 0;
//
OLED_init();
//設(shè)置OLED藍色背景顯示
BSP_LCD_Clear_DMA(LCD_DISP_BLUE);
printf("OLED_Clear_DMA\r\n");
/* USER CODE END 2 */
??????? 在主函數(shù)循環(huán)體中,修改代碼實現(xiàn)對usart4接收數(shù)據(jù)處理
/* USER CODE BEGIN WHILE */
while (1)
{
if(HLPUSART_RX_STA&0xC000){//溢出或換行,重新開始
RS485_printf("%.*s\r\n",HLPUSART_RX_STA&0X0FFF, HLPUSART_RX_BUF);
OLED_printf(10,10,"%.*s",HLPUSART_RX_STA&0X0FFF, HLPUSART_RX_BUF);
HLPUSART_RX_STA=0;//接收錯誤,重新開始
HAL_Delay(100);//等待
}
if(USART4_RX_STA&0xC000){//溢出或換行,重新開始
if(1==(USART4_RX_STA&0x0FFF))
{
switch (USART4_RX_BUF[0]){
case 'A':
set_led0_val(1);
break;
case 'B':
set_led0_val(0);
break;
default:
break;
}
}
printf("%.*s\r\n",USART4_RX_STA&0X0FFF, USART4_RX_BUF);
OLED_printf(10,42,"%.*s",USART4_RX_STA&0X0FFF, USART4_RX_BUF);
USART4_RX_STA=0;//接收錯誤,重新開始
HAL_Delay(100);//等待
}
/* USER CODE END WHILE */
?????? 3.2 編譯及下載及測試
???????? LED0在發(fā)送'A'字符是關(guān)閉(LED燈是低位有效)文章來源:http://www.zghlxwxcb.cn/news/detail-807559.html
文章來源地址http://www.zghlxwxcb.cn/news/detail-807559.html
到了這里,關(guān)于STM32CubeIDE開發(fā)(二十二), stm32的RS485/232串口通信開發(fā)要點的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!