AT86RF231: Systematic TRX_UR interruption when trying to transmit packet

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

Hello,

 

I am currently facing an error, trying to use the AT86RF231 radio transceiver, that I just cannot understand.

 

I am trying to send 802.15.4 packets from a mote using an ARM Cortex-M3 MCU linked to this transceiver.

I have no problem accessing the transceiver by SPI, reading or setting its registers, or receiving packets: for the latter task, I react to TRX_END interrupts by reading the frame buffer.

 

But when trying to send a packet, the transmission systematically fails, and a TRX_UR interrupt is launched whenever I actually start the TX.

 

To be more precise, I proceed in two steps:

1. I load the transceiver frame buffer, using the following C function:

int at86rf231_load_tx_buf(
        netdev_802154_pkt_kind_t kind,
        netdev_802154_node_addr_t *dest,
        int use_long_addr,
        int wants_ack,
        netdev_hlist_t *upper_layer_hdrs,
        void *buf,
        unsigned int len)
{
    uint8_t mhr[24];
    uint8_t index = 3;

    /* frame type */
    switch (kind) {
        case NETDEV_802154_PKT_KIND_BEACON:
            mhr[0] = 0x00;
            break;
        case NETDEV_802154_PKT_KIND_DATA:
            mhr[0] = 0x01;
            break;
        case NETDEV_802154_PKT_KIND_ACK:
            mhr[0] = 0x02;
            break;
        default:
            return NETDEV_802154_TX_STATUS_INVALID_PARAM;
    }

    if (wants_ack) {
        mhr[0] |= 0x20;
    }

    wait_for_ack = wants_ack;

    uint16_t src_pan = at86rf231_get_pan();
    uint8_t compress_pan = 0;

    if (use_long_addr) {
        mhr[1] = 0xcc;
    }
    else {
        mhr[1] = 0x88;
        if (dest->pan.id == src_pan) {
            compress_pan = 1;
            mhr[0] |= 0x40;
        }
    }

    mhr[2] = sequence_nr++;

    /* First 3 bytes are fixed with FCS and SEQ, resume with index=3 */
    if (use_long_addr) {
        mhr[index++] = (uint8_t)(dest->long_addr & 0xFF);
        mhr[index++] = (uint8_t)(dest->long_addr >> 8);
        mhr[index++] = (uint8_t)(dest->long_addr >> 16);
        mhr[index++] = (uint8_t)(dest->long_addr >> 24);
        mhr[index++] = (uint8_t)(dest->long_addr >> 32);
        mhr[index++] = (uint8_t)(dest->long_addr >> 40);
        mhr[index++] = (uint8_t)(dest->long_addr >> 48);
        mhr[index++] = (uint8_t)(dest->long_addr >> 56);

        uint64_t src_long_addr = at86rf231_get_address_long();
        mhr[index++] = (uint8_t)(src_long_addr & 0xFF);
        mhr[index++] = (uint8_t)(src_long_addr >> 8);
        mhr[index++] = (uint8_t)(src_long_addr >> 16);
        mhr[index++] = (uint8_t)(src_long_addr >> 24);
        mhr[index++] = (uint8_t)(src_long_addr >> 32);
        mhr[index++] = (uint8_t)(src_long_addr >> 40);
        mhr[index++] = (uint8_t)(src_long_addr >> 48);
        mhr[index++] = (uint8_t)(src_long_addr >> 56);
    }
    else {
        mhr[index++] = (uint8_t)(dest->pan.id & 0xFF);
        mhr[index++] = (uint8_t)(dest->pan.id >> 8);

        mhr[index++] = (uint8_t)(dest->pan.addr & 0xFF);
        mhr[index++] = (uint8_t)(dest->pan.addr >> 8);

        if (!compress_pan) {
            mhr[index++] = (uint8_t)(src_pan & 0xFF);
            mhr[index++] = (uint8_t)(src_pan >> 8);
        }

        uint16_t src_addr = at86rf231_get_address();
        mhr[index++] = (uint8_t)(src_addr & 0xFF);
        mhr[index++] = (uint8_t)(src_addr >> 8);
    }

    /* total frame size:
     * index -> MAC header
     * len   -> payload length
     * 2     -> CRC bytes
     * + lengths of upper layers' headers */
    size_t size = index + len + 2;
    if (upper_layer_hdrs != NULL) {
        size += netdev_get_hlist_len(upper_layer_hdrs);
    }

    if (size > AT86RF231_MAX_PKT_LENGTH) {
        DEBUG("at86rf231: packet too long, dropped it.\n");
        return NETDEV_802154_TX_STATUS_PACKET_TOO_LONG;
    }

    /* change into transmission (PLL_ON) state */
    at86rf231_reg_write(AT86RF231_REG__TRX_STATE,
                        AT86RF231_TRX_STATE__PLL_ON);
    do {
        int max_wait = _MAX_RETRIES;
        if (!--max_wait) {
            DEBUG("at86rf231 : ERROR : could not enter PLL_ON mode\n");
            return NETDEV_802154_TX_STATUS_ERROR;
        }
    } while (at86rf231_current_mode() != AT86RF231_TRX_STATUS__PLL_ON);

    /* ...then go into TX_ARET_ON state if ACKnowledgement is needed */
    if (wait_for_ack) {
        at86rf231_reg_write(AT86RF231_REG__TRX_STATE,
                            AT86RF231_TRX_STATE__TX_ARET_ON);
        do {
            int max_wait = _MAX_RETRIES;
            if (!--max_wait) {
                DEBUG("at86rf231 : ERROR : could not enter TX_ARET_ON mode\n");
                return NETDEV_802154_TX_STATUS_ERROR;
            }
        } while (at86rf231_current_mode() != AT86RF231_TRX_STATUS__TX_ARET_ON);
    }

    /* load transceiver buffer (FIFO) */
    uint8_t size_byte = (uint8_t)size;
    netdev_hlist_t *ptr = upper_layer_hdrs;

    at86rf231_write_fifo(&size_byte, 1);
    at86rf231_write_fifo(mhr, (radio_packet_length_t)index);
    if (upper_layer_hdrs != NULL) {
        do {
            at86rf231_write_fifo(ptr->header,
                                (radio_packet_length_t)(ptr->header_len));
            netdev_hlist_advance(&ptr);
        } while (ptr != upper_layer_hdrs);
    }
    at86rf231_write_fifo((uint8_t*)buf, len);
    /* place 2 bytes in FIFO for checksum calculation;
       value is not important: these bytes will be overwritten by the correct
       FCS computed by the transceiver, but they MUST be present... */
    at86rf231_write_fifo(mhr, 2);

DEBUG("Frame buffer loaded with %d (%d + %d + 2) bytes.\n", size, index, len);
for (int i = 0; i < index; i++) {
    DEBUG(" %2.2x", mhr[i]);
}
DEBUG("\n");
for (int i = 0; i < len; i++) {
    DEBUG(" %2.2x", ((uint8_t*)buf)[i]);
}
DEBUG("\n");

    return NETDEV_802154_TX_STATUS_OK;
}

