If you just landed here please read the short introduction that I provide in Part 1 of this series for some context. In this post I’ll walk through the set of software tools that I will use in the rest of the series. The examples are focused on the SparkFun Pro nRF52840 Mini – Bluetooth Development Board, but should be relevant to almost any board based on nRF52840 with small modifications.
Build Machine
I’m using Ubuntu 20.04.1 LTS running inside a Virtual Box VM. Although I haven’t tried this on another system, it should mostly work on any Linux system. The only thing that I know may be different between systems is the file used for setting environment variables. I’m using ~/.bashrc, but other systems my use a different file. It may also be possible to get this all working under WSL, but again, I haven’t tested that yet. Because I’m using a virtual machine, I do need to pass the USB connections through to the VM, but so far this hasn’t created a problem.
Compiler
I’m using GNU Arm Embedded Toolchain for compiling, linking, and debugging. You can download the latest version for your system here, or you can use the same version that I did by running the following command in the terminal.
wget https://developer.arm.com/-/media/Files/downloads/gnu-rm/9-2020q2/gcc-arm-none-eabi-9-2020-q2-update-x86_64-linux.tar.bz2
Once you have the package, extract it to the folder of your choice (I chose to put it in /opt for this example.)
sudo tar -xjf gcc-arm-none-eabi-9-2020-q2-update-x86_64-linux.tar.bz2 -C /opt
Now create an environment variable that points to your arm compiler. This variable will be used by the Makefiles in these projects to find the arm compiler. I also use it to configure VS Code.
echo 'export GCC_ARM = "/opt/gcc-arm-none-eabi-9-2020-q2-update-x86_64-linux"' >> ~/.bashrc
SDK
Next download the nRF5 SDK from Nodic Semiconductor. Go to https://www.nordicsemi.com/Software-and-tools/Software/nRF5-SDK/Download, select the most recent SDK (17.0.2 as of this posting), and select the the compatible soft devices for nRF52840 (S113 andn S140). By default all free soft devices are selected for download so you’ll actually want to deselect the unnecessary ones. This will download a zipped package. First unzip the top level package to a temporary location , then unzip the sub packages to their permanent homes. I created a development directory ~/nRF_dev, and extracted them there. Lastly create an environment variable that points to the SDK. Again this is used by the Makefiles that are part of these projects.
echo 'export NRF_SDK = "/home/thenerd/nRF_dev/nRF5_SDK_17.0.2_d674dde"' >> ~/.bashrc
Optional
Initialize the SDK as a git repo. This isn’t necessary for the projects presented here, but if you are going build the example projects that ship with the SDK, or if you plan to adapt those projects, it will come in handy down the road. Getting the example projects to build, requires modifying some of the Makefiles, and if you later want to switch to a new version of the SDK, you’ll need to modify all those files again. If you initialize the SDK as a git repo before you make changes, you can later create a patch with your changes, and apply that to the new version of the SDK. From the top level of the sdk run the following commands: (if you don’t plan to use VS Code you exclude the second line)
git init
echo '.vscode/' >> .git/info/exclude
git add .
git commit -m 'initial commit of SDK'
Bootloader
After that, install the DFU bootloader to allow new programs to be loaded over USB. You’ll need python 3 installed if you don’t already have it.
pip3 install --user adafruit-nrfutil
Examples Repository
Finally, clone my repo from github.com with all the example code used in these posts. My repo uses https://github.com/mpaland/printf as a submodule for printf support in the usb examples, which is why the submodule commands are there. You should also make sure you exclude any files associated with your editor/IDE from the git repo. I use .gitignore to exclude the build files, but you should use .git/info/exclude IDE files. I’m using VS Code, so I exclude the .vscode directory and workspace files. If you’re using a different editor you should changes the last two lines below as necessary.
git clone https://github.com/andyj144/nrf52840_examples.git
cd nrf52840_examples
git submodule init
git submodule update
echo '.vscode/' >> .git/info/exclude
echo '*.code-workspace' >> .git/info/exclude
How to use the the repo
File Structure
The basic file structure is laid out as follows:
. ├── printf │ └── test ├── projects │ ├── blinky │ │ ├── _build │ │ ├── config │ │ └── output │ ├── button_blinky │ │ ├── _build │ │ ├── config │ │ └── output │ ├── pwm_example │ │ ├── _build │ │ ├── config │ │ └── output │ ├── usbd_cdc_acm │ │ ├── _build │ │ ├── config │ │ └── output │ └── usb_shell │ ├── _build │ ├── config │ └── output └── tasks
The ‘printf’ folder is a submodule cloned from: https://github.com/mpaland/printf.git. It provides a compact printf implementation that is used in some of the later examples.
The ‘projects’ folder contains a subfolder for each project, and each project contains a config folder. After building the project, a ‘_build’ folder and an ‘output’ folder are also created but these are not tracked in git. The projects shown above are a subset of the projects include in repo.
For projects that utilize FreeRTOS, the tasks are placed in the ‘tasks’ folder so that they can be easily shared between different projects. At this time there is no additional hierarchy within the tasks folder, but that may change in the future.
Building a Project
I’ll detail how the Makefiles are set in the next post, but if you just want to get started building and running some of the example projects you can following these instructions in a terminal.
- From the top level of the repo navigate to the project you would like to build. (in this case, blinky)
- Run:
make
- Assuming you don’t get any build errors, place your board into DFU mode (2 quick presses of the reset button)
- Run:
make bootload SERIAL_PORT='ttyACM0
‘- note: you will need to replace ‘ttyACM0’ with correct device name on your system.
- The bootloaded program should start to run on its own, but projects that use the USB port will need a manual press of the reset button in order for the USB to enumerate correctly.
cd ./projects/blinky make make bootload SERIAL_PORT='ttyACM0'
Editor – Visual Studio Code
I’m using Visual Studio Code as my editor. Its free and it has a lot of extensions – including a debugging extensions that I’ll discuss in later post. It’s more than good enough for what I need. Lots of other things work well too. But if you do want to use Visual Studio Code, here’s how I set it up for these projects.
After installation, you’ll first need to install the “C/C++ IntelliSense, debugging, and code browsing” extension. Open the folder of the project that you’re working on, and configure the settings by pressing ctrl+shift+p and selecting C/C++: Edit Configurations (JSON) in the drop down.
This will create a .vscode folder in the project, and a file called .vscode/c_cpp_properties.json, where you can configure the project so that code completion and error checking work. Below is a copy of the settings I use with most of the example projects.
{ "env": { "tasks": "/home/thenerd/nRF_dev/nrf52840_examples/rtos_projects/tasks", "printf": "/home/thenerd/nRF_dev/nrf52840_examples/printf" }, "configurations": [ { "name": "nRF52840", "includePath": [ "${workspaceFolder}/**", "${env:NRF_SDK}/**", "${env:NRF_SDK}/external/freertos/portable/GCC/nrf52", "${env:NRF_SDK}/external/freertos/portable/CMSIS/nrf52", "${tasks}", "${printf}" ], "defines": [ "BSP_DEFINES_ONLY", "CONFIG_GPIO_AS_PINRESET", "FLOAT_ABI_HARD", "NRF52840_XXAA", "BSP_DEFINES_ONLY", "CONFIG_GPIO_AS_PINRESET", "FLOAT_ABI_HARD", "NR52840_XXAA" ], "compilerPath": "${env:GCC_ARM}/bin/arm-none-eabi-gcc", "cStandard": "c11", "cppStandard": "c++17", "intelliSenseMode": "gcc-arm", "compilerArgs": [ "-mcpu=cortex-m4", "-mthumb", "-mabi=aapcs", "-Wall", "-mfloat-abi=hard", "-mfpu=fpv4-sp-d16", "-ffunction-sections", "-fdata-sections", "-fno-strict-aliasing", "-fno-builtin", "-fshort-enums" ] } ], "version": 4 }
I’m not going to go through this line by line, because most things have descriptive names, but there is one subtlety that I would like point out. VS Code uses ‘**’ to indicate a recursive search, so by putting “${env:NRF_SDK}/**” in the include path, it should include everything in the SDK subfolders, and it does. But then why also have the lines:
"${env:NRF_SDK}/external/freertos/portable/GCC/nrf52", "${env:NRF_SDK}/external/freertos/portable/CMSIS/nrf52",
There are some files that have different versions depending on which compiler and chip family you are using. For VS Code to find the right version, you need to explicitly include the folder with the correct version. If you don’t, VS Code will tell you there’s an include error, when there really isn’t one. This is just for VS Code to work smoothly. It has nothing to do with building the projects, but it helps for real time error checking, and debugging.
Conclusion
At this point you’ll have all the tools you need to compile and load the rest of the projects in this series of examples. In Part 3 of this series I’ll explain the organization of the repository, and I’ll go through the blinky example in detail. That includes how to setup the linker file, and what else from the SDK needs to be built and linked.
Is there going to be any follow on to this series? What happened to Post 3?