If you’ve heard the news about the nRF9160 then you know it’s a game-changer. ARM Cortex M33 + LTE Cat-M + NB-IoT and GPS all included under one System in Package. Drool-worthy, right?
@jaredwolff Thank you Jared for this great post! May I ask you a question about Kconfig? Apart from the conditional compilation in CMakeList, it seems to me that according to the boolean value of Kconfig symbols, some driver API functions will be compiled and some won’t (e.g. in /zephyr/include/uart.h). So to see which API functions my application code is able to use, is going into corresponding driver API source and header file and looking for selected Kconfig symbols the only way?
Essentially yes. You can also run a west build -t menuconfig and this will open up the configuration screen which allows you to search for specific names. It also makes it a little easier to view the notes for those KConfig values. Otherwise, yep, your other option is to look manually at the driver code to see what those KConfig entries actually do.
Another thing I found useful is to look at the example code scattered throughout the Zephyr repo and NCS (if you’re using Nordic chips). The prj.conf files give you a great insight on what’s needed where.
I am currently using nRF52840 DK. On that board I2S peripheral is not available by default in device tree, but Zephyr has provided drivers for I2S. So i added I2S peripheral in dtsi and dts file. Also configured CONFIG_I2S=y in project configuration file. And after building the object i can see it’s giving a status Okay in Zephy.dts file.
Now after getting the device label, when i call device get binding function on it, result is a NULL pointer. Most probably the DEVICE_DEFINE() is not getting executed for I2S peripheral otherwise it would have found the I2S peripheral config pointers from ROM.
Below is my code for reference :
#include <zephyr.h>
#include <sys/printk.h>
#include <sys/util.h>
#include <string.h>
#include <usb/usb_device.h>
#include <drivers/uart.h>
#include <stdio.h>
#include <device.h>
#include <devicetree.h>
#include <drivers/gpio.h>
#include <drivers/i2s.h>
//Device structures
const struct device *usb_dev;
const struct device *i2s_dev;
/ * The devicetree node identifier for the I2S */
#define MY_I2S DT_NODELABEL(i2s)
void main(void)
{
uint32_t dtr = 0;
//get usb device
usb_dev = device_get_binding(CONFIG_UART_CONSOLE_ON_DEV_NAME);
if (usb_enable(NULL)) {
return;
}
while (!dtr) {
uart_line_ctrl_get(usb_dev, UART_LINE_CTRL_DTR, &dtr);
}
if (strlen(CONFIG_UART_CONSOLE_ON_DEV_NAME) !=
strlen("CDC_ACM_0") ||
strncmp(CONFIG_UART_CONSOLE_ON_DEV_NAME, "CDC_ACM_0",
strlen(CONFIG_UART_CONSOLE_ON_DEV_NAME))) {
printk("Error: Console device name is not USB ACM\n");
return;
}
k_msleep(100);
//Checking status of I2S in device tree
#if DT_NODE_HAS_STATUS(MY_I2S, okay)
printk("I2S enabled in Zephyr DeviceTree \r\n");
#else
printk("I2S is disabled in DeviceTree\r\n");
#endif
//Get I2S device
i2s_dev = device_get_binding(DT_LABEL(MY_I2S));
if(i2s_dev == NULL){
printk("I2S not found\r\n");
}else{
printk("I2S found\r\n");
}
while (1)
{
}
}
Pulling the correct device from the .dts can be tricky. Generally you’ll get a compile error if you’re using the macros incorrectly. You’ll have to pinpoint in your code where it’s NULL where it shouldn’t be.
I’m not sure why your i2s device is inside another label though. Generally those devices are outside (no indentation). For example here’s my i2c definition for the nRF9160 Feather:
Hello,
Thanks for the suggestion, it solved the issue. And my I2S device is under SOC label like all other peripherals of the board in Zephyr Dts file.
@jaredwolff thanks for the great guide. I tried following it to implement my own driver for an accelerometer in a Zephyr nRF project. The devicetree seems to be ok and I can see the c file for the driver getting compiled but it seems it is not getting linked to the main app. I fail to get a device_get_binding result. I am getting “undefined reference to” errors if I try to directly reference functions from the driver in the main app. Also I can’t find any strings from the driver in the .elf file.
Is there anything special that is responsible for linking the “zephyr_library” that I could have missed?
Any tips on how to troubleshoot?
Is there a way to download the code you have used in the guide?
In general when you’re creating a device driver you’re going to be putting it within an “external” module. That means outside your project code. You can see that in a few repositories that I’ve worked on in the past:
There are a few important things you need to make sure you do:
Point your west.yml to the module with the driver in it
Make sure your module has a zephyr/module.yml defined
Make sure that your module has a Kconfig and CMakeLists.txt chain that point to your driver.
Modules always get called and compiled before your application. So that all has to be in place before you start building.
One of the users on my forum has been working though getting an accelerometer driver going as well. You can read through our dialogue and it may help your process.
I wanted to use it to write a driver for my display with an SSD7317.
As a template, I took the display driver of the SSD1306 which is already available in the device tree of Zephyr.
I have packed the shield definition into my overlay file, as I have connected it directly to my board.
Now when I want to use it with LVGL I have the problem that it doesn’t seem to find the device in the DTS.
I get a No SOURCES given to Zephyr library: drivers__display as a warning when compiling and at the end the linker fails with undefined reference to __device_dts_ord_128`'.
In zephyr.dts my driver is listed as expected.
However, if I place the same driver under zephyr/drivers/display and adjust the Kconfig and CMakeList.txt the compilation runs through without any errors.
It also works if I omit the zephyr_library() in the CMakeLists.txt of the driver.
Do you have any hints what I am doing wrong with the driver in my project or what I could still check?
Thanks for your reply @jaredwolff, but my problem isn’t the usage of different boards.
I wanted to create a new display driver within my project dir as mentioned in your blog post. But if I use this with lvgl it doesn’t compile (" undefined reference to __device_dts_ord_128").
That’s why I copied the unchanged driver files in my zephyr directory (zephyr/drivers/display) and edited manually the CMakeList and Kconfig of the zephyr driver directory and then it works and compiles correctly (without any change to my app code or project config)
So your most likely problem is you’re trying to change or update a driver with the same name as it exists elsewhere in the Zephyr tree. In my experience I had to rename my updated driver to something else so I could define it in the code. For example I added FIFO to the LIS2DH driver and had to rename it with an _ENHANCED suffix.
menuconfig LIS2DH_ENHANCED
bool "LIS2DH Three Axis Accelerometer (Enhanced)"
depends on I2C || SPI
help
Enable SPI/I2C-based driver for LIS2DH, LIS3DH, LSM303DLHC,
LIS2DH12, LSM303AGR triaxial accelerometer sensors.
Whereas the original was simply:
menuconfig LIS2DH
A good clue to see what’s happening is to take a look at your build/zephyr/.config file to see whether things are enabled correctly or not.
I would like to know if you can provide a way to implement a driver when I have to use my API implementation (what I actually mean is how to create new zephyr/drivers/sensor.h)
I saw that most of the time when someone can’t find a proper driver API for his need, they use the sensor.h which can be used but needs a lot of workarounds.