simpleSA/si4432.cpp
M0WID 6479c6050b RXSweep mode added
Also scale for bandscope
2020-08-21 22:02:22 +01:00

863 lines
28 KiB
C++

/*
* "Si4432.cpp"
*
* Added to the "TinySA" program in Version 1.7 by John Price (WA2FZW):
*
* Functions to handle the Si4432 transceivers as an objects. Some of the
* functions in here apply only to the receiver and some apply only to the
* transmitter. The "Init" functions remember what type of module (transmitter
* or receiver) is being initialized.
*
* For further information on the register settings, please refer to the
* "Silicon Labs Si4430/31/32 - B1" data sheet and "Silicon Labs Technical
* Note A440".
*/
#include <SPI.h> // Serial Peripheral Interface library
#include "si4432.h" // Our header file
#include <esp32-hal-spi.h>
/*
* The "bandpassFilters" array contains a selection of the standard bandpass settings.
*
*/
bandpassFilter_t Si4432::_bandpassFilters[]
{
// bw*10, settle, dwn3, ndec, filset
{ 26, 6800, 0, 5, 1 }, // 0 "AUTO" selection possibility
{ 28, 6600, 0, 5, 2 }, // 1 "AUTO" selection possibility
{ 31, 6600, 0, 5, 3 }, // 2 If user selects 3KHz -> 3.1KHz actual
{ 32, 6600, 0, 5, 4 }, // 3 "AUTO" selection possibility
{ 37, 6600, 0, 5, 5 }, // 4 "AUTO" selection possibility
{ 42, 6600, 0, 5, 6 }, // 5 "AUTO" selection possibility
{ 45, 5500, 0, 5, 7 }, // 6 "AUTO" selection possibility
{ 49, 5000, 0, 4, 1 }, // 7 "AUTO" selection possibility
{ 54, 4200, 0, 4, 2 }, // 8 "AUTO" selection possibility
{ 59, 3700, 0, 4, 3 }, // 9 "AUTO" selection possibility
{ 72, 3300, 0, 4, 5 }, // 10 "AUTO" selection possibility
{ 106, 2500, 0, 3, 2 }, // 11 If user selects 10KHz -> 10.6KHz actual
{ 162, 2000, 0, 3, 6 }, // 12 "AUTO" selection possibility
{ 210, 1600, 0, 2, 2 }, // 13 "AUTO" selection possibility
{ 227, 1500, 0, 2, 3 }, // 14 "AUTO" selection possibility
{ 240, 1400, 0, 2, 4 }, // 15 "AUTO" selection possibility
{ 282, 1000, 0, 2, 5 }, // 16 "AUTO" selection possibility
{ 322, 1000, 0, 2, 6 }, // 17 If user selects 30KHz -> 32.2KHz actual
{ 377, 1000, 0, 1, 1 }, // 18 "AUTO" selection possibility
{ 562, 700, 0, 1, 5 }, // 19 "AUTO" selection possibility
{ 832, 600, 0, 0, 2 }, // 20 "AUTO" selection possibility
{ 1121, 500, 0, 0, 5 }, // 21 If user selects 100KHz -> 112.1KHz actual
{ 1811, 500, 1, 1, 9 }, // 22 "AUTO" selection possibility
{ 2251, 1400, 1, 0, 1 }, // 23 "AUTO" selection possibility
{ 2488, 450, 1, 0, 2 }, // 24 "AUTO" selection possibility
{ 3355, 400, 1, 0, 8 }, // 25 If user selects 300KHz -> 335.5KHz actual
{ 3618, 300, 1, 0, 9 }, // 26 "AUTO" selection possibility
{ 4685, 300, 1, 0, 11 }, // 27 "AUTO" selection possibility
{ 6207, 300, 1, 0, 14 } // 28 "AUTO" selection possibility
};
uint8_t Si4432::_bpfCount = ELEMENTS ( Si4432::_bandpassFilters ); // Number of entries in the array
/*
* The constructor takes two arguments which are the pointer to the SPI object
* and "Chip Select" pin for the module.
*
* The SPI object can be shared with other objects, so is declared in the main sketch as
*
* SPIClass* vspi = new SPIClass ( VSPI );
* or SPIClass* hspi = new SPIClass ( HSPI );
*
* The SPI object is initialized once in the main sketch setup, along with the relevant pins:
*
* pinMode ( V_SCLK, OUTPUT ); // SPI Clock pin
* pinMode ( V_SDO, INPUT ); // SDO (MISO) pin
* pinMode ( V_SDI, OUTPUT ); // SDI (MOSI) pin
*
* digitalWrite ( V_SCLK, LOW ); // Make SPI clock LOW
* digitalWrite ( V_SDI, LOW ); // Along with MOSI
*
* vspi->begin ( V_SCLK, V_SDO, V_SDI ); // Start the VSPI: SCLK, MISO, MOSI
* vspi->setFrequency(10000000); // Max speed according to datasheet
*/
Si4432::Si4432 ( SPIClass* spi, uint8_t cs, uint8_t id ) // Constructor (argument is chip select pin number)
{
_cs = cs; // Remember the chip select pin number
_bw = 3355; // Set bandwidth to 335.5KHz for now
_dt = 400; // Proper delay time for wide bandwidth
_spi = spi; // Pointer to SPI object
_type = id;
pinMode ( _cs, OUTPUT ); // Chip select pin is an output
digitalWrite ( _cs, HIGH ); // Deselect the module
}
/*
* There are two different initialization functions, as there are obviously some
* differences in how you set up the transmitter and receiver modules.
*
* ** Modified for version 3.0e by M0WID to be one Init as the SI4432 can be used
* for either RX aor TX depending on mode. All SI4432 are set as RX to start with
* with no GPIO2 reference output
*
* The "SubInit" function takes care of the register settings common to both.
*/
bool Si4432::Init ( uint8_t cap )
{
if ( !Reset () ) // Reset the module
return false; // If that failed
/*
* "REG_FBS" is the frequency band select register. We select upper sideband
* (0x40) and the band is set to (0x06). What that means is dependent on the
* setting of the "HBSEL" bit (0x10), which is set to low band here.
*
* As near as I can tell from A440, this says we will be tuning in a range of
* 300 to 310MHz.
*/
// WriteByte ( REG_FBS, 0x46 ); // Select high sideband & low freq range?
/*
* The following two instructions seem to set the carrier frequency to zero
* per A440. The setting of "REG_NFC1" to 0x62 was commented out in the
* original code.
*/
// WriteByte ( REG_NCF1, 0x62 ); // Nominal Carrier Frequency 1
// WriteByte ( REG_NCF1, 0x00 ); // WE USE 433.92 MHz
// WriteByte ( REG_NCF0, 0x00 ); // Nominal Carrier Frequency 0
/*
* Set the receiver modem IF bandwidth. In the original code, the bandwidth
* was set to 37.3KHz (0x81); I changed it to 335.5KHz (maximum for the
* application).
*/
// WriteByte ( REG_IFBW, 0x81 ); // RX Modem IF bandwidth (original value)
WriteByte ( REG_IFBW, 0x18 ); // IF bandwidth (for 335.5 KHz)
/*
* Set the AFC loop gearshift override to minimum, turn off AFC
*/
WriteByte ( REG_AFCGSO, 0x00 ); // AFC Loop Gearshift Override
WriteByte ( REG_AFCTC, 0x02 ); // AFC Timing Control
/*
* Set the "Clock Recovery Gearshift Value". The original code set it to 0x00,
* however, the recommended value from A440 is 0x05.
* We are not sending data so have no need to syncronise clocks between Tx and Rx
* so just leave at the default value
*/
WriteByte ( REG_CRGO, 0x03 ); // Recommended value from A440
// WriteByte ( REG_CROSR, 0x78 ); // Clock Recovery Oversampling Ratio
WriteByte ( REG_CRO2, 0x01 ); // Clock Recovery Offset 2
WriteByte ( REG_CRO1, 0x11 ); // Clock Recovery Offset 1
WriteByte ( REG_CRO0, 0x11 ); // Clock Recovery Offset 0
WriteByte ( REG_CRTLG1, 0x01 ); // Clock Recovery Timing Loop Gain 1
WriteByte ( REG_CRTLG0, 0x13 ); // Clock Recovery Timing Loop Gain 0
WriteByte ( REG_AFCLIM, 0xFF ); // AFC Limiter - Maximum
WriteByte ( REG_OOKC1, 0x28 ); // OOK Counter Value 1
WriteByte ( REG_OOKC2, 0x0C ); // OOK Counter Value 2
WriteByte ( REG_SPH, 0x28 ); // OOK Attack & Decay settings
WriteByte ( REG_DATAC, 0x61 ); // Disable packet handling
/*
* The original code had all these choices for what to put into the "REG_AGCOR1"
* register (0x69) to control the LNA and pre-amp. Pick only one.
*/
// WriteByte ( REG_AGCOR1, 0x00 ); // No AGC, min LNA
// WriteByte ( REG_AGCOR1, LNAGAIN ); // No AGC, max LNA of 20dB
// WriteByte ( REG_AGCOR1, AGCEN ); // AGC enabled, min LNA
WriteByte ( REG_AGCOR1, 0x60 ); // AGC, min LNA, Gain increase during signal reductions
// WriteByte ( REG_AGCOR1, 0x30 ); // AGC, max LNA
// WriteByte ( REG_AGCOR1, 0x70 ); // AGC, max LNA, Gain increase during signal reductions
WriteByte ( REG_GPIO0, 0x12 ); // GPIO-0 TX State (output)
WriteByte ( REG_GPIO1, 0x15 ); // GPIO-1 RX State (output)
WriteByte ( REG_GPIO2, 0x1F ); // Set GPIO-2 output to ground until needed
/*
*
* We turn on receive mode ("RXON"), the PLL ("PLLON") and ready mode
* ("XTON"). The Si4432 module does not have the 32.768 kHz crystal for
* the microcontroller, so we do not turn on the "X32KSEL" bit.
*
* The initial frequency is set to 433.92 MHz which is a typical IF
* Finally the GPIO-2 pin is set to ground.
*
* 03/24 - Logic verified against A440 register edscription document
*/
Tune ( cap ); // Set the crystal capacitance to fine tune frequency
WriteByte ( REG_OFC1, ( RXON | PLLON | XTON )); // Receiver, PLL and "Ready Mode" all on
SetFrequency ( 443920000 ); // 443.92MHz
delayMicroseconds ( 300 ); // Time to allow the SI4432 state machine to do its stuff
Serial.printf ( "End of Init - _cs = %i\n", _cs );
return true;
}
/*
* "Reset" - Initializes the Si4432.
*
* We write the "SW_RESET" (0x80) bit in the "REG_OFC1" register (0x07).
* Doing so resets all the registers to their default values. The process
* is complete when the "ICHIPRDY" bit (0x02) in the "REG_IS2" register
* (0x04) goes high.
*
* We will try reading the ready bit 100 times with a slight pause between
* tries. When it goes high, we're done.
*
* 03/24 - Logic verified against A440
*/
bool Si4432::Reset ()
{
uint32_t count = 0;
uint32_t startTime = millis ();
uint8_t regRead = 0;
const char* unit[4] = { "RX", "TX", "TGIF", "TGLO" }; // For debugging
WriteByte ( REG_OFC1, SW_RESET ); // Always perform a system reset
while ( millis() - startTime < 10000 ) // Try for 10 seconds
{
regRead = ReadByte ( REG_IS2 );
if ( ( regRead & ICHIPRDY ) && ( regRead != 0xFF ) ) // Wait for chip ready bit
{
Serial.printf ( " Si4432 Good to go - regRead = %02X _cs = %i\n", regRead, _cs );
return true; // Good to go!
}
/*
* If we don't have "ICHIPRDY" yet, only display the error message once per second.
*/
if ((( millis () - startTime ) % 1000 ) == 0 )
{
// Serial.print ( "Waiting for " );
// Serial.print ( unit[_type] );
// Serial.print ( " Si4432 - regRead " );
// Serial.println ( regRead );
// Serial.printf ( "Type %X Version %X Status %X \n", ReadByte(0), ReadByte(1), ReadByte(2));
}
delay ( 1 ); // Slight pause
}
Serial.printf ( "Si4432 Reset failed - _cs = %i\n", _cs );
return false;
}
/*
* "WriteByte" sends a byte of data into the selected Si4432 register. The
* specified register number is transmitted first with the MSB turned on
* which indicates it's a write operation. That is followed by the data byte
* to be written to the specified register.
*/
void Si4432::WriteByte ( byte reg, byte setting )
{
reg |= 0x80 ; // Indicate this is a "write" operation
//_spi->beginTransaction ( SPISettings ( BUS_SPEED, BUS_ORDER, BUS_MODE ));
//spiSimpleTransaction(_spi->bus());
digitalWrite ( _cs, LOW ); // Select the correct device
_spi->transfer ( reg ); // Send the register address
_spi->transfer ( setting ); // and the data byte
digitalWrite ( _cs, HIGH ); // Deselect the device
//_spi->endTransaction(); // Release the bus
// delayMicroseconds ( WRITE_DELAY );
}
/*
* "ReadByte" reads a byte of data from the selected Si4432. Here we send the
* register with the MSB set to zero indicating that we want to read the
* specified register.
*
* The "transfer" call to read the data has a dummy argument of '0'; the
* argument is needed but has absolutely no meaning.
*/
uint8_t Si4432::ReadByte ( uint8_t reg )
{
uint8_t regValue; // Contains the requested data
//_spi->beginTransaction ( SPISettings ( BUS_SPEED, BUS_ORDER, BUS_MODE ));
//spiSimpleTransaction(_spi->bus());
digitalWrite ( _cs, LOW ); // Select the correct device
_spi->transfer ( reg ); // Send the register address
regValue = _spi->transfer ( 0 ); // Read the data byte
digitalWrite ( _cs, HIGH ); // Deselect the device
//_spi->endTransaction();
return regValue; // Return the answer
}
/*
* "SetFrequency" sets the Si4432 frequency. Technical note A440 explains some of
* what's going on in here!
*/
void Si4432::SetFrequency ( uint32_t freq )
{
int hbsel; // high band select, but shifted to the correct location in the byte
int sbsel; // Sideband select bit
uint16_t carrier; // Carrier frequency
uint32_t reqFreq = freq; // Copy of requested frequency
uint8_t fbs; // Frequency band select value (N - 24)
uint8_t ofc1; // To read the "REG_OFC1" register
uint8_t registerBuf[4]; // Used to send frequency data in burst mode
if ( freq >= 480000000 ) // Frequency > 480MHz (high band)?
{
hbsel = HBSEL; // High band is 480MHz to 960MHz
freq = freq / 2; // And divide the frequency in half
}
else // Frequency requested is less than 480MHz
{
hbsel = 0x00; // Low band is 240KHz to 479.9MHz
}
sbsel = SBSEL; // Select high sideband (always)
/*
* add half the frequency resolution to required frequency so the actual value is rounded to nearest, not lowest
* Frequency resoluion is 156.25Hz in low band, 312.5Hz in high band but freq is already divided above
*/
freq = freq + 78;
/*
* "N" picks the 10MHz range for low band and the 20MHz range for the high band.
* It's explained in "Table 12" in the Silicon Labs datasheet.
*/
int N = freq / 10000000; // Divide freq by 10MHz
/*
* The low order 5 bits of the "Frequency Band Select" register (0x75) get
* loaded with "N - 24" and we add in the band select and sideband select
* bits.
*/
fbs = (( N - 24 ) | hbsel | sbsel );
/*
* Compute the actual carrier frequency.
*
* It takes a few things to actually set the frequency. See Tech Note A440
* for an explanation (it made my head hurt)!
*
* The carrier frequency is actually an offset from the lower end of the
* frequency band selected (N-24), so for example in the initialization
* sequence we set the carrier frequency to 443,920,000. From Table 12 in
* the datasheet, we see that the frequency is in the "Low Band" (less than
* 480MHz) and the actual band will be '20', and "N" will be '44'.
*
* The "carrier" value is the number of '156.25Hz" increments from the base
* frequency of the band. For a frequency of 443,920,000, the answer is
* 25,088.
*
* If we're operating on the "High Band", the "carrier" is the number of
* '312.5Hz' increments from the base frequency of the band.
*/
carrier = ( 4 * ( freq - N * 10000000 )) / 625;
//if (_cs == 2 )
// Serial.printf ( "\nRequested frequency = %u \n", reqFreq );
/*
* M0WID mod - Update the frequency in "burst" mode as opposed to separate
* writes for each register
*/
uint8_t ncf1 = ( carrier >> 8 ) & 0xFF;
registerBuf[0] = REG_FBS|0x80; // First register in write mode (bit 7 set)
registerBuf[1] = fbs; // FBS register value
registerBuf[2] = ncf1 ; // NCF1 value
registerBuf[3] = carrier & 0xFF; // NCF0 value
//_spi->beginTransaction ( SPISettings ( BUS_SPEED, BUS_ORDER, BUS_MODE ));
// spiSimpleTransaction(_spi->bus());
digitalWrite ( _cs, LOW ); // Select the correct device
_spi->transfer ( registerBuf, 4 ); // Send the data
digitalWrite ( _cs, HIGH ); // Deselect the device
//_spi->endTransaction();
_ncf1 = ncf1;
_fbs = fbs;
uint32_t fb = ( fbs & F_BAND ) ;
_hbsel = hbsel>>5; // should be 1 or 0
// _freq will contain the actual frequency, not necessarily what was requested
_freq = (double)(10000000 * (_hbsel + 1 )) * ( (double)fb + (double)24 + (double)carrier / (double)64000) ;
// Serial.printf("set Freq :%i, actual:%i, fb:%i, fc:%i, hbsel:%i\n", reqFreq, _freq, fb, carrier, hbsel);
// Serial.print ( ", N = " );
// Serial.print ( N );
// fbs = ReadByte ( REG_FBS );
// Serial.print ( ", Freq_Band = " );
// Serial.println ( fbs & F_BAND );
// Serial.print ( "hbsel = " );
// Serial.print ( fbs & HBSEL, HEX );
// carrier = ReadByte ( REG_NCF1 ) << 8;
// carrier |= ReadByte ( REG_NCF0 );
// Serial.print ( ", Carrier = " );
// Serial.print ( carrier );
// ofc1 = ReadByte ( REG_OFC1 );
// Serial.print ( ", REG_OFC1 = " );
// Serial.println ( ofc1, HEX );
/*
* This delay is needed in the test programs. In the real TinySA software it is
* handled by the calling program.
*/
// delayMicroseconds ( _dt );
}
/*
* "SetOffsetFreq" adjusts the frequency from the nominal value set in SetFrequency.
* It is intended for use by the SI4432 AFC algorithm to enable more accurate
* frequency matching of different radios, but we can turn off the AFC and use it
* to adjust the frequency without having the Si4432 go through its TX-RX-TX state machine
* and enable reduced edlay times from setting a frequency to getting valid readings
* The offset can vary +- 80kHz in low bands (f<480MHz) and +-160kHz in high bands (480-960MHz)
* negative numbers are twos complement of the positive offset
*/
void Si4432::SetOffsetFreq ( int32_t freq )
{
uint8_t registerBuf[3]; // Used to send data in burst mode
uint32_t posFreq;
if (freq < 0)
posFreq = -freq;
else
posFreq = freq;
uint16_t fo = (double)posFreq/(156.25 * (double)(_hbsel + 1));
if (freq < 0)
// do twos complement - invert the bits (0-9) (use XOR) and add one
fo = (fo ^ 0x3FF) + 1;
//Serial.printf(" offset frequency %i fo=%3x \n", freq, fo);
// write to the Si4432
registerBuf[0] = REG_FOFF1|0x80; // First register in write mode (bit 7 set)
registerBuf[1] = fo & 0xFF; // first 8 bits
registerBuf[2] = (fo >> 8 ) & 0x03; // last 2 bits
//_spi->beginTransaction ( SPISettings ( BUS_SPEED, BUS_ORDER, BUS_MODE ));
// spiSimpleTransaction(_spi->bus());
digitalWrite ( _cs, LOW ); // Select the correct device
_spi->transfer ( registerBuf, 3 ); // Send the data
digitalWrite ( _cs, HIGH ); // Deselect the device
//_spi->endTransaction();
}
/*
* "SetOffset" sets the frequency offset registers to correspond to the number of offset steps
*/
void Si4432::SetOffset ( int32_t offset )
{
uint8_t registerBuf[3]; // Used to send data in burst mode
uint16_t posOffset;
if (offset < 0)
posOffset = -offset;
else
posOffset = offset;
uint16_t fo = posOffset;
if (offset < 0)
// do twos complement - invert the bits (0-9) (use XOR) and add one
fo = (fo ^ 0x3FF) + 1;
//Serial.printf(" posoffset %3x, offset %i fo=%3x \n", posOffset, offset, fo);
// write to the Si4432
registerBuf[0] = REG_FOFF1|0x80; // First register in write mode (bit 7 set)
registerBuf[1] = fo & 0xFF; // first 8 bits
registerBuf[2] = (fo >> 8 ) & 0x03; // last 2 bits
//_spi->beginTransaction ( SPISettings ( BUS_SPEED, BUS_ORDER, BUS_MODE ));
// spiSimpleTransaction(_spi->bus());
digitalWrite ( _cs, LOW ); // Select the correct device
_spi->transfer ( registerBuf, 3 ); // Send the data
digitalWrite ( _cs, HIGH ); // Deselect the device
//_spi->endTransaction();
}
/*
* "SetRBW" Sets the "Resolution Bandwidth" based on a required value passed in
* and returns the actual value chosen as well as the required delay to allow the
* FIR filter in the SI4432 to settle (in microseconds) Delay time is longer for
* narrower bandwidths.
*/
float Si4432::SetRBW ( float reqBandwidth10, unsigned long* delaytime_p ) // "reqBandwidth" in kHz * 10
{
int filter = _bpfCount-1; // Elements in the "bandpassFilters" array
// Serial.printf ( "bpfCount %i\n", filter );
/*
* "filter" is the index into "bandpassFilters" array. If the requested
* bandwidth is less than the bandwith in the first entry, we use that entry (2.6KHz).
*/
if ( reqBandwidth10 <= _bandpassFilters[0].bandwidth10 )
{
filter = 0;
Serial.print ("Minimum RBW");
}
/*
* If the requested bandwidth is greater or equal to the value in the first entry,
* find the setting that is nearest (and above) the requested setting.
*/
else
while (( _bandpassFilters[filter-1].bandwidth10 > reqBandwidth10 - 0.01 ) && ( filter > 0 ))
filter--;
Serial.printf ( "Required = %f, filter = %i\n", reqBandwidth10, filter); Serial.println ( filter );
/*
* Ok, we found the appropriate setting (or ended up with the maximum one),
* formulate the byte to sent to the "REG_IFBW" register (0x1C) from the piece parts.
*/
byte BW = ( _bandpassFilters[filter].dwn3_bypass << 7 )
| ( _bandpassFilters[filter].ndec_exp << 4 )
| _bandpassFilters[filter].filset;
WriteByte ( REG_IFBW ,BW ); // Send the bandwidth setting to the Si4432
/*
* "Oversampling rate for clock recovery". Let me know if you understand the explanation
* in Tech Note A440!
*/
float rxosr = 500.0 * ( 1.0 + 2.0 * _bandpassFilters[filter].dwn3_bypass )
/ ( pow ( 2.0, ( _bandpassFilters[filter].ndec_exp - 3.0 ))
* _bandpassFilters[filter].bandwidth10 / 10.0 );
byte integer = (byte) rxosr ;
byte fractio = (byte) (( rxosr - integer ) * 8 );
byte memory = ( integer << 3 ) | ( 0x07 & fractio );
WriteByte ( REG_CROSR , memory ); // Clock recovery oversampling rate
/*
* Set the bandwidth and delay time in the "RBW" structure returned by the function
* and in our internal variables.
*/
_bw = _bandpassFilters[filter].bandwidth10 / 10.0;
_dt = _bandpassFilters[filter].settleTime;
*delaytime_p = _dt;
return _bw;
}
void Si4432::SetPreampGain ( int gain ) // Sets preamp gain
{
WriteByte ( REG_AGCOR1, gain ); // Just feed it to the Si4432
_gainReg = gain;
_autoGain = (bool)(gain & AGCEN);
// Serial.printf("Si4432 set gain:%i, auto:%i\n", _gainReg, _autoGain);
}
/*
* "GetPreampGain" was added by M0WID to read the LNA/PGA gain from the RX Si4432. Later
* modified to return the AGC setting state (on or off) and the "PGAGAIN | LNAGAIN"
* settings via the pointers in the argument list.
*/
int Si4432::GetPreampGain ()
{
if (_autoGain) {
return ReadByte ( REG_AGCOR1 ); // Just return the register
} else {
return _gainReg;
}
}
int Si4432::ReadPreampGain ()
{
return ReadByte ( REG_AGCOR1 ); // Just return the register
}
/*
* "PreAmpAGC" returns "true" if the AGC is set to auto:
*/
bool Si4432::PreAmpAGC () // Return true if agc set to auto
{
/*
* Register 0x69 (REG_AGCOR1) contains the current setting of the LNA and PGA
* amplifiers. If AGC is enabled the value can vary during a sweep
*/
byte Reg69 = ReadByte ( REG_AGCOR1 ); // Read the register
// Serial.printf ( "REG_AGCOR1 %X \n", Reg69 ); // Debugging
return ( Reg69 & AGCEN ); // And the answer is!
}
bool Si4432::GetPreAmpAGC () // Return true if agc set to auto
{
return ( _autoGain );
}
/*
* "GetRSSI" is perhaps the most important function in the whole TinySA program.
* It returns the "Received Signal Strength Indicator" value from the receiver
* module. The RSSI is essentially an "S" meter indication from the receiver that
* will be used to measure the received level. The daya dheet indicates and accuracy
* of +/- 0.5dB.
*/
uint8_t Si4432::GetRSSI ()
{
uint8_t rawRSSI;
rawRSSI = ReadByte ( REG_RSSI );
// float dBm = 0.5 * rawRSSI - 120.0 ;
// Serial.println ( dBm, 2 );
return rawRSSI ;
}
/*
* "SetPowerReference" - Set the GPIO-2 output for the LO (TX) SI4432 to required
* frequency, or off.
*
* If freq < 0 or > 6 GPIO-2 is grounded
*
* Freq = 0 30MHz
* Freq = 1 15MHz
* Freq = 2 10MHz
* Freq = 3 4MHz
* Freq = 4 3MHz
* Freq = 5 2MHz
* Freq = 6 1MHz
*/
void Si4432::SetPowerReference ( int freq )
{
if ( freq < 0 || freq > 6 ) // Illegal frequency selection?
WriteByte ( REG_GPIO2, 0x1F ); // Set GPIO-2 to ground
else
{
WriteByte ( REG_GPIO2, 0xC0 ); // Maximum drive and microcontroller clock output
WriteByte ( REG_MCOC, freq & 0x07 ); // Set GPIO-2 frequency as specified
}
}
/*
* "SetDrive" can be used to to set the transmitter (aka local oscillator) power
* output level.
*
* The drive value can be from '0' to '7' and the output power will be set
* according to the following table (from page 39 of the Si4432 datasheet):
*
* 0 => +1dBm 4 => +11dBm
* 1 => +2dBm 5 => +14dBm
* 2 => +5dBm 6 => +17dBm
* 3 => +8dBm 7 => +20dBm
*/
void Si4432::SetDrive ( uint8_t level ) // Sets the LO drive level
{
if (( level < 0 ) || ( level > 7 ))
{
// Serial.printf ( "VFO %i Drive request refused level was %i\n", _type, level );
return;
}
else
{
_pwr = level;
WriteByte ( REG_TXPWR, _pwr ); // Set power level
// Serial.printf ( "VFO %i Drive set to %i\n", _type, _pwr );
}
}
/*
* The transmitter module isn't always in transmit mode and the receiver module isn't
* always in receive mode, so these two functions allow us to switch modes for one or
* the other:
*/
void Si4432::TxMode ( uint8_t level ) // Put module in TX mode
{
WriteByte ( REG_OFC1, ( TXON | PLLON | XTON )); // Transmitter, PLL and "Ready Mode" all on
SetDrive ( level );
}
void Si4432::RxMode () // Put module in RX mode
{
WriteByte ( REG_OFC1, ( RXON | PLLON | XTON )); // Receiver, PLL and "Ready Mode" all on
}
void Si4432::ReadyMode () // Put module into ready mode, rx and tx off
{
WriteByte ( REG_OFC1, XTON ); // "Ready Mode" on
}
void Si4432::SetMode (uint8_t newMode)
{
WriteByte ( REG_OFC1, newMode ); // "Ready Mode" on
}
uint8_t Si4432::GetMode ()
{
return ReadByte ( REG_OFC1 );
}
/*
* "Tune" sets the crystal tuning capacitance.
*
* We need to cycle the PLL to force a recalibration after the tune
*/
void Si4432::Tune ( uint8_t cap ) // Set the crystal tuning capacitance
{
_capacitance = cap; // Save in local data
WriteByte ( REG_COLC, _capacitance ); // Send to the Si4432
uint8_t currentMode = ReadByte ( REG_OFC1 );
ReadyMode ();
delayMicroseconds(300);
SetMode ( currentMode );
}
/*
* Get frequency from Si4432
*/
uint32_t Si4432::GetFrequency ()
{
return _freq;
}
uint32_t Si4432::ReadFrequency ()
{
uint8_t fbs = ReadByte(REG_FBS);
uint16_t ncf1 = ReadByte(REG_NCF1);
uint16_t ncf0 = ReadByte(REG_NCF0);
uint32_t fc = ( ncf1<<8 ) + ncf0;
uint32_t fb = ( fbs & F_BAND ) ;
uint32_t hb = ( fbs & HBSEL ) >> 5; // HBSEL is bit 5
// Serial.printf ( "FBS=%X ncf1=%X ncf0=%X HBSEL=%X F_BAND=%X fc=%X (%u)\n",
// fbs, ncf1, ncf0, hb, fb, fc, fc);
uint32_t f = (uint32_t) ( 10000000.0 * ( (float) hb + 1.0 )
* ( (float) fb + 24.0 + ( (float) fc ) / 64000.0 ));
return f;
}
/*
* "GetBandpassFilter10" - Get filter bandwidth * 10 from the specifed element of
* the bandpassfilter array
*/
uint16_t Si4432::GetBandpassFilter10 ( uint8_t index )
{
if ( index >= _bpfCount )
return 0;
else
return _bandpassFilters[index].bandwidth10;
}
uint8_t Si4432::GetBandpassFilterCount ()
{
return _bpfCount;
}
/*
* For debugging, we can do a register dump on the module.
*/
void Si4432::PrintRegs () // Dump all the Si4432 registers
{
for ( int r = 0; r < 0x80; r++)
if ( _type == RX_4432 ) // Receiver?
Serial.printf ( "RX Reg[0x%02X] = 0x%02X\n", r, ReadByte ( r ));
else if ( _type == TX_4432 ) // Transmitter?
Serial.printf ( "TX Reg[0x%02X] = 0x%02X\n", r, ReadByte ( r ));
else if ( _type == TGIF_4432 ) // Transmitter?
Serial.printf ( "TGIF Reg[0x%02X] = 0x%02X\n", r, ReadByte ( r ));
else if ( _type == TGLO_4432 ) // Transmitter?
Serial.printf ( "TGLO Reg[0x%02X] = 0x%02X\n", r, ReadByte ( r ));
}