AT86RF231: Correct way to put transceiver into and out of sleep mode

Go To Last Post
11 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hello,

 

While working on a device equipped with the AT86RF231 transceiver, I face a strange and disrupting behavior.

After I have put the transceiver to sleep mode, then back in reception mode, I am just not able receive transceiver-related IRQs any more. It means I can't receive packets, since the RX_START and (more importantly) TRX_END interrupts, which allow me to know when to read the transceiver's frame buffer, have disappeared.

 

After some tests, this problem happens only when I turn the transceiver into and out of sleep mode: if I leave the transceiver constantly "on", the IRQs continue to work normally.

 

Consequently, I am posting hereafter the C functions I use to turn the trasceiver into and out of sleep mode, in the hope that someone can tell me what's wrong with them.

 

To put the transceiver in sleep mode ("turn off"):

void at86rf231_off(void)
{
    /* Send a FORCE TRX OFF command */
    at86rf231_reg_write(AT86RF231_REG__TRX_STATE,
                        AT86RF231_TRX_STATE__FORCE_TRX_OFF);

    /* busy wait for TRX_OFF state */
    do {
        int delay = _MAX_RETRIES;
        if (!--delay) {
            FATAL("at86rf231 : ERROR : could not enter TRX_OFF mode\n");
            return;
        }
    } while (at86rf231_current_mode() != AT86RF231_TRX_STATUS__TRX_OFF);

    /* assert SLP_TR line */
    gpio_set(AT86RF231_SLEEP);

    at86rf231_active = false;
    DEBUG("AT86RF231 transceiver goes off.\n");
}

To get the transceiver out of sleep mode ("on"):

int at86rf231_on(void)
{
    /* negate SLP_TR line */
    gpio_clear(AT86RF231_SLEEP);

    /* busy wait for TRX_OFF state */
    do {
        int delay = _MAX_RETRIES;
        if (!--delay) {
            FATAL("at86rf231 : ERROR : could not wake up transceiver!\n");
            return 0;
        }
    } while (at86rf231_current_mode() != AT86RF231_TRX_STATUS__TRX_OFF);

    at86rf231_active = true;

    /* change into reception mode */
    at86rf231_switch_to_rx();

    DEBUG("AT86RF231 transceiver is up and running.\n");

    return 1;
}

void at86rf231_switch_to_rx(void)
{
    gpio_irq_disable(AT86RF231_INT);

    /* now change to PLL_ON state for extended operating mode */
    at86rf231_reg_write(AT86RF231_REG__TRX_STATE,
                        AT86RF231_TRX_STATE__PLL_ON);

    do {
        int max_wait = _MAX_RETRIES;
        if (!--max_wait) {
            FATAL("at86rf231 : ERROR : could not enter PLL_ON mode\n");
            break;
        }
    } while (at86rf231_current_mode() != AT86RF231_TRX_STATUS__PLL_ON);

    /* Read IRQ status register to clear it */
    at86rf231_reg_read(AT86RF231_REG__IRQ_STATUS);

    /* Enable IRQ interrupt */
    gpio_irq_enable(AT86RF231_INT);

    /* Enter RX_AACK_ON state */
    at86rf231_reg_write(AT86RF231_REG__TRX_STATE,
                        AT86RF231_TRX_STATE__RX_AACK_ON);

    do {
        int max_wait = _MAX_RETRIES;
        if (!--max_wait) {
            FATAL("at86rf231 : ERROR : could not enter RX_AACK_ON mode\n");
            break;
        }
    } while (at86rf231_current_mode() != AT86RF231_TRX_STATUS__RX_AACK_ON);
}

These functions use the following utility code:

#define _MAX_RETRIES    (100)

static volatile bool at86rf231_active;


uint8_t at86rf231_current_mode(void)
{
    return (at86rf231_reg_read(AT86RF231_REG__TRX_STATUS)
            & AT86RF231_TRX_STATUS_MASK__TRX_STATUS);
}

I don't understand why transceiver interrupts do not fire anymore when going out of sleep mode, since the contents of AT86RF231 registers are supposed to preserved in SLEEP state, according to datasheet.

 

Any ideas or comments are warmly welcomed!

 

Thanks in advance,

Kévin Roussel
Doctorant, projet LAR
Équipe MADYNES, INRIA Nancy Grand-Est / LORIA
Tél. : +33 3 54 95 86 27
Kevin.Roussel@inria.fr

Last Edited: Fri. Oct 16, 2015 - 12:29 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

There is nothing obviously wrong with the code, so it is hard to say what might be wrong.

NOTE: I no longer actively read this forum. Please ask your question on www.eevblog.com/forum if you want my answer.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

