PScript – Part 9 – Streams

Both Ethernet, UART, USART, USB, SPI, I2C, I2S and CAN might be used as a serial 2-way, buffered byte stream. This is an important abstraction as it allow for an easy, uniform usage of byte streams regardless of underlying technology. To support this I create the same interface on all technologies and an object “stream” that contain the buffers.

open Open a stream.
close Close a stream.
send Send bytes through the stream.
receive Receive Bytes from the stream.
Event onreceive Receive bytes through event.

The open function dictate if the stream is continuous bytes or packages. CAN (as an example) have max 8 byte packages on the messages, but it is easy enough to create virtual byte streams or larger packages if that is what you want.

CAN also have IDE, RTR, Standard and extended ID that is part of layer 1 header. I need to allow the user to control those and at the same time abstract away from CAN. This is what stream does. If you want to access raw CAN you access the CAN1 or CAN2 objects, but declaring a stream on CAN1 convert that to a buffered, 2-way byte stream where the details of the underlying CANbus is hidden from us.

Examples

Stream1.Open(CAN1, 115200)
Stream1.Send("Hello World")
Stream1.Close()

In this case I connect Stream1 to CAN1, but I could have connected it to any object capable of serial stream support. Receiving bytes can be done in two ways. You can call “receive” as follows:

uint8 buf[40]
Stream1.Open(CAN1, 115200)
int x = Stream1.Receive(buf)
Stream1.Close()

This will receive a maximum of 40 bytes from Stream1. But, a more elegant way of receiving is using the event as follows:

Event Stream1.OnReceive(uint8 bytes[])
   ...
End

I sneaked in empty brackets here to indicate that this is an array of uint8 passed as a parameter. In effect we will only pass a hidden pointer to the location in the buffer. This location will be destroyed as the event finish, so we need to copy the bytes to a local storage that survive the scope of the event.

uint8 myCopy[20]
Event Stream1.OnReceive(reference uint8 bytes[])
      myCopy = bytes
End

The empty brackets are used to indicate that we receive an array of bytes. The keyword reference indicate that this is a reference to an array, not the array itself. I do however still have one issue here – I need to tell stream1 how many bytes I accepted.

As I end the event, Stream1 will by default mark all bytes that was in the reference as received. This is ok if you receive packets or want to receive everything, but it is not ok if you need to examine the stream and receive only some of the bytes now, waiting for more to arrive later.

uint8 myCopy[20]
Event Stream1.OnReceive(reference uint8 bytes[]) : uint16 bytesReceived
     myCopy = bytes
     bytesReceived = myCopy.length;
End

The change above solves that. I add a return value to the event that is bytesReceived and set this default to the same number as inserted allowing the event to specify a different number if needed.

myCopy = bytes will in effect transfer max 20 bytes, but it might transfer less.

Again, I need to experiment a little with syntax, but I think this starts to look good.

Leave a Reply