Generating Microsecond Delays in ZDev

ZDev provides APIs for generating millisecond delays. But currently there is no API for generating microsecond delays. Microsecond delays are required in some applications, for example when playing an 8kHz audio. In this article, we will see how to generate microseconds delays, while developing applications with ZDev.

The general idea, is to calibrate the time taken to execute a single for loop. Once the time taken to execute a single for loop is known, the for loop can be executed a specific no. of times, to generate the required delay.

Software

for-loop
Figure 1. Flowchart

The program sequence to measure the time required to execute a for loop is shown below.

  • Let loop_count = 1

  • Initialize timer for one millisecond.

  • Execute for loop loop_count times.

  • If timer has not expired, loop_count++ and goto step 2.

  • loop_count contains the no. of loops per millisecond.

Once the delay loop has been calibrated using the above procedure, the required microsecond delay can be generated using the following steps.

  • Calculate the no. of loops for the required microsecond delay, nloops.

  • Execute for loop for nloops.

The implementation of the calibration code and the delay function is shown below.

#include <board.h>
#include <lcd.h>
#include <timer.h>
#include <systick.h>
#include <stdio.h>

static unsigned long loops_per_msec;

/*
 * Loop for the specified no. of times. Used for generating delays.
 */
static void busy_loop(unsigned int nloops)
{
        unsigned int i;
        for (i = 0; i < nloops; i++);
}

/*
 * Calibrate the loop, to find out the no. of loops executed in 1
 * milli-second, and store the result in loops_per_msec.
 */
void udelay_calibrate_loop(void)
{
        timer_t timer;
        ticks_t start;

        loops_per_msec = 1;

        /*
         * Calculate loop count. Use geometrical progression to get a
         * coarse grained value quickly - 1, 2, 4, 8, 16, 32, 64 ...
         */
        while (1) {
                /*
                 * Wait till we are the start of a millisecond.
                 */
                start = systick_get_ticks();
                while (systick_get_ticks() == start);

                /* timer for one milli seconds */
                timer_setup(&timer, 1);
                busy_loop(loops_per_msec);
                if (timer_expired(&timer))
                        break;
                loops_per_msec <<= 1;
        }
        loops_per_msec >>= 1;

        /*
         * Calculate fine grained loop count. Use arithmetic
         * progression to get an accurate value.
         */
        while (1) {
                /* timer for one milli seconds */
                timer_setup(&timer, 1);
                busy_loop(loops_per_msec);
                if (timer_expired(&timer))
                        break;
                loops_per_msec += 1;
        }
}

/*
 * Generate a micro-second delay.
 *
 * The function executes the required no. of delay loops, using the
 * previously calibrated loops_per_msec, to generate the delay.
 */
void udelay_delay(unsigned int usec)
{
        busy_loop((loops_per_msec * usec) / 1000);
}

/*
 * Test code to use the udelay, implementation.
 *
 * Generates a 1 second delay.
 */
int main()
{
        int i;

        board_init();
        timer_init();
        lcd_init();
        board_stdout(LCD_STDOUT);

        udelay_calibrate_loop();
        printf("Loops: [%lu]\n", loops_per_msec);

        printf("Start\n");
        /* Generate a 1 second delay. */
        for (i = 0; i < 1000; i++)
                udelay_delay(1000);
        printf("End");

        return 0;
}

Credits

The hourglass icon is from the Oxygen Icon Set. http://icongal.com/gallery/iconset/840/oxygen