Force timer buffer into advertisement manufacturer data at time of transmit.

Is there a way to modify the PHY layer, or SoftDevice, so that a timestamp is immediately read from a timer and forced into the manufacturer data right before transmission? I am looking for ways to timestamp manufacturing data with a known and unchanging delay small delay. 

Ultimately I would want the advertising data to look directly at the hardware counter during it's transmission, so that whenever a transmit occurs it must send whatever contents are in the counter buffer.

The device must be a beacon (no connection is allowed for synchronization), and must asynchronously send timestamps without any delay through the application layers.

I am aware that the reception (RX) of this will have it's own delay but I am not worried about that right now. I just want to solve the first problem above first.

Thanks

  • Hello,

    Please correct me if I'm wrong, but I assume you are working with the nRF5 SDK since you mentioned using the SoftDevice. The buffer(s) containing the advertisement payload are kept in the application and cannot be altered by the SoftDevice. This means you also need to update the advertisement data from the application.

    The SoftDevice includes a feature called Radio Notification, which allows you to configure the SoftDevice to trigger a callback prior to every advertisement event. You could use this to update your payload with a new TIMER value.

    Best regards,

    Vidar

  • You may not be understanding.

    I update the manufacturing data in the advertisement payload every 0.25 seconds using the application. Here I can also update a time stamp, using the application.

    However there is a delay between application updates and radio transmission.

    What I want is to edit the radio code to pull a counter value right before each advertisement transmission. I am hoping for sub millisecond delays.

    I do not want call backs, and I do not want to involve the app with updating this part of the advertisement data. I want it to be autonomous outside of the application.

    i saw examples of synced timing. I don’t want this because I will never be opening a connection. This is a transmit only application.

    there must be a buffer somewhere that is copied to the radio modulator, and this is what I want access to. It is a hack but it what I want to do. 
    thanks

  • RADIO->PACKETPTR is the ticket, I think. It may not be allowed by the softdevice to change the pointer to a different buffer, but the buffer itself could be edited just-in-time. Perhaps use debugger to examine the buffer to verify the location of the timestamp. There is no true DMA on the nRF52 to non-RAM memory, otherwise the register address could be modified by PPI.

        /* Set packet pointer */
        NRF_RADIO->PACKETPTR = (uint32_t) &m_adv_pdu[0];

    RW PACKETPTR Packet pointer Packet address to be used for the next transmission or reception. When transmitting, the packet pointed to by this address will be transmitted and when receiving, the received packet will be written to this address. This address is a byte aligned ram address.

  • In case this of use, the data sent over-the-air is subtly different from the data stored in RAM:

    // BLE Advertising Packet
    // ======================
    // Every packet begins with an 8 bit preamble, an alternating binary sequence. This is followed by a 32 bit access
    // address (AA) which can be thought of as a unique identifier which defines a particular connection. When a
    // device (master or slave) transmits on an advertising channel it uses a fixed value of 0x8e89bed6 as the access
    // address.
    // Following the 32 bit access address are optional S0, LENGTH and S1 fields then a variable length Protocol Data
    // Unit (PDU) which contains the message data payload. Finally all packets end with a 24 bit CRC.
    // By default, the Nordic BLE address is derived from the NRF_FICR->DEVICEADDR[] and is 48 bits in length (6 bytes)
    // If S0, LENGTH or S1 are specified with zero length their fields will be omitted in memory, otherwise each
    // field will be represented as a separate byte, regardless of the number of bits in their on-air counterpart
    // For the field sizes defined in bits, the occupation in RAM will always be rounded up to the next full byte size
    // (for instance 3 bit length will allocate 1 byte in RAM, 9 bit length will allocate 2 bytes, etc.).
    // On-air packet layout (Note Header in RAM is 3 bytes if S0INC is '1')
    // +-------------------------------------------------------------------------------------------------------------------------+
    // |                                         BLE Advertising Packet - Legacy                                                 |
    // +-------------------------------------------------------------------------------------------------------------------------+
    // |                                         1MHz: 47 octets max total length                                                |
    // +--------+-------+------------------------------------------------------------------------------------------+------+------+
    // |        |Access |                                                                                          | Add  |      |
    // |Preamble|Address|                   Protocol Data Unit PDU                                                 | On   | CRCC | Tone Ext
    // +--------+-------+------------------------------------------------------------------------------------------+------+------+
    // |   1(2) |   4   |                                    2 - 39                                                | (0)  |  3   | 16 - 160uSec
    // |        |       +-----------------+------------------------------------------------------------------------+      |      |
    // |        |       |    Header       |                  Payload                                               |      |      |
    // |        |       +-----------------+------------------------------------------------------------------------+      |      |
    // |        |       |       2         |                  0 - 37                                                |      |      |
    // |        |       +-----+-----+-----+------------------------------------------------------------------------+      |      |
    // |        |       | Opt | Opt | Opt |  MAC Addr  |     AdvData List (example 3 fields)                       |      |      |
    // |        |       | S0  | Len | S1  +------------+-----------------------------------------------------------+      |      |
    // |        |       |-----+-----+-----|    6       |     0 - 31                                                |      |      |
    // |        |       | (1) | (1) | (1) |            +-------------------+-------------------+-------------------+      |      |
    // |        |       |     |     |     |            |    AdvData Flags  |    AdvData Name   |   (AdvData Data)  |      |      |
    // |        |       |     |     |     |            +-------------------+-------------------+-------------------+      |      |
    // |        |       |     |     |     |            |        3          |          6        |       0 - 22      |      |      |
    // |        |       |     |     |     |            +-----+------+------+-----+------+------+-----+------+------+      |      |
    // |        |       |     |     |     |            | Len | Type | Data | Len | Type | Data | Len | Type | Data |      |      |
    // |        |       |     |     |     |            +-----+------+------+-----+------+------+-----+------+------+      |      |
    // |        |       |     |     |     |            |  1  |  1   |  1   |  1  |  1   |  4   |  1  |  1   |  20  |      |      |
    // |        |       |     |     |     |            |     |      |      |     |      |      |     |      |      |      |      |
    // |  1(2)  |  4    | (1) | (1) | (1) |   6        |  1  |  1   |  1   |  1  |  1   |  4   |  1  |  1   |  20  | (0)  |  3   |
    // +--------+-------+-----+-----+-----+------------+-----+------+------+-----+------+------+-----+------+------+------+------+
    //         0x8E89BED6     |     |      \
    //                /       |     |       \
    //               /  RAM uses 2+ bytes    \
    //              / 1 byte | 2 bit | 6 bit  \
    //             /                           \
    //            /  OTA only 2 bytes for BLE   \
    //           |                         |     |
    //           Type RFU ChSet TxAdd RxAdd Length
    //    bits:    4   1    1     1     1     8

    If using bare-metal transmission of (say) an advertising packet when timestamp is required, this is an example of a test packet:

    void nrf_radio_advertise(void)
    {
        // The S0 length is one byte, and exactly maps to p_beacon_pdu[0]. The length field is 6 bits,
        // and uses 6 bits of p_beacon_pdu[1]. S1 field is 2 bits and uses 2 bits of p_beacon_pdu[2].
        // This means the PDU header takes 3 bytes in RAM, but only 2 bytes on air.
        // If S0, LENGTH or S1 are specified with zero length their fields will be omitted in memory, otherwise each
        // field will be represented as a separate byte, regardless of the number of bits in their on-air counterpart
        // ie if the S0INCL field in PCNF0 determines if S0 is present in RAM at all if its length is zero. If present, one byte is allocated in RAM
    
        m_adv_pdu[0]  = 0x40 | 0x02;    // S0:     Optional First header byte: TxAdd|RxAdd (0x40) + PDU Type
        m_adv_pdu[1]  = 0;              // Length: Optional Second header byte: LENGTH (will be updated at the end)
        m_adv_pdu[2]  = 0;              // S1:     Optional Third header byte
    
        m_adv_pdu[3]  = (NRF_FICR->DEVICEADDR[0]      ) & 0xFF; // DEVICEADDR[0] bits 31-0 of the BLE MAC address.
        m_adv_pdu[4]  = (NRF_FICR->DEVICEADDR[0] >>  8) & 0xFF;
        m_adv_pdu[5]  = (NRF_FICR->DEVICEADDR[0] >> 16) & 0xFF;
        m_adv_pdu[6]  = (NRF_FICR->DEVICEADDR[0] >> 24)       ;
        m_adv_pdu[7]  = (NRF_FICR->DEVICEADDR[1]      ) & 0xFF;
        m_adv_pdu[8]  = (NRF_FICR->DEVICEADDR[1] >>  8) & 0xFF;
    
        m_adv_pdu[9]  = 0x02;  // 2 bytes (3 Tot)
        m_adv_pdu[10] = 0x01;  // BLE_GAP_AD_TYPE_FLAGS
        m_adv_pdu[11] = 0x06;  // BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE
    
        m_adv_pdu[12] = 0x05;  // 5 bytes (6 Tot)
        m_adv_pdu[13] = 0x09;  // BLE_GAP_AD_TYPE_COMPLETE_LOCAL_NAME
        m_adv_pdu[14] = 'T';
        m_adv_pdu[15] = 'E';
        m_adv_pdu[16] = 'S';
        m_adv_pdu[17] = 'T';
    
        m_adv_pdu[18] =  21;  // 21 bytes (22 Tot)
        m_adv_pdu[19] = 0xFF; // BLE_GAP_AD_TYPE_MANUFACTURER_SPECIFIC_DATA  Manufacturer Specific Data
        m_adv_pdu[20] = 0x01; // Nordic is 0x59 0x00
        m_adv_pdu[21] = 0x02; // Timestamp starts here ..
        m_adv_pdu[22] = 0x03;
        m_adv_pdu[23] = 0x04;
        m_adv_pdu[24] = 0x05;// .. Timestamp ends here
        m_adv_pdu[25] = 0x06;
        m_adv_pdu[26] = 0x07;
        m_adv_pdu[27] = 0x08;
        m_adv_pdu[28] = 0x09;
        m_adv_pdu[29] = 0x0A;
        m_adv_pdu[30] = 0x0B;
        m_adv_pdu[31] = 0x0C;
        m_adv_pdu[32] = 0x0D;
        m_adv_pdu[33] = 0x0E;
        m_adv_pdu[34] = 0x0F;
        m_adv_pdu[35] = 0x10;
        m_adv_pdu[36] = 0x11;
        m_adv_pdu[37] = 0x12;
        m_adv_pdu[38] = 0x13;
        m_adv_pdu[39] = 0x14;

    Then the first event available on transmission is the READY (nRF52832) or better TXREADY on nRF52833:

    void RADIO_IRQHandler(void)
    {
       if (NRF_RADIO->EVENTS_TXREADY)
       {
          // Start of transmission - Update timestamp
          m_adv_pdu[21] = 0x12; // Dummy test timestamp, say 32-bit
          m_adv_pdu[22] = 0x34;
          m_adv_pdu[23] = 0x56;
          m_adv_pdu[24] = 0x78;
          NRF_RADIO->EVENTS_READY = 0;
       }
       if (NRF_RADIO->EVENTS_DISABLED)
       {
          // end of tx transmission
    

    Out of all the events, TXREADY is the first prior to sending the data:

    // RADIO Events:
    // ============
     //   Event                      IDE 1st Peripheral Base Address  Offset     Description
     //   =========================  ===============================  =======    ===============================================================================================
    //   {"EVENTS_READY",            &NRF_RADIO->EVENTS_READY,       0x100}, // RADIO has ramped up and is ready to be started
    //   {"EVENTS_ADDRESS",          &NRF_RADIO->EVENTS_ADDRESS,     0x104}, // Address sent or received
    //   {"EVENTS_PAYLOAD",          &NRF_RADIO->EVENTS_PAYLOAD,     0x108}, // Packet payload sent or received
    //   {"EVENTS_END",              &NRF_RADIO->EVENTS_END,         0x10C}, // Packet sent or received
    //   {"EVENTS_DISABLED",         &NRF_RADIO->EVENTS_DISABLED,    0x110}, // RADIO has been disabled
    //   {"EVENTS_DEVMATCH",         &NRF_RADIO->EVENTS_DEVMATCH,    0x114}, // A device address match occurred on the last received packet
    //   {"EVENTS_DEVMISS",          &NRF_RADIO->EVENTS_DEVMISS,     0x118}, // No device address match occurred on the last received packet
    //   {"EVENTS_RSSIEND",          &NRF_RADIO->EVENTS_RSSIEND,     0x11C}, // Sampling of receive signal strength complete
    //   {"EVENTS_BCMATCH",          &NRF_RADIO->EVENTS_BCMATCH,     0x128}, // Bit counter reached bit count value
    //   {"EVENTS_CRCOK",            &NRF_RADIO->EVENTS_CRCOK,       0x130}, // Packet received with CRC ok
    //   {"EVENTS_CRCERROR",         &NRF_RADIO->EVENTS_CRCERROR,    0x134}, // Packet received with CRC error
    //#if defined(NRF52840_XXAA) || defined(NRF52833_XXAA) //|| defined(TESTING_ON_nRF52833DK)
    //   {"EVENTS_FRAMESTART",       &NRF_RADIO->EVENTS_FRAMESTART,  0x138}, // IEEE 802.15.4 length field received
    //   {"EVENTS_EDEND",            &NRF_RADIO->EVENTS_EDEND,       0x13C}, // Sampling of energy detection complete. A new ED sample is ready for readout from the RADIO.EDSAMPLE register.
    //   {"EVENTS_EDSTOPPED",        &NRF_RADIO->EVENTS_EDSTOPPED,   0x140}, // The sampling of energy detection has stopped
    //   {"EVENTS_CCAIDLE",          &NRF_RADIO->EVENTS_CCAIDLE,     0x144}, // Wireless medium in idle - clear to send
    //   {"EVENTS_CCABUSY",          &NRF_RADIO->EVENTS_CCABUSY,     0x148}, // Wireless medium busy - do not send
    //   {"EVENTS_CCASTOPPED",       &NRF_RADIO->EVENTS_CCASTOPPED,  0x14C}, // The CCA has stopped
    //   {"EVENTS_RATEBOOST",        &NRF_RADIO->EVENTS_RATEBOOST,   0x150}, // Ble_LR CI field received, receive mode is changed from Ble_LR125Kbit to Ble_LR500Kbit.
    //   {"EVENTS_TXREADY",          &NRF_RADIO->EVENTS_TXREADY,     0x154}, // RADIO has ramped up and is ready to be started TX path
    //   {"EVENTS_RXREADY",          &NRF_RADIO->EVENTS_RXREADY,     0x158}, // RADIO has ramped up and is ready to be started RX path
    //   {"EVENTS_MHRMATCH",         &NRF_RADIO->EVENTS_MHRMATCH,    0x15C}, // MAC header match found
    //   {"EVENTS_SYNC",             &NRF_RADIO->EVENTS_SYNC,        0x168}, // Preamble indicator.
    //   {"EVENTS_PHYEND",           &NRF_RADIO->EVENTS_PHYEND,      0x16C}, // Generated in Ble_LR125Kbit, Ble_LR500Kbit and Ieee802154_250Kbit modes when last bit is sent on air
    //   {"EVENTS_CTEPRESENT",       &NRF_RADIO->EVENTS_CTEPRESENT,  0x170}, // CTE is present (early warning right after receiving CTEInfo byte)

    Better still even with softdevice available FORK from the triggering timer is allowed:

    // Some of the PPI channels are pre-programmed. These channels cannot be configured by the CPU, but can
    // be added to groups and enabled and disabled like the general purpose PPI channels. The FORK TEP for
    // these channels are still programmable and can be used by the application.
    // Table 38: Pre-programmed channels
    // Channel EEP                        TEP
    // 20      TIMER0->EVENTS_COMPARE[0]  RADIO->TASKS_TXEN
    // 28      RTC0->EVENTS_COMPARE[0]    RADIO->TASKS_TXEN
    
    Example drive output pin high via FORK:
       NRF_PPI->FORK[20].TEP = (uint32_t)&NRF_GPIOTE->TASKS_SET[3];
    and drive low after packet:
       NRF_PPI->CH[5].EEP = (uint32_t)&NRF_RADIO->EVENTS_PHYEND; NRF_PPI->CH[5].TEP   = (uint32_t)&NRF_GPIOTE->TASKS_CLR[2];
                                                                 NRF_PPI->FORK[5].TEP = (uint32_t)&NRF_GPIOTE->TASKS_CLR[3];
    

    I'm doing something similar, will probably use 1 node (Central) to transmit timesyncs which all the other BLE nodes use to correct their timestamps. Not using the published stuff. Ideally I'll make the update just before triggering the transmission, but for testing this code has about 100uS to change the data on the fly.

    // |<------------- TASKS_ENABLE
    // |          |<-- TASKS_START +- Stuff Timestamp
    // |  EVENTS  ^                |                                              |<---TASKS_END
    // |  READY   +---------------------------------------------------------------+
    // |          |                                                               |
    // +----------+                                                               +---------------
    // |
    // |  ADDRESS    +------------------------------------------------------------+
    // |             |                                                            |
    // +-------------+                                                            +---------------
    // |
    // |  PAYLOAD                                                             +---+
    // |                                                                      |   |
    // +----------------------------------------------------------------------+   +---------------
    // |
    // |  END                                                                    ++
    // |                                                                         ||
    // +-------------------------------------------------------------------------++---------------
    // |
    // |  PHYEND                                                                  +
    // |                                                                          |
    // +--------------------------------------------------------------------------+---------------

    Alternative is to report actual time to Central and let Central adjust differences for each peripheral without changing peripherals; might be simpler.

Related