梳理STM32F429之通信傳輸部分---NO.7 RS485 通訊

梳理STM32F429之通信傳輸部分---NO.6 RS485 通訊

目錄

一、RS-485 通訊協議簡介

RS-485 的物理層

二、RS-485—雙機通訊實驗

1、硬件設計

2、軟件設計

3、下載驗證


一、RS-485 通訊協議簡介

       與 CAN 類似, RS-485 是一種工業控制環境中常用的通訊協議,它具有抗干擾能力強、傳輸距離遠的特點。 RS-485 通訊協議由 RS-232 協議改進而來,協議層不變,只是改進了物理層,因而保留了串口通訊協議應用簡單的特點。

RS-485 的物理層

       從《CAN—通訊實驗》章節中瞭解到,差分信號線具有很強的干擾能力,特別適合應用於電磁環境複雜的工業控制環境中, RS-485 協議主要是把 RS-232 的信號改進成差分信號,從而大大提高了抗干擾特性,它的通訊網絡示意圖見下圖。

       對比 CAN 通訊網絡,可發現它們的網絡結構組成是類似的,每個節點都是由一個通訊控制器和一個收發器組成,在 RS-485 通訊網絡中,節點中的串口控制器使用 RX 與 TX信號線連接到收發器上,而收發器通過差分線連接到網絡總線,串口控制器與收發器之間一般使用 TTL 信號傳輸,收發器與總線則使用差分信號來傳輸。發送數據時,串口控制器的 TX 信號經過收發器轉換成差分信號傳輸到總線上,而接收數據時,收發器把總線上的差分信號轉化成 TTL 信號通過 RX 引腳傳輸到串口控制器中。
       RS-485 通訊網絡的最大傳輸距離可達 1200 米,總線上可掛載 128 個通訊節點,而由於 RS-485 網絡只有一對差分信號線,它使用差分信號來表達邏輯,當 AB 兩線間的電壓差爲-6V~-2V 時表示邏輯 1,當電壓差爲+2V~+6V 表示邏輯 0,在同一時刻只能表達一個信號,所以它的通訊是半雙工形式的,它與 RS-232 通訊協議的特性對比見下圖。

       RS-485 與 RS-232 的差異只體現在物理層上,它們的協議層是相同的,也是使用串口數據包的形式傳輸數據。而由於 RS-485 具有強大的組網功能,人們在基礎協議之上還制定了 MODBUS 協議,被廣泛應用在工業控制網絡中。此處說的基礎協議是指前面串口章節中講解的,僅封裝了基本數據包格式的協議(基於數據位),而 MODBUS 協議是使用基本數據包組合成通訊幀格式的高層應用協議(基於數據包或字節)。感興趣的讀者可查找MODBUS 協議的相關資料瞭解。
       由於 RS-485 與 RS-232 的協議層沒有區別,進行通訊時,我們同樣是使用 STM32 的USART 外設作爲通訊節點中的串口控制器,再外接一個 RS-485 收發器芯片把 USART 外設的 TTL 電平信號轉化成 RS-485 的差分信號即可。

二、RS-485—雙機通訊實驗

       本小節演示如何使用 STM32 的 USART 控制器與 MAX485 收發器,在兩個設備之間使用 RS-485 協議進行通訊,本實驗中使用了兩個實驗板,無法像 CAN 實驗那樣使用迴環測試(把 STM32 USART 外設的 TXD 引腳使用杜邦線連接到 RXD 引腳可進行自收發測試,不過這樣的通訊不經過 RS-485 收發器,跟普通 TTL 串口實驗沒有區別),本教程主要以“ USART—485 通訊”工程進行講解。

1、硬件設計

      上圖中的是兩個實驗板的硬件連接。在單個實驗板中,作爲串口控制器的 STM32從 USART 外設引出 TX 和 RX 兩個引腳與 RS-485 收發器 MAX485 相連,收發器使用它的A 和 B 引腳連接到 RS-485 總線網絡中。爲了方便使用,我們每個實驗板引出的 A 和 B 之間都連接了 1 個 120 歐的電阻作爲 RS-485 總線的端電阻,所以要注意如果您要把實驗板作爲一個普通節點連接到現有的 RS-485 總線時,是不應添加該電阻的!
      由於 485 只能以半雙工的形式工作,所以需要切換狀態, MAX485 芯片中有“ RE”和“ DE”兩個引腳,用於控制 485 芯片的收發工作狀態的,當 RE 引腳爲低電平時, 485 芯片處於接收狀態,當 DE 引腳爲高電平時芯片處於發送狀態。實驗板中使用了 STM32 的PB8 直接連接到這兩個引腳上,所以通過控制 PB8 的輸出電平即可控制 485 的收發狀態。

