Attempting to publish a tree
Dependencies: BLE_API mbed-dev-bin nRF51822
Fork of microbit-dal by
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 }
Generated on Tue Jul 12 2022 19:58:09 by 1.7.2