Solution for Bluetooth SIG hands-on training course
Dependencies: BLE_API mbed-dev-bin nRF51822-bluetooth-mdw
Fork of microbit-dal-bluetooth-mdw_starter by
Revision 22:23d7b9a4b082, committed 2016-07-13
- Comitter:
- LancasterUniversity
- Date:
- Wed Jul 13 12:17:54 2016 +0100
- Parent:
- 21:cab56b701601
- Child:
- 23:6055f6c19fa6
- Commit message:
- Synchronized with git rev 7cf98c22
Author: James Devine
microbit-dal: patch for fiber_wake_on_event
fiber_wake_on_event used to crash after forking a FOB fiber.
It would attempt to obtain a new fiber context, and would place it on the wait queue.
Then when that fiber was paged in, the context of that fiber would not have been
initialised, as the function presumed schedule would be called immediately after
fiber initialisation.
This patch catches that edge case.
Changed in this revision
--- a/inc/bluetooth/ExternalEvents.h Wed Jul 13 12:17:53 2016 +0100 +++ b/inc/bluetooth/ExternalEvents.h Wed Jul 13 12:17:54 2016 +0100 @@ -33,9 +33,6 @@ #define MICROBIT_ID_BLE 1000 #define MICROBIT_ID_BLE_UART 1001 -#define MICROBIT_BLE_CONNECTED 1 -#define MICROBIT_BLE_DISCONNECTED 2 - #include "MESEvents.h" #endif \ No newline at end of file
--- a/inc/drivers/MicroBitDisplay.h Wed Jul 13 12:17:53 2016 +0100 +++ b/inc/drivers/MicroBitDisplay.h Wed Jul 13 12:17:54 2016 +0100 @@ -44,7 +44,7 @@ // // Internal constants // - +#define MICROBIT_DISPLAY_DEFAULT_AUTOCLEAR 1 #define MICROBIT_DISPLAY_SPACING 1 #define MICROBIT_DISPLAY_GREYSCALE_BIT_DEPTH 8 #define MICROBIT_DISPLAY_ANIMATE_DEFAULT_POS -255 @@ -56,6 +56,7 @@ ANIMATION_MODE_PRINT_TEXT, ANIMATION_MODE_SCROLL_IMAGE, ANIMATION_MODE_ANIMATE_IMAGE, + ANIMATION_MODE_ANIMATE_IMAGE_WITH_CLEAR, ANIMATION_MODE_PRINT_CHARACTER }; @@ -463,6 +464,8 @@ * @param startingPosition the starting position on the display for the animation * to begin at. Defaults to MICROBIT_DISPLAY_ANIMATE_DEFAULT_POS. * + * @param autoClear defines whether or not the display is automatically cleared once the animation is complete. By default, the display is cleared. Set this parameter to zero to disable the autoClear operation. + * * @return MICROBIT_OK, MICROBIT_BUSY if the screen is in use, or MICROBIT_INVALID_PARAMETER. * * @code @@ -474,7 +477,7 @@ * display.animateAsync(i,100,5); * @endcode */ - int animateAsync(MicroBitImage image, int delay, int stride, int startingPosition = MICROBIT_DISPLAY_ANIMATE_DEFAULT_POS); + int animateAsync(MicroBitImage image, int delay, int stride, int startingPosition = MICROBIT_DISPLAY_ANIMATE_DEFAULT_POS, int autoClear = MICROBIT_DISPLAY_DEFAULT_AUTOCLEAR); /** * "Animates" the current image across the display with a given stride, finishing on the last frame of the animation. @@ -488,6 +491,8 @@ * @param startingPosition the starting position on the display for the animation * to begin at. Defaults to MICROBIT_DISPLAY_ANIMATE_DEFAULT_POS. * + * @param autoClear defines whether or not the display is automatically cleared once the animation is complete. By default, the display is cleared. Set this parameter to zero to disable the autoClear operation. + * * @return MICROBIT_OK, MICROBIT_CANCELLED or MICROBIT_INVALID_PARAMETER. * * @code @@ -499,7 +504,7 @@ * display.animate(i,100,5); * @endcode */ - int animate(MicroBitImage image, int delay, int stride, int startingPosition = MICROBIT_DISPLAY_ANIMATE_DEFAULT_POS); + int animate(MicroBitImage image, int delay, int stride, int startingPosition = MICROBIT_DISPLAY_ANIMATE_DEFAULT_POS, int autoClear = MICROBIT_DISPLAY_DEFAULT_AUTOCLEAR); /** * Configures the brightness of the display.
--- a/inc/drivers/MicroBitSerial.h Wed Jul 13 12:17:53 2016 +0100 +++ b/inc/drivers/MicroBitSerial.h Wed Jul 13 12:17:54 2016 +0100 @@ -105,9 +105,13 @@ * @param len the length of the string, and ultimately the maximum number of bytes * that will be copied dependent on the state of txBuff * + * @param mode determines whether to configure the current fiber context or not. If + * The mode is SYNC_SPINWAIT, the context will not be configured, otherwise + * no context will be configured. + * * @return the number of bytes copied into the buffer. */ - int setTxInterrupt(uint8_t *string, int len); + int setTxInterrupt(uint8_t *string, int len, MicroBitSerialMode mode); /** * Locks the mutex so that others can't use this serial instance for reception @@ -584,4 +588,4 @@ }; -#endif +#endif \ No newline at end of file
--- a/source/bluetooth/MicroBitBLEManager.cpp Wed Jul 13 12:17:53 2016 +0100 +++ b/source/bluetooth/MicroBitBLEManager.cpp Wed Jul 13 12:17:54 2016 +0100 @@ -109,8 +109,6 @@ */ static void bleDisconnectionCallback(const Gap::DisconnectionCallbackParams_t *reason) { - MicroBitEvent(MICROBIT_ID_BLE,MICROBIT_BLE_DISCONNECTED); - storeSystemAttributes(reason->handle); if (manager) @@ -118,14 +116,6 @@ } /** - * Callback when a BLE connection is established. - */ -static void bleConnectionCallback(const Gap::ConnectionCallbackParams_t *params) -{ - MicroBitEvent(MICROBIT_ID_BLE,MICROBIT_BLE_CONNECTED); -} - -/** * Callback when a BLE SYS_ATTR_MISSING. */ static void bleSysAttrMissingCallback(const GattSysAttrMissingCallbackParams *params) @@ -277,9 +267,6 @@ // automatically restart advertising after a device disconnects. ble->gap().onDisconnection(bleDisconnectionCallback); ble->gattServer().onSysAttrMissing(bleSysAttrMissingCallback); - - // generate an event when a Bluetooth connection is lost - ble->gap().onConnection(bleConnectionCallback); // Configure the stack to hold onto the CPU during critical timing events. // mbed-classic performs __disable_irq() calls in its timers that can cause
--- a/source/bluetooth/MicroBitMagnetometerService.cpp Wed Jul 13 12:17:53 2016 +0100 +++ b/source/bluetooth/MicroBitMagnetometerService.cpp Wed Jul 13 12:17:54 2016 +0100 @@ -74,7 +74,7 @@ magnetometerPeriodCharacteristicHandle = magnetometerPeriodCharacteristic.getValueHandle(); ble.gattServer().notify(magnetometerDataCharacteristicHandle,(uint8_t *)magnetometerDataCharacteristicBuffer, sizeof(magnetometerDataCharacteristicBuffer)); - ble.gattServer().notify(magnetometerBearingCharacteristicHandle,(uint8_t *)&magnetometerBearingCharacteristicBuffer, sizeof(magnetometerBearingCharacteristicBuffer)); + ble.gattServer().notify(magnetometerBearingCharacteristicHandle,(uint8_t *)&magnetometerBearingCharacteristicBuffer, sizeof(magnetometerDataCharacteristicBuffer)); ble.gattServer().write(magnetometerPeriodCharacteristicHandle, (const uint8_t *)&magnetometerPeriodCharacteristicBuffer, sizeof(magnetometerPeriodCharacteristicBuffer)); ble.onDataWritten(this, &MicroBitMagnetometerService::onDataWritten);
--- a/source/core/MicroBitFiber.cpp Wed Jul 13 12:17:53 2016 +0100 +++ b/source/core/MicroBitFiber.cpp Wed Jul 13 12:17:54 2016 +0100 @@ -417,7 +417,7 @@ if (messageBus == NULL || !fiber_scheduler_running()) return MICROBIT_NOT_SUPPORTED; - // Sleep is a blocking call, so if we'r ein a fork on block context, + // Sleep is a blocking call, so if we're in a fork on block context, // it's time to spawn a new fiber... if (currentFiber->flags & MICROBIT_FIBER_FLAG_FOB) { @@ -428,20 +428,25 @@ // If we're out of memory, there's nothing we can do. // keep running in the context of the current thread as a best effort. if (forkedFiber != NULL) - f = forkedFiber; + { + f = forkedFiber; + dequeue_fiber(f); + queue_fiber(f, &runQueue); + schedule(); + } } // Encode the event data in the context field. It's handy having a 32 bit core. :-) f->context = value << 16 | id; - // Remove ourselve from the run queue + // Remove ourselves from the run queue dequeue_fiber(f); // Add ourselves to the sleep queue. We maintain strict ordering here to reduce lookup times. queue_fiber(f, &waitQueue); // Register to receive this event, so we can wake up the fiber when it happens. - // Special case for teh notify channel, as we always stay registered for that. + // Special case for the notify channel, as we always stay registered for that. if (id != MICROBIT_ID_NOTIFY && id != MICROBIT_ID_NOTIFY_ONE) messageBus->listen(id, value, scheduler_event, MESSAGE_BUS_LISTENER_IMMEDIATE); @@ -961,4 +966,4 @@ idle(); schedule(); } -} +} \ No newline at end of file
--- a/source/drivers/MicroBitDisplay.cpp Wed Jul 13 12:17:53 2016 +0100 +++ b/source/drivers/MicroBitDisplay.cpp Wed Jul 13 12:17:54 2016 +0100 @@ -287,7 +287,7 @@ if (animationMode == ANIMATION_MODE_SCROLL_IMAGE) this->updateScrollImage(); - if (animationMode == ANIMATION_MODE_ANIMATE_IMAGE) + if (animationMode == ANIMATION_MODE_ANIMATE_IMAGE || animationMode == ANIMATION_MODE_ANIMATE_IMAGE_WITH_CLEAR) this->updateAnimateImage(); if(animationMode == ANIMATION_MODE_PRINT_CHARACTER) @@ -384,8 +384,11 @@ //wait until we have rendered the last position to give a continuous animation. if (scrollingImagePosition <= -scrollingImage.getWidth() + (MICROBIT_DISPLAY_WIDTH + scrollingImageStride) && scrollingImageRendered) { + if (animationMode == ANIMATION_MODE_ANIMATE_IMAGE_WITH_CLEAR) + this->clear(); + animationMode = ANIMATION_MODE_NONE; - this->clear(); + this->sendAnimationCompleteEvent(); return; } @@ -891,6 +894,8 @@ * @param startingPosition the starting position on the display for the animation * to begin at. Defaults to MICROBIT_DISPLAY_ANIMATE_DEFAULT_POS. * + * @param autoClear defines whether or not the display is automatically cleared once the animation is complete. By default, the display is cleared. Set this parameter to zero to disable the autoClear operation. + * * @return MICROBIT_OK, MICROBIT_BUSY if the screen is in use, or MICROBIT_INVALID_PARAMETER. * * @code @@ -902,7 +907,7 @@ * display.animateAsync(i,100,5); * @endcode */ -int MicroBitDisplay::animateAsync(MicroBitImage image, int delay, int stride, int startingPosition) +int MicroBitDisplay::animateAsync(MicroBitImage image, int delay, int stride, int startingPosition, int autoClear) { //sanitise the delay value if(delay <= 0) @@ -922,7 +927,7 @@ animationDelay = stride == 0 ? 0 : delay; animationTick = delay-1; - animationMode = ANIMATION_MODE_ANIMATE_IMAGE; + animationMode = autoClear ? ANIMATION_MODE_ANIMATE_IMAGE_WITH_CLEAR : ANIMATION_MODE_ANIMATE_IMAGE; } else { @@ -944,6 +949,8 @@ * @param startingPosition the starting position on the display for the animation * to begin at. Defaults to MICROBIT_DISPLAY_ANIMATE_DEFAULT_POS. * + * @param autoClear defines whether or not the display is automatically cleared once the animation is complete. By default, the display is cleared. Set this parameter to zero to disable the autoClear operation. + * * @return MICROBIT_OK, MICROBIT_CANCELLED or MICROBIT_INVALID_PARAMETER. * * @code @@ -955,7 +962,7 @@ * display.animate(i,100,5); * @endcode */ -int MicroBitDisplay::animate(MicroBitImage image, int delay, int stride, int startingPosition) +int MicroBitDisplay::animate(MicroBitImage image, int delay, int stride, int startingPosition, int autoClear) { //sanitise the delay value if(delay <= 0) @@ -969,7 +976,7 @@ if (animationMode == ANIMATION_MODE_NONE) { // Start the effect. - this->animateAsync(image, delay, stride, startingPosition); + this->animateAsync(image, delay, stride, startingPosition, autoClear); // Wait for completion. //TODO: Put this in when we merge tight-validation
--- a/source/drivers/MicroBitMessageBus.cpp Wed Jul 13 12:17:53 2016 +0100 +++ b/source/drivers/MicroBitMessageBus.cpp Wed Jul 13 12:17:54 2016 +0100 @@ -375,10 +375,10 @@ // Otherwise, we invoke it in a 'fork on block' context, that will automatically create a fiber // should the event handler attempt a blocking operation, but doesn't have the overhead // of creating a fiber needlessly. (cool huh?) - if (l->flags & MESSAGE_BUS_LISTENER_NONBLOCKING || !fiber_scheduler_running()) - async_callback(l); - else - invoke(async_callback, l); + if (l->flags & MESSAGE_BUS_LISTENER_NONBLOCKING || !fiber_scheduler_running()) + async_callback(l); + else + invoke(async_callback, l); } else {
--- a/source/drivers/MicroBitRadio.cpp Wed Jul 13 12:17:53 2016 +0100 +++ b/source/drivers/MicroBitRadio.cpp Wed Jul 13 12:17:54 2016 +0100 @@ -99,7 +99,7 @@ { this->id = id; this->status = 0; - this->group = 0; + this->group = MICROBIT_RADIO_DEFAULT_GROUP; this->queueDepth = 0; this->rssi = 0; this->rxQueue = NULL; @@ -279,7 +279,7 @@ NRF_RADIO->BASE0 = MICROBIT_RADIO_BASE_ADDRESS; // Join the default group. This will configure the remaining byte in the RADIO hardware module. - setGroup(MICROBIT_RADIO_DEFAULT_GROUP); + setGroup(this->group); // The RADIO hardware module supports the use of multiple addresses, but as we're running anonymously, we only need one. // Configure the RADIO module to use the default address (address 0) for both send and receive operations. @@ -356,6 +356,9 @@ // deregister ourselves from the callback event used to empty the receive queue. fiber_remove_idle_component(this); + // record that the radio is now disabled + status &= ~MICROBIT_RADIO_STATUS_INITIALISED; + return MICROBIT_OK; }
--- a/source/drivers/MicroBitSerial.cpp Wed Jul 13 12:17:53 2016 +0100 +++ b/source/drivers/MicroBitSerial.cpp Wed Jul 13 12:17:54 2016 +0100 @@ -164,9 +164,13 @@ * @param len the length of the string, and ultimately the maximum number of bytes * that will be copied dependent on the state of txBuff * + * @param mode determines whether to configure the current fiber context or not. If + * The mode is SYNC_SPINWAIT, the context will not be configured, otherwise + * no context will be configured. + * * @return the number of bytes copied into the buffer. */ -int MicroBitSerial::setTxInterrupt(uint8_t *string, int len) +int MicroBitSerial::setTxInterrupt(uint8_t *string, int len, MicroBitSerialMode mode) { int copiedBytes = 0; @@ -182,7 +186,8 @@ break; } - fiber_wake_on_event(MICROBIT_ID_NOTIFY, MICROBIT_SERIAL_EVT_TX_EMPTY); + if(mode != SYNC_SPINWAIT) + fiber_wake_on_event(MICROBIT_ID_NOTIFY, MICROBIT_SERIAL_EVT_TX_EMPTY); //set the TX interrupt attach(this, &MicroBitSerial::dataWritten, Serial::TxIrq); @@ -406,7 +411,7 @@ uint8_t toTransmit[2] = { c, '\0'}; - int bytesWritten = setTxInterrupt(toTransmit, 1); + int bytesWritten = setTxInterrupt(toTransmit, 1, mode); send(mode); @@ -484,7 +489,7 @@ return result; } - int bytesWritten = setTxInterrupt(buffer, bufferLen); + int bytesWritten = setTxInterrupt(buffer, bufferLen, mode); send(mode);