2、軟件設計

(1)編程要點

  1. 初始化 485 通訊使用的 USART 外設及相關引腳;
  2. 編寫控制 MAX485 芯片進行收發數據的函數;
  3. 編寫測試程序,收發數據。

(2)代碼分析

NO.1 485 硬件相關宏定義我們把 485 硬件相關的配置都以宏的形式定義到 “ bsp_485.h”文件中,見下面的代碼清單。

/*USART 號、時鐘、波特率*/
#define RS485_USART USART2
#define RS485_USART_CLK RCC_APB1Periph_USART2
#define RS485_USART_BAUDRATE 115200
/*RX 引腳*/
#define RS485_USART_RX_GPIO_PORT GPIOD
#define RS485_USART_RX_GPIO_CLK RCC_AHB1Periph_GPIOD
#define RS485_USART_RX_PIN GPIO_Pin_6
#define RS485_USART_RX_AF GPIO_AF_USART2
#define RS485_USART_RX_SOURCE GPIO_PinSource6
/*TX 引腳*/
#define RS485_USART_TX_GPIO_PORT GPIOD
#define RS485_USART_TX_GPIO_CLK RCC_AHB1Periph_GPIOD
#define RS485_USART_TX_PIN GPIO_Pin_5
#define RS485_USART_TX_AF GPIO_AF_USART2

#define RS485_USART_TX_SOURCE GPIO_PinSource5
/*485 收發控制引腳*/
#define RS485_RE_GPIO_PORT GPIOB
#define RS485_RE_GPIO_CLK RCC_AHB1Periph_GPIOB
#define RS485_RE_PIN GPIO_Pin_8
/*中斷相關*/
#define RS485_INT_IRQ USART2_IRQn
#define RS485_IRQHandler USART2_IRQHandler

      以上代碼根據硬件連接, 把與 485 通訊使用的 USART 外設號引腳號引腳源以及複用功能映射都以宏封裝起來,並且定義了接收中斷的中斷向量中斷服務函數,我們通過中斷來獲知接收數據

NO.2 初始化 485 的 USART 配置

利用上面的宏,編寫 485 的 USART 初始化函數,見下面的代碼清單。

/*
* 函數名: RS485_Config
* 描述 : USART GPIO 配置,工作模式配置
* 輸入 :無
* 輸出 : 無
* 調用 :外部調用
*/
void RS485_Config(void)
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
/* 配置 USART 時鐘 */
RCC_AHB1PeriphClockCmd(RS485_USART_RX_GPIO_CLK|
RS485_USART_TX_GPIO_CLK|
RS485_RE_GPIO_CLK, ENABLE);
RCC_APB1PeriphClockCmd(RS485_USART_CLK, ENABLE);
/* TX 引腳源*/
GPIO_PinAFConfig(RS485_USART_RX_GPIO_PORT,RS485_USART_RX_SOURCE, RS485_USART_RX_AF);
/* RX 引腳源*/
GPIO_PinAFConfig(RS485_USART_TX_GPIO_PORT,RS485_USART_TX_SOURCE,RS485_USART_TX_AF);
/* USART GPIO 配置 */
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
/*TX*/
GPIO_InitStructure.GPIO_Pin = RS485_USART_TX_PIN;
GPIO_Init(RS485_USART_TX_GPIO_PORT, &GPIO_InitStructure);
/*RX */
GPIO_InitStructure.GPIO_Pin = RS485_USART_RX_PIN;
GPIO_Init(RS485_USART_RX_GPIO_PORT, &GPIO_InitStructure);
/* 485 收發控制管腳 */
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStructure.GPIO_Pin = RS485_RE_PIN ;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(RS485_RE_GPIO_PORT, &GPIO_InitStructure);
/* USART 模式配置*/
USART_InitStructure.USART_BaudRate = RS485_USART_BAUDRATE;
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_Rx | USART_Mode_Tx;
USART_Init(RS485_USART, &USART_InitStructure);
/*使能 USART*/
USART_Cmd(RS485_USART, ENABLE);
/*配置中斷優先級*/
NVIC_Configuration();
/* 使能串口接收中斷 */
USART_ITConfig(RS485_USART, USART_IT_RXNE, ENABLE);
/*控制 485 芯片進入接收模式*/
GPIO_ResetBits(RS485_RE_GPIO_PORT,RS485_RE_PIN);
}

       與所有使用到 GPIO 的外設一樣,都要先把使用到的 GPIO 引腳模式初始化,配置好複用功能,其中用於控制 MAX485 芯片的收發狀態的引腳被初始化成普通推輓輸出模式,以便手動控制它的電平輸出,切換狀態。 485 使用到的 USART 也需要配置好波特率、有效字長、停止位及校驗位等基本參數,在通訊中,兩個 485 節點的串口參數應一致,否則會導致通訊解包錯誤。在實驗中還使能了串口的接收中斷功能,當檢測到新的數據時,進入中斷服務函數中獲取數據

