doc: guides: dts: improve device section

- Move to 1st option DEVICE_DT_GET
- Make device_get_binding 2nd choice
- Extend details on differences between DEVICE_DT_GET/device_get_binding
- Extend the troubleshooting section.

Signed-off-by: Gerard Marull-Paretas <gerard.marull@nordicsemi.no>
This commit is contained in:
Gerard Marull-Paretas 2022-03-09 20:56:42 +01:00 committed by Carles Cufí
parent 222d42c22a
commit 893b941938

View file

@ -99,9 +99,31 @@ works best for your requirements. Here are some examples:
/* Option 4: by path */
#define MY_SERIAL DT_PATH(soc, serial_40002000)
Once you have a node identifier there are two ways to proceed. The
classic way is to get the ``struct device`` by combining
:c:func:`DT_LABEL` with :c:func:`device_get_binding`:
Once you have a node identifier there are two ways to proceed. One way to get a
device is to use :c:func:`DEVICE_DT_GET`:
.. code-block:: c
const struct device *uart_dev = DEVICE_DT_GET(MY_SERIAL);
if (!device_is_ready(uart_dev)) {
/* Not ready, do not use */
return -ENODEV;
}
There are variants of :c:func:`DEVICE_DT_GET` such as
:c:func:`DEVICE_DT_GET_OR_NULL`, :c:func:`DEVICE_DT_GET_ONE` or
:c:func:`DEVICE_DT_GET_ANY`. This idiom fetches the device pointer at
build-time, which means there is no runtime penalty. This method is useful if
you want to store the device pointer as configuration data. But because the
device may not be initialized, or may have failed to initialize, you must verify
that the device is ready to be used before passing it to any API functions.
(This check is done for you by :c:func:`device_get_binding`.)
In some situations the device cannot be known at build-time, e.g., if it depends
on user input like in a shell application. In this case you can get the
``struct device`` by combining :c:func:`DT_LABEL` with
:c:func:`device_get_binding`:
.. code-block:: c
@ -115,23 +137,6 @@ There's no need to override the ``label`` property to something else: just make
a node identifier and pass it to ``DT_LABEL`` to get the right string to pass
to ``device_get_binding()``.
The second way to get a device is to use :c:func:`DEVICE_DT_GET`:
.. code-block:: c
const struct device *uart_dev = DEVICE_DT_GET(MY_SERIAL);
if (!device_is_ready(uart_dev)) {
/* Not ready, do not use */
return -ENODEV;
}
This idiom fetches the device pointer at build-time, which is useful when you
want to store the device pointer as configuration data. But because the
device may not be initialized, or may have failed to initialize, you must
verify that the device is ready to be used before passing it to any API
functions. (This check is done for you by :c:func:`device_get_binding`.)
If you're having trouble, see :ref:`dt-trouble`. The first thing to check is
that the node has ``status = "okay"``, like this:
@ -140,15 +145,33 @@ that the node has ``status = "okay"``, like this:
#define MY_SERIAL DT_NODELABEL(my_serial)
#if DT_NODE_HAS_STATUS(MY_SERIAL, okay)
const struct device *uart_dev = device_get_binding(DT_LABEL(MY_SERIAL));
const struct device *uart_dev = DEVICE_DT_GET(MY_SERIAL);
#else
#error "Node is disabled"
#endif
If you see the ``#error`` output, make sure to enable the node in your
devicetree. If you don't see the ``#error`` but ``uart_dev`` is NULL, then
there's likely either a Kconfig issue preventing the device driver from
creating the device, or the device's initialization function failed.
devicetree. In some situations your code will compile but it will fail to link
with a message similar to::
...undefined reference to `__device_dts_ord_N'
collect2: error: ld returned 1 exit status
This likely means there's a Kconfig issue preventing the device driver from
being built, resulting in a reference that does not exist. If your code compiles
successfully, the last thing to check is if the device is ready, like this:
.. code-block:: c
if (!device_is_ready(uart_dev)) {
printk("Device not ready\n");
}
If you find that the device is not ready, it likely means that the device's
initialization function failed. Enabling logging or debugging driver code may
help in such situations. Note that you can also use :c:func:`device_get_binding`
to obtain a reference at runtime. If it returns ``NULL`` it can either mean that
device's driver failed to initialize or that it does not exist.
.. _dts-find-binding: