Attempting to publish a tree

Dependencies:   BLE_API mbed-dev-bin nRF51822

Fork of microbit-dal by Lancaster University

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;
00071     sd_softdevice_is_enabled(&t);
00072     return t==1;
00073 }
00074 
00075 /**
00076  * Derived a unique, consistent serial number of this device from internal data.
00077  *
00078  * @return the serial number of this device.
00079  */
00080 uint32_t microbit_serial_number()
00081 {
00082     return NRF_FICR->DEVICEID[1];
00083 }
00084 
00085 /**
00086  * Derive the friendly name for this device, based on its serial number.
00087  *
00088  * @return the serial number of this device.
00089  */
00090 char* microbit_friendly_name()
00091 {
00092     const uint8_t codebook[MICROBIT_NAME_LENGTH][MICROBIT_NAME_CODE_LETTERS] =
00093     {
00094         {'z', 'v', 'g', 'p', 't'},
00095         {'u', 'o', 'i', 'e', 'a'},
00096         {'z', 'v', 'g', 'p', 't'},
00097         {'u', 'o', 'i', 'e', 'a'},
00098         {'z', 'v', 'g', 'p', 't'}
00099     };
00100 
00101     // We count right to left, so create a pointer to the end of the buffer.
00102     char *name = friendly_name;
00103     name += MICROBIT_NAME_LENGTH;
00104 
00105     // Terminate the string.
00106     *name = 0;
00107 
00108     // Derive our name from the nrf51822's unique ID.
00109     uint32_t n = microbit_serial_number();
00110     int ld = 1;
00111     int d = MICROBIT_NAME_CODE_LETTERS;
00112     int h;
00113 
00114     for (int i=0; i<MICROBIT_NAME_LENGTH; i++)
00115     {
00116         h = (n % d) / ld;
00117         n -= h;
00118         d *= MICROBIT_NAME_CODE_LETTERS;
00119         ld *= MICROBIT_NAME_CODE_LETTERS;
00120         *--name = codebook[i][h];
00121     }
00122 
00123     return friendly_name;
00124 }
00125 
00126 /**
00127   * Perform a hard reset of the micro:bit.
00128   */
00129 void
00130 microbit_reset()
00131 {
00132     NVIC_SystemReset();
00133 }
00134 
00135 /**
00136   * Determine the version of microbit-dal currently running.
00137   * @return a pointer to a character buffer containing a representation of the semantic version number.
00138   */
00139 const char *
00140 microbit_dal_version()
00141 {
00142     return MICROBIT_DAL_VERSION;
00143 }
00144 
00145 /**
00146  * Defines the length of time that the device will remain in a error state before resetting.
00147  *
00148  * @param iteration The number of times the error code will be displayed before resetting. Set to zero to remain in error state forever.
00149  *
00150  * @code
00151  * microbit_panic_timeout(4);
00152  * @endcode
00153  */
00154 void microbit_panic_timeout(int iterations)
00155 {
00156     panic_timeout = iterations;
00157 }
00158 
00159 /**
00160   * Disables all interrupts and user processing.
00161   * Displays "=(" and an accompanying status code on the default display.
00162   * @param statusCode the appropriate status code - 0 means no code will be displayed. Status codes must be in the range 0-255.
00163   *
00164   * @code
00165   * microbit_panic(20);
00166   * @endcode
00167   */
00168 void microbit_panic(int statusCode)
00169 {
00170     DigitalIn resetButton(MICROBIT_PIN_BUTTON_RESET);
00171     resetButton.mode(PullUp);
00172 
00173     uint32_t    row_mask = 0;
00174     uint32_t    col_mask = 0;
00175     uint32_t    row_reset = 0x01 << microbitMatrixMap.rowStart;
00176     uint32_t    row_data = row_reset;
00177     uint8_t     count = panic_timeout ? panic_timeout : 1;
00178     uint8_t     strobeRow = 0;
00179 
00180     row_mask = 0;
00181     for (int i = microbitMatrixMap.rowStart; i < microbitMatrixMap.rowStart + microbitMatrixMap.rows; i++)
00182         row_mask |= 0x01 << i;
00183 
00184     for (int i = microbitMatrixMap.columnStart; i < microbitMatrixMap.columnStart + microbitMatrixMap.columns; i++)
00185         col_mask |= 0x01 << i;
00186 
00187     PortOut LEDMatrix(Port0, row_mask | col_mask);
00188 
00189     if(statusCode < 0 || statusCode > 255)
00190         statusCode = 0;
00191 
00192     __disable_irq(); //stop ALL interrupts
00193 
00194 
00195     //point to the font stored in Flash
00196     const unsigned char * fontLocation = MicroBitFont::defaultFont;
00197 
00198     //get individual digits of status code, and place it into a single array/
00199     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)};
00200 
00201     //enter infinite loop.
00202     while(count)
00203     {
00204         //iterate through our chars :)
00205         for(int characterCount = 0; characterCount < MICROBIT_PANIC_ERROR_CHARS; characterCount++)
00206         {
00207             int outerCount = 0;
00208 
00209             //display the current character
00210             while(outerCount < 500)
00211             {
00212                 uint32_t col_data = 0;
00213 
00214                 int i = 0;
00215 
00216                 //if we have hit the row limit - reset both the bit mask and the row variable
00217                 if(strobeRow == microbitMatrixMap.rows)
00218                 {
00219                     strobeRow = 0;
00220                     row_data = row_reset;
00221                 }
00222 
00223                 // Calculate the bitpattern to write.
00224                 for (i = 0; i < microbitMatrixMap.columns; i++)
00225                 {
00226                     int index = (i * microbitMatrixMap.rows) + strobeRow;
00227 
00228                     int bitMsk = 0x10 >> microbitMatrixMap.map[index].x; //chars are right aligned but read left to right
00229                     int y = microbitMatrixMap.map[index].y;
00230 
00231                     if(chars[characterCount][y] & bitMsk)
00232                         col_data |= (1 << i);
00233                 }
00234 
00235                 col_data = ~col_data << microbitMatrixMap.columnStart & col_mask;
00236 
00237                 LEDMatrix = col_data | row_data;
00238 
00239                 //burn cycles
00240                 i = 1000;
00241                 while(i>0)
00242                 {
00243                     // Check if the reset button has been pressed. Interrupts are disabled, so the normal method can't be relied upon...
00244                     if (resetButton == 0)
00245                         microbit_reset();
00246 
00247                     i--;
00248                 }
00249 
00250                 //update the bit mask and row count
00251                 row_data <<= 1;
00252                 strobeRow++;
00253                 outerCount++;
00254             }
00255         }
00256 
00257         if (panic_timeout)
00258             count--;
00259     }
00260 
00261     microbit_reset();
00262 }
00263 
00264 /**
00265   * Generate a random number in the given range.
00266   * We use a simple Galois LFSR random number generator here,
00267   * as a Galois LFSR is sufficient for our applications, and much more lightweight
00268   * than the hardware random number generator built int the processor, which takes
00269   * a long time and uses a lot of energy.
00270   *
00271   * KIDS: You shouldn't use this is the real world to generte cryptographic keys though...
00272   * have a think why not. :-)
00273   *
00274   * @param max the upper range to generate a number for. This number cannot be negative.
00275   *
00276   * @return A random, natural number between 0 and the max-1. Or MICROBIT_INVALID_VALUE if max is <= 0.
00277   *
00278   * @code
00279   * microbit_random(200); //a number between 0 and 199
00280   * @endcode
00281   */
00282 int microbit_random(int max)
00283 {
00284     uint32_t m, result;
00285 
00286     if(max <= 0)
00287         return MICROBIT_INVALID_PARAMETER;
00288 
00289     // Our maximum return value is actually one less than passed
00290     max--;
00291 
00292     do {
00293         m = (uint32_t)max;
00294         result = 0;
00295         do {
00296             // Cycle the LFSR (Linear Feedback Shift Register).
00297             // We use an optimal sequence with a period of 2^32-1, as defined by Bruce Schneier here (a true legend in the field!),
00298             // For those interested, it's documented in his paper:
00299             // "Pseudo-Random Sequence Generator for 32-Bit CPUs: A fast, machine-independent generator for 32-bit Microprocessors"
00300             // https://www.schneier.com/paper-pseudorandom-sequence.html
00301             uint32_t rnd = random_value;
00302 
00303             rnd = ((((rnd >> 31)
00304                           ^ (rnd >> 6)
00305                           ^ (rnd >> 4)
00306                           ^ (rnd >> 2)
00307                           ^ (rnd >> 1)
00308                           ^ rnd)
00309                           & 0x0000001)
00310                           << 31 )
00311                           | (rnd >> 1);
00312 
00313             random_value = rnd;
00314 
00315             result = ((result << 1) | (rnd & 0x00000001));
00316         } while(m >>= 1);
00317     } while (result > (uint32_t)max);
00318 
00319     return result;
00320 }
00321 
00322 /**
00323   * Seed the random number generator (RNG).
00324   *
00325   * This function uses the NRF51822's in built cryptographic random number generator to seed a Galois LFSR.
00326   * We do this as the hardware RNG is relatively high power, and is locked out by the BLE stack internally,
00327   * with a less than optimal application interface. A Galois LFSR is sufficient for our
00328   * applications, and much more lightweight.
00329   */
00330 void microbit_seed_random()
00331 {
00332     random_value = 0;
00333 
00334     if(ble_running())
00335     {
00336         // If Bluetooth is enabled, we need to go through the Nordic software to safely do this.
00337         uint32_t result = sd_rand_application_vector_get((uint8_t*)&random_value, sizeof(random_value));
00338 
00339         // If we couldn't get the random bytes then at least make the seed non-zero.
00340         if (result != NRF_SUCCESS)
00341             random_value = 0xBBC5EED;
00342     }
00343     else
00344     {
00345         // Othwerwise we can access the hardware RNG directly.
00346 
00347         // Start the Random number generator. No need to leave it running... I hope. :-)
00348         NRF_RNG->TASKS_START = 1;
00349 
00350         for(int i = 0; i < 4; i++)
00351         {
00352             // Clear the VALRDY EVENT
00353             NRF_RNG->EVENTS_VALRDY = 0;
00354 
00355             // Wait for a number ot be generated.
00356             while(NRF_RNG->EVENTS_VALRDY == 0);
00357 
00358             random_value = (random_value << 8) | ((int) NRF_RNG->VALUE);
00359         }
00360 
00361         // Disable the generator to save power.
00362         NRF_RNG->TASKS_STOP = 1;
00363     }
00364 }
00365 
00366 /**
00367   * Seed the pseudo random number generator (RNG) using the given 32-bit value.
00368   * This function does not use the NRF51822's in built cryptographic random number generator.
00369   *
00370   * @param seed The value to use as a seed.
00371   */
00372 void microbit_seed_random(uint32_t seed)
00373 {
00374     random_value = seed;
00375 }