Structure

Create a new directory dm163_example to host your new application and driver code. Once we are done, the file structure will look like this:

dm163_example
├── boards
│   └── disco_l475_iot1.overlay
├── CMakeLists.txt
├── dm163_module
│   └── zephyr
│       ├── CMakeLists.txt
│       ├── dm163.c
│       ├── Kconfig
│       └── module.yml
├── dts
│   └── bindings
│       └── siti,dm163.yaml
├── prj.conf
└── src
    └── main.c

(you do not need to create this hierarchy right now, you'll be guided when it is time to do that)

Here are some explanations of the structure:

  • You are already familiar with CMakeLists.txt, prj.conf and src/main.c.
  • The dm163_module directory contains the DM163 device driver.
  • The dts/bindings directory contains the siti,dm163.yaml file which describes what is permitted and what is required in a node marked compatible with our device driver, whose name is, as you guessed, siti,dm163 (from its maker SiTI and its model DM163).
  • boards/disco_l475_iot1.overlay contains the device-tree part describing how the DM163 device is connected to our board.

From now on, all file names we indicate in the instructions will be relative to the top of the dm163_example folder.

What is in Zephyr's struct device?

A struct device in Zephyr is made of several fields, including:

  • A config pointer, which points to read-only data describing the configuration gathered from the device tree and the Kconfig options. In our case, the configuration will be a structure containing information about the pins to use in order to drive the DM163 chip.
  • A data pointer, which points to data describing the current state of the device driver. In our case, it will contain the brightness and color information for the leds.
  • A api pointer, which points to a read-only data structure compatible with the driver we want to be compatible with. Here we want to be compatible with the led driver API, so our api field will be a const struct led_driver_api * const.

Most functions will receive a struct device * parameter which you can use to extract the config and data field containing the information you need. For example, to turn on one of the leds controlled by the DM163, you will modify the state of the led in the data structure and send the new led configuration using pins whose configuration is located in the config structure.

There exist other fields to store power management operations if the device can be put to sleep, as well as the initialization function to call to initialize the device.