rousselk wrote:
the RX_START and (more importantly) TRX_END interrupts, which allow me to know when to read the transceiver's frame buffer, have disappeared.

Do you mean the transceiver ceases to issue them, or your code ceases to recognise them?

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I suggest doing the timed wakeup delay before trying to access any of the SPI registers (or even 2x that time to begin with). Also, if the MCU resets while the radio is sleeping it may do the initialize sequence with wrong timings (using e.g. power on delay instead of the longer wake from sleep delay) which can lead to strange radio behavior.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hello awneil, and thanks for your attention,

 

After new tests, I discovered that the transceiver ceases to issue interrupts, because the IRQ_MASK register of the AT86RF231 is reset to 0!

 

This is quite a surprise to me, since the AT86RF231 datasheet asserts that "During SLEEP the register contents remains valid" (section 7.1.2.2 page 35).

I am certain that the MCU is not reset during the timespan of my tests, nor is transceiver powered of or reset (via its /RESET pin).

 

Does anybody have an idea on the reason why such a register reset could happen?

 

Kévin Roussel
Doctorant, projet LAR
Équipe MADYNES, INRIA Nancy Grand-Est / LORIA
Tél. : +33 3 54 95 86 27
Kevin.Roussel@inria.fr

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hello dak664,

 

I am certain that the MCU is not reset during the execution of my tests.

 

However, if accessing the SPI registers too early during wakeup can provoke this strange register reset, it may explain my problem.

I'll try to delay my interrogation of the TRX_STATUS register in my at86rf231_on() function, and see if it solves my problem.

 

Thanks for your suggestions.

 

Kévin Roussel
Doctorant, projet LAR
Équipe MADYNES, INRIA Nancy Grand-Est / LORIA
Tél. : +33 3 54 95 86 27
Kevin.Roussel@inria.fr

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

Is this the only register that is corrupt? Try to set a known value into one of the registers that has all 8 bit writable (like CSMA SEED) and check back on wakeup.

 

Sleep should not affect register values.

NOTE: I no longer actively read this forum. Please ask your question on www.eevblog.com/forum if you want my answer.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

It doesn't seem that other registers are affected.

 

Moreover, adding delays to follow the transition delays shown in datasheet doesn't seem to change anything in my case...

 

To be honest, I just don't understand anything... Is there by any chance a working example of using "extended modes" (i.e.: RX_AAXK and TX_ARET) of the AT86RF231 radio transceiver?

Kévin Roussel
Doctorant, projet LAR
Équipe MADYNES, INRIA Nancy Grand-Est / LORIA
Tél. : +33 3 54 95 86 27
Kevin.Roussel@inria.fr

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The delay variable is in the scope of the inner loop, so never times out?

Suggest have the error message report the actual mode, that should give a clue where to look further.

    do {
        int delay = _MAX_RETRIES;
        if (!--delay) {
            FATAL("at86rf231 : ERROR : could not enter TRX_OFF mode\n");
            return;
        }
    } while (at86rf231_current_mode() != AT86RF231_TRX_STATUS__TRX_OFF);
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Here are my ideas for hunting down the issue.

 

a) Does any of the "FATAL()" messages appear? If yes, which one?

 

b) Can you verify if the PLL_LOCK_IRQ appears, when you change from TRX_OFF to PLL_ON?

 

(If the PLL does not lock, there is also no frame reception possible.)

 

Axel.

 

 

 

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hello everyone,

 

@dak664: I inherited this portion of code when I began to work on this piece of code. I indeed find this construction strange, and changed it to a clearer alternative:

int delay;

[...]
{
    int delay = _MAX_RETRIES;
    do {
        if (!--delay) {
            FATAL("at86rf231: ERROR, could not enter TRX_OFF mode\n");
        }
    } while (at86rf231_current_mode() != AT86RF231_TRX_STATUS__TRX_OFF);
}

... but that did not change anything to the situation.

 

@uracolix: no FATAL message is triggered, there is no timeout. I didn't monitor PLL_LOCK interrupt, but since I manage to send packets without error, I think the PLL manages to lock correctly.

 

 

To be honest, I have solved my problem by setting the correct value in the IRQ_MASK register every time I get out of sleep mode.

 

I kept waiting the prescribed delays when changing states (i.e.: 380 µs when going out of sleep mode, 330 µs at reset/power-on) to stay on the safe side.

 

I just do not really understand what caused this strange behavior, but I haven't really got time to investigate further...

 

Thanks to everyone who took the time to answer this thread.

Kévin Roussel
Doctorant, projet LAR
Équipe MADYNES, INRIA Nancy Grand-Est / LORIA
Tél. : +33 3 54 95 86 27
Kevin.Roussel@inria.fr