第41章 RS-485通訊實驗—零死角玩轉STM32-F429系列


 

41     RS-485通訊實驗

全套200集視頻教程和1000PDF教程請到秉火論壇下載:www.firebbs.cn

野火視頻教程優酷觀看網址:http://i.youku.com/firege

 

 

 

本章參考資料:《STM32F4xx中文參考手冊》USART章節。

學習本章時,配合本書前面的《USART—串口通訊》及《CAN—通訊實驗》章節進行對比學習,效果更佳。

關於實驗板中使用的MAX485收發器資料可查閱《MAX485》規格書了解。

41.1 RS-485通訊協議簡介

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

41.1.1 RS-485的物理層

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

411 RS-485通訊網絡示意圖

對比CAN通訊網絡,可發現它們的網絡結構組成是類似的,每個節點都是由一個通訊控制器和一個收發器組成,在RS-485通訊網絡中,節點中的串口控制器使用RXTX信號線連接到收發器上,而收發器通過差分線連接到網絡總線,串口控制器與收發器之間一般使用TTL信號傳輸,收發器與總線則使用差分信號來傳輸。發送數據時,串口控制器的TX信號經過收發器轉換成差分信號傳輸到總線上,而接收數據時,收發器把總線上的差分信號轉化成TTL信號通過RX引腳傳輸到串口控制器中。

RS-485通訊網絡的最大傳輸距離可達1200米,總線上可掛載128個通訊節點,而由於RS-485網絡只有一對差分信號線,它使用差分信號來表達邏輯,當AB兩線間的電壓差為-6V~-2V時表示邏輯1,當電壓差為+2V~+6V表示邏輯0,在同一時刻只能表達一個信號,所以它的通訊是半雙工形式的,它與RS-232通訊協議的特性對比見圖 411

411 RS-232/422/485 標准對比

通訊標准

信號線

通訊方向

電平標准

通訊距離

通訊節點數

RS232

單端TXDRXDGND

全雙工

邏輯1-15V~-3V

邏輯0+3V~+15V

100米以內

只有兩個節點

RS485

差分線AB

半雙工

邏輯1-6V~-2V

邏輯0+2V~+6V

1200

支持多個節點。支持多個主設備,任意節點間可互相通訊

RS-485RS-232的差異只體現在物理層上,它們的協議層是相同的,也是使用串口數據包的形式傳輸數據。而由於RS-485具有強大的組網功能,人們在基礎協議之上還制定了MODBUS協議,被廣泛應用在工業控制網絡中。此處說的基礎協議是指前面串口章節中講解的,僅封裝了基本數據包格式的協議(基於數據位),而MODBUS協議是使用基本數據包組合成通訊幀格式的高層應用協議(基於數據包或字節)。感興趣的讀者可查找MODBUS協議的相關資料了解。

由於RS-485RS-232的協議層沒有區別,進行通訊時,我們同樣是使用STM32USART外設作為通訊節點中的串口控制器,再外接一個RS-485收發器芯片把USART外設的TTL電平信號轉化成RS-485的差分信號即可。

41.2 RS-485—雙機通訊實驗

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

41.2.1 硬件設計

412 CAN通訊實驗硬件連接圖

4016中的是兩個實驗板的硬件連接。在單個實驗板中,作為串口控制器的STM32USART外設引出TXRX兩個引腳與RS-485收發器MAX485相連,收發器使用它的AB引腳連接到RS-485總線網絡中。為了方便使用,我們每個實驗板引出的AB之間都連接了1120歐的電阻作為RS-485總線的端電阻,所以要注意如果您要把實驗板作為一個普通節點連接到現有的RS-485總線時,是不應添加該電阻的!

由於485只能以半雙工的形式工作,所以需要切換狀態,MAX485芯片中有"RE"和"DE"兩個引腳,用於控制485芯片的收發工作狀態的,當RE引腳為低電平時,485芯片處於接收狀態,當DE引腳為高電平時芯片處於發送狀態。實驗板中使用了STM32PD11直接連接到這兩個引腳上,所以通過控制PD11的輸出電平即可控制485的收發狀態。

