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

  • nataddrho said:

    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.

    Yes, but I thought you could use the radion notification signal synchronize the advertisement update with the advertisement events to make the timing more deterministic.

    nataddrho said:
    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.

    Updating the data packet will require CPU involvment. 

    nataddrho said:
    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. 

    If you are using the s132, you will need to disable MWU protection (see Memory isolation and runtime protection) to get write access to buffers kept in the Softdevice's RAM region. RE: Disable Softdevice sandboxing in fault handler  

  •   This is exactly the answer I was looking for. Thank you.

  • As Vidar says, the softdevice enables the MWU which appears to protect the RAM buffer if held in the Softdevice area; however if using the Softdevice there is a way to use DMA to write directly into that area without disabling the MWU. Put a fast SPI into loopback mode and point the Rx at the desired memory location pointed to by RADIO->PACKETPTR; desired data (the timestamp) is placed in the SPI Tx buffer.

    The packet RAM can be directly addressed by the RADIO PACKETPTR:


    Instead of using possible unknown pdu location
      // Update timestamp
      m_adv_pdu[21] = 0x12;
    
    Use instead the PACKETPTR
      // Update timestamp
      ((volatile uint8_t *)NRF_RADIO->PACKETPTR)[21] = 0x12;

    "When the SoftDevice is enabled, RAM up to APP_RAM_BASE will be used by the SoftDevice and will be write protected."

    "The MWU is only able to detect memory accesses in the Data RAM and Peripheral memory segments from the CPU. EasyDMA Peripherals accesses are not monitored by the MWU"

    Edit: Better still, trigger the SPI transfer from the timer FORK (or FORK the radio EVENTS_TXREADY); then there is no delay except for the 1uSec per byte SPI transfer, no cpu especially if the SPI TX points to the timestamp memory source location. Brilliant - we just invented true RAM-to-RAM DMA on the nRF52.

  • Couldn't resist trying this - it works! This test is without softdevice, uses timestamp to change name so easy to check on phone App. Point SPI tx to actual timestamp data for real, plus test with SD when get some time.

    // The MWU is only able to detect memory accesses in the Data RAM and Peripheral memory segments from the CPU. EasyDMA Peripherals accesses are not monitored by the MWU
    // When the SoftDevice is enabled, RAM up to APP_RAM_BASE will be used by the SoftDevice and will be write protected.
    // trigger the SPI transfer from the timer FORK (or the radio EVENTS_TXREADY); then there is no delay except for the 1uSec per byte SPI transfer,
    // no cpu especially if the SPI TX points to the timestamp memory source location. Brilliant - we just invented true RAM-to-RAM DMA on the nRF52
    void InitRadioProbes(void)
    {
       // Configuration               Direction                Input                          Pullup                 Drive Level        Sense Level
       // ==========================  =======================  =============================  =====================  =================  ====================
       nrf_gpio_cfg(RADIO_TEST_1_PIN, NRF_GPIO_PIN_DIR_OUTPUT, NRF_GPIO_PIN_INPUT_DISCONNECT, NRF_GPIO_PIN_NOPULL,   NRF_GPIO_PIN_H0H1, NRF_GPIO_PIN_NOSENSE);
       nrf_gpio_cfg(RADIO_TEST_2_PIN, NRF_GPIO_PIN_DIR_OUTPUT, NRF_GPIO_PIN_INPUT_DISCONNECT, NRF_GPIO_PIN_NOPULL,   NRF_GPIO_PIN_H0H1, NRF_GPIO_PIN_NOSENSE);
       nrf_gpio_cfg(LED_RED_PIN,      NRF_GPIO_PIN_DIR_OUTPUT, NRF_GPIO_PIN_INPUT_DISCONNECT, NRF_GPIO_PIN_NOPULL,   NRF_GPIO_PIN_H0H1, NRF_GPIO_PIN_NOSENSE);
       nrf_gpio_cfg(LED_GREEN_PIN,    NRF_GPIO_PIN_DIR_OUTPUT, NRF_GPIO_PIN_INPUT_DISCONNECT, NRF_GPIO_PIN_NOPULL,   NRF_GPIO_PIN_H0H1, NRF_GPIO_PIN_NOSENSE);
       nrf_gpio_cfg(LED_BLUE_PIN,     NRF_GPIO_PIN_DIR_OUTPUT, NRF_GPIO_PIN_INPUT_DISCONNECT, NRF_GPIO_PIN_NOPULL,   NRF_GPIO_PIN_H0H1, NRF_GPIO_PIN_NOSENSE);
       // Configure GPIOTE to control output pin
       NRF_GPIOTE->CONFIG[0] = GPIOTE_CONFIG_MODE_Task << GPIOTE_CONFIG_MODE_Pos | GPIOTE_CONFIG_POLARITY_LoToHi << GPIOTE_CONFIG_POLARITY_Pos | RADIO_TEST_1_PIN << GPIOTE_CONFIG_PSEL_Pos;
       NRF_GPIOTE->CONFIG[1] = GPIOTE_CONFIG_MODE_Task << GPIOTE_CONFIG_MODE_Pos | GPIOTE_CONFIG_POLARITY_LoToHi << GPIOTE_CONFIG_POLARITY_Pos | RADIO_TEST_2_PIN << GPIOTE_CONFIG_PSEL_Pos;
       NRF_GPIOTE->CONFIG[2] = GPIOTE_CONFIG_MODE_Task << GPIOTE_CONFIG_MODE_Pos | GPIOTE_CONFIG_POLARITY_LoToHi << GPIOTE_CONFIG_POLARITY_Pos | LED_RED_PIN      << GPIOTE_CONFIG_PSEL_Pos;
       NRF_GPIOTE->CONFIG[3] = GPIOTE_CONFIG_MODE_Task << GPIOTE_CONFIG_MODE_Pos | GPIOTE_CONFIG_POLARITY_LoToHi << GPIOTE_CONFIG_POLARITY_Pos | LED_GREEN_PIN    << GPIOTE_CONFIG_PSEL_Pos;
       NRF_GPIOTE->CONFIG[4] = GPIOTE_CONFIG_MODE_Task << GPIOTE_CONFIG_MODE_Pos | GPIOTE_CONFIG_POLARITY_LoToHi << GPIOTE_CONFIG_POLARITY_Pos | LED_BLUE_PIN     << GPIOTE_CONFIG_PSEL_Pos;
       NRF_PPI->CH[0].EEP = (uint32_t)&NRF_RADIO->EVENTS_READY;    NRF_PPI->CH[0].TEP = (uint32_t)&NRF_GPIOTE->TASKS_SET[0];
                                                                   NRF_PPI->FORK[0].TEP = (uint32_t)&NRF_SPIM0->TASKS_START;
       NRF_PPI->CH[1].EEP = (uint32_t)&NRF_RADIO->EVENTS_ADDRESS;  NRF_PPI->CH[1].TEP = (uint32_t)&NRF_GPIOTE->TASKS_SET[1];
       NRF_PPI->CH[2].EEP = (uint32_t)&NRF_RADIO->EVENTS_PAYLOAD;  NRF_PPI->CH[2].TEP = (uint32_t)&NRF_GPIOTE->TASKS_SET[2];
       NRF_PPI->CH[3].EEP = (uint32_t)&NRF_RADIO->EVENTS_END;      NRF_PPI->CH[3].TEP = (uint32_t)&NRF_GPIOTE->TASKS_SET[3];
                                                                 NRF_PPI->FORK[3].TEP = (uint32_t)&NRF_SPIM0->TASKS_STOP;
    
       NRF_PPI->CH[4].EEP = (uint32_t)&NRF_RADIO->EVENTS_PHYEND; NRF_PPI->CH[4].TEP   = (uint32_t)&NRF_GPIOTE->TASKS_CLR[0];
                                                                 NRF_PPI->FORK[4].TEP = (uint32_t)&NRF_GPIOTE->TASKS_CLR[1];
    
       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];
    
       NRF_PPI->CHEN = PPI_CHEN_CH0_Msk | PPI_CHEN_CH1_Msk | PPI_CHEN_CH2_Msk | PPI_CHEN_CH3_Msk | PPI_CHEN_CH4_Msk | PPI_CHEN_CH5_Msk;
    }
    
    #define SCK_PIN  15  // spi clock
    #define MOSI_PIN 16  // spi mosi
    // Spoof actual timestamp data with a test buffer
    uint8_t TimestampTXBuf[4] = {'1','2','3','4'};
    void TestRamRamDMAviaSPI(void)
    {
      NRF_GPIO->PIN_CNF[SCK_PIN] =  0x301; // output, high drive high and low H0H1
      NRF_GPIO->PIN_CNF[MOSI_PIN] = 0x30D; // output, high drive high and low H0H1, input connected, pull-up
      NRF_SPIM0->PSEL.SCK  = SCK_PIN;
      NRF_SPIM0->PSEL.MOSI = MOSI_PIN;
      NRF_SPIM0->PSEL.MISO = MOSI_PIN; // Loopback
      NRF_SPIM0->CONFIG = 0;
      NRF_SPIM0->FREQUENCY = 0x80000000;  //8 Mbps
      NRF_SPIM0->ENABLE = 7;  //enable
      NRF_SPIM0->TXD.PTR = (uint32_t)TimestampTXBuf;
      NRF_SPIM0->TXD.MAXCNT = sizeof(TimestampTXBuf);
      NRF_SPIM0->RXD.PTR = (uint32_t)&((volatile uint8_t *)NRF_RADIO->PACKETPTR)[14];
      NRF_SPIM0->RXD.MAXCNT = sizeof(TimestampTXBuf);
      NRF_SPIM0->EVENTS_ENDTX = 0;
      NRF_SPIM0->EVENTS_ENDRX = 0;
    }
    
    {
      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';
    
      // Ensure packet pointer set before SPI init
      NRF_RADIO->PACKETPTR = (uint32_t) &m_adv_pdu[0];
      // Set up DMA transfer of Timestamp
      TestRamRamDMAviaSPI();
    }

Related