📄 si57x_freqprogfirmware_withf300.c
字号:
// of the valid HS_DIV values from the datasheet.
// initial_n1 conversion
initial_n1 = (( reg[0] & 0x1F ) << 2 ) + // get reg 7 bits 0 to 4
(( reg[1] & 0xC0 ) >> 6 ); // add with reg 8 bits 7 and 8
if(initial_n1 == 0)
{
initial_n1 = 1;
}
else if(initial_n1 & 1 != 0)
{
// add one to an odd number
initial_n1 = initial_n1 + 1;
}
// (old method) RFREQ conversion (reconstruct the fractional portion (bits 0 to 28) from the registers)
// this method requires double precision floating point data type to be accurate
/* frac_bits = (( reg[2] & 0xF ) * POW_2_24 );
frac_bits = frac_bits + (reg[3] * POW_2_16);
frac_bits = frac_bits + (reg[4] * 256);
frac_bits = frac_bits + reg[5];
rfreq = frac_bits;
rfreq = rfreq / POW_2_28;
*/
// (new method) RFREQ conversion using unsigned long when only a single precision floating point data type is available
initial_rfreq_long = ( reg[1] & 0x3F );
initial_rfreq_long = (initial_rfreq_long << 8) + ( reg[2] );
initial_rfreq_long = (initial_rfreq_long << 8) + ( reg[3] );
initial_rfreq_long = (initial_rfreq_long << 8) + ( reg[4] );
initial_rfreq_long = (initial_rfreq_long << 6) + ( reg[5] >> 2 ); //ignore lowest two bits to fit into long
// (old method) RFREQ conversion (reconstruct the integer portion from the registers)
rfreq = rfreq +
( (( reg[1] & 0x3F ) << 4 ) +
(( reg[2] & 0xF0 ) >> 4 ) );
// (old method) crystal frequency (fxtal) calculation -- new method avoids this conversion to save precision
fxtal = (fout0 * initial_n1 * initial_hsdiv) / rfreq; //MHz
}
//
// Program the next frequency in the list
//
void RunFreqProg()
{
float ratio = 0;
currentFrequency = fout1[currFreq];
// find dividers (get the max and min divider range for the HS_DIV and N1 combo)
divider_max = floorf(FDCO_MAX / fout1[currFreq]); //floorf for SDCC
curr_div = ceilf(FDCO_MIN / fout1[currFreq]); //ceilf for SDCC
validCombo = 0;
while (curr_div <= divider_max)
{
//check all the HS_DIV values with the next curr_div
for(counter=0; counter<6; counter++)
{
// get the next possible n1 value
hsdiv = HS_DIV[counter];
curr_n1 = (curr_div * 1.0) / (hsdiv * 1.0);
// determine if curr_n1 is an integer and an even number or one
// then it will be a valid divider option for the new frequency
n1_tmp = floorf(curr_n1);
n1_tmp = curr_n1 - n1_tmp;
if(n1_tmp == 0.0)
{
//then curr_n1 is an integer
n1 = (unsigned char) curr_n1;
if( (n1 == 1) || ((n1 & 1) == 0) )
{
// then the calculated N1 is either 1 or an even number
validCombo = 1;
}
}
if(validCombo == 1) break;
}
if(validCombo == 1) break;
//increment curr_div to find the next divider
//since the current one was not valid
curr_div = curr_div + 1;
}
// if(validCombo == 0) at this point then there's an error
// in the calculatio. Check if the provided fout0 and fout1
// are not valid frequencies
// (old method) new RFREQ calculation -- kept for comparison purposes
rfreq = (fout1[currFreq] * n1 * hsdiv) / fxtal; //using float
// (new method) calculate RFREQ organizing the float variables to save precision;
// RFREQ is kept as an unsigned long
// only 32 bits are available in the long format
// RFREQ in the device has 34 bits of precision
// only 34 of the 38 bits are needed since RFREQ is between 42.0 and 50.0 for fxtal of 114.285MHz (nominal)
ratio = fout1[currFreq] / fout0; // try to keep ration near 1 to maintain precision
ratio = ratio * ((1.0 * n1)/(1.0 * initial_n1));
ratio = ratio * ((1.0 * hsdiv)/(1.0 * initial_hsdiv));
final_rfreq_long = ratio * initial_rfreq_long; //using alternative method (unsigned long)
for(counter = 0; counter < 6; counter++)
{
reg[counter] = 0; //clear registers
}
// new HS_DIV conversion
hsdiv = hsdiv - 4;
//reset this memory
reg[0] = 0;
//set the top 3 bits of reg 13
reg[0] = (hsdiv << 5);
// convert new N1 to the binary representation
if(n1 == 1) n1 = 0;
else if((n1 & 1) == 0) n1 = n1 - 1; //if n1 is even, subtract one
// set reg 7 bits 0 to 4
reg[0] = SetBits(reg[0], 0xE0, n1 >> 2);
// set reg 8 bits 6 and 7
reg[1] = (n1 & 3) << 6;
// (new method) load new RFREQ into register map
reg[1] = reg[1] | (((final_rfreq_long >> 10) >> 10) >> 10); //SDCC does not like to shift by more than ~12
reg[2] = ((final_rfreq_long >> 11) >> 11);
reg[3] = ((final_rfreq_long >> 7) >> 7);
reg[4] = final_rfreq_long >> 6;
reg[5] = final_rfreq_long << 2;
/*
// (old method)
// convert new RFREQ to the binary representation
// separate the integer part
whole = floorf(rfreq);
// get the binary representation of the fractional part
frac_bits = floorf((rfreq - whole) * POW_2_28);
// set reg 12 to 10 making frac_bits smaller by
// shifting off the last 8 bits everytime
for(counter=5; counter >=3; counter--)
{
reg[counter] = frac_bits & 0xFF;
frac_bits = frac_bits >> 8;
}
// set the last 4 bits of the fractional portion in reg 9
reg[2] = SetBits(reg[2], 0xF0, (frac_bits & 0xF));
// set the integer portion of RFREQ across reg 8 and 9
reg[2] = SetBits(reg[2], 0x0F, (whole & 0xF) << 4);
reg[1] = SetBits(reg[1], 0xC0, (whole >> 4) & 0x3F);
*/
// Load the new frequency
// get the current state of register 137
reg137 = I2C_ByteRead(137);
// set the Freeze DCO bit in that register
I2C_ByteWrite(137, reg137 | 0x10);
// load the new values into the device at registers 7 to 12; this MCU code only supports single I2C address writes
for(counter=0; counter<6; counter++)
{
I2C_ByteWrite(counter+7, reg[counter]);
}
// get the current state of register 137
reg137 = I2C_ByteRead(137);
// clear the FZ_DCO bit in that register
I2C_ByteWrite(137, reg137 & 0xEF);
// set the NewFreq bit, bit will clear itself once the device is ready
I2C_ByteWrite(135, 0x40);
}
unsigned char SetBits(unsigned char original, unsigned char reset_mask, unsigned char new_val)
{
return (( original & reset_mask ) | new_val );
}
//-----------------------------------------------------------------------------
// Initialization Routines
//-----------------------------------------------------------------------------
void Peripheral_Config(void){
unsigned char i; // Temporary counter variable
PCA0MD &= ~0x40; // WDTE = 0 (disable watchdog timer)
// Set internal oscillator to highest
// setting of 24500000
OSCICN |= 0x03;
// If slave is holding SDA low because of an improper SMBus reset or error
while(!SDA)
{
// Provide clock pulses to allow the slave to advance out
// of its current state. This will allow it to release SDA.
XBR1 = 0x40; // Enable Crossbar
SCL = 0; // Drive the clock low
for(i = 0; i < 255; i++); // Hold the clock low
SCL = 1; // Release the clock
while(!SCL); // Wait for open-drain
// clock output to rise
for(i = 0; i < 10; i++); // Hold the clock high
XBR1 = 0x00; // Disable Crossbar
}
Port_Initializer (); // Initialize Crossbar and GPIO
CKCON = 0x10; // Timer 1 is sysclk
// Timer 2 is sysclk/12 (see TMR2CN)
Timer1_Init (); // Configure Timer1 for use as SMBus
// clock source
Timer2_Init (); // Configure Timer2 for use with SMBus
// low timeout detect
SMBus_Init (); // Configure and enable SMBus
IP |= 0x01;
EIE1 |= 0x01; //enable SMBus interrupt
IT01CF |= 0x07; //0x03;
IE |= 0x81;
TCON |= 0x01; // Enable INT0 to be edge sensitive
EA = 1; // Global interrupt enable ****MUST BE LAST****
}
//-----------------------------------------------------------------------------
// SMBus_Init
//-----------------------------------------------------------------------------
//
// Return Value : None
// Parameters : None
//
// The SMBus peripheral is configured as follows:
// - SMBus enabled
// - Slave mode disabled
// - Timer1 used as clock source. The maximum SCL frequency will be
// approximately 1/3 the Timer1 overflow rate
// - Setup and hold time extensions enabled
// - Free and SCL low timeout detection enabled
//
void SMBus_Init (void)
{
SMB0CF = 0x5D; // Use Timer1 overflows as SMBus clock
// source;
// Disable slave mode;
// Enable setup & hold time extensions;
// Enable SMBus Free timeout detect;
// Enable SCL low timeout detect;
SMB0CF |= 0x80; // Enable SMBus;
}
//-----------------------------------------------------------------------------
// Timer1_Init
//-----------------------------------------------------------------------------
//
// Return Value : None
// Parameters : None
//
// Timer1 is configured as the SMBus clock source as follows:
// - Timer1 in 8-bit auto-reload mode
// - SYSCLK / 12 as Timer1 clock source
// - Timer1 overflow rate => 3 * SMB_FREQUENCY
// - The maximum SCL clock rate will be ~1/3 the Timer1 overflow rate
// - Timer1 enabled
//
void Timer1_Init (void){
// Make sure the Timer can produce the appropriate frequency in 8-bit mode
// Supported SMBus Frequencies range from 10kHz to 100kHz. The CKCON register
// settings may need to change for frequencies outside this range.
TMOD = 0x20; // Timer1 in 8-bit auto-reload mode
//CKCON |= 0x10; // Timer1 clock source = SYSCLK
TH1 = 0xFF - (SYSCLK/SMB_FREQUENCY/3) + 1; // 100kHz or 400kHz for SCL
TL1 = TH1; // Init Timer1
TR1 = 1; // Timer1 enabled
}
//-----------------------------------------------------------------------------
// Timer2_Init
//-----------------------------------------------------------------------------
//
// Return Value : None
// Parameters : None
//
// Timer2 configured for Si57x reset delay as
// follows:
// - Timer2 in 16-bit auto-reload mode
// - SYSCLK/12 as Timer2 clock source
// - Timer2 reload registers loaded for RESET_DELAY_TIME overflow
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -