第41章 RS-485通讯实验—零死角玩转STM32-F429系列
?
第41章 ????RS-485通訊實驗
全套200集視頻教程和1000頁PDF教程請到秉火論壇下載: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通訊網絡中,節點中的串口控制器使用RX與TX信號線連接到收發器上,而收發器通過差分線連接到網絡總線,串口控制器與收發器之間一般使用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 | 單端TXD、RXD、GND | 全雙工 | 邏輯1:-15V~-3V 邏輯0:+3V~+15V | 100米以內 | 只有兩個節點 |
| RS485 | 差分線AB | 半雙工 | 邏輯1:-6V~-2V 邏輯0:+2V~+6V | 1200米 | 支持多個節點。支持多個主設備,任意節點間可互相通訊 |
RS-485與RS-232的差異只體現在物理層上,它們的協議層是相同的,也是使用串口數據包的形式傳輸數據。而由于RS-485具有強大的組網功能,人們在基礎協議之上還制定了MODBUS協議,被廣泛應用在工業控制網絡中。此處說的基礎協議是指前面串口章節中講解的,僅封裝了基本數據包格式的協議(基于數據位),而MODBUS協議是使用基本數據包組合成通訊幀格式的高層應用協議(基于數據包或字節)。感興趣的讀者可查找MODBUS協議的相關資料了解。
由于RS-485與RS-232的協議層沒有區別,進行通訊時,我們同樣是使用STM32的USART外設作為通訊節點中的串口控制器,再外接一個RS-485收發器芯片把USART外設的TTL電平信號轉化成RS-485的差分信號即可。
41.2 RS-485—雙機通訊實驗
本小節演示如何使用STM32的USART控制器與MAX485收發器,在兩個設備之間使用RS-485協議進行通訊,本實驗中使用了兩個實驗板,無法像CAN實驗那樣使用回環測試(把STM32 USART外設的TXD引腳使用杜邦線連接到RXD引腳可進行自收發測試,不過這樣的通訊不經過RS-485收發器,跟普通TTL串口實驗沒有區別),本教程主要以"USART—485通訊"工程進行講解。
41.2.1 硬件設計
圖 412 雙CAN通訊實驗硬件連接圖
圖 4016中的是兩個實驗板的硬件連接。在單個實驗板中,作為串口控制器的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的PD11直接連接到這兩個引腳上,所以通過控制PD11的輸出電平即可控制485的收發狀態。
要注意的是,由于我們的實驗板485使用的信號線與液晶屏共用了,為防止干擾,平時我們默認是不給485收發器供電的,使用485的時候一定要把485接線端子旁邊的"C/4-5V"排針使用跳線帽與"5V"排針連接起來進行供電,并且把液晶屏從板子上拔下來;而又由于實驗板的RS-232與RS-485通訊實驗都使用STM32的同一個USART外設及收發引腳,實驗時注意必須要把STM32的"PD5引腳"與MAX485的"485_D"及"PD6"與"485_R"使用跳線帽連接起來(這些信號都在485接線端子旁邊的排針上)。
要實現通訊,我們還要使用導線把實驗板引出的A和B兩條總線連接起來,才能構成完整的網絡。實驗板之間A與A連接,B與B連接即可。
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配置
利用上面的宏,編寫485的USART初始化函數,見代碼清單 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<--->PD6、485_D<--->PD5、C/4-5V<--->5V ;
(3)????用USB線使實驗板"USB TO UART"接口跟電腦連接起來,在電腦端打開串口調試助手,編譯本章配套的程序,并給兩個板子都下載該程序,然后復位。
(4)復位后在串口調試助手應看到485測試的調試信息,按一下其中一個實驗板上的KEY1按鍵,另一個實驗板會接收到報文,在串口調試助手可以看到相應的發送和接收的信息。
?
轉載于:https://www.cnblogs.com/firege/p/5806049.html
總結
以上是生活随笔為你收集整理的第41章 RS-485通讯实验—零死角玩转STM32-F429系列的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 课设-51单片机+PM2.5+温湿度(程
- 下一篇: 室内电子地图-自定义室内地图-室内地图在