NO.3 使用中斷接收數據
       接下來我們編寫在 USART 中斷服務函數中接收數據的相關過程,見代碼清單,其中的 bsp_RS485_IRQHandler 函數直接被 bsp_stm32f4xx_it.c 文件的 USART 中斷服務函數調用,不在此列出。

//中斷緩存串口數據
#define UART_BUFF_SIZE 1024
volatile uint16_t uart_p = 0;
uint8_t uart_buff[UART_BUFF_SIZE];
void bsp_RS485_IRQHandler(void)
{
if (uart_p<UART_BUFF_SIZE) {
if (USART_GetITStatus(RS485_USART, USART_IT_RXNE) != RESET) {
uart_buff[uart_p] = USART_ReceiveData(RS485_USART);
uart_p++;
USART_ClearITPendingBit(RS485_USART, USART_IT_RXNE);
}
} else {
USART_ClearITPendingBit(RS485_USART, USART_IT_RXNE);
}
}

//獲取接收到的數據和長度
char *get_rebuff(uint16_t *len)
{
*len = uart_p;
return (char *)&uart_buff;
}
//清空緩衝區
void clean_rebuff(void)
{
uint16_t i=UART_BUFF_SIZE+1;
uart_p = 0;
while (i)
uart_buff[--i]=0;
}

       這個數據接收過程主要思路是使用了接收緩衝區,當 USART 有新的數據引起中斷時,調用庫函數 USART_ReceiveData 把新數據讀取到緩衝區數組 uart_buff 中,其中 get_rebuff函數可以用於獲緩衝區中有效數據的長度,而 clean_rebuff 函數可以用於對緩衝區整體清 0,這些函數配合使用,實現了簡單的串口接收緩衝機制。這部分串口數據接收的過程跟 485收發器無關,是串口協議通用的。

NO.4 切換收發狀態
       在前面我們瞭解到 RS-485 是半雙工通訊協議,發送數據和接收數據需要分時進行,所以需要經常切換收發狀態。而 MAX485 收發器根據其“ RE”和“ DE”引腳的外部電平信號切換收發狀態,所以控制與其相連的 STM32 普通 IO 電平即可控制收尾,爲簡便起見,我們把收發狀態切換定義成了宏。

// 簡單的延時
static void RS485_delay(__IO u32 nCount)
{
for (; nCount != 0; nCount--);
}

/*控制收發引腳*/
//進入接收模式,必須要有延時等待 485 處理完數據
#define RS485_RX_EN() RS485_delay(1000);\
GPIO_ResetBits(RS485_RE_GPIO_PORT,RS485_RE_PIN); \
RS485_delay(1000);

//進入發送模式,必須要有延時等待 485 處理完數據
#define RS485_TX_EN() RS485_delay(1000); \
GPIO_SetBits(RS485_RE_GPIO_PORT,RS485_RE_PIN);\
RS485_delay(1000);

       這兩個宏中,主要是在控制電平輸出前後加了一小段時間延時,這是爲了給 MAX485芯片預留響應時間,因爲 STM32 的引腳狀態電平變換後, MAX485 芯片可能存在響應延時。例如,當 STM32 控制自己的引腳電平輸出高電平(控制成發送狀態),然後立即通過 TX 信號線發送數據給 MAX485 芯片,而 MAX485 芯片由於狀態不能馬上切換,會導致丟失了部分 STM32 傳送過來的數據,造成錯誤。

NO.5 發送數據
       STM32 使用 485 發送數據的過程也與普通的 USART 發送數據過程差不多,我們定義了一個 RS485_SendByte 函數來發送一個字節的數據內容,見代碼清單。

/***************** 發送一個字符 **********************/
//使用單字節數據發送前要使能發送引腳,發送後要使能接收引腳。
void RS485_SendByte( uint8_t ch )
{
/* 發送一個字節數據到 USART1 */
USART_SendData(RS485_USART,ch);
/* 等待發送完畢 */
while (USART_GetFlagStatus(RS485_USART, USART_FLAG_TXE) == RESET);
}

       上述代碼中就是直接調用了 STM32 庫函數 USART_SendData 把要發送的數據寫入到USART 的數據寄存器,然後檢查標誌位等待發送完成。在調用 RS485_SendByte 函數前,需要先使用前面提到的切換收發狀態宏,把MAX485 切換到發送模式, STM32 發出的數據才能正常傳輸到 485 網絡總線上,當發送完數據的時候,應重新把 MAX485 切換回接收模式,以便獲取網絡總線上的數據。
