UHSDR/UHSDR-active-devel/mchf-eclipse/drivers/ui/oscillator/soft_tcxo.c
2022-11-08 16:13:55 +01:00

330 lines
9.7 KiB
C

/* -*- mode: c; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4; coding: utf-8 -*- */
/************************************************************************************
** **
** UHSDR **
** a powerful firmware for STM32 based SDR transceivers **
** **
**---------------------------------------------------------------------------------**
** **
** File name: **
** Description: **
** Last Modified: **
** Licence: GNU GPLv3 **
************************************************************************************/
//
// SI570 frequency shift table
// 0-100 degrees C range
// shots from 20m band (56Mhz)
//
// This frequency compensation table referenced to 14.000 MHz by KA7OEI, 10/14 using
// a GPS reference in a temperature-controlled environment, extrapolated below 17C and above
// 70C.
//
// Temperature normalized to 43C, a temperature achieved very soon after power-up
// with both the Si570 and the temperature sensor thermally bonded.
//
// Note: The exact frequency/temperature dependencies will likely vary from unit to unit
// for each Si570, but the values below appear to approximately follow typical AT-cut
// temperature-frequency curves.
//
#include "uhsdr_board.h"
#include "soft_tcxo.h"
#include "radio_management.h"
#include "uhsdr_hw_i2c.h"
LoTcxo lo;
// -------------------------------------------------------------------------------------
// Temperature sensor
// ------------------
#define MCP_ADDR (0x90)
// MCP registers
#define MCP_TEMP (0x00)
#define MCP_CONFIG (0x01)
#define MCP_HYSTR (0x02)
#define MCP_LIMIT (0x03)
// MCP CONFIG register bits
#define MCP_ONE_SHOT (7)
#define MCP_ADC_RES (5)
#define MCP_FAULT_QUEUE (3)
#define MCP_ALERT_POL (2)
#define MCP_INT_MODE (1)
#define MCP_SHUTDOWN (0)
#define R_BIT (1)
#define W_BIT (0)
#define MCP_ADC_RES_9 0
#define MCP_ADC_RES_10 1
#define MCP_ADC_RES_11 2
#define MCP_ADC_RES_12 3
#define MCP_POWER_UP 0
#define MCP_POWER_DOWN 1
static int32_t MCP9801_ConvExternalTemp(uint8_t *temp)
{
int32_t ts = 0;
ts = temp[1];
ts += (int8_t)temp[0] << 8;
ts *= 10000;
ts /= 256;
return ts;
}
/*
* @brief configures the MCP9801 external temperature sensor for monitoring Si570 temperature
* @returns 0 if detected and working, 1 if not detected on I2C, 2 for other errors
*/
static uint8_t MCP9801_InitExternalTempSensor(void)
{
uint8_t config, retval = 0;
uint16_t res;
// Read config reg
res = UhsdrHw_I2C_ReadRegister(MCP_I2C, MCP_ADDR, MCP_CONFIG, 1, &config);
if(res != 0)
{
retval = 1;
}
else
{
// Modify resolution
config &= ~(3 << MCP_ADC_RES);
config |= (MCP_ADC_RES_12 << MCP_ADC_RES);
// Modify power mode
config &= ~(1 << MCP_SHUTDOWN);
config |= (MCP_POWER_UP << MCP_SHUTDOWN);
// Write config reg
res = UhsdrHw_I2C_WriteRegister(MCP_I2C, MCP_ADDR, MCP_CONFIG, 1, config);
if(res != 0)
{
retval = 2;
}
}
return retval;
}
static uint8_t MCP9801_ReadExternalTempSensor(int32_t *temp)
{
uint8_t data[2];
uint8_t retval = 0;
// Read temperature
if(temp != NULL && UhsdrHw_I2C_ReadBlock(MCP_I2C, MCP_ADDR, MCP_TEMP, 1, data, 2) == 0)
{
*temp = MCP9801_ConvExternalTemp(data);
}
else
{
retval = 2;
}
return retval;
}
#define TCXO_TBL_SIZE 100
const short tcxo_table_20m[TCXO_TBL_SIZE] =
{
-165 // 0 C
-162, // 1 C
-160, // 2 C
-157, // 3 C
-155, // 4 C
-152, // 5 C
-150, // 6 C
-148, // 7 C
-144, // 8 C
-142, // 9 C
-136, // 10 C
-132, // 11 C
-130, // 12 C
-129, // 13 C
-126, // 14 C
-121, // 15 C
-116, // 16 C
-112, // 17 C
-108, // 18 C
-103, // 19 C
-99, // 20 C
-95, // 21 C
-90, // 22 C
-86, // 23 C
-81, // 24 C
-75, // 25 C
-70, // 26 C
-66, // 27 C
-61, // 28 C
-57, // 29 C
-53, // 30 C
-46, // 31 C
-42, // 32 C
-38, // 33 C
-32, // 34 C
-28, // 35 C
-25, // 36 C
-20, // 37 C
-17, // 38 C
-13, // 39 C
-9, // 40 C
-5, // 41 C
-2, // 42 C
1, // 43 C
4, // 44 C
7, // 45 C
10, // 46 C
12, // 47 C
15, // 48 C
16, // 49 C
18, // 50 C
20, // 51 C
21, // 52 C
22, // 53 C
23, // 54 C
23, // 55 C
24, // 56 C
23, // 57 C
23, // 58 C
22, // 59 C
21, // 60 C
20, // 61 C
18, // 62 C
16, // 63 C
14, // 64 C
11, // 65 C
7, // 66 C
3, // 67 C
-1, // 68 C
-5, // 69 C
-11,// 70 C
-17, // 71 C
-24, // 72 C
-31, // 73 C
-38, // 74 C
-45, // 75 C
-52, // 76 C
-58, // 77 C
-67, // 78 C
-71, // 79 C
-77, // 80 C
-83, // 81 C
-89, // 82 C
-95, // 83 C
-102, // 84 C
-108, // 85 C
-115, // 86 C
-121, // 87 C
-127, // 88 C
-134, // 89 C
-141, // 90 C
-147, // 91 C
-153, // 92 C
-159, // 93 C
-166, // 94 C
-177, // 95 C
-184, // 96 C
-190, // 97 C
-197, // 98 C
-203 // 99 C
};
void SoftTcxo_Init()
{
lo.comp = 0;
// Temp sensor setup
lo.sensor_present = MCP9801_InitExternalTempSensor() == 0;
}
/*
* @brief measure local oscillator temperature and calculates compensation value
* @return true if the temperature value has been measured
*/
bool SoftTcxo_HandleLoTemperatureDrift()
{
int32_t temp = 0;
int comp, comp_p;
float dtemp, remain, t_index;
uchar tblp;
uint8_t temp_mode = RadioManagement_TcxoGetMode();
bool retval = false;
// No need to process if no chip avail or tcxo is disabled
if((lo.sensor_present == true) && RadioManagement_TcxoIsEnabled())
{
{
// Get current temperature
if(MCP9801_ReadExternalTempSensor(&temp) == 0)
{
// Get temperature from sensor with its maximum precision
dtemp = (float)temp; // get temperature
dtemp /= 10000; // convert to decimal degrees
remain = truncf(dtemp); // get integer portion of temperature
remain = dtemp - remain; // get fractional portion
// Compensate only if enabled
if((temp_mode == TCXO_ON))
{
// Temperature to unsigned table pointer
t_index = (uchar)((temp%1000000)/100000);
t_index *= 10;
t_index += (uchar)((temp%100000)/10000);
// Check for overflow - keep within the lookup table
if((t_index < 0) || (t_index > 150)) // the temperature sensor function "wraps around" below zero
{
t_index = 0; // point at the bottom of the temperature table
dtemp = 0; // zero out fractional calculations
remain = 0;
}
else if(t_index > TCXO_TBL_SIZE - 2) // High temperature - limit to maximum
{
t_index = TCXO_TBL_SIZE - 2; // Point to (near) top of table
dtemp = 0; // zero out fractional calculations
remain = 0;
}
tblp = (uchar)t_index; // convert to index
comp = tcxo_table_20m[tblp]; // get the first entry in the table
comp_p = tcxo_table_20m[tblp + 1]; // get the next entry in the table to determine fraction of frequency step
comp_p = comp_p - comp; // // get frequency difference between the two steps
dtemp = (float)comp_p; // change it to float for the calculation
dtemp *= remain; // get proportion of temperature difference between the two steps using the fraction
comp += (int)dtemp; // add the compensation value to the lower of the two frequency steps
// Change needed ?
if(lo.comp != comp) // is it there a difference?
{
// Update frequency, without reflecting it on the LCD
df.temp_factor = comp;
df.temp_factor_changed = true;
lo.comp = comp;
}
}
// Refresh UI
retval = true;
lo.temp = temp;
}
}
}
return retval;
}