microbit-dal

Dependencies:   BLE_API mbed-dev-bin nRF51822

Fork of microbit-dal by Lancaster University

Revision:
1:8aa5cdb4ab67
Child:
47:69f452b1a5c9
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/source/core/MicroBitDevice.cpp	Thu Apr 07 01:33:22 2016 +0100
@@ -0,0 +1,375 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2016 British Broadcasting Corporation.
+This software is provided by Lancaster University by arrangement with the BBC.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+/**
+  * Compatibility / portability funcitons and constants for the MicroBit DAL.
+  */
+#include "MicroBitConfig.h"
+#include "MicroBitButton.h"
+#include "MicroBitDevice.h"
+#include "MicroBitFont.h"
+#include "mbed.h"
+#include "ErrorNo.h"
+
+/*
+ * The underlying Nordic libraries that support BLE do not compile cleanly with the stringent GCC settings we employ
+ * If we're compiling under GCC, then we suppress any warnings generated from this code (but not the rest of the DAL)
+ * The ARM cc compiler is more tolerant. We don't test __GNUC__ here to detect GCC as ARMCC also typically sets this
+ * as a compatability option, but does not support the options used...
+ */
+#if !defined(__arm)
+#pragma GCC diagnostic ignored "-Wunused-function"
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wunused-parameter"
+#endif
+
+#include "nrf_soc.h"
+#include "nrf_sdm.h"
+
+/*
+ * Return to our predefined compiler settings.
+ */
+#if !defined(__arm)
+#pragma GCC diagnostic pop
+#endif
+
+static char friendly_name[MICROBIT_NAME_LENGTH+1];
+static const uint8_t panicFace[5] = {0x1B, 0x1B,0x0,0x0E,0x11};
+static int panic_timeout = 0;
+static uint32_t random_value = 0;
+
+/**
+  * Determines if a BLE stack is currently running.
+  *
+  * @return true is a bluetooth stack is operational, false otherwise.
+  */
+bool ble_running()
+{
+    uint8_t t;
+    sd_softdevice_is_enabled(&t);
+    return t==1;
+}
+
+/**
+ * Derived a unique, consistent serial number of this device from internal data.
+ *
+ * @return the serial number of this device.
+ */
+uint32_t microbit_serial_number()
+{
+    return NRF_FICR->DEVICEID[1];
+}
+
+/**
+ * Derive the friendly name for this device, based on its serial number.
+ *
+ * @return the serial number of this device.
+ */
+char* microbit_friendly_name()
+{
+    const uint8_t codebook[MICROBIT_NAME_LENGTH][MICROBIT_NAME_CODE_LETTERS] =
+    {
+        {'z', 'v', 'g', 'p', 't'},
+        {'u', 'o', 'i', 'e', 'a'},
+        {'z', 'v', 'g', 'p', 't'},
+        {'u', 'o', 'i', 'e', 'a'},
+        {'z', 'v', 'g', 'p', 't'}
+    };
+
+    // We count right to left, so create a pointer to the end of the buffer.
+	char *name = friendly_name;
+    name += MICROBIT_NAME_LENGTH;
+
+    // Terminate the string.
+    *name = 0;
+
+	// Derive our name from the nrf51822's unique ID.
+    uint32_t n = microbit_serial_number();
+    int ld = 1;
+    int d = MICROBIT_NAME_CODE_LETTERS;
+    int h;
+
+    for (int i=0; i<MICROBIT_NAME_LENGTH; i++)
+    {
+        h = (n % d) / ld;
+        n -= h;
+        d *= MICROBIT_NAME_CODE_LETTERS;
+        ld *= MICROBIT_NAME_CODE_LETTERS;
+        *--name = codebook[i][h];
+    }
+
+    return friendly_name;
+}
+
+/**
+  * Perform a hard reset of the micro:bit.
+  */
+void
+microbit_reset()
+{
+    NVIC_SystemReset();
+}
+
+/**
+  * Determine the version of microbit-dal currently running.
+  * @return a pointer to a character buffer containing a representation of the semantic version number.
+  */
+const char *
+microbit_dal_version()
+{
+    return MICROBIT_DAL_VERSION;
+}
+
+/**
+ * Defines the length of time that the device will remain in a error state before resetting.
+ *
+ * @param iteration The number of times the error code will be displayed before resetting. Set to zero to remain in error state forever.
+ *
+ * @code
+ * microbit_panic_timeout(4);
+ * @endcode
+ */
+void microbit_panic_timeout(int iterations)
+{
+    panic_timeout = iterations;
+}
+
+/**
+  * Disables all interrupts and user processing.
+  * Displays "=(" and an accompanying status code on the default display.
+  * @param statusCode the appropriate status code - 0 means no code will be displayed. Status codes must be in the range 0-255.
+  *
+  * @code
+  * microbit_panic(20);
+  * @endcode
+  */
+void microbit_panic(int statusCode)
+{
+    DigitalIn resetButton(MICROBIT_PIN_BUTTON_RESET);
+    resetButton.mode(PullUp);
+
+    uint32_t    row_mask = 0;
+    uint32_t    col_mask = 0;
+    uint32_t    row_reset = 0x01 << microbitMatrixMap.rowStart;
+    uint32_t    row_data = row_reset;
+    uint8_t     count = panic_timeout ? panic_timeout : 1;
+    uint8_t     strobeRow = 0;
+
+    row_mask = 0;
+    for (int i = microbitMatrixMap.rowStart; i < microbitMatrixMap.rowStart + microbitMatrixMap.rows; i++)
+        row_mask |= 0x01 << i;
+
+    for (int i = microbitMatrixMap.columnStart; i < microbitMatrixMap.columnStart + microbitMatrixMap.columns; i++)
+        col_mask |= 0x01 << i;
+
+    PortOut LEDMatrix(Port0, row_mask | col_mask);
+
+    if(statusCode < 0 || statusCode > 255)
+        statusCode = 0;
+
+    __disable_irq(); //stop ALL interrupts
+
+
+    //point to the font stored in Flash
+    const unsigned char * fontLocation = MicroBitFont::defaultFont;
+
+    //get individual digits of status code, and place it into a single array/
+    const uint8_t* chars[MICROBIT_PANIC_ERROR_CHARS] = { panicFace, fontLocation+((((statusCode/100 % 10)+48)-MICROBIT_FONT_ASCII_START) * 5), fontLocation+((((statusCode/10 % 10)+48)-MICROBIT_FONT_ASCII_START) * 5), fontLocation+((((statusCode % 10)+48)-MICROBIT_FONT_ASCII_START) * 5)};
+
+    //enter infinite loop.
+    while(count)
+    {
+        //iterate through our chars :)
+        for(int characterCount = 0; characterCount < MICROBIT_PANIC_ERROR_CHARS; characterCount++)
+        {
+            int outerCount = 0;
+
+            //display the current character
+            while(outerCount < 500)
+            {
+                uint32_t col_data = 0;
+
+                int i = 0;
+
+                //if we have hit the row limit - reset both the bit mask and the row variable
+                if(strobeRow == microbitMatrixMap.rows)
+                {
+                    strobeRow = 0;
+                    row_data = row_reset;
+                }
+
+                // Calculate the bitpattern to write.
+                for (i = 0; i < microbitMatrixMap.columns; i++)
+                {
+                    int index = (i * microbitMatrixMap.rows) + strobeRow;
+
+                    int bitMsk = 0x10 >> microbitMatrixMap.map[index].x; //chars are right aligned but read left to right
+                    int y = microbitMatrixMap.map[index].y;
+
+                    if(chars[characterCount][y] & bitMsk)
+                        col_data |= (1 << i);
+                }
+
+                col_data = ~col_data << microbitMatrixMap.columnStart & col_mask;
+
+                LEDMatrix = col_data | row_data;
+
+                //burn cycles
+                i = 1000;
+                while(i>0)
+                {
+                    // Check if the reset button has been pressed. Interrupts are disabled, so the normal method can't be relied upon...
+                    if (resetButton == 0)
+                        microbit_reset();
+
+                    i--;
+                }
+
+                //update the bit mask and row count
+                row_data <<= 1;
+                strobeRow++;
+                outerCount++;
+            }
+        }
+
+        if (panic_timeout)
+            count--;
+    }
+
+    microbit_reset();
+}
+
+/**
+  * Generate a random number in the given range.
+  * We use a simple Galois LFSR random number generator here,
+  * as a Galois LFSR is sufficient for our applications, and much more lightweight
+  * than the hardware random number generator built int the processor, which takes
+  * a long time and uses a lot of energy.
+  *
+  * KIDS: You shouldn't use this is the real world to generte cryptographic keys though...
+  * have a think why not. :-)
+  *
+  * @param max the upper range to generate a number for. This number cannot be negative.
+  *
+  * @return A random, natural number between 0 and the max-1. Or MICROBIT_INVALID_VALUE if max is <= 0.
+  *
+  * @code
+  * microbit_random(200); //a number between 0 and 199
+  * @endcode
+  */
+int microbit_random(int max)
+{
+    uint32_t m, result;
+
+    if(max <= 0)
+        return MICROBIT_INVALID_PARAMETER;
+
+    // Our maximum return value is actually one less than passed
+    max--;
+
+    do {
+        m = (uint32_t)max;
+        result = 0;
+		do {
+            // Cycle the LFSR (Linear Feedback Shift Register).
+            // We use an optimal sequence with a period of 2^32-1, as defined by Bruce Schneier here (a true legend in the field!),
+            // For those interested, it's documented in his paper:
+            // "Pseudo-Random Sequence Generator for 32-Bit CPUs: A fast, machine-independent generator for 32-bit Microprocessors"
+            // https://www.schneier.com/paper-pseudorandom-sequence.html
+			uint32_t rnd = random_value;
+
+            rnd = ((((rnd >> 31)
+                          ^ (rnd >> 6)
+                          ^ (rnd >> 4)
+                          ^ (rnd >> 2)
+                          ^ (rnd >> 1)
+                          ^ rnd)
+                          & 0x0000001)
+                          << 31 )
+                          | (rnd >> 1);
+
+			random_value = rnd;
+
+            result = ((result << 1) | (rnd & 0x00000001));
+        } while(m >>= 1);
+    } while (result > (uint32_t)max);
+
+    return result;
+}
+
+/**
+  * Seed the random number generator (RNG).
+  *
+  * This function uses the NRF51822's in built cryptographic random number generator to seed a Galois LFSR.
+  * We do this as the hardware RNG is relatively high power, and is locked out by the BLE stack internally,
+  * with a less than optimal application interface. A Galois LFSR is sufficient for our
+  * applications, and much more lightweight.
+  */
+void microbit_seed_random()
+{
+    random_value = 0;
+
+    if(ble_running())
+    {
+        // If Bluetooth is enabled, we need to go through the Nordic software to safely do this.
+        uint32_t result = sd_rand_application_vector_get((uint8_t*)&random_value, sizeof(random_value));
+
+        // If we couldn't get the random bytes then at least make the seed non-zero.
+        if (result != NRF_SUCCESS)
+            random_value = 0xBBC5EED;
+    }
+    else
+    {
+        // Othwerwise we can access the hardware RNG directly.
+
+        // Start the Random number generator. No need to leave it running... I hope. :-)
+        NRF_RNG->TASKS_START = 1;
+
+        for(int i = 0; i < 4; i++)
+        {
+            // Clear the VALRDY EVENT
+            NRF_RNG->EVENTS_VALRDY = 0;
+
+            // Wait for a number ot be generated.
+            while(NRF_RNG->EVENTS_VALRDY == 0);
+
+            random_value = (random_value << 8) | ((int) NRF_RNG->VALUE);
+        }
+
+        // Disable the generator to save power.
+        NRF_RNG->TASKS_STOP = 1;
+    }
+}
+
+/**
+  * Seed the pseudo random number generator (RNG) using the given 32-bit value.
+  * This function does not use the NRF51822's in built cryptographic random number generator.
+  *
+  * @param seed The value to use as a seed.
+  */
+void microbit_seed_random(uint32_t seed)
+{
+    random_value = seed;
+}