(This is actually part of a transceiver driver for a custom network stack.)

 

I use it to load a simple data packet with a 9-byte payload. The final DEBUG lines of the function output me the following:

Frame buffer loaded with 20 (9 + 9 + 2) bytes.
 41 88 00 34 12 ff ff 01 00
 ee 40 01 00 00 60 85 01 00

This function then executes without an error.

 

2. I actually starts the transmission, using this function:

int at86rf231_transmit_tx_buf(void)
{
    /* Start TX */
    at86rf231_reg_write(AT86RF231_REG__TRX_STATE, AT86RF231_TRX_STATE__TX_START);
/*    gpio_set(AT86RF231_SLEEP);*/
    DEBUG("at86rf231: Started TX\n");
/*    gpio_clear(AT86RF231_SLEEP);*/

    /* if no ACK is needed, the procedure is simple */
    if (!wait_for_ack) {
        DEBUG("at86rf231: Don't wait for ACK, TX done.\n");
        return NETDEV_802154_TX_STATUS_OK;
    }

    uint8_t trac_status;
    do {
        trac_status = at86rf231_reg_read(AT86RF231_REG__TRX_STATE);
        trac_status &= AT86RF231_TRX_STATE_MASK__TRAC;
    }
    while (trac_status == AT86RF231_TRX_STATE__TRAC_INVALID);

    switch (trac_status) {
        case AT86RF231_TRX_STATE__TRAC_CHANNEL_ACCESS_FAILURE:
            return NETDEV_802154_TX_STATUS_MEDIUM_BUSY;

        case AT86RF231_TRX_STATE__TRAC_NO_ACK:
            return NETDEV_802154_TX_STATUS_NOACK;

        default:
            return NETDEV_802154_TX_STATUS_OK;
    }
}

And that's where the problem occurs: as soon as TX is started (by writing TX_START to TX_STATE register), a TRX_UR interrupts immediately fires, and the packet is never actually emitted.

 

The utility functions called by these two above TX functions are:

enum at86rf231_access {
    AT86RF231_ACCESS_REG = 0x80,
    AT86RF231_ACCESS_FRAMEBUFFER = 0x20,
    AT86RF231_ACCESS_SRAM = 0x00,

    AT86RF231_ACCESS_READ = 0x00,
    AT86RF231_ACCESS_WRITE = 0x40,
};

void at86rf231_reg_write(uint8_t addr, uint8_t value)
{
    /* Acquire exclusive access to the bus. */
    spi_acquire(AT86RF231_SPI);
    /* Start the SPI transfer */
    gpio_clear(AT86RF231_CS);
    /* write to register */
    spi_transfer_reg(AT86RF231_SPI,
                     AT86RF231_ACCESS_REG | AT86RF231_ACCESS_WRITE | addr,
                     value,
                     0);
    /* End the SPI transfer */
    gpio_set(AT86RF231_CS);
    /* Release the bus for other threads. */
    spi_release(AT86RF231_SPI);
}

uint8_t at86rf231_reg_read(uint8_t addr)
{
    char value;

    /* Acquire exclusive access to the bus. */
    spi_acquire(AT86RF231_SPI);
    /* Start the SPI transfer */
    gpio_clear(AT86RF231_CS);
    /* read from register */
    spi_transfer_reg(AT86RF231_SPI,
                     AT86RF231_ACCESS_REG | AT86RF231_ACCESS_READ | addr,
                     0,
                     &value);
    /* End the SPI transfer */
    gpio_set(AT86RF231_CS);
    /* Release the bus for other threads. */
    spi_release(AT86RF231_SPI);
    return (uint8_t)value;
}

#define at86rf231_current_mode()  \
        (at86rf231_get_status() & AT86RF231_TRX_STATUS_MASK__TRX_STATUS)


void at86rf231_write_fifo(const uint8_t *data, radio_packet_length_t length)
{
    /* Acquire exclusive access to the bus. */
    spi_acquire(AT86RF231_SPI);
    /* Start the SPI transfer */
    gpio_clear(AT86RF231_CS);
    /* Send Frame Buffer Write access */
    spi_transfer_regs(AT86RF231_SPI,
                      AT86RF231_ACCESS_FRAMEBUFFER | AT86RF231_ACCESS_WRITE,
                      (char*)data,
                      0,
                      length);
    /* End the SPI transfer */
    gpio_set(AT86RF231_CS);
    /* Release the bus for other threads. */
    spi_release(AT86RF231_SPI);
}

 

I just cannot understand why this happens... I think I followed the procedure given in the AT86RF231 datasheet.

 

Any ideas or comments are thus warmly welcomed!

 

Thanks in advance,

This topic has a solution.

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:32 AM
This reply has been marked as the solution. 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Kevin,

you use several write_fifo() calls to write a single  frame - thats not what the datasheet suggests. Frames have to be written in one fifo access. Each time you pull the CS to low and issue frame write command, the fifo write pointer in the transceiver is reset to 0. So the first byte in the write_fifo() function will become the framelength.

 

Hth, Axel.

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

You are completely right: I changed the load_tx_buf() function to write the frame in an unique buffer access, and that solved my problem.

 

Thank you very much, Axel!

 

 

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