Go Down

Topic: Hardware RS485 (Read 1 time) previous topic - next topic


I'm trying to utilize the hardware RTS, by enabling the RS485 mode of serial ports - but I'm unable to get it working. (please see datasheet http://www.atmel.com/Images/doc11057.pdf page 810 and 839). I'm writing USART1->US_MR to achieve this.

Here is what I tried:
Code: [Select]

#include <RS485_non_blocking.h> // Lib by by Nick Gammon, v1.0

#define RTS 23 // Pin used for RTS, PA14/D23

size_t fWrite (const byte what) // The write function of the RS485 class
  return Serial2.write (what); 

RS485 myChannel (NULL, NULL, fWrite, 0); // RS485 class, by Nick Gammon. Basically a wrapper class that complements data, makes checksums and handles packages in both transmit and recieve.

void setup ()
  Serial.begin (115200); // USB UART for Serial monitor
  Serial2.begin (115200); // USART1 for, RS485 communication
  myChannel.begin (); // Class initializer: mallocs buffer, resets control flags.
  unsigned int test = USART1->US_MR; // Read USART mode register
  Serial.print(test,BIN); //Reads out as 0000 0000 0000 0000 0000 1000 1100 0000, i.e. Normal mode, 8bit, 1 stop, async.

   USART1->US_MR |= 0x00000001; // Set the mode to 0x1 = RS485
   test = USART1->US_MR; // Check that the register was written (it can be write protected)
   Serial.print(test,BIN); //Reads out as 0000 0000 0000 0000 0000 1000 1100 0001, i.e. RS485 mode, 8bit, 1 stop, async.
    pinMode(RTS,OUTPUT);//RTS pin as output.
}  // end of setup

const byte msg [5] = {0x01,0x01,0x01,0x01,0x01}; // Random message

void loop ()
    myChannel.sendMsg (msg, sizeof (msg)); // Send message as package, implements Serial2.write() when data has been packed.
    delay (100);  // limit loop speed
}  // end of loop

This however does not toggle the RTS pin (D23/PA14) belongning to Serial2 (the SAM3x USART1).

I can toggle the pin myself, by calling:
Code: [Select]

    PIOA->PIO_SODR=1<<14; //RTS pin to HIGH
    myChannel.sendMsg (msg, sizeof (msg)); // Send message as package, implements Serial2.write() when data has been packed.
    //  Serial2.flush(); //Doesn't work yet, still bytes left to send
    while(USART1->US_CSR & US_CSR_TXEMPTY != US_CSR_TXEMPTY){}; //Wait for transmit buffer to empty. Also exits before transmit complete
    delayMicroseconds(150); // My busywait hack to ensure complete transmission
    PIOA->PIO_CODR=1<<14; //Clear RTS pin   

But this is really not what I want, I would much prefer it to be handled in hardware (since RTS seems to be present, at least on USART0=Serial1 and USART1=Serial2). I realize a patch has been made to implement Serial.flush() properly, but I'm aiming to control it in hardware.

I have considered whether it's due to PIOA pin multiplexing, PA14 can also be function TK, but it seems the #define for both TK and RTS1 is the same.
Any help would be greatly appreciated.


You can only write to the US_MR register (and many others) when the WPEN bit is clear, exactly how you do that I'm not sure but it involves writing a key into the US_WPMR register.

Rob Gray aka the GRAYnomad www.robgray.com


Hi, Thank you for your reply.

You can only write to the US_MR register (and many others) when the WPEN bit is clear, exactly how you do that I'm not sure but it involves writing a key into the US_WPMR register.

I realize that this register exists (according to above mentioned datasheet section 36.8.18, one has to write 0x55534100 into US_WPMR to disable write protect), but as you can see in my setup() code, I read USART1->US_MR both before and after setting the US_MR to enable RS485 - and it reads the changes I wrote - shouldn't that be confirmation that the register is written correctly?


Yes I see your write appear to be working, didn't spot that before.

Next question, where is the pin set to be peripheral B?

Rob Gray aka the GRAYnomad www.robgray.com


Jul 10, 2013, 09:42 am Last Edit: Jul 10, 2013, 09:44 am by hnygaard87 Reason: 1
No worries. I tried disabling the write protection, just in case, but to no avail.

I must admit that the pin mux'ing is beyond my comprehension. However, what I could dust up with some recursive string searches was that "PIO_PA14" could be found in:

hardware\arduino\sam\system\CMSIS\Device\ATMEL\sam3xa\include\pio\pio_sam3x8e.h:47:#define PIO_PA14             (1u << 14) /**< \brief Pin Controlled by PA14 */
hardware\arduino\sam\system\CMSIS\Device\ATMEL\sam3xa\include\pio\pio_sam3x8e.h:373:#define PIO_PA14B_TK         (1u << 14) /**< \brief Ssc signal: TK */
hardware\arduino\sam\system\CMSIS\Device\ATMEL\sam3xa\include\pio\pio_sam3x8e.h:430:#define PIO_PA14A_RTS1       (1u << 14) /**< \brief Usart1 signal: RTS1 */
hardware\arduino\sam\system\CMSIS\Device\ATMEL\sam3xa\include\pio\pio_sam3x8e.h:461:#define PIO_PA14_IDX         14

