Forum Rule: Always post complete source code & details to reproduce any issue!
Results 1 to 18 of 18

Thread: RS485 half duplex using Teensy 3.0 hardware pins

  1. #1
    Senior Member Epyon's Avatar
    Join Date
    Apr 2013
    Location
    Belgium
    Posts
    357

    RS485 half duplex using Teensy 3.0 hardware pins

    I'm trying to get Modbus to run over a half-duplex RS485 link, with my Teensy acting as a Modbus master. I successfully ported my Modbus stack to the Teensy 3.0, but I'm at a loss on how to toggle the DE/!RE pin of the RS485 transceiver. On the Arduino, I set a digital pin by checking the UCSR0A register, which contains a bit signaling the depletion of the transmit buffer. However, since the Teensy 3.0 has hardware handshaking pins, can't e.g. the RTS pin be used for toggling the transceiver? Or any other idea's on how to get a digital pin to function as the transceiver toggle? The Dynamixel library I found on this forum didn't help me much .

  2. #2
    Senior Member Epyon's Avatar
    Join Date
    Apr 2013
    Location
    Belgium
    Posts
    357
    I fixed it by using Uart.flush() before disabling my DE pin. It's two instructions more, but it works.

    Click image for larger version. 

Name:	TEK00000.PNG 
Views:	765 
Size:	5.8 KB 
ID:	405

  3. #3
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    14,489
    Teensy 2.0's HardwareSerial object has an undocumented feature to automatically control the DE pin. You use with the Serial1.begin(baud, pin).

    I've been considering adding this to Teensy 3.0.....

  4. #4
    Member
    Join Date
    Apr 2013
    Location
    Oregon USA
    Posts
    29
    Quote Originally Posted by Epyon View Post
    I fixed it by using Uart.flush() before disabling my DE pin. It's two instructions more, but it works.

    Click image for larger version. 

Name:	TEK00000.PNG 
Views:	765 
Size:	5.8 KB 
ID:	405
    I would be deeply appreciative if you were able to post your code that you used to port the library? (yours or the standard modbus master?) and the DE control pin. Thank you!

  5. #5
    Senior Member Epyon's Avatar
    Join Date
    Apr 2013
    Location
    Belgium
    Posts
    357
    Quote Originally Posted by tunahammer View Post
    I would be deeply appreciative if you were able to post your code that you used to port the library? (yours or the standard modbus master?) and the DE control pin. Thank you!
    Ofcourse, see the sketch attached. Works on the Teensy 3.0. You can determine the pin to use as DE toggle in the header.

    It's actually based on this sketch which implements functions 3 and 16 (read holding registers and preset multiple registers) of the Modbus RTU Protocol. I planned on making it into a library, but haven't really had some free time lately .
    Attached Files Attached Files

  6. #6
    Member
    Join Date
    Apr 2013
    Location
    Oregon USA
    Posts
    29
    Quote Originally Posted by Epyon View Post
    Ofcourse, see the sketch attached. Works on the Teensy 3.0. You can determine the pin to use as DE toggle in the header.

    It's actually based on this sketch which implements functions 3 and 16 (read holding registers and preset multiple registers) of the Modbus RTU Protocol. I planned on making it into a library, but haven't really had some free time lately .
    Thank you so much!!!

    Is there any reason this couldn't work on a Due board? I have 3 Teensys in this project already and realized I filled their serial ports, but the Due has both. I've looked all over the Arduino forums and can't really find where someone has interfaced with the MAX chip and controlled the DE pin perfectly.

  7. #7
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    14,489
    Is there any reason this couldn't work on a Due board?
    Yes. You'll probably run into problems related to Arduino Due's hardware serial code.

    The worst problem is Due's flush() function returns too soon. Here's their code:

    Code:
    void USARTClass::flush( void )
    {
      // Wait for transmission to complete
      while ((_pUsart->US_CSR & US_CSR_TXRDY) != US_CSR_TXRDY)
    ;
    }
    The trouble is the TXRDY flag indicates when the UART is ready to accept the next character of data to transmit, which happens before the final stop bit of the currently transmitting character has been sent. If you rely upon this to clear the DE pin, you'll turn off the RS485 transmitter too soon.

    Arduino Uno had this same bug for quite some time. It was finally fixed when Michele Mazzucchi ported my code from Teensy to fix Arduino's Serial.flush().


    The other problem you're likely to encounter is Due's lack of transmit buffering. Due's write (and therefore print) function does not implement a buffer. When you use Serial.print(anything) or Serial.write(buf, len), only the first 2 bytes go into the UART quickly. If you write more than 2 characters, the function just waits until the UART's 2-byte buffer can accept the last 2 characters.

    Arduino Uno has implemented transmit buffering since Arduino 1.0. Teensy has had it much longer, since Arduino 0016. Even using just one serial port, this really makes your program much faster, since you can start doing other work while a block of data from Serial.write() or a string from Serial.print() is transmitted from the buffer.

    If you're using more than one port, the transmit buffering feature is pretty much essential to maintaining simultaneous data output. Arduino Due gives you 4 serial ports, and they work great if you're mostly receiving data and you only need to occasionally transmit, or you mostly transmit on just 1 port, or if you transmit only 1 or 2 byte messages with delays between them.

    Arduino Due is an amazing piece of hardware, but if you're building an application that sends any substantial amount of data simultaneously on multiple serial ports, you'll quickly run into the limitations of Due's software.
    Last edited by PaulStoffregen; 05-05-2013 at 01:27 PM.

  8. #8
    Member
    Join Date
    Apr 2013
    Location
    Oregon USA
    Posts
    29
    Quote Originally Posted by PaulStoffregen View Post
    Yes. You'll probably run into problems related to Arduino Due's hardware serial code.

    The worst problem is Due's flush() function returns too soon. Here's their code:

    Code:
    void USARTClass::flush( void )
    {
      // Wait for transmission to complete
      while ((_pUsart->US_CSR & US_CSR_TXRDY) != US_CSR_TXRDY)
    ;
    }
    The trouble is the TXRDY flag indicates when the UART is ready to accept the next character of data to transmit, which happens before the final stop bit of the currently transmitting character has been sent. If you rely upon this to clear the DE pin, you'll turn off the RS485 transmitter too soon.

    Arduino Uno had this same bug for quite some time. It was finally fixed when Michele Mazzucchi ported my code from Teensy to fix Arduino's Serial.flush().


    The other problem you're likely to encounter is Due's lack of transmit buffering. Due's write (and therefore print) function does not implement a buffer. When you use Serial.print(anything) or Serial.write(buf, len), only the first 2 bytes go into the UART quickly. If you write more than 2 characters, the function just waits until the UART's 2-byte buffer can accept the last 2 characters.

    Arduino Uno has implemented transmit buffering since Arduino 1.0. Teensy has had it much longer, since Arduino 0016. Even using just one serial port, this really makes your program much faster, since you can start doing other work while a block of data from Serial.write() or a string from Serial.print() is transmitted from the buffer.

    If you're using more than one port, the transmit buffering feature is pretty much essential to maintaining simultaneous data output. Arduino Due gives you 4 serial ports, and they work great if you're mostly receiving data and you only need to occasionally transmit, or you mostly transmit on just 1 port, or if you transmit only 1 or 2 byte messages with delays between them.

    Arduino Due is an amazing piece of hardware, but if you're building an application that sends any substantial amount of data simultaneously on multiple serial ports, you'll quickly run into the limitations of Due's software.
    Thank you for such a detailed response! I will have to find a way to fit yet another teensy into my project. At least they are, in fact, teensy.

  9. #9
    Senior Member Epyon's Avatar
    Join Date
    Apr 2013
    Location
    Belgium
    Posts
    357
    Quote Originally Posted by tunahammer View Post
    Thank you so much!!!

    Is there any reason this couldn't work on a Due board? I have 3 Teensys in this project already and realized I filled their serial ports, but the Due has both. I've looked all over the Arduino forums and can't really find where someone has interfaced with the MAX chip and controlled the DE pin perfectly.
    I was looking into porting it to the Due, because the Teensy 3.0 has a yet to be resolved bug which prevents it from using more than two SPI peripherals at the same time (more specifically a Wiznet Ethernet chip and a SD card), and this project needed both those features. Unfortunately, other projects required more of my attention and I haven't been able to either port the DE-toggle to the Due or search for ways to circumvent the SPI bug.
    For the former, I was going to look if there were any registers in the SAM3X chip of the Due which can be used to signal an empty transmit buffer, if necessary coupled with some minor delay function. For the latter, I suspect the CS lines of the Teensy don't behave as they should, activating the two SPI peripherals at the same time. But I'll need to hit the lab to test that .
    Last edited by Epyon; 05-05-2013 at 08:00 PM.

  10. #10
    Junior Member
    Join Date
    May 2013
    Posts
    2
    This is working for rs485 on the arduino due for serial3 :

    while (( USART3->US_CSR & US_CSR_TXEMPTY) != US_CSR_TXEMPTY) {};

    Click image for larger version. 

