Skip to content

Conversation

@fabian18
Copy link
Contributor

Contribution description

The dio2 and dio3 pins of sx126x are optional. They can be used as IRQ pins. dio2 can be used as RF switch pin and dio3 can be used to feed 32MHz to a Temperature-Compensated Crystal Oscillator (TCXO). Those are special hardware configurations.
Read:
dio2
dio3

By default if the modules sx126x_dio2 and sx126x_dio3 are used, the pins are assumed to be interrupt pins.

You can use the dio2 RF switch like this:

#define SX126X_DIO2_MODE             SX126X_DIO2_RF_SWITCH
#define SX126X_PARAM_DIO2_ARG        { .rf_switch_pin = GPIO_UNDEF }

You can use a TCX0 on dio3 like this:

#define SX126X_PARAM_DIO3_MODE             SX126X_DIO3_TCX0
#define SX126X_PARAM_DIO3_ARG              { .tcxo_volt = SX126X_COMMON_PARAM_TCXO_VOLTAGE, \
                                             .tcx0_timeout = SX126X_COMMON_PARAM_TCXO_TIMEOUT }

Testing procedure

You need special hardware to test this, which is using a TCXO. We have that.

USEMODULE += sx126x_dio2 sx126xdio3 make -C examples/networking/gnrc/gnrc_lorawan flash term

If you have the hardware and the chip runs, then it worked.

Issues/PRs references

Splitout of a splitout #21675

@github-actions github-actions bot added Area: drivers Area: Device drivers Area: LoRa Area: LoRa radio support labels Aug 25, 2025
@fabian18 fabian18 added the CI: ready for build If set, CI server will compile all applications for all available boards for the labeled PR label Aug 25, 2025
@riot-ci
Copy link

riot-ci commented Aug 25, 2025

Murdock results

✔️ PASSED

e15a65e drivers/sx126x: add support for Dio2 and Dio3

Success Failures Total Runtime
10535 0 10536 17m:35s

Artifacts

@kfessel
Copy link
Contributor

kfessel commented Aug 25, 2025

CI seems to be missing a header for atmega256rfr2-xpro board

sx126x_driver.h

https://ci.riot-os.org/results/22b9170445c940af95b0558ce4563086/output/builds/tests/drivers/sx126x/atmega256rfr2-xpro:gnu.txt

Copy link
Contributor

@kfessel kfessel left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks good to me would trust the author to have tested

Copy link
Contributor

@kfessel kfessel left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i had another look.

specifically at the GPIO configuration, I think GPIO_UNDEF the most likely usecase for the gpio field that the GPIO configuration thing ( gpio_init_int )could just be left out of this PR without inflicting on the purpose of this PR.

I think the GPIO configuration and its callback is also very likely to be untested for a long time since it such an unusual board configuration.

Comment on lines 202 to 203
res = gpio_init_int(dev->params->u_dio2_arg.dio2_pin, GPIO_IN, GPIO_RISING,
dev->event_cb, dev->event_arg);
Copy link
Contributor