and that this is used in:

hardware\arduino\sam\variants\arduino_due_x\variant.cpp:165:  { PIOA, PIO_PA14,          ID_PIOA, PIO_OUTPUT_0, PIO_DEFAULT, PIN_ATTR_DIGITAL,                  NO_ADC, NO_ADC, NOT_ON_PWM,  NOT_ON_TIMER }, // PIN 23

Where variant.cpp:165 is part of array extern const PinDescription g_APinDescription[]. This Array is called in variant.cpp:404-408 (amongst other places):

Code: [Select]

Do you recon it's a matter of replacing PIO_OUTPUT_0 with PIO_PERIPH_A? I'll give it a try today.



I would be inclined to write to the register myself so the code still works after the next IDE download overwrites your changes.

Code: [Select]
Pio *myPio = (Pio*)0x400E0E00;   // create a pointer to a "structure" that is the PIO controller for port A
myPio->PIO_ABSR |= (1u << 14); 

I'm just flying by data sheet here but I tried something similar the other day and at least it compiled (as indeed does this).

Maybe try both ways and we'll both learn a bit more about programming the SAM.

Rob Gray aka the GRAYnomad www.robgray.com


Hi again. There we go, it finally works! :)

In addition to defining what peripheral the pin should be (PIO_ABSR), one also had to disable the PIO from controlling the pin, thus allowing the Peripheral to take control. This is achieved by writing 1 to the corresponding bit in PIO_PDR (The current setting can be read in PIO_PSR where 0='peripheral is active' and 1='peripheral is inactive').

To summarize here is what I did to enable RS485 RTS to be controlled in Arduino Due hardware:
Code: [Select]

  pinMode(23,OUTPUT); //RTS for USART1=Serial2 set as output.
  USART1->US_WPMR = 0x55534100; //Unlock the USART Mode register, just in case. (mine wasn't locked).
  USART1->US_MR |= (1u << 0); //Set USART1 mode to RS485.

  PIOA->PIO_WPMR = 0x50494F00;   //Unlock PIOA register, just in case. (mine wasn't locked).
  PIOA->PIO_ABSR |= (0u << 14); //Set PIOA14 to 0 => Choosing peripheral A = RTS for D23/PA14. (my whole port was already set to 0)
  PIOA->PIO_PDR |= (1u << 14); //: Disables the PIO from controlling the corresponding pin (enables peripheral control of the pin).

Now RTS toggles all by itself, with no need to write it manually and busywaiting with delay() or Serial.flush().

This should work for Serial1 (i.e. USART0) as well. The RTS pin is located at PB25 (D2 on the Arduino Due board).

Serial3 (i.e. USART3) RTS pin is not present on the chip package used for Due. (is located on PF5 on larger chips packages).
Furthermore the currently unsupported USART2 with RX/TX at PB21/PB20 (i.e. D52/A11), supports RTS, but the pin (PB22) is not connected on the Due.

My thanks to Graynomad for the fast replies, hints and discussion.



Great, obviously the pointer stuff I had is already done in the background which makes life easier. And I assume the regs are either never locked in the first place or unlocked by the Arduino startup code.

Serial.flush() won't work for the last char as you probably know, there's still one in the pipeline when it returns.

is located on PF5 on larger chips packages

I wonder why they don't release the larger chip.

Did you read the value for that key, 0x555341 or "USA", cute.

Rob Gray aka the GRAYnomad www.robgray.com


Yeah, PIOA and so on is already defined - but sometimes it's good to have a look at the actual addresses - gives you a reality check on what exactly you are doing..
I couldn't find any instances to the registers being unlocked by Arduino, so either I haven't looked properly or they are unlocked by default.

Yes I read about the Serial.flush() problem, and my scope confirmed that it did indeed return before the actual transmission was done - however, changing to wait for USART1->US_CSR & US_CSR_TXEMPTY != US_CSR_TXEMPTY didn't do the trick either - opposed to what I expected from reading Figure 36-37 p. 810 of the SAM datasheet. I still had to wait an additional 150us to ensure transmission complete (at 115200 baud).

Hehe, yeah gave me a laugh as well :P (it was only afterwards i realized it's the first three characters of USART, but decided not to read it like that..).

Again, thanks for your help (and your pinout diagram was a good help as well, much appreciated)


Go Up

Please enter a valid email to subscribe

Confirm your email address

We need to confirm your email address.
To complete the subscription, please click the link in the email we just sent you.

Thank you for subscribing!

via Egeo 16
Torino, 10131