Name:	rs485.jpg 
Views:	565 
Size:	84.8 KB 
ID:	466

  11. #11
    Junior Member
    Join Date
    May 2013
    Posts
    2
    Or you can change it for all serials in the due ide 1.5.2, and use the Serial.flush() command

    go to --> arduino-1.5.2\hardware\arduino\sam\cores\arduino

    edit in the USARTClass.CPP ( NOT UARTClass.CPP)!!!!

    Use the TXEMPTY flag, and not the TXRDY flag

    FROM :

    void USARTClass::flush( void )
    {
    // Wait for transmission to complete
    while ((_pUsart->US_CSR & US_CSR_TXRDY) != US_CSR_TXRDY)
    ;
    }


    TO :
    void USARTClass::flush( void )
    {
    // Wait for transmission to complete
    while ((_pUsart->US_CSR & US_CSR_TXEMPTY) != US_CSR_TXEMPTY)
    ;
    }


    Now you can use the flush command before you disable the DE pin.

    For Serial3 --> Serial3.flush();
    then disable the DE pin

    I am using the ISL3178EIBZ RS485, at 18.432 Mhz clock for the slave (ATmega44), at 115200Baud (c.q. bps) speed, without any error.

  12. #12
    Member
    Join Date
    Apr 2013
    Location
    Oregon USA
    Posts
    29
    Quote Originally Posted by xenox007 View Post
    Or you can change it for all serials in the due ide 1.5.2, and use the Serial.flush() command

    go to --> arduino-1.5.2\hardware\arduino\sam\cores\arduino

    edit in the USARTClass.CPP ( NOT UARTClass.CPP)!!!!

    Use the TXEMPTY flag, and not the TXRDY flag

    FROM :

    void USARTClass::flush( void )
    {
    // Wait for transmission to complete
    while ((_pUsart->US_CSR & US_CSR_TXRDY) != US_CSR_TXRDY)
    ;
    }


    TO :
    void USARTClass::flush( void )
    {
    // Wait for transmission to complete
    while ((_pUsart->US_CSR & US_CSR_TXEMPTY) != US_CSR_TXEMPTY)
    ;
    }


    Now you can use the flush command before you disable the DE pin.

    For Serial3 --> Serial3.flush();
    then disable the DE pin

    I am using the ISL3178EIBZ RS485, at 18.432 Mhz clock for the slave (ATmega44), at 115200Baud (c.q. bps) speed, without any error.
    Thank you both for your further followup! Your contributions will be most helpful to me!!!

  13. #13
    Member
    Join Date
    Apr 2013
    Location
    Oregon USA
    Posts
    29
    Quote Originally Posted by Epyon View Post
    Ofcourse, see the sketch attached. Works on the Teensy 3.0. You can determine the pin to use as DE toggle in the header.

    It's actually based on this sketch which implements functions 3 and 16 (read holding registers and preset multiple registers) of the Modbus RTU Protocol. I planned on making it into a library, but haven't really had some free time lately .
    So I am having some crazy difficulty with this... I am ending up talking to a modbus device that uses RS232 (still modbus RTU not ASCII) instead of 485, so I ditched my max485 chip and plugged straight in. I get the same reply from the device no matter what I do:


    Code:
    read returned: 65280 2757 59760 0 0 0 0 0 0 0 
    write returned: 0
    When I use a modbus test application on my PC it comes back with the correct numbers (a 345 in the 1st register and a 1 in the 7th register)

    I'm really confused what is going on and would love any suggestions.

    Thanks!

    Code:
    const int ledPin = 13;
    int read_holding_registers(int slave, int start_addr, int count,int *dest, int dest_size);
    unsigned int Txenpin = 14; //this pin will be the DE toggle
    void setup()
    {
            const int baudrate = 9600;
            if (baudrate <= 19200)
                    interframe_delay = (unsigned long)(3.5 * 11 / baudrate);  /* Modbus t3.5 */
            Serial1.begin(baudrate); 	/* format 8N1, DOES NOT comply with Modbus spec. */
            pinMode(Txenpin, OUTPUT); 
            Serial.begin(9600);
            delay(1000);
            Serial.println("Ready");
            pinMode(ledPin, OUTPUT);
            
      
    }
    
    /* example data */
    int retval;
    int setval;
    int data[11];
    
    int data2[1] = {2};
    void loop()
    {
                    Serial.print("read returned: ");
                    digitalWrite(ledPin,HIGH);
                    retval = read_holding_registers(1,0,10,data,10);
                    for(int i = 0; i < 10; i++){
                      Serial.print(data[i]);
                      Serial.print(' ');}
                      
                    Serial.println();
    
    
    
     delay(125);
    //                Serial.print("write returned: ");
    //int preset_multiple_registers(int slave, int start_addr,int reg_count, int *data);                
    //                setval = preset_multiple_registers(1,40008,1,data2);
    //                setval = preset_multiple_registers(1,1,10,data2);
    //                setval = preset_multiple_registers(2,1,10,data2);
    //                setval = preset_multiple_registers(3,1,10,data2);
    //                setval = preset_multiple_registers(4,1,10,data2);
                    digitalWrite(ledPin,LOW);
    //                Serial.println(setval);
    
                     delay(125);
      
    }

    My logic analyzer is seeing this:

    Please ignore the attached thumbnail, I don't see a way to remove it. Just use the inline image here
    Click image for larger version. 

