microbit-dal

Dependencies:   BLE_API mbed-dev-bin nRF51822

Fork of microbit-dal by Lancaster University

Committer:
jancumps
Date:
Wed Nov 07 19:56:26 2018 +0000
Revision:
75:23164d324459
Parent:
66:2fc7d7c2fffc
unchanged

Who changed what in which revision?

UserRevisionLine numberNew contents of line
Jonathan Austin 1:8aa5cdb4ab67 1 /*
Jonathan Austin 1:8aa5cdb4ab67 2 The MIT License (MIT)
Jonathan Austin 1:8aa5cdb4ab67 3
Jonathan Austin 1:8aa5cdb4ab67 4 Copyright (c) 2016 British Broadcasting Corporation.
Jonathan Austin 1:8aa5cdb4ab67 5 This software is provided by Lancaster University by arrangement with the BBC.
Jonathan Austin 1:8aa5cdb4ab67 6
Jonathan Austin 1:8aa5cdb4ab67 7 Permission is hereby granted, free of charge, to any person obtaining a
Jonathan Austin 1:8aa5cdb4ab67 8 copy of this software and associated documentation files (the "Software"),
Jonathan Austin 1:8aa5cdb4ab67 9 to deal in the Software without restriction, including without limitation
Jonathan Austin 1:8aa5cdb4ab67 10 the rights to use, copy, modify, merge, publish, distribute, sublicense,
Jonathan Austin 1:8aa5cdb4ab67 11 and/or sell copies of the Software, and to permit persons to whom the
Jonathan Austin 1:8aa5cdb4ab67 12 Software is furnished to do so, subject to the following conditions:
Jonathan Austin 1:8aa5cdb4ab67 13
Jonathan Austin 1:8aa5cdb4ab67 14 The above copyright notice and this permission notice shall be included in
Jonathan Austin 1:8aa5cdb4ab67 15 all copies or substantial portions of the Software.
Jonathan Austin 1:8aa5cdb4ab67 16
Jonathan Austin 1:8aa5cdb4ab67 17 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
Jonathan Austin 1:8aa5cdb4ab67 18 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
Jonathan Austin 1:8aa5cdb4ab67 19 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
Jonathan Austin 1:8aa5cdb4ab67 20 THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
Jonathan Austin 1:8aa5cdb4ab67 21 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
Jonathan Austin 1:8aa5cdb4ab67 22 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
Jonathan Austin 1:8aa5cdb4ab67 23 DEALINGS IN THE SOFTWARE.
Jonathan Austin 1:8aa5cdb4ab67 24 */
Jonathan Austin 1:8aa5cdb4ab67 25
Jonathan Austin 1:8aa5cdb4ab67 26 /**
Jonathan Austin 1:8aa5cdb4ab67 27 * Compatibility / portability funcitons and constants for the MicroBit DAL.
Jonathan Austin 1:8aa5cdb4ab67 28 */
Jonathan Austin 1:8aa5cdb4ab67 29 #include "MicroBitConfig.h"
Jonathan Austin 1:8aa5cdb4ab67 30 #include "MicroBitButton.h"
Jonathan Austin 1:8aa5cdb4ab67 31 #include "MicroBitDevice.h"
Jonathan Austin 1:8aa5cdb4ab67 32 #include "MicroBitFont.h"
Jonathan Austin 1:8aa5cdb4ab67 33 #include "mbed.h"
Jonathan Austin 1:8aa5cdb4ab67 34 #include "ErrorNo.h"
Jonathan Austin 1:8aa5cdb4ab67 35
Jonathan Austin 1:8aa5cdb4ab67 36 /*
Jonathan Austin 1:8aa5cdb4ab67 37 * The underlying Nordic libraries that support BLE do not compile cleanly with the stringent GCC settings we employ
Jonathan Austin 1:8aa5cdb4ab67 38 * If we're compiling under GCC, then we suppress any warnings generated from this code (but not the rest of the DAL)
Jonathan Austin 1:8aa5cdb4ab67 39 * The ARM cc compiler is more tolerant. We don't test __GNUC__ here to detect GCC as ARMCC also typically sets this
Jonathan Austin 1:8aa5cdb4ab67 40 * as a compatability option, but does not support the options used...
Jonathan Austin 1:8aa5cdb4ab67 41 */
Jonathan Austin 1:8aa5cdb4ab67 42 #if !defined(__arm)
Jonathan Austin 1:8aa5cdb4ab67 43 #pragma GCC diagnostic ignored "-Wunused-function"
Jonathan Austin 1:8aa5cdb4ab67 44 #pragma GCC diagnostic push
Jonathan Austin 1:8aa5cdb4ab67 45 #pragma GCC diagnostic ignored "-Wunused-parameter"
Jonathan Austin 1:8aa5cdb4ab67 46 #endif
Jonathan Austin 1:8aa5cdb4ab67 47
Jonathan Austin 1:8aa5cdb4ab67 48 #include "nrf_soc.h"
Jonathan Austin 1:8aa5cdb4ab67 49 #include "nrf_sdm.h"
Jonathan Austin 1:8aa5cdb4ab67 50
Jonathan Austin 1:8aa5cdb4ab67 51 /*
Jonathan Austin 1:8aa5cdb4ab67 52 * Return to our predefined compiler settings.
Jonathan Austin 1:8aa5cdb4ab67 53 */
Jonathan Austin 1:8aa5cdb4ab67 54 #if !defined(__arm)
Jonathan Austin 1:8aa5cdb4ab67 55 #pragma GCC diagnostic pop
Jonathan Austin 1:8aa5cdb4ab67 56 #endif
Jonathan Austin 1:8aa5cdb4ab67 57
Jonathan Austin 1:8aa5cdb4ab67 58 static char friendly_name[MICROBIT_NAME_LENGTH+1];
Jonathan Austin 1:8aa5cdb4ab67 59 static const uint8_t panicFace[5] = {0x1B, 0x1B,0x0,0x0E,0x11};
Jonathan Austin 1:8aa5cdb4ab67 60 static int panic_timeout = 0;
Jonathan Austin 1:8aa5cdb4ab67 61 static uint32_t random_value = 0;
Jonathan Austin 1:8aa5cdb4ab67 62
Jonathan Austin 1:8aa5cdb4ab67 63 /**
Jonathan Austin 1:8aa5cdb4ab67 64 * Determines if a BLE stack is currently running.
Jonathan Austin 1:8aa5cdb4ab67 65 *
Jonathan Austin 1:8aa5cdb4ab67 66 * @return true is a bluetooth stack is operational, false otherwise.
Jonathan Austin 1:8aa5cdb4ab67 67 */
Jonathan Austin 1:8aa5cdb4ab67 68 bool ble_running()
Jonathan Austin 1:8aa5cdb4ab67 69 {
LancasterUniversity 66:2fc7d7c2fffc 70 uint8_t t = 0;
LancasterUniversity 66:2fc7d7c2fffc 71
LancasterUniversity 66:2fc7d7c2fffc 72 #if CONFIG_ENABLED(MICROBIT_BLE_ENABLED) || CONFIG_ENABLED(MICROBIT_BLE_PAIRING_MODE)
Jonathan Austin 1:8aa5cdb4ab67 73 sd_softdevice_is_enabled(&t);
LancasterUniversity 66:2fc7d7c2fffc 74 #endif
LancasterUniversity 66:2fc7d7c2fffc 75
Jonathan Austin 1:8aa5cdb4ab67 76 return t==1;
Jonathan Austin 1:8aa5cdb4ab67 77 }
Jonathan Austin 1:8aa5cdb4ab67 78
Jonathan Austin 1:8aa5cdb4ab67 79 /**
Jonathan Austin 1:8aa5cdb4ab67 80 * Derived a unique, consistent serial number of this device from internal data.
Jonathan Austin 1:8aa5cdb4ab67 81 *
Jonathan Austin 1:8aa5cdb4ab67 82 * @return the serial number of this device.
Jonathan Austin 1:8aa5cdb4ab67 83 */
Jonathan Austin 1:8aa5cdb4ab67 84 uint32_t microbit_serial_number()
Jonathan Austin 1:8aa5cdb4ab67 85 {
Jonathan Austin 1:8aa5cdb4ab67 86 return NRF_FICR->DEVICEID[1];
Jonathan Austin 1:8aa5cdb4ab67 87 }
Jonathan Austin 1:8aa5cdb4ab67 88
Jonathan Austin 1:8aa5cdb4ab67 89 /**
Jonathan Austin 1:8aa5cdb4ab67 90 * Derive the friendly name for this device, based on its serial number.
Jonathan Austin 1:8aa5cdb4ab67 91 *
Jonathan Austin 1:8aa5cdb4ab67 92 * @return the serial number of this device.
Jonathan Austin 1:8aa5cdb4ab67 93 */
Jonathan Austin 1:8aa5cdb4ab67 94 char* microbit_friendly_name()
Jonathan Austin 1:8aa5cdb4ab67 95 {
Jonathan Austin 1:8aa5cdb4ab67 96 const uint8_t codebook[MICROBIT_NAME_LENGTH][MICROBIT_NAME_CODE_LETTERS] =
Jonathan Austin 1:8aa5cdb4ab67 97 {
Jonathan Austin 1:8aa5cdb4ab67 98 {'z', 'v', 'g', 'p', 't'},
Jonathan Austin 1:8aa5cdb4ab67 99 {'u', 'o', 'i', 'e', 'a'},
Jonathan Austin 1:8aa5cdb4ab67 100 {'z', 'v', 'g', 'p', 't'},
Jonathan Austin 1:8aa5cdb4ab67 101 {'u', 'o', 'i', 'e', 'a'},
Jonathan Austin 1:8aa5cdb4ab67 102 {'z', 'v', 'g', 'p', 't'}
Jonathan Austin 1:8aa5cdb4ab67 103 };
Jonathan Austin 1:8aa5cdb4ab67 104
Jonathan Austin 1:8aa5cdb4ab67 105 // We count right to left, so create a pointer to the end of the buffer.
Jonathan Austin 1:8aa5cdb4ab67 106 char *name = friendly_name;
Jonathan Austin 1:8aa5cdb4ab67 107 name += MICROBIT_NAME_LENGTH;
Jonathan Austin 1:8aa5cdb4ab67 108
Jonathan Austin 1:8aa5cdb4ab67 109 // Terminate the string.
Jonathan Austin 1:8aa5cdb4ab67 110 *name = 0;
Jonathan Austin 1:8aa5cdb4ab67 111
Jonathan Austin 1:8aa5cdb4ab67 112 // Derive our name from the nrf51822's unique ID.
Jonathan Austin 1:8aa5cdb4ab67 113 uint32_t n = microbit_serial_number();
Jonathan Austin 1:8aa5cdb4ab67 114 int ld = 1;
Jonathan Austin 1:8aa5cdb4ab67 115 int d = MICROBIT_NAME_CODE_LETTERS;
Jonathan Austin 1:8aa5cdb4ab67 116 int h;
Jonathan Austin 1:8aa5cdb4ab67 117
Jonathan Austin 1:8aa5cdb4ab67 118 for (int i=0; i<MICROBIT_NAME_LENGTH; i++)
Jonathan Austin 1:8aa5cdb4ab67 119 {
Jonathan Austin 1:8aa5cdb4ab67 120 h = (n % d) / ld;
Jonathan Austin 1:8aa5cdb4ab67 121 n -= h;
Jonathan Austin 1:8aa5cdb4ab67 122 d *= MICROBIT_NAME_CODE_LETTERS;
Jonathan Austin 1:8aa5cdb4ab67 123 ld *= MICROBIT_NAME_CODE_LETTERS;
Jonathan Austin 1:8aa5cdb4ab67 124 *--name = codebook[i][h];
Jonathan Austin 1:8aa5cdb4ab67 125 }
Jonathan Austin 1:8aa5cdb4ab67 126
Jonathan Austin 1:8aa5cdb4ab67 127 return friendly_name;
Jonathan Austin 1:8aa5cdb4ab67 128 }
Jonathan Austin 1:8aa5cdb4ab67 129
Jonathan Austin 1:8aa5cdb4ab67 130 /**
Jonathan Austin 1:8aa5cdb4ab67 131 * Perform a hard reset of the micro:bit.
Jonathan Austin 1:8aa5cdb4ab67 132 */
Jonathan Austin 1:8aa5cdb4ab67 133 void
Jonathan Austin 1:8aa5cdb4ab67 134 microbit_reset()
Jonathan Austin 1:8aa5cdb4ab67 135 {
Jonathan Austin 1:8aa5cdb4ab67 136 NVIC_SystemReset();
Jonathan Austin 1:8aa5cdb4ab67 137 }
Jonathan Austin 1:8aa5cdb4ab67 138
Jonathan Austin 1:8aa5cdb4ab67 139 /**
Jonathan Austin 1:8aa5cdb4ab67 140 * Determine the version of microbit-dal currently running.
Jonathan Austin 1:8aa5cdb4ab67 141 * @return a pointer to a character buffer containing a representation of the semantic version number.
Jonathan Austin 1:8aa5cdb4ab67 142 */
Jonathan Austin 1:8aa5cdb4ab67 143 const char *
Jonathan Austin 1:8aa5cdb4ab67 144 microbit_dal_version()
Jonathan Austin 1:8aa5cdb4ab67 145 {
Jonathan Austin 1:8aa5cdb4ab67 146 return MICROBIT_DAL_VERSION;
Jonathan Austin 1:8aa5cdb4ab67 147 }
Jonathan Austin 1:8aa5cdb4ab67 148
Jonathan Austin 1:8aa5cdb4ab67 149 /**
Jonathan Austin 1:8aa5cdb4ab67 150 * Defines the length of time that the device will remain in a error state before resetting.
Jonathan Austin 1:8aa5cdb4ab67 151 *
Jonathan Austin 1:8aa5cdb4ab67 152 * @param iteration The number of times the error code will be displayed before resetting. Set to zero to remain in error state forever.
Jonathan Austin 1:8aa5cdb4ab67 153 *
Jonathan Austin 1:8aa5cdb4ab67 154 * @code
Jonathan Austin 1:8aa5cdb4ab67 155 * microbit_panic_timeout(4);
Jonathan Austin 1:8aa5cdb4ab67 156 * @endcode
Jonathan Austin 1:8aa5cdb4ab67 157 */
Jonathan Austin 1:8aa5cdb4ab67 158 void microbit_panic_timeout(int iterations)
Jonathan Austin 1:8aa5cdb4ab67 159 {
Jonathan Austin 1:8aa5cdb4ab67 160 panic_timeout = iterations;
Jonathan Austin 1:8aa5cdb4ab67 161 }
Jonathan Austin 1:8aa5cdb4ab67 162
Jonathan Austin 1:8aa5cdb4ab67 163 /**
Jonathan Austin 1:8aa5cdb4ab67 164 * Disables all interrupts and user processing.
Jonathan Austin 1:8aa5cdb4ab67 165 * Displays "=(" and an accompanying status code on the default display.
LancasterUniversity 47:69f452b1a5c9 166 * @param statusCode the appropriate status code, must be in the range 0-999.
Jonathan Austin 1:8aa5cdb4ab67 167 *
Jonathan Austin 1:8aa5cdb4ab67 168 * @code
Jonathan Austin 1:8aa5cdb4ab67 169 * microbit_panic(20);
Jonathan Austin 1:8aa5cdb4ab67 170 * @endcode
Jonathan Austin 1:8aa5cdb4ab67 171 */
Jonathan Austin 1:8aa5cdb4ab67 172 void microbit_panic(int statusCode)
Jonathan Austin 1:8aa5cdb4ab67 173 {
Jonathan Austin 1:8aa5cdb4ab67 174 DigitalIn resetButton(MICROBIT_PIN_BUTTON_RESET);
Jonathan Austin 1:8aa5cdb4ab67 175 resetButton.mode(PullUp);
Jonathan Austin 1:8aa5cdb4ab67 176
Jonathan Austin 1:8aa5cdb4ab67 177 uint32_t row_mask = 0;
Jonathan Austin 1:8aa5cdb4ab67 178 uint32_t col_mask = 0;
Jonathan Austin 1:8aa5cdb4ab67 179 uint32_t row_reset = 0x01 << microbitMatrixMap.rowStart;
Jonathan Austin 1:8aa5cdb4ab67 180 uint32_t row_data = row_reset;
Jonathan Austin 1:8aa5cdb4ab67 181 uint8_t count = panic_timeout ? panic_timeout : 1;
Jonathan Austin 1:8aa5cdb4ab67 182 uint8_t strobeRow = 0;
Jonathan Austin 1:8aa5cdb4ab67 183
Jonathan Austin 1:8aa5cdb4ab67 184 row_mask = 0;
Jonathan Austin 1:8aa5cdb4ab67 185 for (int i = microbitMatrixMap.rowStart; i < microbitMatrixMap.rowStart + microbitMatrixMap.rows; i++)
Jonathan Austin 1:8aa5cdb4ab67 186 row_mask |= 0x01 << i;
Jonathan Austin 1:8aa5cdb4ab67 187
Jonathan Austin 1:8aa5cdb4ab67 188 for (int i = microbitMatrixMap.columnStart; i < microbitMatrixMap.columnStart + microbitMatrixMap.columns; i++)
Jonathan Austin 1:8aa5cdb4ab67 189 col_mask |= 0x01 << i;
Jonathan Austin 1:8aa5cdb4ab67 190
Jonathan Austin 1:8aa5cdb4ab67 191 PortOut LEDMatrix(Port0, row_mask | col_mask);
Jonathan Austin 1:8aa5cdb4ab67 192
LancasterUniversity 47:69f452b1a5c9 193 if(statusCode < 0 || statusCode > 999)
Jonathan Austin 1:8aa5cdb4ab67 194 statusCode = 0;
Jonathan Austin 1:8aa5cdb4ab67 195
Jonathan Austin 1:8aa5cdb4ab67 196 __disable_irq(); //stop ALL interrupts
Jonathan Austin 1:8aa5cdb4ab67 197
Jonathan Austin 1:8aa5cdb4ab67 198
Jonathan Austin 1:8aa5cdb4ab67 199 //point to the font stored in Flash
LancasterUniversity 47:69f452b1a5c9 200 const unsigned char* fontLocation = MicroBitFont::defaultFont;
Jonathan Austin 1:8aa5cdb4ab67 201
Jonathan Austin 1:8aa5cdb4ab67 202 //get individual digits of status code, and place it into a single array/
Jonathan Austin 1:8aa5cdb4ab67 203 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)};
Jonathan Austin 1:8aa5cdb4ab67 204
Jonathan Austin 1:8aa5cdb4ab67 205 while(count)
Jonathan Austin 1:8aa5cdb4ab67 206 {
Jonathan Austin 1:8aa5cdb4ab67 207 //iterate through our chars :)
Jonathan Austin 1:8aa5cdb4ab67 208 for(int characterCount = 0; characterCount < MICROBIT_PANIC_ERROR_CHARS; characterCount++)
Jonathan Austin 1:8aa5cdb4ab67 209 {
Jonathan Austin 1:8aa5cdb4ab67 210 int outerCount = 0;
Jonathan Austin 1:8aa5cdb4ab67 211
Jonathan Austin 1:8aa5cdb4ab67 212 //display the current character
Jonathan Austin 1:8aa5cdb4ab67 213 while(outerCount < 500)
Jonathan Austin 1:8aa5cdb4ab67 214 {
Jonathan Austin 1:8aa5cdb4ab67 215 uint32_t col_data = 0;
Jonathan Austin 1:8aa5cdb4ab67 216
Jonathan Austin 1:8aa5cdb4ab67 217 int i = 0;
Jonathan Austin 1:8aa5cdb4ab67 218
Jonathan Austin 1:8aa5cdb4ab67 219 //if we have hit the row limit - reset both the bit mask and the row variable
Jonathan Austin 1:8aa5cdb4ab67 220 if(strobeRow == microbitMatrixMap.rows)
Jonathan Austin 1:8aa5cdb4ab67 221 {
Jonathan Austin 1:8aa5cdb4ab67 222 strobeRow = 0;
Jonathan Austin 1:8aa5cdb4ab67 223 row_data = row_reset;
Jonathan Austin 1:8aa5cdb4ab67 224 }
Jonathan Austin 1:8aa5cdb4ab67 225
Jonathan Austin 1:8aa5cdb4ab67 226 // Calculate the bitpattern to write.
Jonathan Austin 1:8aa5cdb4ab67 227 for (i = 0; i < microbitMatrixMap.columns; i++)
Jonathan Austin 1:8aa5cdb4ab67 228 {
Jonathan Austin 1:8aa5cdb4ab67 229 int index = (i * microbitMatrixMap.rows) + strobeRow;
Jonathan Austin 1:8aa5cdb4ab67 230
Jonathan Austin 1:8aa5cdb4ab67 231 int bitMsk = 0x10 >> microbitMatrixMap.map[index].x; //chars are right aligned but read left to right
Jonathan Austin 1:8aa5cdb4ab67 232 int y = microbitMatrixMap.map[index].y;
Jonathan Austin 1:8aa5cdb4ab67 233
Jonathan Austin 1:8aa5cdb4ab67 234 if(chars[characterCount][y] & bitMsk)
Jonathan Austin 1:8aa5cdb4ab67 235 col_data |= (1 << i);
Jonathan Austin 1:8aa5cdb4ab67 236 }
Jonathan Austin 1:8aa5cdb4ab67 237
Jonathan Austin 1:8aa5cdb4ab67 238 col_data = ~col_data << microbitMatrixMap.columnStart & col_mask;
Jonathan Austin 1:8aa5cdb4ab67 239
LancasterUniversity 47:69f452b1a5c9 240 if(chars[characterCount] == chars[(characterCount - 1) % MICROBIT_PANIC_ERROR_CHARS] && outerCount < 50)
LancasterUniversity 47:69f452b1a5c9 241 LEDMatrix = 0;
LancasterUniversity 47:69f452b1a5c9 242 else
LancasterUniversity 47:69f452b1a5c9 243 LEDMatrix = col_data | row_data;
Jonathan Austin 1:8aa5cdb4ab67 244
Jonathan Austin 1:8aa5cdb4ab67 245 //burn cycles
LancasterUniversity 47:69f452b1a5c9 246 i = 2000;
Jonathan Austin 1:8aa5cdb4ab67 247 while(i>0)
Jonathan Austin 1:8aa5cdb4ab67 248 {
Jonathan Austin 1:8aa5cdb4ab67 249 // Check if the reset button has been pressed. Interrupts are disabled, so the normal method can't be relied upon...
Jonathan Austin 1:8aa5cdb4ab67 250 if (resetButton == 0)
Jonathan Austin 1:8aa5cdb4ab67 251 microbit_reset();
Jonathan Austin 1:8aa5cdb4ab67 252
Jonathan Austin 1:8aa5cdb4ab67 253 i--;
Jonathan Austin 1:8aa5cdb4ab67 254 }
Jonathan Austin 1:8aa5cdb4ab67 255
Jonathan Austin 1:8aa5cdb4ab67 256 //update the bit mask and row count
Jonathan Austin 1:8aa5cdb4ab67 257 row_data <<= 1;
Jonathan Austin 1:8aa5cdb4ab67 258 strobeRow++;
Jonathan Austin 1:8aa5cdb4ab67 259 outerCount++;
Jonathan Austin 1:8aa5cdb4ab67 260 }
Jonathan Austin 1:8aa5cdb4ab67 261 }
Jonathan Austin 1:8aa5cdb4ab67 262
Jonathan Austin 1:8aa5cdb4ab67 263 if (panic_timeout)
Jonathan Austin 1:8aa5cdb4ab67 264 count--;
Jonathan Austin 1:8aa5cdb4ab67 265 }
Jonathan Austin 1:8aa5cdb4ab67 266
Jonathan Austin 1:8aa5cdb4ab67 267 microbit_reset();
Jonathan Austin 1:8aa5cdb4ab67 268 }
Jonathan Austin 1:8aa5cdb4ab67 269
Jonathan Austin 1:8aa5cdb4ab67 270 /**
Jonathan Austin 1:8aa5cdb4ab67 271 * Generate a random number in the given range.
Jonathan Austin 1:8aa5cdb4ab67 272 * We use a simple Galois LFSR random number generator here,
Jonathan Austin 1:8aa5cdb4ab67 273 * as a Galois LFSR is sufficient for our applications, and much more lightweight
Jonathan Austin 1:8aa5cdb4ab67 274 * than the hardware random number generator built int the processor, which takes
Jonathan Austin 1:8aa5cdb4ab67 275 * a long time and uses a lot of energy.
Jonathan Austin 1:8aa5cdb4ab67 276 *
Jonathan Austin 1:8aa5cdb4ab67 277 * KIDS: You shouldn't use this is the real world to generte cryptographic keys though...
Jonathan Austin 1:8aa5cdb4ab67 278 * have a think why not. :-)
Jonathan Austin 1:8aa5cdb4ab67 279 *
Jonathan Austin 1:8aa5cdb4ab67 280 * @param max the upper range to generate a number for. This number cannot be negative.
Jonathan Austin 1:8aa5cdb4ab67 281 *
Jonathan Austin 1:8aa5cdb4ab67 282 * @return A random, natural number between 0 and the max-1. Or MICROBIT_INVALID_VALUE if max is <= 0.
Jonathan Austin 1:8aa5cdb4ab67 283 *
Jonathan Austin 1:8aa5cdb4ab67 284 * @code
Jonathan Austin 1:8aa5cdb4ab67 285 * microbit_random(200); //a number between 0 and 199
Jonathan Austin 1:8aa5cdb4ab67 286 * @endcode
Jonathan Austin 1:8aa5cdb4ab67 287 */
Jonathan Austin 1:8aa5cdb4ab67 288 int microbit_random(int max)
Jonathan Austin 1:8aa5cdb4ab67 289 {
Jonathan Austin 1:8aa5cdb4ab67 290 uint32_t m, result;
Jonathan Austin 1:8aa5cdb4ab67 291
Jonathan Austin 1:8aa5cdb4ab67 292 if(max <= 0)
Jonathan Austin 1:8aa5cdb4ab67 293 return MICROBIT_INVALID_PARAMETER;
Jonathan Austin 1:8aa5cdb4ab67 294
Jonathan Austin 1:8aa5cdb4ab67 295 // Our maximum return value is actually one less than passed
Jonathan Austin 1:8aa5cdb4ab67 296 max--;
Jonathan Austin 1:8aa5cdb4ab67 297
Jonathan Austin 1:8aa5cdb4ab67 298 do {
Jonathan Austin 1:8aa5cdb4ab67 299 m = (uint32_t)max;
Jonathan Austin 1:8aa5cdb4ab67 300 result = 0;
Jonathan Austin 1:8aa5cdb4ab67 301 do {
Jonathan Austin 1:8aa5cdb4ab67 302 // Cycle the LFSR (Linear Feedback Shift Register).
Jonathan Austin 1:8aa5cdb4ab67 303 // We use an optimal sequence with a period of 2^32-1, as defined by Bruce Schneier here (a true legend in the field!),
Jonathan Austin 1:8aa5cdb4ab67 304 // For those interested, it's documented in his paper:
Jonathan Austin 1:8aa5cdb4ab67 305 // "Pseudo-Random Sequence Generator for 32-Bit CPUs: A fast, machine-independent generator for 32-bit Microprocessors"
Jonathan Austin 1:8aa5cdb4ab67 306 // https://www.schneier.com/paper-pseudorandom-sequence.html
Jonathan Austin 1:8aa5cdb4ab67 307 uint32_t rnd = random_value;
Jonathan Austin 1:8aa5cdb4ab67 308
Jonathan Austin 1:8aa5cdb4ab67 309 rnd = ((((rnd >> 31)
Jonathan Austin 1:8aa5cdb4ab67 310 ^ (rnd >> 6)
Jonathan Austin 1:8aa5cdb4ab67 311 ^ (rnd >> 4)
Jonathan Austin 1:8aa5cdb4ab67 312 ^ (rnd >> 2)
Jonathan Austin 1:8aa5cdb4ab67 313 ^ (rnd >> 1)
Jonathan Austin 1:8aa5cdb4ab67 314 ^ rnd)
Jonathan Austin 1:8aa5cdb4ab67 315 & 0x0000001)
Jonathan Austin 1:8aa5cdb4ab67 316 << 31 )
Jonathan Austin 1:8aa5cdb4ab67 317 | (rnd >> 1);
Jonathan Austin 1:8aa5cdb4ab67 318
Jonathan Austin 1:8aa5cdb4ab67 319 random_value = rnd;
Jonathan Austin 1:8aa5cdb4ab67 320
Jonathan Austin 1:8aa5cdb4ab67 321 result = ((result << 1) | (rnd & 0x00000001));
Jonathan Austin 1:8aa5cdb4ab67 322 } while(m >>= 1);
Jonathan Austin 1:8aa5cdb4ab67 323 } while (result > (uint32_t)max);
Jonathan Austin 1:8aa5cdb4ab67 324
Jonathan Austin 1:8aa5cdb4ab67 325 return result;
Jonathan Austin 1:8aa5cdb4ab67 326 }
Jonathan Austin 1:8aa5cdb4ab67 327
Jonathan Austin 1:8aa5cdb4ab67 328 /**
Jonathan Austin 1:8aa5cdb4ab67 329 * Seed the random number generator (RNG).
Jonathan Austin 1:8aa5cdb4ab67 330 *
Jonathan Austin 1:8aa5cdb4ab67 331 * This function uses the NRF51822's in built cryptographic random number generator to seed a Galois LFSR.
Jonathan Austin 1:8aa5cdb4ab67 332 * We do this as the hardware RNG is relatively high power, and is locked out by the BLE stack internally,
Jonathan Austin 1:8aa5cdb4ab67 333 * with a less than optimal application interface. A Galois LFSR is sufficient for our
Jonathan Austin 1:8aa5cdb4ab67 334 * applications, and much more lightweight.
Jonathan Austin 1:8aa5cdb4ab67 335 */
Jonathan Austin 1:8aa5cdb4ab67 336 void microbit_seed_random()
Jonathan Austin 1:8aa5cdb4ab67 337 {
Jonathan Austin 1:8aa5cdb4ab67 338 random_value = 0;
Jonathan Austin 1:8aa5cdb4ab67 339
Jonathan Austin 1:8aa5cdb4ab67 340 if(ble_running())
Jonathan Austin 1:8aa5cdb4ab67 341 {
Jonathan Austin 1:8aa5cdb4ab67 342 // If Bluetooth is enabled, we need to go through the Nordic software to safely do this.
Jonathan Austin 1:8aa5cdb4ab67 343 uint32_t result = sd_rand_application_vector_get((uint8_t*)&random_value, sizeof(random_value));
Jonathan Austin 1:8aa5cdb4ab67 344
Jonathan Austin 1:8aa5cdb4ab67 345 // If we couldn't get the random bytes then at least make the seed non-zero.
Jonathan Austin 1:8aa5cdb4ab67 346 if (result != NRF_SUCCESS)
Jonathan Austin 1:8aa5cdb4ab67 347 random_value = 0xBBC5EED;
Jonathan Austin 1:8aa5cdb4ab67 348 }
Jonathan Austin 1:8aa5cdb4ab67 349 else
Jonathan Austin 1:8aa5cdb4ab67 350 {
Jonathan Austin 1:8aa5cdb4ab67 351 // Othwerwise we can access the hardware RNG directly.
Jonathan Austin 1:8aa5cdb4ab67 352
Jonathan Austin 1:8aa5cdb4ab67 353 // Start the Random number generator. No need to leave it running... I hope. :-)
Jonathan Austin 1:8aa5cdb4ab67 354 NRF_RNG->TASKS_START = 1;
Jonathan Austin 1:8aa5cdb4ab67 355
Jonathan Austin 1:8aa5cdb4ab67 356 for(int i = 0; i < 4; i++)
Jonathan Austin 1:8aa5cdb4ab67 357 {
Jonathan Austin 1:8aa5cdb4ab67 358 // Clear the VALRDY EVENT
Jonathan Austin 1:8aa5cdb4ab67 359 NRF_RNG->EVENTS_VALRDY = 0;
Jonathan Austin 1:8aa5cdb4ab67 360
Jonathan Austin 1:8aa5cdb4ab67 361 // Wait for a number ot be generated.
Jonathan Austin 1:8aa5cdb4ab67 362 while(NRF_RNG->EVENTS_VALRDY == 0);
Jonathan Austin 1:8aa5cdb4ab67 363
Jonathan Austin 1:8aa5cdb4ab67 364 random_value = (random_value << 8) | ((int) NRF_RNG->VALUE);
Jonathan Austin 1:8aa5cdb4ab67 365 }
Jonathan Austin 1:8aa5cdb4ab67 366
Jonathan Austin 1:8aa5cdb4ab67 367 // Disable the generator to save power.
Jonathan Austin 1:8aa5cdb4ab67 368 NRF_RNG->TASKS_STOP = 1;
Jonathan Austin 1:8aa5cdb4ab67 369 }
Jonathan Austin 1:8aa5cdb4ab67 370 }
Jonathan Austin 1:8aa5cdb4ab67 371
Jonathan Austin 1:8aa5cdb4ab67 372 /**
Jonathan Austin 1:8aa5cdb4ab67 373 * Seed the pseudo random number generator (RNG) using the given 32-bit value.
Jonathan Austin 1:8aa5cdb4ab67 374 * This function does not use the NRF51822's in built cryptographic random number generator.
Jonathan Austin 1:8aa5cdb4ab67 375 *
Jonathan Austin 1:8aa5cdb4ab67 376 * @param seed The value to use as a seed.
Jonathan Austin 1:8aa5cdb4ab67 377 */
Jonathan Austin 1:8aa5cdb4ab67 378 void microbit_seed_random(uint32_t seed)
Jonathan Austin 1:8aa5cdb4ab67 379 {
Jonathan Austin 1:8aa5cdb4ab67 380 random_value = seed;
LancasterUniversity 47:69f452b1a5c9 381 }