Another dice program for the mbuino.

Dependencies:   mbed mBuino_Sleep

You probably want http://mbed.org/users/maxint/code/mBuino_Dice/ rather than this one, that was the original mbuino dice program.

This version is based off the original release of the project above. It was then significantly re-written for a mixture of power consumption, randomness and coding style reasons. Most of the changes and improvements have since been incorporated into later versions of maxint's dice program (together with a few of his later ideas being copied into this version) so there are no meaningful functional differences between the two.

This version is posted mainly to provide an example of a slightly different way to do the same thing.

Revision:
0:24177fc1e0e3
Child:
1:05f369319854
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dice.cpp	Wed Sep 17 13:48:57 2014 +0000
@@ -0,0 +1,330 @@
+/*
+** mBuino_Dice
+**
+** This electronic dice application allows the user to throw an electronic dice by the press of a button
+**
+** On mBuino P0.4 is close to GND and the two pitch distance is exactly the same as a mini-switch!
+**
+** Base code from version 1 of http://mbed.org/users/maxint/code/mBuino_Dice/
+**
+** Modifed to improve randomness and decrease power draw and a few random changes to coding style
+** Most meaningful changes are now implimented in the later versions of the above code.
+*/
+
+#include "mbed.h"
+
+BusOut LEDOuts(LED1, LED2, LED3, LED4, LED5, LED6, LED7);// declare 7 LEDs
+InterruptIn ActionButton(P0_4);  // Vibration sensor
+AnalogIn RandomIn(P0_14); // use the random noise on this analog input to seed the random generator
+
+// LED bus value to display
+uint8_t LEDs = 0;
+
+// fix power draw
+DigitalIn progMode(P0_3);
+
+// sleep can be sleep, deepsleep or power down.
+// clean power down and cleen deep sleep do a clean shutdown of the PLL.
+// However any code called during the wakeup IRQ will then run at a slower speed.
+enum sleepMode_t {powerDown=0, deepSleep, lightSleep, cleanPowerDown, cleanDeepSleep};
+
+
+// idle time to wait before sleeping
+const float timeoutBeforeSleep = 15;
+
+
+const uint8_t DicePattern[6] = { 0x08, // 0b 000_1_000
+                                 0x22, // 0b 010_0_010
+                                 0x2a, // 0b 010_1_010
+                                 0x55, // 0b 101_0_101
+                                 0x5d, // 0b 101_1_101
+                                 0x77  // 0b 111_0_111
+                               };
+
+volatile bool fButtonPressed;
+
+volatile bool sleepNow=false;
+
+Timer timeTracker;
+Timeout sleepMode;
+
+
+// this cycles the LEDs so they are on for 1/7th of the time with only one lit at a time.
+// but since it does it at 1kHz they just look a little dimmer to the human eye.
+// This can give a big power saving when running on a battery.
+Ticker ledCycle;
+
+void on1msTick()
+{
+    static uint8_t ledIndex = 0;
+    LEDOuts = LEDs & 0x01<<ledIndex++;
+    if (ledIndex == 7)
+        ledIndex = 0;
+}
+
+
+
+// LED state setting functions.
+
+void SetLeds(bool on)
+{
+    if (on)
+        LEDs=0x7f;
+    else
+        LEDs=0;
+}
+
+void SetLed(uint8_t ledID, bool on)
+{
+    if (ledID <= 6) {
+        if (on)
+            LEDs = LEDs | (0x01 << ledID);
+        else
+            LEDs = LEDs & ~(0x01 << ledID);
+    }
+}
+
+void ToggleLeds()
+{
+    LEDs = ~LEDs;
+}
+
+void ToggleLed(uint8_t ledID)
+{
+    if (ledID <= 6)
+        LEDs = LEDs ^ (0x01 << ledID);
+}
+
+void BlinkLeds(bool on=true, float delay=0.1)
+{
+    uint8_t state = LEDs;
+    SetLeds(on);
+    wait(delay);
+    LEDs = state;
+    wait(delay);
+}
+
+void ShowDice(uint8_t nNumber)
+{
+// switch the leds of a particular dice-number on or off
+    if(nNumber<1) nNumber=1;
+    if(nNumber>6) nNumber=6;
+
+    LEDs=DicePattern[nNumber - 1];
+}
+
+
+void BlinkOneLed(uint8_t ledID, float delay = 0.1)
+{
+    ToggleLed(ledID);
+    wait(delay); // delay
+    ToggleLed(ledID);
+    wait(delay); // delay
+}
+
+void SweepSingleLed(bool leftToRight = true, float delay = 0.2)
+{
+    SetLeds(false);
+    for(int n=0; n<7; n++)
+        BlinkOneLed(leftToRight?n:6-n, delay);
+}
+
+void StackLEDs(float delay = 0.2)
+{
+    SetLeds(false);
+    int i;
+    int j;
+    for (i = 7; i > 0; i--) {
+        for (j=0; j<i; j++) {
+            SetLed(6-j,true);
+            wait(delay);
+            SetLed(6-j,false);
+        }
+        SetLed(7-j,true);
+    }
+}
+
+void SweepAllLeds(bool leftToRight = true, float delay=0.2)
+{
+    for(int n=0; n<7; n++) {
+        SetLed(leftToRight?n:6-n, true);
+        wait(delay); // delay
+    }
+    for(int n=0; n<7; n++) {
+        SetLed(leftToRight?6-n:n, false);
+        wait(delay); // delay
+    }
+}
+
+
+void mySleep() // ONLY CALL FROM MAIN LOOP
+{
+
+    LPC_PMU->PCON = 0x0;
+    SCB->SCR &= ~SCB_SCR_SLEEPDEEP_Msk;
+    __WFI();
+}
+
+// deepSleep is higher power but faster wakeup
+// clean PLL shutdown is technically needed but seems to work without it.
+// Note - If using clean PLL shutdown the IRQ that wakes us runs on the IRC
+// not the system clock because the clock isn't set up until after the IRQ is complete.
+void myPowerDown(bool cleanPLLShutdown = false, bool deepSleep = false)
+{
+
+    if (deepSleep)
+        LPC_PMU->PCON = 0x1;
+    else
+        LPC_PMU->PCON = 0x2;
+
+
+    LPC_SYSCON->PDSLEEPCFG |= 0x7f;  // shut off everything we can.
+    SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk;
+
+    bool IRCPowered = (LPC_SYSCON->PDRUNCFG & 0x01); // only used for cleen shutdown but defined here for scope reasons.
+
+    if (cleanPLLShutdown) {
+        if (!IRCPowered)
+            LPC_SYSCON->PDRUNCFG &= 0xfffffffe; // power up the IRC
+
+        LPC_SYSCON->MAINCLKSEL    = 0x00; // switch to IRC to avoid glitches
+        LPC_SYSCON->MAINCLKUEN    = 0x01;               /* Update MCLK Clock Source */
+        LPC_SYSCON->MAINCLKUEN    = 0x00;               /* Toggle Update Register   */
+        LPC_SYSCON->MAINCLKUEN    = 0x01;
+        while (!(LPC_SYSCON->MAINCLKUEN & 0x01));       /* Wait Until Updated       */
+    }
+
+    LPC_SYSCON->PDAWAKECFG = LPC_SYSCON->PDRUNCFG; // switch on everything that is currently on when we wake up.
+
+    __WFI();
+
+    if (cleanPLLShutdown) {
+        LPC_SYSCON->MAINCLKSEL    = 0x03; // switch to PLL output
+        LPC_SYSCON->MAINCLKUEN    = 0x01;               /* Update MCLK Clock Source */
+        LPC_SYSCON->MAINCLKUEN    = 0x00;               /* Toggle Update Register   */
+        LPC_SYSCON->MAINCLKUEN    = 0x01;
+        while (!(LPC_SYSCON->MAINCLKUEN & 0x01));       /* Wait Until Updated       */
+
+        if (!IRCPowered)
+            LPC_SYSCON->PDRUNCFG |= 0x01; // power down the IRC
+    }
+}
+
+
+void enterSleep(enum sleepMode_t mode = powerDown)
+{
+
+    SweepSingleLed(0.1);
+
+    // all LEDs off.
+    SetLeds(false);
+    // stop timers.
+    timeTracker.stop();
+    ledCycle.detach();
+
+    switch (mode) {
+        case powerDown:
+        case cleanPowerDown:
+        default:
+            myPowerDown(mode == cleanPowerDown);
+            break;
+        case lightSleep:
+            mySleep();
+            break;
+        case deepSleep:
+        case cleanDeepSleep:
+            myPowerDown(mode == cleanDeepSleep, true);
+            break;
+    }
+
+    // awake again amd running at full speed at this point.
+    timeTracker.start();
+    ledCycle.attach_us(on1msTick,1000);
+    sleepNow = false;
+    StackLEDs(0.1);
+    //  SweepAllLeds();
+}
+
+
+void enterSleepTimeout(void)
+{
+    sleepNow = true;
+}
+
+
+void buttonPressedIRQ()
+{
+    sleepMode.detach();
+    fButtonPressed=true;
+}
+
+
+
+void setup(void)
+{
+    // perform initialisations
+
+    // ensure no pullup on the progMode pin
+    progMode.mode(PullNone);
+
+    // create a 32 bit number out of 32 LSBs from the ADC
+    uint32_t seedValue = 0;
+    uint16_t value;
+    uint8_t counter;
+
+    for (counter = 0; counter < 32; counter++) {
+        seedValue = seedValue<<1;
+        value = RandomIn.read_u16(); // reads a 10 bit ADC normalised to 16 bits.
+        if (value & 0x0040)          // LSB of ADC output = 1
+            seedValue++;
+    }
+
+    srand(seedValue);     // seed the random generator with the background noise of an analog input
+
+    // start a timer used to determin delay between button presses, used to increase randomness.
+    timeTracker.start();
+    
+    // start LED cycling ticker
+    ledCycle.attach_us(on1msTick,1000);
+
+    // startup animation.
+    StackLEDs();
+
+    // vibration sensor/button
+    ActionButton.fall(buttonPressedIRQ);
+
+    // Sleep timeout.
+    sleepMode.attach(enterSleepTimeout,timeoutBeforeSleep);
+}
+
+int main()
+{
+    setup();
+    while(true) {
+
+        while(!fButtonPressed) {
+            if (sleepNow) enterSleep(cleanPowerDown);
+        }
+
+        uint8_t cycles = (timeTracker.read_us() % 20) + 30;
+        uint8_t value = 1;
+
+        while (cycles > 0) {
+            value = rand()%6+1;
+            ShowDice(value);
+            wait_ms((55-cycles)*2);
+            cycles--;
+        }
+
+        // clear the button flag now rather than as soon as we see it to avoid the need for de-bouncing.
+        fButtonPressed=false;
+
+        wait(0.5);
+
+        for (int i = 0; i<3; i++) {
+            BlinkLeds(false,0.2);
+        }
+
+        sleepMode.attach(enterSleepTimeout,timeoutBeforeSleep);
+    }
+}