要注意的是,由於我們的實驗板485使用的信號線與液晶屏共用了,為防止干擾,平時我們默認是不給485收發器供電的,使用485的時候一定要把485接線端子旁邊的"C/4-5V"排針使用跳線帽與"5V"排針連接起來進行供電,並且把液晶屏從板子上拔下來;而又由於實驗板的RS-232RS-485通訊實驗都使用STM32的同一個USART外設及收發引腳,實驗時注意必須要把STM32的"PD5引腳"與MAX485的"485_D"及"PD6"與"485_R"使用跳線帽連接起來(這些信號都在485接線端子旁邊的排針上)

要實現通訊,我們還要使用導線把實驗板引出的AB兩條總線連接起來,才能構成完整的網絡。實驗板之間AA連接,BB連接即可。

41.2.2 軟件設計

為了使工程更加有條理,我們把RS485控制相關的代碼獨立分開存儲,方便以后移植。在"串口實驗"之上新建"bsp_485.c"及"bsp_485.h"文件,這些文件也可根據您的喜好命名,它們不屬於STM32標准庫的內容,是由我們自己根據應用需要編寫的。這個實驗的底層STM32驅動與串口控制區別不大,上層實驗功能上與CAN實驗類似。

1.    編程要點

(1)    初始化485通訊使用的USART外設及相關引腳;

(2)    編寫控制MAX485芯片進行收發數據的函數;

(3)    編寫測試程序,收發數據。

2.    代碼分析
485硬件相關宏定義

我們把485硬件相關的配置都以宏的形式定義到"bsp_485.h"文件中,見代碼清單 242

代碼清單 411 485硬件配置相關的宏(bsp_485.h文件)

1 /*USART號、時鍾、波特率*/

2 #define RS485_USART USART2

3 #define RS485_USART_CLK RCC_APB1Periph_USART2

4 #define RS485_USART_BAUDRATE 115200

5

6 /*RX引腳*/

7 #define RS485_USART_RX_GPIO_PORT GPIOD

8 #define RS485_USART_RX_GPIO_CLK RCC_AHB1Periph_GPIOD

9 #define RS485_USART_RX_PIN GPIO_Pin_6

10 #define RS485_USART_RX_AF GPIO_AF_USART2

11 #define RS485_USART_RX_SOURCE GPIO_PinSource6

12

13 /*TX引腳*/

14 #define RS485_USART_TX_GPIO_PORT GPIOD

15 #define RS485_USART_TX_GPIO_CLK RCC_AHB1Periph_GPIOD

16 #define RS485_USART_TX_PIN GPIO_Pin_5

17 #define RS485_USART_TX_AF GPIO_AF_USART2

18 #define RS485_USART_TX_SOURCE GPIO_PinSource5

19

20 /*485收發控制引腳*/

21 #define RS485_RE_GPIO_PORT GPIOD

22 #define RS485_RE_GPIO_CLK RCC_AHB1Periph_GPIOD

23 #define RS485_RE_PIN GPIO_Pin_11

24

25 /*中斷相關*/

26 #define RS485_INT_IRQ USART2_IRQn

27 #define RS485_IRQHandler USART2_IRQHandler

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

初始化485的USART配置

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

代碼清單 412 RS485的初始化函數(bsp_485.c文件)

1

2 /*

3 * 函數名:RS485_Config

4 * 描述USART GPIO 配置,工作模式配置

5 * 輸入:無

6 * 輸出 :

7 * 調用:外部調用

8 */

9 void RS485_Config(void)

10 {

11 GPIO_InitTypeDef GPIO_InitStructure;

12 USART_InitTypeDef USART_InitStructure;

13

14 /* 配置 USART時鍾 */

15 RCC_AHB1PeriphClockCmd(RS485_USART_RX_GPIO_CLK|

16 RS485_USART_TX_GPIO_CLK|

17 RS485_RE_GPIO_CLK, ENABLE);

18 RCC_APB1PeriphClockCmd(RS485_USART_CLK, ENABLE);

19

20 /* TX 引腳源*/

21 GPIO_PinAFConfig(RS485_USART_RX_GPIO_PORT,RS485_USART_RX_SOURCE, RS485_USART_RX_AF);

22

23 /* RX 引腳源*/

24 GPIO_PinAFConfig(RS485_USART_TX_GPIO_PORT,RS485_USART_TX_SOURCE,RS485_USART_TX_AF);

25

26 /* USART GPIO配置 */

27 GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;

28 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;

29 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;

30 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

31

32 /*TX*/

33 GPIO_InitStructure.GPIO_Pin = RS485_USART_TX_PIN;

34 GPIO_Init(RS485_USART_TX_GPIO_PORT, &GPIO_InitStructure);

35

36 /*RX */

37 GPIO_InitStructure.GPIO_Pin = RS485_USART_RX_PIN;

38 GPIO_Init(RS485_USART_RX_GPIO_PORT, &GPIO_InitStructure);

39

40 /* 485收發控制管腳 */

41 GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;

42 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;

43 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;

44 GPIO_InitStructure.GPIO_Pin = RS485_RE_PIN ;

45 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

46 GPIO_Init(RS485_RE_GPIO_PORT, &GPIO_InitStructure);

47

48 /* USART 模式配置*/

49 USART_InitStructure.USART_BaudRate = RS485_USART_BAUDRATE;

50 USART_InitStructure.USART_WordLength = USART_WordLength_8b;

51 USART_InitStructure.USART_StopBits = USART_StopBits_1;

52 USART_InitStructure.USART_Parity = USART_Parity_No ;

53 USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;

54 USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;

55

56 USART_Init(RS485_USART, &USART_InitStructure);

57 /*使能USART*/

58 USART_Cmd(RS485_USART, ENABLE);

59

60 /*配置中斷優先級*/

61 NVIC_Configuration();

62 /* 使能串口接收中斷 */

63 USART_ITConfig(RS485_USART, USART_IT_RXNE, ENABLE);

64

65 /*控制485芯片進入接收模式*/

66 GPIO_ResetBits(RS485_RE_GPIO_PORT,RS485_RE_PIN);

67 }

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

使用中斷接收數據

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

代碼清單 413 中斷接收數據的過程(bsp_485.c文件)

1 //中斷緩存串口數據

2 #define UART_BUFF_SIZE 1024

3 volatile uint16_t uart_p = 0;

4 uint8_t uart_buff[UART_BUFF_SIZE];

5

6 void bsp_RS485_IRQHandler(void)

7 {

8 if (uart_p<UART_BUFF_SIZE) {

9 if (USART_GetITStatus(RS485_USART, USART_IT_RXNE) != RESET) {

10 uart_buff[uart_p] = USART_ReceiveData(RS485_USART);

11 uart_p++;

12

13 USART_ClearITPendingBit(RS485_USART, USART_IT_RXNE);

14 }

15 } else {

16 USART_ClearITPendingBit(RS485_USART, USART_IT_RXNE);

17 }

18 }

19

20 //獲取接收到的數據和長度

21 char *get_rebuff(uint16_t *len)

22 {

23 *len = uart_p;

24 return (char *)&uart_buff;

25 }

26

27 //清空緩沖區

28 void clean_rebuff(void)

29 {

30 uint16_t i=UART_BUFF_SIZE+1;

31 uart_p = 0;

32 while (i)

33 uart_buff[--i]=0;

34 }

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

切換收發狀態

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

代碼清單 414 切換收發狀態(bsp_485.h文件)

1 /// 簡單的延時

2 static void RS485_delay(__IO u32 nCount)

3 {

4 for (; nCount != 0; nCount--);

5 }

6

7 /*制收發引腳*/

8 //進入接收模式,必須要有延時等待485處理完數據

9 #define RS485_RX_EN() RS485_delay(1000);\

10     GPIO_ResetBits(RS485_RE_GPIO_PORT,RS485_RE_PIN); \

11 RS485_delay(1000);

12 //進入發送模式,必須要有延時等待485處理完數據

13 #define RS485_TX_EN() RS485_delay(1000); \

14 GPIO_SetBits(RS485_RE_GPIO_PORT,RS485_RE_PIN);\

15 RS485_delay(1000);

16

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

發送數據

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

代碼清單 415 發送數據(bsp_485.c文件)

1

2 /***************** 發送一個字符 **********************/

3 //使用單字節數據發送前要使能發送引腳,發送后要使能接收引腳。

4 void RS485_SendByte( uint8_t ch )

5 {

6 /* 發送一個字節數據到USART1 */

7 USART_SendData(RS485_USART,ch);

8 /* 等待發送完畢 */

9 while (USART_GetFlagStatus(RS485_USART, USART_FLAG_TXE) == RESET);

10

11 }

12

上述代碼中就是直接調用了STM32庫函數USART_SendData把要發送的數據寫入到USART的數據寄存器,然后檢查標志位等待發送完成。

在調用RS485_SendByte 函數前,需要先使用前面提到的切換收發狀態宏,把MAX485切換到發送模式,STM32發出的數據才能正常傳輸到485網絡總線上,當發送完數據的時候,應重新把MAX485切換回接收模式,以便獲取網絡總線上的數據。