Name:	IrTSgyM.jpg 
Views:	301 
Size:	66.4 KB 
ID:	709
    Attached Thumbnails Attached Thumbnails Click image for larger version. 

Name:	UxdOCcO.jpg 
Views:	276 
Size:	95.6 KB 
ID:	708  
    Last edited by tunahammer; 07-25-2013 at 01:55 AM.

  14. #14
    Member
    Join Date
    Apr 2013
    Location
    Oregon USA
    Posts
    29
    Here is a shot of the PC based program
    Click image for larger version. 

Name:	vjtJtEv.jpg 
Views:	344 
Size:	27.3 KB 
ID:	710

    it is configured for 19200 baud, 8N1 no RTS

  15. #15
    Member
    Join Date
    Apr 2013
    Location
    Oregon USA
    Posts
    29
    Lord almighty...

    this line:

    Code:
     * start_addr: address of the slave's first register (+1)
    means "you need to add one yourself" I guess... so I just changed to

    retval = read_holding_registers(1,1,10,data,10);

    and amazingly... it works perfectly now!!

  16. #16
    Senior Member Constantin's Avatar
    Join Date
    Nov 2012
    Location
    In the yard with a 17' Dia. Ferris Wheel
    Posts
    1,406
    Quote Originally Posted by PaulStoffregen View Post
    Teensy 2.0's HardwareSerial object has an undocumented feature to automatically control the DE pin. You use with the Serial1.begin(baud, pin).

    I've been considering adding this to Teensy 3.0.....
    Hi Paul!
    Was this ever implemented for Teensy 3? My latest experiment suggests no… hope you are well!

  17. #17
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    14,489
    I've added Serial.transmitterEnable(pin) for both Teensy 2.0 and 3.0/3.1. Currently it's only on Serial1 in Teensy 3, but I'll add it on Serial2 and Serial3.

    The code is on github.

    https://github.com/PaulStoffregen/cores

    For Teensy 3, you need to update your copy of serial1.c and HardwareSerial.h. For Teensy 2.0, update HardwareSerial.cpp and HardwareSerial.h.

    I tested this by viewing waveforms on my scope. It seems to work (and the code in Teensy 2.0 has been there for years, but it's new in 3.0).

    Please give this a try, especially if you have any projects with real RS-485 hardware connected, and let me know how it works for you? Unless any major problems are discovered, I'd like to release this in 1.18 and then officially document it on the website.

  18. #18
    Quote Originally Posted by Epyon View Post
    Ofcourse, see the sketch attached. Works on the Teensy 3.0. You can determine the pin to use as DE toggle in the header.

    It's actually based on this sketch which implements functions 3 and 16 (read holding registers and preset multiple registers) of the Modbus RTU Protocol. I planned on making it into a library, but haven't really had some free time lately .
    Great, do you have a newer version of this code or even a library?

    Thanks,
    Regards,
    Mike

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •