-
Notifications
You must be signed in to change notification settings - Fork 52
/
MicroBitDevice.cpp
493 lines (389 loc) · 13.5 KB
/
MicroBitDevice.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
/*
The MIT License (MIT)
Copyright (c) 2016 Lancaster University, UK.
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.
*/
#include "MicroBitConfig.h"
#include "MicroBitDevice.h"
#include "nrf.h"
#include "hal/nrf_gpio.h"
#include "cmsis_compiler.h"
#ifdef SOFTDEVICE_PRESENT
#include "nrf_sdm.h"
#endif
using namespace codal;
static char friendly_name[MICROBIT_NAME_LENGTH+1];
// internal reference to any created instance of the class - used purely for convenience functions.
MicroBitDevice *microbit_device_instance = NULL;
/**
* Constructor.
*/
MicroBitDevice::MicroBitDevice()
{
microbit_device_instance = this;
}
namespace codal {
/**
* Perfom scheduler idle
*/
void MicroBitDevice::schedulerIdle()
{
}
/**
* Seed the pseudo random number generator using the hardware random number generator.
*
* @code
* uBit.seedRandom();
* @endcode
*/
void MicroBitDevice::seedRandom()
{
uint32_t r = 0xBBC5EED;
if(!ble_running())
{
// 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);
r = (r << 8) | ((int) NRF_RNG->VALUE);
}
// Disable the generator to save power.
NRF_RNG->TASKS_STOP = 1;
}
seedRandom(r);
}
/**
* Seed the pseudo random number generator using the given value.
*
* @param seed The 32-bit value to seed the generator with.
*
* @code
* uBit.seedRandom(0xBB5EED);
* @endcode
*/
int MicroBitDevice::seedRandom(uint32_t seed)
{
return CodalDevice::seedRandom(seed);
}
int microbit_random(int max)
{
return microbit_device_instance ? microbit_device_instance->random(max) : 0;
}
/**
* Derive 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 friendly name 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 MicroBit's serial number.
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;
}
/**
* Determines if a BLE stack is currently running.
*
* @return true is a bluetooth stack is operational, false otherwise.
*/
bool ble_running()
{
uint8_t t = 0;
#ifdef SOFTDEVICE_PRESENT
sd_softdevice_is_enabled(&t);
#endif
return t==1;
}
/**
* Perform a hard reset of the micro:bit.
*/
__NO_RETURN void microbit_reset()
{
NVIC_SystemReset();
// __NO_RETURN added to NVIC_SystemReset() in CMSIS V5.0.5
// Currently using V5.0.3, so this can be removed if updated in the future
// Looks like it gets compiled out anyway
for (;;);
}
/**
* For DAL compatibility, determine the version of DAL/CODAL 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;
}
/**
* Seed the random number generator (RNG).
*
* This function uses the NRF52833'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()
{
if(microbit_device_instance)
microbit_device_instance->seedRandom();
}
/**
* Seed the pseudo random number generator (RNG) using the given 32-bit value.
* This function does not use the NRF52833's in built cryptographic random number generator.
*
* @param seed The value to use as a seed.
*/
void microbit_seed_random(uint32_t seed)
{
if(microbit_device_instance)
microbit_device_instance->seedRandom(seed);
}
// LED matrix map for use by microbit_panic
typedef uint8_t microbit_LEDMapPinNumber;
typedef int (*microbit_LEDMapFnRCtoXY)( int row, int col);
typedef struct microbit_LEDMapStr
{
int width; // The physical width of the LED matrix, in pixels.
int height; // The physical height of the LED matrix, in pixels.
int rows; // The number of drive pins connected to LEDs.
int columns; // The number of sink pins connected to the LEDs.
const microbit_LEDMapPinNumber *rowPins; // Array of pin numbers to drive.
const microbit_LEDMapPinNumber *columnPins; // Array of pin numbers to sink.
microbit_LEDMapFnRCtoXY mapRCtoX; // Map logical LED positions to physical positions
microbit_LEDMapFnRCtoXY mapRCtoY;
} microbit_LEDMapStr;
// LED matrix map values for micro:bit V2
// Width and height of LEDs
#ifndef microbit_LEDMap_WIDTH
#define microbit_LEDMap_WIDTH 5
#endif
#ifndef microbit_LEDMap_HEIGHT
#define microbit_LEDMap_HEIGHT 5
#endif
// Width and height of LED pins
#ifndef microbit_LEDMap_COLS
#define microbit_LEDMap_COLS 5
#endif
#ifndef microbit_LEDMap_ROWS
#define microbit_LEDMap_ROWS 5
#endif
// LED rows pinmap
#ifndef microbit_LEDMap_PINROW0
#define microbit_LEDMap_PINROW0 21
#endif
#ifndef microbit_LEDMap_PINROW1
#define microbit_LEDMap_PINROW1 22
#endif
#ifndef microbit_LEDMap_PINROW2
#define microbit_LEDMap_PINROW2 15
#endif
#ifndef microbit_LEDMap_PINROW3
#define microbit_LEDMap_PINROW3 24
#endif
#ifndef microbit_LEDMap_PINROW4
#define microbit_LEDMap_PINROW4 19
#endif
// LED columns pinmap
#ifndef microbit_LEDMap_PINCOL0
#define microbit_LEDMap_PINCOL0 28
#endif
#ifndef microbit_LEDMap_PINCOL1
#define microbit_LEDMap_PINCOL1 11
#endif
#ifndef microbit_LEDMap_PINCOL2
#define microbit_LEDMap_PINCOL2 31
#endif
#ifndef microbit_LEDMap_PINCOL3
#define microbit_LEDMap_PINCOL3 37
#endif
#ifndef microbit_LEDMap_PINCOL4
#define microbit_LEDMap_PINCOL4 30
#endif
static const microbit_LEDMapPinNumber microbit_LEDMap_rowPins[ microbit_LEDMap_ROWS] =
{
microbit_LEDMap_PINROW0, microbit_LEDMap_PINROW1, microbit_LEDMap_PINROW2, microbit_LEDMap_PINROW3, microbit_LEDMap_PINROW4
};
static const microbit_LEDMapPinNumber microbit_LEDMap_columnPins[ microbit_LEDMap_COLS] =
{
microbit_LEDMap_PINCOL0, microbit_LEDMap_PINCOL1, microbit_LEDMap_PINCOL2, microbit_LEDMap_PINCOL3, microbit_LEDMap_PINCOL4
};
static int microbit_LEDMap_RCtoX( int row, int col) { return col; }
static int microbit_LEDMap_RCtoY( int row, int col) { return row; }
static const microbit_LEDMapStr microbit_LEDMap =
{
microbit_LEDMap_WIDTH, microbit_LEDMap_HEIGHT,
microbit_LEDMap_COLS, microbit_LEDMap_ROWS,
microbit_LEDMap_rowPins,
microbit_LEDMap_columnPins,
microbit_LEDMap_RCtoX,
microbit_LEDMap_RCtoY
};
// Low level LEDs pin functions
static inline void microbit_LEDMap_rowOn( int row) { nrf_gpio_pin_set( microbit_LEDMap.rowPins[ row]); }
static inline void microbit_LEDMap_rowOff( int row) { nrf_gpio_pin_clear( microbit_LEDMap.rowPins[ row]); }
static inline void microbit_LEDMap_columnOn( int col, bool on) { nrf_gpio_pin_write( microbit_LEDMap.columnPins[ col], on ? 0 : 1); }
static inline void microbit_LEDMap_columnOff( int col) { nrf_gpio_pin_set( microbit_LEDMap.columnPins[ col]); }
static inline void microbit_LEDMap_pinOutput( uint8_t pin) { nrf_gpio_cfg_output( pin); }
static inline void microbit_LEDMap_configure()
{
for ( int i = 0; i < microbit_LEDMap.columns; i++) microbit_LEDMap_pinOutput( microbit_LEDMap.columnPins[i]);
for ( int i = 0; i < microbit_LEDMap.rows; i++) microbit_LEDMap_pinOutput( microbit_LEDMap.rowPins[i]);
}
// Constants for microbit_panic
// length of message: face, code digit, code digit, code digit
#ifndef microbit_panic_MSGLEN
#define microbit_panic_MSGLEN 4
#endif
// position of first code digit
#ifndef microbit_panic_MSG1STDIGIT
#define microbit_panic_MSG1STDIGIT 1
#endif
// divisor for first digit
#ifndef microbit_panic_DIVMAX
#define microbit_panic_DIVMAX 100
#endif
// divisor base
#ifndef microbit_panic_DIVBASE
#define microbit_panic_DIVBASE 10
#endif
// Number of burn passes for each character
#ifndef microbit_panic_SCANS
#define microbit_panic_SCANS 48 //1000 ~ 25s
#endif
// Passes left clear for repeated character
#ifndef microbit_panic_CLEARSCANS
#define microbit_panic_CLEARSCANS 8 //1000 ~ 25s
#endif
// Delay cycles for each row
#ifndef microbit_panic_ROWDELAY
#define microbit_panic_ROWDELAY (40000)
#endif
static const uint8_t panic_face[ MICROBIT_FONT_WIDTH] = { 0x1B, 0x1B, 0x0, 0x0E, 0x11};
static int panic_timeout = 0;
void microbit_panic_timeout(int iterations)
{
panic_timeout = iterations;
}
__NO_RETURN void microbit_panic( int statusCode)
{
const microbit_LEDMapStr &mm = microbit_LEDMap;
uint8_t chr;
target_disable_irq();
for (int i=0; i<8; i++)
NRF_GPIOTE->CONFIG[i] = 0;
microbit_LEDMap_configure();
for ( int row = 0; row < mm.rows; row++)
microbit_LEDMap_rowOff( row);
for ( int repeat = 0; panic_timeout == 0 || repeat < panic_timeout; repeat++)
{
for ( int msgIdx = 0; msgIdx < microbit_panic_MSGLEN; msgIdx++)
{
// find the the current character and its font bytes
chr = 0;
if ( msgIdx >= microbit_panic_MSG1STDIGIT)
{
// calculate divisor for this digit: 100s, 10s or units
int div = microbit_panic_DIVMAX;
for ( int digit = msgIdx - microbit_panic_MSG1STDIGIT; digit > 0; digit--)
div /= microbit_panic_DIVBASE;
chr = 0x30 + ( statusCode / div) % microbit_panic_DIVBASE;
}
const uint8_t *fontBytes = chr ? ( BitmapFont::defaultFont + BITMAP_FONT_WIDTH * ( chr - BITMAP_FONT_ASCII_START)) : panic_face;
for ( int scan = 0; scan < microbit_panic_SCANS; scan++)
{
int rowOnCycles = microbit_panic_ROWDELAY;
if ( scan >= microbit_panic_SCANS - microbit_panic_CLEARSCANS) // blank display between characters
rowOnCycles = 0;
else if ( repeat >= 5) // dim display after 5 repetitions
rowOnCycles = rowOnCycles / 10;
// turn each row on and off
for ( int row = 0; row < mm.rows; row++)
{
for ( int col = 0; col < mm.columns; col++)
{
microbit_LEDMap_columnOn( col,
fontBytes[ mm.mapRCtoY( row, col)] & ( 1 << ( mm.width - 1 - mm.mapRCtoX( row, col))));
}
if ( rowOnCycles)
microbit_LEDMap_rowOn( row);
for ( int i = 0; i < microbit_panic_ROWDELAY; i++)
{
nrf_gpio_pin_out_read( microbit_LEDMap.columnPins[0]);
if ( i == rowOnCycles - 1)
microbit_LEDMap_rowOff( row);
}
}
}
}
}
microbit_reset();
}
} // namespace codal
// Override for CODAL target HAL
__attribute__((weak)) void target_panic( int statusCode)
{
codal::microbit_panic( statusCode);
}
extern "C"
{
__attribute__((weak)) int __wrap_atexit(void (*function)(void))
{
return -1;
}
} // extern "C"
__attribute__((weak)) void target_scheduler_idle()
{
if ( microbit_device_instance)
microbit_device_instance->schedulerIdle();
else
target_wait_for_event();
}