Merge branch 'master' into ble-profile

Conflicts:
	source/CMakeLists.txt
	source/MicroBit.cpp
	source/MicroBitFiber.cpp
	source/MicroBitMessageBus.cpp
	source/MicroBitSuperMain.cpp
This commit is contained in:
Joe Finney 2015-10-18 17:54:37 +01:00
commit 80f79f7faf
18 changed files with 204 additions and 95 deletions

View File

@ -136,7 +136,16 @@ ManagedType<T>::ManagedType(const ManagedType<T> &t)
template<typename T>
ManagedType<T>::~ManagedType()
{
if (--(*ref) == 0)
// Special case - we were created using a default constructor, and never assigned a value.
if (*ref == 0)
{
// Simply destroy our reference counter and we're done.
free(ref);
}
// Normal case - we have a valid piece of data.
// Decrement our reference counter and free all allocated memory if we're deleting the last reference.
else if (--(*ref) == 0)
{
delete object;
free(ref);
@ -152,7 +161,14 @@ ManagedType<T>& ManagedType<T>::operator = (const ManagedType<T>&t)
if (this == &t)
return *this;
if (--(*ref) == 0)
// Special case - we were created using a default constructor, and never assigned a value.
if (*ref == 0)
{
// Simply destroy our reference counter, as we're about to adopt another.
free(ref);
}
else if (--(*ref) == 0)
{
delete object;
free(ref);

View File

@ -254,6 +254,14 @@ class MicroBit
*/
unsigned long systemTime();
/**
* Determine the version of the micro:bit runtime currently in use.
*
* @return A textual description of the currentlt executing micro:bit runtime.
* TODO: handle overflow case.
*/
char *systemVersion();
/**
* Triggers a microbit panic where an infinite loop will occur swapping between the panicFace and statusCode if provided.
*

View File

@ -149,7 +149,7 @@
// This enables the standard BLE device information service.
// Set '1' to enable.
#ifndef MICROBIT_BLE_DEVICE_INFORMATION_SERVICE
#define MICROBIT_BLE_DEVICE_INFORMATION_SERVICE 0
#define MICROBIT_BLE_DEVICE_INFORMATION_SERVICE 1
#endif
@ -289,6 +289,16 @@
#define MICROBIT_HEAP_DBG 0
#endif
// Versioning options.
// We use semantic versioning (http://semver.org/) to identify differnet versions of the micro:bit runtime.
// Where possible we use yotta (an ARM mbed build tool) to help us track versions.
// if this isn't available, it can be defined manually as a configuration option.
//
#ifndef MICROBIT_DAL_VERSION
#define MICROBIT_DAL_VERSION "unknown"
#endif
//
// Helper macro used by the micro:bit runtime to determine if a boolean configuration option is set.
//

View File

@ -85,8 +85,6 @@ enum DisplayMode {
struct MatrixPoint {
uint8_t x;
uint8_t y;
MatrixPoint(uint8_t x, uint8_t y);
};
/**

View File

@ -24,6 +24,7 @@
#define MICROBIT_FIBER_FLAG_FOB 0x01
#define MICROBIT_FIBER_FLAG_PARENT 0x02
#define MICROBIT_FIBER_FLAG_CHILD 0x04
#define MICROBIT_FIBER_FLAG_DO_NOT_PAGE 0x08
/**
* Thread Context for an ARM Cortex M0 core.
@ -67,6 +68,7 @@ struct Fiber
Fiber *next, *prev; // Position of this Fiber on the run queues.
};
extern Fiber *currentFiber;
/**
* Initialises the Fiber scheduler.
@ -119,7 +121,6 @@ Fiber *create_fiber(void (*entry_fn)(void), void (*completion_fn)(void) = releas
Fiber *create_fiber(void (*entry_fn)(void *), void *param, void (*completion_fn)(void *) = release_fiber);
/**
* Calls the Fiber scheduler.
* The calling Fiber will likely be blocked, and control given to another waiting fiber.
@ -227,10 +228,15 @@ void queue_fiber(Fiber *f, Fiber **queue);
*/
void dequeue_fiber(Fiber *f);
/**
* Set of tasks to perform when idle.
* Service any background tasks that are required, and attmept power efficient sleep.
*/
void idle();
/**
* IDLE task.
* Only scheduled for execution when the runqueue is empty.
* Performs a procressor sleep operation, then returns to the scheduler - most likely after a timer interrupt.
* Only scheduled for execution when the runqueue is empty. Typically calls idle().
*/
void idle_task();

View File

@ -19,50 +19,50 @@
#if MICROBIT_DISPLAY_TYPE == MICROBUG_REFERENCE_DEVICE
const MatrixPoint MicroBitDisplay::matrixMap[MICROBIT_DISPLAY_COLUMN_COUNT][MICROBIT_DISPLAY_ROW_COUNT] =
{ {MatrixPoint(0,0),MatrixPoint(0,1),MatrixPoint(0,2), MatrixPoint(0,3), MatrixPoint(0,4)},
{MatrixPoint(1,0),MatrixPoint(1,1),MatrixPoint(1,2), MatrixPoint(1,3), MatrixPoint(1,4)},
{MatrixPoint(2,0),MatrixPoint(2,1),MatrixPoint(2,2), MatrixPoint(2,3), MatrixPoint(2,4)},
{MatrixPoint(3,0),MatrixPoint(3,1),MatrixPoint(3,2), MatrixPoint(3,3), MatrixPoint(3,4)},
{MatrixPoint(4,0),MatrixPoint(4,1),MatrixPoint(4,2), MatrixPoint(4,3), MatrixPoint(4,4)}
{ {{0,0},{0,1},{0,2},{0,3},{0,4}},
{{1,0},{1,1},{1,2},{1,3},{1,4}},
{{2,0},{2,1},{2,2},{2,3},{2,4}},
{{3,0},{3,1},{3,2},{3,3},{3,4}},
{{4,0},{4,1},{4,2},{4,3},{4,4}}
};
#endif
#if MICROBIT_DISPLAY_TYPE == MICROBIT_3X9
const MatrixPoint MicroBitDisplay::matrixMap[MICROBIT_DISPLAY_COLUMN_COUNT][MICROBIT_DISPLAY_ROW_COUNT] =
{
{MatrixPoint(0,4),MatrixPoint(0,3),MatrixPoint(1,1)},
{MatrixPoint(1,4),MatrixPoint(4,2),MatrixPoint(0,1)},
{MatrixPoint(2,4),MatrixPoint(3,2),MatrixPoint(4,0)},
{MatrixPoint(3,4),MatrixPoint(2,2),MatrixPoint(3,0)},
{MatrixPoint(4,4),MatrixPoint(1,2),MatrixPoint(2,0)},
{MatrixPoint(4,3),MatrixPoint(0,2),MatrixPoint(1,0)},
{MatrixPoint(3,3),MatrixPoint(4,1),MatrixPoint(0,0)},
{MatrixPoint(2,3),MatrixPoint(3,1),MatrixPoint(NO_CONN,NO_CONN)},
{MatrixPoint(1,3),MatrixPoint(2,1),MatrixPoint(NO_CONN,NO_CONN)}
{{0,4},{0,3},{1,1}},
{{1,4},{4,2},{0,1}},
{{2,4},{3,2},{4,0}},
{{3,4},{2,2},{3,0}},
{{4,4},{1,2},{2,0}},
{{4,3},{0,2},{1,0}},
{{3,3},{4,1},{0,0}},
{{2,3},{3,1},{NO_CONN,NO_CONN}},
{{1,3},{2,1},{NO_CONN,NO_CONN}}
};
#endif
#if MICROBIT_DISPLAY_TYPE == MICROBIT_SB1
const MatrixPoint MicroBitDisplay::matrixMap[MICROBIT_DISPLAY_COLUMN_COUNT][MICROBIT_DISPLAY_ROW_COUNT] =
{
{MatrixPoint(0,4), MatrixPoint(1,4), MatrixPoint(2,4), MatrixPoint(3,4), MatrixPoint(4,4), MatrixPoint(4,3), MatrixPoint(3,3), MatrixPoint(2,3), MatrixPoint(1,3)},
{MatrixPoint(0,3), MatrixPoint(4,2), MatrixPoint(3,2), MatrixPoint(2,2), MatrixPoint(1,2), MatrixPoint(0,2), MatrixPoint(4,1), MatrixPoint(3,1), MatrixPoint(2,1)},
{MatrixPoint(1,1), MatrixPoint(0,1), MatrixPoint(4,0), MatrixPoint(3,0), MatrixPoint(2,0), MatrixPoint(1,0), MatrixPoint(0,0), MatrixPoint(NO_CONN,NO_CONN), MatrixPoint(NO_CONN,NO_CONN)}
{{0,4},{1,4},{2,4},{3,4},{4,4},{4,3},{3,3},{2,3},{1,3}},
{{0,3},{4,2},{3,2},{2,2},{1,2},{0,2},{4,1},{3,1},{2,1}},
{{1,1},{0,1},{4,0},{3,0},{2,0},{1,0},{0,0},{NO_CONN,NO_CONN},{NO_CONN,NO_CONN}}
};
#endif
#if MICROBIT_DISPLAY_TYPE == MICROBIT_SB2
const MatrixPoint MicroBitDisplay::matrixMap[MICROBIT_DISPLAY_COLUMN_COUNT][MICROBIT_DISPLAY_ROW_COUNT] =
{
{MatrixPoint(0,0),MatrixPoint(4,2),MatrixPoint(2,4)},
{MatrixPoint(2,0),MatrixPoint(0,2),MatrixPoint(4,4)},
{MatrixPoint(4,0),MatrixPoint(2,2),MatrixPoint(0,4)},
{MatrixPoint(4,3),MatrixPoint(1,0),MatrixPoint(0,1)},
{MatrixPoint(3,3),MatrixPoint(3,0),MatrixPoint(1,1)},
{MatrixPoint(2,3),MatrixPoint(3,4),MatrixPoint(2,1)},
{MatrixPoint(1,3),MatrixPoint(1,4),MatrixPoint(3,1)},
{MatrixPoint(0,3),MatrixPoint(NO_CONN,NO_CONN),MatrixPoint(4,1)},
{MatrixPoint(1,2),MatrixPoint(NO_CONN,NO_CONN),MatrixPoint(3,2)}
{{0,0},{4,2},{2,4}},
{{2,0},{0,2},{4,4}},
{{4,0},{2,2},{0,4}},
{{4,3},{1,0},{0,1}},
{{3,3},{3,0},{1,1}},
{{2,3},{3,4},{2,1}},
{{1,3},{1,4},{3,1}},
{{0,3},{NO_CONN,NO_CONN},{4,1}},
{{1,2},{NO_CONN,NO_CONN},{3,2}}
};
#endif

View File

@ -179,7 +179,7 @@ class MicroBitMessageBus : public MicroBitComponent
* uBit.MessageBus.ignore(MICROBIT_ID_BUTTON_B, MICROBIT_BUTTON_EVT_CLICK, onButtonBClick);
* @endcode
*/
void ignore(int id, int value, void (*handler)(MicroBitEvent, void*), void* arg);
void ignore(int id, int value, void (*handler)(MicroBitEvent, void*));
/**
* Unregister a listener function.

View File

@ -11,7 +11,7 @@ void panic(int statusCode);
* Resets the micro:bit.
* @param statusCode the appropriate status code. Status codes must be in the range 0-255.
*/
void reset(int statusCode);
void microbit_reset();
#endif

View File

@ -178,6 +178,11 @@ class MicroBitPin : public MicroBitComponent
* @param period The new period for the analog output in milliseconds.
*/
void setAnalogPeriod(int period);
/**
* Same thing as setAnalogPeriodUs, but with microseconds.
*/
void setAnalogPeriodUs(int period);
};
#endif

View File

@ -1,17 +1,24 @@
{
"name": "microbit-dal",
"version": "0.0.1",
"version": "1.2.2",
"license": "Apache2",
"description": "The runtime library for the BBC micro:bit, developed by Lancaster University",
"keywords": ["mbed-classic", "microbit", "runtime", "library", "lancaster", "University"],
"author": "James Devine <j.devine@lancaster.ac.uk (mailto:j.devine@lancaster.ac.uk) >",
"keywords": [
"mbed-classic",
"microbit",
"runtime",
"library",
"lancaster",
"University"
],
"author": "Joe Finney <j.finney@lancaster.ac.uk (mailto:j.finney@lancaster.ac.uk) >",
"homepage": "https://developer.mbed.org/teams/Lancaster-University/code/microbit/",
"dependencies":{
"dependencies": {
"mbed-classic": "~0.0.4",
"ble": "lancaster-university/BLE_API#master",
"ble-nrf51822": "lancaster-university/nrf51822#master"
},
"extraIncludes":[
"extraIncludes": [
"inc"
]
}

View File

@ -39,6 +39,19 @@ set(YOTTA_AUTO_MICROBIT-DAL_CPP_FILES
"ble-services/MicroBitTemperatureService.cpp"
)
execute_process(WORKING_DIRECTORY "../../yotta_modules/${PROJECT_NAME}" COMMAND "git" "log" "--pretty=format:%h" "-n" "1" OUTPUT_VARIABLE git_hash)
execute_process(WORKING_DIRECTORY "../../yotta_modules/${PROJECT_NAME}" COMMAND "git" "rev-parse" "--abbrev-ref" "HEAD" OUTPUT_VARIABLE git_branch OUTPUT_STRIP_TRAILING_WHITESPACE)
if (${git_branch} STREQUAL "master")
set(MICROBIT_DAL_VERSION_STRING "${YOTTA_MICROBIT_DAL_VERSION_STRING}")
else()
set(MICROBIT_DAL_VERSION_STRING "${YOTTA_MICROBIT_DAL_VERSION_STRING}-${git_branch}-g${git_hash}")
endif()
set(MICROBIT_DAL_VERSION_FLAGS "-DMICROBIT_DAL_VERSION=\\\"${MICROBIT_DAL_VERSION_STRING}\\\"")
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${MICROBIT_DAL_VERSION_FLAGS}")
if (YOTTA_CFG_MICROBIT_CONFIGFILE)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${YOTTA_FORCE_INCLUDE_FLAG} \"${YOTTA_CFG_MICROBIT_CONFIGFILE}\"")
endif ()

View File

@ -6,7 +6,7 @@ char MICROBIT_BLE_DEVICE_NAME[] = "BBC micro:bit [xxxxx]";
const char* MICROBIT_BLE_MANUFACTURER = "The Cast of W1A";
const char* MICROBIT_BLE_MODEL = "BBC micro:bit";
const char* MICROBIT_BLE_HARDWARE_VERSION = "1.0";
const char* MICROBIT_BLE_FIRMWARE_VERSION = "1.1";
const char* MICROBIT_BLE_FIRMWARE_VERSION = MICROBIT_DAL_VERSION;
const char* MICROBIT_BLE_SOFTWARE_VERSION = NULL;
#endif
@ -18,6 +18,16 @@ void panic(int statusCode)
uBit.panic(statusCode);
}
/**
* Perform a hard reset of the micro:bit.
*/
void
microbit_reset()
{
NVIC_SystemReset();
}
/**
* Callback when a BLE GATT disconnect occurs.
*/
@ -232,7 +242,7 @@ ManagedString MicroBit::getSerial()
*/
void MicroBit::reset()
{
reset();
microbit_reset();
}
/**
@ -423,7 +433,7 @@ void MicroBit::removeIdleComponent(MicroBitComponent *component)
{
int i = 0;
while(idleThreadComponents[i] != component && i < MICROBIT_IDLE_COMPONENTS)
while(idleThreadComponents[i] != component && i < MICROBIT_IDLE_COMPONENTS)
i++;
if(i == MICROBIT_IDLE_COMPONENTS)
@ -443,6 +453,18 @@ unsigned long MicroBit::systemTime()
return ticks;
}
/**
* Determine the version of the micro:bit runtime currently in use.
*
* @return A textual description of the currentlt executing micro:bit runtime.
* TODO: handle overflow case.
*/
char *MicroBit::systemVersion()
{
return MICROBIT_DAL_VERSION;
}
/**
* Triggers a microbit panic where an infinite loop will occur swapping between the panicFace and statusCode if provided.
*

View File

@ -37,20 +37,20 @@ void string_reverse(char *s)
void itoa(int n, char *s)
{
int i = 0;
int sign = (n >= 0);
int positive = (n >= 0);
// Record the sign of the number,
// Ensure our working value is positive.
if (!sign)
if (positive)
n = -n;
// Calculate each character, starting with the LSB.
do {
s[i++] = n % 10 + '0';
} while ((n /= 10) > 0);
s[i++] = abs(n % 10) + '0';
} while (abs(n /= 10) > 0);
// Add a negative sign as needed
if (!sign)
if (!positive)
s[i++] = '-';
// Terminate the string.

View File

@ -10,17 +10,6 @@
const float timings[MICROBIT_DISPLAY_GREYSCALE_BIT_DEPTH] = {0.000010, 0.000047, 0.000094, 0.000187, 0.000375, 0.000750, 0.001500, 0.003000};
/**
* Constructor.
* Create a Point representation of an LED on a matrix
* Used to handle non-linear matrix layouts.
*/
MatrixPoint::MatrixPoint(uint8_t x, uint8_t y)
{
this->x = x;
this->y = y;
}
/**
* Constructor.
* Create a representation of a display of a given size.

View File

@ -15,7 +15,7 @@
*/
Fiber *currentFiber = NULL; // The context in which the current fiber is executing.
Fiber *forkedFiber = NULL; // The context in which a newly created child fiber is executing.
Fiber *idle = NULL; // IDLE task - performs a power efficient sleep, and system maintenance tasks.
Fiber *idleFiber = NULL; // IDLE task - performs a power efficient sleep, and system maintenance tasks.
/*
* Scheduler state.
@ -159,10 +159,10 @@ void scheduler_init()
// Create the IDLE fiber.
// Configure the fiber to directly enter the idle task.
idle = getFiberContext();
idle->tcb.SP = CORTEX_M0_STACK_BASE - 0x04;
idle->tcb.LR = (uint32_t) &idle_task;
idleFiber = getFiberContext();
idleFiber->tcb.SP = CORTEX_M0_STACK_BASE - 0x04;
idleFiber->tcb.LR = (uint32_t) &idle_task;
// Flag that we now have a scheduler running
uBit.flags |= MICROBIT_FLAG_SCHEDULER_RUNNING;
}
@ -618,7 +618,7 @@ void schedule()
// We're in a normal scheduling context, so perform a round robin algorithm across runnable fibers.
// OK - if we've nothing to do, then run the IDLE task (power saving sleep)
if (runQueue == NULL || fiber_flags & MICROBIT_FLAG_DATA_READY)
currentFiber = idle;
currentFiber = idleFiber;
else if (currentFiber->queue == &runQueue)
// If the current fiber is on the run queue, round robin.
@ -628,18 +628,38 @@ void schedule()
// Otherwise, just pick the head of the run queue.
currentFiber = runQueue;
if (currentFiber == idleFiber && oldFiber->flags & MICROBIT_FIBER_FLAG_DO_NOT_PAGE)
{
// Run the idle task right here using the old fiber's stack.
// Keep idling while the runqueue is empty, or there is data to process.
// Run in the context of the original fiber, to preserve state of flags...
// as we are running on top of this fiber's stack.
currentFiber = oldFiber;
do
{
idle();
}
while (runQueue == NULL || fiber_flags & MICROBIT_FLAG_DATA_READY);
// Switch to a non-idle fiber.
// If this fiber is the same as the old one then there'll be no switching at all.
currentFiber = runQueue;
}
// Swap to the context of the chosen fiber, and we're done.
// Don't bother with the overhead of switching if there's only one fiber on the runqueue!
if (currentFiber != oldFiber)
{
// Special case for the idle task, as we don't maintain a stack context (just to save memory).
if (currentFiber == idle)
if (currentFiber == idleFiber)
{
idle->tcb.SP = CORTEX_M0_STACK_BASE - 0x04;
idle->tcb.LR = (uint32_t) &idle_task;
idleFiber->tcb.SP = CORTEX_M0_STACK_BASE - 0x04;
idleFiber->tcb.LR = (uint32_t) &idle_task;
}
if (oldFiber == idle)
if (oldFiber == idleFiber)
{
// Just swap in the new fiber, and discard changes to stack and register context.
swap_context(NULL, &currentFiber->tcb, NULL, currentFiber->stack_top);
@ -655,6 +675,25 @@ void schedule()
}
}
/**
* Set of tasks to perform when idle.
* Service any background tasks that are required, and attmept power efficient sleep.
*/
void idle()
{
// Service background tasks
uBit.systemTasks();
// If the above did create any useful work, enter power efficient sleep.
if(scheduler_runqueue_empty())
{
if (uBit.ble)
uBit.ble->waitForEvent();
else
__WFI();
}
}
/**
* IDLE task.
* Only scheduled for execution when the runqueue is empty.
@ -664,16 +703,7 @@ void idle_task()
{
while(1)
{
uBit.systemTasks();
if(scheduler_runqueue_empty())
{
if (uBit.ble)
uBit.ble->waitForEvent();
else
__WFI();
}
idle();
schedule();
}
}

View File

@ -350,12 +350,13 @@ void MicroBitMessageBus::ignore(int id, int value, void (*handler)(MicroBitEvent
* uBit.MessageBus.ignore(MICROBIT_ID_BUTTON_B, MICROBIT_BUTTON_EVT_CLICK, onButtonBClick);
* @endcode
*/
void MicroBitMessageBus::ignore(int id, int value, void (*handler)(MicroBitEvent, void*), void* arg)
void MicroBitMessageBus::ignore(int id, int value, void (*handler)(MicroBitEvent, void*))
{
if (handler == NULL)
return;
MicroBitListener listener(id, value, handler, arg);
// The remove function is not comparing the [arg] anyhow.
MicroBitListener listener(id, value, handler, NULL);
remove(&listener);
}

View File

@ -241,11 +241,18 @@ int MicroBitPin::isTouched()
* If this pin is not configured as an analog output, the operation
* has no effect.
*
* @param period The new period for the analog output in milliseconds.
*/
void MicroBitPin::setAnalogPeriod(int period)
* @param period The new period for the analog output in microseconds.
*/
void MicroBitPin::setAnalogPeriodUs(int period)
{
if (status & IO_STATUS_ANALOG_OUT)
((DynamicPwm *)pin)->setPeriodUs(period*1000);
((DynamicPwm *)pin)->setPeriodUs(period);
}
/**
* Same thing as setAnalogPeriodUs, but with milliseconds.
*/
void MicroBitPin::setAnalogPeriod(int period)
{
setAnalogPeriodUs(period*1000);
}

View File

@ -9,28 +9,25 @@ InterruptIn resetButton(MICROBIT_PIN_BUTTON_RESET);
extern char* MICROBIT_BLE_DEVICE_NAME;
void
reset()
{
NVIC_SystemReset();
}
int main()
{
// Bring up soft reset button.
resetButton.mode(PullUp);
resetButton.fall(reset);
resetButton.fall(microbit_reset);
#if CONFIG_ENABLED(MICROBIT_DBG)
pc.baud(115200);
// For diagnostics. Gives time to open the console window. :-)
for (int i=3; i>0; i--)
{
pc.printf("=== SUPERMAIN: Starting in %d ===\n", i);
wait(1.0);
}
pc.printf("micro:bit runtime DAL version %s\n", MICROBIT_DAL_VERSION);
#endif
// Bring up our nested heap allocator.