I'm struggling with SDKConnect and the device tree stuff... and finding it hard to get good examples or docs TBH
So, I have the sample dts files for the nRF5340DK, which defines the config for the 'uart0' node:
I'm struggling with SDKConnect and the device tree stuff... and finding it hard to get good examples or docs TBH
So, I have the sample dts files for the nRF5340DK, which defines the config for the 'uart0' node:
Hi BrianW,
I empathize with your difficulties in getting starts with Devicetree. We do offer an online course to help with the early stages, but what you are trying to do is unfortunately more advanced than what the course covers.
You will first want to look into the concept of phandle, then you can find the pin control API here: https://docs.nordicsemi.com/bundle/ncs-2.6.1/page/zephyr/build/dts/api/api.html#pinctrl-pin-control.
Devicetree API is makes it more straightforward when using the Zephyr API to access peripheral. As you want to use nrfx drives, it becomes a little more complex.
The benefit of using the API above is that your hardware description is still concentrated in the Devicetree, allowing the project to support multiple hardware with just the Devicetree board files change. You can just disable the nodes in Devicetree, and use pin number directly in code, at the cost of this benefit.
Hieu
Devicetree API is makes it more straightforward when using the Zephyr API to access peripheral. As you want to use nrfx drives, it becomes a little more complex.
Yes, it does. And I was kind of hoping for a reply with more specific help about how to do it, the documentation is VERY basic.
Specific questions then:
I want to get the uart config to populate an nrfx uarte config structure,
Devicetree API is makes it more straightforward when using the Zephyr API to access peripheral. As you want to use nrfx drives, it becomes a little more complex.
Yes, it does. And I was kind of hoping for a reply with more specific help about how to do it, the documentation is VERY basic.
Specific questions then:
I want to get the uart config to populate an nrfx uarte config structure,
by the way, I tried doing
char* nodepath = DT_PATH(DT_ALIAS(console));
and this fails to compile:
C:/work/dev/nordic_connect/zephyr/include/zephyr/devicetree.h:90:17: error: 'DT_N_S_DT_N_S_soc_S_peripheral_40000000_S_uart_8000' undeclared (first use in this function); did you mean 'DT_N_S_soc_S_peripheral_40000000_S_uart_8000_ORD'?
The duplicated "DT_N_S_" part makes me think that the DT_PATH and DT_ALIAS macros don't play well together, despite what I understand the parameter to DT_PATH to be? (a node id).
any pointers on this?
BrianW said:Q1 : how to go from a runtime string "console" to a DT node to be able look up properties? Are there callable methods that do the same jobs as the macros to interrogate the DTS at runtime?
BrianW said:The duplicated "DT_N_S_" part makes me think that the DT_PATH and DT_ALIAS macros don't play well together, despite what I understand the parameter to DT_PATH to be? (a node id).
any pointers on this?
Note in the device tree that the console is associated with an UART node by setting the zephyr-console property of the chosen node.
console is thus not an alias of the UART node.
To access such chosen values, you can use DT_CHOSEN.
DT_CHOSEN(zephyr_console)
BrianW said:Q2 : how to get the pinctrl property for the specific pin use? (eg the pin for rxd in 'default' pinctrl?)
You can get the pinctrl node by using DT_PINCTRL_BY_NAME.
BrianW said:Q3 : how to get the gpio pin reference for the txd, rxd etc pins from the pinctrl (given the use of the PSEL() macro to merge pin use, port, gpio into a single 32 bit value)?
There are a few methods.
Under the pinctrl nodes are its group child node, and under the group nodes are the psels property.
They are still Devicetree child nodes and properties, so they can be access with DT_PROP, DT_CHILD, DT_FOREACH_PROP_ELEM, DT_FOREACH_CHILD.
Alternatively, you can use the Pin Control API.
BrianW said:A code sample would be very helpful.
Here is the sample, just replace the code into main.c in the Hello World sample.
/* * References: * C:\ncs\v2.6.1\zephyr\drivers\pinctrl\pinctrl_nrf.c * C:\ncs\v2.6.1\zephyr\include\zephyr\drivers\pinctrl.h * C:\ncs\v2.6.1\zephyr\drivers\serial\uart_nrfx_uarte.c * C:\ncs\v2.6.1\zephyr\dts\bindings\pinctrl\nordic,nrf-pinctrl.yaml * C:\REDACTED\hello_world_261\build_52840dk\zephyr\include\generated\devicetree_generated.h * C:\ncs\v2.6.1\zephyr\soc\common\nordic_nrf\pinctrl_soc.h * https://devzone.nordicsemi.com/f/nordic-q-a/109455/how-to-use-device-tree-to-set-up-pdm-pin-configurations-with-nrfx-pdm-directly-no-dmic */ #include <stdio.h> #include <zephyr/devicetree.h> #include <zephyr/drivers/pinctrl.h> // See also this file: #include <devicetree_generated.h> #define M_CONSOLE_UART_NODE_ID DT_CHOSEN(zephyr_console) // uart0 node ID, DT_N_S_soc_S_uart_40002000 #define M_PINCTRL_0_NODE_ID DT_PINCTRL_BY_IDX(M_CONSOLE_UART_NODE_ID, 0, 0) // See also DT_PINCTRL_0, DT_PINCTRL_BY_NAME /************************************************************************************************** * Method 1 *************************************************************************************************/ #define M_PINCTRL_0_GROUP1_NODE_ID DT_CHILD(M_PINCTRL_0_NODE_ID, group1) const uint32_t pinctrl_0_group1_psels[] = DT_PROP(M_PINCTRL_0_GROUP1_NODE_ID, psels); const uint32_t pinctrl_0_group1_psels_size = DT_PROP_LEN(M_PINCTRL_0_GROUP1_NODE_ID, psels); /************************************************************************************************** * Method 2 *************************************************************************************************/ #define PRINT_FROM_PSEL_MACRO_FN(node_id, prop, idx) \ do { \ print_psel(DT_PROP_BY_IDX(node_id, prop, idx)); \ } while(0); #define RUN_FOREACH_PSELS_ELEM_FN(GROUP_NODE_ID) \ do { \ printf("RUN_FOREACH_PSELS_ELEM_FN: %s\n", DT_NODE_FULL_NAME(GROUP_NODE_ID)); \ DT_FOREACH_PROP_ELEM(GROUP_NODE_ID, psels, PRINT_FROM_PSEL_MACRO_FN) \ } while (0); #define RUN_FOREACH_PINCTRL_0_GROUP_FN(PINCTRL_NODE_ID) DT_FOREACH_CHILD(PINCTRL_NODE_ID, RUN_FOREACH_PSELS_ELEM_FN) void print_psel(const uint32_t psel) { uint32_t pin = NRF_GET_PIN(psel); switch (NRF_GET_FUN(psel)) { case NRF_FUN_UART_TX: printf("PSEL 0x%08x - TX: %d\n", psel, pin); break; case NRF_FUN_UART_RX: printf("PSEL 0x%08x - RX: %d\n", psel, pin); break; case NRF_FUN_UART_RTS: printf("PSEL 0x%08x - RTS: %d\n", psel, pin); break; case NRF_FUN_UART_CTS: printf("PSEL 0x%08x - CTS: %d\n", psel, pin); break; } } // Refer pinctrl_configure_pins() void breakdown_pinctrl_state(const struct pinctrl_state* state) { const pinctrl_soc_pin_t* pins = state->pins; const uint8_t pin_cnt = state->pin_cnt; for (uint8_t i = 0U; i < pin_cnt; i++) { print_psel(pins[i]); } } /************************************************************************************************** * Method 3 *************************************************************************************************/ // Necessary to use PINCTRL_DT_DEV_CONFIG_GET later // Expands to: static const pinctrl_soc_pin_t __pinctrl_state_pins_0__device_dts_ord_113[] = {(6 | ((1U * 0) << 7U) | ((3U * 0) << 7U) | (0 << 9U) | ((1U * 0) << 13U) | (0 << 14U) ), (131077 | ((1U * 0) << 7U) | ((3U * 0) << 7U) | (0 << 9U) | ((1U * 0) << 13U) | (0 << 14U) ), (65544 | ((1U * 0) << 7U) | ((3U * 1) << 7U) | (0 << 9U) | ((1U * 0) << 13U) | (0 << 14U) ), (196615 | ((1U * 0) << 7U) | ((3U * 1) << 7U) | (0 << 9U) | ((1U * 0) << 13U) | (0 << 14U) ),} ; ; static const struct pinctrl_state __pinctrl_states__device_dts_ord_113[] = { { .id = 0U, .pins = __pinctrl_state_pins_0__device_dts_ord_113, .pin_cnt = ((size_t) (((int) sizeof(char[1 - 2 * !(!__builtin_types_compatible_p(__typeof__(__pinctrl_state_pins_0__device_dts_ord_113), __typeof__(&(__pinctrl_state_pins_0__device_dts_ord_113)[0])))]) - 1) + (sizeof(__pinctrl_state_pins_0__device_dts_ord_113) / sizeof((__pinctrl_state_pins_0__device_dts_ord_113)[0])))) } , }; static const struct pinctrl_dev_config __pinctrl_dev_config__device_dts_ord_113 = { .reg = 1073750016, .states = __pinctrl_states__device_dts_ord_113, .state_cnt = ((size_t) (((int) sizeof(char[1 - 2 * !(!__builtin_types_compatible_p(__typeof__(__pinctrl_states__device_dts_ord_113), __typeof__(&(__pinctrl_states__device_dts_ord_113)[0])))]) - 1) + (sizeof(__pinctrl_states__device_dts_ord_113) / sizeof((__pinctrl_states__device_dts_ord_113)[0])))), } // Note that ordinance varies with different build PINCTRL_DT_DEFINE(M_CONSOLE_UART_NODE_ID); void method_3(void) { const struct pinctrl_dev_config* p_pinctrl_dev_cfg = PINCTRL_DT_DEV_CONFIG_GET(M_CONSOLE_UART_NODE_ID); uint8_t t_state_cnt = p_pinctrl_dev_cfg->state_cnt; printf("pinctrl state_cnt = %d\n", t_state_cnt); for (uint8_t i = 0; i < t_state_cnt; i++) { const struct pinctrl_state* tp_pinctrl_state = &p_pinctrl_dev_cfg->states[i]; printf(" state #%02d - id = %d\n", i, tp_pinctrl_state->id); uint8_t t_pin_cnt = tp_pinctrl_state->pin_cnt; printf(" t_pin_cnt = %d\n", t_pin_cnt); for (uint8_t j = 0; j < t_pin_cnt; j++) { const pinctrl_soc_pin_t t_pin = tp_pinctrl_state->pins[j]; print_psel(t_pin); } } } int main(void) { printf("Hello World! %s\n", CONFIG_BOARD); printf("Test console node full name: %s\n", DT_NODE_FULL_NAME(M_CONSOLE_UART_NODE_ID)); printf("\n\n\nMethod 1 ===================================================================\n"); for (uint8_t i = 0; i < pinctrl_0_group1_psels_size; i++) { print_psel(pinctrl_0_group1_psels[i]); } printf("\n\n\nMethod 2 ===================================================================\n"); RUN_FOREACH_PINCTRL_0_GROUP_FN(M_PINCTRL_0_NODE_ID); printf("\n\n\nMethod 3 ===================================================================\n"); method_3(); return 0; }
Excellent, thanks. Using the NRF_GET_PIN/NRF_GET_FUN macros was what I needed to extract the pin definitions, and with the PINCTRL_DT_DEV_CONFIG_GET I can get the full set of state/groups and find the pins i want.
My code now looks like this:
I'm still stuck with 1 point: I would like to pass the alias name ('myconsole') to the function so that I can call it for multiple uart configurations... however, the use of the string "myconsole" in the macros makes this impossible (resolved at compile time). Am I right in thinking there is no way to find nodes in the DTS dynamically (ie with the node label, name or alias only known at run time)?
thanks!
BrianW said:I'm still stuck with 1 point: I would like to pass the alias name ('myconsole') to the function so that I can call it for multiple uart configurations... however, the use of the string "myconsole" in the macros makes this impossible (resolved at compile time). Am I right in thinking there is no way to find nodes in the DTS dynamically (ie with the node label, name or alias only known at run time)?
I am a little confused. How is the alias defined at runtime? In Zephyr, everything on the Devicetree is defined in compile time. Dynamic update of the Devicetree is not supported.
It is not clear to me what "myconsole" is in your application. If it is the console that is printk() get routed to by default, then you could just use DT_CHOSEN(zephyr_console) like in my sample code.
In Zephyr, everything on the Devicetree is defined in compile time. Dynamic update of the Devicetree is not supported.
I think this is what I was not really grasping, the concept that the DTS is that part you update per board and is then static for that particular hardware. I was thinking of a 'config file' that would be dynamic and change at run time to suit the specific device environment (eg select different uart configs based on a device name..).