serial 9-bit on the t3

Status
Not open for further replies.

virtualdave

Well-known member
Hi all,
I've been asked if it's possible to communicate using a 9-bit serial protocol. Did a bit of searching and looks like some changes were made recently to the main Serial.begin() function to allow custom serial communication beyond baud rate, however a 9-bit option wasn't added (although was part of the original request). If I knew what I was doing (which I don't) is this possible via some more fundamental changes to the core serial library? Does the chip on the t3 allow for 9-bit protocol?

Thanks in advance for any pointers!

David
 
Well, the hardware has 9 bit mode. It's enabled with the M bit of UART0_C1. See 45.3.3 on page 1055 of the manual.

Once enabled, you send and receive the 9th bit using a couple bits of UART0_C3.

The code in serial1.c implements a circular buffer of 8 bit bytes. You'll need to change that to 9 bits, maybe by adding another array and using the head and tail (and some code) to index bits, or maybe by changing the array to 16 bit shorts and just waste 7 bits. You'll need to edit the read and write to move 9 bits,as well as the interrupt routines.

The hardware has the capability,so hopefully this at least gets you a start?
 
Oye! Thanks, Paul! I think I'm a bit over my head, but digging into the manual and seeing if I can't by some miracle figure this out. I'll report back if I come up with anything.

Thanks!
David

P.S. Sort of related to the topic (serial communication)...I'm able to communicate on the Tx/Rx1 pins @ 921600 baud which is pretty exciting.
 
I was curious if this library:
"Mods to HardwareSerial to handle 9-bit data"
http://forum.arduino.cc/index.php/topic,91377
could be adapted to work on the teensy3?

Not directly, no.

Nick did pretty much the work I suggested in message #2 above, plus a couple other necessary things I didn't think of (he's pretty awesome like that). Nick's work would need to be repeated for Teensy3's serial1.c, but at least his changes to Arduino's HardwareSerial.cpp could help as a guide.
 
9-bit serial changes for Teensy3

I have done a preliminary attempt at 9-bit serial for the Teensy3.

Download from: http://gammon.com.au/Arduino/9bit_Teensy3_Serial.zip

You need to copy the 4 files in that .zip file to the teensy3 folder in your install directory:

Code:
<install location>/hardware/teensy/cores/teensy3

The files are:

Code:
HardwareSerial.h
serial1.c
serial2.c
serial3.c

Example code:

Code:
void setup ()
  {
  Serial1.begin (115200, true);
  Serial2.begin (115200, true);
  Serial3.begin (115200, true);
  }  // end of setup

unsigned long lastSend;

void loop ()
  {
  if (millis () - lastSend >= 500)
    {
    Serial1.println ("Hello world");
    Serial1.write9bit (0x123);
    Serial1.write9bit (0x142);
    lastSend = millis ();
    }
    
  if (Serial2.available ())
    Serial3.write9bit (Serial2.read ());
    
  }  // end of loop

This tests writing (Serial1) and reading back (Serial2) and then writing again (Serial3). The logic analyzer output shows that the 9-bit data (0x123 and 0x142) are sent and received OK.

Teensy_forum_23873.png


To use the 9-bit mode you need to have the extra argument "true" on the Serialx.begin line. The default is false, in which case it continues to use 8-bit mode.

To write with the high-order bit set use the write9bit function as illustrated above.

To read 9-bits, just read normally as Serialx.read() already returns an integer (not a byte).

If you have any problems please let me know.
 
Ah. My brother has a big 48 footer. The radar and nav and autopilot use some sort of RS485-like bus. Not sure what.
 
Yes, I think that's what it is for. The RS485 is really an electrical protocol, but you could certainly use it to connect to gadgets on the boat.
 
I have done a preliminary attempt at 9-bit serial for the Teensy3.

Awesome Nick !!! :)

Can anyone here confirm it works with real 9 bit devices? (I do not have any such things here)

Nick, how would you feel about changing the way 9 bit mode is selected. One of the recent Arduino releases added a 2nd paramter to begin() for data format. Maybe this could extend that scheme instead of using true/false? So far, I'm not supporting the 2nd parameter (nobody has asked yet), but I plan to add it.
 
You could do that easily enough yourself, it's just a boolean that is saved and used when deciding how to send/receive. Maybe mask the low-order bit and use it for that, leaving the other bits free. (The current system would be backwards compatible with that).

I just checked 1.0.5 HardwareSerial and that seems to only have the baud rate.
 
I just checked 1.0.5 HardwareSerial and that seems to only have the baud rate.

There are 2 begin functions. The 2nd one is at line 365 in HardwareSerial.cpp.

Code:
void HardwareSerial::begin(unsigned long baud, byte config)
{
  uint16_t baud_setting;
  uint8_t current_config;
  bool use_u2x = true;

#if F_CPU == 16000000UL
  // hardcoded exception for compatibility with the bootloader shipped
  // with the Duemilanove and previous boards and the firmware on the 8U2
  // on the Uno and Mega 2560.
  if (baud == 57600) {
    use_u2x = false;
  }
#endif

try_again:

  if (use_u2x) {
    *_ucsra = 1 << _u2x;
    baud_setting = (F_CPU / 4 / baud - 1) / 2;
  } else {
    *_ucsra = 0;
    baud_setting = (F_CPU / 8 / baud - 1) / 2;
  }

  if ((baud_setting > 4095) && use_u2x)
  {
    use_u2x = false;
    goto try_again;
  }

  // assign the baud_setting, a.k.a. ubbr (USART Baud Rate Register)
  *_ubrrh = baud_setting >> 8;
  *_ubrrl = baud_setting;

  //set the data bits, parity, and stop bits
#if defined(__AVR_ATmega8__)
  config |= 0x80; // select UCSRC register (shared with UBRRH)
#endif
  *_ucsrc = config;

  sbi(*_ucsrb, _rxen);
  sbi(*_ucsrb, _txen);
  sbi(*_ucsrb, _rxcie);
  cbi(*_ucsrb, _udrie);
}
 
Hi,
I have been experimenting with Nick's 9-bit modes to read Raymarine SeaTalk from a Smartpilot unit.

Using Serial1 I found it was reading the correct bytes data except detection of the 9th bit. I have been using a simple test for the output of Serial1.read :

int inbyte = (Serial1.read());
if (inbyte & 0x100)

Output was of the form:
02 05 AE 02 00 08 00 9C 01 00 00 84 06 00 00 00 00 00 ;
02 05 AE 02 00 08 00 9C 01 00 00 84 06 00 00 00 00 00 ;
02 05 AE 02 00 08 00 9C 01 00 00 84 06 00 00 00 00 00 ;
02 05 AE 02 00 08 00 9C 01 00 00 84 06 00 00 00 00 00 ;
02 05 AE 02 00 08 00 90 10 ;
05 54 81 A3 12 56 91 0F 0D 9C 01 00 00 84 06 00 00 00 00 00 ;
02 05 AE 02 00 08 00 9C 01 00 00 84 06 00 00 00 00 00 ;
02 05 AE 02 00 08 00 9C 01 00 00 84 06 00 00 00 00 00 ;
02 05 AE 02 00 08 00 9C 01 00 00 84 06 00 00 00 00 00 ;


I had a quick scan through the code for Serial1.c but could not spot an issue.

I should point out, I am very new to the Teensy (about 4 hours exposure so fair) and the closest I have come to this type device before is Raspberry Pi.

More on a whim than anything else I had a go at using Serial2. To my surprise and relief this works just fine. output is now:


84 06 00 00 00 00 00 02 05 ;
AE 02 00 08 00 ;
53 40 04 ;
52 01 00 00 ;
9C 01 00 00 ;
84 06 00 00 00 00 00 02 05 ;
AE 02 00 08 00 ;
53 70 18 ;
52 01 00 00 ;
9C 01 00 00 ;
84 06 00 00 00 00 00 02 05 ;
AE 02 00 08 00 ;
53 B0 1C ;
52 01 00 00 ;
9C 01 00 00 ;

Now, as I said, I am new to this. So I do not know if I have just made an assumption about the abilities of different UART's.

I this a hardware difference of a software issue?

By the way, I like the hardware, this site and the forum. Good effort.

Cheers
Paul
 
I can't tell from the tiny snippet of code, nor the output, what you are seeing compared to what you expect to see.

Code:
int inbyte = (Serial1.read());
if (inbyte & 0x100)

What is that doing?
 
Nick,

below is the full code I am using.

Code:
void setup()
{
  Serial.begin(115200) ;
  Serial1.begin(4800,true) ;
  delay(6000) ;
}
void loop()
{
  if (Serial1.available ()) 
{ int inbyte = (Serial1.read());
   if (inbyte & 0x100)
   Serial.println (";") ;
   
   // print any leading zero's
   if((inbyte & 0xf0) == 0)
     Serial.print(B0);
   Serial.print (inbyte & 0xFF, HEX) ;
   Serial.print (" ");
 
}  
 
}

So pretty simple. With the code snippet in my previous post I was just trying to point out I was checking for the 9th bit. I am using a technique I saw in a thread you contributed to in another forum.

