
Waterfall next, then speed up frequency changes with offsets and reduce delaytime. Sort out 1 pixel error at start/end
825 lines
26 KiB
C++
825 lines
26 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, 7500, 0, 5, 1 }, // 0 "AUTO" selection possibility
|
|
{ 31, 7000, 0, 5, 3 }, // 1 If user selects 3KHz -> 3.1KHz actual
|
|
{ 59, 3700, 0, 4, 3 }, // 2 "AUTO" selection possibility
|
|
{ 106, 2500, 0, 3, 2 }, // 3 If user selects 10KHz -> 10.6KHz actual
|
|
{ 322, 1000, 0, 2, 6 }, // 4 If user selects 30KHz -> 32.2KHz actual
|
|
{ 377, 1000, 0, 1, 1 }, // 5 "AUTO" selection possibility
|
|
{ 562, 700, 0, 1, 5 }, // 6 "AUTO" selection possibility
|
|
{ 832, 600, 0, 0, 2 }, // 7 "AUTO" selection possibility
|
|
{ 1121, 500, 0, 0, 5 }, // 8 If user selects 100KHz -> 112.1KHz actual
|
|
{ 1811, 500, 1, 1, 9 }, // 9 "AUTO" selection possibility
|
|
{ 2488, 450, 1, 0, 2 }, // 10 "AUTO" selection possibility
|
|
{ 3355, 400, 1, 0, 8 }, // 11 If user selects 300KHz -> 335.5KHz actual
|
|
{ 3618, 300, 1, 0, 9 }, // 12 "AUTO" selection possibility
|
|
{ 4685, 300, 1, 0, 11 }, // 13 "AUTO" selection possibility
|
|
{ 6207, 300, 1, 0, 14 } // 14 "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
|
|
SubInit (); // Things common to both modules
|
|
|
|
|
|
/*
|
|
*
|
|
* 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
|
|
*/
|
|
|
|
WriteByte ( REG_OFC1, ( RXON | PLLON | XTON )); // Receiver, PLL and "Ready Mode" all on
|
|
Tune ( cap ); // Set the crystal capacitance to fine tune frequency
|
|
|
|
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;
|
|
}
|
|
|
|
|
|
//bool Si4432::TX_Init ( uint8_t cap, uint8_t power )
|
|
//{
|
|
// _type = TX_4432; // We're a transmitter
|
|
//
|
|
// if ( !Reset () ) // Reset the module
|
|
// return false; // If that failed
|
|
// _pwr = power; // Set the transmitter power level
|
|
// SubInit (); // Things common to both modules
|
|
//
|
|
//
|
|
///*
|
|
// * Settings specific to the transmitter module.
|
|
// *
|
|
// * This is almost identical to how we set up the receiver except we turn
|
|
// * on the "TXON" bit (0x08) instead of the "RXON" bit. We also set the
|
|
// * transmitter power based on the mixer module being used. ("CalcPower"
|
|
// * function sets the value).
|
|
// *
|
|
// * GPIO-2 is handled differently; here, we set GPIO-2 to output the
|
|
// * microcontroller clock at maximum drive level (0xC0). We also set the
|
|
// * microcontroller clock speed to 10MHz.
|
|
// *
|
|
// * 03/24 - Logic verified against A440
|
|
// */
|
|
//
|
|
// Tune ( cap ); // Set the crystal capacitance
|
|
// WriteByte ( REG_TXPWR, _pwr ); // Power based on mixer in use
|
|
//
|
|
// WriteByte ( REG_GPIO2, 0xC0 ); // Maximum drive and microcontroller clock output
|
|
// WriteByte ( REG_MCOC, 0x02 ); // Set 10MHz clock output
|
|
//
|
|
// SetFrequency ( 443920000 ); // 433.92MHz (setting.IF_Freq???)
|
|
//
|
|
// delayMicroseconds ( 300 ); // Doesn't work without this!
|
|
//
|
|
// WriteByte ( REG_OFC1, ( TXON | PLLON | XTON )); // Transmitter, PLL and "Ready Mode" all on
|
|
//
|
|
//// Serial.println ( "End of TX_Init" );
|
|
//
|
|
// return true;
|
|
//}
|
|
|
|
|
|
/*
|
|
* "SubInit" is used by the "Init" function to set up
|
|
* all the registers.
|
|
*/
|
|
|
|
void Si4432::SubInit ()
|
|
{
|
|
|
|
|
|
|
|
/*
|
|
* "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
|
|
|
|
}
|
|
|
|
|
|
/*
|
|
* "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/low band select bit
|
|
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.25 in low band, 312.5 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, but only update what is needed
|
|
*/
|
|
uint8_t ncf1 = ( carrier >> 8 ) & 0xFF;
|
|
|
|
// if (fbs != _fbs) // write all three registers
|
|
// {
|
|
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;
|
|
// }
|
|
// else if (ncf1 != _ncf1) // Only write both bytes of the carrier data
|
|
// {
|
|
// registerBuf[0] = REG_NCF1|0x80; // First register in write mode (bit 7 set)
|
|
// registerBuf[1] = ncf1 ; // NCF1 value
|
|
// registerBuf[2] = carrier & 0xFF; // NCF0 value
|
|
// digitalWrite ( _cs, LOW ); // Select the correct device
|
|
// _spi->transfer ( registerBuf, 3 ); // Send the data
|
|
// digitalWrite ( _cs, HIGH ); // Deselect the device
|
|
// _ncf1 = ncf1;
|
|
// }
|
|
// else // Only write the least significant byte of the carrier register
|
|
// {
|
|
// registerBuf[0] = REG_NCF0|0x80; // First register in write mode (bit 7 set)
|
|
// registerBuf[1] = carrier & 0xFF; // NCF0 value
|
|
// digitalWrite ( _cs, LOW ); // Select the correct device
|
|
// _spi->transfer ( registerBuf, 2 ); // Send the data
|
|
// digitalWrite ( _cs, HIGH ); // Deselect the device
|
|
//
|
|
// }
|
|
|
|
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);
|
|
|
|
// _spi->endTransaction(); // Release the bus
|
|
// delayMicroseconds ( WRITE_DELAY ); // Delay needed when writing frequency
|
|
|
|
// 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 ); // M0WID - Delay depends on RBW
|
|
}
|
|
|
|
|
|
/*
|
|
* "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;
|
|
|
|
/*
|
|
* 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.print ( "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
|
|
}
|
|
|
|
|
|
/*
|
|
* "Tune" sets the crystal tuning capacitance.
|
|
*/
|
|
|
|
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
|
|
}
|
|
|
|
/*
|
|
* 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 ));
|
|
}
|