An FPGA And Embedded Engineering Blog

Month: July 2023

nrf5340-DK vs nrf52-DK

The nrf5340-DK from Nordic Semiconductors just arrived in the mail. Let’s have a look at what sets it apart from the nrf52-DK featuring an nrf52832!

Picture of a breadboard, an nrf52-DK on the left and an nrf5340-DK on the right. Saleae Logic Analyzer trying to steal itself into the picture.
nrf52-DK on the left, nrf53-DK on the right. Saleae Logic Analyzer trying to steal itself into the picture.

The Dev-Board

The differences

Looking at the two boards side-by-side, the first thing we notice is the bigger size of the dev-board. This is probably mostly due to the increased number of GPIO pins the nrf5340 supports. It features twice as many I/O pins as the nrf52832 (2 ports of 32 pins instead of just 1).

The nrf53-DK also comes with the possibility to run it on a Li-po battery and the power source can be chosen accordingly with a switch. In addition, the nrf53-DK also has a USB connector that is directly interfaced to the target MCU. The ESD protection circuit features a PRTR5V0U2X, which looks interesting and will probably be on the BOM of my next PCB featuring an USB connector, but I digress…

The commonalities

For the rest, the two boards have a lot in common, they feature an NFC connector as well as a PCB antenna and SMA connectors for adding an external antenna. Both have debug headers that can be used to program and debug external devices, and they also both have current measurement support, which comes in handy if we need to do current profiling of applications.

The nrf5340-DK actually features two nrf5340s! Here, the first one (U1) is the one that’s actually connected to all the peripherals, on the DK. The second one (U2) is only used for programming and debugging (U2). Nordic calls this second MCU the interface MCU .This is similar to the nrf52-DK, however the older revision of the nrf52-DK I am using still features a Microchip ARM-MCU (ATSAM3U2CA) as an interface MCU, while on newer revisions you’d also find an nrf5340.

As usual, the board comes in an Arduino compatible design. Arduino shields are easily applied and used for prototyping. LEDs and Buttons can be found on both, even though they aren’t interfaced to the same pins.

The Documentation

I have to say, when it comes to documentation, Nordic is on top of the game. They provide a lots of detail while not overloading you with information. A single ZIP-File containing all the production files is available for download, including gerber files. Even pick-and-place files are in there so these development boards are essentially open source hardware! There is also a pdf schematic in the ZIP-file and we find everything with a simple Google-Search. (Here for the nrf52-DK and here for the nrf53-DK) That’s top of the line and I can’t see a big difference between the nrf52-DK and the nrf53-DK. Keep that up Nordic!

The Processor

Now for the heart and brain of the boards, the processors. The nrf5340 is quite a lot beefier than the nrf52832.

Cores

Instead of a single processor, the nrf5340 features two Arm® Cortex®-M33 processors, where one is marketed as the “Application core” and one as the “Network core”. The application core comes with a lot of debug-ability supporting different Trace-interfaces. It also contains a Arm TrustZone CryptoCell™-312 security subsystem that allows for hardware accelerated computation of common cryptographic algorithms and PRNG.

Memory

While the nrf52832 only has measly 512kB flash and 64kB of RAM, the nrf5340 has 1MB of flash memory and 512(!)kB of RAM – now that’s an improvement.

Peripherals

In terms of peripherals, there isn’t too much of a difference between the two, as both feature the common usual suspects – SPI, I²C, I²S and UART. The nrf5340 has the already mentioned USB-support and also supports QSPI. Everything can be interfaced using the programmable peripheral interconnect so we aren’t forced to use certain pins for certain functions.

Wireless communication

Finally, in addition to the nrf52’s BLE, BL Mesh and ANT™ support, the nrf5340 also supports IEEE 802.15.4, which makes it ready for Matter, Thread and Zigbee, so it is even more applicable for the IoT devices of the coming decade.

Conclusion

The nrf52-DK and nrf53-DK have commonalities and differences. Both devices have their place and knowing the differences is key in knowing when to use which device. In general, these devices are quality products and if you can get away with an nrf52832, it should definitely be used. Having both to my disposal enables me to quickly evaluate solutions and find the most appropriate device for the task.

Driver development for Zephyr RTOS and nrfConnect – Part 1

Introduction

