Metawear “Hacked”!
Do you own one of the little MetaWear developer boards? And you want to use your own custom firware? Awesome! You are on the right track! Metwear is a neat, tiny ARM+Bluetooth LE Platform for developing Wearable products. MetaWear comes with different hardware implementations serving different application needs. MetaWear uses the award winning BLE SoC, the nRF51x. When shipped, the MetaWear comes with its own pre-installed firmware suited for rapid-android/IOS typeof-prototyping. Pretty cool!
Nevertheless, I know that out there, there are more demanding users that want to implement their own custom firmware! This guide explains how to achieve this using any of the Nordic’s Softdevices, the GNU toolchain under Linux. However, the idea on how to achieve this, is the same with any type of toolchain.
I will be using the nRF51-DK as Debugger/Programmer and the MetaWear R Series as the platform. Click below to see how to use the nRF51-DK as Debugger/Programmer.
SoftDevice Configuration Overview
To adapt our application to the MetaWear but also to any other custom firmware we need to perform two steps:
nRF51x IC revision overview
This section shows the packet and memory variants for each nRF51x IC revision. These tables are important because as we will see later, the MetaWear RG and the nRF51 DK use different IC revisions and as a result there is a difference in the RAM memory.
nRF51422 IC revision | Packet variant | Build code | Package | Flash [kB] | RAM [kB] |
---|---|---|---|---|---|
1 | QF AA | C0 | QFN48 | 256 | 16 |
CE AA | A0A | WLCSP | |||
2 | QF AA | Ex0 | QFN48 | 256 | 16 |
QF AB | A00 | 128 | |||
CE AA | Bx0 | WLCSP | 256 | ||
3 | QF AA | Fx0 | QFN48 | 256 | 16 |
QF AB | Bx0 | 128 | |||
QF AC | Ax0 | 256 | 32 | ||
CD AB | Ax0 | WLCSP | 128 | 16 | |
CE AA | Cx0 | 256 | |||
CF AC | Ax0 | 32 |
nRF51822 IC revision | Packet variant | Build code | Package | Flash [kB] | RAM [kB] |
---|---|---|---|---|---|
1 | QF AA | C0 | QFN48 | 256 | 16 |
QF AB | A0 | 128 | |||
CE AA | B0 | WLCSP | 256 | ||
2 | QF AA | Gx0 | QFN48 | 256 | 16 |
QF AB | Bx0 | 128 | |||
CE AA | CA0 | WLCSP | 256 | ||
DA0 | |||||
Dx0 | |||||
3 | QF AA | Hx0 | QFN48 | 256 | 16 |
QF AB | Cx0 | 128 | |||
QF AC | Ax0 | 256 | 32 | ||
CD AB | Ax0 | WLCSP | 128 | 16 | |
CE AA | Ex0 | 256 | |||
CF AC | Ax0 | 32 | |||
CT AC | Ax0 | ||||
CT AA | B00 | 16 | |||
WF AC | A00 | Wafer | 32 |
MetaWear RG: EYSFCNZXX
The MetaWear RG uses the the Taiyo Yuden EYSFCNZXX SoC with the following specs:
Step 1:
Memory resource map and usage
The memory map for program memory and RAM at run time with the SoftDevice enabled is illustrated in below. We will focus on the RAM memory here (right image) as it is important to adapt the linker-configuration to reflect the changed SoftDevice RAM requirement.
Defining SizeOfRam
As we have see before we might have either 16 kB or 32kB of RAM. Which means:
- SizeOfRam = 0x4000 for 16 kB RAM
- SizeOfRam = 0x8000 for 32 kB RAM
This translates to the following:
RAM | Start address | End Address |
16 kB RAM | 0x2000 0000 | 0x2000 4000 |
32 kB RAM | 0x2000 0000 | 0x2000 8000 |
Defining SizeOfProgram
As we have see before we might have either 128 kB or 256kB of program memory (flash). Which means:
- SizeOfProgram = 0x20000 for 128 kB Flash
- SizeOfProgram = 0x40000 for 256 kB Flash
Defining APP_RAM_BASE & APP_CODE_BASE
The APP_RAM_BASE is different for each SoftDevice being used. This page provides the documentation for each SoftDevice where this information can be retrieved. The table below gives this information in one table.
SoftDevice | SDS Version | APP_RAM_BASE = Start + ATTR_TAB_SIZE |
Stack Size |
Heap Size | APP_CODE_BASE | ||
Start | ATTR_TAB_SIZE (default) |
Base | max (bytes) | max (bytes) | |||
S110 | v2.0 | 0x20001900 | 0x700 | 0x20002000 | 0x600 | 0 | 0x00018000 |
S120 | v2.1 | 0x20002800 | – | 0x20002800 | 0x600 | 0 | 0x0001D000 |
S130 | v1.0 | 0x20002200 | 0x600 | 0x20002800 | 0x600 | 0 | 0x0001C000 |
v2.0 | 0x200013C8 | – | 0x200013C8 | 0x600 | 0 | 0x0001B000 | |
S210 | v3.0 | 0x20000900 | – | 0x20000900 | 0x400 | 0 | 0x0000D000 |
S310 | v3.0 | 0x200022002 | – | 0x200022002 | 0x800 | 0 | 0x0001D000 |
Defining Stack and Heap size
Stack and heap size is depended on the application and thus it is something you need to figure out yourselves. One important remark:
Putting everything together in the linker…
Now we have every information needed in order to configure correctly our linker! Inside our Makefile we call the linker script -Tble_app_gcc_nrf51.ld . This linker script has the configuration for our program memory and RAM. This is the file we need to adapt to our setup. The file looks like the following:
/* Linker script to configure memory regions. */ SEARCH_DIR(.) GROUP(-lgcc -lc -lnosys) MEMORY { FLASH (rx) : ORIGIN = 0x1c000, LENGTH = 0x24000 /* 256kB Flash / S130 v1.0 SD. 0x40000-0x1c000=0x24000 */ RAM (rwx) : ORIGIN = 0x20002800, LENGTH = 0x1800 /* 16kB RAM / S130 v1.0 SD. 0x4000-0x2800=0x1800 */ } INCLUDE "gcc_nrf51_common.ld"
The above is an example where the nordic nRF51x chip has 16kB of RAM and uses the S130 SoftDevice. 🙂
Configure Stack and Heap size
To configure stack and heap size edit the gcc_startup_nrf51.s file. Look at the start of the file:
#ifdef __STACK_SIZE .equ Stack_Size, __STACK_SIZE #else .equ Stack_Size, 2048 #endif .globl __StackTop .globl __StackLimit __StackLimit: .space Stack_Size .size __StackLimit, . - __StackLimit __StackTop: .size __StackTop, . - __StackTop .section .heap .align 3 #ifdef __HEAP_SIZE .equ Heap_Size, __HEAP_SIZE #else .equ Heap_Size, 512 #endif .globl __HeapBase .globl __HeapLimit
Of course you can also define __STACK_SIZE and __HEAP_SIZE .
The MetaWear RG has 16 kB RAM!
Step 2: Configuring the Clock
If you are using and external crystal as your clock source you need to perform the following:
- Change the definition at the top of the system.nrf51.c :
#define __SYSTEM_CLOCK (16000000UL) /* 16MHz Crystal */
or
#define __SYSTEM_CLOCK (32000000UL) /* 32MHz Crystal */ - Enable it explicitly with the following code:
int main(void) { // Set the external high frequency clock source to 32 MHz NRF_CLOCK->XTALFREQ = 0xFFFFFF00; // 32 MHz (CLOCK_XTALFREQ_XTALFREQ_32MHz) OR 0xFFFFFFFF for 16 MHz (CLOCK_XTALFREQ_XTALFREQ_16MHz) // Start the external high frequency crystal NRF_CLOCK->EVENTS_HFCLKSTARTED = 0; NRF_CLOCK->TASKS_HFCLKSTART = 1; // Wait for the external oscillator to start up while (NRF_CLOCK->EVENTS_HFCLKSTARTED == 0) {} while (true) { } }
The MetaWear RG uses a 32 MHz external crystal!