/* * digidie.c * * A digital die. A random number between 1 and 6 is displayed * on a 7-segment display when a button is pressed and also for a * few seconds after the button is released. Otherwise a rotating * pattern is displayed. If there have been no button presses * for around 30 seconds the processor goes to sleep but will wake * again when the button is pressed. A random number generator isn't * used; the randomness arises from the uncertainty in the time * the button is pressed relative to the state of an internal timer. * * The program can be used to try out the PIC C compiler from HI-TECH * Software (a time-limited demo is available: http://www.htsoft.com). * Compile for the 16F84 using full optimization. * * The digital die can use either the 16C84 or 16F84 and was designed * to be programmed in-circuit using very simple hardware (see: * http://www.man.ac.uk/~mbhstdj/icp84.html for more details). A * common anode 7-segment LED display is attached to the PIC via * 470 ohm resistors as follows: * * RA0 segment a * RA1 segment b * RA2 segment c * RA3 segment d * RA4 segment e * RB4 segment f * RB5 segment g * * The common connection goes to VDD. A normally open switch is * connected from RB7 to GND via a 470 ohm resistor. The PIC * configuration word is set for a crystal oscillator (the Xtal * frequency should be communicated to the program via the FREQ * macro below). * * Copyright (C) 1998 David Tait (david.tait@man.ac.uk) * */ #include #define FREQ 3579454. // crystal frequency in Hz // #define FREQ 4000000. #define TICKSIZ (1024./FREQ) #define SECS(x) ((unsigned)((x)/TICKSIZ)) #define WAIT() do delay(SECS(0.1)); while ( button == 0 ) #define SLEEP() asm("\tsleep") typedef unsigned char BYTE; bit volatile button @ (unsigned) &PORTB*8+7; // button on RB7 BYTE volatile pressed; // set by button press interrupt BYTE volatile number; // a number grabbed from TMR0 const BYTE segtab[10] = // convert digit to segment form { 0x40, 0x79, 0x24, 0x30, 0x19, // only entries 1 to 6 used here 0x12, 0x02, 0x78, 0x00, 0x10 }; void interrupt grab_timer(void) // grab TMR0 when button pressed { if ( button == 0 && !pressed ) { number = TMR0; pressed = 1; } RBIF = 0; } void display(BYTE segs) { PORTA = segs; PORTB = (segs>>1)&0x30; } void delay(unsigned ticks) // waste time in ticks (256us @ 4MHz) { while ( ticks-- ) { T0IF = 0; while ( T0IF == 0 ) continue; } } void roll(void) // display a rotating pattern { unsigned i = 0; BYTE segs = 1; pressed = 0; for (;;) { display(~segs); delay(SECS(.05)); if ( (segs <<= 1) == 0x40 ) segs = 1; if ( ++i > 600 ) { // power-down after 30 secs i = 0; display(0xFF); // turn off all segments SLEEP(); // sleep until button pressed WAIT(); // wait for button release pressed = 0; // and continue } if ( pressed ) break; // return if button pressed } } BYTE mod6(BYTE n) // compute n%6 without using library { BYTE count, m6 = 6<<5; for ( count = 0; count < 6; ++count, m6 >>= 1) if ( n >= m6 ) n -= m6; return n; } void main(void) { static BYTE bias = 0; // used to ensure equal probabilities BYTE n; __CONFIG(0x3FF9); // XT oscillator (and PWRTE if 16C84) TRISA = 0; // PORTA all outputs TRISB = 0x80; // PORTB all outputs except RB7 OPTION = 8; // enable weak pullups, TMR0 rate 1:1 INTCON = 0x88; // enable change on RB<4:7> interrupt for (;;) { if ( ++bias >= 6 ) // update bias bias = 0; roll(); // wait for a button press n = mod6(number); n = mod6(n + bias) + 1; display(segtab[n]); // display die value WAIT(); // wait for button release delay(SECS(3.0)); // hold display for 3 seconds } }