Changing Baud rates using "fullDuplexSerial4port.spin"?
lab_ges
Posts: 91
in Propeller 1
Hi All,
I have a project where I am using a propeller processor (with serial buffer IC's) to intercept data from one serial port, filter that data in real time (blocks some commands & acts on others, inserts some extra data from the other serial ports / i2c ports as required) and then sends the data out on another port.
This is working fine in both directions using "fullDuplexSerial4port.spin" and separate COGs for the send and receive data paths.
I am using SPIN for all functions & so far and it is working well.
So now for the challenge.
Both the input & output ports use the same baud rate, but the source baud rate can be changed by one of these commands I am intercepting, so I need to be able to alter my baud rate in response to a command to do so.
To do this, I think I would follow a process like this:-
(1) Stop the Serial driver;
(2) Change the SysBaud variable (this is the variable for just these 2 ports, the other 2 are fixed at 9600);
(3) re-start the start_uarts routine
My question is, Have I missed something simpler?
Is there a way of just changing the 2 baud rates concerned without shutting down the "fullDuplexSerial4port" driver and re-initializing it?
I can't think how but I am fairly new to SPIN and the propeller so could easily be missing something obvious.
My thanks for any comments on this topic
I have a project where I am using a propeller processor (with serial buffer IC's) to intercept data from one serial port, filter that data in real time (blocks some commands & acts on others, inserts some extra data from the other serial ports / i2c ports as required) and then sends the data out on another port.
This is working fine in both directions using "fullDuplexSerial4port.spin" and separate COGs for the send and receive data paths.
I am using SPIN for all functions & so far and it is working well.
So now for the challenge.
Both the input & output ports use the same baud rate, but the source baud rate can be changed by one of these commands I am intercepting, so I need to be able to alter my baud rate in response to a command to do so.
To do this, I think I would follow a process like this:-
(1) Stop the Serial driver;
(2) Change the SysBaud variable (this is the variable for just these 2 ports, the other 2 are fixed at 9600);
(3) re-start the start_uarts routine
S4P.Stop ' stop 4 port serial driver (SysBaud must contain new system baud rate by now) start_uarts ' re-initialise the UARTs
My question is, Have I missed something simpler?
Is there a way of just changing the 2 baud rates concerned without shutting down the "fullDuplexSerial4port" driver and re-initializing it?
I can't think how but I am fairly new to SPIN and the propeller so could easily be missing something obvious.
My thanks for any comments on this topic
Comments
Thanks for your reply, I can sort of understand what you are saying but have no idea how to do it.
I have only been using the Propeller chip for a few months and am still getting a grip on SPIN, though I am finding it fairly easy to follow.
I looked through the "fullDuplexSerial4port" source code but the only references to the baud rate that was obvious to me in the code was:- which change the variables
Since I am interested in changing the baud rates for ports 0 & 1, I guess I could recalculate the values of and but am not sure if that is all that would be needed?
It looks like that part of memory is over-written by the serial buffers anyway so I guess that wouldn't work?
The bit ticks are used by the WAITCNT instruction to synchronize the bit timing so that for a 96MHz clock rate and 9600 baud timing the bit ticks = 10,000 for instance. So that is all that is needed really although I like to set a start bit sample time as well that is half that bit time (20,000) plus a few extra cycles to compensate for overheads.
However it's important to realize that bitticks in the cog cannot be set directly by Spin, it can only set a hub variable and pass the pointer to those parameters during a cognew in the PAR register as @rx_head. But there is no need to stop and start the cog since the information to change the baud rate is provided in the serial data stream. If you can tell me what this command looks like I can devise a scheme to handle this in the receive cog plus give you the code for that which would probably include a modified FDS object.
The command string is very simple just "B09600<cr>
I was thinking about just offsetting the String by +1 and then converting the next 5 digits to decimal as the baud rate, and passing that into the code to do the change, that was when I thought it could be done in Spin of course.
The equipment is old and will only change up to 19200 anyway, the only valid rates are 'B00300'; 'B00600'; 'B01200'; 'B02400'; 'B04800'; 'B09600'; 'B19200' and I don't think the lower 2 or 3 would ever be used.
Also, looking at the code for "fullDuplexSerial4port" I am not sure they are supported either as they aren't mentioned in the CON section of the code, but I also don't see where the mentioned ones are used and think they are perhaps just examples.
However it may be simpler to accumulate all decimal digits as a binary long but any non-digit character can clear it but that character would also be latched as a "command". Therefore the B would clear the accumulator as any other character would but the B would be latched as a possible command. Then as the digits come in they are converted to binary (ch-$30) and added to the accumulator after a x10 decimal shift. When the cr is sensed it checks the "command" and if it is a B it then checks the accumulator for a valid number (and possibly a digit count) which is then used to lookup a small table of bit tick value in the PASM cog to load into bit ticks. Instantly that cog has changed its baud rate, all without any Spin overhead or stopping/starting. That sounds simple enough and it's fast and it's effective. I will look at some code shortly.
If you have cogs to spare it is easier to assign a cog as a receive or transmit channel so these baud rates can be different.
My only concern in doing it all in PASM is how does the spin program know that the Baud rate has changed?
I suppose I could still monitor these same characters in spin to determine this, they would still pass through in the serial stream in the normal way, wouldn't they? The reason this is important is that I would need to save the BAUD rate to eeprom to recall it at the next power up. Otherwise we could get the Baud rates out of sync.
I would need to change both ports 0 & 1 at the same time as I have a COG receiving on one port and transmitting the filtered code to the other in real time (more or less), ditto for a second COG in the opposite direction, I do have a few spare cogs at the moment but suspect they will be used sooner rather then latter by some un-written routines I have planned.
Must be getting late in Brisbane, have a good night.
The baud rate can be written to hub, perhaps even back to the bit_ticks variable. Just monitor that for a change and write to EEPROM. Since the Propeller loads 32k on boot from EEPROM mirrored to RAM you will find that the location of the hub variable in RAM is the same as it is in EEPROM, so you don't need a different location etc, just eewrlong(@myloc,myloc) or something like that and it will be automatically restored at boot.
I'm not sure what timing issues you might have but obviously any system that changes baud rates on the fly should have some kind of delay in there, however short. This time can be used to read/write the hub although I'm not sure of the exact details that you may be aware of to make the final implementation decisions.
Here is the patch implemented on port0, the last two lines of this snippet: This isolates the ascii codes from 250 to 255. The code at Peeti then applies the patch. You could use different codes for different baud rates. Those codes are not transmitted, but otherwise the port0 tx operates normally. The code at Peeti exits with a jmp #transmit. In this modified version of the 4port object, the patch was supporting a watchdog timer (Peeti is the name of my son's dog, a high strung chihuahua) via the cog counters.
I don't think I really appreciated just how much I still needed to learn but I am getting the idea now.
The two different approaches to the same problem are an education in themselves.
I am not sure I would have thought of either technique as I was thinking along the lines of restarting the 4 port COG with the new values, I now think this would probably have caused my other 2 COGs to crash since they were constantly calling the one I disabled, a whole can of worms there.
Modifying the BAUD rates directly in the "fullDuplexSerial4port" routine seems like the way to proceed, though I will probably need more hints as I get to grips with it.
Sorry Peter but I just don't know enough to even attempt yours at this point.
Below is my first poor attempt at PASM, in the form of the Peeti subroutine suggested by Tracey.
I would need to add this routine up to 4 times, once for each channel i need to change, if i can get it working that is.
I am trying to check the baud rates in reverse since I think the higher rates are more likely to be used and by using CMP with WC it is logical (to me) to do the higher values first and if the value is less then it falls through to the next comparison. I did toy with the idea of filtering the value of "txdata" with an "xor" filter of F8 to give a result of 0 - 7 for the baud rates but don't think this would have gained me anything much.
I don't know how to calculate the longs for bit_ticks & bit4_ticks (shown below as {VALUEa} & {VALUEb}
in SPIN it looks like this but can't see how I would do them in PASM, Any clues? I realize the above is probably very long winded, particularly if used 4 times but any comments about being the correct track or not with this?
Thanks all
Add a variable to DAT section that contains the constants that are used for patching the code.
bit_ticks_300 long clkfreq/300
The bit tick constants for other 7 rates that you want are easily calculated from the 300 baud constant.
Then, (caveat emptor) I don't think you necessarily have to write the value back to the hub in pasm, although you certainly could do so with a WRLONG. You will be decoding the new baud rate in spin, so you could update the hub version from Spin, and more importantly, the eeprom version so that it will survive a power cycle or reset.
I have added the "bit_ticks_300" to the DAT section of "fullDuplexSerial4port.spin".
The [ ] below gives my interpretation of the meaning of your code and some questions, like I am not sure how T1 got it's initial value? You say " You will be decoding the new baud rate in spin, so you could update the hub version from Spin".
Yes that is true, I will store the Hub version of the baud rate prior to sending the command to "Tx0" buffer from spin & also update the eeprom values from spin.
Is my code analysis even close to the truth??
mov t1, txdata . ' txdata contains a number from 248 to 255
although it's more efficient written like this, (again caveat emptor) You probably don't want to change the baud rate on port 0, but it is not out of the question so long as Spin compensates too. The pasm can change the parameters for any of the ports, but only in the same cog. I'm not sure what you meant in your first post by, "separate COGs for the send and receive data paths". Your code analysis is fine except for my mistake and the bit about only being able to change port zero.
Come to think of it, the Spin should not write anything to the hub location of the bit_ticks variables. This is because once those variables get loaded to the cog during the Start method, the contents of those locations is no longer needed in Spin, and they become repurposed as part of the data buffers. So, don't even think of putting anything there. See the note in the program in the declared DAT section, "Start of HUB overlay".
On the other hand, Spin could update those locations in the eeprom image so that they will be loaded at startup. For that to be effective, you will have to take out the initialization of bit_ticks for the affected ports in the AddPort method. Delete the two lines from the AddPort method, and instead enter your default starting baud rates in the DAT section. and also for bit4_ticks. That is one way. There are other ways to do it to fill in the baud rates at Addport time.
If I am understanding you correctly you intended this code to be on a fixed port (preferably one that doesn't ever have a baud rate change) but to have the code in it to change the baud rates of other specific targeted ports?
Does that sound right??
In my application, I use a COG to read data on Port0 & immediately send it out on Port1, but am Monitoring the bytes in some cases blocking or Adding specific bytes or strings as required. The other direction is the similar in a second COG, it reads Port1 & sends the data out on Port0 with monitoring / filtering, there is some interaction between the 2, in some cases I look for something coming in on port0, block it and set a variable to tell the other "SIDE / COG" to send back a reply. Sounds convoluted but works well and means I don't need to add any extra delays caused by buffering everything yet again.
Due to the above I will always need both ports 0 & 1 to have the same baud rate (of course I could just change the port numbers in code if I really need to keep port 0 fixed for some reason, the other 2 serial ports will be fixed at 9600 so I could add your code to one of those.
The former values are in Hub Ram but I guess this "bit4_ticks1" is now in COG ram with the same <local> name?
I assume that happens because the DAT section code is copied to COG ram then executed, thus allowing you to re-use the HUB ram as buffer space, rather cleaver really.
I now have a working Baud change routine
I have called this version "fullDuplexSerial4Port(CB).spin" the changes to "fullDuplexSerial4Port.spin" V1.1 to allow the change Baud function.
Full Credit to Tracy Allen for the code, all changes are marked TTA2017.
My thanks too, to "Peter Jakacki" who helped get me started on this, one day I might understand enough PASM to do it your way.
You've answered your own questions. When transferred to the cog, the code there refers to locations there by the same variable name they have in the hub. It only knows the hub location of that variable when that information is initialized or passed in.
In your scenario I think it would be okay to change the baud rate on any port including the one that takes the commands. The important thing is that the baud rate change shouldn't happen in the middle of receiving or xmitting a character. The only(?) consequence of a collision would be a junk character or maybe loss of sync if the characters are sent/received in tight packets.
Does your app use all 4 ports in each of the two cogs? Check that the sizes of the receive and transmit buffers are fixed by CONstants to what the app needs.
My NEW Port arrangement is as follows.
I renumbered them to use Port0 as the Baud Rate controller.
I have lettered the COGs below just to indicate what each COG does, I don't know or care which actual COG is used for what task (yet at least).
Quad Serial Port #
Port 0 Fixed 9600 Baud Rx only , though this might change (Tx used to control the Baud Rates of Ports 1&2) - controlled by COG_A
Port 1 Up-link Port - COG_B/C, see below
Port 2 Legacy Equipment Port - COG_B/C, see below
Port 3 Fixed 9600 Rx & Tx - COG_D
COG_B (Rx on Port 1 & Tx on Port 2) | Most of the work is done by these 2 cogs
COG_C (Rx on Port 2 & Tx on Port 1) |
COG_E of course the Quad Serial Port driver itself uses one COG
COG_F Main Program startup also uses one but I may stop this after startup but is left looping for debugging at the moment
COG_G I2C driver
I have detailed my usage above for you, but no, I only Transmit & Receive in the same COG, not necessarily to the same port though, as explained above.
I have structured my code so that all TX to a specific Port is from a specific COG for example.
I wasn't sure it was wise to try transmitting (or receiving) from different COGs, looked too complicated.
Having said that I am not sure how much buffer space I will need in any of these, the defaults seem to work fine, so far at least.
as I couldn't think of another way to make sure the variable would be correct regardless of the clock speed as you don't seem to be able to do calculations with variables in the DAT section.
It seems to work OK but would there have been a better way of doing this? It seems like I am doing the same thing twice.
In my Case the the 80Mhz/300 is fine but if I change the crystal to 6Mhz I would want the same code to work there as well without having to change the driver code for all permutations. Any hints of doing this in a better way?
When the Prop reset, will it need to maintain the current baud rate, that might have been changed during operation? The current baud rate values have to be stored somewhere in the eeprom.
I'm still mixed up, not understanding the mix of cogs and ports, or I'm wondering if it is your misunderstanding. The Spin code for the 4-port object would normally be running in what you call COG_F, and its PASM component to service 4 ports would be in an auxiliary cog launched by the Start method.
Unless there is some speed bottleneck, it looks like all of the serial ports 0 to 3 could fit into 1 pasm cog. I don't understand why you mention using 5 cogs in connection with the serial port driver, A, B, C, D and E. How so?
Broadly I am trying to upgrade some old equipment to add functions but leaving existing functions intact (for the most part). To do this I need a lot of serial ports as will as I2C for the Real time clock & IMU units.
I then "Peek" at the main data stream (in both directions) & depending on what I see can block the data or "Poke" some extra strings into the data stream.
At startup the code is loaded then runs MAIN.
MAIN just populates some defined VARs then does the initialization & loads my default drivers [i2c, UARTs (your "4 serial ports 0 to 3 could fit into 1 pasm cog") & DEBUG - all in separate COGs] then runs my SPIN code to deal with the comm ports, in separate COGs (these deal with the data streams & communicate with the driver).
Serial Ports 1&2 are where most things happen & I treat these as 2 separate paths (Rx on Port 1 & Tx on Port 2) in one COG & (Rx on Port 2 & Tx on Port 1) in another COG. I have about 250 lines of Spin code running in each of these COGs.
It is possible I might eventually move the other 2 serial ports back into these 2 cogs as well as they are very simple but at the moment they are a COG each as well. This is all a steep learning curve for me and perhaps I will discover I am doing it wrong but it seems to be working well to this point.