What's the official way to implement a simple peripheral (SPI, I2C, etc) with NCS?

Hi there,

I'm struggling to find straight forward peripheral interface documentation for an NCS project.  I see there's something called nrfx (https://github.com/NordicSemiconductor/nrfx) and also Zephyr has peripheral APIs (https://developer.nordicsemi.com/nRF_Connect_SDK/doc/latest/zephyr/reference/peripherals/index.html).  Which is correct?  

For nrfx, I don't see any examples of usage of this.  For Zephyr APIs, how do I configure them?

Thanks

  • As far as I know, the Zephyr API uses the the nrfx HAL as can be seen in https://github.com/zephyrproject-rtos/zephyr/blob/master/drivers/spi/spi_nrfx_spim.c for example. These functions (as well as other drivers) are the once that are called from the api->dostuff() function calls in the Zephyr drivers.

    If I understood correctly, when you enable perihperals in the Kconfig you choose which underlying driver that gets called from the Zephyr API. I guess using Zephyr API makes your project more easy to port if you switch MCU in the future.

    I personally try to use the Zephyr API as much as possible to try to keep the code as coherent as possible. One drawback of this is that there is not 100% clear what is happening configuration wise with the perihperals and you need to manually check the drivers to see what Zephyr does when it sets up everything.

    However, I am wondering, same as you, which method is the "correct" one according to nordic since I've seen on Devzone that several people use the nrfx functions directly instead of the Zephyr one. I guess it is a convenice vs control issue.

    Would be nice to have an official answer from a Nordic rep about the subject. Will NCS diverge more from Zephyr in the future?

  • Thanks .  Hopefully we can get a Nordic engineer to see this and give us some guidance.

  • If you would like to fully utilize all the advantages Zephyr brings with it, I would recommend you to use the Zephyr API. In Zephyr you use Kconfigs (through prj.conf) and the device tree/dts (through overlay files) to set up a project, read more about this in 1. Concepts in NCS. If you use the nrfx layer, the stuff you've set in prj.conf and overlay files (and all the other Kconfig and dts files) will not be seen. Using the Zephyr API makes it quicker and easier to set up a project (Given that everything works as expected and you have a good intuitive understanding of how the SDK works). There might be more advantages of using the Zephyr API that I have missed, but this is what I though of right now.

    Some developers likes to have more control over what they're doing, with less abstractions from the hardware, then the nrfx drivers might be a better option. 

    From my point of view, I would recommend you to use the Zephyr API, as it will reduce the development time, make the application more scalable/portable/cross-compatible and reduce the amount of code you write (less chances of bugs). I don't have a complete overview of this, so if anyone could prove me wrong on any of these points, I would be more than happy.

    Since NCS/Zephyr is still quite new, there are circumstances where the nrfx layer has to be used. E.g. if you want to use I2S with Zephyr, you have to use the nrfx drivers, since it's not supported on the top layer (I think there is on-going work right on this right now).

    Shaney is correct in that the Zephyr API uses the nrfx drivers. The figure below (from NCS tutorial part 3) might give you an overview of how it works:

    Best regards,

    Simon

  • I'm still struggling with this.  I'm using the nRF52840dk and it seems to be "kind of" supported.  I find myself piecing together how to do this from forum posts and disparate documentation.  Honestly this is strange, why can't I just go to one set of documentation that describes the libraries needed and how to use them?  Why is so much of the NCS "documentation" actually just obfuscated examples that I need to reverse engineer?  I feel like I should just be able to #include a header file and do straight forward established I2C functions.

  • Do I look at NCS docs?  Zephyr docs?  nrfx, the old softdevices?  This is not clear.

  • I'm sorry for giving you a very general answer initially. I'll try to be more concrete now.

    The samples in ncs/zephyr/samples/drivers are samples that uses the API of the peripherals (SPI, I2C etc..) directly. They use the API ncs/zephyr/include/drivers/<peripheral>.h. Looking at theses samples may give you an understanding of how to use a peripheral.

    However, these are generic examples, and you also need to enable and configure the specific peripheral in the device tree, by adding an overlay file <board name>.overlay to the sample. I could probably give you a lot of more explanations but I think it's better to just provide you with some samples so you can see how it's done:

    I2C

    SPI

    • In this reply, you'll see an SPI loopback sample for NCS v1.4.1. Add an overlay file for your particular board and connect the mosi pin to the miso pin together, and the sample will communicate with itself
    • In this reply are two more SPI samples. One SPI slave (spis_thread_test.zip) and one SPI master (7585.spi_test.zip). Build and program these samples to two different boards and connect them together.

    If you are interested in using a specific sensor, you should take a look at the samples in  ncs/zephyr/samples/sensor and see if you can find your sensor there. It adds a layer on top of the peripheral API by using the API/drivers in ncs/zephyr/drivers/sensor. It will save you a lot of work, and datasheet-reading. Check out the guide 1.2 Walkthrough for BME280 for instructions how to use these samples for a particular board.

    Best regards,

    Simon

  • Hi Simon,

    This worked well.  Thanks for your help and apologies about my impatience, I now understand a bit better how the Zephyr framework works.