How to use waitcnt for long delays (10+ minutes)?
twm47099
Posts: 867
As part of my learning to use the prop I want to write a program to monitor some condition (i.e. barometric pressure) over a long period of time, taking readings every 10 (or 30 or 60) minutes and saving them to an SD card.
I did write such a program, and it worked, but I wonder if I wrote the delay part as well as it could have been.
I wrote the program in C using SimpleIDE and used the waitcnt() function.
The code below shows the declarations and how I used waitcnt.
I printed the delay calculated and found that if I used an interval greater than 30 the counter rolled over and I was getting saves every few seconds. I believe that the system counter rolls over at 50 or so seconds at 80MHz, so I limited each waitcnt to 30 seconds and used a "for" loop to do as many waitcnts as needed to get the delay I wanted for each sensor reading.
Is there a better way? I am using an Activity Board for this.
Thanks
Tom
I did write such a program, and it worked, but I wonder if I wrote the delay part as well as it could have been.
I wrote the program in C using SimpleIDE and used the waitcnt() function.
The code below shows the declarations and how I used waitcnt.
I printed the delay calculated and found that if I used an interval greater than 30 the counter rolled over and I was getting saves every few seconds. I believe that the system counter rolls over at 50 or so seconds at 80MHz, so I limited each waitcnt to 30 seconds and used a "for" loop to do as many waitcnts as needed to get the delay I wanted for each sensor reading.
Is there a better way? I am using an Activity Board for this.
Thanks
Tom
int num_mins = 10; // number of minutes between each reading int interval = 30; // delay (seconds) per waitcnt int k, time_steps; // loop number of waitcnts to get num_mins unsigned int delay, tcnt; time_steps = num_mins * 2; // based on 30 second waitcnt delay = CLKFREQ * interval; // 30 second intervals print("clkfreq = %d delay = %d\n", CLKFREQ, delay); // for debugging // Do Some Stuff to initialize sensor ... while (1) { //do some stuff, collect & save data to SD card ... // first time through initalize tcnt with the system counter value if(!P0) { P0 = P; tcnt = CNT;} for(k = 0; k < time_steps; k++) waitcnt(tcnt += delay); // delay nummins } end while loop
Comments
This gets rid of the error that accumulates from the overhead involved in calling waitcnt.
As you have it now, the time to execute the code to collect and save gets added to the interval time. If this is not a problem then I think you already have the best solution.
Andy
In cutting and pasting I put the "if (! P0)" after the saving in my example above. It is actually after calculating P but before the save.
That if is true only the first time average P is calculated. So it sets the zero point of the 10 minute delay just after the first average P is calculated. Each subsequent waitcnt has "delay" added to the previous value.
I thought about setting tcnt = CNT in the initialization, but the conversion takes some time and is repeated a number of times to get an average of both P and temperature. So I decided to set the "zero" after the first average was calculated.
Tom
@David
It does not really matters when you calculate the next waitcnt value and how long it takes, as long as it happens in the 30 second interval.
Andy
I have some similar projects and use a RTC instead. That way I get date as well as an easy to work with time base. If I don't need great accuracy or don't need to run it for a long time, I use the RTC object in the obex. If I do need the accuracy or long term running I use a real RTC. Makes it all real easy. Of course, it costs either a cog or some pins.
Jonathan
Thanks, l didn't know about sleep.
I believe that waitcnt puts the cog into a low power mode which is attractive for a battery operated long time data recorder. Does sleep do the same?
Thanks
Tom
No, unfortunately sleep() does a busy wait.
I'm shocked... and disgusted... and... what??? Isn't the concept of sleep pretty popular among various architectures? Why doesn't GCC have some kind of support to allow different architectures to implement this however they deem necessary? Or, was this a decision made by the PropGCC team?
sleep() is part of the library, not the compiler. The PropGCC library has a lot of conflicting goals. At one point there was a demand for pthreads to work with multiple threads running on the same COG, and to do that sleep() has to call into pthreads (to allow scheduling of different threads). If pthreads isn't linked then sleep ends up busy waiting.
The pthreads hook (napuntil_ptr) could be used to do a waitcnt() instead of busy wait. In fact that's probably a good idea. It'd be great if some volunteer could step up to do this
Sounds like about as small of a bite as could possibly be taken out of a GCC contribution. I'll try and look into this. This would be the right Git repo right?
Yes, that's the right repo. I've added you as a collaborator. The guts of the code are in sys/propeller/nap.c (the __napuntil function). The whole "busy wait calling yield" code should probably be replaced with a waitcnt call. I think it was left there during a transition phase while we were updating pthreads and it didn't set the __napuntil_ptr, but pthreads does do that now (this is defined by the REAL_SLEEP define in pthreads/pthread_create.c). So the update itself is trivial. Testing it is the hard part, but it probably won't be too bad.
Thanks,
Eric
If power matters, you could look at lowering the SysCLK during Sleep.
5MHz can get directly to 10 minutes.