@kfessel kfessel Aug 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dev->event_cb seems not to exist (couldn't find it in struct sx126x in drivers/include/sx126x.h)

Copy link
Contributor

@kfessel kfessel Aug 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe cb-function and arg could be part of the params

but please consider leaving this whole gpio_init thing out first

Comment on lines 225 to 226
res = gpio_init_int(dev->params->u_dio3_arg.dio3_pin, GPIO_IN, GPIO_RISING,
dev->event_cb, dev->event_arg);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

directing two pins to the same cb does n't make much sense

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am going to add _dio2_isr and _dio2_isr

Copy link
Contributor

@kfessel kfessel Aug 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

with function will _dio2_isr and _dio3_isr provide` what will be their purpose (afaik dio1 usually handles all the irq)

leaving out the gpio init would make much sense imao

Copy link
Contributor

@kfessel kfessel Aug 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh i see _dio2 and _dio3 trigger the same netdev event -> not reasonable

makes the different dio triggers indistinguishable.

or are we some how able to process two netdev irq events at the same time or would this provide another advantage to have some irq go to dio1 and some to dio2 and then use the same function to ask the sx.. what happend.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you doubting in general that it is reasonable to have 3 IRQ pins for distinguished events? I would provide this because it is possible, and maybe someone can make use of it. What if the dio2 and dio3 ISR is a weak function?
RIOT consumes the interrupts on dio1. An application could get the events through dio2 or dio3, for example to count CRC errors or whatever.

Copy link
Contributor

@kfessel kfessel Aug 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RIOT consumes the interrupts on dio1. An application could get the events through dio2 or dio3, for example to count CRC errors or whatever.

this does not work as you intend if the application consumes the irq riot does not get it and the other way around the one who gets it first will cosume it an then its gone even if you have different gpio irq

so if the application get the crc error riot will not receive that irq only it the application doe not consume but then the gpio might stay in its triggered state

you are implementing a feature that you do not need and did not test and its unlikely to be tested by someone else

remember KISS

@crasbe crasbe added the Type: new feature The issue requests / The PR implemements a new feature for RIOT label Aug 26, 2025
Comment on lines 179 to 200
#define SX126X_DIO2_MODE .dio2_mode = SX126X_PARAM_DIO2_MODE,
/**
* @brief DIO2 pin argument
*/
#define SX126X_DIO2_PIN .dio2_arg = SX126X_PARAM_DIO2_ARG,
#else
#define SX126X_DIO2_MODE
#define SX126X_DIO2_PIN
#endif

#if IS_USED(MODULE_SX126X_DIO3) || defined(DOXYGEN)
/**
* @brief DIO3 pin mode
*/
#define SX126X_DIO3_MODE .dio3_mode = SX126X_PARAM_DIO3_MODE,
/**
* @brief DIO3 pin argument
*/
#define SX126X_DIO3_PIN .u_dio3_arg = SX126X_PARAM_DIO3_ARG,
#else
#define SX126X_DIO3_MODE
#define SX126X_DIO3_PIN
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
#define SX126X_DIO2_MODE .dio2_mode = SX126X_PARAM_DIO2_MODE,
/**
* @brief DIO2 pin argument
*/
#define SX126X_DIO2_PIN .dio2_arg = SX126X_PARAM_DIO2_ARG,
#else
#define SX126X_DIO2_MODE
#define SX126X_DIO2_PIN
#endif
#if IS_USED(MODULE_SX126X_DIO3) || defined(DOXYGEN)
/**
* @brief DIO3 pin mode
*/
#define SX126X_DIO3_MODE .dio3_mode = SX126X_PARAM_DIO3_MODE,
/**
* @brief DIO3 pin argument
*/
#define SX126X_DIO3_PIN .u_dio3_arg = SX126X_PARAM_DIO3_ARG,
#else
#define SX126X_DIO3_MODE
#define SX126X_DIO3_PIN
# define SX126X_DIO2_MODE .dio2_mode = SX126X_PARAM_DIO2_MODE,
/**
* @brief DIO2 pin argument
*/
# define SX126X_DIO2_PIN .dio2_arg = SX126X_PARAM_DIO2_ARG,
#else
# define SX126X_DIO2_MODE
# define SX126X_DIO2_PIN
#endif
#if IS_USED(MODULE_SX126X_DIO3) || defined(DOXYGEN)
/**
* @brief DIO3 pin mode
*/
# define SX126X_DIO3_MODE .dio3_mode = SX126X_PARAM_DIO3_MODE,
/**
* @brief DIO3 pin argument
*/
# define SX126X_DIO3_PIN .u_dio3_arg = SX126X_PARAM_DIO3_ARG,
#else
# define SX126X_DIO3_MODE
# define SX126X_DIO3_PIN

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Although these definitions feel a bit redundant. Are they used somewhere else except for the SX126X_PARMAS structure defintion?

You could use the same #if IS_USED(MODULE_SX126X_DIO3) approach for lines 212 to 217 instead of creating and documenting the single use defines.
See the next comment.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also think this would make it easier to read

Comment on lines 216 to 217
SX126X_DIO3_MODE \
SX126X_DIO3_PIN \
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
SX126X_DIO3_MODE \
SX126X_DIO3_PIN \
#if IS_USED(MODULE_SX126X_DIO3)
.dio3_mode = SX126X_PARAM_DIO3_MODE, \
.u_dio3_arg = SX126X_PARAM_DIO3_ARG, \
#endif

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same for DIO2 of course.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

an #if within a #define is not allowed, hence the seemingly redundant defines.
I tried this several times before.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another thought I had: can't you leave all the fields in the struct unconditionally? The compiler will probably optimize the unused fields away anyway.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a quick google tells me compilers do not optimize struct members away. Conditional struct members are commonly used

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also think this would make it easier to read

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sadly this does not work for the broken line \ the #if' is in the same line as the #define' -> not at the beginning
-> cant fix readbility that way


/* Once the command SetDIO3AsTCXOCtrl(...) is sent to the device,
the register controlling the internal cap on XTA will be automatically
changed to 0x2F (33.4 pF) to filter any spurious which could occur
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

any spurious what? I think there are is missing a word.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Screenshot from 2025-08-26 13-26-50 LOL

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

spurious injection might be that

sx126x_irq_mask_t irq_mask;
sx126x_get_irq_status(sx126x, &irq_mask);
irq_mask &= sx126x->params->dio2_arg.dio2_irq_mask; /* only process events from DIO2 */
sx126x_clear_irq_status(sx126x, irq_mask);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this will clear the interrupts also for dio1 (and 3) -> if the same irq is enabled for both dios , which is very likely since dio1 catches all

which ISR works as intended is dependent on how the mcu prioritized differed pin IRQ ( good case ) or the pcb signal propagation delay (track capacitance, length, drive strength by IC lottery) of the dio1 vs dio2 net

same for dio3

Copy link
Contributor Author

@fabian18 fabian18 Aug 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The current only works with distinguished events on DIO pins.
But: "One IRQ can be mapped to all DIOs, one DIO can be mapped to all IRQs"
I would have preferred to make it work also for overlapping interrupts.
... I will drop it now

@fabian18 fabian18 force-pushed the pr/sx126x_dio2_dio3 branch from e03c07c to ae40562 Compare August 27, 2025 11:38
Copy link
Contributor

@kfessel kfessel left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please squash

@fabian18 fabian18 force-pushed the pr/sx126x_dio2_dio3 branch from ae40562 to e15a65e Compare August 27, 2025 11:46
@fabian18 fabian18 added this pull request to the merge queue Aug 27, 2025
Merged via the queue into RIOT-OS:master with commit 3eee368 Aug 27, 2025
27 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Area: drivers Area: Device drivers Area: LoRa Area: LoRa radio support CI: ready for build If set, CI server will compile all applications for all available boards for the labeled PR Type: new feature The issue requests / The PR implemements a new feature for RIOT

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants