mbed.org local branch of microbit-dal. The real version lives in git at https://github.com/lancaster-university/microbit-dal

Dependencies:   BLE_API nRF51822 mbed-dev-bin

Dependents:   microbit Microbit IoTChallenge1 microbit ... more

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers MicroBitDevice.cpp Source File

MicroBitDevice.cpp

00001 /*
00002 The MIT License (MIT)
00003 
00004 Copyright (c) 2016 British Broadcasting Corporation.
00005 This software is provided by Lancaster University by arrangement with the BBC.
00006 
00007 Permission is hereby granted, free of charge, to any person obtaining a
00008 copy of this software and associated documentation files (the "Software"),
00009 to deal in the Software without restriction, including without limitation
00010 the rights to use, copy, modify, merge, publish, distribute, sublicense,
00011 and/or sell copies of the Software, and to permit persons to whom the
00012 Software is furnished to do so, subject to the following conditions:
00013 
00014 The above copyright notice and this permission notice shall be included in
00015 all copies or substantial portions of the Software.
00016 
00017 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
00018 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
00019 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
00020 THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
00021 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
00022 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
00023 DEALINGS IN THE SOFTWARE.
00024 */
00025 
00026 /**
00027   * Compatibility / portability funcitons and constants for the MicroBit DAL.
00028   */
00029 #include "MicroBitConfig.h"
00030 #include "MicroBitButton.h"
00031 #include "MicroBitDevice.h"
00032 #include "MicroBitFont.h"
00033 #include "mbed.h"
00034 #include "ErrorNo.h"
00035 
00036 /*
00037  * The underlying Nordic libraries that support BLE do not compile cleanly with the stringent GCC settings we employ
00038  * If we're compiling under GCC, then we suppress any warnings generated from this code (but not the rest of the DAL)
00039  * The ARM cc compiler is more tolerant. We don't test __GNUC__ here to detect GCC as ARMCC also typically sets this
00040  * as a compatability option, but does not support the options used...
00041  */
00042 #if !defined(__arm)
00043 #pragma GCC diagnostic ignored "-Wunused-function"
00044 #pragma GCC diagnostic push
00045 #pragma GCC diagnostic ignored "-Wunused-parameter"
00046 #endif
00047 
00048 #include "nrf_soc.h"
00049 #include "nrf_sdm.h"
00050 
00051 /*
00052  * Return to our predefined compiler settings.
00053  */
00054 #if !defined(__arm)
00055 #pragma GCC diagnostic pop
00056 #endif
00057 
00058 static char friendly_name[MICROBIT_NAME_LENGTH+1];
00059 static const uint8_t panicFace[5] = {0x1B, 0x1B,0x0,0x0E,0x11};
00060 static int panic_timeout = 0;
00061 static uint32_t random_value = 0;
00062 
00063 /**
00064   * Determines if a BLE stack is currently running.
00065   *
00066   * @return true is a bluetooth stack is operational, false otherwise.
00067   */
00068 bool ble_running()
00069 {
00070     uint8_t t = 0;
00071 
00072 #if CONFIG_ENABLED(MICROBIT_BLE_ENABLED) || CONFIG_ENABLED(MICROBIT_BLE_PAIRING_MODE)
00073     sd_softdevice_is_enabled(&t);
00074 #endif
00075 
00076     return t==1;
00077 }
00078 
00079 /**
00080  * Derived a unique, consistent serial number of this device from internal data.
00081  *
00082  * @return the serial number of this device.
00083  */
00084 uint32_t microbit_serial_number()
00085 {
00086     return NRF_FICR->DEVICEID[1];
00087 }
00088 
00089 /**
00090  * Derive the friendly name for this device, based on its serial number.
00091  *
00092  * @return the serial number of this device.
00093  */
00094 char* microbit_friendly_name()
00095 {
00096     const uint8_t codebook[MICROBIT_NAME_LENGTH][MICROBIT_NAME_CODE_LETTERS] =
00097     {
00098         {'z', 'v', 'g', 'p', 't'},
00099         {'u', 'o', 'i', 'e', 'a'},
00100         {'z', 'v', 'g', 'p', 't'},
00101         {'u', 'o', 'i', 'e', 'a'},
00102         {'z', 'v', 'g', 'p', 't'}
00103     };
00104 
00105     // We count right to left, so create a pointer to the end of the buffer.
00106     char *name = friendly_name;
00107     name += MICROBIT_NAME_LENGTH;
00108 
00109     // Terminate the string.
00110     *name = 0;
00111 
00112     // Derive our name from the nrf51822's unique ID.
00113     uint32_t n = microbit_serial_number();
00114     int ld = 1;
00115     int d = MICROBIT_NAME_CODE_LETTERS;
00116     int h;
00117 
00118     for (int i=0; i<MICROBIT_NAME_LENGTH; i++)
00119     {
00120         h = (n % d) / ld;
00121         n -= h;
00122         d *= MICROBIT_NAME_CODE_LETTERS;
00123         ld *= MICROBIT_NAME_CODE_LETTERS;
00124         *--name = codebook[i][h];
00125     }
00126 
00127     return friendly_name;
00128 }
00129 
00130 /**
00131   * Perform a hard reset of the micro:bit.
00132   */
00133 void
00134 microbit_reset()
00135 {
00136     NVIC_SystemReset();
00137 }
00138 
00139 /**
00140   * Determine the version of microbit-dal currently running.
00141   * @return a pointer to a character buffer containing a representation of the semantic version number.
00142   */
00143 const char *
00144 microbit_dal_version()
00145 {
00146     return MICROBIT_DAL_VERSION;
00147 }
00148 
00149 /**
00150  * Defines the length of time that the device will remain in a error state before resetting.
00151  *
00152  * @param iteration The number of times the error code will be displayed before resetting. Set to zero to remain in error state forever.
00153  *
00154  * @code
00155  * microbit_panic_timeout(4);
00156  * @endcode
00157  */
00158 void microbit_panic_timeout(int iterations)
00159 {
00160     panic_timeout = iterations;
00161 }
00162 
00163 /**
00164   * Disables all interrupts and user processing.
00165   * Displays "=(" and an accompanying status code on the default display.
00166   * @param statusCode the appropriate status code, must be in the range 0-999.
00167   *
00168   * @code
00169   * microbit_panic(20);
00170   * @endcode
00171   */
00172 void microbit_panic(int statusCode)
00173 {
00174     DigitalIn resetButton(MICROBIT_PIN_BUTTON_RESET);
00175     resetButton.mode(PullUp);
00176 
00177     uint32_t    row_mask = 0;
00178     uint32_t    col_mask = 0;
00179     uint32_t    row_reset = 0x01 << microbitMatrixMap.rowStart;
00180     uint32_t    row_data = row_reset;
00181     uint8_t     count = panic_timeout ? panic_timeout : 1;
00182     uint8_t     strobeRow = 0;
00183 
00184     row_mask = 0;
00185     for (int i = microbitMatrixMap.rowStart; i < microbitMatrixMap.rowStart + microbitMatrixMap.rows; i++)
00186         row_mask |= 0x01 << i;
00187 
00188     for (int i = microbitMatrixMap.columnStart; i < microbitMatrixMap.columnStart + microbitMatrixMap.columns; i++)
00189         col_mask |= 0x01 << i;
00190 
00191     PortOut LEDMatrix(Port0, row_mask | col_mask);
00192 
00193     if(statusCode < 0 || statusCode > 999)
00194         statusCode = 0;
00195 
00196     __disable_irq(); //stop ALL interrupts
00197 
00198 
00199     //point to the font stored in Flash
00200     const unsigned char* fontLocation = MicroBitFont::defaultFont;
00201 
00202     //get individual digits of status code, and place it into a single array/
00203     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)};
00204 
00205     while(count)
00206     {
00207         //iterate through our chars :)
00208         for(int characterCount = 0; characterCount < MICROBIT_PANIC_ERROR_CHARS; characterCount++)
00209         {
00210             int outerCount = 0;
00211 
00212             //display the current character
00213             while(outerCount < 500)
00214             {
00215                 uint32_t col_data = 0;
00216 
00217                 int i = 0;
00218 
00219                 //if we have hit the row limit - reset both the bit mask and the row variable
00220                 if(strobeRow == microbitMatrixMap.rows)
00221                 {
00222                     strobeRow = 0;
00223                     row_data = row_reset;
00224                 }
00225 
00226                 // Calculate the bitpattern to write.
00227                 for (i = 0; i < microbitMatrixMap.columns; i++)
00228                 {
00229                     int index = (i * microbitMatrixMap.rows) + strobeRow;
00230 
00231                     int bitMsk = 0x10 >> microbitMatrixMap.map[index].x; //chars are right aligned but read left to right
00232                     int y = microbitMatrixMap.map[index].y;
00233 
00234                     if(chars[characterCount][y] & bitMsk)
00235                         col_data |= (1 << i);
00236                 }
00237 
00238                 col_data = ~col_data << microbitMatrixMap.columnStart & col_mask;
00239 
00240                 if(chars[characterCount] == chars[(characterCount - 1) % MICROBIT_PANIC_ERROR_CHARS] && outerCount < 50)
00241                     LEDMatrix =  0;
00242                 else
00243                     LEDMatrix = col_data | row_data;
00244 
00245                 //burn cycles
00246                 i = 2000;
00247                 while(i>0)
00248                 {
00249                     // Check if the reset button has been pressed. Interrupts are disabled, so the normal method can't be relied upon...
00250                     if (resetButton == 0)
00251                         microbit_reset();
00252 
00253                     i--;
00254                 }
00255 
00256                 //update the bit mask and row count
00257                 row_data <<= 1;
00258                 strobeRow++;
00259                 outerCount++;
00260             }
00261         }
00262 
00263         if (panic_timeout)
00264             count--;
00265     }
00266 
00267     microbit_reset();
00268 }
00269 
00270 /**
00271   * Generate a random number in the given range.
00272   * We use a simple Galois LFSR random number generator here,
00273   * as a Galois LFSR is sufficient for our applications, and much more lightweight
00274   * than the hardware random number generator built int the processor, which takes
00275   * a long time and uses a lot of energy.
00276   *
00277   * KIDS: You shouldn't use this is the real world to generte cryptographic keys though...
00278   * have a think why not. :-)
00279   *
00280   * @param max the upper range to generate a number for. This number cannot be negative.
00281   *
00282   * @return A random, natural number between 0 and the max-1. Or MICROBIT_INVALID_VALUE if max is <= 0.
00283   *
00284   * @code
00285   * microbit_random(200); //a number between 0 and 199
00286   * @endcode
00287   */
00288 int microbit_random(int max)
00289 {
00290     uint32_t m, result;
00291 
00292     if(max <= 0)
00293         return MICROBIT_INVALID_PARAMETER;
00294 
00295     // Our maximum return value is actually one less than passed
00296     max--;
00297 
00298     do {
00299         m = (uint32_t)max;
00300         result = 0;
00301         do {
00302             // Cycle the LFSR (Linear Feedback Shift Register).
00303             // We use an optimal sequence with a period of 2^32-1, as defined by Bruce Schneier here (a true legend in the field!),
00304             // For those interested, it's documented in his paper:
00305             // "Pseudo-Random Sequence Generator for 32-Bit CPUs: A fast, machine-independent generator for 32-bit Microprocessors"
00306             // https://www.schneier.com/paper-pseudorandom-sequence.html
00307             uint32_t rnd = random_value;
00308 
00309             rnd = ((((rnd >> 31)
00310                           ^ (rnd >> 6)
00311                           ^ (rnd >> 4)
00312                           ^ (rnd >> 2)
00313                           ^ (rnd >> 1)
00314                           ^ rnd)
00315                           & 0x0000001)
00316                           << 31 )
00317                           | (rnd >> 1);
00318 
00319             random_value = rnd;
00320 
00321             result = ((result << 1) | (rnd & 0x00000001));
00322         } while(m >>= 1);
00323     } while (result > (uint32_t)max);
00324 
00325     return result;
00326 }
00327 
00328 /**
00329   * Seed the random number generator (RNG).
00330   *
00331   * This function uses the NRF51822's in built cryptographic random number generator to seed a Galois LFSR.
00332   * We do this as the hardware RNG is relatively high power, and is locked out by the BLE stack internally,
00333   * with a less than optimal application interface. A Galois LFSR is sufficient for our
00334   * applications, and much more lightweight.
00335   */
00336 void microbit_seed_random()
00337 {
00338     random_value = 0;
00339 
00340     if(ble_running())
00341     {
00342         // If Bluetooth is enabled, we need to go through the Nordic software to safely do this.
00343         uint32_t result = sd_rand_application_vector_get((uint8_t*)&random_value, sizeof(random_value));
00344 
00345         // If we couldn't get the random bytes then at least make the seed non-zero.
00346         if (result != NRF_SUCCESS)
00347             random_value = 0xBBC5EED;
00348     }
00349     else
00350     {
00351         // Othwerwise we can access the hardware RNG directly.
00352 
00353         // Start the Random number generator. No need to leave it running... I hope. :-)
00354         NRF_RNG->TASKS_START = 1;
00355 
00356         for(int i = 0; i < 4; i++)
00357         {
00358             // Clear the VALRDY EVENT
00359             NRF_RNG->EVENTS_VALRDY = 0;
00360 
00361             // Wait for a number ot be generated.
00362             while(NRF_RNG->EVENTS_VALRDY == 0);
00363 
00364             random_value = (random_value << 8) | ((int) NRF_RNG->VALUE);
00365         }
00366 
00367         // Disable the generator to save power.
00368         NRF_RNG->TASKS_STOP = 1;
00369     }
00370 }
00371 
00372 /**
00373   * Seed the pseudo random number generator (RNG) using the given 32-bit value.
00374   * This function does not use the NRF51822's in built cryptographic random number generator.
00375   *
00376   * @param seed The value to use as a seed.
00377   */
00378 void microbit_seed_random(uint32_t seed)
00379 {
00380     random_value = seed;
00381 }