2017年3月26日 星期日

[Arduino]RS485 初嘗

買了一塊WaveShare 的UART轉RS485(5V)板子來玩玩看。

先簡介一下RS485:

EIA-485(過去叫做RS-485或者RS485])是隸屬於OSI模型實體層的電氣特性規定為2線,半雙工,多點通訊的標準。它的電氣特性和RS-232不大一樣。用纜線兩端的電壓差值來表示傳遞訊號,1極的電壓標識為邏輯1,另一段標識為邏輯0。兩端的電壓差最小為0.2V以上時有效,任何不大於12V或者不小於-7V的差值對接受端都被認為是正確的。

EIA-485僅僅規定了接受端和傳送端的電氣特性。它沒有規定或推薦任何資料協定。EIA-485可以應用於配置便宜的廣域網路和採用單機傳送,多機接受通訊連結。它提供高速的資料通訊速率(10m時35Mbit/s;1200m時100kbit/s)。EIA-485和EIA-422一樣使用雙絞線進行高電壓差分平衡傳輸,它可以進行大面積長距離傳輸(超過4000英尺,1200公尺)。
EIA-485經常和常用裝置UART一起使用來實現在飛機上的低速率資料傳輸,舉個例子,一些乘客控制單元採用這種裝置,從而只需要很少的線纜就可以實現幾端子共享線纜,從而減輕整個裝置的重量。

                                                          所以RS485上會常見到一些協定,例如Modbus,BacNet and Profibus  。

當然RS485 也可以利用2組A/B來達成全雙工(一對專送,一對專收)

以上資料來源:

EIA-485 WIKI


簡單來說,RS485並沒有規定使用任何界面,只要電氣特性相符即可。

最左邊是接UART , 你可以看到它提供3種介面的RS485 A/B ,除了杜邦線之外就是2P端子 ,以及一個
































腳立說明如下,資料來源:



























主要特點為RSE這一支腳,它控制了資料流的方向,當RSE為HIGH時,代表要送資料,

RSE為LOW時表示要接收資料,所以一開始Master會預設為發送,remote會預設為接收。


由於RS485是半雙工的通訊方式,所以每一個裝都只能是傳送或是接收其中一種狀態。

參考了以下網址的範例。

https://arduino-info.wikispaces.com/SoftwareSerialRS485Example

這個範例master每次只能傳送一個byte,然候就會切換到接收模式直到Serial有再次輸入,就會切回去發送模式(此時remote傳再多它也收不到了),而remote在接收到資料後會切換成傳送模式把資料發送回去,10ms後就會再回到接收模式。


接腳如下:

arduino              RS485
10                        RX
11                        TX
3                         RSE
5V                       VCC
GND                    GDN 

二塊板子之間的A接A,B接B。















































之後燒錄程式,一邊為Master,一邊為remote


Master端程式如下:

/* YourDuino SoftwareSerialExample1
   - Connect to another Arduino running "YD_SoftwareSerialExampleRS485_1Remote"
   - Connect this unit Pins 10, 11, Gnd
   - Pin 3 used for RS485 direction control
   - To other unit Pins 11,10, Gnd  (Cross over)
   - Open Serial Monitor, type in top window. 
   - Should see same characters echoed back from remote Arduino

   Questions: terry@yourduino.com 
*/

/*-----( Import needed libraries )-----*/
#include <SoftwareSerial.h>
/*-----( Declare Constants and Pin Numbers )-----*/
#define SSerialRX        10  //Serial Receive pin
#define SSerialTX        11  //Serial Transmit pin

#define SSerialTxControl 3   //RS485 Direction control

#define RS485Transmit    HIGH
#define RS485Receive     LOW

#define Pin13LED         13

/*-----( Declare objects )-----*/
SoftwareSerial RS485Serial(SSerialRX, SSerialTX); // RX, TX

/*-----( Declare Variables )-----*/
int byteReceived;
int byteSend;

void setup()   /****** SETUP: RUNS ONCE ******/
{
  // Start the built-in serial port, probably to Serial Monitor
  Serial.begin(9600);
  Serial.println("YourDuino.com SoftwareSerial remote loop example");
  Serial.println("Use Serial Monitor, type in upper window, ENTER");
  
  pinMode(Pin13LED, OUTPUT);   
  pinMode(SSerialTxControl, OUTPUT);    
  
  digitalWrite(SSerialTxControl, RS485Receive);  // Init Transceiver   
  
  // Start the software serial port, to another device
  RS485Serial.begin(4800);   // set the data rate 

}//--(end setup )---


