RemBoxBot project
Rsadeika
Posts: 3,837
I have started a new project, RemBoxBot which is made out of cardboard; my prototypes are usually made out of cardboard. Since I am using two different boards, the thing that I am trying to accomplish is communication between the two, which at this point is not working. I am using a three wire connection between the two, where it has a common ground, and the 5v supply for the PSCU board comes from the DNA board. I am also using the serial in pinout which is on the PSCU board, so far no luck on the communication part. Must be some problem with my code, or my understanding of how the comm process should work. I guess I need some expert opinion on this.
The one unusual thing that I did notice is, in the slave program, when the datain cog is running it has an affect on how the servos run, they run in spurts. When I have the datain cog disabled, then the servos run as expected. I thought the cog was a separate entity, and should not affect the other cogs?
Ray
master(DNA_RTC board)
slave(PSCU board)
The one unusual thing that I did notice is, in the slave program, when the datain cog is running it has an affect on how the servos run, they run in spurts. When I have the datain cog disabled, then the servos run as expected. I thought the cog was a separate entity, and should not affect the other cogs?
Ray
master(DNA_RTC board)
/** * @file RBBmaster.c * This is the main RBBmaster program start point. */ #include <propeller.h> #include <stdio.h> #include <string.h> #include <stdlib.h> #include <pthread.h> #include <unistd.h> #include <sys/sd.h> extern _Driver _FileDriver; /* When this is not implemented, then the thread works. */ //_Driver *_driverlist[] = { // &_FileDriver, // NULL //}; FILE *cmdinfile; FILE *stdinfile; FILE *stdoutfile; FILE *cmdout; volatile int t1cmdout; void *do_job1(void *arg) { pthread_set_affinity_thiscog_np(); pthread_detach(pthread_self()); /* Tx Rx */ cmdout = fopen("SSER:38400,16,16","r+"); waitcnt(CLKFREQ + CNT); fprintf(stdout,"Thread Started\n"); while(1) { fprintf(cmdout,"%c\n",t1cmdout); usleep(3000); } } void Help() { fprintf(stdout, "This is the Help File.\n"); } /* This routine returns a pointer to the first character that doesn't match val. */ char *SkipChar(char *ptr, int val) { while (*ptr) { if (*ptr != val) break; ptr++; } return ptr; } /* This routine returns a pointer to the first character that matches val. */ char *FindChar(char *ptr, int val) { while (*ptr) { if (*ptr == val) break; ptr++; } return ptr; } /* This routine extracts tokens from a string that are separated by one or more spaces. It returns the number of tokens found. */ int tokenize(char *ptr, char *tokens[]) { int num = 0; while (*ptr) { ptr = SkipChar(ptr, ' '); if (*ptr == 0) break; if (ptr[0] == '>') { ptr++; if (ptr[0] == '>') { tokens[num++] = ">>"; ptr++; } else tokens[num++] = ">"; continue; } if (ptr[0] == '<') { ptr++; tokens[num++] = "<"; continue; } tokens[num++] = ptr; ptr = FindChar(ptr, ' '); if (*ptr) *ptr++ = 0; } return num; } /* This routine searches the list of tokens for the redirection operators and opens the files for input, output or append depending on the operator. */ int CheckRedirection(char **tokens, int num) { int i, j; for (i = 0; i < num-1; i++) { if (!strcmp(tokens[i], ">")) { stdoutfile = fopen(tokens[i+1], "w"); if (!stdoutfile) { perror(tokens[i+1]); stdoutfile = stdout; return 0; } } else if (!strcmp(tokens[i], ">>")) { stdoutfile = fopen(tokens[i+1], "a"); if (!stdoutfile) { perror(tokens[i+1]); stdoutfile = stdout; return 0; } } else if (!strcmp(tokens[i], "<")) { stdinfile = fopen(tokens[i+1], "r"); if (!stdinfile) { perror(tokens[i+1]); stdinfile = stdin; return 0; } } else continue; for (j = i + 2; j < num; j++) tokens[j-2] = tokens[j]; i--; num -= 2; } return num; } /* This routine closes files that were open for redirection */ void CloseRedirection() { if (stdinfile != stdin) { fclose(stdinfile); stdinfile = stdin; } if (stdoutfile != stdout) { fclose(stdoutfile); stdoutfile = stdout; } } pthread_t thr[1]; /** * Main program function. */ int main(void) { int num; char *tokens[20]; uint8_t buffer[80]; cmdinfile = stdin; stdinfile = stdin; stdoutfile = stdout; int i; pthread_create(&thr[i], NULL, do_job1, NULL); setvbuf(stdin, NULL, _IONBF, 0); // no buffering stdin setvbuf(stdout, NULL, _IONBF, 0); // no buffering stdout stdin->_flag |= _IOCOOKED; // turn on echo waitcnt(CLKFREQ + CNT); printf("Start of program\n"); while(1) { if (cmdinfile == stdin) { printf(">"); fflush(stdout); gets(buffer); } else if (!fgets(buffer, 80, cmdinfile)) { cmdinfile = stdin; continue; } num = tokenize(buffer, tokens); num = CheckRedirection(tokens, num); if (num == 0) continue; if (!strcmp(tokens[0], "help")) Help(); else if(!strcmp(tokens[0], "gostop")) t1cmdout = 's'; else if(!strcmp(tokens[0], "gofore")) t1cmdout = 'f'; else { printf("Invalid command\n"); } } return 0; }
slave(PSCU board)
/** * @file RBBslave.c * This is the main RBBslave program start point. */ #include <propeller.h> #include <stdio.h> #include <sys/thread.h> FILE *cmdin; #define STACK_SIZE 32 int LEFT_SERVO_PIN = 1; int RIGHT_SERVO_PIN = 0; #define LEFT_CENTER 1500 #define RIGHT_CENTER 1500 #define PAUSE_DIVISOR 50 /* stacks for COGs */ static int LeftServoStack [STACK_SIZE]; static int RightServoStack [STACK_SIZE]; static int DataInStack [STACK_SIZE]; /* per-thread library variables */ static _thread_state_t LeftThreadData; static _thread_state_t RightThreadData; static _thread_state_t DataInThreadData; /* WheelServo COG variables */ int LeftServoSpeed; int RightServoSpeed; int LeftRotation; int RightRotation; int StopNoPulse = 0; //1 static int MaxServoSpeed = 500; void LeftServoThread(void *arg __attribute__((unused))) { int pinmask = (1<<LEFT_SERVO_PIN); int center = LEFT_CENTER; int ustime = CLKFREQ/1000000; DIRA |= pinmask; for(;;) { /* start with signal off */ OUTA &= ~pinmask; if(StopNoPulse && LeftServoSpeed == 0) { waitcnt(CLKFREQ/PAUSE_DIVISOR+CNT); LeftRotation = 0; continue; } if(LeftServoSpeed > MaxServoSpeed) LeftServoSpeed = MaxServoSpeed; if(LeftServoSpeed < -MaxServoSpeed) LeftServoSpeed = -MaxServoSpeed; /* move servo */ LeftRotation = (LeftServoSpeed/2*center)/MaxServoSpeed+center; waitcnt(CLKFREQ/PAUSE_DIVISOR+CNT); OUTA |= pinmask; waitcnt(ustime*LeftRotation+CNT); OUTA ^= pinmask; } } void RightServoThread(void *arg __attribute__((unused))) { int pinmask = (1<<RIGHT_SERVO_PIN); int center = RIGHT_CENTER; int ustime = CLKFREQ/1000000; DIRA |= pinmask; for(;;) { /* start with signal off */ OUTA &= ~pinmask; if(StopNoPulse && RightServoSpeed == 0) { waitcnt(CLKFREQ/PAUSE_DIVISOR+CNT); RightRotation = 0; continue; } if(RightServoSpeed > MaxServoSpeed) RightServoSpeed = MaxServoSpeed; if(RightServoSpeed < -MaxServoSpeed) RightServoSpeed = -MaxServoSpeed; /* move servo */ RightRotation = (-RightServoSpeed/2*center)/MaxServoSpeed+center; waitcnt(CLKFREQ/PAUSE_DIVISOR+CNT); OUTA |= pinmask; waitcnt(ustime*RightRotation+CNT); OUTA ^= pinmask; } } void DataInThread(void *arg __attribute__((unused))) { int speed = 20; int t1datain; /* Tx Rx */ cmdin = fopen("SSER:38400,26,27","r+"); setvbuf(cmdin, NULL, _IONBF, 0); // no buffering stdin // setvbuf(cmdout, NULL, _IONBF, 0); // no buffering stdout cmdin->_flag |= _IOCOOKED; // turn on echo while(1) { fscanf(cmdin, "%c", &t1datain); switch(t1datain){ case 's': botStop(); break; case 'f': botForward(speed); break; } usleep(3010); } } void startServoThreads(void) { int n; n = _start_cog_thread(LeftServoStack + STACK_SIZE, LeftServoThread, NULL, &LeftThreadData); n = _start_cog_thread(RightServoStack + STACK_SIZE, RightServoThread, NULL, &RightThreadData); n = _start_cog_thread(DataInStack + STACK_SIZE, DataInThread, NULL, &DataInThreadData); } void botStop(void) { LeftServoSpeed = 0; RightServoSpeed = 0; } void botForward(int rate) { LeftServoSpeed = rate; RightServoSpeed = rate; } void botReverse(int rate) { LeftServoSpeed = -rate; RightServoSpeed = -rate; } void botSpeed(int rate) { LeftServoSpeed = LeftServoSpeed >= 0 ? rate : -rate; RightServoSpeed = RightServoSpeed >= 0 ? rate : -rate; } /** * Main program function. */ int main(void) { int speed = 20; char cmdstr[2]; int cmd; setvbuf(stdin, NULL, _IONBF, 0); // no buffering stdin setvbuf(stdout, NULL, _IONBF, 0); // no buffering stdout stdin->_flag |= _IOCOOKED; // turn on echo waitcnt(CLKFREQ + CNT); startServoThreads(); printf("Threads started\n"); for(;;) { putchar('>'); fgets(cmdstr, 2, stdin); cmd = cmdstr[0]; if(!cmd) { continue; } switch(cmd) { case 's': botStop(); printf("\n"); break; case 'f': botForward(speed); printf("Forward %d\n",speed); break; case 'b': botReverse(speed); printf("Backward %d\n",speed); break; case '-': if(speed >= 10) speed -= 10; printf("speed- %d\n",speed); botSpeed(speed); break; case '+': if(speed <= 90) speed += 10; printf("speed+ %d\n",speed); botSpeed(speed); break; } } return 0; }
Comments
A pure pthread started with pthread_create(...) does not run by itself in a COG. Pthreads is an advanced subject not recommended for a beginner.
A cogstart function is a C function that runs on top of an LMM interpreter. The cogstart just happens to use the pthread data struct for stack and other communications, but it is not a pthread.
The only ways to make a COG run pure code by itself (without some interpreter) is by using PASM, GAS, or COGC.
Everything we could think of that you need to know about libraries and drivers is referenced here.
https://sites.google.com/site/propellergcc/documentation/libraries
The order of the _Driver struct list entries is important.
The default serial driver is the Simple Serial Driver device named SSER
The current alternate serial driver is the Full Duplex Serial Driver named FDS
There is a NULL driver you can use if you don't want a serial driver.
You or anyone else can define your own driver that has special functions.
For the serial drivers, Simple Serial(SSER) is the default, meaning you do not have to use an 'extern' call or set up a driver list structure. This driver is half duplex, and does not need another cog start to work. Very simple and straight forward.
The Full Duplex Serial(FDS), needs to use an 'extern' call plus a drivers list structure in order to work. This driver is full duplex, and it does set up in another cog. This is a little more complex, but you should expect that because it is full duplex.
For the threads, I have been exposed to the cog thread, which behaves a lot like a cognew, in the Spin world, but it is still handled as a thread. This can run in an LMM/CMM mode, but not in XMM/XMMC mode. This type of thread is not severely limited by a blocking function(sleep, or waitcnt), so it can be used to run the servo code, which relies on the waitcnt function to work correctly. But their are some limitations, as I found out in my RemBoxBot program. When I introduced another cog thread that contained an fgets function, that seems to have an affect on how the servo functions would work.
The pthread, an advanced subject, can work in CMM/LMM/XMM/XMMC modes, but is hampered by the blocking functions(sleep or waitcnt). But in my programs, the servo code to be exact, it will not work because of the waitcnt blocking affect. The pthread is nice, because you can use all available memory, but it has its limitations.
It will be interesting to see what PropGCC for the P2 will look like. Will a lot of the limitations that I have been experiencing so far, go away with the new chip and the new PropGCC? But when you think about it, Spin will have to be improved, to use the extra memory capabilities, so the new Spin might be able to use LMM/XMM/XMMC modes? Will be able to start and use threads in the new Spin? Will I end up severely hampered by the inability to choose between Spin or PropGCC for a programming choice?
Ray
Theoretically COG "threads" started with cogstart or _start_cog_thread could be made to run in XMM/XMMC mode if the thread function is set to run from hub memory using the HUBTEXT attribute. All functions called by the thread would also need to be set to run in hub memory. This needs to be tested.
This refers to COG "threads" started with cogstart .... There should not be any interaction with fgets if the IO device is set to non-buffered operation as described several times before.
Propeller GCC for P2 will be very much like P1 Propeller GCC except running faster in all memory models.
There has been some discussion of using spin2cpp in a C or C++ project to integrate SPIN and C. The names of SPIN functions may be different though. This could probably be done today, but I haven't tested it. My priority however is finishing updates to SimpleIDE for education.
Well, theoretically not. The XMM machine is not loaded into HUB RAM like the LMM machine is. I'll make an issue for the P2 phase to keep track.
Ray
I swear I saw code that makes it possible to invert the sense of the serial lines. Did I dream it?
This question has come up before and we have not provided a good answer.
Meanwhile, you could try the FDSerial port I posted in another thread.
FDSerial for Propeller-GCC
We can change that in a future release, but for now it'll be necessary to use a different driver, either the one jazzed posted a link to or a conversion of FullDuplexSerial using spin2cpp.
Eric
I guess in order to communicate with the PSCU board, you need to do the FdSerial_tx/FdSerial_rx commands, I guess the fputs()/fgetc() stuff is not working for me, at least not with the PSCU/DNA-RTC boards .
Ray
Ray
RBBmaster
RBBslave
Please explain your expectation.
Thanks,
--Steve
Ray
I see. Very clever. My advice is that clever things should usually be commented.
Making the first entry of the driver structure NULL turns off the standard console as noted in the comments.
Yes, it should work like that. I can only assume that you ended up with an invalid configuration somewhere along the way. For example, we can not use FdSerial on the same IO as the standard console (P30/31) without turning the console off. The FdSerial pins must be different.
If you want to turn off the console and still have other standard IO devices, the first entry should be _NullDriver which is different from NULL (NULL terminates the driver setup list).
The other thing that I noticed, in programming mode, if I change the board type to DNA-SDLOAD the program no longer works correctly. In fact that occurs in all the board types, except for just the DNA selection. Is this a problem with the cfg file? I downloaded the latest from the DNA-RTC web site.
I need some help with this one.
Thanks
Ray
Assumption 1. always consider hardware without modifications as not bad.
That is, until you know that the software works perfectly, unmodified hardware is innocent.
Assumption 2. Any modified hardware may be mistakenly assembled.
Check the connections for open/short circuits.
Assumption 3. Something about the way the program starts is causing the problem.
There are differences in timing between RAM start and EEPROM start. Check if that's an issue.
Assumption 4. It is possible but unlikely that the EEPROM may not be correctly written.
We can use some tools to read and compare the EEPROM to binaries.
I don't have any such tools right now, and I don't have time to write them.
Focus on assumption 1 through 3 for now or find a way to dump the EEPROM.
BTW, I see you are still using waitcnt with pthreads. This is a big no-no as mentioned before.
As for the DNA-RTC, I am starting to lose faith in the product, but there is no close replacement for the board configuration. What I liked about the board was access to 5V on the header, the flash RAM, the screw down power block, the RTC, and of course the SD. Now if somebody had a fully assembled expansion board for the QuickStart, with the same configuration, and I put an emphasis on assembled, not kit, that could be a winner.
The reason I am not just using the C3 for the servos, and the master program, I still do not know how to mix the cog threads that the servos need, and a pthread that the access to the XBee needs. There are other things that need to be done, which will force me to use a two board setup. This prototype robot is starting to look like a complete mess with all kinds of wires sticking out, and boards that look like they belong in another project.
So, on to working with the C3 board, until I run into a problem with that board, then I do not know what I will do next...
Ray
Ray
I'm sorry to hear that. I'm curious though. Can you describe your typical connections with power, servos, and other devices?
Since I am using the C3 board now, and it has the flash ram, and the SRAM, how do you make use of the SRAM via some C code? Is there any C example code for using the SRAM?
I think the next thing I might try is adding access to the SD card in the RBBmaster program. Since there is an SD library, does it have commands similar to what you would find in the FSRW program? I like Dave Hine's filetest program, but I wonder if there is a different way of writing to the SD card?
Ray
The XMM-SPLIT memory model uses Flash and SRAM on the C3 for code and data.
Ray
Ever heard of RCA? RCA stands for the Radio Corporation of America. There is another meaning of RCA in Silicon Valley (and other) engineering circles: that is, Root Cause Analysis.
If you can't do a Root Cause Analysis of the problem, the DNA-RTC board may continue to fail intermittently.
I didn't see anything in your usage description that would cause an issue. Perhaps there is a loose pin on the board or a voltage regulation problem? If you can isolate some error as a perfect explanation of the cause of your problem and fix it, then you're probably Ok.