3.    main函數

最后我們來閱讀main函數,了解整個通訊過程,見代碼清單 2414。這個main函數的整體設計思路是,實驗板檢測自身的按鍵狀態,若按鍵被按下,則通過485發送256個測試數據到網絡總線上,若自身接收到總線上的256個數據,則把這些數據作為調試信息打印到電腦端。所以,如果把這樣的程序分別應用到485總線上的兩個通訊節點時,就可以通過按鍵控制互相發送數據了。

代碼清單 416 main函數

1

2 /**

3 * @brief 主函數

4 * @param

5 * @retval

6 */

7 int main(void)

8 {

9

10 char *pbuf;

11 uint16_t len;

12

13 LED_GPIO_Config();

14

15 /*初始化USART1*/

16 Debug_USART_Config();

17

18 /*初始化485使用的串口,使用中斷模式接收*/

19 RS485_Config();

20

21 LED_BLUE;

22

23 Key_GPIO_Config();

24

25 printf("\r\n歡迎使用秉火 STM32 F429 開發板。\r\n");

26 printf("\r\n秉火F429 485通訊實驗例程\r\n");

27

28 printf("\r\n實驗步驟:\r\n");

29

30 printf("\r\n 1.使用導線連接好兩個485通訊設備\r\n");

31 printf("\r\n 2.使用跳線帽連接好:5v --- C/4-5V,485-D --- PD5,485-R ---PD6 \r\n");

32printf("\r\n 3.若使用兩個秉火開發板進行實驗,給兩個開發板都下載本程序即可。\r\n");

33 printf("\r\n 4.准備好后,按下其中一個開發板的KEY1鍵,會使用485向外發送0-255的數字 \r\n");

34 printf("\r\n 5.若開發板的485接收到256個字節數據,會把數據以16進制形式打印出來。 \r\n");

35

36 while (1) {

37 /*按一次按鍵發送一次數據*/

38 if ( Key_Scan(KEY1_GPIO_PORT,KEY1_PIN) == KEY_ON) {

39 uint16_t i;

40 LED_BLUE;

41         //切換到發送狀態    

42 RS485_TX_EN();

43

44 for (i=0; i<=0xff; i++) {

45 RS485_SendByte(i); //發送數據

46 }

47

48 /*加短暫延時,保證485發送數據完畢*/

49 Delay(0xFFF);

50 RS485_RX_EN();//切換回接收狀態

51

52 LED_GREEN;

53 printf("\r\n發送數據成功!\r\n"); //使用調試串口打印調試信息到終端

54

55 } else {

56 LED_BLUE;

57

58 pbuf = get_rebuff(&len);

59 if (len>=256) {

60 LED_GREEN;

61 printf("\r\n接收到長度為%d的數據\r\n",len);

62 RS485_DEBUG_ARRAY((uint8_t*)pbuf,len);

63 clean_rebuff();

64 }

65 }

66 }

67 }

main函數中,首先初始化了LED、按鍵以及調試使用的串口,再調用前面分析的RS485_Config函數初始化了RS-485通訊使用的串口工作模式。

初始化后485就進入了接收模式,當接收到數據的時候會進入中斷並把數據存儲到接收緩沖數組中,我們在main函數的while循環中(else部分)調用get_rebuff來查看該緩沖區的狀態,若接收到256個數據就把這些數據通過調試串口打印到電腦端,然后清空緩沖區。

while循環中,還檢測了按鍵的狀態,若按鍵被按下,就把MAX485芯片切換到發送狀態並調用RS485_SendByte函數發送測試數據0x00-0xFF,發送完畢后切換回接收狀態以檢測總線的數據。

41.2.3 下載驗證

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

(1)    按照"硬件設計"小節中的圖例連接兩個板子的485總線;

(2)    使用跳線帽連接 : 485_R<--->PD6485_D<--->PD5C/4-5V<--->5V ;

(3)    USB線使實驗板"USB TO UART"接口跟電腦連接起來,在電腦端打開串口調試助手,編譯本章配套的程序,並給兩個板子都下載該程序,然后復位。

4)復位后在串口調試助手應看到485測試的調試信息,按一下其中一個實驗板上的KEY1按鍵,另一個實驗板會接收到報文,在串口調試助手可以看到相應的發送和接收的信息。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM