Shop OBEX P1 Docs P2 Docs Learn Events
C: Testing Serial Protocol And Simulating Inter-Propeller Communication — Parallax Forums

C: Testing Serial Protocol And Simulating Inter-Propeller Communication

idbruceidbruce Posts: 6,197
edited 2015-04-22 11:48 in Propeller 1
I am currently in a position, where I want to move forward with my programming, which involves inter-Propeller communication, but at the moment, I am just not too enthusiatic, about doing the necessary board modifications.

As I was pondering the simplest solution, I came up with an idea. I don't know if anyone has done this before, but I have never seen anything posted on the subject.

Anyhow, instead of doing the necessary board modifications for inter-Propeller communication, I simply want to simulate it, and I was thinking about using the Propeller Board of Education for this simulation.

The main idea is to pass a C struct from one prop to another

Here are my thoughts....
    2 pairs of IO pins inter-connected with a 2.2K resistor, with each pair having an RX and a TX end.
    Have two seperate instances of full duplex serial running in two seperate cogs, with each having RX and TX pins, and with each having a header include for the C struct

With this type of setup, I think I can simulate inter-Propeller communication. Will this work for a proper simulation?
«1

Comments

  • jmgjmg Posts: 15,173
    edited 2015-04-18 04:23
    I'd suspect 2k2 is a little high for high-speed coms testing and 330~470R may be better.
    It should give a good starting point, but the signal phases on a single prop tester will be less variable than on two props, unless you have a common oscillator ?
  • idbruceidbruce Posts: 6,197
    edited 2015-04-18 05:37
    jmg

    If I am understanding you correctly, it will work, but I will have better results with the test setup, as compared to the actual inter-prop communications, unless I have a common oscillator. Is this correct?

    No, I will not have a common oscillator.
  • kwinnkwinn Posts: 8,697
    edited 2015-04-18 06:33
    I tested prop to prop comms using full duplex serial up to 230Kbaud on a single Project board and with 2 Project boards as listed below. All the tests were running overnight so approximately for 10 hours. The 1000 feet of cat6 cable was still in the box.

    Single board cog1 rx pin same as cog 2 tx pin & vice-versa - No errors up to 230K

    Single board, 4 separate pins, 12" wire from rx to tx. No errors up to 230K.

    Single board, 4 separate pins, RS485 drivers, 1000 feet cat6 pairs from rx to tx. No errors up to 230K.

    Two boards, 4 separate pins, RS485 drivers, 1000 feet cat6 pairs from rx to tx. No errors up to 115K, 13 single character errors at 230K
  • idbruceidbruce Posts: 6,197
    edited 2015-04-18 07:54
    kwinn

    Thank you for that very helpful information. It is always nice to have test case scenarios :)
  • jmgjmg Posts: 15,173
    edited 2015-04-18 13:36
    idbruce wrote: »
    If I am understanding you correctly, it will work, but I will have better results with the test setup, as compared to the actual inter-prop communications, unless I have a common oscillator. Is this correct?
    Correct, there are two additional details to watch with separate oscillators -
    * SysCLK phase can be anything, so jitter/uncertainty is worse by + ~1 sysclk
    * if using Aync, & long unbroken duplex packets can need extra stop bits to avoid data squeeze errors.
    With same-batch crystals, you should be ok to > 1000 bytes. With resonators, that can be 100 bytes.
  • idbruceidbruce Posts: 6,197
    edited 2015-04-18 18:30
    jmg

    Thank you for the clarification and extra information.

    Had to get some rest. Now it is time to start setting it up and do some testing :)

    I rarely use the Prop BOE, but in sitiuations like this, it is an indispenable tool.
  • idbruceidbruce Posts: 6,197
    edited 2015-04-20 14:57
    Well I finally got around to setting up the Prop BOE and writing some code for Prop to Prop simulation. Itried to keep everything as close as I could pertaining to real use. As mentioned, it has been quite some time since I have messed around with serial, and serial comm has always been a bear for me. Anyhow, I am having problems and could sure use some guidance.

    There are two main sections of code, which I will discuss, and I have also atttached the simulation project as it stands.

    If I comment the following section of code, the program works as directed, and if I uncomment it (attempting to serially send the struct) the code fails. The following code is in the main function:
    				/////////////////////////////////////////////////////////////
    				// Insert test code here
    				/////////////////////////////////////////////////////////////
    
    				// Copy GCODE_STRUCT into char array for sending
    				char send_struct_array[sizeof(current_gcode)];
    				memcpy(send_struct_array, &current_gcode, sizeof(current_gcode));				
    
    				for(uint32_t i = 0; i < sizeof(send_struct_array); i++)
    				{
    					fdserial_txChar(parser, send_struct_array[i]);
    				}
    
    				while(gcode_struct_received == false){}
    
    				gcode_struct_received = false;
    
    				/////////////////////////////////////////////////////////////
    

    In the next bit of code, located in processor.c, this runs in a seperate cog and attempts to read the struct being sent, and further copy it into a global GCODE_STRUCT, identified as sent_gcode, for serial terminal display purposes. In normal use case, GCODE_STRUCT received_gcode, would simply be added to an array of GCODE_STRUCT(s)
    void serial_processor(void *par)
    {
    	fdserial *processor;
    
    	processor = fdserial_open(PROCESSOR_TX, PROCESSOR_TX, PROCESSOR_MODE, PROCESSOR_BAUD);
    
    	do
    	{
    		if(fdserial_rxReady(processor) != 0)
    		{
    			volatile GCODE_STRUCT received_gcode;
    			char received_struct_array[sizeof(received_gcode)];
    
    			for(uint32_t i = 0; i < sizeof(received_gcode); i++)
    			{
    				received_struct_array[i] = fdserial_rxChar(processor);
    			}
    
    			memcpy(&received_gcode, &received_struct_array, sizeof(received_gcode));
    			memcpy(&sent_gcode, &received_gcode, sizeof(sent_gcode));
    
    			gcode_struct_received = true;
    		}		
    	}
    	while(serial_processor_cog > 0);
    }
    
  • idbruceidbruce Posts: 6,197
    edited 2015-04-20 15:32
    Going a step further, if I insert two print commands in the test code, only one of them is issued, so gcode_struct_received is never equaling "true":
    				/////////////////////////////////////////////////////////////
    				// Insert test code here
    				/////////////////////////////////////////////////////////////
    
    				// Copy GCODE_STRUCT into char array for sending
    				char send_struct_array[sizeof(current_gcode)];
    				memcpy(send_struct_array, &current_gcode, sizeof(current_gcode));				
    
    				for(uint32_t i = 0; i < sizeof(send_struct_array); i++)
    				{
    					fdserial_txChar(parser, send_struct_array[i]);
    				}
    
    				print("Hello\n");
    
    				while(gcode_struct_received == false){}
    
    				print("Hello\n");
    
    				gcode_struct_received = false;
    
    				/////////////////////////////////////////////////////////////
    

    So the problem lies in the receiving cog.
  • Dave HeinDave Hein Posts: 6,347
    edited 2015-04-20 15:44
    Create a simple version of your test program that just sends an ASCII string and print out what you receive from the serial port. Once you get that working incorporate it into your program.
  • idbruceidbruce Posts: 6,197
    edited 2015-04-20 15:49
    Dave

    I have it narrowed down to:
    if(fdserial_rxReady(processor) != 0)
    

    Bytes are being sent, but fdserial_rxReady is never returning > 0.
  • DavidZemonDavidZemon Posts: 2,973
    edited 2015-04-20 15:54
    Bruce,

    If you're only testing software, you can make your hardware really simple. For demonstrating a duplex UART in PropWare, I just connected two random pins together - 12 and 13 to be exact - with no resistors or anything. One cog outputs on pin 12 and the other cog reads pin 13. It's the only time I ever use the 0.1" breadboard wires! :P

    I've suspected for a while (but haven't tried yet) that you could even use the same pin. The you don't have any hardware at all :)

    Here's the code for the example I'm referring to:
    http://david.zemon.name/PropWare/DuplexUART__Demo_8cpp_source.html
  • idbruceidbruce Posts: 6,197
    edited 2015-04-20 16:13
    David

    Looks like a fairly nice site that you have going there.

    As for the hardware.....

    #define PROCESSOR_TX 0
    #define PROCESSOR_RX 2
    #define PARSER_RX 1
    #define PARSER_TX 3

    A resistor between pins 0 and 1, and a resistor between pins 2 and 3. Nothing too complex :)
  • idbruceidbruce Posts: 6,197
    edited 2015-04-20 17:07
    Okay, going back to the main, with the following test code:
    				/////////////////////////////////////////////////////////////
    				// Insert test code here
    				/////////////////////////////////////////////////////////////
    
    				// Copy GCODE_STRUCT into char array for sending
    				char send_struct_array[sizeof(current_gcode)];
    				memcpy(send_struct_array, &current_gcode, sizeof(current_gcode));				
    
    				for(uint32_t i = 0; i < sizeof(send_struct_array); i++)
    				{
    					print("%d\n",fdserial_txChar(parser, send_struct_array[i]));
    					print("%d\n",send_struct_array[i]);
    				}
    
    				print("Hello\n");
    
    				while(gcode_struct_received == false){}
    
    				print("Hello\n");
    
    				gcode_struct_received = false;
    
    				/////////////////////////////////////////////////////////////
    

    But more specifcally the following two lines:
    					print("%d\n",fdserial_txChar(parser, send_struct_array[i]));
    					print("%d\n",send_struct_array[i]);
    
    

    According to documentation, these two print commands, should output equal results, yet the top print command always outputs -1, while the lower one varies, but never outputs -1.

    EDIT: So I am now assuming that fdserial_txChar is never succeeding.
  • jazzedjazzed Posts: 11,803
    edited 2015-04-20 17:17
    idbruce wrote: »
                        print("%d\n",fdserial_txChar(parser, send_struct_array[i]));
                        print("%d\n",send_struct_array[i]);
    
    

    According to documentation, these two print commands, should output equal results ...

    What documentation said this?
  • idbruceidbruce Posts: 6,197
    edited 2015-04-20 17:23
    jazzed

    SimpleIDE help for the fdserial library says:
    int fdserial_txChar(fdserial *term, int txbyte)

    Send a byte by adding it to the transmit buffer.

    Parameters
    *term Device ID returned by fdserial_open.
    txbyte is byte to send.

    Returns
    The byte that was sent, or returns the byte that was received if mode bit 3 was set in the fdserial_open call.

    If the byte was sent, that should be the return, therefore they should be equal shouldn't they?
  • jazzedjazzed Posts: 11,803
    edited 2015-04-20 17:56
    idbruce wrote: »
    jazzed

    SimpleIDE help for the fdserial library says:



    If the byte was sent, that should be the return, therefore they should be equal shouldn't they?


    You have found a bug. The function always returns -1 unless the mode bit 3 was set.

    Funny thing about documentation. Sometimes it states an ideal situation. The real documentation is the code in this case.
    [TABLE]
    [TR]
    [TD]/*[/TD]
    [/TR]
    [TR]
    [TD] * tx sends a byte on the transmit queue.[/TD]
    [/TR]
    [TR]
    [TD] * @param txbyte is byte to send.[/TD]
    [/TR]
    [TR]
    [TD] */[/TD]
    [/TR]
    [TR]
    [TD]int fdserial_txChar(fdserial *term, int txbyte)[/TD]
    [/TR]
    [TR]
    [TD]{[/TD]
    [/TR]
    [TR]
    [TD]  int rc = -1;[/TD]
    [/TR]
    [TR]
    [TD]  volatile fdserial_st* fdp = (fdserial_st*) term->devst;[/TD]
    [/TR]
    [TR]
    [TD]  volatile char* txbuf = (volatile char*) fdp->buffptr + FDSERIAL_BUFF_MASK+1;[/TD]
    [/TR]
    [TR]
    [TD][/TD]
    [/TR]
    [TR]
    [TD]  while(fdp->tx_tail == ((fdp->tx_head+1) & FDSERIAL_BUFF_MASK))[/TD]
    [/TR]
    [TR]
    [TD]      ; // wait for queue to be empty[/TD]
    [/TR]
    [TR]
    [TD]  txbuf[fdp->tx_head] = txbyte;[/TD]
    [/TR]
    [TR]
    [TD]  fdp->tx_head = (fdp->tx_head+1) & FDSERIAL_BUFF_MASK;[/TD]
    [/TR]
    [TR]
    [TD]  if(fdp->mode & FDSERIAL_MODE_IGNORE_TX_ECHO)[/TD]
    [/TR]
    [TR]
    [TD]      rc = fdserial_rxChar(term); // why not rxcheck or timeout ... this blocks for char[/TD]
    [/TR]
    [TR]
    [TD]  return rc;[/TD]
    [/TR]
    [TR]
    [TD]}[/TD]
    [/TR]
    [/TABLE]
    
    
    
    

    BTW, a cleaner way to do this is by using "writeChar(parser, ch);"
    That way if you change the device you won't have to change hundreds of lines of code.
  • idbruceidbruce Posts: 6,197
    edited 2015-04-21 06:06
    After many hours, I finally have it nailed down to this loop:
    		for(uint32_t i = 0; i < sizeof(received_gcode); i++)
    		{
    			received_struct_array[i] = fdserial_rxChar(processor);
    		}
    

    1) Is there anything visibly wrong with with adding characters to an array like this?
    2) This loop is attempting to process 324 bytes, so what is the maximum size of a character array for Propeller GCC?
  • DavidZemonDavidZemon Posts: 2,973
    edited 2015-04-21 06:18
    idbruce wrote: »
    After many hours, I finally have it nailed down to this loop:
    		for(uint32_t i = 0; i < sizeof(received_gcode); i++)
    		{
    			received_struct_array[i] = fdserial_rxChar(processor);
    		}
    

    1) Is there anything visibly wrong with with adding characters to an array like this?
    2) This loop is attempting to process 324 bytes, so what is the maximum size of a character array for Propeller GCC?

    The size of an array is only limited by how much memory you have available. 324 bytes should not be a problem.

    The only thing I see that might be an issue is the lack of a null-terminator.
  • DavidZemonDavidZemon Posts: 2,973
    edited 2015-04-21 06:24
    idbruce wrote: »
    for (uint32_t i = 0; i < sizeof(received_gcode); i++)
    

    To be really nitpicky...

    I prefer using size_t for instances like this. It should compile into identical code, but since you're comparing `i` against a size_t value, might as well let `i` be an instance of size_t.
    for (size_t i = 0; i < sizeof(received_gcode); i++)
    
  • idbruceidbruce Posts: 6,197
    edited 2015-04-21 06:28
    David
    The only thing I see that might be an issue is the lack of a null-terminator.

    I suppose I should have mentioned, that it is never exiting the loop. By commenting out the loop, I have verified, that characters are indeed getting to the function, so the fdserial object is definitely valid, and the function permits further processing in the parent cog. I just can't understand why it isn't exiting a loop of 324 iterations.
  • davidsaundersdavidsaunders Posts: 1,559
    edited 2015-04-21 06:29
    idbruce wrote: »
    After many hours, I finally have it nailed down to this loop:
    		for(uint32_t i = 0; i < sizeof(received_gcode); i++)
    		{
    			received_struct_array[i] = fdserial_rxChar(processor);
    		}
    

    1) Is there anything visibly wrong with with adding characters to an array like this?
    2) This loop is attempting to process 324 bytes, so what is the maximum size of a character array for Propeller GCC?
    the only thing I see wrong with it is that it will process the entire array regardless of how many characters are in it.

    The maximum size for an array is equal to the maximum available continuous memory.
  • Dave HeinDave Hein Posts: 6,347
    edited 2015-04-21 06:33
    You declared received_gcode and received_struct_array within a function, which puts them on the stack. Your stack is declared as 100 ints, which is 400 bytes. If the struct is 324 bytes in size and the array is also the same size you are trying to fit 648 bytes of data in a 400-byte stack. You need to either increase the size of the stack or declare the struct and array outside of the function.

    BTW, you don't really need to declare received_struct_array as an array. It could just be a pointer that contains the address of the struct as in "char *received_struct_array = &received_gcode;". That way you don't need to allocate a 324-byte array, and you don't need to do the memcpy after you receive the data.
  • idbruceidbruce Posts: 6,197
    edited 2015-04-21 06:44
    Dave

    Okay, I got some good information out of that.... However, several things have changed since the original post, one of which is the stack size, it is now at 1400.

    I took my troubleshooting one step further and just commented out this line, at which point it exits the loop.
    received_struct_array[i] = fdserial_rxChar(processor);
    

    One thing of interest, which was hard to find.... the two fdserial_open functions did not recognize the constants in my config file. That one had me going until I hard coded the pins, mode and baud rate.
  • idbruceidbruce Posts: 6,197
    edited 2015-04-21 06:55
    And this line produced some results :) Not what I wanted to see of course :)
    received_struct_array[i] = i; //fdserial_rxChar(processor);
    
  • Dave HeinDave Hein Posts: 6,347
    edited 2015-04-21 07:16
    idbruce wrote: »
    I suppose I should have mentioned, that it is never exiting the loop. By commenting out the loop, I have verified, that characters are indeed getting to the function, so the fdserial object is definitely valid, and the function permits further processing in the parent cog. I just can't understand why it isn't exiting a loop of 324 iterations.
    Send a few extra characters. Maybe the receive cog is missing the first character or two. Of course that won't fix the problem, but at least you can figure out what's going on. Sending raw binary on a serial link doesn't allow for any error detection. You may want to add a magic word at the beginning and a checksum at the end so you can ensure that the data is correct. If you do this you could also add a message ID, which would allow you to use the serial port for other things like sending debug messages.

    If you really want to make it bullet-proof you could add a timeout on the receive side, and exit the receive loop if you haven't received a character for a while. I have some YMODEM code written in Spin that you could adapt to your needs if you're interested.
  • idbruceidbruce Posts: 6,197
    edited 2015-04-21 07:45
    Dave
    Send a few extra characters. Maybe the receive cog is missing the first character or two. Of course that won't fix the problem, but at least you can figure out what's going on. Sending raw binary on a serial link doesn't allow for any error detection. You may want to add a magic word at the beginning and a checksum at the end so you can ensure that the data is correct. If you do this you could also add a message ID, which would allow you to use the serial port for other things like sending debug messages.

    If you really want to make it bullet-proof you could add a timeout on the receive side, and exit the receive loop if you haven't received a character for a while. I have some YMODEM code written in Spin that you could adapt to your needs if you're interested.

    Sure let me see it.

    Several years ago, kuroneko and I put together some code for inter-prop comms, which included a begin trans, data, checksum, and end trans. Although not perfect, I am sure it could be altered to suit this purpose.... but to be perfectly honest, I am really not up to the port, because it looks a bit complicated :)
  • jazzedjazzed Posts: 11,803
    edited 2015-04-21 09:10
    idbruce wrote: »
    David



    I suppose I should have mentioned, that it is never exiting the loop. By commenting out the loop, I have verified, that characters are indeed getting to the function, so the fdserial object is definitely valid, and the function permits further processing in the parent cog. I just can't understand why it isn't exiting a loop of 324 iterations.

    /**
    * @brief Get a byte from the receive buffer, or if it's emtpy, wait until
    * a byte is received.
    *
    * @param *term Device ID returned by fdserial_open.
    *
    */
    int fdserial_rxChar(fdserial *term);

    Which means if you're not receiving enough data the loop will hang.

    More importantly though, I suggest you consider testing your ideas with smaller pieces and build on pieces that you know work for you. Changing from one method to another every few days won't solve your problem.
  • idbruceidbruce Posts: 6,197
    edited 2015-04-21 10:07
    jazzed
    More importantly though, I suggest you consider testing your ideas with smaller pieces and build on pieces that you know work for you. Changing from one method to another every few days won't solve your problem.

    I agree. I am close and I know where the problem is, I just need to work it out.
    /**
    * @brief Get a byte from the receive buffer, or if it's emtpy, wait until
    * a byte is received.
    *
    * @param *term Device ID returned by fdserial_open.
    *
    */
    int fdserial_rxChar(fdserial *term);

    Which means if you're not receiving enough data the loop will hang.

    I should have paid better attention..... I missed the point about waiting and that would surely cause it to hang.


    Thanks Steve
  • idbruceidbruce Posts: 6,197
    edited 2015-04-21 17:47
    Okay....

    I have wittled it down to a much smaller application, and I have had a little success. The program no longer hangs and the first three members of the struct are filled correctly, but the remaining members are all zeroed out.

    I have attached the entire project below, but here is a snippet from the receiving cog:
    void serial_processor(void *par)
    {
    	uint32_t index;
    	fdserial *processor;
    	int Char;
    
    	processor = fdserial_open(2, 0, 0, 115200);
    
    	do
    	{
    		if(fdserial_rxTime(processor, 500) == '<')
    		{
    			index = 0;
    
    			while((Char = fdserial_rxChar(processor)) && (Char != '>'))
    			{
    				received_struct_array[index] = Char;
    				index++;
    			}
    
    			memcpy(&received_gcode, &received_struct_array, sizeof(GCODE_STRUCT));
    			memcpy(&sent_gcode, &received_gcode, sizeof(GCODE_STRUCT));
    			memset(received_struct_array, 0, sizeof(GCODE_STRUCT));
    
    			gcode_struct_received = true;
    		}
    	}
      
    	while(serial_processor_cog > 0);
    }
    
  • idbruceidbruce Posts: 6,197
    edited 2015-04-21 18:44
    In an attempt to make it a little easier to follow, I have rearranged a few things, and eliminated two more files, but the results have not changed from the previous upload.
Sign In or Register to comment.