Full Duplex Eight Port Serial Driver In C?
I'm moving an XMM C program I wrote on the Prop1 over to the Prop2.
I'm using the FlexProp compiler and so far it's going well as I've moved the various Menu displays over and I'm able to invoke them using the Prop2 Console Port (P62 & P63).
Now, it's time to bring additional serial ports into the picture, including an auxiliary diagnostic port, two GPS ports, and three radio ports.
I've been looking at this full duplex 8-Port driver for the Prop2 written in Spin2:
https://www.parallax.com/multiple-serial-port-16-object/
I've compiled and run the demo2, demo4, and demo16 programs using FlexProp and they work fine.
What I would like to do is have the C equivalent functions for these, so I tried using spin2cpp to convert them without success. I got these errors:
C:\Programs\Utility\Spin2Cpp\Sample>spin2cpp --p2 --ccode mpx_demo16.spin2
C:/Programs/Utility/Spin2Cpp/Sample/mpx_fullduplexserial.spin2:189: error: muldiv64 is not a function
C:/Programs/Utility/Spin2Cpp/Sample/mpx_fullduplexserial.spin2:191: error: pinstart is not a function
C:/Programs/Utility/Spin2Cpp/Sample/mpx_fullduplexserial.spin2:205: error: pinclear is not a function
C:/Programs/Utility/Spin2Cpp/Sample/mpx_fullduplexserial.spin2:330: error: waitct is not a functionC:\Programs\Utility\Spin2Cpp\Sample>
Has anyone tried doing this?
Is anyone aware of an 8-Port Full Duplex Serial driver with C functions for the Prop2 that will work under FlexProp or Catalina?
I suppose I could append the existing Spin2 code to my C code and make it work since FlexProp allows mixing of the code types.
But it seems that having pure C code functions to handle the serial I/O on these 8 ports would be much cleaner.
Comments
In FlexC, you can just do
to include a Spin2 object.
Thanks, that simplified things quite a bit.
For this test I'm attempting to use the standard Prop2 Console Port pins (P62 & P63) to keep things simple before I wander off trying to open a bunch of other ports using different pins.
I can call this function from C to open the port using FlexProp, but I can't get it to work because of the first variable:
As shown below, all of these arguments are self-explanatory except for the p_portctl which is the hub address. And that's where I'm totally lost. I don't even know where to begin when it comes to hub memory management.
@Cluso99 and @JonnyMac could you provide me with some insight on this hub address variable? Thanks.
p_portctl is a pointer (ie address) in hub where the control buffer is located.
This is a structure which will be used to interface to the mpx driver.
I have written a serial driver in C for the P2 that I use. It integrates into the FlexProp libraries allowing it to be used with file descriptors so you can use formatted output.
Full duplex on the P2 is kind of built into the hardware so it is much simpler to implement then on the P1.
While my library does not have full duplex in it it can be used as base to built one very simply. What I need to do is add a check function to check if a character is ready and then return with the answer which would be simple enough to add.
Here is a sample of a full duplex program that reads a BNO080 IMU.
Buy adding a check function to the cog function you could monitor N number of serial pins.
Here is a link to my Library enjoy.
Mike
PS: The easiest way to write code is to use Visual Studio Code. It has code completion and you can easily add the compile functions so you never have to leave the environment.
OK, so it's a pointer to a structure in HubRam.
But what are the elements of the structure and where is it located in HubRam?
Is it defined within the driver itself?
Or do I need to create a structure in C, populate it, assign it to a location in HubRam, then pass a pointer to that address to the Console.start() function?
And if I have to define, populate, and locate the structure in C, to what location in HubRam do I assign it?
I got used to working with the Four Port Serial driver in Catalina on the Prop1 where HubRam locations were handled behind the scenes without my intervention, so I guess I've lived a sheltered life in that regard...
Thanks, I'll take a look at your Library.
You have to define the data block. The structure is shown in the example code.
The reason you pass a pointer to the structure in the driver is because the driver needs to know where this structure (ie a block of hub memory) is located. The driver interfaces to this, and uses it to control which ports are used.
This is what the structure looks like (from the mpx_multiportserialdriver.spin2 at the end of the file)
This will usually be followed by the physical buffers for each slot. A slot is a single port (either a transmitter or a receiver - you would need one each for a whole full duplex serial port). So the buffer(s) are one for each transmit or receive port (or pin if you like to think this way). Often these buffers would follow the port_control and port_params bytes.
This structure is filled/initialised by the following call(s)
pub start(p_portctl, rxxport, rxpin, rxmode, rxbaud, p_rxstart, p_rxend, txxport, txpin, txmode, txbaud, p_txstart, p_txend)
or
pub openport(p_portctl, xport, xpin, xdirn, xmode, xbaud, p_xstart, p_xend) : status | spmode, baudcfg
In spin, you define the structure like this. You'll need to do this in C.
and
Forum software crashed again
So you need to create (reserve) a hub block like this
I'm having problems with the Forum Software regardless of which browser I'm using. I'm getting a lot of "Page Unresponsive" notifications, freezes, and glacial updates on the screen. Maybe Parallax should take a look at this problem and see what's happening. I don't remember it being like this before.
OK, regarding my code. I tried this late last night. It compiled fine but didn't do anything after I ran it:
I think I'm at a disadvantage because I don't know Spin.
I wonder if there could be some port conflict because the standard I/O of the compiler is to use P62 and P63, and I'm trying to do the same thing here by calling the driver. But I didn't #include <stdio.h> so that shouldn't be the problem.
I will try to muddle my way through this over the next few days and see if I can make sense of what is happening.
I hope there's a path forward on this as I would really like to be able to call your functions from C.
Unfortunately I don’t know C well enough to really help you so perhaps someone can chime in.
The spin routines such as txchar need to be converted to C. Have you done this?
You may need a delay before you try sending characters.
Can you try another port rather than the P62-63 paint? Can you attach something to this port?
If you have a LED and resistor (500 ohm to 4K7 should be fine just to test). Connect this to the tx pin. Then just loop outputting a character. These continuous pulses should light the led so we can see the led light. By delaying between groups of characters we should be able to see flashing.
What P2 board are you using? Is there a LED on the board we can use for the above test?
Also, zip all the files and attach it. One of our C experts might take a look and see the problem. Are you using Eric’s FlexProp?
I think in order to use mpx_fullduplexserial you also need mpx_multiportserialdriver. The port_control structure is used to pass information between multiportserialdriver and fullduplexserial. Here's a simple demo program in C:
Oh I missed noticing that the mpx_multiportserialdriver was not being started.
It runs in its' own cog and is pasm code.
Eric you are 100% correct on this. I didn't include the driver itself. Thank you for catching this. My code is working perfectly now.
https://www.parallax.com/multiple-serial-port-16-object/
@Cluso99 and @JonnyMac thank you very much for creating this driver and its various support functions.
One more function you might consider adding would be txCheck(). It would return the remaining size of the transmit buffer such that if it was 0 the user could opt to have their code go do other things while the buffer is emptying, rather than have the code stop and wait until the buffer is ready. @RossH implemented this function in Catalina which I've found to be very handy on the Prop1. Actually for my application it wasn't just handy, it was essential.
I can now proceed with transferring my GPS application over from the Prop1 to the Prop2 and have up to 8 full duplex Serial Ports in use. Right now my code has allocated 7 for use.
Again, many thanks to all of you for getting me across the finish line on this serial port driver.
I really look forward to putting this driver to work on my Project.
This should do the trick (in spin)
Mike I'm also taking a close look at your Serial functions as well.
I like the fact they are in C and thus I can tweak, adjust, and maybe even create a couple of new ones to meet the Project requirements.
If I assign your functions to a Cog then I should be able to get the full duplex functionality I need to handle all of the I/O traffic in the background without my main program having to micromanage it.
Right, the code is all C so you should have no issues. I have added the rxCheck function to my code but have not put it up on Github yet. I am not buffering the output but it should be simple enough to do.
One got ya maybe that there are not enough file descriptor available to do 8 ports.
Mike
A txCheck() function could be quite helpful too, especially if you are using low baud rates and you don't want your code waiting for the TX buffer to empty. That could be fatal in time critical situations.
Here's how @RossH put it when he added it to Catalina:
On the Prop1 I found this function to be absolutely essential because my GPS receiver could only do a max of 38.4Kbps and I couldn't afford to have the application wait until the TX buffer emptied enough to add another character. That would have killed the realtime functionality.
OK, now back to the serial port setup and my use of a Prop2 Rev B Eval Board to do this.
I've defined up to 8 Ports so far and FlexProp compiled fine.
However, it looks like only 3 ports can be defined and active at a time.
I have each Port outputting the message "This Is A Test" within a loop that has a 1 second
delay before outputting the message again.
If I define and try to use more than 3 Ports, the messages are no longer output on any Port.
Any way the functions can be tweaked to allow up to 8 simultaneous full duplex ports?
I have run 60 ports (ie 30 full duplex connections) by daisy chaining each tx to the next rx so I suspect that you may have a problem. IIRC I used 115200 baud.
Also you might consider posting your code (zip) so we can see what you're doing.
The file table is set at 10 files with stdin, stdout, and stderr taking the first 3 slots.
That should leave 7 slots available but there maybe others used.
In file includes/sys/unistd.h the value: #define _MAX_FILES 10
Otherwise the file system functions of the serial driver will have to be removed to allow as many ports as you need.
Mike
I changed the MAX_FILES to 20 and it appears to work.
I've got 8 serial ports open and in use at the same time.
I could see the first four ports (PortZero thru PortThree) by looking at the blinking LEDs on the Prop2 Eval Board.
I verified data transmission for ports PortFour thru PortSeven by connecting a scope to the transmit pin for each one.
I'm delighted!
Excellent job with your serial port driver! If you want to add buffering or any other features that would be even better.
Here's my code:
I tried the rxCheck() function you posted on Github but couldn't get it to work.
If a character is ready I want the function to return it, otherwise return -1. So, I just did a simple modification to your rxChar() function and it worked great:
I think the function you are looking for is _rxraw(). It returns -1 if no char is waiting, and 0-255 (the ascii code) if a char is available.
There is also a _txraw() companion function you may find useful.
Correct, but from what I can see those only work on the default serial port, what I call the Console port, which is P62 & P63.
For my application I'm using up to 8 serial ports, including the Console port, so I needed a way to access each of them from C.
@iseries (Mike) provided a list of functions to do that by reading from and writing to the smart pins.
Each serial port is configured as a FILE system, thus the standard file I/O functions can be used for input and output.
On my Prop2 Eval Board I have 8 serial ports open simultaneously and able to do data transfers. It's really slick.
Was trying to make it the same as the fdserial functions on the P1. Should have looked at the documentation.
rxReady is the function I did which is not the rxCheck. Will fix those to conform.
Mike
The next big challenge will be to add buffering, especially on the receive size.
If you can't add that to the existing functions, then I will need to do that with my application.
I suppose I can assign a cog to handle all the serial ports and incorporate ring buffers or something.
Considering all of the serial traffic taking place, I don't see how it can be done without buffering .
It is simple enough to do buffering on the receive side. I already gave you that code.
This will buffer up to 1024 bytes of data before the buffer wraps on itself.
Then you need to start a cog to fill the buffer.
Use GetData to empty the buffer.
Mike
My driver does the buffering. Just assign big enough buffers. Each can be a different size.
Hello, @Wingineer19
I've just released a beta version of Catalina (4.7) which contains the 8 port serial driver. The interface is intentionally the same as the existing 2 and 4 port serial drivers, but with the addition of two new functions you can use to manually open and close the ports at run time if you want. However, you don't need to use these functions because you can configure Catalina to open the ports for you automatically. This means that modifying a program that uses either the existing 2 or 4 port driver to use the new 8 port driver should be trivial. Attached is the latest version of the header file. There are a couple of simple test programs in the demos\serial8 folder.
rossh
@RossH,
Many thanks for your work on this.
I also saw your other post under "Catalina" announcing the 4.7 Beta release.
I downloaded and installed 4.7 and will take it for a spin (no pun intended).
I like the idea that I can open and close the serial ports on the fly under the Prop2.
I'll let you know if I see any odd or weird behavior.
@RossH,
OK, I got the 4.7 Beta release installed.
I modified my Miniplate test program to use the s8 library functions.
I'm still using Code::Blocks so I just added the -lserial8 by editing the Project->Build Options->Linker Settings and adding -lserial8 to the Other Linker Options box.
The program compiled fine. I chose the Tools->Download To HubRam And Interact option under Code::Blocks and I was able to pull up the various Menus on the program. Excellent.
Then, I wanted to upload the program to the Prop2 Eval Board so from the command prompt I typed:
flash_payload Miniplate.bin and it appeared to upload the program to the board. Or at least it didn't give any errors.
But when I reboot the P2 nothing ever shows up on my HyperTerminal screen. No Menus, nothing. The Port is open but nothing ever shows up on the screen.
Maybe it didn't upload correctly? Maybe the .bin file is incorrect? I want to make sure that I don't need to select the EEPROM, Flash, or SD Card Loader options under Code::Blocks like I do for the Prop1.
If I compiled the program correctly (apparently so since I can upload to HubRam and Interact), and it uploaded correctly to the Board (no errors received), then I have another mystery as to why it doesn't work when using HyperTerminal.
EDIT1: OK, reviewing the Catalina Prop2 Manual, it certainly appears that I don't need to use any Loader option unless I want to upload to an SD Card, which I don't. I want to program the on-board flash. I've checked the Flash Switch settings on the Board and it appears to be OK. I'm able to upload a program from FlexProp to the Board and execute it, but not having the same luck using Catalina. The investigation continues...