-
Notifications
You must be signed in to change notification settings - Fork 1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Question] Wake up from IRQ on RP2040? #857
Comments
I'm not familiar with the pico SDK's sleep functionality. We have a IRQ pico example, but it only uses interrupts for the TX side because there was problems (plural) in the RX side. RF24/examples_pico/interruptConfigure.cpp Lines 75 to 81 in 92f4a11
I'll have to research some things in the picoSDK first, specifically how to sleep/wake the RP2040 and how to wake the RP2040 via a ISR. |
I've tested with shorting the gpio manually and it seems to work, but not with the NRF (yet) sleep_run_from_xosc();
sleep_goto_dormant_until_pin(0, 0, false); // Wait till pin 0 is low. |
Does the power to radio get cut when the RP2040 goes to sleep? That would be a problem. |
@2bndy5 Nope, the radio stays powered on all the time. |
I found one issue. I was using radio.maskIRQ(0,0,1) to get the RX interrupt. But it should be maskIRQ(1,1,0). |
/*! \brief Send system to sleep until the specified GPIO changes
* \ingroup hardware_sleep
*
* One of the sleep_run_* functions must be called prior to this call
*
* \param gpio_pin The pin to provide the wake up
* \param edge true for leading edge, false for trailing edge
* \param high true for active high, false for active low
*/
void sleep_goto_dormant_until_pin(uint gpio_pin, bool edge, bool high); I think edge needs to be |
Unfortunately same issue. I also tried to call radio.whatHappened() after wakeup in order to clean the interrupt flag, but this also didn't help. |
I'm starting to think disabling the clock might have a negative impact. My primary concern is with using the USB UART. From the pico-extras sleep example: // UART will be reconfigured by sleep_run_from_xosc
sleep_run_from_xosc();
printf("Running from XOSC\n");
uart_default_tx_wait_blocking();
printf("XOSC going dormant\n");
uart_default_tx_wait_blocking(); and a note directly from sleep.c: // The difference between sleep and dormant is that ALL clocks are stopped in dormant mode,
// until the source (either xosc or rosc) is started again by an external event.
// In sleep mode some clocks can be left running controlled by the SLEEP_EN registers in the clocks
// block. For example you could keep clk_rtc running. Some destinations (proc0 and proc1 wakeup logic)
// can't be stopped in sleep mode otherwise there wouldn't be enough logic to wake up again. I don't think the SPI SCK needs to be running when the radio's CSN is LOW, but this is my secondary concern. |
Found a couple notes calling the pico_sleep lib "WIP" and "not yet stable". IDK if that has anything to do with this issue. In developing the PicoSDK support, we really didn't get into testing with the pico-extras libs, so I'm in new territory here. Originally, my intention was to just use the IRQ functions in the PicoSDK, but your app is taking a different approach. |
I will hook up my logic analyser and try to figure out what could be going wrong. |
BTW, you don't need to re-post my entire comment in reply. |
Is everything before that when the RP2040 is awake? Am I looking at the interrupt after |
I've figured out the deepsleep waking actually works when tying that gpio to gnd (and without nrf24). |
IRQ going active LOW shouldn't be a pulse. It should remain LOW until |
Alternatively, Lines 1542 to 1550 in 92f4a11
|
Even if not calling read() or what_happened(), the IRQ is just a short pulse and goes to high. |
No idea why it is only a pulse. From you pic, I think Turning off hysteresis might be causing the RP2040 to not "see" the pulse. See picoSDK docs about |
For reference, the current code is: |
I did some more testing today and weirdly enough, it received always 3 packets after wakeup (no matter in which interval) and then becomes unreachable. Edit: Sometimes it is 4. |
That just means the radio's RX FIFO is full (max capacity is 3 payloads). |
I tried now also flushing the RX/TX before going to sleep. I tried dormant mode with a bare (no RF24) project and it seems to work properly with active low. I am calling whatHappened() after waking up again, but somehow have the feeling that the interrupt doesn't get cleared because the IRQ gpio stays low. |
Wait, do mean it is waking up via interrupt now? Or are you still manually pulling the pin low? Nonetheless, if the radio's RX FIFO is full, then any incoming transmission is ignored. If it received 4 payloads before blocking, then something must have fetched/cleared a single payload from the RX FIFO.
The RX FIFO is an independent stack from the TX FIFO. The TX FIFO is only used in RX mode if you're using custom Ack payloads in the AutoAck'd packets.
Are you also checking the flags' states? That function was specifically meant to inform the app what triggered the IRQ. If the tx_ok or tx_fail flags are triggered, then there is something else going on (usually with ACK payloads), and the
At least it isn't a pulse anymore. we're closer to expected behavior there. I think you can fool the lib into clearing the RX_DR flag by calling |
I tried to manually wake up the RP2040 by a low gpio to verify the sleep is working properly. My IRQ mask is set to 1, 1, 0, so it should be only getting the RX_DR. Current code with your recommended changes: #include "common.h"
#include "pico/sleep.h"
#include "hardware/clocks.h"
#include "hardware/rosc.h"
#include "hardware/structs/scb.h"
#include "hardware/structs/watchdog.h"
#include "hardware/structs/psm.h"
#include "hardware/regs/psm.h"
#include "hardware/pll.h"
#include "hardware/xosc.h"
#include "pico/stdlib.h" // printf(), sleep_ms(), getchar_timeout_us(), to_us_since_boot(), get_absolute_time()
#include "pico/bootrom.h" // reset_usb_boot()
//#include <tusb.h> // tud_cdc_connected()
#include <RF24.h> // RF24 radio object
RF24 radio(1, 5);
// on every successful transmission
uint8_t payload[4] = {0x23, 0x42, 0x05, 0x00};
uint8_t address[][6] = {"1Node", "2Node", "3Node", "4Node", "5Node", "6Node"};
#ifdef __cplusplus
extern "C"
{
#endif
void recover_from_sleep(uint scb_orig, uint clock0_orig, uint clock1_orig)
{
// Re-enable ring Oscillator control
rosc_write(&rosc_hw->ctrl, ROSC_CTRL_ENABLE_LSB);
// reset procs back to default
scb_hw->scr = scb_orig;
clocks_hw->sleep_en0 = clock0_orig;
clocks_hw->sleep_en1 = clock1_orig;
return;
}
void setup_radio()
{
if (!radio.begin())
{
printf("radio hardware is not responding!!\n");
}
else
{
printf("radio hardware is responding!!\n");
}
radio.setDataRate(RF24_250KBPS);
radio.setChannel(110);
radio.maskIRQ(1, 1, 0);
// Set the PA Level low to try preventing power supply related problems
// because these examples are likely run with nodes in close proximity to
// each other.
radio.setPALevel(RF24_PA_MIN); // RF24_PA_MAX is default.
// save on transmission time by setting the radio to only transmit the
// number of bytes we need to transmit a float
radio.setPayloadSize(sizeof(payload) / sizeof(uint8_t)); // float datatype occupies 4 bytes
radio.enableDynamicPayloads();
printf("Payload size is %d\n", sizeof(payload) / sizeof(uint8_t));
// set the TX address of the RX node into the TX pipe
radio.openWritingPipe(address[0]); // always uses pipe 0
// set the RX address of the TX node into a RX pipe
radio.openReadingPipe(1, address[1]); // using pipe
radio.printDetails(); // (smaller) function that prints raw register values
radio.printPrettyDetails(); // (larger) function that prints human readable data
radio.startListening();
}
int main(void)
{
/*
vreg_set_voltage(VREG_VOLTAGE_0_95);
// set_sys_clock_48mhz();
// Change clk_sys to be 48MHz. The simplest way is to take this from PLL_USB
// which has a source frequency of 48MHz
clock_configure(clk_sys,
CLOCKS_CLK_SYS_CTRL_SRC_VALUE_CLKSRC_CLK_SYS_AUX,
CLOCKS_CLK_SYS_CTRL_AUXSRC_VALUE_CLKSRC_PLL_USB,
48 * MHZ,
48 * MHZ);
// Turn off PLL sys for good measure
pll_deinit(pll_sys);
// CLK peri is clocked from clk_sys so need to change clk_peri's freq
clock_configure(clk_peri,
0,
CLOCKS_CLK_PERI_CTRL_AUXSRC_VALUE_CLK_SYS,
48 * MHZ,
48 * MHZ);
*/
stdio_init_all();
// initialize the transceiver on the SPI bus
setup_radio();
// Wake Up GPIO
gpio_init(14);
gpio_set_dir(14, GPIO_IN);
// Debug LED
gpio_init(15);
gpio_set_dir(15, GPIO_OUT);
gpio_put(15, 1);
sleep_ms(1000);
// save values for later
uint scb_orig = scb_hw->scr;
uint clock0_orig = clocks_hw->sleep_en0;
uint clock1_orig = clocks_hw->sleep_en1;
// This works and sees interrupts, but dormant mode doesn't
// gpio_set_irq_enabled_with_callback(14, GPIO_IRQ_EDGE_FALL, 1, gpio_callback);
while (1)
{
// blink led on pin 15 to check if we are still running
gpio_put(15, 1);
sleep_ms(50);
gpio_put(15, 0);
sleep_ms(50);
if (radio.available())
{
uint8_t payloadSize = radio.getDynamicPayloadSize();
uint8_t payload[payloadSize];
radio.read(&payload, payloadSize);
printf("Payload size: %i\n", payloadSize);
#ifdef DEBUG_PAYLOAD
for (int i = 0; i < payloadSize; i++)
{
printf("%02X ", payload[i]);
}
printf("\n");
#endif
}
sleep_run_from_xosc();
// 14, true, false doesn't trigger the wakeup
sleep_goto_dormant_until_pin(14, false, false); // Wait till pin 14 is low.
recover_from_sleep(scb_orig, clock0_orig, clock1_orig);
// dummy read to clear interrupt flag in nrf24l01
uint8_t buf[1];
radio.read(buf, 0);
}
}
#ifdef __cplusplus
}
#endif |
|
It might be better to use |
I think sleep now actually works after removing sleep_run_from_xosc() and changing sleep_goto_dormant_until_pin(14, true, false); It's a bit hard to debug, but my led flashes and then stays off till another packet is received, so it seems as if the MCU sleeps. |
Its possible that |
If we get this working, I would be inclined to include this in the examples_pico for this lib. I think this approach would be very desirable for other's projects. And, we'd also have a reason to use the pico-extras libs in our CI as well. BTW, I'm very big on using explanatory comments when specific to the example's application. |
I am currently having trouble with RF24 to get the RP2040 to wake up from dormant mode.
It sucessfully received one packet and then stops receiving. Only resetting the MCU makes it receive another packet.
The text was updated successfully, but these errors were encountered: