We have a DS TWR application that we’re trying to get running on a Cortex M4 in a Nordic nRF82540 that talks to the DWM1000 via SPI. I’ve ported the DS code (initiator and responder) found in the examples of the API. My strategy is to use a general purpose timer to poll for a ranging packet with the role switching every timer interval. I have two devices running the same code but neither one of them ever sees the SYS_STATUS_RXFCG bit set. Is this a reasonable strategy and what’s the most expedient way to debug the handshake? Do I implement one unit in “sniffer” mode? I also have a Trek1000 dev kit if that helps. Please advise. Code below (timer ISR and init code).
Timer ISR:
static void twr_poll_timer_isr(void * p_context)
{
app_timer_stop(m_twr_timer_id);
//toggle initiator role each timer interval
static bool initiator;
if (initiator == true) {
initiator = false;
}
else {
initiator = true;
}
if (initiator)
{
static unsigned long status_reg = 0;
/* Write frame data to DW1000 and prepare transmission. See NOTE 8 below. /
tx_poll_msg[ALL_MSG_SN_IDX] = frame_seq_nb;
dwt_writetxdata(sizeof(tx_poll_msg), tx_poll_msg, 0); / Zero offset in TX buffer. /
dwt_writetxfctrl(sizeof(tx_poll_msg), 0, 1); / Zero offset in TX buffer, ranging. */
/* Start transmission, indicating that a response is expected so that reception is enabled automatically after the frame is sent and the delay
* set by dwt_setrxaftertxdelay() has elapsed. */
dwt_starttx(DWT_START_TX_IMMEDIATE | DWT_RESPONSE_EXPECTED);
/* We assume that the transmission is achieved correctly, poll for reception of a frame or error/timeout. See NOTE 9 below. */
status_reg = dwt_read32bitreg(SYS_STATUS_ID);
/* Increment frame sequence number after transmission of the poll message (modulo 256). */
frame_seq_nb++;
if (status_reg & SYS_STATUS_RXFCG)
{
uint32 frame_len;
/* Clear good RX frame event and TX frame sent in the DW1000 status register. */
dwt_write32bitreg(SYS_STATUS_ID, SYS_STATUS_RXFCG | SYS_STATUS_TXFRS);
/* A frame has been received, read it into the local buffer. */
frame_len = dwt_read32bitreg(RX_FINFO_ID) & RX_FINFO_RXFLEN_MASK;
if (frame_len <= RX_BUF_LEN)
{
dwt_readrxdata(rx_buffer, frame_len, 0);
}
/* Check that the frame is the expected response from the companion "DS TWR responder" example.
* As the sequence number field of the frame is not relevant, it is cleared to simplify the validation of the frame. */
rx_buffer[ALL_MSG_SN_IDX] = 0;
if (memcmp(rx_buffer, rx_resp_msg, ALL_MSG_COMMON_LEN) == 0)
{
uint32 final_tx_time;
int ret;
/* Retrieve poll transmission and response reception timestamp. */
poll_tx_ts = get_tx_timestamp_u64();
resp_rx_ts = get_rx_timestamp_u64();
/* Compute final message transmission time. See NOTE 10 below. */
final_tx_time = (resp_rx_ts + (RESP_RX_TO_FINAL_TX_DLY_UUS * UUS_TO_DWT_TIME)) >> 8;
dwt_setdelayedtrxtime(final_tx_time);
/* Final TX timestamp is the transmission time we programmed plus the TX antenna delay. */
final_tx_ts = (((uint64)(final_tx_time & 0xFFFFFFFEUL)) << 8) + TX_ANT_DLY;
/* Write all timestamps in the final message. See NOTE 11 below. */
final_msg_set_ts(&tx_final_msg[FINAL_MSG_POLL_TX_TS_IDX], poll_tx_ts);
final_msg_set_ts(&tx_final_msg[FINAL_MSG_RESP_RX_TS_IDX], resp_rx_ts);
final_msg_set_ts(&tx_final_msg[FINAL_MSG_FINAL_TX_TS_IDX], final_tx_ts);
/* Write and send final message. See NOTE 8 below. */
tx_final_msg[ALL_MSG_SN_IDX] = frame_seq_nb;
dwt_writetxdata(sizeof(tx_final_msg), tx_final_msg, 0); /* Zero offset in TX buffer. */
dwt_writetxfctrl(sizeof(tx_final_msg), 0, 1); /* Zero offset in TX buffer, ranging. */
ret = dwt_starttx(DWT_START_TX_DELAYED);
/* If dwt_starttx() returns an error, abandon this ranging exchange and proceed to the next one. See NOTE 12 below. */
if (ret == DWT_SUCCESS)
{
/* Poll DW1000 until TX frame sent event set. See NOTE 9 below. */
while (!(dwt_read32bitreg(SYS_STATUS_ID) & SYS_STATUS_TXFRS))
{ };
/* Clear TXFRS event. */
dwt_write32bitreg(SYS_STATUS_ID, SYS_STATUS_TXFRS);
/* Increment frame sequence number after transmission of the final message (modulo 256). */
frame_seq_nb++;
}
}
}
else //no frame received
{
/* Clear RX error/timeout events in the DW1000 status register. */
dwt_write32bitreg(SYS_STATUS_ID, SYS_STATUS_ALL_RX_TO | SYS_STATUS_ALL_RX_ERR);
/* Reset RX to properly reinitialise LDE operation. */
dwt_rxreset();
}
} //initiator role
else { //responder role
/* Clear reception timeout to start next ranging process. */
dwt_setrxtimeout(0);
/* Activate reception immediately. */
dwt_rxenable(DWT_START_RX_IMMEDIATE);
/* Poll for reception of a frame or error/timeout. See NOTE 8 below. */
while (!((status_reg = dwt_read32bitreg(SYS_STATUS_ID)) & (SYS_STATUS_RXFCG | SYS_STATUS_ALL_RX_TO | SYS_STATUS_ALL_RX_ERR)))
{ };
if (status_reg & SYS_STATUS_RXFCG)
{
uint32 frame_len;
/* Clear good RX frame event in the DW1000 status register. */
dwt_write32bitreg(SYS_STATUS_ID, SYS_STATUS_RXFCG);
/* A frame has been received, read it into the local buffer. */
frame_len = dwt_read32bitreg(RX_FINFO_ID) & RX_FINFO_RXFL_MASK_1023;
if (frame_len <= RX_BUFFER_LEN)
{
dwt_readrxdata(rx_buffer, frame_len, 0);
}
/* Check that the frame is a poll sent by "DS TWR initiator" example.
* As the sequence number field of the frame is not relevant, it is cleared to simplify the validation of the frame. */
rx_buffer[ALL_MSG_SN_IDX] = 0;
if (memcmp(rx_buffer, rx_poll_msg, ALL_MSG_COMMON_LEN) == 0)
{
uint32 resp_tx_time;
int ret;
/* Retrieve poll reception timestamp. */
poll_rx_ts = get_rx_timestamp_u64();
/* Set send time for response. See NOTE 9 below. */
resp_tx_time = (poll_rx_ts + (POLL_RX_TO_RESP_TX_DLY_UUS * UUS_TO_DWT_TIME)) >> 8;
dwt_setdelayedtrxtime(resp_tx_time);
/* Set expected delay and timeout for final message reception. See NOTE 4 and 5 below. */
dwt_setrxaftertxdelay(RESP_TX_TO_FINAL_RX_DLY_UUS);
dwt_setrxtimeout(FINAL_RX_TIMEOUT_UUS);
/* Write and send the response message. See NOTE 10 below.*/
tx_resp_msg[ALL_MSG_SN_IDX] = frame_seq_nb;
dwt_writetxdata(sizeof(tx_resp_msg), tx_resp_msg, 0); /* Zero offset in TX buffer. */
dwt_writetxfctrl(sizeof(tx_resp_msg), 0, 1); /* Zero offset in TX buffer, ranging. */
ret = dwt_starttx(DWT_START_TX_DELAYED | DWT_RESPONSE_EXPECTED);
/* If dwt_starttx() returns an error, abandon this ranging exchange and proceed to the next one. See NOTE 11 below. */
if (ret == DWT_ERROR)
{
return;
}
/* Poll for reception of expected "final" frame or error/timeout. See NOTE 8 below. */
while (!((status_reg = dwt_read32bitreg(SYS_STATUS_ID)) & (SYS_STATUS_RXFCG | SYS_STATUS_ALL_RX_TO | SYS_STATUS_ALL_RX_ERR)))
{ };
/* Increment frame sequence number after transmission of the response message (modulo 256). */
frame_seq_nb++;
if (status_reg & SYS_STATUS_RXFCG)
{
/* Clear good RX frame event and TX frame sent in the DW1000 status register. */
dwt_write32bitreg(SYS_STATUS_ID, SYS_STATUS_RXFCG | SYS_STATUS_TXFRS);
/* A frame has been received, read it into the local buffer. */
frame_len = dwt_read32bitreg(RX_FINFO_ID) & RX_FINFO_RXFLEN_MASK;
if (frame_len <= RX_BUF_LEN)
{
dwt_readrxdata(rx_buffer, frame_len, 0);
}
/* Check that the frame is a final message sent by "DS TWR initiator" example.
* As the sequence number field of the frame is not used in this example, it can be zeroed to ease the validation of the frame. */
rx_buffer[ALL_MSG_SN_IDX] = 0;
if (memcmp(rx_buffer, rx_final_msg, ALL_MSG_COMMON_LEN) == 0)
{
uint32 poll_tx_ts, resp_rx_ts, final_tx_ts;
uint32 poll_rx_ts_32, resp_tx_ts_32, final_rx_ts_32;
double Ra, Rb, Da, Db;
int64 tof_dtu;
/* Retrieve response transmission and final reception timestamps. */
resp_tx_ts = get_tx_timestamp_u64();
final_rx_ts = get_rx_timestamp_u64();
/* Get timestamps embedded in the final message. */
final_msg_get_ts(&rx_buffer[FINAL_MSG_POLL_TX_TS_IDX], &poll_tx_ts);
final_msg_get_ts(&rx_buffer[FINAL_MSG_RESP_RX_TS_IDX], &resp_rx_ts);
final_msg_get_ts(&rx_buffer[FINAL_MSG_FINAL_TX_TS_IDX], &final_tx_ts);
/* Compute time of flight. 32-bit subtractions give correct answers even if clock has wrapped. See NOTE 12 below. */
poll_rx_ts_32 = (uint32)poll_rx_ts;
resp_tx_ts_32 = (uint32)resp_tx_ts;
final_rx_ts_32 = (uint32)final_rx_ts;
Ra = (double)(resp_rx_ts - poll_tx_ts);
Rb = (double)(final_rx_ts_32 - resp_tx_ts_32);
Da = (double)(final_tx_ts - resp_rx_ts);
Db = (double)(resp_tx_ts_32 - poll_rx_ts_32);
tof_dtu = (int64)((Ra * Rb - Da * Db) / (Ra + Rb + Da + Db));
tof = tof_dtu * DWT_TIME_UNITS;
distance = tof * SPEED_OF_LIGHT;
/* Display computed distance on LCD. */
sprintf(dist_str, "DIST: %3.2f m", distance);
SEGGER_RTT_printf(0,"%s\r\n", dist_str);
//lcd_display_str(dist_str);
}
}
else
{
/* Clear RX error/timeout events in the DW1000 status register. */
dwt_write32bitreg(SYS_STATUS_ID, SYS_STATUS_ALL_RX_TO | SYS_STATUS_ALL_RX_ERR);
/* Reset RX to properly reinitialise LDE operation. */
dwt_rxreset();
}
}
}
else
{
/* Clear RX error/timeout events in the DW1000 status register. */
dwt_write32bitreg(SYS_STATUS_ID, SYS_STATUS_ALL_RX_TO | SYS_STATUS_ALL_RX_ERR);
/* Reset RX to properly reinitialise LDE operation. */
dwt_rxreset();
}
}
app_timer_start(m_twr_timer_id, TWR_POLL_INTERVAL, NULL);
}
Init routine:
void twr_init(void) {
/* Default communication configuration. We use here EVK1000’s default mode (mode 3). /
static dwt_config_t config = {
2, / Channel number. /
DWT_PRF_64M, / Pulse repetition frequency. /
DWT_PLEN_1024, / Preamble length. Used in TX only. /
DWT_PAC32, / Preamble acquisition chunk size. Used in RX only. /
9, / TX preamble code. Used in TX only. /
9, / RX preamble code. Used in RX only. /
1, / 0 to use standard SFD, 1 to use non-standard SFD. /
DWT_BR_110K, / Data rate. /
DWT_PHRMODE_STD, / PHY header mode. /
(1025 + 64 - 32) / SFD timeout (preamble length + 1 + SFD length - PAC size). Used in RX only. */
};
// The DW1000 needs a reset before the SPI interface behaves
nrf_gpio_pin_write(UWB_WAKEUP_PIN, 0);
nrf_delay_ms(2);
nrf_gpio_pin_write(UWB_WAKEUP_PIN, 1);
nrf_delay_ms(2);
dwt_softreset();
nrf_delay_ms(2);
//port_set_dw1000_slowrate();
int dwt_error = dwt_initialise(DWT_LOADUCODE);
if (dwt_error == 0) {
SEGGER_RTT_printf(0,“UWB module interface go! \r\n”);
}
/*test write and read register
//GPIO mode register
dwt_write32bitoffsetreg(GPIO_CTRL_ID, GPIO_MODE_OFFSET, 0x00100000);
nrf_delay_ms(1);
//GPIO direction register (1=input, 0=output)
dwt_write32bitoffsetreg(GPIO_CTRL_ID, GPIO_DIR_OFFSET, 0xF0F0F0F0); //F’s are for the mask bits
nrf_delay_ms(1);
//Set actual bits
//Note GPIO0 output value is set by bit 24 of this
//dwt_write32bitoffsetreg(GPIO_CTRL_ID, GPIO_DOUT_OFFSET, 0xFFFFFFFF); //set GOM’s to 1
//dwt_write32bitoffsetreg(EXT_SYNC_ID, EC_CTRL_OFFSET, 0x44);
dwt_write32bitoffsetreg(GPIO_CTRL_ID, 0x08, 0x0011F9FA);
nrf_delay_ms(1);
dwt_write32bitoffsetreg(GPIO_CTRL_ID, 0x0C, 0x0011F7F8);
nrf_delay_ms(1);
dwt_write32bitoffsetreg(GPIO_CTRL_ID, 0x24, 0x00000154);
nrf_delay_ms(1);
//read back
//unsigned long gpio_test = dwt_read32bitoffsetreg(GPIO_CTRL_ID, GPIO_DOUT_OFFSET);
//unsigned long gpio_test = dwt_read32bitoffsetreg(EXT_SYNC_ID, EC_CTRL_OFFSET);
//unsigned long gpio_test = dwt_read32bitoffsetreg(GPIO_CTRL_ID, GPIO_IRQE_OFFSET);
unsigned long reg32test1 = dwt_read32bitoffsetreg(GPIO_CTRL_ID, 0x08);
unsigned long reg32test2 = dwt_read32bitoffsetreg(GPIO_CTRL_ID, 0x0C);
unsigned long reg32test3 = dwt_read32bitoffsetreg(GPIO_CTRL_ID, 0x24);
// uint16_t expected = (reg32test1>>24) + (reg32test2<<8);
uint16_t reg16test = dwt_read16bitoffsetreg(GPIO_CTRL_ID, 0x23); //should return the last 16 bits of
// the system status register, bits 24-31 of offset zero in the least sig byte and bits
// 0-7 of offset 3 in the most sig byte with the bit ordering exactly the same as the register
// description in the DW1000 user manual.
uint8_t reg8test = dwt_read8bitoffsetreg(GPIO_CTRL_ID, 0x25);
dwt_write8bitoffsetreg(GPIO_CTRL_ID, 0x0D, 0xF5);
nrf_delay_ms(1);
reg8test = dwt_read8bitoffsetreg(GPIO_CTRL_ID, 0x0D);
*/
//port_set_dw1000_fastrate();
/* Configure DW1000. See NOTE 7 below. */
dwt_configure(&config);
/* Apply default antenna delay value. See NOTE 1 below. */
dwt_setrxantennadelay(RX_ANT_DLY);
dwt_settxantennadelay(TX_ANT_DLY);
/* Set expected response’s delay and timeout. See NOTE 4, 5 and 6 below.
- As this example only handles one incoming frame with always the same delay and timeout, those values can be set here once for all. */
dwt_setrxaftertxdelay(POLL_TX_TO_RESP_RX_DLY_UUS);
dwt_setrxtimeout(RESP_RX_TIMEOUT_UUS);
dwt_setpreambledetecttimeout(PRE_TIMEOUT);
}