Setting up the environment
In this article we will see about setting up a development environment for Silicon Labs' BLE/Thread range of MCUs. Specifically for the EFR32MG21A020F768IM32 chip which appears in the inexpensive ZYZBP008 module. I built a breakout board for this module. KiCAD files available here.
Silicon labs' EFR32 series IOT low power wireless chips are low cost and have good availability. They're know for good power efficiency and being of western origin; have great documentation. Another attractive feature (unlike Nordic) is the traditional embedded development SDK.
Like ST they provide an Eclipse based IDE. But this is resource heavy to run on the host, and has been laggy under Linux (for me). As usual in this blog we avoid propriety IDEs and have a generic VIM/makefile workflow.
Producing makefiles by hand has another inadvertent advantage: you intricately learn the project dependencies. Which helps you understand the environment better.
First install dependencies:
sudo apt install git git-lfs gcc-arm-none-eabi make bear gcc-multilib libstdc++-arm-none-eabi-newlib openocd gdb-multiarch
On arch:
sudo pacman -Sy git git-lfs make bear arm-none-eabi-gcc arm-none-eabi-newlib arm-none-eabi-gdb openocd
Then clone the gecko SDK:
git clone https://github.com/SiliconLabs/gecko_sdk.git
Alternatively download the SDK zip file from their releases. This results in a smaller download.
Now CD into this directory. We now need to create the files for a makefile structured project. The SDK uses a SLC
based projects, but we will ignore this. I only succeeded to generate the blinky example using this. We will use makefile instead. This is the resulting file tree:
gecko-sdk
|
├── platform
│ ├── CMSIS
│ └── emlib
│ │
│ ...
│
└── myapps
└── blinky
├── Makefile
├── main.mk
├── emlib.mk
├── compile_commands.json
├── linker_script
│ └── efr32mg21.ld
└── src
└── main.c
We see the familiar CMSIS
directory. emlib
is Silicon labs' equivalent of peripheral HAL. The main make Makefile
is a template available here. I copied the linker script over from gecko-sdk/platform/Device/SiliconLabs/EFR32MG21/Source/GCC/efr32mg21.ld
. Note that this file differs between projects, especially if an RTOS is used.
main.mk
:
CC = arm-none-eabi-gcc
SIZE = arm-none-eabi-size
OBJCOPY = arm-none-eabi-objcopy
COMMON_FLAGS = \
-mcpu=cortex-m33 \
-mthumb \
-mfpu=fpv5-sp-d16 \
-mfloat-abi=hard \
-std=c99 \
-Wall \
-Wextra \
-Os \
-fdata-sections \
-ffunction-sections \
-fomit-frame-pointer \
-imacros sl_gcc_preinclude.h \
-mcmse \
-g
DEFINES = -DEFR32MG21A020F768IM32
INCLUDE += -I include -I ../../platform/CMSIS/Core/Include -I ./ \
-I../../platform/Device/SiliconLabs/EFR32MG21/Include \
-I../../platform/common/inc \
-I ../../platform/common/toolchain/inc \
-I ../../platform/emlib/inc
CFLAGS := $(COMMON_FLAGS) $(INCLUDE) $(DEFINES)
SUBMAKEFILES := emlib.mk
SOURCES := ../../platform/Device/SiliconLabs/EFR32MG21/Source/system_efr32mg21.c \
../../platform/Device/SiliconLabs/EFR32MG21/Source/startup_efr32mg21.c \
src/main.c
BUILD_DIR := build
TARGET_DIR := build_output
#nosys.specs are required for printf
LDFLAGS = \
-mcpu=cortex-m33 \
-mthumb \
-mfpu=fpv5-sp-d16 \
-mfloat-abi=hard \
-T"linker_script/efr32mg21.ld" \
--specs=nano.specs \
#--specs=nosys.specs \
-Xlinker -Map=$(TARGET_DIR)/main.map \
-Wl,--gc-sections
# Startup file
#LDLIBS := ../../platform/Device/SiliconLabs/EFR32MG21/Source/GCC/startup_efr32mg21.S
###########
GROUP_START =-Wl,--start-group
GROUP_END =-Wl,--end-group
PROJECT_LIBS = \
-lgcc \
-lc \
-lm \
-lnosys
LIBS += $(GROUP_START) $(PROJECT_LIBS) $(GROUP_END)
TGT_LDFLAGS += $(LIBS)
##########
TARGET := main.elf
size: $(TARGET_DIR)/main.elf
$(SIZE) $(TARGET_DIR)/main.elf
emlib.mk
SOURCES := ../../platform/emlib/src/em_cmu_fpga.c \
../../platform/emlib/src/em_dma.c \
../../platform/emlib/src/em_csen.c \
../../platform/emlib/src/em_msc.c \
../../platform/emlib/src/em_lcd.c \
../../platform/emlib/src/em_rtcc.c \
../../platform/emlib/src/em_can.c \
../../platform/emlib/src/em_wdog.c \
../../platform/emlib/src/em_system.c \
../../platform/emlib/src/em_prs.c \
../../platform/emlib/src/em_se.c \
../../platform/emlib/src/em_eusart.c \
../../platform/emlib/src/em_dbg.c \
../../platform/emlib/src/em_cmu.c \
../../platform/emlib/src/em_rmu.c \
../../platform/emlib/src/em_vcmp.c \
../../platform/emlib/src/em_gpio.c \
../../platform/emlib/src/em_aes.c \
../../platform/emlib/src/em_burtc.c \
../../platform/emlib/src/em_usart.c \
../../platform/emlib/src/em_qspi.c \
../../platform/emlib/src/em_lesense.c \
../../platform/emlib/src/em_adc.c \
../../platform/emlib/src/em_core.c \
../../platform/emlib/src/em_letimer.c \
../../platform/emlib/src/em_idac.c \
../../platform/emlib/src/em_ldma.c \
../../platform/emlib/src/em_vdac.c \
../../platform/emlib/src/em_rtc.c \
../../platform/emlib/src/em_dac.c \
../../platform/emlib/src/em_pcnt.c \
../../platform/emlib/src/em_pdm.c \
../../platform/emlib/src/em_cryotimer.c \
../../platform/emlib/src/em_iadc.c \
../../platform/emlib/src/em_acmp.c \
../../platform/emlib/src/em_opamp.c \
../../platform/emlib/src/em_timer.c \
../../platform/emlib/src/em_i2c.c \
../../platform/emlib/src/em_gpcrc.c \
../../platform/emlib/src/em_crypto.c \
../../platform/emlib/src/em_emu.c \
../../platform/emlib/src/em_ebi.c \
../../platform/emlib/src/em_leuart.c
main.c
:
#include "em_cmu.h"
#include "em_gpio.h"
int main (void)
{
CMU_ClockEnable(cmuClock_GPIO, true);
GPIO_PinModeSet(gpioPortA, 0/*pin 0*/, gpioModePushPull /*push-pull output*/, 1/*output level*/);
while(1) {
GPIO_PinOutToggle(gpioPortA, 0/*pin 0*/);
for (volatile uint32_t i = 0; i < 100000; i++) { } // busy delay
}
return 0;
}
You can run make -j
to build. To get intellisense in VIM/VSCODE we need to produce the compile_commands.json
file. To do this make sure to clean the build rm -rf build build_output
and run bear -- make -j
.