In this tutorial we will look at how we can create a device driver for Zephyr RTOS.
Zephyr RTOS already supports a lot of different devices and it may already have drivers for your target. If that’s not the case, you came to the right place. We will find out how we can create device drivers for Zephyr RTOS in a streamlined manner. The device that we will be targeting is the MS8607 sensor by TE connectivity.

We will put the driver together with the source code of our main application. This is the simplest form of implementing the driver and it will serve us well. When projects become larger or if multiple projects depend on the same device, it may become necessary to move the driver out, however for small to medium projects, having the driver right there with the rest of the source is completely sufficient.

The Bare Minimum

The Necessary Files

After generating a hello-world project in VSCode using nrf Connect, the first thing we need to do is add several directories and files, such that our tree looks like the following (ignoring some of the pre-generated files):


├───dts
│   └───bindings
│       └───drivers
│           └───example,test-driver.yaml
└───src
    └───driver
    │    └───test_driver.c
    │    └───test_driver.h
    └───main.c
    └───CMakeLists.txt
    └───prj.conf
    └───nrf52dk_nrf52832.overlay

The attentive reader will see that this is built for the nrf52-DK featuring an nrf52832.

CMakeLists Entry

Before we forget it, let’s quickly add the c file we just added to the CMakeLists.txt file. Simply add the line


target_sources(app PRIVATE src/driver/test_driver.c)

to the bottom of the file and it will be included when building the project.

The Devicetree Bindings

Next, let’s create the simplest possible devicetree binding file. Edit dts/bindings/drivers/example,test-driver.yaml to contain the following:

description: Driver Test

compatible: "example,test-driver"

include: base.yaml

This will create a compatible that we will be able to use in our device tree. By including base.yaml our driver automatically inherits some important properties, such as status and compatible, which you will have come across already when you have used Zephyr RTOS before.

The Driver Source

We will create a multi-instance driver. Like this, it is possible to have multiple instances of the same device in our project.

Let us fill out the barebones for src/driver/test_driver.c next:

#include <zephyr/device.h>

#define DT_DRV_COMPAT example_test_driver

static int driver_init(const struct device *dev) {
    printk("Initializing driver!\n");
}

#define ZEPHYR_DRIVER_TEST_INST(inst) \
    DEVICE_DT_INST_DEFINE(  \
        inst,        \
        driver_init, \
        NULL, \
        NULL, \
        NULL, \
        APPLICATION, \
        CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, \
        NULL);

DT_INST_FOREACH_STATUS_OKAY(ZEPHYR_DRIVER_TEST_INST)

The line …

#define DT_DRV_COMPAT example_test_driver

… registers our driver with the compatible we defined earlier. Commas and hyphens are replaced by underscores and everything needs to be lowercase.

Next, a macro is defined that will allow for an instance to be initalized. Actually, the last line …

DT_INST_FOREACH_STATUS_OKAY(ZEPHYR_DRIVER_TEST_INST)

… will run this macro for each instance that is added to the devicetree that has a status of okay. You will want to look at the documentation for DEVICE_DT_INST_DEFINE to see, what the different entries stand for. A word of caution though: If you have several instances of the same device, the value for inst will not have any relationship with the ordering of the devices in the devicetree file, nor to the memory address of that device, so you should never rely on that directly.

We set the driver_init function as an initialization function for our driver and we choose to initialize the driver at APPLICATION level, which allows us to see the output of the printk function.

Now only a last change needs to be done before we can build, flash and reap the fruits of our work!

The Devicetree Overlay

In the file nrf52dk_nrf52832.overlay (or whatever board you are using), create an instance for our newly created driver in the soc node:


/ {
    soc {
        drivertest: driver_test0 {
            compatible = "example,test-driver";
            status = "okay";
        };
    };
};

Build and Flash

That’s it for the first part! Build the code by hitting Ctrl+Alt+B. You will probably come across the line…

node '/soc/driver_test0' compatible 'example,test-driver' has unknown vendor prefix 'example'

… which is due to the fact that there is no vendor called example in the file zephyr/dts/bindings/vendor-prefixes.txt in your zephyr installation. This is not an error though and everything compiles fine anyway.

After that, flash the code to your device and you’ll be greeted by the wonderful line…

Initializing driver!

We didn’t even have to add any code to main.c yet our driver is initialized and ready to be used. Next, we will look at adding a bit more functionality to the driver and making it actually useful.

Conclusion

That concludes the first part of the driver development! In the next part, we will look at how to choose an API for our driver and make it actually useful. Stay tuned!