UHSDR/UHSDR-active-devel/mchf-eclipse/hardware/uhsdr_hmc1023.c
2022-08-24 08:39:13 +02:00

304 lines
9.2 KiB
C
Executable File

/* -*- 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 **
** **
**---------------------------------------------------------------------------------**
** **
** Licence: GNU GPLv3 **
************************************************************************************/
#include "uhsdr_board.h"
#include "uhsdr_hmc1023.h"
HMC1023_t hmc1023;
#ifdef USE_HMC1023
#define HMC1023_CS_PIN GPIO_PIN_9
#define HMC1023_CS_PORT GPIOG
#define hspiHmc1023 (hspi6)
#include <spi.h>
#define HMC1023_REG3_FINE_LIMIT (11)
#define HMC1023_REG2_OPAMP_LIMIT (3)
#define HMC1023_REG2_OPAMP_SHIFT (0)
#define HMC1023_REG2_OPAMP_MASK (0x03)
#define HMC1023_REG2_DRVR_LIMIT (3)
#define HMC1023_REG2_DRVR_SHIFT (2)
#define HMC1023_REG2_DRVR_MASK (0x03)
#define HMC1023_REG2_GAIN_LIMIT (1)
#define HMC1023_REG2_GAIN_SHIFT (4)
#define HMC1023_REG2_GAIN_MASK (0x01)
#define HMC1023_REG2_BYPASS_LIMIT (1)
#define HMC1023_REG2_BYPASS_SHIFT (5)
#define HMC1023_REG2_BYPASS_MASK (0x01)
#define HMC1023_REG2_COARSE_LIMIT (8)
#define HMC1023_REG2_COARSE_SHIFT (6)
#define HMC1023_REG2_COARSE_MASK (0x0f)
#define HMC1023_REG1_USE_SPI_SETTINGS (1 << 1)
#define HMC1023_REG1_FORCE_CAL_CODE (1 << 4)
/**
* Switch hardware SPI settings from tx to rx mode and vice versa. Also provides necessary delay between
* tx and rx.
* HMC1023LP5E needs phase == 1EDGE for transmit by STM and 2EDGE for RX sampling by STM. Who designs such an SPI protocol?
* @param is_tx true -> configure for tx mode, false -> for rx mode
*/
static void hmc1023_ll_spi_tx(bool is_tx)
{
hspiHmc1023.Init.CLKPhase = is_tx == true? SPI_PHASE_1EDGE : SPI_PHASE_2EDGE;
if (HAL_SPI_Init(&hspi6) != HAL_OK)
{
Error_Handler();
}
__HAL_SPI_ENABLE(&hspi6);
}
/**
* Control selection of HMC1023LP5E. Please note, output of SDO does not go tristate if deselected!
* @param on true -> chip is selected (HMC1023LP5E input EN == LOW), false -> not selected
*/
static void hmc1023_ll_cs(bool on)
{
if (on)
{
GPIO_ResetBits(HMC1023_CS_PORT,HMC1023_CS_PIN);
}
else
{
GPIO_SetBits(HMC1023_CS_PORT,HMC1023_CS_PIN);
}
}
/**
* Write register value into the HMC1023LP5E. It is not checked if the value was received correctly!
* @param regaddr which register to write to, not range checked!
* @param value 24 bit value
* @return HMC1023_ERROR if failed, HMC1023_OK otherwise.
*/
static uint32_t hmc1023_ll_write(uint32_t regaddr, uint32_t value)
{
uint8_t tx_data[4];
uint32_t retval = HMC1023_ERROR;
tx_data[0] = value >> 16;
tx_data[1] = value >> 8;
tx_data[2] = value;
tx_data[3] = 0x5 | (regaddr << 3); // chip id & register to write;
hmc1023_ll_cs(true);
if (HAL_SPI_Transmit(&hspi6,tx_data,4,100) == HAL_OK)
{
retval = HMC1023_OK;
}
hmc1023_ll_cs(false);
return retval;
}
/**
*
* @param regaddr which register to read, not range checked!
* @return 24 bit value in case of success, HMC1023_ERROR otherwise
*/
static uint32_t hmc1023_ll_read(uint32_t regaddr)
{
uint32_t retval = HMC1023_ERROR;
// write register address to read to register 0
if (hmc1023_ll_write(0,regaddr) == HAL_OK)
{
uint8_t tx_data[4] = { 0, 0, 0, 0};
uint8_t rx_data[4];
hmc1023_ll_spi_tx(false);
// reconfig through HAL should cause enough delay
hmc1023_ll_cs(true);
// now all tx_data is zero and we try to read using
// the rx spi config
if (HAL_SPI_TransmitReceive(&hspi6,tx_data,rx_data, 4,100) == HAL_OK)
{
if (( rx_data[3] & 0x07) == 5) // we found the correct "chip id"
{
retval = rx_data[0] << 16 | rx_data[1] << 8 | rx_data[2];
}
}
hmc1023_ll_cs(false);
hmc1023_ll_spi_tx(true);
}
return retval;
}
/**
*
* @return true if HMC1023LP5E was detected by reading the correct id
*/
static bool hmc1023_ll_presence(void)
{
uint32_t value = hmc1023_ll_read(0);
return (value == 0x114080);
}
/**
* Set bits in register 2
* @param bits actual bits to set (zero based, bits will be later shifted)
* @param limit maximum permitted value for bits (0...limit) is permitted range
* @param mask bit mask for bits (zero based, bits will be later shifted)
* @param shift position of mask (how many bits to shift left for final position)
*/
static void hmc1023_set_reg2_bits(uint8_t bits,uint8_t limit, uint32_t mask, uint8_t shift)
{
if (bits <= limit)
{
const uint32_t shifted_mask = (mask << shift);
const uint32_t shifted_bits = (bits << shift);
if ( (hmc1023.reg2 & shifted_mask) != shifted_bits)
{
MODIFY_REG(hmc1023.reg2,shifted_mask,shifted_bits);
hmc1023_ll_write(2,hmc1023.reg2);
}
}
}
/**
* Sets the coarse bandwidth from about 5 / 7 / 10 / 14 / 20 / 28 / 40 / 50 / 72
* Note +/-20% variation if not calibrated!
* @param coarse 0 - 8
*/
void hmc1023_set_coarse(uint8_t value)
{
hmc1023_set_reg2_bits(value,HMC1023_REG2_COARSE_LIMIT,HMC1023_REG2_COARSE_MASK, HMC1023_REG2_COARSE_SHIFT);
}
/**
* Control HMC1023LP5E opamp bias
* @param value: 0 - 3 (higher value, higher bias)
*/
void hmc1023_set_bias_opamp(uint8_t value)
{
hmc1023_set_reg2_bits(value,HMC1023_REG2_OPAMP_LIMIT,HMC1023_REG2_OPAMP_MASK, HMC1023_REG2_OPAMP_SHIFT);
}
/**
* Control HMC1023LP5E driver bias
* @param value: 0 - 3 (higher value, higher bias)
*/
void hmc1023_set_bias_drvr(uint8_t value)
{
hmc1023_set_reg2_bits(value,HMC1023_REG2_DRVR_LIMIT,HMC1023_REG2_DRVR_MASK, HMC1023_REG2_DRVR_SHIFT);
}
/**
* Sets the 10db amplifier on or off
* @param on true on / false off
*/
void hmc1023_set_gain(bool on)
{
hmc1023_set_reg2_bits(on?1:0,HMC1023_REG2_GAIN_LIMIT,HMC1023_REG2_GAIN_MASK, HMC1023_REG2_GAIN_SHIFT);
}
/**
* Sets the LPF bypass on or off
* @param on true on / false off
*/
void hmc1023_set_bypass(bool on)
{
hmc1023_set_reg2_bits(on?1:0,HMC1023_REG2_BYPASS_LIMIT,HMC1023_REG2_BYPASS_MASK, HMC1023_REG2_BYPASS_SHIFT);
}
/**
* adjusts the lpf bandwidth from about 0.803 to about 1.183 of the coarse frequency
* Note +/-20% variation if not calibrated!
* @param fine 0 - 11
*/
void hmc1023_set_fine(uint8_t fine)
{
if (fine < HMC1023_REG3_FINE_LIMIT)
{
if ( (hmc1023.reg3) != fine )
{
hmc1023.reg3 = fine;
hmc1023_ll_write(3,hmc1023.reg3);
}
}
}
/**
* use HMC1023LP5E register settings for coarse and fine LPF bandwidth control
* please note that this enables both coarse and fine bandwidth from SPI, this is incompatible with
* automatic calibration as discussed in data sheet.
* @param on true -> use bandwidth settings in reg2/reg3, false -> use internal defaults
*/
void hmc1023_use_spi_settings(bool on)
{
const uint32_t target_bits = on == true ? (HMC1023_REG1_USE_SPI_SETTINGS | HMC1023_REG1_FORCE_CAL_CODE) : 0;
const uint32_t target_mask = (HMC1023_REG1_USE_SPI_SETTINGS | HMC1023_REG1_FORCE_CAL_CODE);
if ((hmc1023.reg1 & target_mask) != target_bits)
{
MODIFY_REG(hmc1023.reg1, target_mask, target_bits);
hmc1023_ll_write(1,hmc1023.reg1);
}
}
/**
* Configures SPI bus 6 AND CS GPIO PG9 to detect and control an HMC1023LP5E LPF
* presence of an LPF is return via hmc1023.present
* Does not return hardware configuration to original state, even if LPF is not detected!
*/
void hmc1023_init()
{
// TODO: Move to HAL Config
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(HMC1023_CS_PORT, HMC1023_CS_PIN, GPIO_PIN_SET);
GPIO_InitTypeDef GPIO_InitStruct;
/*Configure GPIO pins : PFPin PFPin PFPin */
GPIO_InitStruct.Pin = HMC1023_CS_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(HMC1023_CS_PORT, &GPIO_InitStruct);
// setup SPI with correct data
hspiHmc1023.Init.Mode = SPI_MODE_MASTER;
hspiHmc1023.Init.Direction = SPI_DIRECTION_2LINES;
hspiHmc1023.Init.DataSize = SPI_DATASIZE_8BIT;
hspiHmc1023.Init.CLKPolarity = SPI_POLARITY_LOW;
hspiHmc1023.Init.CLKPhase = SPI_PHASE_1EDGE;
hspiHmc1023.Init.NSS = SPI_NSS_SOFT;
hspiHmc1023.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_8;
hspiHmc1023.Init.FirstBit = SPI_FIRSTBIT_MSB;
hmc1023_ll_spi_tx(true);
// make sure we are in right config for TX,
// which is assumed by the read/write code
hmc1023.present = hmc1023_ll_presence();
if (hmc1023.present)
{
hmc1023.reg1 = hmc1023_ll_read(1);
hmc1023.reg2 = hmc1023_ll_read(2);
hmc1023.reg2 = hmc1023_ll_read(3);
hmc1023_use_spi_settings(true);
hmc1023_set_bias_opamp(1);
hmc1023_set_bias_opamp(0);
}
}
#endif // UI_BRD_OVI40