304 lines
9.2 KiB
C
304 lines
9.2 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 **
|
|
** **
|
|
**---------------------------------------------------------------------------------**
|
|
** **
|
|
** 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
|