Sleep and Delay

From Nutwiki
Revision as of 17:03, 27 October 2016 by Harald (Talk | contribs) (1 revision imported)

(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search

Test Environments

Hardware Comments Nut/OS
4.8.3
Nut/OS
4.8.7
Ethernut 1.3 H OK
Binaries
Compiler: AVR-GCC 4.3.2
OK
Binaries
Compiler: AVR-GCC 4.3.2
Ethernut 2.1 B OK
Binaries
Compiler: AVR-GCC 4.3.2
OK
Binaries
Compiler: AVR-GCC 4.3.2
Ethernut 3.0 E OK
Binaries
Compiler: ARM-GCC 4.3.3
OK
Binaries
Compiler: ARM-GCC 4.3.3

Overview

In this example you'll see the difference between NutSleep and NutMicroDelay. Both methods have their advantages and disadvantages, it is just important to be aware of the difference in how they work.

The first step of the example attempts to delay for 60 seconds with NutSleep.

The seconds step does exactly the same thing but with NutMicroDelay instead.

In both cases, a background thread is running and occupying the CPU for brief moments when it is allowed to. This will severely impact the correctness of NutSleep. NutMicroDelay is only slightly affected by this because it simply does not allow the background thread to run. Depending on your application you may need the first or the second behaviour.

Note: Even NutMicroDelay is slightly inaccurate in this example. This is a result of the overhead introduced by the function calls used for the example.

Source Code

<source lang="c">

  1. include <stdio.h>
  2. include <io.h>
  3. include <stdlib.h>
  4. include <time.h>
  1. include <dev/board.h>
  1. include <sys/thread.h>
  2. include <sys/timer.h>

/* This thread will do some delays to consume CPU cycles */ THREAD(BackgroundWorker, arg) {

   srand(NutGetTickCount());
   for (;;) {
       /* Idle for 5-5.75 milliseconds */
       NutMicroDelay(5000 + (rand() % 750));
       NutSleep(0);
   }

}


int main(void) {

   u_long baud = 115200;
   time_t cur_time = 0;
   time_t prev_time = 0;
   int tick_prev = 0;
   int tick_cur = 0;
   int tick_diff = 0;
   int min_diff = 10;
   int max_diff = 0;
   int i = 0;
   NutRegisterDevice(&DEV_DEBUG, 0, 0);
   freopen(DEV_DEBUG_NAME, "w", stdout);
   _ioctl(_fileno(stdout), UART_SETSPEED, &baud);
   puts("\nDelay example");
   puts("First we'll use NutSleep to delay for 6000x 10 milliseconds. A background thread does some background work during this time, consuming CPU cycles. Then the same thing is repeated with NutMicroDelay instead.\n\n");
   NutThreadCreate("Bg", BackgroundWorker, NULL, 512);
   printf("Time recorded, starting NutSleep delay...\n");
   time(&prev_time);
   for (i = 0; i < 6000; i++) {
       tick_prev = NutGetTickCount();
       NutSleep(10);
       tick_cur = NutGetTickCount();
       tick_diff = (tick_cur - tick_prev - 10);
       if (tick_diff < min_diff) {
           min_diff = tick_diff;
       }
       if (tick_diff > max_diff) {
           max_diff = tick_diff;
       }
   }
   time(&cur_time);
   printf("Time is over.\nTotal time taken: %d seconds\nMinimum tick difference: %d\nMaximum tick difference: %d\n\n",
          (int) (cur_time - prev_time), min_diff, max_diff);
   printf("Time recorded, starting NutMicroDelay delay...\n");
   time(&prev_time);
   tick_diff = 0;
   min_diff = 10;
   max_diff = 0;
   for (i = 0; i < 6000; i++) {
       tick_prev = NutGetTickCount();
       NutMicroDelay(10000);
       tick_cur = NutGetTickCount();
       tick_diff = (tick_cur - tick_prev - 10);
       if (tick_diff < min_diff) {
           min_diff = tick_diff;
       }
       if (tick_diff > max_diff) {
           max_diff = tick_diff;
       }
   }
   time(&cur_time);
   printf("Time is over.\nTotal time taken: %d seconds\nMinimum tick difference: %d\nMaximum tick difference: %d",
          (int) (cur_time - prev_time), min_diff, max_diff);
   for (;;);
   return 0;

} </source>

Details

<source lang="c"> /* This thread will do some delays to consume CPU cycles */ THREAD(BackgroundWorker, arg) {

   srand(NutGetTickCount());
   for (;;) {
       /* Idle for 5-5.75 milliseconds */
       NutMicroDelay(5000 + (rand() % 750));
       NutSleep(0);
   }

} </source>

This is the background thread. In real applications you often have such threads that do things in the background like reading data from a device or stream or processing information. This thread doesn't really do anything except take a random, unpredictable amount of CPU time. While it will never need longer than 10 milliseconds for its "task", it may need longer than NutSleep(10). For example if NutSleep(10) has already waited for 5 milliseconds when this thread takes over, it will take between 5-5.75 milliseconds. This is more than 10 milliseconds and will block the CPU!

NutMicroDelay on the other hand is not affected by this thread at all because it does not even let other threads run in the background. This allows for far more precise timing but you can't read, write or process data or, in fact, do anything at all while this function delays the CPU.

<source lang="c"> int main(void) {

   u_long baud = 115200;
   time_t cur_time = 0;
   time_t prev_time = 0;
   int tick_prev = 0;
   int tick_cur = 0;
   int tick_diff = 0;
   int min_diff = 10;
   int max_diff = 0;
   int i = 0;
   NutRegisterDevice(&DEV_DEBUG, 0, 0);
   freopen(DEV_DEBUG_NAME, "w", stdout);
   _ioctl(_fileno(stdout), UART_SETSPEED, &baud);
   puts("\nDelay example");
   puts("First we'll use NutSleep to delay for 6000x 10 milliseconds. A background thread does some background work during this time, consuming CPU cycles. Then the same thing is repeated with NutMicroDelay instead.\n\n");
   NutThreadCreate("Bg", BackgroundWorker, NULL, 512);

</source>

This part initializes the debug device, declares the variables we'll use for storing time and tick differences, then prints a short explanation of what this example does and starts the background thread. We're now set and ready to go.

<source lang="c"> printf("Time recorded, starting NutSleep delay...\n");

time(&prev_time);

for (i = 0; i < 6000; i++) {

   tick_prev = NutGetTickCount();
   NutSleep(10);
   tick_cur = NutGetTickCount();
   tick_diff = (tick_cur - tick_prev - 10);
   if (tick_diff < min_diff) {
       min_diff = tick_diff;
   }
   if (tick_diff > max_diff) {
       max_diff = tick_diff;
   }

}

time(&cur_time);

printf("Time is over.\nTotal time taken: %d seconds\nMinimum tick difference: %d\nMaximum tick difference: %d\n\n",

      (int) (cur_time - prev_time), min_diff, max_diff);

</source>

This loop tries to wait for 6000x 10 milliseconds = 60 seconds while measuring the difference between our expected tick count (10) and the actual tick count. The difference can be pretty high because every time NutSleep(10) is called the background thread takes over and does its thing. After we're done looping, we output the results we gathered. These may differ from board to board. The actual time taken will also likely be far more than 60 seconds, depending on your hardware.

<source lang="c"> printf("Time recorded, starting NutMicroDelay delay...\n");

time(&prev_time);

tick_diff = 0; min_diff = 10; max_diff = 0;

for (i = 0; i < 6000; i++) {

   tick_prev = NutGetTickCount();
   NutMicroDelay(10000);
   tick_cur = NutGetTickCount();
   tick_diff = (tick_cur - tick_prev - 10);
   if (tick_diff < min_diff) {
       min_diff = tick_diff;
   }
   if (tick_diff > max_diff) {
       max_diff = tick_diff;
   }

}

time(&cur_time);

printf("Time is over.\nTotal time taken: %d seconds\nMinimum tick difference: %d\nMaximum tick difference: %d",

      (int) (cur_time - prev_time), min_diff, max_diff);

</source>

This loops does almost the same thing. It tries to wait for 6000x 10 milliseconds = 60 seconds. The major difference is that NutMicroDelay does NOT allow other threads to run. This allows us to get a way more precise timing. While this loop runs no multithreading is possible, though, because NutMicroDelay hogs the CPU for that time, allowing no other threads to run. The total time taken is slightly above 60 seconds, still, but most of that delay is due to overhead in the function calls used and in the loop itself.

See the output below for an idea of how much the methods differ in terms of precision.

Output

<source lang="text"> Delay example First we'll use NutSleep to delay for 60x 1 second. A background thread does som e background work during this time, consuming CPU cycles.


Time recorded, starting delay... Time is over. Total time taken: 104 seconds Minimum tick difference: 6 Maximum tick difference: 9

Time recorded, starting delay... Time is over. Total time taken: 65 seconds Minimum tick difference: 0 Maximum tick difference: 1 </source>

See also





Template:Languages