Indentify problem first, threads, then ...
Rsadeika
Posts: 3,837
Below is a C program that I developed for using the Sensirion sht11 module on my Raspberry Pi, the Sensirion code was already available, in other words not my code. The program works as expected, but the concern that I have is this: When I start a session with putty and I run the sht11 program, it works as expected. Leaving the program running, I start another putty session and I start running another instance of sht11, the program starts as expected, but when I request 'temp' or 'humid', all I get are zero's instead of an actual temperature and humidity. Am I missing something about my understanding of how threads work? Why isn't the second instance of sht11 not accessing the sht11 thread?
I have developed a C program for using the Sensirion sht11 on my DNA-RTC board, which works as expected, but because I am using COGs, I am almost out of memory for some needed expansion for the program. So, I am thinking about re-writing the program to use threads instead of COGs, and therefore able to use the extra memory that is available. Because this program is using an XBee module, I am wondering if I will run into the same problem as mentioned above, when I have more than one XBee trying to access and run the program. I know that when I use COGs, I do not have a problem with multiple instances of accessing a COG, but will I have it with threads?
This is as good of an example that I could think of for developing a program in C, except for the code differences in accessing the Sensrion module, all the rest of the code could be a cut/paste session. Having said that, I am not so sure that the use of threads would result in the same kind of outcome when used on the Propeller. It seems like on the Raspberry Pi, a thread is much like a COG, except for the multiple instances problem that I am experiencing. On the Propeller, I am not sure that a thread acts the same way. So, there is that porting problem, but the rest is cut/paste. Anybody have a similar problem, and you got it resolved? Or how would I resolve the multiple instances of thread?
Ray
I have developed a C program for using the Sensirion sht11 on my DNA-RTC board, which works as expected, but because I am using COGs, I am almost out of memory for some needed expansion for the program. So, I am thinking about re-writing the program to use threads instead of COGs, and therefore able to use the extra memory that is available. Because this program is using an XBee module, I am wondering if I will run into the same problem as mentioned above, when I have more than one XBee trying to access and run the program. I know that when I use COGs, I do not have a problem with multiple instances of accessing a COG, but will I have it with threads?
This is as good of an example that I could think of for developing a program in C, except for the code differences in accessing the Sensrion module, all the rest of the code could be a cut/paste session. Having said that, I am not so sure that the use of threads would result in the same kind of outcome when used on the Propeller. It seems like on the Raspberry Pi, a thread is much like a COG, except for the multiple instances problem that I am experiencing. On the Propeller, I am not sure that a thread acts the same way. So, there is that porting problem, but the rest is cut/paste. Anybody have a similar problem, and you got it resolved? Or how would I resolve the multiple instances of thread?
Ray
/* sht11.c */ #include <pthread.h> #include <stdio.h> #include <stdlib.h> #include <time.h> #include <bcm2835.h> #include "RPi_SHT1x.h" #define PIN RPI_GPIO_P1_11 FILE *tp; static volatile float Gnewt; static volatile float Gtemp_val; static volatile float Ghumi_val; static volatile int starthour; static volatile int stophour; static volatile int SST; static volatile int TlT; void *sht11Thread(void *arg) { unsigned char noError = 1; value humi_val, temp_val; while(1) { /* Set up the SHT1x Data and Clock pins.*/ SHT1x_InitPins(); /* Reset the SHT1x.*/ SHT1x_Reset(); /* Request Temperature measurment.*/ noError = SHT1x_Measure_Start(SHT1xMeaT ); if(!noError) return; /* Read Temperature measurement.*/ noError = SHT1x_Get_Measure_Value((unsigned short int*)&temp_val.i); if(!noError) return; /* Request Humidity Measurement.*/ noError = SHT1x_Measure_Start(SHT1xMeaRh); if(!noError) return; /* Read Humidity measurement.*/ noError = SHT1x_Get_Measure_Value((unsigned short int*)&humi_val.i); if(!noError) return; /* Convert integers to float and calculate true values.*/ temp_val.f = (float)temp_val.i; humi_val.f = (float)humi_val.i; /* Calculate F from given Celsius measurement.*/ SHT1x_Calc(&humi_val.f, &temp_val.f); float newt = ((temp_val.f*1.8)+30.60); // C to F conversion. Gnewt = newt; Gtemp_val = temp_val.f; Ghumi_val = humi_val.f; usleep(250000); } pthread_exit(NULL); } void *loggerThread(void *arg) { SST = 0; char datebuff[16]; char timebuff[16]; char startbuff[16]; char stopbuff[16]; time_t now = 0; now = time(&now); strftime(datebuff,16,"%b %d, %Y",localtime(&now)); tp = fopen("testlog.txt","a"); fprintf(tp,"%s\n",datebuff); fclose(tp); while(1) { now = time(&now); strftime(timebuff,16,"%T",localtime(&now)); if(SST == 1) { tp = fopen("testlog.txt","a"); fprintf(tp,"%s",timebuff); fprintf(tp," T:%0.2f F %0.2f C",Gnewt,Gtemp_val); fprintf(tp," H:%0.2f %%\n",Ghumi_val); fclose(tp); sleep(60); } else { usleep(500000); } } pthread_exit(NULL); } void *TloggerThread(void *arg) { while(1) { if(TlT == 1) { printf("%d\n",starthour); printf("%d",stophour); TlT = 0; } else { usleep(250000); } } pthread_exit(NULL); } void menu() { printf("Menu: quit, temp, timeron\n"); printf(" help, humid,timeroff\n"); } void set_Timer() { char startH[1]; char stopH[1]; printf("Start hour: "); gets(startH); starthour = atoi(startH); // Convert str to int printf("\nStop hour: "); gets(stopH); stophour = atoi(stopH); // Convert str to int TlT = 1; } void set_Timer_on() { SST = 1; } void set_Timer_off() { SST = 0; } int main() { char inBuff[40]; if(!bcm2835_init()) return 0; bcm2835_gpio_fsel(PIN, BCM2835_GPIO_FSEL_OUTP); bcm2835_gpio_write(PIN,HIGH); pthread_t threads[5]; pthread_create(&threads[1],NULL,sht11Thread,NULL); pthread_create(&threads[2],NULL,loggerThread,NULL); pthread_create(&threads[3],NULL,TloggerThread,NULL); sleep(2); printf("This is the sht11 program.\n"); while(1) { printf("> "); gets(inBuff); if(!strcmp(inBuff,"quit")) break; else if(!strcmp(inBuff, "temp")) { printf("Temperature: %0.2f F %0.2f C\n",Gnewt,Gtemp_val); } else if(!strcmp(inBuff, "humid")) { printf("Humidity: %0.2f %%\n",Ghumi_val); } else if(!strcmp(inBuff,"help")) { menu(); } else if(!strcmp(inBuff,"timer")) { set_Timer(); } else if(!strcmp(inBuff,"timeron")) { set_Timer_on(); } else if(!strcmp(inBuff,"timeroff")) { set_Timer_off(); } else { printf("?\n"); } //sleep(1); } printf("Good Bye.\n"); bcm2835_gpio_write(PIN, LOW); return 0; }
Comments
As I mentioned a few years ago, threading is an advanced concept where more rules must be followed. Globals are not thread safe for example. Typically, if a thread never writes to a global, you're Ok. If you have to use a global and write to it, then the idea of mutex must be used. A mutex allows locking access to data so that only one thread at a time can access it. That is, a thread should have mutually exclusive (mut-ex) access. Another issue is access to the hardware. Only one thread at a time can access the hardware until the required operation is completed. Then there is the idea of yielding. If your threads don't yield properly, the program can get hopelessly lost.
All your threads so stuff sequentially and then sleep a bit. Looks like this could all be done from one main loop.
In the brief descriptions that I have seen of threading, especially when used on a PC, or in my case the Raspberry Pi, a thread is supposed to work almost like a COG, or at least that is my interpretation. And if the system happens to have multiple cores, the thread uses a core, not sure how that would be accomplished. Yes, I know this is an advanced topic, but I am here and I am trying to figure things out. If my reading about the thread using a core, when available, is correct, then why doesn't it use a COG when threading is run on the Propeller?
Because there were some discussions, in another thread, about the porting capabilities of C, I thought this might be a good exercise in porting the Raspberry Pi sht11 version, with threads, to my DNA-RTC board, too see how that really works. In theory, as discussed here and at other places, it all sounds so great, well I thought maybe I would test it out for real, maybe separate fact from fiction, in the discussions.
So far I have been using the internet to find some answers, but the discussions all revolve around C++ examples or problems, I am still having trouble with C, let alone trying to figure out how it is supposed to work in other languages. Now the part about one thread instance only can access the hardware aspect sounds like I may not be able to achieve what I would like to do, oh well, I will just keep digging.
Ray
Access to resources, memory or hardware, needs to be coordinated between threads. Using mutexes is the common approach all though it can be done with just flags in simple cases of a single producer of data and a single consumer.
If you are using pthreads you should assume that you have this problem, no matter if the threads run on two COGs or one.
Others will have to suggest how to get pthreads spread around COGs or kept to a single COG.
Portability of C can only be judged by code designed to work properly by someone who understands the issues of programming.
I normally judge portability by trying to get the code compiled for the new target platform and running it there. Hopefully with some tests that can be applied to it,
As an example I'm sure Roy Eltham knows how to make portable code. Even so openspin is not portable. It won't run correctly on architectures that are fussy about aligned memory accesses.
I found an internet sight, POSIX Thread Programming, which seems to have a good explanation of how to use a mutex, but I guess I will not be sure until I really get into the subject. Any other suggestions would be appreciated.
Ray
If you want to make an experiment with threads and mutual exclusion using some hardware interface what about outputting on a serial port? A serial port on the Prop is easy enough and the Pi has one on the GPIO headers if you don't want to use a USB/Serial adapter.
Here would be a plan.
Create two or more threads.
Have them output lines of human readable text to the serial port that you can observe using whatever terminal program on your PC.
Have that text be changing all the time.
The challenge is that complete lines must be preserved intact. Lines from different threads can be received in a jumbled order but each line must be complete and correct.
Use mutexes to coordinate access to the serial port resource. Get the mutex, send a line, release the mutex.
To make things more interesting, make a program on the PC that echoes those lines back to the Prop/Raspi. Kind of like a command/response set up.
Now the threads have to:
1) Get the mutex,
2) Send the "command" lines.
3) Wait for and receive the echo reply.
4) Check that it is correct
5) Release the mutex.
Now we have the framework of a shared hardware device, could be SPI memory or whatever.
Of course you can skip the PC and have the Pi and Prop talk to each other.
Use a LED at each end to indicate FAIL/ERROR of course:)
Ray
I'm sure propgcc has a means of using a serial port available in it's libs. Not sure what is what there, haven't checked for a long time.
For the Pi I have a drop dead easy to use serial C code I can let you have. That will have to wait till Monday though.
Or look at "Wiring Pi" which seems to have a lot of GPIO stuff all wrapped up for easy use. Perhaps even a serial port driver.
Now I have to prepare my RPi Model-A with the new whezzy OS, since it does not have a lan connection I will not be able to get anything off the internet. Since I have a GG PPUSB board available that is what I will use for the Propeller side. And since it does not have any XMM, I will be using the LMM and CMM modes, I think.
Ray