// // 10m32k.c -- Prototype PIC-based 10 MHz to 32 kHz divider. // // - This is simply a PC test of algorithms, not the actual PIC code. // // 23-Jul-2008 /tvb www.LeapSecond.com // #include #include int pin = 0; // // Dumb version 1. // // A 10 MHz PIC clock gives 2.5 million instructions per second (mips). // // To generate a 32 kHz square wave we toggle the output at 64 kHz. // But note that 65,536 Hz doesn't divide evenly into 2,500,000 Hz. // To be precise, 2500000 / 65536 = 38.14697265625. // // So the idea is to use a 38 instruction loop most of the time and // and compensate with a 39 instruction loop the rest of the time such // that the exact number of 32 kHz cycles occur each second. // // Note 2500000 % 65536 = 9632, and 65536 - 9632 = 55904, so that // (55904 * 38) + (9632 * 39) = 2500000. // // It should all add up perfectly: 10 MHz input and 32.768 kHz output. // The output waveforms, however, are not all identical. Probably close // enough for a 32 kHz-driven clock display, though. // // Here we run the test and see if it all comes out right. // void ver1 (void) { long ic, oic; long loop; // Run 2.5 million instruction cycles (which is 1 second at 10 MHz). oic = ic = 0; for (loop = 0; ic < 2500000; loop += 1) { printf("%ld (%ld) %ld %d\n", ic, ic - oic, loop, pin); oic = ic; // The first 9632 times through the loop are longer by one cycle. if (loop < 9632) { ic += 39; } else { ic += 38; } // Toggle output pin each half-cycle of the loop. pin ^= 1; } // Echo final input and output cycle counts (expect 10 MHz and 32.768 kHz). fprintf(stderr, "Check: %.3lf MHz clock input, %.6lf Hz output\n", ic * 4.0, loop / 2.0); } // // Clever version 2. // // A 10 MHz PIC clock gives 2.5 million instructions per second (mips). // // To generate a 32 kHz square wave we toggle the output at 64 kHz. // But note that 65,536 Hz doesn't divide evenly into 2,500,000 Hz. // To be precise, 2500000 / 65536 = 38.14697265625. // // So the idea is to use a 38 instruction loop most of the time and // and compensate with a 39 instruction loop the rest of the time such // that the exact number of 32 kHz cycles occur each second. // // Note 2500000 % 65536 = 9632, and 65536 - 9632 = 55904, so that // (55904 * 38) + (9632 * 39) = 2500000. // // We make the loop 38 cycles long, but 9632 of the 65536 loop times // need to have one extra cycle. And unlike the first version it would // also be nice to spread out these extra cycles as evenly as possible. // // This makes me think of leap years. The year is about 365.242 days long. // We can't have calendar day fractions; we add full (leap) days instead. // So we define the calendar year be 365 days but every 4th year, except // every 100th year, except every 400th year, we insert an extra day. The // mean calendar year is 365 + 1/4 - 1/100 + 1/400 long = 365.2425 days. // // We can do something similar here and keeping realtime PIC code in mind // we'll use alternating binary steps. Let's insert some "leap cycles". // // 2500000 / 65536 = 38.14697265625 is 38 cycles plus a "leap cycle" every // 4th time, except every 8th time, except every 32nd time, except every // 64th time, except every 128th time, except every 512th time, except // every 2048th time. It's a fun exercise to derive or to verify this. // // 38.00000000000 // +0.25000000000 ( + 1/4 ) // -0.12500000000 ( - 1/8 ) // +0.03125000000 ( + 1/32 ) // -0.01562500000 ( - 1/64 ) // +0.00781250000 ( + 1/128 ) // -0.00195312500 ( - 1/512 ) // +0.00048828125 ( + 1/2048 ) // -------------- // 38.14697265625 // // It should all add up perfectly: 10 MHz input and 32.768 kHz output. // Moreover, the output waveform will be as close as possible to the ideal // 32 kHz square wave for averaging times less than 1 second. The output // timing will be perfect for any averinging times a multiple of 1 second. // // Here we run the test and see if it all comes out right. // void ver2 (void) { long ic, oic; long loop; // Run 2.5 million instruction cycles (which is 1 second at 10 MHz). oic = ic = 0; for (loop = 0; ic < 2500000; loop += 1) { printf("%ld (%ld) %ld %d\n", ic, ic - oic, loop, pin); oic = ic; // Delay 38 instructions each half-cycle (gets close to 32 kHz). ic += 38; // Also insert 9632 well-spaced half "leap cycles" every second. if ((loop & (4-1)) == 0) ic += 1; if ((loop & (8-1)) == 0) ic -= 1; if ((loop & (32-1)) == 0) ic += 1; if ((loop & (64-1)) == 0) ic -= 1; if ((loop & (128-1)) == 0) ic += 1; if ((loop & (512-1)) == 0) ic -= 1; if ((loop & (2048-1)) == 0) ic += 1; // Toggle output pin each half-cycle of the loop. pin ^= 1; } // Echo final input and output cycle counts (expect 10 MHz and 32.768 kHz). fprintf(stderr, "Check: %.3lf MHz clock input, %.6lf Hz output\n", ic * 4.0, loop / 2.0); } int main (int argc, char *argv[]) { int ver = (argc > 1) ? atoi(argv[1]) : 0; switch (ver) { case 1 : ver1(); break; case 2 : ver2(); break; default : fprintf(stderr, "Pick version 1 or 2\n"); fprintf(stderr, "E.g., %s 1 > out1\n", argv[0]); fprintf(stderr, "E.g., %s 2 | head\n", argv[0]); fprintf(stderr, "E.g., %s 2 | tail\n", argv[0]); } return 0; }