Teensy as RS-232 converter, qualifier

Status
Not open for further replies.
I have a demanding RS-232 application for Teensy (3.2). I am fluent with Microchip PICs but very few have 3 H/W serial ports. I thought I would try to use a TEENSY for this application and learn Arduino at the same time.

The application needs 3 interrupt-driven H/W serial ports to process packetized data. That is why TEENSY seems like a good fit.

All packets are 8-256 bytes and come in at a rate of about once per second. The second byte in each packet contains the (variable) packet length.


Port #1 RECEIVES RS-232 packetized data at 115200 baud and puts it in BUFFER 1.
Port #2 TRANSMITS the data in BUFFER 1 out at 115200 baud (unchanged).
Port #3 TRANSMITS the data in BUFFER 1 out at 57600 baud (unchanged - except for baud rate)

Port #2 RECEIVES data at 115200 baud and puts it in BUFFER 2
Port #3 RECEIVES data at 57600 baud and puts it in BUFFER 3

A CRC algorithm is run against BUFFER 2 and BUFFER 3
If the CRC of BUFFER 3 is correct, BUFFER 3 is sent out PORT #1 at 115200 baud (unchanged - except for baud rate)
If the CRC of BUFFER 3 is is not correct, and the CRC of BUFFER 2 is correct , then the contents of BUFFER 2 is sent out PORT #1 at 115200 baud (unchanged).
If the CRC of both BUFFER 2 and BUFFER 3 are correct, the contents of BUFFER 3 is sent out of PORT #1 at 115200 baud (unchanged - except for baud rate).

So I have some questions:

Would a TEENSY 3.2 be capable of this?
Are all the H/W serial ports interrupt-driven?
What libraries should I try to use?
Any other "words of wisdom"?

Alternately, would someone write this routine for me for less than $300?
 
Would a TEENSY 3.2 be capable of this?

Yes. Teensy 3.2 can easily manage 3 simultaneous data flows at 115200 baud.

Are all the H/W serial ports interrupt-driven?

Yes, all 3 use interrupts, all 3 have software-based buffering on both transmit and receive.

Serial1 and Serial2 also have 8 byte FIFOs, which allows fewer interrupts and more interrupt latency when running at high speeds. But for speeds under 1 Mbit/sec, these rarely matter.

What libraries should I try to use?

Just use Serial1, Serial2, Serial3. Details here:

https://www.pjrc.com/teensy/td_uart.html

In Arduino, use File > Examples > Teensy > Serial for an example.

Also, you might look at File > Examples > Teensy > USB_Serial > USBtoSerial for how to use block read & write for more efficient data transfer. But honestly, this is probably overkill for such slow baud rates.

Any other "words of wisdom"?

If you structure your program in a simple way where you only listen for incoming data on a single port until the full message is received, then you'll be depending on the buffers of the other 2 ports to capture any arriving data for that lengthy period of time. Obviously that's not a good design if you care about performance, but if it's simple and gets the job done, maybe that'll be enough. The default buffer sizes are fairly small, but you can increase them by editing serial1.c, serial2.c and serial3.c within hardware/teensy/avr/cores/teensy3.

Building a state machine approach with 3 large buffer in your programs, where you use available() and availableForWrite() to always do the maximum transfer possible without ever waiting (as shown in the USBtoSerial example) will give the best performance. But this sort of code can be more complex, depending on how you parse the incoming data and other details.

Also, if any of your devices support hardware flow control, you might consider using the CTS & RTS signals. Those have allowed people to use 4 Mbit/sec speeds in some projects.
 
Thanks for the info! How do I set up a buffer in Arduino? Or is one automatically created?

In PIC Basic, you would use the structure:

serial1buffer var byte[256] <-define buffer size and type

Then in the ISR, I would use something like -

GrabData:
hserin [seria1buffer [x]] < - load a byte into the array using the hardware serial "in" command.
if x = packetsize - 1 then DonePacket <- get the entire packet
incr x
goto GrabData

DonePacket
DataFlag = 1

INT_RETURN

I'm certain it is lots different in Arduino-land. Can you point me to a resource on buffer handling?
 
That's all already done in the libraries. You don't need to program this yourself (even the interrupts..).
For serial, the libraries are part of the core.
I guess, it will work without increasing the buffers - but if you need to, you can just edit the files in the Teensyduino-core.
\Arduino\hardware\teensy\avr\cores\teensy3\serial1.c to serial3.c
Code:
#define SERIAL1_TX_BUFFER_SIZE     64 // number of outgoing bytes to buffer
#define SERIAL1_RX_BUFFER_SIZE     64 // number of incoming bytes to buffer
Please note, the "Serial." is the Teensy-USB Serial interface.
"Serial1" is the first hardware-serial interface. A simple "proxy" that translate usb to hardware-serial in both directions looks like this:
Code:
 void setup() {  
  Serial.begin(115200);
  Serial1.begin(115200);   
}

void serialProxy() {
  
    if (Serial1.available()) {
        Serial.write( Serial1.read() );          
    }
    
    if (Serial.available()) {
      Serial1.write(Serial.read());          
    }    
    
}

void loop() {
  serialProxy();
}

Buffers and interrupts are used in the background.
No need to invest more work - makes no sense, the teensy is fast enough.. and capable to do really *way* more..

Edit: There's a lib that makes it easy to use the inbuilt-hardware-CRC, too.. what kind of CRC you are using ?
But it will work best with a buffer, not singles bytes (more overhead)
 
Last edited:
OK, I'm getting it. But my problem is that I can't just take in a packet and "spit it out". Some of the incoming packets will be corrupted and I need to know that. Also, the packets are variable-length. The packets always begin (byte 0) with 0xFE. Byte 1 contains the length of the packet. And the CRC is not just a "straight" CRC. Byte 5 is fed into a lookup table and the returned value is appended to the end of the data before the CRC is calculated (across bytes 1 to the appended value). The CRC method is an ITU X.25/SAE AS-4 hash.

The data is binary, so there can be a 0xFE anywhere in the data and it is quite possible that I could receive a partial packet. So the only way I know I have a valid packet is to get a CRC match. Due to the nature of what I'm doing, I cannot pass on a non-valid packet!

The packets are received in "bursts" once a second. Each burst can contain several packets. It would be helpful if the buffers were flushed (or pointers reset) if a valid packet was not received every 800 mSec to prevent the possibility of junk in the buffers corrupting the packet.
 
OK, I'm getting it. But my problem is that I can't just take in a packet and "spit it out". Some of the incoming packets will be corrupted and I need to know that. Also, the packets are variable-length. The packets always begin (byte 0) with 0xFE. Byte 1 contains the length of the packet. And the CRC is not just a "straight" CRC. Byte 5 is fed into a lookup table and the returned value is appended to the end of the data before the CRC is calculated (across bytes 1 to the appended value). The CRC method is an ITU X.25/SAE AS-4 hash.

The data is binary, so there can be a 0xFE anywhere in the data and it is quite possible that I could receive a partial packet. So the only way I know I have a valid packet is to get a CRC match. Due to the nature of what I'm doing, I cannot pass on a non-valid packet!

The packets are received in "bursts" once a second. Each burst can contain several packets. It would be helpful if the buffers were flushed (or pointers reset) if a valid packet was not received every 800 mSec to prevent the possibility of junk in the buffers corrupting the packet.

AND - If I want to send the same data out of two different ports (at different baud rates) can I simply use the structure -

if (Serial1.available()) {
Serial2.write(Serial1.read());
Serial3.write(Serial1.read());
}

Or will the library move the pointers on the first WRITE and cause the routine to fail?
 
I'm getting closer - I found that the routine

if (Serial1.available()) {
Serial2.write(Serial1.read());
Serial3.write(Serial1.read());
}

does NOT work.

I had to change it to:

if (Serial1.available()) {
char InChar = Serial1.read();
Serial2.write(InChar);
Serial3.write(InChar);
}

Which DOES work. The only problem is - it looks like I'm just getting a character and outputting a character. I need to put the incoming in a 8-256 byte (variable-length) buffer. I'll try to figure that out now, unless someone steps in and helps me.
 
The data is binary, so there can be a 0xFE anywhere in the data and it is quite possible that I could receive a partial packet. So the only way I know I have a valid packet is to get a CRC match. Due to the nature of what I'm doing, I cannot pass on a non-valid packet!

Sounds like you need to add an array to your program, and put the incoming bytes into the array rather than immediately retransmit them. You're going to also need a variable to remember how many bytes you've put into the array.

If 0xFE always marks the beginning of a packet, at least that's a fairly simple protocol. You'll need to add code to check if the byte you've just received is 0xFE. Perhaps when you get this byte, set the number of bytes variable back to zero? Or maybe take some other action? How you design your program is up to you....

It would be helpful if the buffers were flushed (or pointers reset) if a valid packet was not received every 800 mSec to prevent the possibility of junk in the buffers corrupting the packet.

This can be done by adding an elapsedMicros variable. Again, you have many options for how to structure your program. Perhaps one way could involve if-else checks on Serial1.available, where the case of incoming data sets the elapsedMicros back to zero, and the else case where no data was available has another if check to see if the elapsedMicros variable has incremented past 800. In that case, you'd probably do something like setting that number of bytes variable back to zero, so the rest of your code will treat the next incoming data as the beginning of a new packet.

One more tip... when you get stuck and need help here, the expectation is posting a complete program, not merely small code fragments. If you look over the many threads on this forum, you'll see we do very well at helping when complete programs are posted. Often the problems lie in the minor details you don't anticipate are important, like the types used for certain variables. That's why we have the "Forum Rule" in red at the top of every page. Do this and we can help you much, much better.
 
Last edited:
Thanks. I understand all about arrays and timersl I'm trying to make the change from Pic Basic Pro (PBP). The whole task would be trivial in PBP, but very, very few PICs have 3 serial ports, which I need. It is a struggle for me to learn Arduino, since I have to in-learn a different way to do things.
PBP is much more "bare metal", and I have a hard time figuring out Arduino interrupts. Whether a routine uses them or not, priorities, etc. With PBP, you have to write most routines yourself, but then you know exactly what they do. With Arduino, I have to look at the libraries and try to figure out what someone else didl
 
It's easy in arduino, too.
For this task, just forget the interrupts (which are incredible easy on Teensy!) and any "complicated" things. They are really not needed here.
This task is very easy doable without all that. Just make some arrays(for double-buffering -> fill one array, calculate &send the second). (But remember, "int" is 32BIT)
A simple approach is sufficiant, don't underestimate the teensy-speed... don't make it more complicated than needed ;-)
Worst case, increase the buffers and all is good.
 
Last edited:
The following doesn't compile, but I think I'm close. I have a real problem with understanding the flow of the program. What is blocking, what isn't.
At any rate, I would appreciate a bit of help debugging this. I really miss PBP here, since all PBP input routines have a timeout variable.

void loop() {
if (Serial1.available()) { <----Grab anything that comes in
buffer1[x] = Serial1.read(); <---- Read it into Array posn 0
if (buffer1[x] == 254) { <---- Don't do anything until you find 0xFE
flag1 = 1; <---- Set a flag to indicate that we found a 0xFE

}
if (flag1 == 1) && (x == 0){ <--- Don't increment x, grab the length byte (second byte) ** COMPILER DOESN'T LIKE THIS LINE!
data1length1 = Serial1.read(); <------ Move the length into var
}
for (x = 0; x < data1length+6; x++) { < ----- Grab the rest of the data
buffer1[x] = Serial1.read();
}
}
}

}
 
The following doesn't compile, but I think I'm close.
if (flag1 == 1) && (x == 0){ <--- Don't increment x, grab the length byte (second byte) ** COMPILER DOESN'T LIKE THIS LINE!

You need parentheses around the whole 'if' conditions, that means:
if ((flag1 == 1) && (x == 0)){
or:
if (flag1 == 1 && x == 0){
 
Status
Not open for further replies.
Back
Top