(3)main 函數
        最後我們來閱讀 main 函數,瞭解整個通訊過程,見代碼清單。這個 main 函數的整體設計思路是,實驗板檢測自身的按鍵狀態,若按鍵被按下,則通過 485 發送 256 個測試數據到網絡總線上,若自身接收到總線上的 256 個數據,則把這些數據作爲調試信息打印到電腦端。所以,如果把這樣的程序分別應用到 485 總線上的兩個通訊節點時,就可以通過按鍵控制互相發送數據了。
 

/**
* @brief 主函數
* @param 無
* @retval 無
*/
int main(void)
{
char *pbuf;
uint16_t len;
LED_GPIO_Config();
/*初始化 USART1*/
Debug_USART_Config();
/*初始化 485 使用的串口,使用中斷模式接收*/
RS485_Config();
LED_BLUE;
Key_GPIO_Config();
printf("\r\n 歡迎使用 STM32 F429 開發板。 \r\n");
printf("\r\n  F429 485 通訊實驗例程\r\n");
printf("\r\n 實驗步驟: \r\n");
printf("\r\n 1.使用導線連接好兩個 485 通訊設備\r\n");
printf("\r\n 2.使用跳線帽連接好: 3V3<--->CAN/485_3V3,485-RX--PD5,485-TX--PD6 \r\n");
printf("\r\n 3.若使用兩個開發板進行實驗,給兩個開發板都下載本程序即可。 \r\n");
printf("\r\n 4.準備好後,按下其中一個開發板的 KEY1 鍵,會使用 485 向外發送 0-255 的數字 \r\n");
printf("\r\n 5.若開發板的 485 接收到 256 個字節數據,會把數據以 16 進制形式打印出來。 \r\n");
while (1) {
/*按一次按鍵發送一次數據*/
if ( Key_Scan(KEY1_GPIO_PORT,KEY1_PIN) == KEY_ON) {
uint16_t i;
LED_BLUE;
//切換到發送狀態
RS485_TX_EN();
for (i=0; i<=0xff; i++) {
RS485_SendByte(i); //發送數據
}
/*加短暫延時,保證 485 發送數據完畢*/
Delay(0xFFF);
RS485_RX_EN();//切換回接收狀態
LED_GREEN;
printf("\r\n 發送數據成功! \r\n"); //使用調試串口打印調試信息到終端
} else {
LED_BLUE;
pbuf = get_rebuff(&len);
if (len>=256) {
LED_GREEN;
printf("\r\n 接收到長度爲%d 的數據\r\n",len);
RS485_DEBUG_ARRAY((uint8_t*)pbuf,len);
clean_rebuff();
}
}
}
}

        在 main 函數中,首先初始化了 LED、按鍵以及調試使用的串口,再調用前面分析的RS485_Config 函數初始化了 RS-485 通訊使用的串口工作模式。
       初始化後 485 就進入了接收模式,當接收到數據的時候會進入中斷並把數據存儲到接收緩衝數組中,我們在 main 函數的 while 循環中(else 部分)調用 get_rebuff 來查看該緩衝區的狀態,若接收到 256 個數據就把這些數據通過調試串口打印到電腦端,然後清空緩衝區。
       在 while 循環中,還檢測了按鍵的狀態,若按鍵被按下,就把 MAX485 芯片切換到發送狀態並調用 RS485_SendByte 函數發送測試數據 0x00-0xFF,發送完畢後切換回接收狀態以檢測總線的數據。

3、下載驗證

下載驗證這個 485 通訊實驗需要您有兩個實驗板,操作步驟如下:

  1. 按照“硬件設計”小節中的圖例連接兩個板子的 485 總線;
  2. 使用跳線帽連接 : 485_TX<--->PD6、 485_RX<--->PD5、 3V3<--->CAN/485_3V3 ;
  3. 用 USB 線使實驗板“ USB TO UART”接口跟電腦連接起來,在電腦端打開串口調試助手,編譯本章配套的程序,並給兩個板子都下載該程序,然後復位。
  4. 復位後在串口調試助手應看到 485 測試的調試信息,按一下其中一個實驗板上的KEY1 按鍵,另一個實驗板會接收到報文,在串口調試助手可以看到相應的發送和接收的信息。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章