So the only change, I made to get this working was changing every "Serial1" to "Serial2" and making the appropriate hardware change.

Paul
 
Hmm. Try changing (in serial1.c) around line 199 from:

Code:
    if (avail == 0) {
      // The only way to clear the IDLE interrupt flag is
      // to read the data register.  But reading with no
      // data causes a FIFO underrun, which causes the
      // FIFO to return corrupted data.  If anyone from
      // Freescale reads this, what a poor design!  There
      // write should be a write-1-to-clear for IDLE.
      c = UART0_D;

to:

Code:
    if (avail == 0) {
      // The only way to clear the IDLE interrupt flag is
      // to read the data register.  But reading with no
      // data causes a FIFO underrun, which causes the
      // FIFO to return corrupted data.  If anyone from
      // Freescale reads this, what a poor design!  There
      // write should be a write-1-to-clear for IDLE.
      c = UART0_D;
      // see if 9th bit set
      if (use9Bits && (UART0_C3 & (1 << 7)))
        c |= 0x100;

Can you let me know if that works please?
 
There is a difference between the way the original code read for Serial1 compared to Serial2 and Serial3. The extra code here:

Code:
		if (avail == 0) {
			// The only way to clear the IDLE interrupt flag is
			// to read the data register.  But reading with no
			// data causes a FIFO underrun, which causes the
			// FIFO to return corrupted data.  If anyone from
			// Freescale reads this, what a poor design!  There
			// write should be a write-1-to-clear for IDLE.
			c = UART0_D;

Seems to me it (sometimes, that is when avail == 0) reads UART0_D for the reasons given. The other files don't do that. However the datasheet says:

In 8-bit or 9-bit data format, only UART data register (D) needs to be accessed to clear the S1[RDRF] bit (assuming receiver buffer level is less than RWFIFO[RXWATER]). The C3 register needs to be read, prior to the D register, only if the ninth bit of data needs to be captured.

The problem with the lines I added is, that the extra bit is now discarded (it doesn't store c at this point). So when UART0_D is read later on the 9th bit is now gone. I don't know enough of the thinking processes of the person that wrote this to suggest a great work-around, however using Serial2 would seem to be one of them. But if Serial2 works without this extra code, why is it needed for Serial1?

As a warning to others using this, you either need to resolve this with the original author, or use Serial1 for sending 9-bits but not receiving it.
 
It looks to me from your output that you are sometimes getting the 9-bit set, would you agree?

Yes, I agree it is sometime set, but I do not think it is every in a correct location, which I guess in itself may be quite a strong diagnostic.

For example the longest line I get:

02 05 AE 02 00 08 00 9C 01 00 00 84 06 00 00 00 00 00 ;

Should break down as
The end of one datagram:
...... 02 05
Then full datagrams:
AE 02 00 08 00
9C 01 00 00
And the start of another:
84 06 00 00 00 00 00

So the 9-bit is not being reported at the end of a datagram even when it is reported.

By the way, thanks for your help on this. I would be this far along if it was not for your previous work on this subject.
 
Another non-ideal workaround would be to adapt the non-FIFO code of Serial2 and Serial3 to use on Serial1, if you've already got your hardware connected to Serial1 and it's too difficult to rewire.

Unfortunately, using the FIFO does add quite a bit of complexity to the code. Yes, I'm the one who wrote the code (except for Nick's 9-bit patch), including that snarky comment about Freescale's design.
 
Another non-ideal workaround would be to adapt the non-FIFO code of Serial2 and Serial3 to use on Serial1, if you've already got your hardware connected to Serial1 and it's too difficult to rewire.

Unfortunately, using the FIFO does add quite a bit of complexity to the code. Yes, I'm the one who wrote the code (except for Nick's 9-bit patch), including that snarky comment about Freescale's design.

Paul, Thanks for the information. Fortunately I am not stuck on using Serial1 as I am still on a breadboard at the moment.

I had noticed the FIFO on serial1 when I was having problems. This is what prompted me to try Serial2, more in blind hope than anything.

I note your comment earlier in the thread about winding Nick's patch into the baseline Serial code. I might have a crack at that when I get a little more experience with the Teensy3.

Nick and Paul, thanks again for your help.

Just as a point of interest. I think I am using the Teensy3 and your software for a reasonably unique application. I am building and automated steering system for a ocean rowing boat. We are having a crack at the world record for the Atlantic in the new year. Take a look at www.oceanusrowing.com to see how we get on.

Thanks again
Paul
 
Status
Not open for further replies.
Back
Top