Merge pull request #1255 from miek/h1r9-fw
Add firmware support for h1r9
This commit is contained in:
112
firmware/common/clkin.c
Normal file
112
firmware/common/clkin.c
Normal file
@ -0,0 +1,112 @@
|
||||
/*
|
||||
* Copyright 2022 Great Scott Gadgets
|
||||
*
|
||||
* This file is part of HackRF.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "gpdma.h"
|
||||
#include <libopencm3/lpc43xx/timer.h>
|
||||
#include <libopencm3/lpc43xx/scu.h>
|
||||
#include <libopencm3/lpc43xx/gima.h>
|
||||
#include <libopencm3/lpc43xx/creg.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define CLOCK_CYCLES_1_MS (204000)
|
||||
#define MEASUREMENT_WINDOW_MS (50)
|
||||
#define MEASUREMENT_CYCLES (CLOCK_CYCLES_1_MS * MEASUREMENT_WINDOW_MS)
|
||||
|
||||
/* DMA linked list item */
|
||||
typedef struct {
|
||||
uint32_t src;
|
||||
uint32_t dest;
|
||||
uint32_t next_lli;
|
||||
uint32_t control;
|
||||
} dma_lli;
|
||||
|
||||
/* timer control register configuration sequence */
|
||||
typedef struct {
|
||||
uint32_t first_tcr;
|
||||
uint32_t second_tcr;
|
||||
} tcr_sequence;
|
||||
|
||||
dma_lli timer_dma_lli;
|
||||
tcr_sequence reset;
|
||||
|
||||
void clkin_detect_init(void)
|
||||
{
|
||||
/* Timer1 triggers periodic measurement */
|
||||
timer_set_prescaler(TIMER1, 0);
|
||||
timer_set_mode(TIMER1, TIMER_CTCR_MODE_TIMER);
|
||||
TIMER1_MCR = TIMER_MCR_MR0R;
|
||||
TIMER1_EMR = (TIMER_EMR_EMC_SET << TIMER_EMR_EMC0_SHIFT) |
|
||||
(TIMER_EMR_EMC_TOGGLE << TIMER_EMR_EMC3_SHIFT);
|
||||
TIMER1_MR3 = MEASUREMENT_CYCLES;
|
||||
TIMER1_MR0 = MEASUREMENT_CYCLES;
|
||||
|
||||
/* prevent TIMER1_MR3 from interfering with SCT */
|
||||
CREG_CREG6 |= CREG_CREG6_CTOUTCTRL;
|
||||
|
||||
/* Timer2 counts CLKIN */
|
||||
timer_set_prescaler(TIMER2, 0);
|
||||
TIMER2_CCR = TIMER_CCR_CAP3RE;
|
||||
GIMA_CAP2_3_IN = 0x20; // T1_MAT3
|
||||
|
||||
/* measure CLKIN_DETECT signal on P4_8, pin 15, CTIN_5 */
|
||||
TIMER2_CTCR = TIMER_CTCR_MODE_COUNTER_RISING | TIMER_CTCR_CINSEL_CAPN_2;
|
||||
scu_pinmux(P4_8, SCU_GPIO_PDN | SCU_CONF_FUNCTION1); // CTIN_5
|
||||
GIMA_CAP2_2_IN = 0x00; // CTIN_5
|
||||
|
||||
reset.first_tcr = TIMER_TCR_CEN | TIMER_TCR_CRST;
|
||||
reset.second_tcr = TIMER_TCR_CEN;
|
||||
timer_dma_lli.src = (uint32_t) & (reset);
|
||||
timer_dma_lli.dest = (uint32_t) & (TIMER2_TCR);
|
||||
timer_dma_lli.next_lli = (uint32_t) & (timer_dma_lli);
|
||||
timer_dma_lli.control = GPDMA_CCONTROL_TRANSFERSIZE(2) |
|
||||
GPDMA_CCONTROL_SBSIZE(0) // 1
|
||||
| GPDMA_CCONTROL_DBSIZE(0) // 1
|
||||
| GPDMA_CCONTROL_SWIDTH(2) // 32-bit word
|
||||
| GPDMA_CCONTROL_DWIDTH(2) // 32-bit word
|
||||
| GPDMA_CCONTROL_S(0) // AHB Master 0
|
||||
| GPDMA_CCONTROL_D(1) // AHB Master 1
|
||||
| GPDMA_CCONTROL_SI(1) // increment source
|
||||
| GPDMA_CCONTROL_DI(0) // do not increment destination
|
||||
| GPDMA_CCONTROL_PROT1(0) // user mode
|
||||
| GPDMA_CCONTROL_PROT2(0) // not bufferable
|
||||
| GPDMA_CCONTROL_PROT3(0) // not cacheable
|
||||
| GPDMA_CCONTROL_I(0); // interrupt disabled
|
||||
gpdma_controller_enable();
|
||||
GPDMA_C0SRCADDR = timer_dma_lli.src;
|
||||
GPDMA_C0DESTADDR = timer_dma_lli.dest;
|
||||
GPDMA_C0LLI = timer_dma_lli.next_lli;
|
||||
GPDMA_C0CONTROL = timer_dma_lli.control;
|
||||
GPDMA_C0CONFIG = GPDMA_CCONFIG_DESTPERIPHERAL(0x3) // T1_MAT0
|
||||
| GPDMA_CCONFIG_FLOWCNTRL(1) // memory-to-peripheral
|
||||
| GPDMA_CCONFIG_H(0); // do not halt
|
||||
gpdma_channel_enable(0);
|
||||
|
||||
/* start counting */
|
||||
timer_reset(TIMER2);
|
||||
timer_reset(TIMER1);
|
||||
timer_enable_counter(TIMER2);
|
||||
timer_enable_counter(TIMER1);
|
||||
}
|
||||
|
||||
uint32_t clkin_frequency(void)
|
||||
{
|
||||
return TIMER2_CR3 * (1000 / MEASUREMENT_WINDOW_MS);
|
||||
};
|
30
firmware/common/clkin.h
Normal file
30
firmware/common/clkin.h
Normal file
@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright 2022 Great Scott Gadgets
|
||||
*
|
||||
* This file is part of HackRF.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __CLKIN_H__
|
||||
#define __CLKIN_H__
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
void clkin_detect_init(void);
|
||||
uint32_t clkin_frequency(void);
|
||||
|
||||
#endif //__CLKIN_H__
|
@ -30,7 +30,7 @@
|
||||
#ifdef JAWBREAKER
|
||||
#define SUPPORTED_PLATFORM PLATFORM_JAWBREAKER
|
||||
#elif HACKRF_ONE
|
||||
#define SUPPORTED_PLATFORM PLATFORM_HACKRF1_OG
|
||||
#define SUPPORTED_PLATFORM (PLATFORM_HACKRF1_OG | PLATFORM_HACKRF1_R9)
|
||||
#elif RAD1O
|
||||
#define SUPPORTED_PLATFORM PLATFORM_RAD1O
|
||||
#else
|
||||
|
@ -26,8 +26,7 @@
|
||||
#include "sgpio.h"
|
||||
#include "si5351c.h"
|
||||
#include "spi_ssp.h"
|
||||
#include "max2837.h"
|
||||
#include "max2837_target.h"
|
||||
#include "max283x.h"
|
||||
#include "max5864.h"
|
||||
#include "max5864_target.h"
|
||||
#include "w25q80bv.h"
|
||||
@ -35,6 +34,8 @@
|
||||
#include "i2c_bus.h"
|
||||
#include "i2c_lpc.h"
|
||||
#include "cpld_jtag.h"
|
||||
#include "platform_detect.h"
|
||||
#include "clkin.h"
|
||||
#include <libopencm3/lpc43xx/cgu.h>
|
||||
#include <libopencm3/lpc43xx/ccu.h>
|
||||
#include <libopencm3/lpc43xx/scu.h>
|
||||
@ -59,11 +60,8 @@ static struct gpio_t gpio_led[] = {
|
||||
// clang-format off
|
||||
static struct gpio_t gpio_1v8_enable = GPIO(3, 6);
|
||||
|
||||
/* MAX2837 GPIO (XCVR_CTL) PinMux */
|
||||
static struct gpio_t gpio_max2837_select = GPIO(0, 15);
|
||||
static struct gpio_t gpio_max2837_enable = GPIO(2, 6);
|
||||
static struct gpio_t gpio_max2837_rx_enable = GPIO(2, 5);
|
||||
static struct gpio_t gpio_max2837_tx_enable = GPIO(2, 4);
|
||||
/* MAX283x GPIO (XCVR_CTL) PinMux */
|
||||
static struct gpio_t gpio_max283x_select = GPIO(0, 15);
|
||||
|
||||
/* MAX5864 SPI chip select (AD_CS) GPIO PinMux */
|
||||
static struct gpio_t gpio_max5864_select = GPIO(2, 7);
|
||||
@ -134,8 +132,17 @@ static struct gpio_t gpio_cpld_pp_tms = GPIO(1, 1);
|
||||
static struct gpio_t gpio_cpld_pp_tdo = GPIO(1, 8);
|
||||
#endif
|
||||
|
||||
static struct gpio_t gpio_hw_sync_enable = GPIO(5,12);
|
||||
static struct gpio_t gpio_rx_q_invert = GPIO(0, 13);
|
||||
/* other CPLD interface GPIO pins */
|
||||
static struct gpio_t gpio_hw_sync_enable = GPIO(5, 12);
|
||||
static struct gpio_t gpio_q_invert = GPIO(0, 13);
|
||||
|
||||
/* HackRF One r9 */
|
||||
#ifdef HACKRF_ONE
|
||||
static struct gpio_t gpio_h1r9_rx = GPIO(0, 7);
|
||||
static struct gpio_t gpio_h1r9_1v8_enable = GPIO(2, 9);
|
||||
static struct gpio_t gpio_h1r9_vaa_disable = GPIO(3, 6);
|
||||
static struct gpio_t gpio_h1r9_hw_sync_enable = GPIO(5, 5);
|
||||
#endif
|
||||
// clang-format on
|
||||
|
||||
i2c_bus_t i2c0 = {
|
||||
@ -165,7 +172,7 @@ si5351c_driver_t clock_gen = {
|
||||
.i2c_address = 0x60,
|
||||
};
|
||||
|
||||
const ssp_config_t ssp_config_max2837 = {
|
||||
const ssp_config_t ssp_config_max283x = {
|
||||
/* FIXME speed up once everything is working reliably */
|
||||
/*
|
||||
// Freq About 0.0498MHz / 49.8KHz => Freq = PCLK / (CPSDVSR * [SCR+1]) with PCLK=PLL1=204MHz
|
||||
@ -176,7 +183,7 @@ const ssp_config_t ssp_config_max2837 = {
|
||||
.data_bits = SSP_DATA_16BITS,
|
||||
.serial_clock_rate = 21,
|
||||
.clock_prescale_rate = 2,
|
||||
.gpio_select = &gpio_max2837_select,
|
||||
.gpio_select = &gpio_max283x_select,
|
||||
};
|
||||
|
||||
const ssp_config_t ssp_config_max5864 = {
|
||||
@ -195,21 +202,14 @@ const ssp_config_t ssp_config_max5864 = {
|
||||
|
||||
spi_bus_t spi_bus_ssp1 = {
|
||||
.obj = (void*) SSP1_BASE,
|
||||
.config = &ssp_config_max2837,
|
||||
.config = &ssp_config_max5864,
|
||||
.start = spi_ssp_start,
|
||||
.stop = spi_ssp_stop,
|
||||
.transfer = spi_ssp_transfer,
|
||||
.transfer_gather = spi_ssp_transfer_gather,
|
||||
};
|
||||
|
||||
max2837_driver_t max2837 = {
|
||||
.bus = &spi_bus_ssp1,
|
||||
.gpio_enable = &gpio_max2837_enable,
|
||||
.gpio_rx_enable = &gpio_max2837_rx_enable,
|
||||
.gpio_tx_enable = &gpio_max2837_tx_enable,
|
||||
.target_init = max2837_target_init,
|
||||
.set_mode = max2837_target_set_mode,
|
||||
};
|
||||
max283x_driver_t max283x = {};
|
||||
|
||||
max5864_driver_t max5864 = {
|
||||
.bus = &spi_bus_ssp1,
|
||||
@ -240,7 +240,7 @@ w25q80bv_driver_t spi_flash = {
|
||||
};
|
||||
|
||||
sgpio_config_t sgpio_config = {
|
||||
.gpio_rx_q_invert = &gpio_rx_q_invert,
|
||||
.gpio_q_invert = &gpio_q_invert,
|
||||
.gpio_hw_sync_enable = &gpio_hw_sync_enable,
|
||||
.slice_mode_multislice = true,
|
||||
};
|
||||
@ -406,14 +406,26 @@ bool sample_rate_frac_set(uint32_t rate_num, uint32_t rate_denom)
|
||||
MSx_P2 = (128 * b) % c;
|
||||
MSx_P3 = c;
|
||||
|
||||
/* MS0/CLK0 is the source for the MAX5864/CPLD (CODEC_CLK). */
|
||||
si5351c_configure_multisynth(&clock_gen, 0, MSx_P1, MSx_P2, MSx_P3, 1);
|
||||
if (detected_platform() == BOARD_ID_HACKRF1_R9) {
|
||||
/*
|
||||
* On HackRF One r9 all sample clocks are externally derived
|
||||
* from MS1/CLK1 operating at twice the sample rate.
|
||||
*/
|
||||
si5351c_configure_multisynth(&clock_gen, 1, MSx_P1, MSx_P2, MSx_P3, 0);
|
||||
} else {
|
||||
/*
|
||||
* On other platforms the clock generator produces three
|
||||
* different sample clocks, all derived from multisynth 0.
|
||||
*/
|
||||
/* MS0/CLK0 is the source for the MAX5864/CPLD (CODEC_CLK). */
|
||||
si5351c_configure_multisynth(&clock_gen, 0, MSx_P1, MSx_P2, MSx_P3, 1);
|
||||
|
||||
/* MS0/CLK1 is the source for the CPLD (CODEC_X2_CLK). */
|
||||
si5351c_configure_multisynth(&clock_gen, 1, 0, 0, 0, 0); //p1 doesn't matter
|
||||
/* MS0/CLK1 is the source for the CPLD (CODEC_X2_CLK). */
|
||||
si5351c_configure_multisynth(&clock_gen, 1, 0, 0, 0, 0); //p1 doesn't matter
|
||||
|
||||
/* MS0/CLK2 is the source for SGPIO (CODEC_X2_CLK) */
|
||||
si5351c_configure_multisynth(&clock_gen, 2, 0, 0, 0, 0); //p1 doesn't matter
|
||||
/* MS0/CLK2 is the source for SGPIO (CODEC_X2_CLK) */
|
||||
si5351c_configure_multisynth(&clock_gen, 2, 0, 0, 0, 0); //p1 doesn't matter
|
||||
}
|
||||
|
||||
if (streaming) {
|
||||
sgpio_cpld_stream_enable(&sgpio_config);
|
||||
@ -474,21 +486,46 @@ bool sample_rate_set(const uint32_t sample_rate_hz)
|
||||
return false;
|
||||
}
|
||||
|
||||
/* MS0/CLK0 is the source for the MAX5864/CPLD (CODEC_CLK). */
|
||||
si5351c_configure_multisynth(&clock_gen, 0, p1, p2, p3, 1);
|
||||
if (detected_platform() == BOARD_ID_HACKRF1_R9) {
|
||||
/*
|
||||
* On HackRF One r9 all sample clocks are externally derived
|
||||
* from MS1/CLK1 operating at twice the sample rate.
|
||||
*/
|
||||
si5351c_configure_multisynth(&clock_gen, 1, p1, p2, p3, 0);
|
||||
} else {
|
||||
/*
|
||||
* On other platforms the clock generator produces three
|
||||
* different sample clocks, all derived from multisynth 0.
|
||||
*/
|
||||
/* MS0/CLK0 is the source for the MAX5864/CPLD (CODEC_CLK). */
|
||||
si5351c_configure_multisynth(&clock_gen, 0, p1, p2, p3, 1);
|
||||
|
||||
/* MS0/CLK1 is the source for the CPLD (CODEC_X2_CLK). */
|
||||
si5351c_configure_multisynth(&clock_gen, 1, p1, 0, 1, 0); //p1 doesn't matter
|
||||
/* MS0/CLK1 is the source for the CPLD (CODEC_X2_CLK). */
|
||||
si5351c_configure_multisynth(
|
||||
&clock_gen,
|
||||
1,
|
||||
p1,
|
||||
0,
|
||||
1,
|
||||
0); //p1 doesn't matter
|
||||
|
||||
/* MS0/CLK2 is the source for SGPIO (CODEC_X2_CLK) */
|
||||
si5351c_configure_multisynth(&clock_gen, 2, p1, 0, 1, 0); //p1 doesn't matter
|
||||
/* MS0/CLK2 is the source for SGPIO (CODEC_X2_CLK) */
|
||||
si5351c_configure_multisynth(
|
||||
&clock_gen,
|
||||
2,
|
||||
p1,
|
||||
0,
|
||||
1,
|
||||
0); //p1 doesn't matter
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool baseband_filter_bandwidth_set(const uint32_t bandwidth_hz)
|
||||
{
|
||||
uint32_t bandwidth_hz_real = max2837_set_lpf_bandwidth(&max2837, bandwidth_hz);
|
||||
uint32_t bandwidth_hz_real;
|
||||
bandwidth_hz_real = max283x_set_lpf_bandwidth(&max283x, bandwidth_hz);
|
||||
|
||||
if (bandwidth_hz_real) {
|
||||
hackrf_ui()->set_filter_bw(bandwidth_hz_real);
|
||||
@ -578,6 +615,7 @@ void cpu_clock_init(void)
|
||||
|
||||
i2c_bus_start(clock_gen.bus, &i2c_config_si5351c_fast_clock);
|
||||
|
||||
si5351c_init(&clock_gen);
|
||||
si5351c_disable_all_outputs(&clock_gen);
|
||||
si5351c_disable_oeb_pin_control(&clock_gen);
|
||||
si5351c_power_down_all_clocks(&clock_gen);
|
||||
@ -587,7 +625,12 @@ void cpu_clock_init(void)
|
||||
si5351c_configure_pll_multisynth(&clock_gen);
|
||||
|
||||
/*
|
||||
* Clocks:
|
||||
* Clocks on HackRF One r9:
|
||||
* CLK0 -> MAX5864/CPLD/SGPIO (sample clocks)
|
||||
* CLK1 -> RFFC5072/MAX2839
|
||||
* CLK2 -> External Clock Output/LPC43xx (power down at boot)
|
||||
*
|
||||
* Clocks on other platforms:
|
||||
* CLK0 -> MAX5864/CPLD
|
||||
* CLK1 -> CPLD
|
||||
* CLK2 -> SGPIO
|
||||
@ -598,22 +641,33 @@ void cpu_clock_init(void)
|
||||
* CLK7 -> LPC43xx (uses a 12MHz crystal by default)
|
||||
*/
|
||||
|
||||
/* MS4/CLK4 is the source for the RFFC5071 mixer (MAX2837 on rad1o). */
|
||||
si5351c_configure_multisynth(
|
||||
&clock_gen,
|
||||
4,
|
||||
20 * 128 - 512,
|
||||
0,
|
||||
1,
|
||||
0); /* 800/20 = 40MHz */
|
||||
/* MS5/CLK5 is the source for the MAX2837 clock input (MAX2871 on rad1o). */
|
||||
si5351c_configure_multisynth(
|
||||
&clock_gen,
|
||||
5,
|
||||
20 * 128 - 512,
|
||||
0,
|
||||
1,
|
||||
0); /* 800/20 = 40MHz */
|
||||
if (detected_platform() == BOARD_ID_HACKRF1_R9) {
|
||||
/* MS0/CLK0 is the reference for both RFFC5071 and MAX2839. */
|
||||
si5351c_configure_multisynth(
|
||||
&clock_gen,
|
||||
0,
|
||||
20 * 128 - 512,
|
||||
0,
|
||||
1,
|
||||
0); /* 800/20 = 40MHz */
|
||||
} else {
|
||||
/* MS4/CLK4 is the source for the RFFC5071 mixer (MAX2837 on rad1o). */
|
||||
si5351c_configure_multisynth(
|
||||
&clock_gen,
|
||||
4,
|
||||
20 * 128 - 512,
|
||||
0,
|
||||
1,
|
||||
0); /* 800/20 = 40MHz */
|
||||
/* MS5/CLK5 is the source for the MAX2837 clock input (MAX2871 on rad1o). */
|
||||
si5351c_configure_multisynth(
|
||||
&clock_gen,
|
||||
5,
|
||||
20 * 128 - 512,
|
||||
0,
|
||||
1,
|
||||
0); /* 800/20 = 40MHz */
|
||||
}
|
||||
|
||||
/* MS6/CLK6 is unused. */
|
||||
/* MS7/CLK7 is unused. */
|
||||
@ -727,7 +781,7 @@ void cpu_clock_init(void)
|
||||
CCU1_CLK_APB3_ADC1_CFG = 0;
|
||||
CCU1_CLK_APB3_CAN0_CFG = 0;
|
||||
CCU1_CLK_APB3_DAC_CFG = 0;
|
||||
CCU1_CLK_M4_DMA_CFG = 0;
|
||||
//CCU1_CLK_M4_DMA_CFG = 0;
|
||||
CCU1_CLK_M4_EMC_CFG = 0;
|
||||
CCU1_CLK_M4_EMCDIV_CFG = 0;
|
||||
CCU1_CLK_M4_ETHERNET_CFG = 0;
|
||||
@ -738,8 +792,8 @@ void cpu_clock_init(void)
|
||||
CCU1_CLK_M4_SDIO_CFG = 0;
|
||||
CCU1_CLK_M4_SPIFI_CFG = 0;
|
||||
CCU1_CLK_M4_TIMER0_CFG = 0;
|
||||
CCU1_CLK_M4_TIMER1_CFG = 0;
|
||||
CCU1_CLK_M4_TIMER2_CFG = 0;
|
||||
//CCU1_CLK_M4_TIMER1_CFG = 0;
|
||||
//CCU1_CLK_M4_TIMER2_CFG = 0;
|
||||
CCU1_CLK_M4_TIMER3_CFG = 0;
|
||||
CCU1_CLK_M4_UART1_CFG = 0;
|
||||
CCU1_CLK_M4_USART0_CFG = 0;
|
||||
@ -757,6 +811,10 @@ void cpu_clock_init(void)
|
||||
// CCU2_CLK_APLL_CFG = 0;
|
||||
// CCU2_CLK_SDIO_CFG = 0;
|
||||
#endif
|
||||
|
||||
if (detected_platform() == BOARD_ID_HACKRF1_R9) {
|
||||
clkin_detect_init();
|
||||
}
|
||||
}
|
||||
|
||||
clock_source_t activate_best_clock_source(void)
|
||||
@ -796,9 +854,9 @@ clock_source_t activate_best_clock_source(void)
|
||||
return source;
|
||||
}
|
||||
|
||||
void ssp1_set_mode_max2837(void)
|
||||
void ssp1_set_mode_max283x(void)
|
||||
{
|
||||
spi_bus_start(max2837.bus, &ssp_config_max2837);
|
||||
spi_bus_start(&spi_bus_ssp1, &ssp_config_max283x);
|
||||
}
|
||||
|
||||
void ssp1_set_mode_max5864(void)
|
||||
@ -856,15 +914,26 @@ void pin_setup(void)
|
||||
#endif
|
||||
|
||||
disable_1v8_power();
|
||||
gpio_output(&gpio_1v8_enable);
|
||||
scu_pinmux(SCU_PINMUX_EN1V8, SCU_GPIO_NOPULL | SCU_CONF_FUNCTION0);
|
||||
if (detected_platform() == BOARD_ID_HACKRF1_R9) {
|
||||
#ifdef HACKRF_ONE
|
||||
gpio_output(&gpio_h1r9_1v8_enable);
|
||||
scu_pinmux(SCU_H1R9_EN1V8, SCU_GPIO_FAST | SCU_CONF_FUNCTION0);
|
||||
#endif
|
||||
} else {
|
||||
gpio_output(&gpio_1v8_enable);
|
||||
scu_pinmux(SCU_PINMUX_EN1V8, SCU_GPIO_FAST | SCU_CONF_FUNCTION0);
|
||||
}
|
||||
|
||||
#ifdef HACKRF_ONE
|
||||
/* Safe state: start with VAA turned off: */
|
||||
disable_rf_power();
|
||||
|
||||
/* Configure RF power supply (VAA) switch control signal as output */
|
||||
gpio_output(&gpio_vaa_disable);
|
||||
if (detected_platform() == BOARD_ID_HACKRF1_R9) {
|
||||
gpio_output(&gpio_h1r9_vaa_disable);
|
||||
} else {
|
||||
gpio_output(&gpio_vaa_disable);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef RAD1O
|
||||
@ -886,10 +955,16 @@ void pin_setup(void)
|
||||
/* enable input on SCL and SDA pins */
|
||||
SCU_SFSI2C0 = SCU_I2C0_NOMINAL;
|
||||
|
||||
spi_bus_start(&spi_bus_ssp1, &ssp_config_max2837);
|
||||
spi_bus_start(&spi_bus_ssp1, &ssp_config_max283x);
|
||||
|
||||
mixer_bus_setup(&mixer);
|
||||
|
||||
#ifdef HACKRF_ONE
|
||||
if (detected_platform() == BOARD_ID_HACKRF1_R9) {
|
||||
rf_path.gpio_rx = &gpio_h1r9_rx;
|
||||
sgpio_config.gpio_hw_sync_enable = &gpio_h1r9_hw_sync_enable;
|
||||
}
|
||||
#endif
|
||||
rf_path_pin_setup(&rf_path);
|
||||
|
||||
/* Configure external clock in */
|
||||
@ -900,12 +975,24 @@ void pin_setup(void)
|
||||
|
||||
void enable_1v8_power(void)
|
||||
{
|
||||
gpio_set(&gpio_1v8_enable);
|
||||
if (detected_platform() == BOARD_ID_HACKRF1_R9) {
|
||||
#ifdef HACKRF_ONE
|
||||
gpio_set(&gpio_h1r9_1v8_enable);
|
||||
#endif
|
||||
} else {
|
||||
gpio_set(&gpio_1v8_enable);
|
||||
}
|
||||
}
|
||||
|
||||
void disable_1v8_power(void)
|
||||
{
|
||||
gpio_clear(&gpio_1v8_enable);
|
||||
if (detected_platform() == BOARD_ID_HACKRF1_R9) {
|
||||
#ifdef HACKRF_ONE
|
||||
gpio_clear(&gpio_h1r9_1v8_enable);
|
||||
#endif
|
||||
} else {
|
||||
gpio_clear(&gpio_1v8_enable);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HACKRF_ONE
|
||||
@ -915,15 +1002,23 @@ void enable_rf_power(void)
|
||||
|
||||
/* many short pulses to avoid one big voltage glitch */
|
||||
for (i = 0; i < 1000; i++) {
|
||||
gpio_clear(&gpio_vaa_disable);
|
||||
gpio_set(&gpio_vaa_disable);
|
||||
if (detected_platform() == BOARD_ID_HACKRF1_R9) {
|
||||
gpio_set(&gpio_h1r9_vaa_disable);
|
||||
gpio_clear(&gpio_h1r9_vaa_disable);
|
||||
} else {
|
||||
gpio_set(&gpio_vaa_disable);
|
||||
gpio_clear(&gpio_vaa_disable);
|
||||
}
|
||||
}
|
||||
gpio_clear(&gpio_vaa_disable);
|
||||
}
|
||||
|
||||
void disable_rf_power(void)
|
||||
{
|
||||
gpio_set(&gpio_vaa_disable);
|
||||
if (detected_platform() == BOARD_ID_HACKRF1_R9) {
|
||||
gpio_set(&gpio_h1r9_vaa_disable);
|
||||
} else {
|
||||
gpio_set(&gpio_vaa_disable);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -970,7 +1065,7 @@ void set_leds(const uint8_t state)
|
||||
|
||||
void hw_sync_enable(const hw_sync_mode_t hw_sync_mode)
|
||||
{
|
||||
gpio_write(&gpio_hw_sync_enable, hw_sync_mode == 1);
|
||||
gpio_write(sgpio_config.gpio_hw_sync_enable, hw_sync_mode == 1);
|
||||
}
|
||||
|
||||
void halt_and_flash(const uint32_t duration)
|
||||
|
@ -34,7 +34,7 @@ extern "C" {
|
||||
#include "si5351c.h"
|
||||
#include "spi_ssp.h"
|
||||
|
||||
#include "max2837.h"
|
||||
#include "max283x.h"
|
||||
#include "max5864.h"
|
||||
#include "mixer.h"
|
||||
#include "w25q80bv.h"
|
||||
@ -108,9 +108,9 @@ extern "C" {
|
||||
#define SCU_PINMUX_SGPIO10 (P1_14)
|
||||
#define SCU_PINMUX_SGPIO11 (P1_17)
|
||||
#define SCU_PINMUX_SGPIO12 (P1_18)
|
||||
#define SCU_PINMUX_SGPIO13 (P4_8)
|
||||
#define SCU_PINMUX_SGPIO14 (P4_9)
|
||||
#define SCU_PINMUX_SGPIO15 (P4_10)
|
||||
#define SCU_HW_SYNC_EN (P4_8) /* GPIO5[12] on P4_8 */
|
||||
|
||||
/* MAX2837 GPIO (XCVR_CTL) PinMux */
|
||||
#ifdef RAD1O
|
||||
@ -232,6 +232,16 @@ extern "C" {
|
||||
|
||||
#define SCU_PINMUX_GP_CLKIN (P4_7)
|
||||
|
||||
/* HackRF One r9 */
|
||||
#define SCU_H1R9_CLKIN_EN (P6_7) /* GPIO5[15] on P6_7 */
|
||||
#define SCU_H1R9_CLKOUT_EN (P1_2) /* GPIO0[9] on P1_2 (has boot pull-down) */
|
||||
#define SCU_H1R9_MCU_CLK_EN (P1_1) /* GPIO0[8] on P1_1 (has boot pull-up) */
|
||||
#define SCU_H1R9_RX (P2_7) /* GPIO0[7] on P4_4 (has boot pull-up) */
|
||||
#define SCU_H1R9_NO_ANT_PWR (P4_4) /* GPIO2[4] on P4_4 */
|
||||
#define SCU_H1R9_EN1V8 (P5_0) /* GPIO2[9] on P5_0 */
|
||||
#define SCU_H1R9_NO_VAA_EN (P6_10) /* GPIO3[6] on P6_10 */
|
||||
#define SCU_H1R9_HW_SYNC_EN (P2_5) /* GPIO5[5] on P2_5 */
|
||||
|
||||
typedef enum {
|
||||
TRANSCEIVER_MODE_OFF = 0,
|
||||
TRANSCEIVER_MODE_RX = 1,
|
||||
@ -258,10 +268,10 @@ void delay_us_at_mhz(uint32_t us, uint32_t mhz);
|
||||
/* TODO: Hide these configurations */
|
||||
extern si5351c_driver_t clock_gen;
|
||||
extern const ssp_config_t ssp_config_w25q80bv;
|
||||
extern const ssp_config_t ssp_config_max2837;
|
||||
extern const ssp_config_t ssp_config_max283x;
|
||||
extern const ssp_config_t ssp_config_max5864;
|
||||
|
||||
extern max2837_driver_t max2837;
|
||||
extern max283x_driver_t max283x;
|
||||
extern max5864_driver_t max5864;
|
||||
extern mixer_driver_t mixer;
|
||||
extern w25q80bv_driver_t spi_flash;
|
||||
@ -271,7 +281,7 @@ extern jtag_t jtag_cpld;
|
||||
extern i2c_bus_t i2c0;
|
||||
|
||||
void cpu_clock_init(void);
|
||||
void ssp1_set_mode_max2837(void);
|
||||
void ssp1_set_mode_max283x(void);
|
||||
void ssp1_set_mode_max5864(void);
|
||||
|
||||
void pin_setup(void);
|
||||
|
@ -45,7 +45,7 @@ struct max2837_driver_t;
|
||||
typedef struct max2837_driver_t max2837_driver_t;
|
||||
|
||||
struct max2837_driver_t {
|
||||
spi_bus_t* const bus;
|
||||
spi_bus_t* bus;
|
||||
gpio_t gpio_enable;
|
||||
gpio_t gpio_rx_enable;
|
||||
gpio_t gpio_tx_enable;
|
||||
|
417
firmware/common/max2839.c
Normal file
417
firmware/common/max2839.c
Normal file
@ -0,0 +1,417 @@
|
||||
/*
|
||||
* Copyright 2012-2022 Great Scott Gadgets <info@greatscottgadgets.com>
|
||||
* Copyright 2012 Will Code <willcode4@gmail.com>
|
||||
* Copyright 2014 Jared Boone <jared@sharebrained.com>
|
||||
*
|
||||
* This file is part of HackRF.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
/*
|
||||
* 'gcc -DTEST -DDEBUG -O2 -o test max2839.c' prints out what test
|
||||
* program would do if it had a real spi library
|
||||
*
|
||||
* 'gcc -DTEST -DBUS_PIRATE -O2 -o test max2839.c' prints out bus
|
||||
* pirate commands to do the same thing.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include "max2839.h"
|
||||
#include "max2839_regs.def" // private register def macros
|
||||
|
||||
static uint8_t requested_lna_gain = 0;
|
||||
static uint8_t requested_vga_gain = 0;
|
||||
|
||||
/* Default register values. */
|
||||
static const uint16_t max2839_regs_default[MAX2839_NUM_REGS] = {
|
||||
0x000, /* 0 */
|
||||
0x00c, /* 1: data sheet says 0x00c but read 0x22c */
|
||||
0x080, /* 2 */
|
||||
0x1b9, /* 3: data sheet says 0x1b9 but read 0x1b0 */
|
||||
0x3e6, /* 4 */
|
||||
0x100, /* 5 */
|
||||
0x000, /* 6 */
|
||||
0x208, /* 7 */
|
||||
0x220, /* 8: data sheet says 0x220 but read 0x000 */
|
||||
0x018, /* 9 */
|
||||
0x00c, /* 10 */
|
||||
0x004, /* 11: data sheet says 0x004 but read 0x000 */
|
||||
0x24f, /* 12 */
|
||||
0x150, /* 13 */
|
||||
0x3c5, /* 14 */
|
||||
0x201, /* 15 */
|
||||
0x01c, /* 16 */
|
||||
0x155, /* 17 */
|
||||
0x155, /* 18 */
|
||||
0x153, /* 19 */
|
||||
0x249, /* 20 */
|
||||
0x02d, /* 21: data sheet says 0x02d but read 0x13d */
|
||||
0x1a9, /* 22 */
|
||||
0x24f, /* 23 */
|
||||
0x180, /* 24 */
|
||||
0x000, /* 25: data sheet says 0x000 but read 0x00a */
|
||||
0x3c0, /* 26 */
|
||||
0x200, /* 27: data sheet says 0x200 but read 0x22a */
|
||||
0x0c0, /* 28 */
|
||||
0x03f, /* 29: data sheet says 0x03f but read 0x07f */
|
||||
0x300, /* 30: data sheet says 0x300 but read 0x398 */
|
||||
0x340}; /* 31: data sheet says 0x340 but read 0x359 */
|
||||
|
||||
/*
|
||||
* All of the discrepancies listed above are in fields that either don't matter
|
||||
* or are undocumented except "set to recommended value". We set them to the
|
||||
* data sheet defaults even though the inital part we tested started up with
|
||||
* different settings.
|
||||
*/
|
||||
|
||||
/* Set up all registers according to defaults specified in docs. */
|
||||
static void max2839_init(max2839_driver_t* const drv)
|
||||
{
|
||||
drv->target_init(drv);
|
||||
max2839_set_mode(drv, MAX2839_MODE_SHUTDOWN);
|
||||
|
||||
memcpy(drv->regs, max2839_regs_default, sizeof(drv->regs));
|
||||
drv->regs_dirty = 0xffffffff;
|
||||
|
||||
/* Write default register values to chip. */
|
||||
max2839_regs_commit(drv);
|
||||
}
|
||||
|
||||
/*
|
||||
* Set up pins for GPIO and SPI control, configure SSP peripheral for SPI, and
|
||||
* set our own default register configuration.
|
||||
*/
|
||||
void max2839_setup(max2839_driver_t* const drv)
|
||||
{
|
||||
max2839_init(drv);
|
||||
|
||||
/* Use SPI control instead of B0-B7 pins for gain settings. */
|
||||
set_MAX2839_LNAgain_SPI(drv, 1);
|
||||
set_MAX2839_VGAgain_SPI(drv, 1);
|
||||
set_MAX2839_TX_VGA_Gain_SPI(drv, 1);
|
||||
|
||||
/* enable RXINB */
|
||||
set_MAX2839_MIMO_SELECT(drv, 1);
|
||||
|
||||
/* set gains for unused RXINA path to minimum */
|
||||
set_MAX2839_LNA1gain(drv, MAX2839_LNA1gain_M32);
|
||||
set_MAX2839_Rx1_VGAgain(drv, 0x3f);
|
||||
|
||||
/* set maximum RX output common-mode voltage */
|
||||
set_MAX2839_RX_VCM(drv, MAX2839_RX_VCM_1_35);
|
||||
|
||||
/* set HPF corner frequency to 1 kHz */
|
||||
set_MAX2839_HPC_STOP(drv, MAX2839_STOP_1K);
|
||||
|
||||
/*
|
||||
* There are two LNA band settings, but we only use one of them.
|
||||
* Switching to the other one doesn't make the overall spectrum any
|
||||
* flatter but adds a surprise step in the middle.
|
||||
*/
|
||||
set_MAX2839_LNAband(drv, MAX2839_LNAband_2_4);
|
||||
|
||||
max2839_regs_commit(drv);
|
||||
}
|
||||
|
||||
static uint16_t max2839_read(max2839_driver_t* const drv, uint8_t r)
|
||||
{
|
||||
uint16_t value = (1 << 15) | (r << 10);
|
||||
spi_bus_transfer(drv->bus, &value, 1);
|
||||
return value & 0x3ff;
|
||||
}
|
||||
|
||||
static void max2839_write(max2839_driver_t* const drv, uint8_t r, uint16_t v)
|
||||
{
|
||||
uint16_t value = (r << 10) | (v & 0x3ff);
|
||||
spi_bus_transfer(drv->bus, &value, 1);
|
||||
}
|
||||
|
||||
uint16_t max2839_reg_read(max2839_driver_t* const drv, uint8_t r)
|
||||
{
|
||||
// always read actual value from SPI for now
|
||||
//if ((drv->regs_dirty >> r) & 0x1) {
|
||||
drv->regs[r] = max2839_read(drv, r);
|
||||
//};
|
||||
return drv->regs[r];
|
||||
}
|
||||
|
||||
void max2839_reg_write(max2839_driver_t* const drv, uint8_t r, uint16_t v)
|
||||
{
|
||||
drv->regs[r] = v;
|
||||
max2839_write(drv, r, v);
|
||||
MAX2839_REG_SET_CLEAN(drv, r);
|
||||
}
|
||||
|
||||
static inline void max2839_reg_commit(max2839_driver_t* const drv, uint8_t r)
|
||||
{
|
||||
max2839_reg_write(drv, r, drv->regs[r]);
|
||||
}
|
||||
|
||||
void max2839_regs_commit(max2839_driver_t* const drv)
|
||||
{
|
||||
int r;
|
||||
for (r = 0; r < MAX2839_NUM_REGS; r++) {
|
||||
if ((drv->regs_dirty >> r) & 0x1) {
|
||||
max2839_reg_commit(drv, r);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void max2839_set_mode(max2839_driver_t* const drv, const max2839_mode_t new_mode)
|
||||
{
|
||||
drv->set_mode(drv, new_mode);
|
||||
}
|
||||
|
||||
max2839_mode_t max2839_mode(max2839_driver_t* const drv)
|
||||
{
|
||||
return drv->mode;
|
||||
}
|
||||
|
||||
void max2839_start(max2839_driver_t* const drv)
|
||||
{
|
||||
set_MAX2839_chip_enable(drv, 1);
|
||||
max2839_regs_commit(drv);
|
||||
max2839_set_mode(drv, MAX2839_MODE_STANDBY);
|
||||
}
|
||||
|
||||
void max2839_tx(max2839_driver_t* const drv)
|
||||
{
|
||||
// FIXME does this do anything without LPFmode_SPI set?
|
||||
// do we need it to?
|
||||
set_MAX2839_LPFmode(drv, MAX2839_LPFmode_TxLPF);
|
||||
max2839_regs_commit(drv);
|
||||
max2839_set_mode(drv, MAX2839_MODE_TX);
|
||||
}
|
||||
|
||||
void max2839_rx(max2839_driver_t* const drv)
|
||||
{
|
||||
set_MAX2839_LPFmode(drv, MAX2839_LPFmode_RxLPF);
|
||||
max2839_regs_commit(drv);
|
||||
max2839_set_mode(drv, MAX2839_MODE_RX);
|
||||
}
|
||||
|
||||
void max2839_stop(max2839_driver_t* const drv)
|
||||
{
|
||||
set_MAX2839_chip_enable(drv, 0);
|
||||
max2839_regs_commit(drv);
|
||||
max2839_set_mode(drv, MAX2839_MODE_SHUTDOWN);
|
||||
}
|
||||
|
||||
void max2839_set_frequency(max2839_driver_t* const drv, uint32_t freq)
|
||||
{
|
||||
uint8_t band;
|
||||
uint32_t div_frac;
|
||||
uint32_t div_int;
|
||||
uint32_t div_rem;
|
||||
uint32_t div_cmp;
|
||||
int i;
|
||||
|
||||
/* Select band. Allow tuning outside specified bands. */
|
||||
if (freq < 2400000000U) {
|
||||
band = MAX2839_LOGEN_BSW_2_3;
|
||||
} else if (freq < 2500000000U) {
|
||||
band = MAX2839_LOGEN_BSW_2_4;
|
||||
} else if (freq < 2600000000U) {
|
||||
band = MAX2839_LOGEN_BSW_2_5;
|
||||
} else {
|
||||
band = MAX2839_LOGEN_BSW_2_6;
|
||||
}
|
||||
|
||||
/* ASSUME 40MHz PLL. Ratio = F*(4/3)/40,000,000 = F/30,000,000 */
|
||||
div_int = freq / 30000000;
|
||||
div_rem = freq % 30000000;
|
||||
div_frac = 0;
|
||||
div_cmp = 30000000;
|
||||
for (i = 0; i < 20; i++) {
|
||||
div_frac <<= 1;
|
||||
div_cmp >>= 1;
|
||||
if (div_rem > div_cmp) {
|
||||
div_frac |= 0x1;
|
||||
div_rem -= div_cmp;
|
||||
}
|
||||
}
|
||||
|
||||
/* Band settings */
|
||||
set_MAX2839_LOGEN_BSW(drv, band);
|
||||
|
||||
/* Write order matters here, so commit INT and FRAC_HI before
|
||||
* committing FRAC_LO, which is the trigger for VCO
|
||||
* auto-select. TODO - it's cleaner this way, but it would be
|
||||
* faster to explicitly commit the registers explicitly so the
|
||||
* dirty bits aren't scanned twice. */
|
||||
set_MAX2839_SYN_INT(drv, div_int);
|
||||
set_MAX2839_SYN_FRAC_HI(drv, (div_frac >> 10) & 0x3ff);
|
||||
max2839_regs_commit(drv);
|
||||
set_MAX2839_SYN_FRAC_LO(drv, div_frac & 0x3ff);
|
||||
max2839_regs_commit(drv);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
uint32_t bandwidth_hz;
|
||||
uint32_t ft;
|
||||
} max2839_ft_t;
|
||||
|
||||
// clang-format off
|
||||
static const max2839_ft_t max2839_ft[] = {
|
||||
{ 1750000, MAX2839_FT_1_75M },
|
||||
{ 2500000, MAX2839_FT_2_5M },
|
||||
{ 3500000, MAX2839_FT_3_5M },
|
||||
{ 5000000, MAX2839_FT_5M },
|
||||
{ 5500000, MAX2839_FT_5_5M },
|
||||
{ 6000000, MAX2839_FT_6M },
|
||||
{ 7000000, MAX2839_FT_7M },
|
||||
{ 8000000, MAX2839_FT_8M },
|
||||
{ 9000000, MAX2839_FT_9M },
|
||||
{ 10000000, MAX2839_FT_10M },
|
||||
{ 12000000, MAX2839_FT_12M },
|
||||
{ 14000000, MAX2839_FT_14M },
|
||||
{ 15000000, MAX2839_FT_15M },
|
||||
{ 20000000, MAX2839_FT_20M },
|
||||
{ 24000000, MAX2839_FT_24M },
|
||||
{ 28000000, MAX2839_FT_28M },
|
||||
{ 0, 0 },
|
||||
};
|
||||
//clang-format on
|
||||
|
||||
uint32_t max2839_set_lpf_bandwidth(max2839_driver_t* const drv, const uint32_t bandwidth_hz) {
|
||||
const max2839_ft_t* p = max2839_ft;
|
||||
while( p->bandwidth_hz != 0 ) {
|
||||
if( p->bandwidth_hz >= bandwidth_hz ) {
|
||||
break;
|
||||
}
|
||||
p++;
|
||||
}
|
||||
|
||||
if( p->bandwidth_hz != 0 ) {
|
||||
set_MAX2839_FT(drv, p->ft);
|
||||
max2839_regs_commit(drv);
|
||||
}
|
||||
|
||||
return p->bandwidth_hz;
|
||||
}
|
||||
|
||||
void max2839_configure_rx_gain(max2839_driver_t* const drv)
|
||||
{
|
||||
/*
|
||||
* restrict requested LNA gain to valid MAX2837 settings:
|
||||
* 0, 8, 16, 24, 32, or 40
|
||||
*/
|
||||
if (requested_lna_gain > 40) {
|
||||
requested_lna_gain = 40;
|
||||
}
|
||||
requested_lna_gain &= 0x38;
|
||||
|
||||
/*
|
||||
* restrict requested VGA gain to valid MAX2837 settings:
|
||||
* even number, 0 through 62
|
||||
*/
|
||||
if (requested_vga_gain > 62) {
|
||||
requested_vga_gain = 62;
|
||||
}
|
||||
requested_vga_gain &= 0x3e;
|
||||
|
||||
/*
|
||||
* MAX2839 has lower full-scale RX output voltage than MAX2837, so we
|
||||
* adjust the VGA (baseband) gain to compensate.
|
||||
*/
|
||||
uint8_t vga_gain = requested_vga_gain + 3;
|
||||
uint8_t lna_gain = requested_lna_gain;
|
||||
|
||||
/*
|
||||
* If that adjustment puts VGA gain out of range, use LNA gain to
|
||||
* compensate. MAX2839 VGA gain can be any number from 0 through 63.
|
||||
*/
|
||||
if (vga_gain > 63) {
|
||||
if (lna_gain <= 32) {
|
||||
vga_gain -= 8;
|
||||
lna_gain += 8;
|
||||
} else {
|
||||
vga_gain = 63;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* MAX2839 lacks max-24 dB and max-40 dB LNA gain settings, so we use
|
||||
* VGA gain to compensate.
|
||||
*/
|
||||
if (lna_gain == 0) {
|
||||
lna_gain = 8;
|
||||
vga_gain = (vga_gain >= 8) ? vga_gain - 8 : 0;
|
||||
}
|
||||
if (lna_gain == 16) {
|
||||
if (vga_gain > 32) {
|
||||
vga_gain -= 8;
|
||||
lna_gain += 8;
|
||||
} else {
|
||||
vga_gain += 8;
|
||||
lna_gain -= 8;
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t val;
|
||||
switch (lna_gain) {
|
||||
case 40:
|
||||
val = MAX2839_LNA2gain_MAX;
|
||||
break;
|
||||
case 32:
|
||||
val = MAX2839_LNA2gain_M8;
|
||||
break;
|
||||
case 24:
|
||||
case 16:
|
||||
val = MAX2839_LNA2gain_M16;
|
||||
break;
|
||||
case 8:
|
||||
case 0:
|
||||
default:
|
||||
val = MAX2839_LNA2gain_M32;
|
||||
break;
|
||||
}
|
||||
set_MAX2839_LNA2gain(drv, val);
|
||||
set_MAX2839_Rx2_VGAgain(drv, (63 - vga_gain));
|
||||
max2839_regs_commit(drv);
|
||||
}
|
||||
|
||||
bool max2839_set_lna_gain(max2839_driver_t* const drv, const uint32_t gain_db)
|
||||
{
|
||||
if ((gain_db & 0x7) || gain_db > 40) {
|
||||
return false;
|
||||
}
|
||||
requested_lna_gain = gain_db;
|
||||
max2839_configure_rx_gain(drv);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool max2839_set_vga_gain(max2839_driver_t* const drv, const uint32_t gain_db)
|
||||
{
|
||||
if ((gain_db & 0x1) || gain_db > 62) {
|
||||
return false;
|
||||
}
|
||||
requested_vga_gain = gain_db;
|
||||
max2839_configure_rx_gain(drv);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool max2839_set_txvga_gain(max2839_driver_t* const drv, const uint32_t gain_db)
|
||||
{
|
||||
uint16_t val = 0;
|
||||
val = 47 - gain_db;
|
||||
|
||||
set_MAX2839_TX_VGA_GAIN(drv, val);
|
||||
max2839_reg_commit(drv, 29);
|
||||
return true;
|
||||
}
|
97
firmware/common/max2839.h
Normal file
97
firmware/common/max2839.h
Normal file
@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright 2012-2022 Great Scott Gadgets <info@greatscottgadgets.com>
|
||||
* Copyright 2012 Will Code <willcode4@gmail.com>
|
||||
* Copyright 2014 Jared Boone <jared@sharebrained.com>
|
||||
*
|
||||
* This file is part of HackRF.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __MAX2839_H
|
||||
#define __MAX2839_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "gpio.h"
|
||||
#include "spi_bus.h"
|
||||
|
||||
/* 32 registers, each containing 10 bits of data. */
|
||||
#define MAX2839_NUM_REGS 32
|
||||
#define MAX2839_DATA_REGS_MAX_VALUE 1024
|
||||
|
||||
typedef enum {
|
||||
MAX2839_MODE_SHUTDOWN,
|
||||
MAX2839_MODE_STANDBY,
|
||||
MAX2839_MODE_TX,
|
||||
MAX2839_MODE_RX,
|
||||
MAX2839_MODE_RX_CAL,
|
||||
MAX2839_MODE_TX_CAL,
|
||||
MAX2839_MODE_CLKOUT,
|
||||
} max2839_mode_t;
|
||||
|
||||
struct max2839_driver_t;
|
||||
typedef struct max2839_driver_t max2839_driver_t;
|
||||
|
||||
struct max2839_driver_t {
|
||||
spi_bus_t* bus;
|
||||
gpio_t gpio_enable;
|
||||
gpio_t gpio_rxtx;
|
||||
void (*target_init)(max2839_driver_t* const drv);
|
||||
void (*set_mode)(max2839_driver_t* const drv, const max2839_mode_t new_mode);
|
||||
max2839_mode_t mode;
|
||||
uint16_t regs[MAX2839_NUM_REGS];
|
||||
uint32_t regs_dirty;
|
||||
};
|
||||
|
||||
/* Initialize chip. */
|
||||
extern void max2839_setup(max2839_driver_t* const drv);
|
||||
|
||||
/* Read a register via SPI. Save a copy to memory and return
|
||||
* value. Mark clean. */
|
||||
extern uint16_t max2839_reg_read(max2839_driver_t* const drv, uint8_t r);
|
||||
|
||||
/* Write value to register via SPI and save a copy to memory. Mark
|
||||
* clean. */
|
||||
extern void max2839_reg_write(max2839_driver_t* const drv, uint8_t r, uint16_t v);
|
||||
|
||||
/* Write all dirty registers via SPI from memory. Mark all clean. Some
|
||||
* operations require registers to be written in a certain order. Use
|
||||
* provided routines for those operations. */
|
||||
extern void max2839_regs_commit(max2839_driver_t* const drv);
|
||||
|
||||
max2839_mode_t max2839_mode(max2839_driver_t* const drv);
|
||||
void max2839_set_mode(max2839_driver_t* const drv, const max2839_mode_t new_mode);
|
||||
|
||||
/* Turn on/off all chip functions. Does not control oscillator and CLKOUT */
|
||||
extern void max2839_start(max2839_driver_t* const drv);
|
||||
extern void max2839_stop(max2839_driver_t* const drv);
|
||||
|
||||
/* Set frequency in Hz. Frequency setting is a multi-step function
|
||||
* where order of register writes matters. */
|
||||
extern void max2839_set_frequency(max2839_driver_t* const drv, uint32_t freq);
|
||||
uint32_t max2839_set_lpf_bandwidth(
|
||||
max2839_driver_t* const drv,
|
||||
const uint32_t bandwidth_hz);
|
||||
bool max2839_set_lna_gain(max2839_driver_t* const drv, const uint32_t gain_db);
|
||||
bool max2839_set_vga_gain(max2839_driver_t* const drv, const uint32_t gain_db);
|
||||
bool max2839_set_txvga_gain(max2839_driver_t* const drv, const uint32_t gain_db);
|
||||
|
||||
extern void max2839_tx(max2839_driver_t* const drv);
|
||||
extern void max2839_rx(max2839_driver_t* const drv);
|
||||
|
||||
#endif // __MAX2839_H
|
259
firmware/common/max2839_regs.def
Normal file
259
firmware/common/max2839_regs.def
Normal file
@ -0,0 +1,259 @@
|
||||
/* -*- mode: c -*- */
|
||||
|
||||
#ifndef __MAX2839_REGS_DEF
|
||||
#define __MAX2839_REGS_DEF
|
||||
|
||||
/* Generate static inline accessors that operate on the global
|
||||
* regs. Done this way to (1) allow defs to be scraped out and used
|
||||
* elsewhere, e.g. in scripts, (2) to avoid dealing with endian
|
||||
* (structs). This may be used in firmware, or on host predefined
|
||||
* register loads. */
|
||||
|
||||
#define MAX2839_REG_SET_CLEAN(_d, _r) (_d->regs_dirty &= ~(1UL<<_r))
|
||||
#define MAX2839_REG_SET_DIRTY(_d, _r) (_d->regs_dirty |= (1UL<<_r))
|
||||
|
||||
/* On set_, register is always set dirty, even if nothing
|
||||
* changed. This makes sure that write that have side effects,
|
||||
* e.g. frequency setting, are not skipped. */
|
||||
|
||||
/* n=name, r=regnum, o=offset (bits from LSB), l=length (bits) */
|
||||
#define __MREG__(n,r,o,l) \
|
||||
static inline uint16_t get_##n(max2839_driver_t* const _d) { \
|
||||
return (_d->regs[r] >> (o-l+1)) & ((1<<l)-1); \
|
||||
} \
|
||||
static inline void set_##n(max2839_driver_t* const _d, uint16_t v) { \
|
||||
_d->regs[r] &= ~(((1<<l)-1)<<(o-l+1)); \
|
||||
_d->regs[r] |= ((v&((1<<l)-1))<<(o-l+1)); \
|
||||
MAX2839_REG_SET_DIRTY(_d, r); \
|
||||
}
|
||||
|
||||
/* REG 0 */
|
||||
__MREG__(MAX2839_RESERVED_0_9,0,9,10)
|
||||
|
||||
/* REG 1 */
|
||||
__MREG__(MAX2839_LNAband,1,1,2)
|
||||
#define MAX2839_LNAband_2_4 0 // 2.3-2.5 GHz
|
||||
#define MAX2839_LNAband_2_6 1 // 2.5-2.7 GHz
|
||||
__MREG__(MAX2839_RESERVED_1_2,1,2,1)
|
||||
__MREG__(MAX2839_MIMO_SELECT,1,3,1)
|
||||
__MREG__(MAX2839_iqerr_trim,1,9,6)
|
||||
// 0b000000 = +4.0 degree phase error
|
||||
// 0b011111 = 0.0
|
||||
// 0b111111 = -4.0
|
||||
|
||||
/* REG 2 */
|
||||
__MREG__(MAX2839_LNAgain_SPI,2,0,1)
|
||||
__MREG__(MAX2839_RESERVED_2_1,2,1,1)
|
||||
__MREG__(MAX2839_RX_IQ_SPI,2,2,1)
|
||||
__MREG__(MAX2839_RESERVED_2_9,2,9,7)
|
||||
|
||||
/* REG 3 */
|
||||
__MREG__(MAX2839_RESERVED_3_9,3,9,10)
|
||||
|
||||
/* REG 4 */
|
||||
__MREG__(MAX2839_RESERVED_4_1,4,1,2)
|
||||
__MREG__(MAX2839_LPF_CUTOFF,4,3,2)
|
||||
__MREG__(MAX2839_RESERVED_4_5,4,5,2)
|
||||
__MREG__(MAX2839_FT,4,9,4)
|
||||
#define MAX2839_FT_1_75M 0
|
||||
#define MAX2839_FT_2_5M 1
|
||||
#define MAX2839_FT_3_5M 2
|
||||
#define MAX2839_FT_5M 3
|
||||
#define MAX2839_FT_5_5M 4
|
||||
#define MAX2839_FT_6M 5
|
||||
#define MAX2839_FT_7M 6
|
||||
#define MAX2839_FT_8M 7
|
||||
#define MAX2839_FT_9M 8
|
||||
#define MAX2839_FT_10M 9
|
||||
#define MAX2839_FT_12M 10
|
||||
#define MAX2839_FT_14M 11
|
||||
#define MAX2839_FT_15M 12
|
||||
#define MAX2839_FT_20M 13
|
||||
#define MAX2839_FT_24M 14
|
||||
#define MAX2839_FT_28M 15
|
||||
|
||||
/* REG 5 */
|
||||
__MREG__(MAX2839_LNA1gain,5,1,2)
|
||||
#define MAX2839_LNA1gain_MAX 0b000 // Pad in 8dB steps, bits reversed
|
||||
#define MAX2839_LNA1gain_M8 0b001
|
||||
#define MAX2839_LNA1gain_M16 0b010
|
||||
#define MAX2839_LNA1gain_M32 0b011
|
||||
__MREG__(MAX2839_Rx1_VGAgain,5,7,6)
|
||||
__MREG__(MAX2839_LPFmode,5,9,2)
|
||||
#define MAX2839_LPFmode_RxCalibration 0
|
||||
#define MAX2839_LPFmode_RxLPF 1
|
||||
#define MAX2839_LPFmode_TxLPF 2
|
||||
#define MAX2839_LPFmode_LPFTrim 3
|
||||
|
||||
/* REG 6 */
|
||||
__MREG__(MAX2839_LNA2gain,6,1,2)
|
||||
#define MAX2839_LNA2gain_MAX 0b000 // Pad in 8dB steps, bits reversed
|
||||
#define MAX2839_LNA2gain_M8 0b001
|
||||
#define MAX2839_LNA2gain_M16 0b010
|
||||
#define MAX2839_LNA2gain_M32 0b011
|
||||
__MREG__(MAX2839_Rx2_VGAgain,6,7,6)
|
||||
__MREG__(MAX2839_RX_VCM,6,9,2)
|
||||
#define MAX2839_RX_VCM_1_0 0b00 // 1.0 V
|
||||
#define MAX2839_RX_VCM_1_1 0b01 // 1.1 V
|
||||
#define MAX2839_RX_VCM_1_2 0b10 // 1.2 V
|
||||
#define MAX2839_RX_VCM_1_35 0b11 // 1.35 V
|
||||
|
||||
/* REG 7 */
|
||||
__MREG__(MAX2839_RESERVED_7_0,7,0,1)
|
||||
__MREG__(MAX2839_RSSIselect,7,1,1)
|
||||
__MREG__(MAX2839_RSSImode,7,2,1)
|
||||
__MREG__(MAX2839_RESERVED_7_6,7,6,4)
|
||||
__MREG__(MAX2839_RXBBI_RXBBQ,7,7,1)
|
||||
__MREG__(MAX2839_RESERVED_7_8,7,8,1)
|
||||
__MREG__(MAX2839_RSSIinput,7,9,1)
|
||||
|
||||
/* REG 8 */
|
||||
__MREG__(MAX2839_RESERVED_8_0,8,0,1)
|
||||
__MREG__(MAX2839_VGAgain_SPI,8,1,1)
|
||||
__MREG__(MAX2839_LPFmode_SPI,8,2,1)
|
||||
__MREG__(MAX2839_RESERVED_8_9,8,9,7)
|
||||
|
||||
/* REG 9 */
|
||||
__MREG__(MAX2839_Temperature_ADC,9,0,1)
|
||||
__MREG__(MAX2839_Temperature_Clk_En,9,1,1)
|
||||
__MREG__(MAX2839_RESERVED_9_2,9,2,1)
|
||||
__MREG__(MAX2839_DOUT_Drive_Sel,9,3,1)
|
||||
__MREG__(MAX2839_DOUT_3state_Ctrl,9,4,1)
|
||||
__MREG__(MAX2839_DOUT_SEL,9,7,3)
|
||||
#define MAX2839_DOUT_SEL_SPI 0 // default, SPI comm
|
||||
#define MAX2839_DOUT_SEL_PLL_LOCK_DETECT 1
|
||||
#define MAX2839_DOUT_SEL_VAS_TEST_OUT 2
|
||||
#define MAX2839_DOUT_SEL_HPFSM_TEST_OUT 3
|
||||
#define MAX2839_DOUT_SEL_LOGEN_TRIM_OUT 4
|
||||
#define MAX2839_DOUT_SEL_RX_FUSE_GASKET 5
|
||||
#define MAX2839_DOUT_SEL_TX_FUSE_GASKET 6
|
||||
#define MAX2839_DOUT_SEL_ZERO 7
|
||||
__MREG__(MAX2839_RESERVED_9_9,9,9,2)
|
||||
|
||||
/* REG 10 */
|
||||
__MREG__(MAX2839_TX_AM_gain,10,1,2)
|
||||
__MREG__(MAX2839_TX_AM_bandwidth,10,4,3)
|
||||
__MREG__(MAX2839_RESERVED_10_9,10,9,5)
|
||||
|
||||
/* REG 11 */
|
||||
__MREG__(MAX2839_RESERVED_11_9,11,9,10)
|
||||
|
||||
/* REG 12 */
|
||||
__MREG__(MAX2839_HPC_10M_RXEN_duration,12,1,2)
|
||||
__MREG__(MAX2839_HPC_10M_B6B7_duration,12,3,2)
|
||||
__MREG__(MAX2839_HPC_600k_RXEN_duration,12,6,3)
|
||||
__MREG__(MAX2839_HPC_600k_B6B7_duration,12,9,3)
|
||||
|
||||
/* REG 13 */
|
||||
__MREG__(MAX2839_HPC_100k_RXEN_duration,13,1,2)
|
||||
__MREG__(MAX2839_HPC_100k_B6B7_duration,13,3,2)
|
||||
__MREG__(MAX2839_HPC_30k_RXEN_duration,13,5,2)
|
||||
__MREG__(MAX2839_HPC_30k_B6B7_duration,13,7,2)
|
||||
__MREG__(MAX2839_HPC_1k_RXEN_duration,13,9,2)
|
||||
|
||||
/* REG 14 */
|
||||
__MREG__(MAX2839_HPC_1k_B6B7_duration,14,1,2)
|
||||
__MREG__(MAX2839_HPC_DELAY,14,3,2)
|
||||
__MREG__(MAX2839_HPC_STOP,14,5,2)
|
||||
#define MAX2839_STOP_100 0
|
||||
#define MAX2839_STOP_1K 1
|
||||
#define MAX2839_STOP_30K 2
|
||||
#define MAX2839_STOP_100K 3
|
||||
__MREG__(MAX2839_HPC_STOP_MODE2,14,7,2)
|
||||
__MREG__(MAX2839_HPC_RXGAIN_EN,14,8,1)
|
||||
__MREG__(MAX2839_PA_DRV_GATE,14,9,1)
|
||||
|
||||
/* REG 15 */
|
||||
__MREG__(MAX2839_RXVGA_HPFSM_Clk_Divider,15,0,1)
|
||||
__MREG__(MAX2839_RESERVED_15_5,15,5,5)
|
||||
__MREG__(MAX2839_RXHP_sequence_bypass,15,6,1)
|
||||
__MREG__(MAX2839_RESERVED_15_8,15,8,2)
|
||||
__MREG__(MAX2839_RXHP_highpass_corner,15,9,1)
|
||||
|
||||
/* REG 16 */
|
||||
__MREG__(MAX2839_chip_enable,16,0,1)
|
||||
__MREG__(MAX2839_RXTX_calibration_enable,16,1,1)
|
||||
__MREG__(MAX2839_RESERVED_16_5,16,5,4)
|
||||
__MREG__(MAX2839_PA_bias_DAC_SPI_enable,16,6,1)
|
||||
__MREG__(MAX2839_PA_bias_DAC_TX_mode_enable,16,7,1)
|
||||
__MREG__(MAX2839_RESERVED_16_9,16,9,2)
|
||||
|
||||
/* REG 17 */
|
||||
__MREG__(MAX2839_SYN_FRAC_LO,17,9,10)
|
||||
|
||||
/* REG 18 */
|
||||
__MREG__(MAX2839_SYN_FRAC_HI,18,9,10)
|
||||
|
||||
/* REG 19 */
|
||||
__MREG__(MAX2839_SYN_INT,19,7,8)
|
||||
__MREG__(MAX2839_LOGEN_BSW,19,9,2)
|
||||
#define MAX2839_LOGEN_BSW_2_3 0 // 2300 - <2400 MHz
|
||||
#define MAX2839_LOGEN_BSW_2_4 1 // 2400 - <2500 MHz
|
||||
#define MAX2839_LOGEN_BSW_2_5 2 // 2500 - <2600 MHz
|
||||
#define MAX2839_LOGEN_BSW_2_6 3 // 2600 - <2700 MHz
|
||||
|
||||
/* REG 20 */
|
||||
__MREG__(MAX2839_RESERVED_20_0,20,0,1)
|
||||
__MREG__(MAX2839_Reference_Divider_Ratio,20,2,2)
|
||||
__MREG__(MAX2839_RESERVED_20_4,20,4,2)
|
||||
__MREG__(MAX2839_CLKOUT_Buffer_Drive,20,5,1)
|
||||
__MREG__(MAX2839_RESERVED_20_9,20,9,4)
|
||||
|
||||
/* REG 21 */
|
||||
__MREG__(MAX2839_RESERVED_21_9,21,9,10)
|
||||
|
||||
/* REG 22 */
|
||||
__MREG__(MAX2839_VAS_Operating_Mode_Select,22,0,1)
|
||||
__MREG__(MAX2839_VAS_Relock_Mode_Select,22,1,1)
|
||||
__MREG__(MAX2839_VAS_Clk_Divide_Ratio,22,4,3)
|
||||
__MREG__(MAX2839_VAS_Delay_Counter_Ratio,22,6,2)
|
||||
__MREG__(MAX2839_VAS_Addr17_Trigger_Enable,22,7,1)
|
||||
__MREG__(MAX2839_RESERVED_22_9,22,9,2)
|
||||
|
||||
/* REG 23 */
|
||||
__MREG__(MAX2839_VAS_Subband_SPI_Overwrite,23,4,5)
|
||||
__MREG__(MAX2839_Crystal_Oscillator_Bias_Select,23,6,2)
|
||||
__MREG__(MAX2839_RESERVED_23_9,23,9,3)
|
||||
|
||||
/* REG 24 */
|
||||
__MREG__(MAX2839_Crystal_Oscillator_Freq_Tuning,24,6,7)
|
||||
__MREG__(MAX2839_RESERVED_24_7,24,7,1)
|
||||
__MREG__(MAX2839_CLKOUT_Divide_Ratio,24,8,1)
|
||||
__MREG__(MAX2839_Crystal_Oscillator_Core_Enable,24,9,1)
|
||||
|
||||
/* REG 25 */
|
||||
__MREG__(MAX2839_RESERVED_25_9,25,9,10)
|
||||
|
||||
/* REG 26 */
|
||||
__MREG__(MAX2839_RESERVED_26_2,26,2,3)
|
||||
__MREG__(MAX2839_LOGEN_RXTX_Gm_Enable,26,3,1)
|
||||
__MREG__(MAX2839_RESERVED_26_5,26,5,2)
|
||||
__MREG__(MAX2839_VAS_Test_Signal_Select,26,9,4)
|
||||
|
||||
/* REG 27 */
|
||||
__MREG__(MAX2839_TX_LO_IQ_Phase_SPI_Adjust_Addr27,27,5,6)
|
||||
__MREG__(MAX2839_TX_LO_IQ_Phase_SPI_Adjust_Enable,27,6,1)
|
||||
__MREG__(MAX2839_TX_VGA_Gain_SPI,27,7,1)
|
||||
__MREG__(MAX2839_TX_DC_Offset_SPI_Adjust_Enable,27,8,1)
|
||||
__MREG__(MAX2839_RESERVED_27_9,27,9,1)
|
||||
|
||||
/* REG 28 */
|
||||
__MREG__(MAX2839_PADAC_Output_Current_Ctrl,28,5,6)
|
||||
__MREG__(MAX2839_PADAC_TurnOn_Delay_Ctrl,28,9,4)
|
||||
|
||||
/* REG 29 */
|
||||
__MREG__(MAX2839_TX_VGA_GAIN,29,5,6)
|
||||
__MREG__(MAX2839_RESERVED_29_9,29,9,4)
|
||||
|
||||
/* REG 30 */
|
||||
__MREG__(MAX2839_TX_DC_Offset_Correction_Addr27,30,5,6)
|
||||
__MREG__(MAX2839_RESERVED_30_7,30,7,2)
|
||||
__MREG__(MAX2839_PA_DAC_IV_Output_Select,30,8,1)
|
||||
__MREG__(MAX2839_PA_DAC_Voltage_Mode_Output_Select,30,9,1)
|
||||
|
||||
/* REG 31 */
|
||||
__MREG__(MAX2839_TX_DC_Offset_Correction_QChannel,31,5,6)
|
||||
__MREG__(MAX2839_RESERVED_31_8,31,8,3)
|
||||
__MREG__(MAX2839_PA_DAC_Clk_Divide_Ratio,31,9,1)
|
||||
|
||||
#endif // __MAX2839_REGS_DEF
|
98
firmware/common/max2839_target.c
Normal file
98
firmware/common/max2839_target.c
Normal file
@ -0,0 +1,98 @@
|
||||
/*
|
||||
* Copyright 2012-2022 Great Scott Gadgets
|
||||
* Copyright 2014 Jared Boone <jared@sharebrained.com>
|
||||
* Copyright 2012 Will Code <willcode4@gmail.com>
|
||||
*
|
||||
* This file is part of HackRF.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "max2839_target.h"
|
||||
|
||||
#include <libopencm3/lpc43xx/scu.h>
|
||||
#include "hackrf_core.h"
|
||||
|
||||
void max2839_target_init(max2839_driver_t* const drv)
|
||||
{
|
||||
/* Configure SSP1 Peripheral (to be moved later in SSP driver) */
|
||||
scu_pinmux(SCU_SSP1_CIPO, (SCU_SSP_IO | SCU_CONF_FUNCTION5));
|
||||
scu_pinmux(SCU_SSP1_COPI, (SCU_SSP_IO | SCU_CONF_FUNCTION5));
|
||||
scu_pinmux(SCU_SSP1_SCK, (SCU_SSP_IO | SCU_CONF_FUNCTION1));
|
||||
|
||||
scu_pinmux(SCU_XCVR_CS, SCU_GPIO_FAST);
|
||||
|
||||
/*
|
||||
* Configure XCVR_CTL GPIO pins.
|
||||
*
|
||||
* The RXTX pin is also known as RXENABLE because of its use on the
|
||||
* MAX2837 which had a separate TXENABLE. On MAX2839 a single RXTX pin
|
||||
* switches between RX (high) and TX (low) modes.
|
||||
*/
|
||||
scu_pinmux(SCU_XCVR_ENABLE, SCU_GPIO_FAST);
|
||||
scu_pinmux(SCU_XCVR_RXENABLE, SCU_GPIO_FAST);
|
||||
|
||||
/* Set GPIO pins as outputs. */
|
||||
gpio_output(drv->gpio_enable);
|
||||
gpio_output(drv->gpio_rxtx);
|
||||
}
|
||||
|
||||
void max2839_target_set_mode(max2839_driver_t* const drv, const max2839_mode_t new_mode)
|
||||
{
|
||||
/* MAX2839_MODE_SHUTDOWN:
|
||||
* All circuit blocks are powered down, except the 4-wire serial bus
|
||||
* and its internal programmable registers.
|
||||
*
|
||||
* MAX2839_MODE_STANDBY:
|
||||
* Used to enable the frequency synthesizer block while the rest of the
|
||||
* device is powered down. In this mode, PLL, VCO, and LO generator
|
||||
* are on, so that Tx or Rx modes can be quickly enabled from this mode.
|
||||
* These and other blocks can be selectively enabled in this mode.
|
||||
*
|
||||
* MAX2839_MODE_TX:
|
||||
* All Tx circuit blocks are powered on. The external PA is powered on
|
||||
* after a programmable delay using the on-chip PA bias DAC. The slow-
|
||||
* charging Rx circuits are in a precharged “idle-off” state for fast
|
||||
* Tx-to-Rx turnaround time.
|
||||
*
|
||||
* MAX2839_MODE_RX:
|
||||
* All Rx circuit blocks are powered on and active. Antenna signal is
|
||||
* applied; RF is downconverted, filtered, and buffered at Rx BB I and Q
|
||||
* outputs. The slow- charging Tx circuits are in a precharged “idle-off”
|
||||
* state for fast Rx-to-Tx turnaround time.
|
||||
*/
|
||||
|
||||
switch (new_mode) {
|
||||
default:
|
||||
case MAX2839_MODE_SHUTDOWN:
|
||||
gpio_clear(drv->gpio_enable);
|
||||
gpio_clear(drv->gpio_rxtx);
|
||||
break;
|
||||
case MAX2839_MODE_STANDBY:
|
||||
gpio_clear(drv->gpio_enable);
|
||||
gpio_set(drv->gpio_rxtx);
|
||||
break;
|
||||
case MAX2839_MODE_TX:
|
||||
gpio_set(drv->gpio_enable);
|
||||
gpio_clear(drv->gpio_rxtx);
|
||||
break;
|
||||
case MAX2839_MODE_RX:
|
||||
gpio_set(drv->gpio_enable);
|
||||
gpio_set(drv->gpio_rxtx);
|
||||
break;
|
||||
}
|
||||
drv->mode = new_mode;
|
||||
}
|
32
firmware/common/max2839_target.h
Normal file
32
firmware/common/max2839_target.h
Normal file
@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright 2012-2022 Great Scott Gadgets
|
||||
* Copyright 2014 Jared Boone <jared@sharebrained.com>
|
||||
* Copyright 2012 Will Code <willcode4@gmail.com>
|
||||
*
|
||||
* This file is part of HackRF.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __MAX2839_TARGET_H
|
||||
#define __MAX2839_TARGET_H
|
||||
|
||||
#include "max2839.h"
|
||||
|
||||
void max2839_target_init(max2839_driver_t* const drv);
|
||||
void max2839_target_set_mode(max2839_driver_t* const drv, const max2839_mode_t new_mode);
|
||||
|
||||
#endif // __MAX2839_TARGET_H
|
280
firmware/common/max283x.c
Normal file
280
firmware/common/max283x.c
Normal file
@ -0,0 +1,280 @@
|
||||
/*
|
||||
* Copyright 2012-2022 Great Scott Gadgets <info@greatscottgadgets.com>
|
||||
* Copyright 2012 Will Code <willcode4@gmail.com>
|
||||
* Copyright 2014 Jared Boone <jared@sharebrained.com>
|
||||
*
|
||||
* This file is part of HackRF.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "max283x.h"
|
||||
|
||||
#include "gpio.h"
|
||||
#include "gpio_lpc.h"
|
||||
#include "max2837.h"
|
||||
#include "max2837_target.h"
|
||||
#include "max2839.h"
|
||||
#include "max2839_target.h"
|
||||
#include "spi_bus.h"
|
||||
|
||||
extern spi_bus_t spi_bus_ssp1;
|
||||
static struct gpio_t gpio_max2837_enable = GPIO(2, 6);
|
||||
static struct gpio_t gpio_max2837_rx_enable = GPIO(2, 5);
|
||||
static struct gpio_t gpio_max2837_tx_enable = GPIO(2, 4);
|
||||
|
||||
max2837_driver_t max2837 = {
|
||||
.bus = &spi_bus_ssp1,
|
||||
.gpio_enable = &gpio_max2837_enable,
|
||||
.gpio_rx_enable = &gpio_max2837_rx_enable,
|
||||
.gpio_tx_enable = &gpio_max2837_tx_enable,
|
||||
.target_init = max2837_target_init,
|
||||
.set_mode = max2837_target_set_mode,
|
||||
};
|
||||
|
||||
max2839_driver_t max2839 = {
|
||||
.bus = &spi_bus_ssp1,
|
||||
.gpio_enable = &gpio_max2837_enable,
|
||||
.gpio_rxtx = &gpio_max2837_rx_enable,
|
||||
.target_init = max2839_target_init,
|
||||
.set_mode = max2839_target_set_mode,
|
||||
};
|
||||
|
||||
/* Initialize chip. */
|
||||
void max283x_setup(max283x_driver_t* const drv, max283x_variant_t type)
|
||||
{
|
||||
drv->type = type;
|
||||
switch (type) {
|
||||
case MAX2837_VARIANT:
|
||||
memcpy(&drv->drv.max2837, &max2837, sizeof(max2837));
|
||||
max2837_setup(&drv->drv.max2837);
|
||||
break;
|
||||
|
||||
case MAX2839_VARIANT:
|
||||
memcpy(&drv->drv.max2839, &max2839, sizeof(max2839));
|
||||
max2839_setup(&drv->drv.max2839);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Read a register via SPI. Save a copy to memory and return
|
||||
* value. Mark clean. */
|
||||
uint16_t max283x_reg_read(max283x_driver_t* const drv, uint8_t r)
|
||||
{
|
||||
switch (drv->type) {
|
||||
case MAX2837_VARIANT:
|
||||
return max2837_reg_read(&drv->drv.max2837, r);
|
||||
break;
|
||||
|
||||
case MAX2839_VARIANT:
|
||||
return max2839_reg_read(&drv->drv.max2839, r);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Write value to register via SPI and save a copy to memory. Mark
|
||||
* clean. */
|
||||
void max283x_reg_write(max283x_driver_t* const drv, uint8_t r, uint16_t v)
|
||||
{
|
||||
switch (drv->type) {
|
||||
case MAX2837_VARIANT:
|
||||
max2837_reg_write(&drv->drv.max2837, r, v);
|
||||
break;
|
||||
|
||||
case MAX2839_VARIANT:
|
||||
max2839_reg_write(&drv->drv.max2839, r, v);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Write all dirty registers via SPI from memory. Mark all clean. Some
|
||||
* operations require registers to be written in a certain order. Use
|
||||
* provided routines for those operations. */
|
||||
void max283x_regs_commit(max283x_driver_t* const drv)
|
||||
{
|
||||
switch (drv->type) {
|
||||
case MAX2837_VARIANT:
|
||||
max2837_regs_commit(&drv->drv.max2837);
|
||||
break;
|
||||
|
||||
case MAX2839_VARIANT:
|
||||
max2839_regs_commit(&drv->drv.max2839);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void max283x_set_mode(max283x_driver_t* const drv, const max283x_mode_t new_mode)
|
||||
{
|
||||
switch (drv->type) {
|
||||
case MAX2837_VARIANT:
|
||||
max2837_set_mode(&drv->drv.max2837, (max2837_mode_t) new_mode);
|
||||
break;
|
||||
|
||||
case MAX2839_VARIANT:
|
||||
max2839_set_mode(&drv->drv.max2839, (max2839_mode_t) new_mode);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
max283x_mode_t max283x_mode(max283x_driver_t* const drv)
|
||||
{
|
||||
switch (drv->type) {
|
||||
case MAX2837_VARIANT:
|
||||
return (max283x_mode_t) max2837_mode(&drv->drv.max2837);
|
||||
break;
|
||||
|
||||
case MAX2839_VARIANT:
|
||||
return (max283x_mode_t) max2839_mode(&drv->drv.max2839);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//max283x_mode_t max283x_mode(max283x_driver_t* const drv);
|
||||
//void max283x_set_mode(max283x_driver_t* const drv, const max283x_mode_t new_mode);
|
||||
|
||||
/* Turn on/off all chip functions. Does not control oscillator and CLKOUT */
|
||||
void max283x_start(max283x_driver_t* const drv)
|
||||
{
|
||||
switch (drv->type) {
|
||||
case MAX2837_VARIANT:
|
||||
max2837_start(&drv->drv.max2837);
|
||||
break;
|
||||
|
||||
case MAX2839_VARIANT:
|
||||
max2839_start(&drv->drv.max2839);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void max283x_stop(max283x_driver_t* const drv)
|
||||
{
|
||||
switch (drv->type) {
|
||||
case MAX2837_VARIANT:
|
||||
max2837_stop(&drv->drv.max2837);
|
||||
break;
|
||||
|
||||
case MAX2839_VARIANT:
|
||||
max2839_stop(&drv->drv.max2839);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Set frequency in Hz. Frequency setting is a multi-step function
|
||||
* where order of register writes matters. */
|
||||
void max283x_set_frequency(max283x_driver_t* const drv, uint32_t freq)
|
||||
{
|
||||
switch (drv->type) {
|
||||
case MAX2837_VARIANT:
|
||||
max2837_set_frequency(&drv->drv.max2837, freq);
|
||||
break;
|
||||
|
||||
case MAX2839_VARIANT:
|
||||
max2839_set_frequency(&drv->drv.max2839, freq);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t max283x_set_lpf_bandwidth(
|
||||
max283x_driver_t* const drv,
|
||||
const uint32_t bandwidth_hz)
|
||||
{
|
||||
switch (drv->type) {
|
||||
case MAX2837_VARIANT:
|
||||
return max2837_set_lpf_bandwidth(&drv->drv.max2837, bandwidth_hz);
|
||||
break;
|
||||
|
||||
case MAX2839_VARIANT:
|
||||
return max2839_set_lpf_bandwidth(&drv->drv.max2839, bandwidth_hz);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool max283x_set_lna_gain(max283x_driver_t* const drv, const uint32_t gain_db)
|
||||
{
|
||||
switch (drv->type) {
|
||||
case MAX2837_VARIANT:
|
||||
return max2837_set_lna_gain(&drv->drv.max2837, gain_db);
|
||||
break;
|
||||
|
||||
case MAX2839_VARIANT:
|
||||
return max2839_set_lna_gain(&drv->drv.max2839, gain_db);
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool max283x_set_vga_gain(max283x_driver_t* const drv, const uint32_t gain_db)
|
||||
{
|
||||
switch (drv->type) {
|
||||
case MAX2837_VARIANT:
|
||||
return max2837_set_vga_gain(&drv->drv.max2837, gain_db);
|
||||
break;
|
||||
|
||||
case MAX2839_VARIANT:
|
||||
return max2839_set_vga_gain(&drv->drv.max2839, gain_db);
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool max283x_set_txvga_gain(max283x_driver_t* const drv, const uint32_t gain_db)
|
||||
{
|
||||
switch (drv->type) {
|
||||
case MAX2837_VARIANT:
|
||||
return max2837_set_txvga_gain(&drv->drv.max2837, gain_db);
|
||||
break;
|
||||
|
||||
case MAX2839_VARIANT:
|
||||
return max2839_set_txvga_gain(&drv->drv.max2839, gain_db);
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void max283x_tx(max283x_driver_t* const drv)
|
||||
{
|
||||
switch (drv->type) {
|
||||
case MAX2837_VARIANT:
|
||||
max2837_tx(&drv->drv.max2837);
|
||||
break;
|
||||
|
||||
case MAX2839_VARIANT:
|
||||
max2839_tx(&drv->drv.max2839);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void max283x_rx(max283x_driver_t* const drv)
|
||||
{
|
||||
switch (drv->type) {
|
||||
case MAX2837_VARIANT:
|
||||
max2837_rx(&drv->drv.max2837);
|
||||
break;
|
||||
|
||||
case MAX2839_VARIANT:
|
||||
max2839_rx(&drv->drv.max2839);
|
||||
break;
|
||||
}
|
||||
}
|
101
firmware/common/max283x.h
Normal file
101
firmware/common/max283x.h
Normal file
@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Copyright 2012-2022 Great Scott Gadgets <info@greatscottgadgets.com>
|
||||
* Copyright 2012 Will Code <willcode4@gmail.com>
|
||||
* Copyright 2014 Jared Boone <jared@sharebrained.com>
|
||||
*
|
||||
* This file is part of HackRF.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __MAX283x_H
|
||||
#define __MAX283x_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "gpio.h"
|
||||
#include "gpio_lpc.h"
|
||||
#include "max2837.h"
|
||||
#include "max2837_target.h"
|
||||
#include "max2839.h"
|
||||
#include "max2839_target.h"
|
||||
#include "spi_bus.h"
|
||||
|
||||
typedef enum {
|
||||
MAX283x_MODE_SHUTDOWN,
|
||||
MAX283x_MODE_STANDBY,
|
||||
MAX283x_MODE_TX,
|
||||
MAX283x_MODE_RX,
|
||||
MAX283x_MODE_RX_CAL,
|
||||
MAX283x_MODE_TX_CAL,
|
||||
MAX283x_MODE_CLKOUT,
|
||||
} max283x_mode_t;
|
||||
|
||||
typedef enum {
|
||||
MAX2837_VARIANT,
|
||||
MAX2839_VARIANT,
|
||||
} max283x_variant_t;
|
||||
|
||||
typedef struct {
|
||||
max283x_variant_t type;
|
||||
|
||||
union {
|
||||
max2837_driver_t max2837;
|
||||
max2839_driver_t max2839;
|
||||
} drv;
|
||||
} max283x_driver_t;
|
||||
|
||||
/* Initialize chip. */
|
||||
void max283x_setup(max283x_driver_t* const drv, max283x_variant_t type);
|
||||
|
||||
/* Read a register via SPI. Save a copy to memory and return
|
||||
* value. Mark clean. */
|
||||
uint16_t max283x_reg_read(max283x_driver_t* const drv, uint8_t r);
|
||||
|
||||
/* Write value to register via SPI and save a copy to memory. Mark
|
||||
* clean. */
|
||||
void max283x_reg_write(max283x_driver_t* const drv, uint8_t r, uint16_t v);
|
||||
|
||||
/* Write all dirty registers via SPI from memory. Mark all clean. Some
|
||||
* operations require registers to be written in a certain order. Use
|
||||
* provided routines for those operations. */
|
||||
void max283x_regs_commit(max283x_driver_t* const drv);
|
||||
|
||||
max283x_mode_t max283x_mode(max283x_driver_t* const drv);
|
||||
void max283x_set_mode(max283x_driver_t* const drv, const max283x_mode_t new_mode);
|
||||
|
||||
/* Turn on/off all chip functions. Does not control oscillator and CLKOUT */
|
||||
void max283x_start(max283x_driver_t* const drv);
|
||||
void max283x_stop(max283x_driver_t* const drv);
|
||||
|
||||
/* Set frequency in Hz. Frequency setting is a multi-step function
|
||||
* where order of register writes matters. */
|
||||
void max283x_set_frequency(max283x_driver_t* const drv, uint32_t freq);
|
||||
uint32_t max283x_set_lpf_bandwidth(
|
||||
max283x_driver_t* const drv,
|
||||
const uint32_t bandwidth_hz);
|
||||
|
||||
bool max283x_set_lna_gain(max283x_driver_t* const drv, const uint32_t gain_db);
|
||||
|
||||
bool max283x_set_vga_gain(max283x_driver_t* const drv, const uint32_t gain_db);
|
||||
bool max283x_set_txvga_gain(max283x_driver_t* const drv, const uint32_t gain_db);
|
||||
|
||||
void max283x_tx(max283x_driver_t* const drv);
|
||||
void max283x_rx(max283x_driver_t* const drv);
|
||||
|
||||
#endif // __MAX283x_H
|
@ -28,11 +28,12 @@
|
||||
#include <hackrf_core.h>
|
||||
|
||||
#include "hackrf_ui.h"
|
||||
|
||||
#include <mixer.h>
|
||||
#include <max2837.h>
|
||||
#include <max5864.h>
|
||||
#include <sgpio.h>
|
||||
#include "gpio_lpc.h"
|
||||
#include "platform_detect.h"
|
||||
#include "mixer.h"
|
||||
#include "max283x.h"
|
||||
#include "max5864.h"
|
||||
#include "sgpio.h"
|
||||
|
||||
#if (defined JAWBREAKER || defined HACKRF_ONE || defined RAD1O)
|
||||
/*
|
||||
@ -81,21 +82,35 @@
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Antenna port power on HackRF One is controlled by GPO1 on the RFFC5072.
|
||||
* This is the only thing we use RFFC5072 GPO for on HackRF One. The value of
|
||||
* SWITCHCTRL_NO_ANT_PWR does not correspond to the GPO1 bit in the gpo
|
||||
* register.
|
||||
* Antenna port power on HackRF One (prior to r9) is controlled by GPO1 on the
|
||||
* RFFC5072. This is the only thing we use RFFC5072 GPO for on HackRF One.
|
||||
* The value of SWITCHCTRL_NO_ANT_PWR does not correspond to the GPO1 bit in
|
||||
* the gpo register.
|
||||
*/
|
||||
|
||||
#define SWITCHCTRL_ANT_PWR (1 << 6) /* turn on antenna port power */
|
||||
|
||||
/*
|
||||
* Starting with HackRF One r9 this control signal has been moved to the
|
||||
* microcontroller.
|
||||
*/
|
||||
|
||||
#ifdef HACKRF_ONE
|
||||
static struct gpio_t gpio_h1r9_no_ant_pwr = GPIO(2, 4);
|
||||
#endif
|
||||
|
||||
#ifdef HACKRF_ONE
|
||||
static void switchctrl_set_hackrf_one(rf_path_t* const rf_path, uint8_t ctrl)
|
||||
{
|
||||
if (ctrl & SWITCHCTRL_TX) {
|
||||
gpio_set(rf_path->gpio_tx);
|
||||
if (detected_platform() != BOARD_ID_HACKRF1_R9) {
|
||||
gpio_set(rf_path->gpio_tx);
|
||||
}
|
||||
gpio_clear(rf_path->gpio_rx);
|
||||
} else {
|
||||
gpio_clear(rf_path->gpio_tx);
|
||||
if (detected_platform() != BOARD_ID_HACKRF1_R9) {
|
||||
gpio_clear(rf_path->gpio_tx);
|
||||
}
|
||||
gpio_set(rf_path->gpio_rx);
|
||||
}
|
||||
|
||||
@ -156,10 +171,22 @@ static void switchctrl_set_hackrf_one(rf_path_t* const rf_path, uint8_t ctrl)
|
||||
gpio_set(rf_path->gpio_no_rx_amp_pwr);
|
||||
}
|
||||
|
||||
if (ctrl & SWITCHCTRL_ANT_PWR) {
|
||||
mixer_set_gpo(&mixer, 0x00); /* turn on antenna power by clearing GPO1 */
|
||||
if (detected_platform() == BOARD_ID_HACKRF1_R9) {
|
||||
if (ctrl & SWITCHCTRL_ANT_PWR) {
|
||||
gpio_clear(&gpio_h1r9_no_ant_pwr);
|
||||
} else {
|
||||
gpio_set(&gpio_h1r9_no_ant_pwr);
|
||||
}
|
||||
} else {
|
||||
mixer_set_gpo(&mixer, 0x01); /* turn off antenna power by setting GPO1 */
|
||||
if (ctrl & SWITCHCTRL_ANT_PWR) {
|
||||
mixer_set_gpo(
|
||||
&mixer,
|
||||
0x00); /* turn on antenna power by clearing GPO1 */
|
||||
} else {
|
||||
mixer_set_gpo(
|
||||
&mixer,
|
||||
0x01); /* turn off antenna power by setting GPO1 */
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@ -254,17 +281,24 @@ void rf_path_pin_setup(rf_path_t* const rf_path)
|
||||
scu_pinmux(SCU_NO_MIX_BYPASS, SCU_GPIO_FAST | SCU_CONF_FUNCTION0);
|
||||
scu_pinmux(SCU_RX_MIX_BP, SCU_GPIO_FAST | SCU_CONF_FUNCTION0);
|
||||
scu_pinmux(SCU_TX_AMP, SCU_GPIO_FAST | SCU_CONF_FUNCTION0);
|
||||
scu_pinmux(SCU_TX, SCU_GPIO_FAST | SCU_CONF_FUNCTION4);
|
||||
scu_pinmux(SCU_MIX_BYPASS, SCU_GPIO_FAST | SCU_CONF_FUNCTION4);
|
||||
scu_pinmux(SCU_RX, SCU_GPIO_FAST | SCU_CONF_FUNCTION4);
|
||||
scu_pinmux(SCU_NO_TX_AMP_PWR, SCU_GPIO_FAST | SCU_CONF_FUNCTION0);
|
||||
scu_pinmux(SCU_AMP_BYPASS, SCU_GPIO_FAST | SCU_CONF_FUNCTION0);
|
||||
scu_pinmux(SCU_RX_AMP, SCU_GPIO_FAST | SCU_CONF_FUNCTION0);
|
||||
scu_pinmux(SCU_NO_RX_AMP_PWR, SCU_GPIO_FAST | SCU_CONF_FUNCTION0);
|
||||
// clang-format on
|
||||
|
||||
/* Configure RF power supply (VAA) switch */
|
||||
scu_pinmux(SCU_NO_VAA_ENABLE, SCU_GPIO_FAST | SCU_CONF_FUNCTION0);
|
||||
if (detected_platform() == BOARD_ID_HACKRF1_R9) {
|
||||
scu_pinmux(SCU_H1R9_RX, SCU_GPIO_FAST | SCU_CONF_FUNCTION0);
|
||||
scu_pinmux(SCU_H1R9_NO_ANT_PWR, SCU_GPIO_FAST | SCU_CONF_FUNCTION0);
|
||||
gpio_clear(&gpio_h1r9_no_ant_pwr);
|
||||
gpio_output(&gpio_h1r9_no_ant_pwr);
|
||||
scu_pinmux(SCU_H1R9_NO_VAA_EN, SCU_GPIO_FAST | SCU_CONF_FUNCTION0);
|
||||
} else {
|
||||
scu_pinmux(SCU_TX, SCU_GPIO_FAST | SCU_CONF_FUNCTION4);
|
||||
scu_pinmux(SCU_RX, SCU_GPIO_FAST | SCU_CONF_FUNCTION4);
|
||||
gpio_output(rf_path->gpio_tx);
|
||||
scu_pinmux(SCU_NO_VAA_ENABLE, SCU_GPIO_FAST | SCU_CONF_FUNCTION0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Safe (initial) switch settings turn off both amplifiers and antenna port
|
||||
@ -283,7 +317,6 @@ void rf_path_pin_setup(rf_path_t* const rf_path)
|
||||
gpio_output(rf_path->gpio_rx_mix_bp);
|
||||
gpio_output(rf_path->gpio_tx_amp);
|
||||
gpio_output(rf_path->gpio_no_tx_amp_pwr);
|
||||
gpio_output(rf_path->gpio_tx);
|
||||
gpio_output(rf_path->gpio_mix_bypass);
|
||||
gpio_output(rf_path->gpio_rx);
|
||||
#elif RAD1O
|
||||
@ -334,9 +367,13 @@ void rf_path_init(rf_path_t* const rf_path)
|
||||
max5864_setup(&max5864);
|
||||
max5864_shutdown(&max5864);
|
||||
|
||||
ssp1_set_mode_max2837();
|
||||
max2837_setup(&max2837);
|
||||
max2837_start(&max2837);
|
||||
ssp1_set_mode_max283x();
|
||||
if (detected_platform() == BOARD_ID_HACKRF1_R9) {
|
||||
max283x_setup(&max283x, MAX2839_VARIANT);
|
||||
} else {
|
||||
max283x_setup(&max283x, MAX2837_VARIANT);
|
||||
}
|
||||
max283x_start(&max283x);
|
||||
|
||||
// On HackRF One, the mixer is now set up earlier in boot.
|
||||
#ifndef HACKRF_ONE
|
||||
@ -364,8 +401,8 @@ void rf_path_set_direction(rf_path_t* const rf_path, const rf_path_direction_t d
|
||||
}
|
||||
ssp1_set_mode_max5864();
|
||||
max5864_tx(&max5864);
|
||||
ssp1_set_mode_max2837();
|
||||
max2837_tx(&max2837);
|
||||
ssp1_set_mode_max283x();
|
||||
max283x_tx(&max283x);
|
||||
sgpio_configure(&sgpio_config, SGPIO_DIRECTION_TX);
|
||||
break;
|
||||
|
||||
@ -383,8 +420,8 @@ void rf_path_set_direction(rf_path_t* const rf_path, const rf_path_direction_t d
|
||||
}
|
||||
ssp1_set_mode_max5864();
|
||||
max5864_rx(&max5864);
|
||||
ssp1_set_mode_max2837();
|
||||
max2837_rx(&max2837);
|
||||
ssp1_set_mode_max283x();
|
||||
max283x_rx(&max283x);
|
||||
sgpio_configure(&sgpio_config, SGPIO_DIRECTION_RX);
|
||||
break;
|
||||
|
||||
@ -399,8 +436,8 @@ void rf_path_set_direction(rf_path_t* const rf_path, const rf_path_direction_t d
|
||||
mixer_disable(&mixer);
|
||||
ssp1_set_mode_max5864();
|
||||
max5864_standby(&max5864);
|
||||
ssp1_set_mode_max2837();
|
||||
max2837_set_mode(&max2837, MAX2837_MODE_STANDBY);
|
||||
ssp1_set_mode_max283x();
|
||||
max283x_set_mode(&max283x, MAX283x_MODE_STANDBY);
|
||||
sgpio_configure(&sgpio_config, SGPIO_DIRECTION_RX);
|
||||
break;
|
||||
}
|
||||
|
@ -25,13 +25,12 @@
|
||||
#include <libopencm3/lpc43xx/scu.h>
|
||||
#include <libopencm3/lpc43xx/sgpio.h>
|
||||
|
||||
#include <hackrf_core.h>
|
||||
#include "hackrf_core.h"
|
||||
#include "platform_detect.h"
|
||||
|
||||
#include <sgpio.h>
|
||||
#include "sgpio.h"
|
||||
|
||||
#ifdef RAD1O
|
||||
static void update_q_invert(sgpio_config_t* const config);
|
||||
#endif
|
||||
|
||||
void sgpio_configure_pin_functions(sgpio_config_t* const config)
|
||||
{
|
||||
@ -48,14 +47,23 @@ void sgpio_configure_pin_functions(sgpio_config_t* const config)
|
||||
scu_pinmux(SCU_PINMUX_SGPIO10, SCU_GPIO_FAST | SCU_CONF_FUNCTION6);
|
||||
scu_pinmux(SCU_PINMUX_SGPIO11, SCU_GPIO_FAST | SCU_CONF_FUNCTION6);
|
||||
scu_pinmux(SCU_PINMUX_SGPIO12, SCU_GPIO_FAST | SCU_CONF_FUNCTION0); /* GPIO0[13] */
|
||||
scu_pinmux(SCU_PINMUX_SGPIO13, SCU_GPIO_FAST | SCU_CONF_FUNCTION4); /* GPIO5[12] */
|
||||
scu_pinmux(SCU_PINMUX_SGPIO14, SCU_GPIO_FAST | SCU_CONF_FUNCTION4); /* GPIO5[13] */
|
||||
scu_pinmux(SCU_PINMUX_SGPIO15, SCU_GPIO_FAST | SCU_CONF_FUNCTION4); /* GPIO5[14] */
|
||||
|
||||
sgpio_cpld_stream_rx_set_q_invert(config, 0);
|
||||
if (detected_platform() == BOARD_ID_HACKRF1_R9) {
|
||||
scu_pinmux(
|
||||
SCU_H1R9_HW_SYNC_EN,
|
||||
SCU_GPIO_FAST | SCU_CONF_FUNCTION4); /* GPIO5[5] */
|
||||
} else {
|
||||
scu_pinmux(
|
||||
SCU_HW_SYNC_EN,
|
||||
SCU_GPIO_FAST | SCU_CONF_FUNCTION4); /* GPIO5[12] */
|
||||
}
|
||||
|
||||
sgpio_cpld_set_mixer_invert(config, 0);
|
||||
hw_sync_enable(0);
|
||||
|
||||
gpio_output(config->gpio_rx_q_invert);
|
||||
gpio_output(config->gpio_q_invert);
|
||||
gpio_output(config->gpio_hw_sync_enable);
|
||||
}
|
||||
|
||||
@ -112,11 +120,9 @@ void sgpio_configure(sgpio_config_t* const config, const sgpio_direction_t direc
|
||||
;
|
||||
// clang-format on
|
||||
|
||||
#ifdef RAD1O
|
||||
/* The data direction might have changed. Check if we need to
|
||||
* adjust the q inversion. */
|
||||
update_q_invert(config);
|
||||
#endif
|
||||
|
||||
// Enable SGPIO pin outputs.
|
||||
const uint_fast16_t sgpio_gpio_data_direction =
|
||||
@ -284,55 +290,52 @@ bool sgpio_cpld_stream_is_enabled(sgpio_config_t* const config)
|
||||
return (SGPIO_GPIO_OUTREG & (1L << 10)) == 0; /* SGPIO10 */
|
||||
}
|
||||
|
||||
#ifdef RAD1O
|
||||
/* The rad1o hardware has a bug which makes it
|
||||
* necessary to also switch between the two options based
|
||||
* on TX or RX mode.
|
||||
/*
|
||||
* The spectrum can be inverted by the analog section of the hardware in two
|
||||
* different ways:
|
||||
*
|
||||
* We use the state of the pin to determine which way we
|
||||
* have to go.
|
||||
* - The front-end mixer can introduce an inversion depending on the frequency
|
||||
* tuning configuration.
|
||||
*
|
||||
* As TX/RX can change without sgpio_cpld_stream_rx_set_q_invert
|
||||
* being called, we store a local copy of its parameter. */
|
||||
static bool sgpio_invert = false;
|
||||
* - Routing of the analog baseband signals can introduce an inversion
|
||||
* depending on the design of the hardware platform and whether we are in RX
|
||||
* or TX mode.
|
||||
*
|
||||
* When one but not both of the above effects inverts the spectrum, we instruct
|
||||
* the CPLD to correct the inversion by inverting the Q sample value.
|
||||
*/
|
||||
static bool mixer_invert = false;
|
||||
|
||||
/* Called when TX/RX changes od sgpio_cpld_stream_rx_set_q_invert
|
||||
* gets called. */
|
||||
/* Called when TX/RX changes or sgpio_cpld_set_mixer_invert() gets called. */
|
||||
static void update_q_invert(sgpio_config_t* const config)
|
||||
{
|
||||
/* 1=Output SGPIO11 High(TX mode), 0=Output SGPIO11 Low(RX mode) */
|
||||
bool tx_mode = (SGPIO_GPIO_OUTREG & (1 << 11)) > 0;
|
||||
|
||||
/* 0.13: P1_18 */
|
||||
if (!sgpio_invert & !tx_mode) {
|
||||
gpio_write(config->gpio_rx_q_invert, 1);
|
||||
} else if (!sgpio_invert & tx_mode) {
|
||||
gpio_write(config->gpio_rx_q_invert, 0);
|
||||
} else if (sgpio_invert & !tx_mode) {
|
||||
gpio_write(config->gpio_rx_q_invert, 0);
|
||||
} else if (sgpio_invert & tx_mode) {
|
||||
gpio_write(config->gpio_rx_q_invert, 1);
|
||||
/*
|
||||
* This switch will need to change if we modify the CPLD to handle
|
||||
* inversion the same way for RX and TX.
|
||||
*/
|
||||
bool baseband_invert = false;
|
||||
switch (detected_platform()) {
|
||||
case BOARD_ID_RAD1O:
|
||||
case BOARD_ID_HACKRF1_R9:
|
||||
baseband_invert = (tx_mode) ? false : true;
|
||||
break;
|
||||
default:
|
||||
baseband_invert = false;
|
||||
}
|
||||
|
||||
gpio_write(config->gpio_q_invert, mixer_invert ^ baseband_invert);
|
||||
}
|
||||
|
||||
void sgpio_cpld_stream_rx_set_q_invert(
|
||||
sgpio_config_t* const config,
|
||||
const uint_fast8_t invert)
|
||||
void sgpio_cpld_set_mixer_invert(sgpio_config_t* const config, const uint_fast8_t invert)
|
||||
{
|
||||
if (invert) {
|
||||
sgpio_invert = true;
|
||||
mixer_invert = true;
|
||||
} else {
|
||||
sgpio_invert = false;
|
||||
mixer_invert = false;
|
||||
}
|
||||
|
||||
update_q_invert(config);
|
||||
}
|
||||
|
||||
#else
|
||||
void sgpio_cpld_stream_rx_set_q_invert(
|
||||
sgpio_config_t* const config,
|
||||
const uint_fast8_t invert)
|
||||
{
|
||||
gpio_write(config->gpio_rx_q_invert, invert);
|
||||
}
|
||||
#endif
|
||||
|
@ -36,7 +36,7 @@ typedef enum {
|
||||
} sgpio_direction_t;
|
||||
|
||||
typedef struct sgpio_config_t {
|
||||
gpio_t gpio_rx_q_invert;
|
||||
gpio_t gpio_q_invert;
|
||||
gpio_t gpio_hw_sync_enable;
|
||||
bool slice_mode_multislice;
|
||||
} sgpio_config_t;
|
||||
@ -49,8 +49,6 @@ void sgpio_cpld_stream_enable(sgpio_config_t* const config);
|
||||
void sgpio_cpld_stream_disable(sgpio_config_t* const config);
|
||||
bool sgpio_cpld_stream_is_enabled(sgpio_config_t* const config);
|
||||
|
||||
void sgpio_cpld_stream_rx_set_q_invert(
|
||||
sgpio_config_t* const config,
|
||||
const uint_fast8_t invert);
|
||||
void sgpio_cpld_set_mixer_invert(sgpio_config_t* const config, uint_fast8_t invert);
|
||||
|
||||
#endif //__SGPIO_H__
|
||||
|
@ -21,6 +21,18 @@
|
||||
*/
|
||||
|
||||
#include "si5351c.h"
|
||||
#include "clkin.h"
|
||||
#include "platform_detect.h"
|
||||
#include "gpio_lpc.h"
|
||||
#include "hackrf_core.h"
|
||||
#include <libopencm3/lpc43xx/scu.h>
|
||||
|
||||
/* HackRF One r9 clock control */
|
||||
// clang-format off
|
||||
static struct gpio_t gpio_h1r9_clkin_en = GPIO(5, 15);
|
||||
static struct gpio_t gpio_h1r9_clkout_en = GPIO(0, 9);
|
||||
static struct gpio_t gpio_h1r9_mcu_clk_en = GPIO(0, 8);
|
||||
// clang-format on
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
@ -179,7 +191,7 @@ void si5351c_configure_clock_control(
|
||||
const enum pll_sources source)
|
||||
{
|
||||
uint8_t pll;
|
||||
uint8_t clk3_ctrl;
|
||||
uint8_t clkout_ctrl;
|
||||
|
||||
#ifdef RAD1O
|
||||
(void) source;
|
||||
@ -191,17 +203,28 @@ void si5351c_configure_clock_control(
|
||||
if (source == PLL_SOURCE_CLKIN) {
|
||||
/* PLLB on CLKIN */
|
||||
pll = SI5351C_CLK_PLL_SRC_B;
|
||||
if (detected_platform() == BOARD_ID_HACKRF1_R9) {
|
||||
/*
|
||||
* HackRF One r9 always uses PLL A on the XTAL input
|
||||
* but externally switches that input to CLKIN.
|
||||
*/
|
||||
pll = SI5351C_CLK_PLL_SRC_A;
|
||||
gpio_set(&gpio_h1r9_clkin_en);
|
||||
}
|
||||
} else {
|
||||
/* PLLA on XTAL */
|
||||
pll = SI5351C_CLK_PLL_SRC_A;
|
||||
if (detected_platform() == BOARD_ID_HACKRF1_R9) {
|
||||
gpio_clear(&gpio_h1r9_clkin_en);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (clkout_enabled) {
|
||||
clk3_ctrl = SI5351C_CLK_INT_MODE | SI5351C_CLK_PLL_SRC(pll) |
|
||||
clkout_ctrl = SI5351C_CLK_INT_MODE | SI5351C_CLK_PLL_SRC(pll) |
|
||||
SI5351C_CLK_SRC(SI5351C_CLK_SRC_MULTISYNTH_SELF) |
|
||||
SI5351C_CLK_IDRV(SI5351C_CLK_IDRV_8MA);
|
||||
} else {
|
||||
clk3_ctrl = SI5351C_CLK_POWERDOWN | SI5351C_CLK_INT_MODE;
|
||||
clkout_ctrl = SI5351C_CLK_POWERDOWN | SI5351C_CLK_INT_MODE;
|
||||
}
|
||||
|
||||
/* Clock to CPU is deactivated as it is not used and creates noise */
|
||||
@ -217,7 +240,7 @@ void si5351c_configure_clock_control(
|
||||
SI5351C_CLK_INT_MODE | SI5351C_CLK_PLL_SRC(pll) |
|
||||
SI5351C_CLK_SRC(SI5351C_CLK_SRC_MULTISYNTH_0_4) |
|
||||
SI5351C_CLK_IDRV(SI5351C_CLK_IDRV_2MA),
|
||||
clk3_ctrl,
|
||||
clkout_ctrl,
|
||||
SI5351C_CLK_INT_MODE | SI5351C_CLK_PLL_SRC(pll) |
|
||||
SI5351C_CLK_SRC(SI5351C_CLK_SRC_MULTISYNTH_SELF) |
|
||||
SI5351C_CLK_IDRV(SI5351C_CLK_IDRV_6MA) | SI5351C_CLK_INV,
|
||||
@ -225,18 +248,28 @@ void si5351c_configure_clock_control(
|
||||
SI5351C_CLK_SRC(SI5351C_CLK_SRC_MULTISYNTH_SELF) |
|
||||
SI5351C_CLK_IDRV(SI5351C_CLK_IDRV_4MA),
|
||||
SI5351C_CLK_POWERDOWN |
|
||||
SI5351C_CLK_INT_MODE /*not connected, but: plla int mode*/
|
||||
,
|
||||
SI5351C_CLK_INT_MODE, /* not connected, but: PLL A int mode */
|
||||
SI5351C_CLK_POWERDOWN |
|
||||
SI5351C_CLK_INT_MODE /*not connected, but: plla int mode*/
|
||||
SI5351C_CLK_INT_MODE /* not connected, but: PLL B int mode */
|
||||
};
|
||||
if (detected_platform() == BOARD_ID_HACKRF1_R9) {
|
||||
data[1] = SI5351C_CLK_INT_MODE | SI5351C_CLK_PLL_SRC_A |
|
||||
SI5351C_CLK_SRC(SI5351C_CLK_SRC_MULTISYNTH_SELF) |
|
||||
SI5351C_CLK_IDRV(SI5351C_CLK_IDRV_6MA);
|
||||
data[2] = SI5351C_CLK_FRAC_MODE | SI5351C_CLK_PLL_SRC_A |
|
||||
SI5351C_CLK_SRC(SI5351C_CLK_SRC_MULTISYNTH_SELF) |
|
||||
SI5351C_CLK_IDRV(SI5351C_CLK_IDRV_4MA);
|
||||
data[3] = clkout_ctrl;
|
||||
data[4] = SI5351C_CLK_POWERDOWN;
|
||||
data[5] = SI5351C_CLK_POWERDOWN;
|
||||
data[6] = SI5351C_CLK_POWERDOWN;
|
||||
}
|
||||
si5351c_write(drv, data, sizeof(data));
|
||||
}
|
||||
|
||||
#define SI5351C_CLK_ENABLE(x) (0 << x)
|
||||
#define SI5351C_CLK_DISABLE(x) (1 << x)
|
||||
#define SI5351C_REG_OUTPUT_EN (3)
|
||||
#define SI5351C_REG_CLK3_CTRL (19)
|
||||
|
||||
void si5351c_enable_clock_outputs(si5351c_driver_t* const drv)
|
||||
{
|
||||
@ -246,10 +279,27 @@ void si5351c_enable_clock_outputs(si5351c_driver_t* const drv)
|
||||
uint8_t value = SI5351C_CLK_ENABLE(0) | SI5351C_CLK_ENABLE(1) |
|
||||
SI5351C_CLK_ENABLE(2) | SI5351C_CLK_ENABLE(4) | SI5351C_CLK_ENABLE(5) |
|
||||
SI5351C_CLK_DISABLE(6) | SI5351C_CLK_DISABLE(7);
|
||||
uint8_t clkout = 3;
|
||||
|
||||
value |= (clkout_enabled) ? SI5351C_CLK_ENABLE(3) : SI5351C_CLK_DISABLE(3);
|
||||
/* HackRF One r9 has only three clock generator outputs. */
|
||||
if (detected_platform() == BOARD_ID_HACKRF1_R9) {
|
||||
clkout = 2;
|
||||
value = SI5351C_CLK_ENABLE(0) | SI5351C_CLK_ENABLE(1) |
|
||||
SI5351C_CLK_DISABLE(3) | SI5351C_CLK_DISABLE(4) |
|
||||
SI5351C_CLK_DISABLE(5) | SI5351C_CLK_DISABLE(6) |
|
||||
SI5351C_CLK_DISABLE(7);
|
||||
}
|
||||
|
||||
value |= (clkout_enabled) ? SI5351C_CLK_ENABLE(clkout) :
|
||||
SI5351C_CLK_DISABLE(clkout);
|
||||
uint8_t data[] = {SI5351C_REG_OUTPUT_EN, value};
|
||||
si5351c_write(drv, data, sizeof(data));
|
||||
|
||||
if ((clkout_enabled) && (detected_platform() == BOARD_ID_HACKRF1_R9)) {
|
||||
gpio_set(&gpio_h1r9_clkout_en);
|
||||
} else {
|
||||
gpio_clear(&gpio_h1r9_clkout_en);
|
||||
}
|
||||
}
|
||||
|
||||
void si5351c_set_int_mode(
|
||||
@ -275,24 +325,67 @@ void si5351c_set_int_mode(
|
||||
|
||||
void si5351c_set_clock_source(si5351c_driver_t* const drv, const enum pll_sources source)
|
||||
{
|
||||
if (source != active_clock_source) {
|
||||
si5351c_configure_clock_control(drv, source);
|
||||
active_clock_source = source;
|
||||
if (source == active_clock_source) {
|
||||
return;
|
||||
}
|
||||
si5351c_configure_clock_control(drv, source);
|
||||
active_clock_source = source;
|
||||
if (detected_platform() == BOARD_ID_HACKRF1_R9) {
|
||||
/* 25MHz XTAL * (0x0e00+512)/128 = 800mhz -> int mode */
|
||||
uint8_t pll_data[] = {26, 0x00, 0x01, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x00};
|
||||
if (source == PLL_SOURCE_CLKIN) {
|
||||
/* 10MHz CLKIN * (0x2600+512)/128 = 800mhz */
|
||||
pll_data[4] = 0x26;
|
||||
}
|
||||
si5351c_write(drv, pll_data, sizeof(pll_data));
|
||||
si5351c_reset_pll(drv);
|
||||
}
|
||||
}
|
||||
|
||||
bool si5351c_clkin_signal_valid(si5351c_driver_t* const drv)
|
||||
{
|
||||
return (si5351c_read_single(drv, 0) & SI5351C_LOS) == 0;
|
||||
if (detected_platform() == BOARD_ID_HACKRF1_R9) {
|
||||
uint32_t f = clkin_frequency();
|
||||
return (f > 9000000) && (f < 11000000);
|
||||
} else {
|
||||
return (si5351c_read_single(drv, 0) & SI5351C_LOS) == 0;
|
||||
}
|
||||
}
|
||||
|
||||
void si5351c_clkout_enable(si5351c_driver_t* const drv, uint8_t enable)
|
||||
{
|
||||
clkout_enabled = (enable > 0);
|
||||
|
||||
//FIXME this should be somewhere else
|
||||
uint8_t clkout = 3;
|
||||
/* HackRF One r9 has only three clock generator outputs. */
|
||||
if (detected_platform() == BOARD_ID_HACKRF1_R9) {
|
||||
clkout = 2;
|
||||
}
|
||||
/* Configure clock to 10MHz */
|
||||
si5351c_configure_multisynth(drv, 3, 80 * 128 - 512, 0, 1, 0);
|
||||
si5351c_configure_multisynth(drv, clkout, 80 * 128 - 512, 0, 1, 0);
|
||||
|
||||
si5351c_configure_clock_control(drv, active_clock_source);
|
||||
si5351c_enable_clock_outputs(drv);
|
||||
}
|
||||
|
||||
void si5351c_init(si5351c_driver_t* const drv)
|
||||
{
|
||||
if (detected_platform() == BOARD_ID_HACKRF1_R9) {
|
||||
/* CLKIN_EN */
|
||||
scu_pinmux(SCU_H1R9_CLKIN_EN, SCU_GPIO_FAST | SCU_CONF_FUNCTION4);
|
||||
gpio_clear(&gpio_h1r9_clkin_en);
|
||||
gpio_output(&gpio_h1r9_clkin_en);
|
||||
|
||||
/* CLKOUT_EN */
|
||||
scu_pinmux(SCU_H1R9_CLKOUT_EN, SCU_GPIO_FAST | SCU_CONF_FUNCTION0);
|
||||
gpio_clear(&gpio_h1r9_clkout_en);
|
||||
gpio_output(&gpio_h1r9_clkout_en);
|
||||
|
||||
/* MCU_CLK_EN */
|
||||
scu_pinmux(SCU_H1R9_MCU_CLK_EN, SCU_GPIO_FAST | SCU_CONF_FUNCTION0);
|
||||
gpio_clear(&gpio_h1r9_mcu_clk_en);
|
||||
gpio_output(&gpio_h1r9_mcu_clk_en);
|
||||
}
|
||||
(void) drv;
|
||||
}
|
||||
|
@ -102,6 +102,7 @@ void si5351c_write(
|
||||
const uint8_t* const data,
|
||||
const size_t data_count);
|
||||
void si5351c_clkout_enable(si5351c_driver_t* const drv, uint8_t enable);
|
||||
void si5351c_init(si5351c_driver_t* const drv);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
@ -22,14 +22,14 @@
|
||||
*/
|
||||
|
||||
#include "tuning.h"
|
||||
|
||||
#include "hackrf_ui.h"
|
||||
|
||||
#include <hackrf_core.h>
|
||||
#include <mixer.h>
|
||||
#include <max2837.h>
|
||||
#include <sgpio.h>
|
||||
#include <operacake.h>
|
||||
#include "hackrf_core.h"
|
||||
#include "mixer.h"
|
||||
#include "max2837.h"
|
||||
#include "max2839.h"
|
||||
#include "sgpio.h"
|
||||
#include "operacake.h"
|
||||
#include "platform_detect.h"
|
||||
|
||||
#define FREQ_ONE_MHZ (1000ULL * 1000)
|
||||
|
||||
@ -70,8 +70,8 @@ bool set_freq(const uint64_t freq)
|
||||
|
||||
success = true;
|
||||
|
||||
const max2837_mode_t prior_max2837_mode = max2837_mode(&max2837);
|
||||
max2837_set_mode(&max2837, MAX2837_MODE_STANDBY);
|
||||
max283x_mode_t prior_max283x_mode = max283x_mode(&max283x);
|
||||
max283x_set_mode(&max283x, MAX283x_MODE_STANDBY);
|
||||
if (freq_mhz < MAX_LP_FREQ_MHZ) {
|
||||
rf_path_set_filter(&rf_path, RF_PATH_FILTER_LOW_PASS);
|
||||
#ifdef RAD1O
|
||||
@ -83,14 +83,14 @@ bool set_freq(const uint64_t freq)
|
||||
mixer_freq_mhz = (max2837_freq_nominal_hz / FREQ_ONE_MHZ) + freq_mhz;
|
||||
/* Set Freq and read real freq */
|
||||
real_mixer_freq_hz = mixer_set_frequency(&mixer, mixer_freq_mhz);
|
||||
max2837_set_frequency(&max2837, real_mixer_freq_hz - freq);
|
||||
sgpio_cpld_stream_rx_set_q_invert(&sgpio_config, 1);
|
||||
max283x_set_frequency(&max283x, real_mixer_freq_hz - freq);
|
||||
sgpio_cpld_set_mixer_invert(&sgpio_config, 1);
|
||||
} else if ((freq_mhz >= MIN_BYPASS_FREQ_MHZ) && (freq_mhz < MAX_BYPASS_FREQ_MHZ)) {
|
||||
rf_path_set_filter(&rf_path, RF_PATH_FILTER_BYPASS);
|
||||
MAX2837_freq_hz = (freq_mhz * FREQ_ONE_MHZ) + freq_hz;
|
||||
/* mixer_freq_mhz <= not used in Bypass mode */
|
||||
max2837_set_frequency(&max2837, MAX2837_freq_hz);
|
||||
sgpio_cpld_stream_rx_set_q_invert(&sgpio_config, 0);
|
||||
max283x_set_frequency(&max283x, MAX2837_freq_hz);
|
||||
sgpio_cpld_set_mixer_invert(&sgpio_config, 0);
|
||||
} else if ((freq_mhz >= MIN_HP_FREQ_MHZ) && (freq_mhz <= MAX_HP_FREQ_MHZ)) {
|
||||
if (freq_mhz < MID1_HP_FREQ_MHZ) {
|
||||
/* IF is graduated from 2170 MHz to 2740 MHz */
|
||||
@ -110,13 +110,13 @@ bool set_freq(const uint64_t freq)
|
||||
mixer_freq_mhz = freq_mhz - (max2837_freq_nominal_hz / FREQ_ONE_MHZ);
|
||||
/* Set Freq and read real freq */
|
||||
real_mixer_freq_hz = mixer_set_frequency(&mixer, mixer_freq_mhz);
|
||||
max2837_set_frequency(&max2837, freq - real_mixer_freq_hz);
|
||||
sgpio_cpld_stream_rx_set_q_invert(&sgpio_config, 0);
|
||||
max283x_set_frequency(&max283x, freq - real_mixer_freq_hz);
|
||||
sgpio_cpld_set_mixer_invert(&sgpio_config, 0);
|
||||
} else {
|
||||
/* Error freq_mhz too high */
|
||||
success = false;
|
||||
}
|
||||
max2837_set_mode(&max2837, prior_max2837_mode);
|
||||
max283x_set_mode(&max283x, prior_max283x_mode);
|
||||
if (success) {
|
||||
freq_cache = freq;
|
||||
hackrf_ui()->set_frequency(freq);
|
||||
@ -147,11 +147,11 @@ bool set_freq_explicit(
|
||||
}
|
||||
|
||||
rf_path_set_filter(&rf_path, path);
|
||||
max2837_set_frequency(&max2837, if_freq_hz);
|
||||
max283x_set_frequency(&max283x, if_freq_hz);
|
||||
if (lo_freq_hz > if_freq_hz) {
|
||||
sgpio_cpld_stream_rx_set_q_invert(&sgpio_config, 1);
|
||||
sgpio_cpld_set_mixer_invert(&sgpio_config, 1);
|
||||
} else {
|
||||
sgpio_cpld_stream_rx_set_q_invert(&sgpio_config, 0);
|
||||
sgpio_cpld_set_mixer_invert(&sgpio_config, 0);
|
||||
}
|
||||
if (path != RF_PATH_FILTER_BYPASS) {
|
||||
(void) mixer_set_frequency(&mixer, lo_freq_hz / FREQ_ONE_MHZ);
|
||||
|
@ -202,7 +202,7 @@ static void ui_update(void)
|
||||
rad1o_lcdDisplay();
|
||||
|
||||
// Don't ask...
|
||||
ssp1_set_mode_max2837();
|
||||
ssp1_set_mode_max283x();
|
||||
}
|
||||
|
||||
static void rad1o_ui_init(void)
|
||||
@ -217,7 +217,7 @@ static void rad1o_ui_deinit(void)
|
||||
rad1o_lcdDeInit();
|
||||
enabled = false;
|
||||
// Don't ask...
|
||||
ssp1_set_mode_max2837();
|
||||
ssp1_set_mode_max283x();
|
||||
}
|
||||
|
||||
static void rad1o_ui_set_frequency(uint64_t frequency)
|
||||
|
@ -170,8 +170,11 @@ macro(DeclareTargets)
|
||||
${PATH_HACKRF_FIRMWARE_COMMON}/sgpio.c
|
||||
${PATH_HACKRF_FIRMWARE_COMMON}/rf_path.c
|
||||
${PATH_HACKRF_FIRMWARE_COMMON}/si5351c.c
|
||||
${PATH_HACKRF_FIRMWARE_COMMON}/max283x.c
|
||||
${PATH_HACKRF_FIRMWARE_COMMON}/max2837.c
|
||||
${PATH_HACKRF_FIRMWARE_COMMON}/max2837_target.c
|
||||
${PATH_HACKRF_FIRMWARE_COMMON}/max2839.c
|
||||
${PATH_HACKRF_FIRMWARE_COMMON}/max2839_target.c
|
||||
${PATH_HACKRF_FIRMWARE_COMMON}/max5864.c
|
||||
${PATH_HACKRF_FIRMWARE_COMMON}/max5864_target.c
|
||||
${PATH_HACKRF_FIRMWARE_COMMON}/mixer.c
|
||||
@ -185,6 +188,8 @@ macro(DeclareTargets)
|
||||
${PATH_HACKRF_FIRMWARE_COMMON}/hackrf_ui.c
|
||||
${PATH_HACKRF_FIRMWARE_COMMON}/platform_detect.c
|
||||
${PATH_HACKRF_FIRMWARE_COMMON}/firmware_info.c
|
||||
${PATH_HACKRF_FIRMWARE_COMMON}/clkin.c
|
||||
${PATH_HACKRF_FIRMWARE_COMMON}/gpdma.c
|
||||
)
|
||||
|
||||
if(BOARD STREQUAL "RAD1O")
|
||||
|
@ -27,6 +27,7 @@
|
||||
#include <libopencm3/lpc43xx/ipc.h>
|
||||
#include <libopencm3/lpc43xx/m4/nvic.h>
|
||||
#include <libopencm3/lpc43xx/rgu.h>
|
||||
#include <libopencm3/lpc43xx/timer.h>
|
||||
|
||||
#include <streaming.h>
|
||||
|
||||
@ -55,6 +56,7 @@
|
||||
#include "portapack.h"
|
||||
#include "hackrf_ui.h"
|
||||
#include "platform_detect.h"
|
||||
#include "clkin.h"
|
||||
|
||||
extern uint32_t __m0_start__;
|
||||
extern uint32_t __m0_end__;
|
||||
@ -64,8 +66,8 @@ extern uint32_t _etext_ram, _text_ram, _etext_rom;
|
||||
static usb_request_handler_fn vendor_request_handler[] = {
|
||||
NULL,
|
||||
usb_vendor_request_set_transceiver_mode,
|
||||
usb_vendor_request_write_max2837,
|
||||
usb_vendor_request_read_max2837,
|
||||
usb_vendor_request_write_max283x,
|
||||
usb_vendor_request_read_max283x,
|
||||
usb_vendor_request_write_si5351c,
|
||||
usb_vendor_request_read_si5351c,
|
||||
usb_vendor_request_set_sample_rate_frac,
|
||||
@ -286,6 +288,8 @@ int main(void)
|
||||
}
|
||||
operacake_init(operacake_allow_gpio);
|
||||
|
||||
clkin_detect_init();
|
||||
|
||||
while (true) {
|
||||
transceiver_request_t request;
|
||||
|
||||
|
@ -33,6 +33,13 @@
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef HACKRF_ONE
|
||||
#include "gpio_lpc.h"
|
||||
static struct gpio_t gpio_h1r9_clkout_en = GPIO(0, 9);
|
||||
static struct gpio_t gpio_h1r9_mcu_clk_en = GPIO(0, 8);
|
||||
static struct gpio_t gpio_h1r9_rx = GPIO(0, 7);
|
||||
#endif
|
||||
|
||||
usb_request_status_t usb_vendor_request_read_board_id(
|
||||
usb_endpoint_t* const endpoint,
|
||||
const usb_transfer_stage_t stage)
|
||||
@ -123,7 +130,19 @@ usb_request_status_t usb_vendor_request_reset(
|
||||
const usb_transfer_stage_t stage)
|
||||
{
|
||||
if (stage == USB_TRANSFER_STAGE_SETUP) {
|
||||
#ifdef HACKRF_ONE
|
||||
/*
|
||||
* Set boot pins as inputs so that the bootloader reads them
|
||||
* correctly after the reset.
|
||||
*/
|
||||
if (detected_platform() == BOARD_ID_HACKRF1_R9) {
|
||||
gpio_input(&gpio_h1r9_mcu_clk_en);
|
||||
gpio_input(&gpio_h1r9_clkout_en);
|
||||
gpio_input(&gpio_h1r9_rx);
|
||||
}
|
||||
#endif
|
||||
wwdt_reset(100000);
|
||||
|
||||
usb_transfer_schedule_ack(endpoint->in);
|
||||
}
|
||||
return USB_REQUEST_STATUS_OK;
|
||||
|
@ -25,7 +25,7 @@
|
||||
|
||||
#include <hackrf_core.h>
|
||||
#include <usb_queue.h>
|
||||
#include <max2837.h>
|
||||
#include <max283x.h>
|
||||
#include <rffc5071.h>
|
||||
|
||||
#include <stddef.h>
|
||||
@ -33,15 +33,15 @@
|
||||
|
||||
#include <hackrf_core.h>
|
||||
|
||||
usb_request_status_t usb_vendor_request_write_max2837(
|
||||
usb_request_status_t usb_vendor_request_write_max283x(
|
||||
usb_endpoint_t* const endpoint,
|
||||
const usb_transfer_stage_t stage)
|
||||
{
|
||||
if (stage == USB_TRANSFER_STAGE_SETUP) {
|
||||
if (endpoint->setup.index < MAX2837_NUM_REGS) {
|
||||
if (endpoint->setup.value < MAX2837_DATA_REGS_MAX_VALUE) {
|
||||
max2837_reg_write(
|
||||
&max2837,
|
||||
max283x_reg_write(
|
||||
&max283x,
|
||||
endpoint->setup.index,
|
||||
endpoint->setup.value);
|
||||
usb_transfer_schedule_ack(endpoint->in);
|
||||
@ -54,14 +54,14 @@ usb_request_status_t usb_vendor_request_write_max2837(
|
||||
}
|
||||
}
|
||||
|
||||
usb_request_status_t usb_vendor_request_read_max2837(
|
||||
usb_request_status_t usb_vendor_request_read_max283x(
|
||||
usb_endpoint_t* const endpoint,
|
||||
const usb_transfer_stage_t stage)
|
||||
{
|
||||
if (stage == USB_TRANSFER_STAGE_SETUP) {
|
||||
if (endpoint->setup.index < MAX2837_NUM_REGS) {
|
||||
const uint16_t value =
|
||||
max2837_reg_read(&max2837, endpoint->setup.index);
|
||||
max283x_reg_read(&max283x, endpoint->setup.index);
|
||||
endpoint->buffer[0] = value & 0xff;
|
||||
endpoint->buffer[1] = value >> 8;
|
||||
usb_transfer_schedule_block(
|
||||
|
@ -27,10 +27,10 @@
|
||||
#include <usb_type.h>
|
||||
#include <usb_request.h>
|
||||
|
||||
usb_request_status_t usb_vendor_request_write_max2837(
|
||||
usb_request_status_t usb_vendor_request_write_max283x(
|
||||
usb_endpoint_t* const endpoint,
|
||||
const usb_transfer_stage_t stage);
|
||||
usb_request_status_t usb_vendor_request_read_max2837(
|
||||
usb_request_status_t usb_vendor_request_read_max283x(
|
||||
usb_endpoint_t* const endpoint,
|
||||
const usb_transfer_stage_t stage);
|
||||
usb_request_status_t usb_vendor_request_write_si5351c(
|
||||
|
@ -32,12 +32,14 @@
|
||||
|
||||
#include "usb_api_cpld.h" // Remove when CPLD update is handled elsewhere
|
||||
|
||||
#include <max2837.h>
|
||||
#include <rf_path.h>
|
||||
#include <tuning.h>
|
||||
#include <streaming.h>
|
||||
#include <usb.h>
|
||||
#include <usb_queue.h>
|
||||
#include "max2837.h"
|
||||
#include "max2839.h"
|
||||
#include "rf_path.h"
|
||||
#include "tuning.h"
|
||||
#include "streaming.h"
|
||||
#include "usb.h"
|
||||
#include "usb_queue.h"
|
||||
#include "platform_detect.h"
|
||||
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
@ -163,8 +165,8 @@ usb_request_status_t usb_vendor_request_set_lna_gain(
|
||||
const usb_transfer_stage_t stage)
|
||||
{
|
||||
if (stage == USB_TRANSFER_STAGE_SETUP) {
|
||||
const uint8_t value =
|
||||
max2837_set_lna_gain(&max2837, endpoint->setup.index);
|
||||
uint8_t value;
|
||||
value = max283x_set_lna_gain(&max283x, endpoint->setup.index);
|
||||
endpoint->buffer[0] = value;
|
||||
if (value) {
|
||||
hackrf_ui()->set_bb_lna_gain(endpoint->setup.index);
|
||||
@ -186,8 +188,8 @@ usb_request_status_t usb_vendor_request_set_vga_gain(
|
||||
const usb_transfer_stage_t stage)
|
||||
{
|
||||
if (stage == USB_TRANSFER_STAGE_SETUP) {
|
||||
const uint8_t value =
|
||||
max2837_set_vga_gain(&max2837, endpoint->setup.index);
|
||||
uint8_t value;
|
||||
value = max283x_set_vga_gain(&max283x, endpoint->setup.index);
|
||||
endpoint->buffer[0] = value;
|
||||
if (value) {
|
||||
hackrf_ui()->set_bb_vga_gain(endpoint->setup.index);
|
||||
@ -209,8 +211,8 @@ usb_request_status_t usb_vendor_request_set_txvga_gain(
|
||||
const usb_transfer_stage_t stage)
|
||||
{
|
||||
if (stage == USB_TRANSFER_STAGE_SETUP) {
|
||||
const uint8_t value =
|
||||
max2837_set_txvga_gain(&max2837, endpoint->setup.index);
|
||||
uint8_t value;
|
||||
value = max283x_set_txvga_gain(&max283x, endpoint->setup.index);
|
||||
endpoint->buffer[0] = value;
|
||||
if (value) {
|
||||
hackrf_ui()->set_bb_tx_vga_gain(endpoint->setup.index);
|
||||
|
Reference in New Issue
Block a user