void loop()   /****** LOOP: RUNS CONSTANTLY ******/
{
  digitalWrite(Pin13LED, HIGH);  // Show activity
  if (Serial.available())
  {
    byteSend = Serial.read();
    digitalWrite(SSerialTxControl, RS485Transmit);  // Enable RS485 Transmit   
    RS485Serial.write(byteSend);          // Send byte to Remote Arduino
    digitalWrite(Pin13LED, LOW);  // Show activity    
    delay(10);
    digitalWrite(SSerialTxControl, RS485Receive);  // Disable RS485 Transmit       
   
  }
    
  if (RS485Serial.available())  //Look for data from other Arduino
   {
    digitalWrite(Pin13LED, HIGH);  // Show activity
    byteReceived = RS485Serial.read();    // Read received byte
    if (byteReceived!=0 && byteReceived!=10 && byteReceived!=13)
     {
        Serial.print("receive:");        // Show on Serial Monitor
        Serial.write(byteReceived);
        Serial.println();
     }
     
    delay(10);
    digitalWrite(Pin13LED, LOW);  // Show activity   
   }  

}//--(end main loop )---

/*-----( Declare User-written Functions )-----*/

//NONE
//*********( THE END )***********

remote端如下

/* YourDuino SoftwareSerialExample1Remote
   - Used with YD_SoftwareSerialExampleRS485_1 on another Arduino
   - Remote: Receive data, loop it back...
   - Connect this unit Pins 10, 11, Gnd
   - To other unit Pins 11,10, Gnd  (Cross over)
   - Pin 3 used for RS485 direction control   
   - Pin 13 LED blinks when data is received  
   
   Questions: terry@yourduino.com 
*/

/*-----( Import needed libraries )-----*/
#include <SoftwareSerial.h>
/*-----( Declare Constants and Pin Numbers )-----*/
#define SSerialRX        10  //Serial Receive pin
#define SSerialTX        11  //Serial Transmit pin

#define SSerialTxControl 3   //RS485 Direction control
#define RS485Transmit    HIGH
#define RS485Receive     LOW

#define Pin13LED         13

/*-----( Declare objects )-----*/
SoftwareSerial RS485Serial(SSerialRX, SSerialTX); // RX, TX

/*-----( Declare Variables )-----*/
int byteReceived;
int byteSend;

void setup()   /****** SETUP: RUNS ONCE ******/
{
  // Start the built-in serial port, probably to Serial Monitor
  Serial.begin(9600);
  Serial.println("SerialRemote");  // Can be ignored
  
  pinMode(Pin13LED, OUTPUT);   
  pinMode(SSerialTxControl, OUTPUT);  
  
  digitalWrite(SSerialTxControl, RS485Receive);  // Init Transceiver
  
  // Start the software serial port, to another device
  RS485Serial.begin(4800);   // set the data rate 
}//--(end setup )---


void loop()   /****** LOOP: RUNS CONSTANTLY ******/
{
  //Copy input data to output  
  if (RS485Serial.available()) 
  {
    byteSend = RS485Serial.read();   // Read the byte 
    digitalWrite(Pin13LED, HIGH);  // Show activity
    delay(10);              
    digitalWrite(Pin13LED, LOW);   
    Serial.write(byteSend);
    digitalWrite(SSerialTxControl, RS485Transmit);  // Enable RS485 Transmit    
    if (byteSend!=0 && byteSend!=10 && byteSend!=13)
        RS485Serial.write(byteSend); // Send the byte back
    delay(10);   
    digitalWrite(SSerialTxControl, RS485Receive);  // Disable RS485 Transmit      
//    delay(100);
  }// End If RS485SerialAvailable
  
}//--(end main loop )---

/*-----( Declare User-written Functions )-----*/
//NONE

//*********( THE END )***********

程式碼來源:(我有稍微做一點修改)
https://arduino-info.wikispaces.com/SoftwareSerialRS485Example

master執行畫面(記得選擇為沒有結尾)




remote執行畫面(使用CoolTerm)
記得先修改一下傳送的結尾模式



























由master傳送一個字元a,會收到一個相同回應

















remote端也會將收到的字元顯示並回傳給Master
















如果一次輸入一堆字傳送,可能就會出現問題,例如我輸入1234567890abcdefg

















remote端收到的也是不正確的字元









所以使用rs485做傳輸時,一定要注意收和送的時間點,建議使用現有的協定,例如ModBus,因為在收和送之間需要切換,這個如果沒有搭配好,會出現異常的資料。

1 則留言: