This RF ring oscillator runs on the NRF52 BLE SOC using the Adafruit feather development board. The NRF52 has an ARM Cortex M4F running at 64 MHz with built in BLE radio. In the oscilloscope trace above, yellow and blue traces each represent a module. A pin is pulled high when the radio is transmitting and pulled low when the radio is receiving. By measuring the period, we can determine the time for a packet round trip (two transmits, two recieves). An arduino sketch for the oscillator is available here, or visible below.
#define toggle_mask 1<<7 //this is for the scope
static uint8_t packet = 12; //declare a buffer for our packet to use with easydma
void wait_for_end(){
while(!(NRF_RADIO->EVENTS_END)){}
NRF_RADIO->EVENTS_END = 0; //clear end event
}
void wait_for_ready(){
while(!(NRF_RADIO->EVENTS_READY)){}
NRF_RADIO->EVENTS_READY = 0; //clear ready event
}
void disable_radio(){
NRF_RADIO->EVENTS_DISABLED = 0; //clear disabled event
NRF_RADIO->TASKS_DISABLE = 1;
while(!(NRF_RADIO->EVENTS_DISABLED)){}
}
void send_token(){
NRF_GPIO->OUTSET = toggle_mask; //set toggle pin high
NRF_RADIO->EVENTS_READY = 0; //clear ready event
NRF_RADIO->TASKS_TXEN=1; //trigger tx enable task
delayMicroseconds(15);
//wait_for_ready(); //since we're switching, maybe we can get around this?
NRF_RADIO->TASKS_START=1; //start
wait_for_end();
NRF_GPIO->OUTCLR = toggle_mask; //set toggle pin low
//put radio in RX mode
NRF_RADIO->EVENTS_READY = 0; //clear ready event
NRF_RADIO->TASKS_RXEN=1; //trigger rx enable task
wait_for_ready();
NRF_RADIO->TASKS_START=1;
}
void setup() {
//helpful bitmasks in nrf52_bitfields.h
//Serial.begin(115200); //for debug only
NRF_GPIO->DIRSET = toggle_mask; //set toggling pin to output
NRF_RADIO->POWER = RADIO_POWER_POWER_Disabled; //turn off radio to reset registers
delay(10);
NRF_RADIO->POWER = RADIO_POWER_POWER_Enabled; //turn on radio
delay(10);
// Radio config
NRF_RADIO->TXPOWER = (RADIO_TXPOWER_TXPOWER_Pos3dBm << RADIO_TXPOWER_TXPOWER_Pos);
NRF_RADIO->FREQUENCY = 7UL; // Frequency bin 7, 2407MHz
NRF_RADIO->MODE = (RADIO_MODE_MODE_Nrf_2Mbit << RADIO_MODE_MODE_Pos);
NRF_RADIO->PREFIX0 = ((uint32_t)0xC0 << 0); // Prefix byte of address 0
NRF_RADIO->BASE0 = 0x01234567UL; // Base address for prefix 0
NRF_RADIO->TXADDRESS = 0x00UL; // Set device address 0 to use when transmitting
NRF_RADIO->RXADDRESSES = 0x01UL; // Enable device address 0 to use to select which addresses to receive
// Packet configuration
NRF_RADIO->PCNF0 = (0 << RADIO_PCNF0_S1LEN_Pos) | (0 << RADIO_PCNF0_S0LEN_Pos) | (0 << RADIO_PCNF0_LFLEN_Pos);
NRF_RADIO->PCNF1 = (RADIO_PCNF1_WHITEEN_Disabled << RADIO_PCNF1_WHITEEN_Pos) |
(RADIO_PCNF1_ENDIAN_Big << RADIO_PCNF1_ENDIAN_Pos) |
(2 << RADIO_PCNF1_BALEN_Pos);
// CRC Config
NRF_RADIO->CRCCNF = (RADIO_CRCCNF_LEN_Disabled << RADIO_CRCCNF_LEN_Pos); // Number of checksum bits
//NRF_RADIO->CRCINIT = 0xFFFFUL; // Initial value
//NRF_RADIO->CRCPOLY = 0x11021UL; // CRC poly: x^16 + x^12^x^5 + 1
NRF_RADIO->MODECNF0 |= RADIO_MODECNF0_RU_Fast << RADIO_MODECNF0_RU_Pos; //turn on fast ramp up
NRF_RADIO->SHORTS = 0; //turn off all shortcuts, for debug
//packet
NRF_RADIO->PACKETPTR = (uint32_t)&packet; //set pointer to packet buffer
//start HFCLK
NRF_CLOCK->TASKS_HFCLKSTART = 1;
while(!(NRF_CLOCK->HFCLKSTAT & CLOCK_HFCLKSTAT_STATE_Msk)){} //wait for hfclk to start
delay(10);
send_token(); //send a starting token
while(1){
if ( NRF_RADIO->EVENTS_END ){
NRF_RADIO->EVENTS_END = 0; //clear end event
packet += 1; //increment for fun
send_token();
}
//delay(100);
//Serial.println(packet); //for debug only
}
}
void loop() {}
This test was run on a board built around the Fanstel BC832 module. The module retails for $7, but other modules are available for $5 from Fanstel. This board has a place for an external 32.768 kHz oscillator, but this isn't necessary and rings at the same speed without it populated. BC832 Board Traces BC832 Board Interior
As seen in the datasheet excerpt below, the NRF52 has faster times for switching between TX and RX as compared to many other transceiver chips.