Merge pull request #1255 from miek/h1r9-fw

Add firmware support for h1r9
This commit is contained in:
Michael Ossmann
2023-01-10 07:48:24 -05:00
committed by GitHub
26 changed files with 1900 additions and 207 deletions

112
firmware/common/clkin.c Normal file
View 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
View 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__

View File

@ -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

View File

@ -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)

View File

@ -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);

View File

@ -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
View 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
View 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

View 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

View 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;
}

View 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
View 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
View 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

View File

@ -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;
}

View File

@ -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

View File

@ -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__

View File

@ -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;
}

View File

@ -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
}

View File

@ -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);

View File

@ -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)

View File

@ -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")

View File

@ -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;

View File

@ -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;

View File

@ -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(

View File

@ -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(

View File

@ -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);