After a hiatus I am once again working on my atomic, lightweight, faster RFM69 library based heavily on discussions and ideas in this forum. These routines should be plug-and-play with Felix's library since my rewrite of SPI calls use the same name.
I've read a lot about automatic frequency correction and how crucial it could/should be to very low bitrate instances but I've not found working routines anywhere online. Below are two the first of which uses
joelucid's technique to oversample the on-chip thermometer to obtain the temperature in Cx10 with better than one degree precision and nearly 0.1 degree accuracy. The second use
john k2ox's regression of Freq vs. Temperature to correct Fstep.
Get Temperature x 10 in C with roughly 0.1C precision// Return the temperature x 10 in C. Leaves the radio in sleep mode once done to save power
static inline int16_t RadioGetTemp(void) {
uint16_t total;
writeReg( REG_OPMODE, SYNTHESIZER_MODE ); // Radio must be in standby or synthesizer to measure
for( uint8_t cnt=40; cnt; cnt-- ) {
writeReg( REG_TEMP1, RF_TEMP1_MEAS_START ); // Initiate a temperature conversion
while ( readReg( REG_TEMP1 )); // Wait for the result to be ready
total += (uint8_t) ~readReg( REG_TEMP2 ) - 91; // Add the current result to the running total
} // Take 40 such temperature readings
writeReg( REG_OPMODE, SLEEP_MODE ); // Place radio in sleep mode to save power
return( (total+2)>>2 ); // Return temp x 10 in C and account for rounding error
}
Correct the RF Carrier Frequency Based on the on-Chip Temperaturestatic inline void Correct_Freq(int32_t Base_Freq) {
uint16_t Tempx10 = RadioGetTemp(); // Get the current radio temperature x10 in C
uint8_t offset =((float) Tempx10*Tempx10*Tempx10/575261ul - (float) Tempx10*Tempx10/724 - (float) 100*Tempx10/526 + (float) 1531/16 + 0.5);
uint32_t target = Base_Freq + offset;
uint32_t Fstep = (uint32_t) readReg( REG_FRFMSB )<<16 | (uint16_t) readReg( REG_FRFMID )<<8 | readReg( REG_FRFLSB );
if ( target != Fstep ) { // Change Fstep
writeReg( REG_FRFMSB, target>>16 );
writeReg( REG_FRFMID, target>>8 );
writeReg( REG_FRFLSB, target);
}
}
Next up a nicety for listen mode, a simple little thing that calibrates the RC oscillator for instances when the radio is powered up for a long time and the temperature may drift (listen mode for instance). The radio must be in standby mode for this routine to do anything otherwise the radio just ignores the call for calibration.
Calibrate RC Oscillator// Transceiver must be in Standby
static inline uint8_t Osc_Cal(void) {
uint8_t i;
writeReg( REG_OSC1, RF_OSC1_RCCAL_START );
while ((readReg(REG_OSC1) & RF_OSC1_RCCAL_DONE) == 0x00) i++;
return(i);
}
And finally, I've never had fast and bulletproof results switching operating modes and I really dislike calls to millis to guard against locking up in code. This routine so far as I know is lightning fast and bulletproof at switching modes. Notably it works whether or not the radio is in auto mode since it only inspects the flags of interest and ignores others in REG_IRQFLAGS1.
Very Robust Mode Switchingstatic inline uint8_t SwitchMode(uint8_t NewMode) {
uint8_t timeout=0, mask = RF_IRQFLAGS1_MODEREADY;
if ( readReg( REG_OPMODE ) == NewMode ) return(255); // 255 = no switch was needed
writeReg( REG_OPMODE, NewMode );
if (NewMode == SYNTHESIZER_MODE) mask |= RF_IRQFLAGS1_PLLLOCK;
if (NewMode == TRANSMITTER_MODE) mask |= RF_IRQFLAGS1_TXREADY;
if (NewMode == RECEIVER_MODE) mask |= RF_IRQFLAGS1_RXREADY;
while ( ++timeout && ( ~readReg(REG_IRQFLAGS1) & mask ) );
return(timeout); // return the number of reads it took to change modes
}
Hope this is useful to someone! I'll keep working away at my own library.