Add software
This commit is contained in:
		@@ -0,0 +1,39 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
 | 
			
		||||
 *
 | 
			
		||||
 * This file is part of PortaPack.
 | 
			
		||||
 *
 | 
			
		||||
 * 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 "debounce.hpp"
 | 
			
		||||
 | 
			
		||||
#include "utility.hpp"
 | 
			
		||||
 | 
			
		||||
bool Debounce::feed(const uint8_t bit) {
 | 
			
		||||
	history_ = (history_ << 1) | (bit & 1);
 | 
			
		||||
 | 
			
		||||
	if( history_ == 0b00001111 ) {
 | 
			
		||||
		state_ = 1;
 | 
			
		||||
		return true;
 | 
			
		||||
	}
 | 
			
		||||
	if( history_ == 0b11110000 ) {
 | 
			
		||||
		state_ = 0;
 | 
			
		||||
		return true;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return false;
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,40 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
 | 
			
		||||
 *
 | 
			
		||||
 * This file is part of PortaPack.
 | 
			
		||||
 *
 | 
			
		||||
 * 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 __DEBOUNCE_H__
 | 
			
		||||
#define __DEBOUNCE_H__
 | 
			
		||||
 | 
			
		||||
#include <cstdint>
 | 
			
		||||
 | 
			
		||||
class Debounce {
 | 
			
		||||
public:
 | 
			
		||||
	bool feed(const uint8_t bit);
 | 
			
		||||
	
 | 
			
		||||
	uint8_t state() const {
 | 
			
		||||
		return state_;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	uint8_t history_ { 0 };
 | 
			
		||||
	uint8_t state_ { 0 };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#endif/*__DEBOUNCE_H__*/
 | 
			
		||||
@@ -0,0 +1,55 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
 | 
			
		||||
 *
 | 
			
		||||
 * This file is part of PortaPack.
 | 
			
		||||
 *
 | 
			
		||||
 * 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 "encoder.hpp"
 | 
			
		||||
 | 
			
		||||
#include "utility.hpp"
 | 
			
		||||
 | 
			
		||||
static const int8_t transition_map[] = {
 | 
			
		||||
	 0,	// 0000: noop
 | 
			
		||||
	 0,	// 0001: start
 | 
			
		||||
	 0,	// 0010: start
 | 
			
		||||
	 0,	// 0011: rate
 | 
			
		||||
	 1,	// 0100: end
 | 
			
		||||
	 0,	// 0101: noop
 | 
			
		||||
	 0,	// 0110: rate
 | 
			
		||||
	-1,	// 0111: end
 | 
			
		||||
	-1,	// 1000: end
 | 
			
		||||
	 0,	// 1001: rate
 | 
			
		||||
	 0,	// 1010: noop
 | 
			
		||||
	 1,	// 1011: end
 | 
			
		||||
	 0,	// 1100: rate
 | 
			
		||||
	 0,	// 1101: start
 | 
			
		||||
	 0,	// 1110: start
 | 
			
		||||
	 0,	// 1111: noop
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
int_fast8_t Encoder::update(
 | 
			
		||||
	const uint_fast8_t phase_0,
 | 
			
		||||
	const uint_fast8_t phase_1
 | 
			
		||||
) {
 | 
			
		||||
	state <<= 1;
 | 
			
		||||
	state |= phase_0;
 | 
			
		||||
	state <<= 1;
 | 
			
		||||
	state |= phase_1;
 | 
			
		||||
 | 
			
		||||
	return transition_map[state & 0xf];
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,38 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
 | 
			
		||||
 *
 | 
			
		||||
 * This file is part of PortaPack.
 | 
			
		||||
 *
 | 
			
		||||
 * 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 __ENCODER_H__
 | 
			
		||||
#define __ENCODER_H__
 | 
			
		||||
 | 
			
		||||
#include <cstdint>
 | 
			
		||||
 | 
			
		||||
class Encoder {
 | 
			
		||||
public:
 | 
			
		||||
	int_fast8_t update(
 | 
			
		||||
		const uint_fast8_t phase_0,
 | 
			
		||||
		const uint_fast8_t phase_1
 | 
			
		||||
	);
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	uint_fast8_t state { 0 };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#endif/*__ENCODER_H__*/
 | 
			
		||||
							
								
								
									
										294
									
								
								Software/portapack-mayhem/firmware/application/hw/max2837.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										294
									
								
								Software/portapack-mayhem/firmware/application/hw/max2837.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,294 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
 | 
			
		||||
 *
 | 
			
		||||
 * This file is part of PortaPack.
 | 
			
		||||
 *
 | 
			
		||||
 * 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 "max2837.hpp"
 | 
			
		||||
 | 
			
		||||
#include "hackrf_hal.hpp"
 | 
			
		||||
#include "hackrf_gpio.hpp"
 | 
			
		||||
using namespace hackrf::one;
 | 
			
		||||
 | 
			
		||||
#include "ch.h"
 | 
			
		||||
#include "hal.h"
 | 
			
		||||
 | 
			
		||||
#include <algorithm>
 | 
			
		||||
 | 
			
		||||
namespace max2837 {
 | 
			
		||||
 | 
			
		||||
namespace lna {
 | 
			
		||||
 | 
			
		||||
constexpr std::array<uint8_t, 8> lookup_8db_steps {
 | 
			
		||||
	0b111, 0b011, 0b110, 0b010,
 | 
			
		||||
	0b100, 0b000, 0b000, 0b000
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static uint_fast8_t gain_ordinal(const int8_t db) {
 | 
			
		||||
	const auto db_sat = gain_db_range.clip(db);
 | 
			
		||||
	return lna::lookup_8db_steps[(db_sat >> 3) & 7];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} /* namespace lna */
 | 
			
		||||
 | 
			
		||||
namespace vga {
 | 
			
		||||
 | 
			
		||||
static uint_fast8_t gain_ordinal(const int8_t db) {
 | 
			
		||||
	const auto db_sat = gain_db_range.clip(db);
 | 
			
		||||
	return ((db_sat >> 1) & 0b11111) ^ 0b11111;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} /* namespace vga */
 | 
			
		||||
 | 
			
		||||
namespace tx {
 | 
			
		||||
 | 
			
		||||
static uint_fast8_t gain_ordinal(const int8_t db) {
 | 
			
		||||
	const auto db_sat = gain_db_range.clip(db);
 | 
			
		||||
	uint8_t value = db_sat & 0x0f;
 | 
			
		||||
	value = (db_sat >= 16) ? (value | 0x20) : value;
 | 
			
		||||
	value = (db_sat >= 32) ? (value | 0x10) : value;
 | 
			
		||||
	return (value & 0b111111) ^ 0b111111;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} /* namespace tx */
 | 
			
		||||
 | 
			
		||||
namespace filter {
 | 
			
		||||
 | 
			
		||||
static uint_fast8_t bandwidth_ordinal(const uint32_t bandwidth) {
 | 
			
		||||
	/* Determine filter setting that will provide bandwidth greater than or
 | 
			
		||||
	 * equal to requested bandwidth.
 | 
			
		||||
	 */
 | 
			
		||||
	return std::lower_bound(bandwidths.cbegin(), bandwidths.cend(), bandwidth) - bandwidths.cbegin();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} /* namespace filter */
 | 
			
		||||
 | 
			
		||||
/* Empirical testing indicates about 25us is necessary to get a valid
 | 
			
		||||
 * temperature sense conversion from the ADC.
 | 
			
		||||
 */
 | 
			
		||||
constexpr float seconds_for_temperature_sense_adc_conversion = 30.0e-6;
 | 
			
		||||
constexpr halrtcnt_t ticks_for_temperature_sense_adc_conversion = (base_m4_clk_f * seconds_for_temperature_sense_adc_conversion + 1);
 | 
			
		||||
 | 
			
		||||
constexpr uint32_t reference_frequency = max2837_reference_f;
 | 
			
		||||
constexpr uint32_t pll_factor = 1.0 / (4.0 / 3.0 / reference_frequency) + 0.5;
 | 
			
		||||
 | 
			
		||||
void MAX2837::init() {
 | 
			
		||||
	set_mode(Mode::Shutdown);
 | 
			
		||||
 | 
			
		||||
	gpio_max2837_enable.output();
 | 
			
		||||
	gpio_max2837_rxenable.output();
 | 
			
		||||
	gpio_max2837_txenable.output();
 | 
			
		||||
 | 
			
		||||
	_map.r.tx_gain.TXVGA_GAIN_SPI_EN = 1;
 | 
			
		||||
	_map.r.tx_gain.TXVGA_GAIN_MSB_SPI_EN = 1;
 | 
			
		||||
	_map.r.tx_gain.TXVGA_GAIN_SPI = 0x00;
 | 
			
		||||
 | 
			
		||||
	_map.r.lpf_3_vga_1.VGAMUX_enable = 1;
 | 
			
		||||
	_map.r.lpf_3_vga_1.VGA_EN = 1;
 | 
			
		||||
 | 
			
		||||
	_map.r.hpfsm_3.HPC_STOP = 1;	/* 1kHz */
 | 
			
		||||
 | 
			
		||||
	_map.r.rx_top_rx_bias.LNAgain_SPI_EN = 1;	/* control LNA gain from SPI */
 | 
			
		||||
	_map.r.rxrf_2.L = 0b000;
 | 
			
		||||
 | 
			
		||||
	_map.r.rx_top_rx_bias.VGAgain_SPI_EN = 1;	/* control VGA gain from SPI */
 | 
			
		||||
	_map.r.vga_2.VGA = 0b01010;
 | 
			
		||||
 | 
			
		||||
	_map.r.lpf_3_vga_1.BUFF_VCM = 0b00;		/* TODO: Check values out of ADC */
 | 
			
		||||
 | 
			
		||||
	_map.r.lpf_1.LPF_EN = 1;				/* Enable low-pass filter */
 | 
			
		||||
	_map.r.lpf_1.ModeCtrl = 0b01;			/* Rx LPF */
 | 
			
		||||
	_map.r.lpf_1.FT = 0b0000;				/* 5MHz LPF */
 | 
			
		||||
 | 
			
		||||
	_map.r.spi_en.EN_SPI = 1;				/* enable chip functions when ENABLE pin set */
 | 
			
		||||
 | 
			
		||||
	_map.r.lo_gen.LOGEN_2GM = 0;
 | 
			
		||||
 | 
			
		||||
#if 0
 | 
			
		||||
	_map.r.rxrf_1.LNA_EN = 1;
 | 
			
		||||
	_map.r.rxrf_1.Mixer_EN = 1;
 | 
			
		||||
	_map.r.rxrf_1.RxLO_EN = 1;
 | 
			
		||||
 | 
			
		||||
	_map.r.rx_top.DOUT_DRVH = 0;			/* slow down DOUT edges */
 | 
			
		||||
 | 
			
		||||
	_map.r.hpfsm_4.DOUT_CSB_SEL	= 0;		/* DOUT not tri-stated, is independent of CSB */
 | 
			
		||||
 | 
			
		||||
	_map.r.xtal_cfg.XTAL_CLKOUT_EN = 0;		/* CLKOUT pin disabled. (Seems to have no effect.) */
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	_map.r.vga_3_rx_top.RSSI_EN_SPIenables = 1;
 | 
			
		||||
	_map.r.vga_3_rx_top.RSSI_MODE = 1;		/* RSSI independent of RXHP */
 | 
			
		||||
 | 
			
		||||
	_dirty.set();
 | 
			
		||||
	flush();
 | 
			
		||||
 | 
			
		||||
	set_mode(Mode::Standby);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MAX2837::set_mode(const Mode mode) {
 | 
			
		||||
	gpio_max2837_enable.write(toUType(mode) & toUType(Mode::Mask_Enable));
 | 
			
		||||
	gpio_max2837_rxenable.write(toUType(mode) & toUType(Mode::Mask_RxEnable));
 | 
			
		||||
	gpio_max2837_txenable.write(toUType(mode) & toUType(Mode::Mask_TxEnable));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MAX2837::flush() {
 | 
			
		||||
	if( _dirty ) {
 | 
			
		||||
		for(size_t n=0; n<reg_count; n++) {
 | 
			
		||||
			if( _dirty[n] ) {
 | 
			
		||||
				write(n, _map.w[n]);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		_dirty.clear();
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MAX2837::flush_one(const Register reg) {
 | 
			
		||||
	const auto reg_num = toUType(reg);
 | 
			
		||||
	write(reg_num, _map.w[reg_num]);
 | 
			
		||||
	_dirty.clear(reg_num);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MAX2837::write(const address_t reg_num, const reg_t value) {
 | 
			
		||||
	uint16_t t = (0U << 15) | (reg_num << 10) | (value & 0x3ffU);
 | 
			
		||||
	_target.transfer(&t, 1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
reg_t MAX2837::read(const address_t reg_num) {
 | 
			
		||||
	uint16_t t = (1U << 15) | (reg_num << 10);
 | 
			
		||||
	_target.transfer(&t, 1U);
 | 
			
		||||
	return t & 0x3ffU;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MAX2837::write(const Register reg, const reg_t value) {
 | 
			
		||||
	write(toUType(reg), value);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
reg_t MAX2837::read(const Register reg) {
 | 
			
		||||
	return read(toUType(reg));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MAX2837::set_tx_vga_gain(const int_fast8_t db) {
 | 
			
		||||
	_map.r.tx_gain.TXVGA_GAIN_SPI = tx::gain_ordinal(db);
 | 
			
		||||
	_dirty[Register::TX_GAIN] = 1;
 | 
			
		||||
	flush();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MAX2837::set_lna_gain(const int_fast8_t db) {
 | 
			
		||||
	_map.r.rxrf_2.L = lna::gain_ordinal(db);
 | 
			
		||||
	_dirty[Register::RXRF_2] = 1;
 | 
			
		||||
	flush();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MAX2837::set_vga_gain(const int_fast8_t db) {
 | 
			
		||||
	_map.r.vga_2.VGA = vga::gain_ordinal(db);
 | 
			
		||||
	_dirty[Register::VGA_2] = 1;
 | 
			
		||||
	flush();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MAX2837::set_lpf_rf_bandwidth(const uint32_t bandwidth_minimum) {
 | 
			
		||||
	_map.r.lpf_1.FT = filter::bandwidth_ordinal(bandwidth_minimum);
 | 
			
		||||
	_dirty[Register::LPF_1] = 1;
 | 
			
		||||
	flush();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool MAX2837::set_frequency(const rf::Frequency lo_frequency) {
 | 
			
		||||
	/* TODO: This is a sad implementation. Refactor. */
 | 
			
		||||
	if( lo::band[0].contains(lo_frequency) ) {
 | 
			
		||||
		_map.r.syn_int_div.LOGEN_BSW = 0b00;	/* 2300 - 2399.99MHz */
 | 
			
		||||
		_map.r.rxrf_1.LNAband = 0;				/* 2.3 - 2.5GHz */
 | 
			
		||||
	} else if( lo::band[1].contains(lo_frequency)  ) {
 | 
			
		||||
		_map.r.syn_int_div.LOGEN_BSW = 0b01;	/* 2400 - 2499.99MHz */
 | 
			
		||||
		_map.r.rxrf_1.LNAband = 0;				/* 2.3 - 2.5GHz */
 | 
			
		||||
	} else if( lo::band[2].contains(lo_frequency) ) {
 | 
			
		||||
		_map.r.syn_int_div.LOGEN_BSW = 0b10;	/* 2500 - 2599.99MHz */
 | 
			
		||||
		_map.r.rxrf_1.LNAband = 1;				/* 2.5 - 2.7GHz */
 | 
			
		||||
	} else if( lo::band[3].contains(lo_frequency) ) {
 | 
			
		||||
		_map.r.syn_int_div.LOGEN_BSW = 0b11;	/* 2600 - 2700Hz */
 | 
			
		||||
		_map.r.rxrf_1.LNAband = 1;				/* 2.5 - 2.7GHz */
 | 
			
		||||
	} else {
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
	_dirty[Register::SYN_INT_DIV] = 1;
 | 
			
		||||
	_dirty[Register::RXRF_1] = 1;
 | 
			
		||||
 | 
			
		||||
	const uint64_t div_q20 = (lo_frequency * (1 << 20)) / pll_factor;
 | 
			
		||||
 | 
			
		||||
	_map.r.syn_int_div.SYN_INTDIV = div_q20 >> 20;
 | 
			
		||||
	_dirty[Register::SYN_INT_DIV] = 1;
 | 
			
		||||
	_map.r.syn_fr_div_2.SYN_FRDIV_19_10 = (div_q20 >> 10) & 0x3ff;
 | 
			
		||||
	_dirty[Register::SYN_FR_DIV_2] = 1;
 | 
			
		||||
	/* flush to commit high FRDIV first, as low FRDIV commits the change */
 | 
			
		||||
	flush();
 | 
			
		||||
 | 
			
		||||
	_map.r.syn_fr_div_1.SYN_FRDIV_9_0 = (div_q20 & 0x3ff);
 | 
			
		||||
	_dirty[Register::SYN_FR_DIV_1] = 1;
 | 
			
		||||
	flush();
 | 
			
		||||
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MAX2837::set_rx_lo_iq_calibration(const size_t v) {
 | 
			
		||||
	_map.r.rx_top_rx_bias.RX_IQERR_SPI_EN = 1;
 | 
			
		||||
	_dirty[Register::RX_TOP_RX_BIAS] = 1;
 | 
			
		||||
	_map.r.rxrf_2.iqerr_trim = v;
 | 
			
		||||
	_dirty[Register::RXRF_2] = 1;
 | 
			
		||||
	flush();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MAX2837::set_rx_bias_trim(const size_t v) {
 | 
			
		||||
	_map.r.rx_top_rx_bias.EN_Bias_Trim = 1;
 | 
			
		||||
	_map.r.rx_top_rx_bias.BIAS_TRIM_SPI = v;
 | 
			
		||||
	_dirty[Register::RX_TOP_RX_BIAS] = 1;
 | 
			
		||||
	flush();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MAX2837::set_vco_bias(const size_t v) {
 | 
			
		||||
	_map.r.vco_cfg.VCO_BIAS_SPI_EN = 1;
 | 
			
		||||
	_map.r.vco_cfg.VCO_BIAS_SPI = v;
 | 
			
		||||
	_dirty[Register::VCO_CFG] = 1;
 | 
			
		||||
	flush();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MAX2837::set_rx_buff_vcm(const size_t v) {
 | 
			
		||||
	_map.r.lpf_3_vga_1.BUFF_VCM = v;
 | 
			
		||||
	_dirty[Register::LPF_3_VGA_1] = 1;
 | 
			
		||||
	flush();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
reg_t MAX2837::temp_sense() {
 | 
			
		||||
	if( !_map.r.rx_top.ts_en ) {
 | 
			
		||||
		_map.r.rx_top.ts_en = 1;
 | 
			
		||||
		flush_one(Register::RX_TOP);
 | 
			
		||||
 | 
			
		||||
		chThdSleepMilliseconds(1);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	_map.r.rx_top.ts_adc_trigger = 1;
 | 
			
		||||
	flush_one(Register::RX_TOP);
 | 
			
		||||
 | 
			
		||||
	halPolledDelay(ticks_for_temperature_sense_adc_conversion);
 | 
			
		||||
 | 
			
		||||
	const auto value = read(Register::TEMP_SENSE);
 | 
			
		||||
 | 
			
		||||
	_map.r.rx_top.ts_adc_trigger = 0;
 | 
			
		||||
	flush_one(Register::RX_TOP);
 | 
			
		||||
 | 
			
		||||
	return value;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										914
									
								
								Software/portapack-mayhem/firmware/application/hw/max2837.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										914
									
								
								Software/portapack-mayhem/firmware/application/hw/max2837.hpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,914 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
 | 
			
		||||
 *
 | 
			
		||||
 * This file is part of PortaPack.
 | 
			
		||||
 *
 | 
			
		||||
 * 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 __MAX2837_H__
 | 
			
		||||
#define __MAX2837_H__
 | 
			
		||||
 | 
			
		||||
#include "gpio.hpp"
 | 
			
		||||
#include "spi_arbiter.hpp"
 | 
			
		||||
 | 
			
		||||
#include <cstdint>
 | 
			
		||||
#include <array>
 | 
			
		||||
 | 
			
		||||
#include "dirty_registers.hpp"
 | 
			
		||||
#include "rf_path.hpp"
 | 
			
		||||
#include "utility.hpp"
 | 
			
		||||
 | 
			
		||||
namespace max2837 {
 | 
			
		||||
 | 
			
		||||
enum class Mode {
 | 
			
		||||
	Mask_Enable = 0b001,
 | 
			
		||||
	Mask_RxEnable = 0b010,
 | 
			
		||||
	Mask_TxEnable = 0b100,
 | 
			
		||||
 | 
			
		||||
	Shutdown = 0b000,
 | 
			
		||||
	Standby = Mask_Enable,
 | 
			
		||||
	Receive = Mask_Enable | Mask_RxEnable,
 | 
			
		||||
	Transmit = Mask_Enable | Mask_TxEnable,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/*************************************************************************/
 | 
			
		||||
 | 
			
		||||
namespace lo {
 | 
			
		||||
 | 
			
		||||
constexpr std::array<rf::FrequencyRange, 4> band { {
 | 
			
		||||
	{ 2300000000, 2400000000 },
 | 
			
		||||
	{ 2400000000, 2500000000 },
 | 
			
		||||
	{ 2500000000, 2600000000 },
 | 
			
		||||
	{ 2600000000, 2700000000 },
 | 
			
		||||
} };
 | 
			
		||||
 | 
			
		||||
} /* namespace lo */
 | 
			
		||||
 | 
			
		||||
/*************************************************************************/
 | 
			
		||||
 | 
			
		||||
namespace lna {
 | 
			
		||||
 | 
			
		||||
constexpr range_t<int8_t> gain_db_range { 0, 40 };
 | 
			
		||||
constexpr int8_t gain_db_step = 8;
 | 
			
		||||
 | 
			
		||||
constexpr std::array<rf::FrequencyRange, 2> band { {
 | 
			
		||||
	{ 2300000000, 2500000000 },
 | 
			
		||||
	{ 2500000000, 2700000000 },
 | 
			
		||||
} };
 | 
			
		||||
 | 
			
		||||
} /* namespace lna */
 | 
			
		||||
 | 
			
		||||
/*************************************************************************/
 | 
			
		||||
 | 
			
		||||
namespace vga {
 | 
			
		||||
 | 
			
		||||
constexpr range_t<int8_t> gain_db_range { 0, 62 };
 | 
			
		||||
constexpr int8_t gain_db_step = 2;
 | 
			
		||||
 | 
			
		||||
} /* namespace vga */
 | 
			
		||||
 | 
			
		||||
/*************************************************************************/
 | 
			
		||||
 | 
			
		||||
namespace tx {
 | 
			
		||||
 | 
			
		||||
constexpr range_t<int8_t> gain_db_range { 0, 47 };
 | 
			
		||||
constexpr int8_t gain_db_step = 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*************************************************************************/
 | 
			
		||||
 | 
			
		||||
namespace filter {
 | 
			
		||||
 | 
			
		||||
constexpr std::array<uint32_t, 16> bandwidths {
 | 
			
		||||
	/* Assumption: these values are in ascending order */
 | 
			
		||||
	 1750000,
 | 
			
		||||
	 2500000,	/* Some documentation says 2.25MHz */
 | 
			
		||||
	 3500000,
 | 
			
		||||
	 5000000,
 | 
			
		||||
	 5500000,
 | 
			
		||||
	 6000000,
 | 
			
		||||
	 7000000,
 | 
			
		||||
	 8000000,
 | 
			
		||||
	 9000000,
 | 
			
		||||
	10000000,
 | 
			
		||||
	12000000,
 | 
			
		||||
	14000000,
 | 
			
		||||
	15000000,
 | 
			
		||||
	20000000,
 | 
			
		||||
	24000000,
 | 
			
		||||
	28000000,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
constexpr auto bandwidth_minimum = bandwidths[0];
 | 
			
		||||
constexpr auto bandwidth_maximum = bandwidths[bandwidths.size() - 1];
 | 
			
		||||
 | 
			
		||||
} /* namespace filter */
 | 
			
		||||
 | 
			
		||||
/*************************************************************************/
 | 
			
		||||
 | 
			
		||||
using reg_t = uint16_t;
 | 
			
		||||
using address_t = uint8_t;
 | 
			
		||||
 | 
			
		||||
constexpr size_t reg_count = 32;
 | 
			
		||||
 | 
			
		||||
enum class Register : address_t {
 | 
			
		||||
	RXRF_1			= 0,
 | 
			
		||||
	RXRF_2			= 1,
 | 
			
		||||
	LPF_1			= 2,
 | 
			
		||||
	LPF_2			= 3,
 | 
			
		||||
	LPF_3_VGA_1		= 4,
 | 
			
		||||
	VGA_2			= 5,
 | 
			
		||||
	VGA_3_RX_TOP	= 6,
 | 
			
		||||
	TEMP_SENSE		= 7,
 | 
			
		||||
	RX_TOP_RX_BIAS	= 8,
 | 
			
		||||
	RX_TOP			= 9,
 | 
			
		||||
	TX_TOP_1		= 10,
 | 
			
		||||
	TX_TOP_2		= 11,
 | 
			
		||||
	HPFSM_1			= 12,
 | 
			
		||||
	HPFSM_2			= 13,
 | 
			
		||||
	HPFSM_3			= 14,
 | 
			
		||||
	HPFSM_4			= 15,
 | 
			
		||||
	SPI_EN			= 16,
 | 
			
		||||
	SYN_FR_DIV_1	= 17,
 | 
			
		||||
	SYN_FR_DIV_2	= 18,
 | 
			
		||||
	SYN_INT_DIV		= 19,
 | 
			
		||||
	SYN_CFG_1		= 20,
 | 
			
		||||
	SYN_CFG_2		= 21,
 | 
			
		||||
	VAS_CFG			= 22,
 | 
			
		||||
	LO_MISC			= 23,
 | 
			
		||||
	XTAL_CFG		= 24,
 | 
			
		||||
	VCO_CFG			= 25,
 | 
			
		||||
	LO_GEN			= 26,
 | 
			
		||||
	PA_DRV_PA_DAC	= 27,
 | 
			
		||||
	PA_DAC			= 28,
 | 
			
		||||
	TX_GAIN			= 29,
 | 
			
		||||
	TX_LO_IQ		= 30,
 | 
			
		||||
	TX_DC_CORR		= 31,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct RXRF_1_Type {
 | 
			
		||||
	reg_t LNA_EN	: 1;
 | 
			
		||||
	reg_t Mixer_EN	: 1;
 | 
			
		||||
	reg_t RxLO_EN	: 1;
 | 
			
		||||
	reg_t Lbias		: 2;
 | 
			
		||||
	reg_t Mbias		: 2;
 | 
			
		||||
	reg_t buf		: 2;
 | 
			
		||||
	reg_t LNAband	: 1;
 | 
			
		||||
	reg_t RESERVED0	: 6;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static_assert(sizeof(RXRF_1_Type) == sizeof(reg_t), "RXRF_1_Type wrong size");
 | 
			
		||||
 | 
			
		||||
struct RXRF_2_Type {
 | 
			
		||||
	reg_t LNAtune		: 1;
 | 
			
		||||
	reg_t LNAde_Q		: 1;
 | 
			
		||||
	reg_t L				: 3;
 | 
			
		||||
	reg_t iqerr_trim	: 5;
 | 
			
		||||
	reg_t RESERVED0		: 6;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static_assert(sizeof(RXRF_2_Type) == sizeof(reg_t), "RXRF_2_Type wrong size");
 | 
			
		||||
 | 
			
		||||
struct LPF_1_Type {
 | 
			
		||||
	reg_t LPF_EN	: 1;
 | 
			
		||||
	reg_t TxBB_EN	: 1;
 | 
			
		||||
	reg_t ModeCtrl	: 2;
 | 
			
		||||
	reg_t FT		: 4;
 | 
			
		||||
	reg_t dF		: 2;
 | 
			
		||||
	reg_t RESERVED0 : 6;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static_assert(sizeof(LPF_1_Type) == sizeof(reg_t), "LPF_1_Type wrong size");
 | 
			
		||||
 | 
			
		||||
struct LPF_2_Type {
 | 
			
		||||
	reg_t PT_SPI	: 4;
 | 
			
		||||
	reg_t Bqd		: 3;
 | 
			
		||||
	reg_t TxRPCM	: 3;
 | 
			
		||||
	reg_t RESERVED0	: 6;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static_assert(sizeof(LPF_2_Type) == sizeof(reg_t), "LPF_2_Type wrong size");
 | 
			
		||||
 | 
			
		||||
struct LPF_3_VGA_1_Type {
 | 
			
		||||
	reg_t RP			: 2;
 | 
			
		||||
	reg_t TxBuff		: 2;
 | 
			
		||||
	reg_t VGA_EN		: 1;
 | 
			
		||||
	reg_t VGAMUX_enable	: 1;
 | 
			
		||||
	reg_t BUFF_Curr		: 2;
 | 
			
		||||
	reg_t BUFF_VCM		: 2;
 | 
			
		||||
	reg_t RESERVED0		: 6;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static_assert(sizeof(LPF_3_VGA_1_Type) == sizeof(reg_t), "LPF_3_VGA_1_Type wrong size");
 | 
			
		||||
 | 
			
		||||
struct VGA_2_Type {
 | 
			
		||||
	reg_t VGA			: 5;
 | 
			
		||||
	reg_t sel_In1_In2	: 1;
 | 
			
		||||
	reg_t turbo15n20	: 1;
 | 
			
		||||
	reg_t VGA_Curr		: 2;
 | 
			
		||||
	reg_t fuse_arm		: 1;
 | 
			
		||||
	reg_t RESERVED0		: 6;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static_assert(sizeof(VGA_2_Type) == sizeof(reg_t), "VGA_2_Type wrong size");
 | 
			
		||||
 | 
			
		||||
struct VGA_3_RX_TOP_Type {
 | 
			
		||||
	reg_t RESERVED0				: 6;
 | 
			
		||||
	reg_t RSSI_EN_SPIenables	: 1;
 | 
			
		||||
	reg_t RSSI_MUX				: 1;
 | 
			
		||||
	reg_t RSSI_MODE				: 1;
 | 
			
		||||
	reg_t LPF_MODE_SEL			: 1;
 | 
			
		||||
	reg_t RESERVED1				: 6;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static_assert(sizeof(VGA_3_RX_TOP_Type) == sizeof(reg_t), "VGA_3_RX_TOP_Type wrong size");
 | 
			
		||||
 | 
			
		||||
struct TEMP_SENSE_Type {
 | 
			
		||||
	reg_t ts_adc							: 5;
 | 
			
		||||
	reg_t RESERVED0							: 1;
 | 
			
		||||
	reg_t PLL_test_output					: 1;
 | 
			
		||||
	reg_t VAS_test_output					: 1;
 | 
			
		||||
	reg_t HPFSM_test_output					: 1;
 | 
			
		||||
	reg_t LOGEN_trim_divider_test_output	: 1;
 | 
			
		||||
	reg_t RESERVED1							: 6;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static_assert(sizeof(TEMP_SENSE_Type) == sizeof(reg_t), "TEMP_SENSE_Type wrong size");
 | 
			
		||||
 | 
			
		||||
struct RX_TOP_RX_BIAS_Type {
 | 
			
		||||
	reg_t LNAgain_SPI_EN	: 1;
 | 
			
		||||
	reg_t VGAgain_SPI_EN	: 1;
 | 
			
		||||
	reg_t EN_Bias_Trim		: 1;
 | 
			
		||||
	reg_t BIAS_TRIM_SPI		: 5;
 | 
			
		||||
	reg_t BIAS_TRIM_CNTRL	: 1;
 | 
			
		||||
	reg_t RX_IQERR_SPI_EN	: 1;
 | 
			
		||||
	reg_t RESERVED0			: 6;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static_assert(sizeof(RX_TOP_RX_BIAS_Type) == sizeof(reg_t), "RX_TOP_RX_BIAS_Type wrong size");
 | 
			
		||||
 | 
			
		||||
struct RX_TOP_Type {
 | 
			
		||||
	reg_t ts_adc_trigger	: 1;
 | 
			
		||||
	reg_t ts_en				: 1;
 | 
			
		||||
	reg_t LPFtrim_SPI_EN	: 1;
 | 
			
		||||
	reg_t DOUT_DRVH			: 1;
 | 
			
		||||
	reg_t DOUT_PU			: 1;
 | 
			
		||||
	reg_t DOUT_SEL			: 3;
 | 
			
		||||
	reg_t fuse_th			: 1;
 | 
			
		||||
	reg_t fuse_burn_gkt		: 1;
 | 
			
		||||
	reg_t RESERVED0			: 6;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static_assert(sizeof(RX_TOP_Type) == sizeof(reg_t), "RX_TOP_Type wrong size");
 | 
			
		||||
 | 
			
		||||
struct TX_TOP_1_Type {
 | 
			
		||||
	reg_t RESERVED0			: 1;
 | 
			
		||||
	reg_t TXCAL_GAIN		: 2;
 | 
			
		||||
	reg_t TXCAL_V2I_FILT	: 3;
 | 
			
		||||
	reg_t TX_BIAS_ADJ		: 2;
 | 
			
		||||
	reg_t RESERVED1			: 2;
 | 
			
		||||
	reg_t RESERVED2			: 6;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static_assert(sizeof(TX_TOP_1_Type) == sizeof(reg_t), "TX_TOP_1_Type wrong size");
 | 
			
		||||
 | 
			
		||||
struct TX_TOP_2_Type {
 | 
			
		||||
	reg_t AMD_SPI_EN		: 1;
 | 
			
		||||
	reg_t TXMXR_V2I_GAIN	: 4;
 | 
			
		||||
	reg_t RESERVED0			: 5;
 | 
			
		||||
	reg_t RESERVED1			: 6;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static_assert(sizeof(TX_TOP_2_Type) == sizeof(reg_t), "TX_TOP_2_Type wrong size");
 | 
			
		||||
 | 
			
		||||
struct HPFSM_1_Type {
 | 
			
		||||
	reg_t HPC_10M		: 2;
 | 
			
		||||
	reg_t HPC_10M_GAIN	: 2;
 | 
			
		||||
	reg_t HPC_600k		: 3;
 | 
			
		||||
	reg_t HPC_600k_GAIN	: 3;
 | 
			
		||||
	reg_t RESERVED0		: 6;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static_assert(sizeof(HPFSM_1_Type) == sizeof(reg_t), "HPFSM_1_Type wrong size");
 | 
			
		||||
 | 
			
		||||
struct HPFSM_2_Type {
 | 
			
		||||
	reg_t HPC_100k		: 2;
 | 
			
		||||
	reg_t HPC_100k_GAIN	: 2;
 | 
			
		||||
	reg_t HPC_30k		: 2;
 | 
			
		||||
	reg_t HPC_30k_GAIN	: 2;
 | 
			
		||||
	reg_t HPC_1k		: 2;
 | 
			
		||||
	reg_t RESERVED0		: 6;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static_assert(sizeof(HPFSM_2_Type) == sizeof(reg_t), "HPFSM_2_Type wrong size");
 | 
			
		||||
 | 
			
		||||
struct HPFSM_3_Type {
 | 
			
		||||
	reg_t HPC_1k_GAIN	: 2;
 | 
			
		||||
	reg_t HPC_DELAY		: 2;
 | 
			
		||||
	reg_t HPC_STOP		: 2;
 | 
			
		||||
	reg_t HPC_STOP_M2	: 2;
 | 
			
		||||
	reg_t HPC_RXGAIN_EN	: 1;
 | 
			
		||||
	reg_t HPC_MODE		: 1;
 | 
			
		||||
	reg_t RESERVED0		: 6;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static_assert(sizeof(HPFSM_3_Type) == sizeof(reg_t), "HPFSM_3_Type wrong size");
 | 
			
		||||
 | 
			
		||||
struct HPFSM_4_Type {
 | 
			
		||||
	reg_t HPC_DIVH		: 1;
 | 
			
		||||
	reg_t HPC_TST		: 5;
 | 
			
		||||
	reg_t HPC_SEQ_BYP	: 1;
 | 
			
		||||
	reg_t DOUT_CSB_SEL	: 1;
 | 
			
		||||
	reg_t RESERVED0		: 2;
 | 
			
		||||
	reg_t RESERVED1		: 6;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static_assert(sizeof(HPFSM_4_Type) == sizeof(reg_t), "HPFSM_4_Type wrong size");
 | 
			
		||||
 | 
			
		||||
struct SPI_EN_Type {
 | 
			
		||||
	reg_t EN_SPI		: 1;
 | 
			
		||||
	reg_t CAL_SPI		: 1;
 | 
			
		||||
	reg_t LOGEN_SPI_EN	: 1;
 | 
			
		||||
	reg_t SYN_SPI_EN	: 1;
 | 
			
		||||
	reg_t VAS_SPI_EN	: 1;
 | 
			
		||||
	reg_t PADRV_SPI_EN	: 1;
 | 
			
		||||
	reg_t PADAC_SPI_EN 	: 1;
 | 
			
		||||
	reg_t PADAC_TX_EN	: 1;
 | 
			
		||||
	reg_t TXMX_SPI_EN	: 1;
 | 
			
		||||
	reg_t TXLO_SPI_EN	: 1;
 | 
			
		||||
	reg_t RESERVED0		: 6;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static_assert(sizeof(SPI_EN_Type) == sizeof(reg_t), "SPI_EN_Type wrong size");
 | 
			
		||||
 | 
			
		||||
struct SYN_FR_DIV_1_Type {
 | 
			
		||||
	reg_t SYN_FRDIV_9_0	: 10;
 | 
			
		||||
	reg_t RESERVED0		: 6;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static_assert(sizeof(SYN_FR_DIV_1_Type) == sizeof(reg_t), "SYN_FR_DIV_1_Type wrong size");
 | 
			
		||||
 | 
			
		||||
struct SYN_FR_DIV_2_Type {
 | 
			
		||||
	reg_t SYN_FRDIV_19_10	: 10;
 | 
			
		||||
	reg_t RESERVED0			: 6;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static_assert(sizeof(SYN_FR_DIV_2_Type) == sizeof(reg_t), "SYN_FR_DIV_2_Type wrong size");
 | 
			
		||||
 | 
			
		||||
struct SYN_INT_DIV_Type {
 | 
			
		||||
	reg_t SYN_INTDIV	: 8;
 | 
			
		||||
	reg_t LOGEN_BSW		: 2;
 | 
			
		||||
	reg_t RESERVED0		: 6;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static_assert(sizeof(SYN_INT_DIV_Type) == sizeof(reg_t), "SYN_INT_DIV_Type wrong size");
 | 
			
		||||
 | 
			
		||||
struct SYN_CFG_1_Type {
 | 
			
		||||
	reg_t SYN_MODE_FR_EN		: 1;
 | 
			
		||||
	reg_t SYN_REF_DIV_RATIO		: 2;
 | 
			
		||||
	reg_t SYN_CP_CURRENT		: 2;
 | 
			
		||||
	reg_t SYN_CLOCKOUT_DRIVE	: 1;
 | 
			
		||||
	reg_t SYN_TURBO_EN			: 1;
 | 
			
		||||
	reg_t CP_TRM_SET			: 1;
 | 
			
		||||
	reg_t CP_TRM_CODE			: 2;
 | 
			
		||||
	reg_t RESERVED0				: 6;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static_assert(sizeof(SYN_CFG_1_Type) == sizeof(reg_t), "SYN_CFG_1_Type wrong size");
 | 
			
		||||
 | 
			
		||||
struct SYN_CFG_2_Type {
 | 
			
		||||
	reg_t SYN_CP_COMMON_MODE_EN			: 1;
 | 
			
		||||
	reg_t SYN_PRESCALER_BIAS_BOOST		: 1;
 | 
			
		||||
	reg_t SYN_CP_BETA_CURRENT_COMP_EN	: 1;
 | 
			
		||||
	reg_t SYN_SD_CLK_SEL				: 1;
 | 
			
		||||
	reg_t SYN_CP_PW_ADJ					: 1;
 | 
			
		||||
	reg_t SYN_CP_LIN_CURRENT_SEL		: 2;
 | 
			
		||||
	reg_t SYN_TEST_OUT_SEL				: 3;
 | 
			
		||||
	reg_t RESERVED0						: 6;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static_assert(sizeof(SYN_CFG_2_Type) == sizeof(reg_t), "SYN_CFG_2_Type wrong size");
 | 
			
		||||
 | 
			
		||||
struct VAS_CFG_Type {
 | 
			
		||||
	reg_t VAS_MODE			: 1;
 | 
			
		||||
	reg_t VAS_RELOCK_SEL	: 1;
 | 
			
		||||
	reg_t VAS_DIV			: 3;
 | 
			
		||||
	reg_t VAS_DLY			: 2;
 | 
			
		||||
	reg_t VAS_TRIG_EN		: 1;
 | 
			
		||||
	reg_t VAS_ADE			: 1;
 | 
			
		||||
	reg_t VAS_ADL_SPI		: 1;
 | 
			
		||||
	reg_t RESERVED0			: 6;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static_assert(sizeof(VAS_CFG_Type) == sizeof(reg_t), "VAS_CFG_Type wrong size");
 | 
			
		||||
 | 
			
		||||
struct LO_MISC_Type {
 | 
			
		||||
	reg_t VAS_SPI			: 5;
 | 
			
		||||
	reg_t XTAL_BIAS_SEL		: 2;
 | 
			
		||||
	reg_t XTAL_E2C_BIAS_SEL	: 1;
 | 
			
		||||
	reg_t VAS_SE			: 1;
 | 
			
		||||
	reg_t VCO_SPI_EN		: 1;
 | 
			
		||||
	reg_t RESERVED0			: 6;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static_assert(sizeof(LO_MISC_Type) == sizeof(reg_t), "LO_MISC_Type wrong size");
 | 
			
		||||
 | 
			
		||||
struct XTAL_CFG_Type {
 | 
			
		||||
	reg_t XTAL_FTUNE		: 7;
 | 
			
		||||
	reg_t XTAL_CLKOUT_EN	: 1;
 | 
			
		||||
	reg_t XTAL_CLKOUT_DIV	: 1;
 | 
			
		||||
	reg_t XTAL_CORE_EN		: 1;
 | 
			
		||||
	reg_t RESERVED0			: 6;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static_assert(sizeof(XTAL_CFG_Type) == sizeof(reg_t), "XTAL_CFG_Type wrong size");
 | 
			
		||||
 | 
			
		||||
struct VCO_CFG_Type {
 | 
			
		||||
	reg_t VCO_BIAS_SPI_EN	: 1;
 | 
			
		||||
	reg_t VCO_BIAS_SPI		: 4;
 | 
			
		||||
	reg_t VCO_CMEN			: 1;
 | 
			
		||||
	reg_t VCO_PDET_TST		: 2;
 | 
			
		||||
	reg_t VCO_BUF_BIASH		: 2;
 | 
			
		||||
	reg_t RESERVED0			: 6;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static_assert(sizeof(VCO_CFG_Type) == sizeof(reg_t), "VCO_CFG_Type wrong size");
 | 
			
		||||
 | 
			
		||||
struct LO_GEN_Type {
 | 
			
		||||
	reg_t LOGEN_BIASH1	: 2;
 | 
			
		||||
	reg_t LOGEN_BIASH2	: 1;
 | 
			
		||||
	reg_t LOGEN_2GM		: 1;
 | 
			
		||||
	reg_t LOGEN_TRIM1	: 1;
 | 
			
		||||
	reg_t LOGEN_TRIM2	: 1;
 | 
			
		||||
	reg_t VAS_TST		: 4;
 | 
			
		||||
	reg_t RESERVED0		: 6;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static_assert(sizeof(LO_GEN_Type) == sizeof(reg_t), "LO_GEN_Type wrong size");
 | 
			
		||||
 | 
			
		||||
struct PA_DRV_PA_DAC_Type {
 | 
			
		||||
	reg_t PADRV_BIAS			: 3;
 | 
			
		||||
	reg_t PADRV_DOWN_SPI_EN		: 1;
 | 
			
		||||
	reg_t PADRV_DOWN_SPI_SEL	: 1;
 | 
			
		||||
	reg_t PADAC_IV				: 1;
 | 
			
		||||
	reg_t PADAC_VMODE			: 1;
 | 
			
		||||
	reg_t PADAC_DIVH			: 1;
 | 
			
		||||
	reg_t TXGATE_EN				: 1;
 | 
			
		||||
	reg_t TX_DCCORR_EN			: 1;
 | 
			
		||||
	reg_t RESERVED0				: 6;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static_assert(sizeof(PA_DRV_PA_DAC_Type) == sizeof(reg_t), "PA_DRV_PA_DAC_Type wrong size");
 | 
			
		||||
 | 
			
		||||
struct PA_DAC_Type {
 | 
			
		||||
	reg_t PADAC_BIAS	: 6;
 | 
			
		||||
	reg_t PADAC_DLY		: 4;
 | 
			
		||||
	reg_t RESERVED0		: 6;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static_assert(sizeof(PA_DAC_Type) == sizeof(reg_t), "PA_DAC_Type wrong size");
 | 
			
		||||
 | 
			
		||||
struct TX_GAIN_Type {
 | 
			
		||||
	reg_t TXVGA_GAIN_SPI_EN		: 1;
 | 
			
		||||
	reg_t TXVGA_GAIN_MSB_SPI_EN	: 1;
 | 
			
		||||
	reg_t TX_DCCORR_SPI_EN		: 1;
 | 
			
		||||
	reg_t FUSE_ARM				: 1;
 | 
			
		||||
	reg_t TXVGA_GAIN_SPI		: 6;
 | 
			
		||||
	reg_t RESERVED0				: 6;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static_assert(sizeof(TX_GAIN_Type) == sizeof(reg_t), "TX_GAIN_Type wrong size");
 | 
			
		||||
 | 
			
		||||
struct TX_LO_IQ_Type {
 | 
			
		||||
	reg_t TXLO_IQ_SPI		: 5;
 | 
			
		||||
	reg_t TXLO_IQ_SPI_EN	: 1;
 | 
			
		||||
	reg_t TXLO_BUFF			: 2;
 | 
			
		||||
	reg_t FUSE_GKT			: 1;
 | 
			
		||||
	reg_t FUSE_RTH			: 1;
 | 
			
		||||
	reg_t RESERVED0			: 6;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static_assert(sizeof(TX_LO_IQ_Type) == sizeof(reg_t), "TX_LO_IQ_Type wrong size");
 | 
			
		||||
 | 
			
		||||
struct TX_DC_CORR_Type {
 | 
			
		||||
	reg_t TX_DCCORR_I	: 5;
 | 
			
		||||
	reg_t TX_DCCORR_Q	: 5;
 | 
			
		||||
	reg_t RESERVED0		: 6;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static_assert(sizeof(TX_DC_CORR_Type) == sizeof(reg_t), "TX_DC_CORR_Type wrong size");
 | 
			
		||||
 | 
			
		||||
struct Register_Type {
 | 
			
		||||
	RXRF_1_Type			rxrf_1;			/*  0 */
 | 
			
		||||
	RXRF_2_Type			rxrf_2;
 | 
			
		||||
	LPF_1_Type			lpf_1;
 | 
			
		||||
	LPF_2_Type			lpf_2;
 | 
			
		||||
	LPF_3_VGA_1_Type	lpf_3_vga_1;	/*  4 */
 | 
			
		||||
	VGA_2_Type			vga_2;
 | 
			
		||||
	VGA_3_RX_TOP_Type	vga_3_rx_top;
 | 
			
		||||
	TEMP_SENSE_Type		temp_sense;
 | 
			
		||||
	RX_TOP_RX_BIAS_Type	rx_top_rx_bias;	/*  8 */
 | 
			
		||||
	RX_TOP_Type			rx_top;
 | 
			
		||||
	TX_TOP_1_Type		tx_top_1;
 | 
			
		||||
	TX_TOP_2_Type		tx_top_2;
 | 
			
		||||
	HPFSM_1_Type		hpfsm_1;		/* 12 */
 | 
			
		||||
	HPFSM_2_Type		hpfsm_2;
 | 
			
		||||
	HPFSM_3_Type		hpfsm_3;
 | 
			
		||||
	HPFSM_4_Type		hpfsm_4;
 | 
			
		||||
	SPI_EN_Type			spi_en;			/* 16 */
 | 
			
		||||
	SYN_FR_DIV_1_Type	syn_fr_div_1;
 | 
			
		||||
	SYN_FR_DIV_2_Type	syn_fr_div_2;
 | 
			
		||||
	SYN_INT_DIV_Type	syn_int_div;
 | 
			
		||||
	SYN_CFG_1_Type		syn_cfg_1;		/* 20 */
 | 
			
		||||
	SYN_CFG_2_Type		syn_cfg_2;
 | 
			
		||||
	VAS_CFG_Type		vas_cfg;
 | 
			
		||||
	LO_MISC_Type		lo_misc;
 | 
			
		||||
	XTAL_CFG_Type		xtal_cfg;		/* 24 */
 | 
			
		||||
	VCO_CFG_Type		vco_cfg;
 | 
			
		||||
	LO_GEN_Type			lo_gen;
 | 
			
		||||
	PA_DRV_PA_DAC_Type	pa_drv_pa_dac;
 | 
			
		||||
	PA_DAC_Type			pa_dac;			/* 28 */
 | 
			
		||||
	TX_GAIN_Type		tx_gain;
 | 
			
		||||
	TX_LO_IQ_Type		tx_lo_iq;
 | 
			
		||||
	TX_DC_CORR_Type		tx_dc_corr;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static_assert(sizeof(Register_Type) == reg_count * sizeof(reg_t), "Register_Type wrong size");
 | 
			
		||||
 | 
			
		||||
struct RegisterMap {
 | 
			
		||||
	constexpr RegisterMap(
 | 
			
		||||
		Register_Type values
 | 
			
		||||
	) : r(values)
 | 
			
		||||
	{
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	union {
 | 
			
		||||
		Register_Type r;
 | 
			
		||||
		std::array<reg_t, reg_count> w;
 | 
			
		||||
	};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static_assert(sizeof(RegisterMap) == reg_count * sizeof(reg_t), "RegisterMap type wrong size");
 | 
			
		||||
 | 
			
		||||
constexpr RegisterMap initial_register_values { Register_Type {
 | 
			
		||||
	/* Best effort to reconcile default values specified in three
 | 
			
		||||
	 * different places in the MAX2837 documentation.
 | 
			
		||||
	 */
 | 
			
		||||
	.rxrf_1 = { /* 0 */
 | 
			
		||||
		.LNA_EN		= 0,
 | 
			
		||||
		.Mixer_EN	= 0,
 | 
			
		||||
		.RxLO_EN	= 0,
 | 
			
		||||
		.Lbias		= 0b10,
 | 
			
		||||
		.Mbias		= 0b10,
 | 
			
		||||
		.buf		= 0b10,
 | 
			
		||||
		.LNAband	= 0,
 | 
			
		||||
		.RESERVED0	= 0,
 | 
			
		||||
	},
 | 
			
		||||
	.rxrf_2 = {	/* 1 */
 | 
			
		||||
		.LNAtune	= 0,
 | 
			
		||||
		.LNAde_Q	= 1,
 | 
			
		||||
		.L			= 0b000,
 | 
			
		||||
		.iqerr_trim	= 0b00000,
 | 
			
		||||
		.RESERVED0	= 0,
 | 
			
		||||
	},
 | 
			
		||||
	.lpf_1 = {	/* 2 */
 | 
			
		||||
		.LPF_EN		= 0,
 | 
			
		||||
		.TxBB_EN	= 0,
 | 
			
		||||
		.ModeCtrl	= 0b01,
 | 
			
		||||
		.FT			= 0b1111,
 | 
			
		||||
		.dF			= 0b01,
 | 
			
		||||
		.RESERVED0	= 0,
 | 
			
		||||
	},
 | 
			
		||||
	.lpf_2 = { /* 3 */
 | 
			
		||||
		.PT_SPI		= 0b1001,
 | 
			
		||||
		.Bqd		= 0b011,
 | 
			
		||||
		.TxRPCM		= 0b011,
 | 
			
		||||
		.RESERVED0	= 0,
 | 
			
		||||
	},
 | 
			
		||||
	.lpf_3_vga_1 = { /* 4 */
 | 
			
		||||
		.RP				= 0b10,
 | 
			
		||||
		.TxBuff			= 0b10,
 | 
			
		||||
		.VGA_EN			= 0,
 | 
			
		||||
		.VGAMUX_enable	= 0,
 | 
			
		||||
		.BUFF_Curr		= 0b00,
 | 
			
		||||
		.BUFF_VCM		= 0b00,
 | 
			
		||||
		.RESERVED0		= 0,
 | 
			
		||||
	},
 | 
			
		||||
	.vga_2 = { /* 5 */
 | 
			
		||||
		.VGA			= 0b00000,
 | 
			
		||||
		.sel_In1_In2	= 0,
 | 
			
		||||
		.turbo15n20		= 0,
 | 
			
		||||
		.VGA_Curr		= 0b01,
 | 
			
		||||
		.fuse_arm		= 0,
 | 
			
		||||
		.RESERVED0		= 0,
 | 
			
		||||
	},
 | 
			
		||||
	.vga_3_rx_top = { /* 6 */
 | 
			
		||||
		.RESERVED0			= 0b000110,
 | 
			
		||||
		.RSSI_EN_SPIenables	= 0,
 | 
			
		||||
		.RSSI_MUX			= 0,
 | 
			
		||||
		.RSSI_MODE			= 0,
 | 
			
		||||
		.LPF_MODE_SEL		= 0,
 | 
			
		||||
		.RESERVED1			= 0,
 | 
			
		||||
	},
 | 
			
		||||
	.temp_sense = { /* 7 */
 | 
			
		||||
		.ts_adc				= 0b00000,
 | 
			
		||||
		.RESERVED0			= 0,
 | 
			
		||||
		.PLL_test_output	= 0,
 | 
			
		||||
		.VAS_test_output	= 0,
 | 
			
		||||
		.HPFSM_test_output	= 0,
 | 
			
		||||
		.LOGEN_trim_divider_test_output = 0,
 | 
			
		||||
		.RESERVED1			= 0,
 | 
			
		||||
	},
 | 
			
		||||
	.rx_top_rx_bias = { /* 8 */
 | 
			
		||||
		.LNAgain_SPI_EN		= 0,
 | 
			
		||||
		.VGAgain_SPI_EN		= 0,
 | 
			
		||||
		.EN_Bias_Trim		= 0,
 | 
			
		||||
		.BIAS_TRIM_SPI		= 0b10000,
 | 
			
		||||
		.BIAS_TRIM_CNTRL	= 0,
 | 
			
		||||
		.RX_IQERR_SPI_EN	= 0,
 | 
			
		||||
		.RESERVED0			= 0,
 | 
			
		||||
	},
 | 
			
		||||
	.rx_top = { /* 9 */
 | 
			
		||||
		.ts_adc_trigger	= 0,
 | 
			
		||||
		.ts_en			= 0,
 | 
			
		||||
		.LPFtrim_SPI_EN	= 0,
 | 
			
		||||
		.DOUT_DRVH		= 1,	/* Documentation mismatch */
 | 
			
		||||
		.DOUT_PU		= 1,
 | 
			
		||||
		.DOUT_SEL		= 0b000,
 | 
			
		||||
		.fuse_th		= 0,
 | 
			
		||||
		.fuse_burn_gkt	= 0,
 | 
			
		||||
		.RESERVED0		= 0,
 | 
			
		||||
	},
 | 
			
		||||
	.tx_top_1 = { /* 10 */
 | 
			
		||||
		.RESERVED0		= 0,
 | 
			
		||||
		.TXCAL_GAIN		= 0b00,
 | 
			
		||||
		.TXCAL_V2I_FILT = 0b011,
 | 
			
		||||
		.TX_BIAS_ADJ	= 0b01,
 | 
			
		||||
		.RESERVED1		= 0b00,
 | 
			
		||||
		.RESERVED2		= 0,
 | 
			
		||||
	},
 | 
			
		||||
	.tx_top_2 = { /* 11 */
 | 
			
		||||
		.AMD_SPI_EN		= 0,
 | 
			
		||||
		.TXMXR_V2I_GAIN	= 0b1011,
 | 
			
		||||
		.RESERVED0		= 0b00000,
 | 
			
		||||
		.RESERVED1		= 0,
 | 
			
		||||
	},
 | 
			
		||||
	.hpfsm_1 = { /* 12 */
 | 
			
		||||
		.HPC_10M		= 0b11,
 | 
			
		||||
		.HPC_10M_GAIN	= 0b11,
 | 
			
		||||
		.HPC_600k		= 0b100,
 | 
			
		||||
		.HPC_600k_GAIN	= 0b100,
 | 
			
		||||
		.RESERVED0		= 0,
 | 
			
		||||
	},
 | 
			
		||||
	.hpfsm_2 = { /* 13 */
 | 
			
		||||
		.HPC_100k		= 0b00,
 | 
			
		||||
		.HPC_100k_GAIN	= 0b00,
 | 
			
		||||
		.HPC_30k		= 0b01,
 | 
			
		||||
		.HPC_30k_GAIN	= 0b01,
 | 
			
		||||
		.HPC_1k			= 0b01,
 | 
			
		||||
		.RESERVED0		= 0,
 | 
			
		||||
	},
 | 
			
		||||
	.hpfsm_3 = { /* 14 */
 | 
			
		||||
		.HPC_1k_GAIN	= 0b01,
 | 
			
		||||
		.HPC_DELAY		= 0b01,
 | 
			
		||||
		.HPC_STOP		= 0b00,
 | 
			
		||||
		.HPC_STOP_M2	= 0b11,
 | 
			
		||||
		.HPC_RXGAIN_EN	= 1,
 | 
			
		||||
		.HPC_MODE		= 0,
 | 
			
		||||
		.RESERVED0		= 0,
 | 
			
		||||
	},
 | 
			
		||||
	.hpfsm_4 = { /* 15 */
 | 
			
		||||
		.HPC_DIVH		= 1,
 | 
			
		||||
		.HPC_TST		= 0b00000,
 | 
			
		||||
		.HPC_SEQ_BYP	= 0,
 | 
			
		||||
		.DOUT_CSB_SEL	= 1,	/* Documentation mismatch */
 | 
			
		||||
		.RESERVED0		= 0b00,
 | 
			
		||||
		.RESERVED1		= 0,
 | 
			
		||||
	},
 | 
			
		||||
	.spi_en = { /* 16 */
 | 
			
		||||
		.EN_SPI			= 0,
 | 
			
		||||
		.CAL_SPI		= 0,
 | 
			
		||||
		.LOGEN_SPI_EN	= 1,
 | 
			
		||||
		.SYN_SPI_EN		= 1,
 | 
			
		||||
		.VAS_SPI_EN		= 1,
 | 
			
		||||
		.PADRV_SPI_EN	= 0,
 | 
			
		||||
		.PADAC_SPI_EN	= 0,
 | 
			
		||||
		.PADAC_TX_EN	= 0,
 | 
			
		||||
		.TXMX_SPI_EN	= 0,
 | 
			
		||||
		.TXLO_SPI_EN	= 0,
 | 
			
		||||
		.RESERVED0		= 0,
 | 
			
		||||
	},
 | 
			
		||||
	.syn_fr_div_1 = { /* 17 */
 | 
			
		||||
		.SYN_FRDIV_9_0	= 0b0101010101,
 | 
			
		||||
		.RESERVED0		= 0,
 | 
			
		||||
	},
 | 
			
		||||
	.syn_fr_div_2 = { /* 18 */
 | 
			
		||||
		.SYN_FRDIV_19_10	= 0b0101010101,
 | 
			
		||||
		.RESERVED0			= 0,
 | 
			
		||||
	},
 | 
			
		||||
	.syn_int_div = { /* 19 */
 | 
			
		||||
		.SYN_INTDIV	= 0b01010011,
 | 
			
		||||
		.LOGEN_BSW	= 0b01,
 | 
			
		||||
		.RESERVED0	= 0,
 | 
			
		||||
	},
 | 
			
		||||
	.syn_cfg_1 = { /* 20 */
 | 
			
		||||
		.SYN_MODE_FR_EN		= 1,
 | 
			
		||||
		.SYN_REF_DIV_RATIO	= 0b00,
 | 
			
		||||
		.SYN_CP_CURRENT		= 0b00,
 | 
			
		||||
		.SYN_CLOCKOUT_DRIVE	= 0,
 | 
			
		||||
		.SYN_TURBO_EN		= 1,
 | 
			
		||||
		.CP_TRM_SET			= 0,
 | 
			
		||||
		.CP_TRM_CODE		= 0b10,
 | 
			
		||||
		.RESERVED0			= 0,
 | 
			
		||||
	},
 | 
			
		||||
	.syn_cfg_2 = { /* 21 */
 | 
			
		||||
		.SYN_CP_COMMON_MODE_EN		= 1,
 | 
			
		||||
		.SYN_PRESCALER_BIAS_BOOST	= 0,
 | 
			
		||||
		.SYN_CP_BETA_CURRENT_COMP_EN = 1,
 | 
			
		||||
		.SYN_SD_CLK_SEL				= 1,
 | 
			
		||||
		.SYN_CP_PW_ADJ				= 0,
 | 
			
		||||
		.SYN_CP_LIN_CURRENT_SEL		= 0b01,
 | 
			
		||||
		.SYN_TEST_OUT_SEL			= 0b000,
 | 
			
		||||
		.RESERVED0					= 0,
 | 
			
		||||
	},
 | 
			
		||||
	.vas_cfg = { /* 22 */
 | 
			
		||||
		.VAS_MODE		= 1,
 | 
			
		||||
		.VAS_RELOCK_SEL	= 0,
 | 
			
		||||
		.VAS_DIV		= 0b010,
 | 
			
		||||
		.VAS_DLY		= 0b01,
 | 
			
		||||
		.VAS_TRIG_EN	= 1,
 | 
			
		||||
		.VAS_ADE		= 1,
 | 
			
		||||
		.VAS_ADL_SPI	= 0,
 | 
			
		||||
		.RESERVED0		= 0,
 | 
			
		||||
	},
 | 
			
		||||
	.lo_misc = { /* 23 */
 | 
			
		||||
		.VAS_SPI			= 0b01111,
 | 
			
		||||
		.XTAL_BIAS_SEL		= 0b10,
 | 
			
		||||
		.XTAL_E2C_BIAS_SEL	= 0,
 | 
			
		||||
		.VAS_SE				= 0,
 | 
			
		||||
		.VCO_SPI_EN			= 1,
 | 
			
		||||
		.RESERVED0			= 0,
 | 
			
		||||
	},
 | 
			
		||||
	.xtal_cfg = { /* 24 */
 | 
			
		||||
		.XTAL_FTUNE			= 0b0000000,
 | 
			
		||||
		.XTAL_CLKOUT_EN		= 0,	/* 1->0 to "turn off" CLKOUT pin. Doesn't seem to work though... */
 | 
			
		||||
		.XTAL_CLKOUT_DIV	= 1,
 | 
			
		||||
		.XTAL_CORE_EN		= 0,
 | 
			
		||||
		.RESERVED0			= 0,
 | 
			
		||||
	},
 | 
			
		||||
	.vco_cfg = { /* 25 */
 | 
			
		||||
		.VCO_BIAS_SPI_EN	= 0,
 | 
			
		||||
		.VCO_BIAS_SPI		= 0b0000,
 | 
			
		||||
		.VCO_CMEN			= 0,
 | 
			
		||||
		.VCO_PDET_TST		= 0b00,
 | 
			
		||||
		.VCO_BUF_BIASH		= 0b01,
 | 
			
		||||
		.RESERVED0			= 0,
 | 
			
		||||
	},
 | 
			
		||||
	.lo_gen = { /* 26 */
 | 
			
		||||
		.LOGEN_BIASH1	= 0b10,
 | 
			
		||||
		.LOGEN_BIASH2	= 0,
 | 
			
		||||
		.LOGEN_2GM		= 1,
 | 
			
		||||
		.LOGEN_TRIM1	= 0,
 | 
			
		||||
		.LOGEN_TRIM2	= 0,
 | 
			
		||||
		.VAS_TST		= 0b1111,
 | 
			
		||||
		.RESERVED0		= 0,
 | 
			
		||||
	},
 | 
			
		||||
	.pa_drv_pa_dac = { /* 27 */
 | 
			
		||||
		.PADRV_BIAS			= 0b011,
 | 
			
		||||
		.PADRV_DOWN_SPI_EN	= 0,
 | 
			
		||||
		.PADRV_DOWN_SPI_SEL	= 0,	/* Documentation mismatch */
 | 
			
		||||
		.PADAC_IV			= 1,
 | 
			
		||||
		.PADAC_VMODE		= 1,
 | 
			
		||||
		.PADAC_DIVH			= 1,
 | 
			
		||||
		.TXGATE_EN			= 1,
 | 
			
		||||
		.TX_DCCORR_EN		= 1,
 | 
			
		||||
		.RESERVED0			= 0,
 | 
			
		||||
	},
 | 
			
		||||
	.pa_dac = { /* 28 */
 | 
			
		||||
		.PADAC_BIAS	= 0b000000,
 | 
			
		||||
		.PADAC_DLY	= 0b0011,
 | 
			
		||||
		.RESERVED0	= 0,
 | 
			
		||||
	},
 | 
			
		||||
	.tx_gain = { /* 29 */
 | 
			
		||||
		.TXVGA_GAIN_SPI_EN		= 0,
 | 
			
		||||
		.TXVGA_GAIN_MSB_SPI_EN	= 0,
 | 
			
		||||
		.TX_DCCORR_SPI_EN		= 0,
 | 
			
		||||
		.FUSE_ARM				= 0,
 | 
			
		||||
		.TXVGA_GAIN_SPI			= 0b111111,
 | 
			
		||||
		.RESERVED0				= 0,
 | 
			
		||||
	},
 | 
			
		||||
	.tx_lo_iq = { /* 30 */
 | 
			
		||||
		.TXLO_IQ_SPI	= 0b00000,
 | 
			
		||||
		.TXLO_IQ_SPI_EN	= 0,
 | 
			
		||||
		.TXLO_BUFF		= 0b10,
 | 
			
		||||
		.FUSE_GKT		= 0,
 | 
			
		||||
		.FUSE_RTH		= 0,
 | 
			
		||||
		.RESERVED0		= 0,
 | 
			
		||||
	},
 | 
			
		||||
	.tx_dc_corr = { /* 31 */
 | 
			
		||||
		.TX_DCCORR_I	= 0b00000,
 | 
			
		||||
		.TX_DCCORR_Q	= 0b00000,
 | 
			
		||||
		.RESERVED0		= 0,
 | 
			
		||||
	},
 | 
			
		||||
} };
 | 
			
		||||
 | 
			
		||||
class MAX2837 {
 | 
			
		||||
public:
 | 
			
		||||
	constexpr MAX2837(
 | 
			
		||||
		spi::arbiter::Target& target
 | 
			
		||||
	) : _target(target)
 | 
			
		||||
	{
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void init();
 | 
			
		||||
	void set_mode(const Mode mode);
 | 
			
		||||
 | 
			
		||||
	void set_tx_vga_gain(const int_fast8_t db);
 | 
			
		||||
	void set_lna_gain(const int_fast8_t db);
 | 
			
		||||
	void set_vga_gain(const int_fast8_t db);
 | 
			
		||||
	void set_lpf_rf_bandwidth(const uint32_t bandwidth_minimum);
 | 
			
		||||
#if 0
 | 
			
		||||
	void rx_cal() {
 | 
			
		||||
		_map.r.spi_en.EN_SPI = 1;
 | 
			
		||||
		_map.r.spi_en.CAL_SPI = 1;
 | 
			
		||||
		flush_one(Register::SPI_EN);
 | 
			
		||||
 | 
			
		||||
		_map.r.vga_3_rx_top.LPF_MODE_SEL = 1;
 | 
			
		||||
		flush_one(Register::VGA_3_RX_TOP);
 | 
			
		||||
 | 
			
		||||
		_map.r.lpf_1.ModeCtrl = 0b00;
 | 
			
		||||
		flush_one(Register::LPF_1);
 | 
			
		||||
 | 
			
		||||
		_map.r.lo_gen.LOGEN_2GM = 1;
 | 
			
		||||
		flush_one(Register::LO_GEN);
 | 
			
		||||
 | 
			
		||||
		chThdSleepMilliseconds(100);
 | 
			
		||||
 | 
			
		||||
		_map.r.spi_en.CAL_SPI = 0;
 | 
			
		||||
		flush_one(Register::SPI_EN);
 | 
			
		||||
 | 
			
		||||
		_map.r.vga_3_rx_top.LPF_MODE_SEL = 0;
 | 
			
		||||
		flush_one(Register::VGA_3_RX_TOP);
 | 
			
		||||
 | 
			
		||||
		_map.r.lpf_1.ModeCtrl = 0b01;
 | 
			
		||||
		flush_one(Register::LPF_1);
 | 
			
		||||
 | 
			
		||||
		_map.r.lo_gen.LOGEN_2GM = 0;
 | 
			
		||||
		flush_one(Register::LO_GEN);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void test_rx_offset(const size_t n) {
 | 
			
		||||
		_map.r.hpfsm_4.HPC_TST = n;
 | 
			
		||||
		_dirty[Register::HPFSM_4] = 1;
 | 
			
		||||
		/*
 | 
			
		||||
		_map.r.hpfsm_3.HPC_STOP = n;
 | 
			
		||||
		_dirty[Register::HPFSM_3] = 1;
 | 
			
		||||
		*/
 | 
			
		||||
		flush();
 | 
			
		||||
	}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	bool set_frequency(const rf::Frequency lo_frequency);
 | 
			
		||||
 | 
			
		||||
	void set_rx_lo_iq_calibration(const size_t v);
 | 
			
		||||
	void set_rx_bias_trim(const size_t v);
 | 
			
		||||
	void set_vco_bias(const size_t v);
 | 
			
		||||
	void set_rx_buff_vcm(const size_t v);
 | 
			
		||||
 | 
			
		||||
	reg_t temp_sense();
 | 
			
		||||
 | 
			
		||||
	reg_t read(const address_t reg_num);
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	spi::arbiter::Target& _target;
 | 
			
		||||
 | 
			
		||||
	RegisterMap _map { initial_register_values };
 | 
			
		||||
	DirtyRegisters<Register, reg_count> _dirty { };
 | 
			
		||||
 | 
			
		||||
	void flush_one(const Register reg);
 | 
			
		||||
 | 
			
		||||
	void write(const address_t reg_num, const reg_t value);
 | 
			
		||||
 | 
			
		||||
	void write(const Register reg, const reg_t value);
 | 
			
		||||
	reg_t read(const Register reg);
 | 
			
		||||
 | 
			
		||||
	void flush();
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif/*__MAX2837_H__*/
 | 
			
		||||
@@ -0,0 +1,36 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
 | 
			
		||||
 *
 | 
			
		||||
 * This file is part of PortaPack.
 | 
			
		||||
 *
 | 
			
		||||
 * 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 "max5864.hpp"
 | 
			
		||||
 | 
			
		||||
#include <cstdint>
 | 
			
		||||
#include <array>
 | 
			
		||||
 | 
			
		||||
#include "utility.hpp"
 | 
			
		||||
 | 
			
		||||
namespace max5864 {
 | 
			
		||||
 | 
			
		||||
void MAX5864::set_mode(const Mode mode) {
 | 
			
		||||
	std::array<uint8_t, 1> command { toUType(mode) };
 | 
			
		||||
	_target.transfer(command.data(), command.size());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,59 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
 | 
			
		||||
 *
 | 
			
		||||
 * This file is part of PortaPack.
 | 
			
		||||
 *
 | 
			
		||||
 * 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 __MAX5864_H__
 | 
			
		||||
#define __MAX5864_H__
 | 
			
		||||
 | 
			
		||||
#include "spi_arbiter.hpp"
 | 
			
		||||
 | 
			
		||||
namespace max5864 {
 | 
			
		||||
 | 
			
		||||
enum class Mode : uint8_t {
 | 
			
		||||
	Shutdown	= 0x00,
 | 
			
		||||
	Idle		= 0x01,
 | 
			
		||||
	Receive		= 0x02,
 | 
			
		||||
	Transmit	= 0x03,
 | 
			
		||||
	Transceiver	= 0x04,
 | 
			
		||||
	Standby		= 0x05,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class MAX5864 {
 | 
			
		||||
public:
 | 
			
		||||
	constexpr MAX5864(
 | 
			
		||||
		spi::arbiter::Target& target
 | 
			
		||||
	) : _target(target)
 | 
			
		||||
	{
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void init() {
 | 
			
		||||
		/* Shut down explicitly, as there is no other reset mechanism. */
 | 
			
		||||
		set_mode(Mode::Shutdown);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void set_mode(const Mode mode);
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	spi::arbiter::Target& _target;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif/*__MAX5864_H__*/
 | 
			
		||||
							
								
								
									
										281
									
								
								Software/portapack-mayhem/firmware/application/hw/rffc507x.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										281
									
								
								Software/portapack-mayhem/firmware/application/hw/rffc507x.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,281 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
 | 
			
		||||
 *
 | 
			
		||||
 * This file is part of PortaPack.
 | 
			
		||||
 *
 | 
			
		||||
 * 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 "rffc507x.hpp"
 | 
			
		||||
 | 
			
		||||
#include <array>
 | 
			
		||||
 | 
			
		||||
#include "utility.hpp"
 | 
			
		||||
 | 
			
		||||
#include "hackrf_hal.hpp"
 | 
			
		||||
#include "hackrf_gpio.hpp"
 | 
			
		||||
using namespace hackrf::one;
 | 
			
		||||
 | 
			
		||||
#include "hal.h"
 | 
			
		||||
 | 
			
		||||
namespace rffc507x {
 | 
			
		||||
 | 
			
		||||
/* Empirical tests indicate no minimum reset pulse width, but the speed
 | 
			
		||||
 * of the processor and GPIO probably produce at least 20ns pulse width.
 | 
			
		||||
 */
 | 
			
		||||
constexpr float seconds_during_reset = 1.0e-6;
 | 
			
		||||
constexpr halrtcnt_t ticks_during_reset = (base_m4_clk_f * seconds_during_reset + 1);
 | 
			
		||||
 | 
			
		||||
/* Empirical testing indicates >3.5us delay required after reset, before
 | 
			
		||||
 * registers can be reliably written. Make it 5us, just for fun. Tests were
 | 
			
		||||
 * conducted at high temperatures (with a hair dryer) increased room
 | 
			
		||||
 * temperature minimum delay of 2.9us to the requirement above.
 | 
			
		||||
 */
 | 
			
		||||
constexpr float seconds_after_reset = 5.0e-6;
 | 
			
		||||
constexpr halrtcnt_t ticks_after_reset = (base_m4_clk_f * seconds_after_reset + 1);
 | 
			
		||||
 | 
			
		||||
constexpr auto reference_frequency = rffc5072_reference_f;
 | 
			
		||||
 | 
			
		||||
namespace vco {
 | 
			
		||||
 | 
			
		||||
constexpr rf::FrequencyRange range { 2700000000, 5400000000 };
 | 
			
		||||
 | 
			
		||||
} /* namespace vco */
 | 
			
		||||
 | 
			
		||||
namespace lo {
 | 
			
		||||
 | 
			
		||||
constexpr size_t divider_log2_min = 0;
 | 
			
		||||
constexpr size_t divider_log2_max = 5;
 | 
			
		||||
 | 
			
		||||
constexpr size_t divider_min = 1U << divider_log2_min;
 | 
			
		||||
constexpr size_t divider_max = 1U << divider_log2_max;
 | 
			
		||||
 | 
			
		||||
constexpr rf::FrequencyRange range { vco::range.minimum / divider_max, vco::range.maximum / divider_min };
 | 
			
		||||
 | 
			
		||||
size_t divider_log2(const rf::Frequency lo_frequency) {
 | 
			
		||||
	/* TODO: Error */
 | 
			
		||||
	/*
 | 
			
		||||
	if( lo::range.out_of_range(lo_frequency) ) {
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
	*/
 | 
			
		||||
	/* Compute LO divider. */
 | 
			
		||||
	auto lo_divider_log2 = lo::divider_log2_min;
 | 
			
		||||
	auto vco_frequency = lo_frequency;
 | 
			
		||||
	while( vco::range.below_range(vco_frequency) ) {
 | 
			
		||||
		vco_frequency <<= 1;
 | 
			
		||||
		lo_divider_log2 += 1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return lo_divider_log2;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} /* namespace lo */
 | 
			
		||||
 | 
			
		||||
namespace prescaler {
 | 
			
		||||
 | 
			
		||||
constexpr rf::Frequency max_frequency = 1600000000U;
 | 
			
		||||
 | 
			
		||||
constexpr size_t divider_log2_min = 1;
 | 
			
		||||
constexpr size_t divider_log2_max = 2;
 | 
			
		||||
 | 
			
		||||
constexpr size_t divider_min = 1U << divider_log2_min;
 | 
			
		||||
constexpr size_t divider_max = 1U << divider_log2_max;
 | 
			
		||||
 | 
			
		||||
constexpr size_t divider_log2(const rf::Frequency vco_frequency) {
 | 
			
		||||
	return (vco_frequency > (prescaler::divider_min * prescaler::max_frequency))
 | 
			
		||||
		? prescaler::divider_log2_max
 | 
			
		||||
		: prescaler::divider_log2_min
 | 
			
		||||
		;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} /* namespace prescaler */
 | 
			
		||||
 | 
			
		||||
struct SynthConfig {
 | 
			
		||||
	const size_t lo_divider_log2;
 | 
			
		||||
	const size_t prescaler_divider_log2;
 | 
			
		||||
	const uint64_t n_divider_q24;
 | 
			
		||||
 | 
			
		||||
	static SynthConfig calculate(
 | 
			
		||||
		const rf::Frequency lo_frequency
 | 
			
		||||
	) {
 | 
			
		||||
		/* RFFC507x frequency synthesizer is is accurate to about 2ppb (two parts
 | 
			
		||||
		 * per BILLION). There's not much point to worrying about rounding and
 | 
			
		||||
		 * tuning error, when it amounts to 8Hz at 5GHz!
 | 
			
		||||
		 */
 | 
			
		||||
		const size_t lo_divider_log2 = lo::divider_log2(lo_frequency);
 | 
			
		||||
		const size_t lo_divider = 1U << lo_divider_log2;
 | 
			
		||||
 | 
			
		||||
		const rf::Frequency vco_frequency = lo_frequency * lo_divider;
 | 
			
		||||
 | 
			
		||||
		const size_t prescaler_divider_log2 = prescaler::divider_log2(vco_frequency);
 | 
			
		||||
 | 
			
		||||
		const uint64_t prescaled_lo_q24 = vco_frequency << (24 - prescaler_divider_log2);
 | 
			
		||||
		const uint64_t n_divider_q24 = prescaled_lo_q24 / reference_frequency;
 | 
			
		||||
 | 
			
		||||
		return {
 | 
			
		||||
			lo_divider_log2,
 | 
			
		||||
			prescaler_divider_log2,
 | 
			
		||||
			n_divider_q24,
 | 
			
		||||
		};
 | 
			
		||||
	}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Readback values, RFFC5072 rev A:
 | 
			
		||||
 * 0000: 0x8a01 => dev_id=1000101000000 mrev_id=001
 | 
			
		||||
 * 0001: 0x3f7c => lock=0 ct_cal=0111111 cp_cal=011111 ctfail=0 0
 | 
			
		||||
 * 0010: 0x806f => v0_cal=10000000 v1_cal=01101111
 | 
			
		||||
 * 0011: 0x0000 => rsm_state=00000 f_errflag=00
 | 
			
		||||
 * 0100: 0x0000 => vco_count_l=0
 | 
			
		||||
 * 0101: 0x0000 => vco_count_h=0
 | 
			
		||||
 * 0110: 0xc000 => cal_fbi=1 cal_fbq=1
 | 
			
		||||
 * 0111: 0x0000 => vco_sel=0 vco_tc_curve=0
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
void RFFC507x::init() {
 | 
			
		||||
	gpio_rffc5072_resetx.set();
 | 
			
		||||
	gpio_rffc5072_resetx.output();
 | 
			
		||||
	reset();
 | 
			
		||||
 | 
			
		||||
	_bus.init();
 | 
			
		||||
 | 
			
		||||
	_dirty.set();
 | 
			
		||||
	flush();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void RFFC507x::reset() {
 | 
			
		||||
	/* TODO: Is RESETB pin ignored if sdi_ctrl.sipin=1? Programming guide
 | 
			
		||||
	 * description of sdi_ctrl.sipin suggests the pin is not ignored.
 | 
			
		||||
	 */
 | 
			
		||||
	gpio_rffc5072_resetx.clear();
 | 
			
		||||
	halPolledDelay(ticks_during_reset);
 | 
			
		||||
	gpio_rffc5072_resetx.set();
 | 
			
		||||
	halPolledDelay(ticks_after_reset);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void RFFC507x::flush() {
 | 
			
		||||
	if( _dirty ) {
 | 
			
		||||
		for(size_t i=0; i<_map.w.size(); i++) {
 | 
			
		||||
			if( _dirty[i] ) {
 | 
			
		||||
				write(i, _map.w[i]);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		_dirty.clear();
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void RFFC507x::write(const address_t reg_num, const spi::reg_t value) {
 | 
			
		||||
	_bus.write(reg_num, value);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
spi::reg_t RFFC507x::read(const address_t reg_num) {
 | 
			
		||||
	return _bus.read(reg_num);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void RFFC507x::write(const Register reg, const spi::reg_t value) {
 | 
			
		||||
	write(toUType(reg), value);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
spi::reg_t RFFC507x::read(const Register reg) {
 | 
			
		||||
	return read(toUType(reg));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void RFFC507x::flush_one(const Register reg) {
 | 
			
		||||
	const auto reg_num = toUType(reg);
 | 
			
		||||
	write(reg_num, _map.w[reg_num]);
 | 
			
		||||
	_dirty.clear(reg_num);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void RFFC507x::enable() {
 | 
			
		||||
	_map.r.sdi_ctrl.enbl = 1;
 | 
			
		||||
	flush_one(Register::SDI_CTRL);
 | 
			
		||||
 | 
			
		||||
	/* TODO: Reset PLLCPL after CT_CAL? */
 | 
			
		||||
 | 
			
		||||
	/* TODO: After device is enabled and CT_cal is complete and VCO > 3.2GHz,
 | 
			
		||||
	 * change prescaler divider to 2, update synthesizer ratio, change
 | 
			
		||||
	 * lf.pllcpl from 3 to 2.
 | 
			
		||||
	 */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void RFFC507x::disable() {
 | 
			
		||||
	_map.r.sdi_ctrl.enbl = 0;
 | 
			
		||||
	flush_one(Register::SDI_CTRL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void RFFC507x::set_mixer_current(const uint8_t value) {
 | 
			
		||||
	/* MIX IDD = 0b000 appears to turn the mixer completely off */
 | 
			
		||||
	/* TODO: Adjust mixer current. Graphs in datasheet suggest:
 | 
			
		||||
	 * MIX_IDD=1 has lowest noise figure (10.1dB vs 13dB @ MIX_IDD=7).
 | 
			
		||||
	 * MIX_IDD=5 has highest IP3 (24dBm vs 10.3dBm @ MIX_IDD=1).
 | 
			
		||||
	 * MIX_IDD=5 has highest P1dB (11.8dBm vs 1.5dBm @ MIX_IDD=1).
 | 
			
		||||
	 * Mixer input impedance ~85 Ohms at MIX_IDD=4.
 | 
			
		||||
	 * Mixer input impedance inversely proportional to MIX_IDD.
 | 
			
		||||
	 * Balun balanced (mixer) side is 100 Ohms. Perhaps reduce MIX_IDD
 | 
			
		||||
	 * a bit to get 100 Ohms from mixer.
 | 
			
		||||
	 */
 | 
			
		||||
	_map.r.mix_cont.p1mixidd = value;
 | 
			
		||||
	_map.r.mix_cont.p2mixidd = value;
 | 
			
		||||
	flush_one(Register::MIX_CONT);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void RFFC507x::set_frequency(const rf::Frequency lo_frequency) {
 | 
			
		||||
	const SynthConfig synth_config = SynthConfig::calculate(lo_frequency);
 | 
			
		||||
 | 
			
		||||
	/* Boost charge pump leakage if VCO frequency > 3.2GHz, indicated by
 | 
			
		||||
	 * prescaler divider set to 4 (log2=2) instead of 2 (log2=1).
 | 
			
		||||
	 */
 | 
			
		||||
	if( synth_config.prescaler_divider_log2 == 2 ) {
 | 
			
		||||
		_map.r.lf.pllcpl = 3;
 | 
			
		||||
	} else {
 | 
			
		||||
		_map.r.lf.pllcpl = 2;
 | 
			
		||||
	}
 | 
			
		||||
	flush_one(Register::LF);
 | 
			
		||||
 | 
			
		||||
	_map.r.p2_freq1.p2n = synth_config.n_divider_q24 >> 24;
 | 
			
		||||
	_map.r.p2_freq1.p2lodiv = synth_config.lo_divider_log2;
 | 
			
		||||
	_map.r.p2_freq1.p2presc = synth_config.prescaler_divider_log2;
 | 
			
		||||
	_map.r.p2_freq2.p2nmsb = (synth_config.n_divider_q24 >> 8) & 0xffff;
 | 
			
		||||
	_map.r.p2_freq3.p2nlsb = synth_config.n_divider_q24 & 0xff;
 | 
			
		||||
	_dirty[Register::P2_FREQ1] = 1;
 | 
			
		||||
	_dirty[Register::P2_FREQ2] = 1;
 | 
			
		||||
	_dirty[Register::P2_FREQ3] = 1;
 | 
			
		||||
	flush();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void RFFC507x::set_gpo1(const bool new_value) {
 | 
			
		||||
	if( new_value ) {
 | 
			
		||||
		_map.r.gpo.p2gpo |= 1;
 | 
			
		||||
		_map.r.gpo.p1gpo |= 1;
 | 
			
		||||
	} else {
 | 
			
		||||
		_map.r.gpo.p2gpo &= ~1;
 | 
			
		||||
		_map.r.gpo.p1gpo &= ~1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	flush_one(Register::GPO);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
spi::reg_t RFFC507x::readback(const Readback readback) {
 | 
			
		||||
	/* TODO: This clobbers the rest of the DEV_CTRL register
 | 
			
		||||
	 * Time to implement bitfields for registers.
 | 
			
		||||
	 */
 | 
			
		||||
	_map.r.dev_ctrl.readsel = toUType(readback);
 | 
			
		||||
	flush_one(Register::DEV_CTRL);
 | 
			
		||||
 | 
			
		||||
	return read(Register::READBACK);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} /* namespace rffc507x */
 | 
			
		||||
							
								
								
									
										835
									
								
								Software/portapack-mayhem/firmware/application/hw/rffc507x.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										835
									
								
								Software/portapack-mayhem/firmware/application/hw/rffc507x.hpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,835 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
 | 
			
		||||
 *
 | 
			
		||||
 * This file is part of PortaPack.
 | 
			
		||||
 *
 | 
			
		||||
 * 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 __RFFC507X_H__
 | 
			
		||||
#define __RFFC507X_H__
 | 
			
		||||
 | 
			
		||||
#include "rffc507x_spi.hpp"
 | 
			
		||||
 | 
			
		||||
#include <cstdint>
 | 
			
		||||
#include <array>
 | 
			
		||||
 | 
			
		||||
#include "dirty_registers.hpp"
 | 
			
		||||
#include "rf_path.hpp"
 | 
			
		||||
 | 
			
		||||
namespace rffc507x {
 | 
			
		||||
 | 
			
		||||
using reg_t = spi::reg_t;
 | 
			
		||||
using address_t = spi::address_t;
 | 
			
		||||
 | 
			
		||||
constexpr size_t reg_count = 31;
 | 
			
		||||
 | 
			
		||||
enum class Register : address_t {
 | 
			
		||||
	LF = 0x00,
 | 
			
		||||
	XO = 0x01,
 | 
			
		||||
	CAL_TIME = 0x02,
 | 
			
		||||
	VCO_CTRL = 0x03,
 | 
			
		||||
	CT_CAL1 = 0x04,
 | 
			
		||||
	CT_CAL2 = 0x05,
 | 
			
		||||
	PLL_CAL1 = 0x06,
 | 
			
		||||
	PLL_CAL2 = 0x07,
 | 
			
		||||
	VCO_AUTO = 0x08,
 | 
			
		||||
	PLL_CTRL = 0x09,
 | 
			
		||||
	PLL_BIAS = 0x0a,
 | 
			
		||||
	MIX_CONT = 0x0b,
 | 
			
		||||
	P1_FREQ1 = 0x0c,
 | 
			
		||||
	P1_FREQ2 = 0x0d,
 | 
			
		||||
	P1_FREQ3 = 0x0e,
 | 
			
		||||
	P2_FREQ1 = 0x0f,
 | 
			
		||||
	P2_FREQ2 = 0x10,
 | 
			
		||||
	P2_FREQ3 = 0x11,
 | 
			
		||||
	FN_CTRL = 0x12,
 | 
			
		||||
	EXT_MOD = 0x13,
 | 
			
		||||
	FMOD = 0x14,
 | 
			
		||||
	SDI_CTRL = 0x15,
 | 
			
		||||
	GPO = 0x16,
 | 
			
		||||
	T_VCO = 0x17,
 | 
			
		||||
	IQMOD1 = 0x18,
 | 
			
		||||
	IQMOD2 = 0x19,
 | 
			
		||||
	IQMOD3 = 0x1a,
 | 
			
		||||
	IQMOD4 = 0x1b,
 | 
			
		||||
	T_CTRL = 0x1c,
 | 
			
		||||
	DEV_CTRL = 0x1d,
 | 
			
		||||
	TEST = 0x1e,
 | 
			
		||||
	READBACK = 0x1f,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum class Readback : uint8_t {
 | 
			
		||||
	DeviceID = 0b0000,
 | 
			
		||||
	TuningCalibration = 0b0001,
 | 
			
		||||
	TuningVoltage = 0b0010,
 | 
			
		||||
	StateMachine = 0b0011,
 | 
			
		||||
	VCOCountL = 0b0100,
 | 
			
		||||
	VCOCountH = 0b0101,
 | 
			
		||||
	DCOffsetCal = 0b0110,
 | 
			
		||||
	VCOMode = 0b0111,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct LF_Type {
 | 
			
		||||
	reg_t pllcpl		: 3;
 | 
			
		||||
	reg_t p1cpdef		: 6;
 | 
			
		||||
	reg_t p2cpdef		: 6;
 | 
			
		||||
	reg_t lfact			: 1;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static_assert(sizeof(LF_Type) == sizeof(reg_t), "LF_Type type wrong size");
 | 
			
		||||
 | 
			
		||||
struct XO_Type {
 | 
			
		||||
	reg_t suwait		: 10;
 | 
			
		||||
	reg_t xocf			: 1;
 | 
			
		||||
	reg_t xoc			: 4;
 | 
			
		||||
	reg_t xoch			: 1;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static_assert(sizeof(XO_Type) == sizeof(reg_t), "XO_Type type wrong size");
 | 
			
		||||
 | 
			
		||||
struct CAL_TIME_Type {
 | 
			
		||||
	reg_t tkv2			: 4;
 | 
			
		||||
	reg_t tkv1			: 4;
 | 
			
		||||
	reg_t reserved0		: 2;
 | 
			
		||||
	reg_t tct			: 5;
 | 
			
		||||
	reg_t wait			: 1;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static_assert(sizeof(CAL_TIME_Type) == sizeof(reg_t), "CAL_TIME_Type type wrong size");
 | 
			
		||||
 | 
			
		||||
struct VCO_CTRL_Type {
 | 
			
		||||
	reg_t reserved0		: 1;
 | 
			
		||||
	reg_t icpup			: 2;
 | 
			
		||||
	reg_t refst			: 1;
 | 
			
		||||
	reg_t xoi3			: 1;
 | 
			
		||||
	reg_t xoi2			: 1;
 | 
			
		||||
	reg_t xoi1			: 1;
 | 
			
		||||
	reg_t kvpol			: 1;
 | 
			
		||||
	reg_t kvrng			: 1;
 | 
			
		||||
	reg_t kvavg			: 2;
 | 
			
		||||
	reg_t clkpl			: 1;
 | 
			
		||||
	reg_t ctpol			: 1;
 | 
			
		||||
	reg_t ctavg			: 2;
 | 
			
		||||
	reg_t xtvco			: 1;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static_assert(sizeof(VCO_CTRL_Type) == sizeof(reg_t), "VCO_CTRL_Type type wrong size");
 | 
			
		||||
 | 
			
		||||
struct CT_CAL1_Type {
 | 
			
		||||
	reg_t p1ctdef		: 7;
 | 
			
		||||
	reg_t p1ct			: 1;
 | 
			
		||||
	reg_t p1ctv			: 5;
 | 
			
		||||
	reg_t p1ctgain		: 3;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static_assert(sizeof(CT_CAL1_Type) == sizeof(reg_t), "CT_CAL1_Type type wrong size");
 | 
			
		||||
 | 
			
		||||
struct CT_CAL2_Type {
 | 
			
		||||
	reg_t p2ctdef		: 7;
 | 
			
		||||
	reg_t p2ct			: 1;
 | 
			
		||||
	reg_t p2ctv			: 5;
 | 
			
		||||
	reg_t p2ctgain		: 3;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static_assert(sizeof(CT_CAL2_Type) == sizeof(reg_t), "CT_CAL2_Type type wrong size");
 | 
			
		||||
 | 
			
		||||
struct PLL_CAL1_Type {
 | 
			
		||||
	reg_t reserved0		: 2;
 | 
			
		||||
	reg_t p1sgn			: 1;
 | 
			
		||||
	reg_t p1kvgain		: 3;
 | 
			
		||||
	reg_t p1dn			: 9;
 | 
			
		||||
	reg_t p1kv			: 1;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static_assert(sizeof(PLL_CAL1_Type) == sizeof(reg_t), "PLL_CAL1_Type type wrong size");
 | 
			
		||||
 | 
			
		||||
struct PLL_CAL2_Type {
 | 
			
		||||
	reg_t reserved0		: 2;
 | 
			
		||||
	reg_t p2sgn			: 1;
 | 
			
		||||
	reg_t p2kvgain		: 3;
 | 
			
		||||
	reg_t p2dn			: 9;
 | 
			
		||||
	reg_t p2kv			: 1;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static_assert(sizeof(PLL_CAL2_Type) == sizeof(reg_t), "PLL_CAL2_Type type wrong size");
 | 
			
		||||
 | 
			
		||||
struct VCO_AUTO_Type {
 | 
			
		||||
	reg_t reserved0		: 1;
 | 
			
		||||
	reg_t ctmin			: 7;
 | 
			
		||||
	reg_t ctmax			: 7;
 | 
			
		||||
	reg_t auto_			: 1;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static_assert(sizeof(VCO_AUTO_Type) == sizeof(reg_t), "VCO_AUTO_Type type wrong size");
 | 
			
		||||
 | 
			
		||||
struct PLL_CTRL_Type {
 | 
			
		||||
	reg_t plldy			: 2;
 | 
			
		||||
	reg_t aloi			: 1;
 | 
			
		||||
	reg_t relok			: 1;
 | 
			
		||||
	reg_t ldlev			: 1;
 | 
			
		||||
	reg_t lden			: 1;
 | 
			
		||||
	reg_t tvco			: 5;
 | 
			
		||||
	reg_t pllst			: 1;
 | 
			
		||||
	reg_t clkdiv		: 3;
 | 
			
		||||
	reg_t divby			: 1;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static_assert(sizeof(PLL_CTRL_Type) == sizeof(reg_t), "PLL_CTRL_Type type wrong size");
 | 
			
		||||
 | 
			
		||||
struct PLL_BIAS_Type {
 | 
			
		||||
	reg_t p2vcoi		: 3;
 | 
			
		||||
	reg_t p2loi			: 4;
 | 
			
		||||
	reg_t reserved0		: 1;
 | 
			
		||||
	reg_t p1vcoi		: 3;
 | 
			
		||||
	reg_t p1loi			: 4;
 | 
			
		||||
	reg_t reserved1		: 1;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static_assert(sizeof(PLL_BIAS_Type) == sizeof(reg_t), "PLL_BIAS_Type type wrong size");
 | 
			
		||||
 | 
			
		||||
struct MIX_CONT_Type {
 | 
			
		||||
	reg_t reserved0		: 9;
 | 
			
		||||
	reg_t p2mixidd		: 3;
 | 
			
		||||
	reg_t p1mixidd		: 3;
 | 
			
		||||
	reg_t fulld			: 1;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static_assert(sizeof(MIX_CONT_Type) == sizeof(reg_t), "MIX_CONT_Type type wrong size");
 | 
			
		||||
 | 
			
		||||
struct P1_FREQ1_Type {
 | 
			
		||||
	reg_t p1vcosel		: 2;
 | 
			
		||||
	reg_t p1presc		: 2;
 | 
			
		||||
	reg_t p1lodiv		: 3;
 | 
			
		||||
	reg_t p1n			: 9;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static_assert(sizeof(P1_FREQ1_Type) == sizeof(reg_t), "P1_FREQ1_Type type wrong size");
 | 
			
		||||
 | 
			
		||||
struct P1_FREQ2_Type {
 | 
			
		||||
	reg_t p1nmsb		: 16;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static_assert(sizeof(P1_FREQ2_Type) == sizeof(reg_t), "P1_FREQ2_Type type wrong size");
 | 
			
		||||
 | 
			
		||||
struct P1_FREQ3_Type {
 | 
			
		||||
	reg_t reserved0		: 8;
 | 
			
		||||
	reg_t p1nlsb		: 8;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static_assert(sizeof(P1_FREQ3_Type) == sizeof(reg_t), "P1_FREQ3_Type type wrong size");
 | 
			
		||||
 | 
			
		||||
struct P2_FREQ1_Type {
 | 
			
		||||
	reg_t p2vcosel		: 2;
 | 
			
		||||
	reg_t p2presc		: 2;
 | 
			
		||||
	reg_t p2lodiv		: 3;
 | 
			
		||||
	reg_t p2n			: 9;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static_assert(sizeof(P2_FREQ1_Type) == sizeof(reg_t), "P2_FREQ1_Type type wrong size");
 | 
			
		||||
 | 
			
		||||
struct P2_FREQ2_Type {
 | 
			
		||||
	reg_t p2nmsb		: 16;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static_assert(sizeof(P2_FREQ2_Type) == sizeof(reg_t), "P2_FREQ2_Type type wrong size");
 | 
			
		||||
 | 
			
		||||
struct P2_FREQ3_Type {
 | 
			
		||||
	reg_t reserved0		: 8;
 | 
			
		||||
	reg_t p2nlsb		: 8;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static_assert(sizeof(P2_FREQ3_Type) == sizeof(reg_t), "P2_FREQ3_Type type wrong size");
 | 
			
		||||
 | 
			
		||||
struct FN_CTRL_Type {
 | 
			
		||||
	reg_t reserved0		: 1;
 | 
			
		||||
	reg_t tzps			: 1;
 | 
			
		||||
	reg_t dmode			: 1;
 | 
			
		||||
	reg_t fm			: 1;
 | 
			
		||||
	reg_t dith			: 1;
 | 
			
		||||
	reg_t mode			: 1;
 | 
			
		||||
	reg_t phsalndly		: 2;
 | 
			
		||||
	reg_t phsalngain	: 3;
 | 
			
		||||
	reg_t phaln			: 1;
 | 
			
		||||
	reg_t sdm			: 2;
 | 
			
		||||
	reg_t dithr			: 1;
 | 
			
		||||
	reg_t fnz			: 1;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static_assert(sizeof(FN_CTRL_Type) == sizeof(reg_t), "FN_CTRL_Type type wrong size");
 | 
			
		||||
 | 
			
		||||
struct EXT_MOD_Type {
 | 
			
		||||
	reg_t reserved0		: 10;
 | 
			
		||||
	reg_t modstep		: 4;
 | 
			
		||||
	reg_t modsetup		: 2;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static_assert(sizeof(EXT_MOD_Type) == sizeof(reg_t), "EXT_MOD_Type type wrong size");
 | 
			
		||||
 | 
			
		||||
struct FMOD_Type {
 | 
			
		||||
	reg_t modulation	: 16;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static_assert(sizeof(FMOD_Type) == sizeof(reg_t), "FMOD_Type type wrong size");
 | 
			
		||||
 | 
			
		||||
struct SDI_CTRL_Type {
 | 
			
		||||
	reg_t reserved0		: 1;
 | 
			
		||||
	reg_t reset			: 1;
 | 
			
		||||
	reg_t reserved1		: 9;
 | 
			
		||||
	reg_t addr			: 1;
 | 
			
		||||
	reg_t fourwire		: 1;
 | 
			
		||||
	reg_t mode			: 1;
 | 
			
		||||
	reg_t enbl			: 1;
 | 
			
		||||
	reg_t sipin			: 1;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static_assert(sizeof(SDI_CTRL_Type) == sizeof(reg_t), "SDI_CTRL_Type type wrong size");
 | 
			
		||||
 | 
			
		||||
struct GPO_Type {
 | 
			
		||||
	reg_t lock			: 1;
 | 
			
		||||
	reg_t gate			: 1;
 | 
			
		||||
	reg_t p1gpo			: 7;
 | 
			
		||||
	reg_t p2gpo			: 7;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static_assert(sizeof(GPO_Type) == sizeof(reg_t), "GPO_Type type wrong size");
 | 
			
		||||
 | 
			
		||||
struct T_VCO_Type {
 | 
			
		||||
	reg_t reserved0		: 7;
 | 
			
		||||
	reg_t curve_vco3	: 3;
 | 
			
		||||
	reg_t curve_vco2	: 3;
 | 
			
		||||
	reg_t curve_vco1	: 3;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static_assert(sizeof(T_VCO_Type) == sizeof(reg_t), "T_VCO_Type type wrong size");
 | 
			
		||||
 | 
			
		||||
struct IQMOD1_Type {
 | 
			
		||||
	reg_t bufdc			: 2;
 | 
			
		||||
	reg_t divbias		: 1;
 | 
			
		||||
	reg_t calblk		: 1;
 | 
			
		||||
	reg_t calnul		: 1;
 | 
			
		||||
	reg_t calon			: 1;
 | 
			
		||||
	reg_t lobias		: 2;
 | 
			
		||||
	reg_t modbias		: 3;
 | 
			
		||||
	/* Also defined as ctrl : 5 */
 | 
			
		||||
	reg_t modiv			: 1;
 | 
			
		||||
	reg_t mod			: 1;
 | 
			
		||||
	reg_t txlo			: 1;
 | 
			
		||||
	reg_t bbgm			: 1;
 | 
			
		||||
	reg_t ctrl			: 1;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static_assert(sizeof(IQMOD1_Type) == sizeof(reg_t), "IQMOD1_Type type wrong size");
 | 
			
		||||
 | 
			
		||||
struct IQMOD2_Type {
 | 
			
		||||
	reg_t modbuf		: 2;
 | 
			
		||||
	reg_t mod			: 2;
 | 
			
		||||
	reg_t calatten		: 2;
 | 
			
		||||
	reg_t rctune		: 6;
 | 
			
		||||
	reg_t bbatten		: 4;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static_assert(sizeof(IQMOD2_Type) == sizeof(reg_t), "IQMOD2_Type type wrong size");
 | 
			
		||||
 | 
			
		||||
struct IQMOD3_Type {
 | 
			
		||||
	/* Documentation error */
 | 
			
		||||
	reg_t reserved0		: 3;
 | 
			
		||||
	reg_t dacen			: 1;
 | 
			
		||||
	reg_t bufdacq		: 6;
 | 
			
		||||
	reg_t bufdaci		: 6;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static_assert(sizeof(IQMOD3_Type) == sizeof(reg_t), "IQMOD3_Type type wrong size");
 | 
			
		||||
 | 
			
		||||
struct IQMOD4_Type {
 | 
			
		||||
	/* Documentation error */
 | 
			
		||||
	reg_t bufbias2		: 2;
 | 
			
		||||
	reg_t bufbias1		: 2;
 | 
			
		||||
	reg_t moddacq		: 6;
 | 
			
		||||
	reg_t moddaci		: 6;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static_assert(sizeof(IQMOD4_Type) == sizeof(reg_t), "IQMOD4_Type type wrong size");
 | 
			
		||||
 | 
			
		||||
struct T_CTRL_Type {
 | 
			
		||||
	reg_t reserved0		: 5;
 | 
			
		||||
	reg_t v_test		: 1;
 | 
			
		||||
	reg_t ldo_by		: 1;
 | 
			
		||||
	reg_t ext_filt		: 1;
 | 
			
		||||
	reg_t ref_sel		: 1;
 | 
			
		||||
	reg_t filt_ctrl		: 2;
 | 
			
		||||
	reg_t fc_en			: 1;
 | 
			
		||||
	reg_t tbl_sel		: 2;
 | 
			
		||||
	reg_t tc_en			: 2;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static_assert(sizeof(T_CTRL_Type) == sizeof(reg_t), "T_CTRL_Type type wrong size");
 | 
			
		||||
 | 
			
		||||
struct DEV_CTRL_Type {
 | 
			
		||||
	reg_t reserved0		: 1;
 | 
			
		||||
	reg_t bypas			: 1;
 | 
			
		||||
	reg_t ctclk			: 1;
 | 
			
		||||
	reg_t dac			: 1;
 | 
			
		||||
	reg_t cpd			: 1;
 | 
			
		||||
	reg_t cpu			: 1;
 | 
			
		||||
	reg_t rsmstopst		: 5;
 | 
			
		||||
	reg_t rsmst			: 1;
 | 
			
		||||
	reg_t readsel		: 4;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static_assert(sizeof(DEV_CTRL_Type) == sizeof(reg_t), "DEV_CTRL_Type type wrong size");
 | 
			
		||||
 | 
			
		||||
struct TEST_Type {
 | 
			
		||||
	reg_t lfsrd			: 1;
 | 
			
		||||
	reg_t rcbyp			: 1;
 | 
			
		||||
	reg_t rgbyp			: 1;
 | 
			
		||||
	reg_t lfsrt			: 1;
 | 
			
		||||
	reg_t lfsrgatetime	: 4;
 | 
			
		||||
	reg_t lfsrp			: 1;
 | 
			
		||||
	reg_t lfsr			: 1;
 | 
			
		||||
	reg_t tsel			: 2;
 | 
			
		||||
	reg_t tmux			: 3;
 | 
			
		||||
	reg_t ten			: 1;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static_assert(sizeof(TEST_Type) == sizeof(reg_t), "TEST_Type type wrong size");
 | 
			
		||||
 | 
			
		||||
struct READBACK_0000_Type {
 | 
			
		||||
	reg_t mrev_id		: 3;
 | 
			
		||||
	reg_t dev_id		: 13;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static_assert(sizeof(READBACK_0000_Type) == sizeof(reg_t), "READBACK_0000_Type type wrong size");
 | 
			
		||||
 | 
			
		||||
struct READBACK_0001_Type {
 | 
			
		||||
	reg_t reserved0		: 1;
 | 
			
		||||
	reg_t ctfail		: 1;
 | 
			
		||||
	reg_t cp_cal		: 6;
 | 
			
		||||
	reg_t ct_cal		: 7;
 | 
			
		||||
	reg_t lock			: 1;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static_assert(sizeof(READBACK_0001_Type) == sizeof(reg_t), "READBACK_0001_Type type wrong size");
 | 
			
		||||
 | 
			
		||||
struct READBACK_0010_Type {
 | 
			
		||||
	reg_t v1_cal		: 8;
 | 
			
		||||
	reg_t v0_cal		: 8;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static_assert(sizeof(READBACK_0010_Type) == sizeof(reg_t), "READBACK_0010_Type type wrong size");
 | 
			
		||||
 | 
			
		||||
struct READBACK_0011_Type {
 | 
			
		||||
	reg_t reserved0		: 9;
 | 
			
		||||
	reg_t f_errflag		: 2;
 | 
			
		||||
	reg_t rsm_state		: 5;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static_assert(sizeof(READBACK_0011_Type) == sizeof(reg_t), "READBACK_0011_Type type wrong size");
 | 
			
		||||
 | 
			
		||||
struct READBACK_0100_Type {
 | 
			
		||||
	reg_t vco_count_l	: 16;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static_assert(sizeof(READBACK_0100_Type) == sizeof(reg_t), "READBACK_0100_Type type wrong size");
 | 
			
		||||
 | 
			
		||||
struct READBACK_0101_Type {
 | 
			
		||||
	reg_t vco_count_h	: 16;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static_assert(sizeof(READBACK_0101_Type) == sizeof(reg_t), "READBACK_0101_Type type wrong size");
 | 
			
		||||
 | 
			
		||||
struct READBACK_0110_Type {
 | 
			
		||||
	reg_t reserved0		: 14;
 | 
			
		||||
	reg_t cal_fbq		: 1;
 | 
			
		||||
	reg_t cal_fbi		: 1;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static_assert(sizeof(READBACK_0110_Type) == sizeof(reg_t), "READBACK_0110_Type type wrong size");
 | 
			
		||||
 | 
			
		||||
struct READBACK_0111_Type {
 | 
			
		||||
	reg_t reserved0		: 11;
 | 
			
		||||
	reg_t vco_tc_curve	: 3;
 | 
			
		||||
	reg_t vco_sel		: 2;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static_assert(sizeof(READBACK_0111_Type) == sizeof(reg_t), "READBACK_0111_Type type wrong size");
 | 
			
		||||
 | 
			
		||||
struct Register_Type {
 | 
			
		||||
	LF_Type			lf;
 | 
			
		||||
	XO_Type			xo;
 | 
			
		||||
	CAL_TIME_Type	cal_time;
 | 
			
		||||
	VCO_CTRL_Type	vco_ctrl;
 | 
			
		||||
	CT_CAL1_Type	ct_cal1;
 | 
			
		||||
	CT_CAL2_Type	ct_cal2;
 | 
			
		||||
	PLL_CAL1_Type	pll_cal1;
 | 
			
		||||
	PLL_CAL2_Type	pll_cal2;
 | 
			
		||||
	VCO_AUTO_Type	vco_auto;
 | 
			
		||||
	PLL_CTRL_Type	pll_ctrl;
 | 
			
		||||
	PLL_BIAS_Type	pll_bias;
 | 
			
		||||
	MIX_CONT_Type	mix_cont;
 | 
			
		||||
	P1_FREQ1_Type	p1_freq1;
 | 
			
		||||
	P1_FREQ2_Type	p1_freq2;
 | 
			
		||||
	P1_FREQ3_Type	p1_freq3;
 | 
			
		||||
	P2_FREQ1_Type	p2_freq1;
 | 
			
		||||
	P2_FREQ2_Type	p2_freq2;
 | 
			
		||||
	P2_FREQ3_Type	p2_freq3;
 | 
			
		||||
	FN_CTRL_Type	fn_ctrl;
 | 
			
		||||
	EXT_MOD_Type	ext_mod;
 | 
			
		||||
	FMOD_Type		fmod;
 | 
			
		||||
	SDI_CTRL_Type	sdi_ctrl;
 | 
			
		||||
	GPO_Type		gpo;
 | 
			
		||||
	T_VCO_Type		t_vco;
 | 
			
		||||
	IQMOD1_Type		iqmod1;
 | 
			
		||||
	IQMOD2_Type		iqmod2;
 | 
			
		||||
	IQMOD3_Type		iqmod3;
 | 
			
		||||
	IQMOD4_Type		iqmod4;
 | 
			
		||||
	T_CTRL_Type		t_ctrl;
 | 
			
		||||
	DEV_CTRL_Type	dev_ctrl;
 | 
			
		||||
	TEST_Type		test;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static_assert(sizeof(Register_Type) == reg_count * sizeof(reg_t), "Register_Type wrong size");
 | 
			
		||||
 | 
			
		||||
struct RegisterMap {
 | 
			
		||||
	constexpr RegisterMap(
 | 
			
		||||
		Register_Type values
 | 
			
		||||
	) : r(values)
 | 
			
		||||
	{
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	union {
 | 
			
		||||
		Register_Type r;
 | 
			
		||||
		std::array<reg_t, reg_count> w;
 | 
			
		||||
	};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static_assert(sizeof(RegisterMap) == reg_count * sizeof(reg_t), "RegisterMap type wrong size");
 | 
			
		||||
#if 0
 | 
			
		||||
struct ReadbackType {
 | 
			
		||||
	union {
 | 
			
		||||
		READBACK_0000_Type	readback_0000;
 | 
			
		||||
		READBACK_0001_Type	readback_0001;
 | 
			
		||||
		READBACK_0010_Type	readback_0010;
 | 
			
		||||
		READBACK_0011_Type	readback_0011;
 | 
			
		||||
		READBACK_0100_Type	readback_0100;
 | 
			
		||||
		READBACK_0101_Type	readback_0101;
 | 
			
		||||
		READBACK_0110_Type	readback_0110;
 | 
			
		||||
		READBACK_0111_Type	readback_0111;
 | 
			
		||||
		reg_t w;
 | 
			
		||||
	};
 | 
			
		||||
};
 | 
			
		||||
#endif
 | 
			
		||||
#if 0
 | 
			
		||||
/* Revision 1 devices (mrev_id = 001):
 | 
			
		||||
 * RFFC2071/2072/5071/5072, RFMD2080/2081
 | 
			
		||||
 */
 | 
			
		||||
constexpr RegisterMap default_revision_1 { std::array<reg_t, reg_count> {
 | 
			
		||||
  0xbefa, 0x4064, 0x9055, 0x2d02,
 | 
			
		||||
  0xb0bf, 0xb0bf, 0x0028, 0x0028,
 | 
			
		||||
  0xfc06, 0x8220, 0x0202, 0x4800,
 | 
			
		||||
  0x2324, 0x6276, 0x2700, 0x2f16,
 | 
			
		||||
  0x3b13, 0xb100, 0x2a80, 0x0000,
 | 
			
		||||
  0x0000, 0x0000, 0x0000, 0x0000,
 | 
			
		||||
  0x0283, 0xf00f, 0x0000, 0x000f,
 | 
			
		||||
  0x0000, 0x1000, 0x0001,
 | 
			
		||||
} };
 | 
			
		||||
 | 
			
		||||
/* Revision 2 devices (mrev_id = 010):
 | 
			
		||||
 * RFFC2071A/2072A/5071A/5072A
 | 
			
		||||
 */
 | 
			
		||||
constexpr RegisterMap default_revision_2 { std::array<reg_t, reg_count> {
 | 
			
		||||
  0xbefa, 0x4064, 0x9055, 0x2d02,
 | 
			
		||||
  0xacbf, 0xacbf, 0x0028, 0x0028,
 | 
			
		||||
  0xff00, 0x8220, 0x0202, 0x4800,
 | 
			
		||||
  0x1a94, 0xd89d, 0x8900, 0x1e84,
 | 
			
		||||
  0x89d8, 0x9d00, 0x2a80, 0x0000,
 | 
			
		||||
  0x0000, 0x0000, 0x0000, 0x4900,
 | 
			
		||||
  0x0281, 0xf00f, 0x0000, 0x0005,
 | 
			
		||||
  0xc840, 0x1000, 0x0005,
 | 
			
		||||
} };
 | 
			
		||||
#endif
 | 
			
		||||
constexpr RegisterMap default_hackrf_one { Register_Type {
 | 
			
		||||
	/* Started with recommended defaults for revision 1 devices
 | 
			
		||||
	 * (mrev_id = 001), RFFC2071/2072/5071/5072, RFMD2080/2081.
 | 
			
		||||
	 * Modified according to mixer programming guide.
 | 
			
		||||
	 */
 | 
			
		||||
	.lf = {		/* 0 */
 | 
			
		||||
		.pllcpl		= 0b010,
 | 
			
		||||
		.p1cpdef	= 0b011111,
 | 
			
		||||
		.p2cpdef	= 0b011111,
 | 
			
		||||
		.lfact		= 1,
 | 
			
		||||
	},
 | 
			
		||||
	.xo = {		/* 1 */
 | 
			
		||||
		.suwait		= 0b0001100100,
 | 
			
		||||
		.xocf		= 0b0,
 | 
			
		||||
		.xoc		= 0b1000,
 | 
			
		||||
		.xoch		= 0b0,
 | 
			
		||||
	},
 | 
			
		||||
	.cal_time = {	/* 2 */
 | 
			
		||||
		.tkv2		= 0b0101,
 | 
			
		||||
		.tkv1		= 0b0101,
 | 
			
		||||
		.reserved0	= 0b00,
 | 
			
		||||
		.tct		= 0b00100,
 | 
			
		||||
		.wait		= 0b1,
 | 
			
		||||
	},
 | 
			
		||||
	.vco_ctrl = {	/* 3 */
 | 
			
		||||
		.reserved0	= 0b0,
 | 
			
		||||
		.icpup		= 0b01,
 | 
			
		||||
		.refst		= 0b0,
 | 
			
		||||
		.xoi3		= 0b0,
 | 
			
		||||
		.xoi2		= 0b0,
 | 
			
		||||
		.xoi1		= 0b0,
 | 
			
		||||
		.kvpol		= 0b0,
 | 
			
		||||
		.kvrng		= 0b1,
 | 
			
		||||
		.kvavg		= 0b10,
 | 
			
		||||
		.clkpl		= 0b1,
 | 
			
		||||
		.ctpol		= 0b0,
 | 
			
		||||
		.ctavg		= 0b01,
 | 
			
		||||
		.xtvco		= 0b0,
 | 
			
		||||
	},
 | 
			
		||||
	.ct_cal1 = {	/* 4 */
 | 
			
		||||
		.p1ctdef	= 0b0111111,
 | 
			
		||||
		.p1ct		= 0b1,
 | 
			
		||||
		.p1ctv		= 12,			/* RFMD recommneded change: 16 -> 12 */
 | 
			
		||||
		.p1ctgain	= 0b101,
 | 
			
		||||
	},
 | 
			
		||||
	.ct_cal2 = {	/* 5 */
 | 
			
		||||
		.p2ctdef	= 0b0111111,
 | 
			
		||||
		.p2ct		= 0b1,
 | 
			
		||||
		.p2ctv		= 12,			/* RFMD recommneded change: 16 -> 12 */
 | 
			
		||||
		.p2ctgain	= 0b101,
 | 
			
		||||
	},
 | 
			
		||||
	.pll_cal1 = {	/* 6 */
 | 
			
		||||
		.reserved0	= 0b00,
 | 
			
		||||
		.p1sgn		= 0b0,
 | 
			
		||||
		.p1kvgain	= 0b101,
 | 
			
		||||
		.p1dn		= 0b000000000,
 | 
			
		||||
		.p1kv		= 0b0,
 | 
			
		||||
	},
 | 
			
		||||
	.pll_cal2 = {	/* 7 */
 | 
			
		||||
		.reserved0	= 0b00,
 | 
			
		||||
		.p2sgn		= 0b0,
 | 
			
		||||
		.p2kvgain	= 0b101,
 | 
			
		||||
		.p2dn		= 0b000000000,
 | 
			
		||||
		.p2kv		= 0b0,
 | 
			
		||||
	},
 | 
			
		||||
	.vco_auto = {	/* 8 */
 | 
			
		||||
		.reserved0	= 0b0,
 | 
			
		||||
		.ctmin		= 0,			/* RFMD recommended change:   3 ->   0 */
 | 
			
		||||
		.ctmax		= 127,			/* RFMD recommended change: 124 -> 127 */
 | 
			
		||||
		.auto_		= 0b1,
 | 
			
		||||
	},
 | 
			
		||||
	.pll_ctrl = {	/* 9 */
 | 
			
		||||
		.plldy		= 0b00,
 | 
			
		||||
		.aloi		= 0b0,
 | 
			
		||||
		.relok		= 0b0,
 | 
			
		||||
		.ldlev		= 0b0,
 | 
			
		||||
		.lden		= 0b1,
 | 
			
		||||
		.tvco		= 0b01000,
 | 
			
		||||
		.pllst		= 0b0,
 | 
			
		||||
		.clkdiv		= 0b000,
 | 
			
		||||
		.divby		= 0b1,
 | 
			
		||||
	},
 | 
			
		||||
	.pll_bias = {	/* 10 */
 | 
			
		||||
		.p2vcoi		= 0b010,
 | 
			
		||||
		.p2loi		= 0b0000,
 | 
			
		||||
		.reserved0	= 0b0,
 | 
			
		||||
		.p1vcoi		= 0b010,
 | 
			
		||||
		.p1loi		= 0b0000,
 | 
			
		||||
		.reserved1	= 0b0,
 | 
			
		||||
	},
 | 
			
		||||
	.mix_cont = {	/* 11 */
 | 
			
		||||
		.reserved0	= 0b000000000,
 | 
			
		||||
		.p2mixidd	= 4,
 | 
			
		||||
		.p1mixidd	= 4,
 | 
			
		||||
		.fulld		= 0b0,			/* Part on HackRF is half-duplex (single mixer) */
 | 
			
		||||
	},
 | 
			
		||||
	.p1_freq1 = {	/* 12 */
 | 
			
		||||
		.p1vcosel	= 0b00,			/* RFMD VCO bank 1 configuration from A series part */
 | 
			
		||||
		.p1presc	= 0b01,
 | 
			
		||||
		.p1lodiv	= 0b001,
 | 
			
		||||
		.p1n		= 0b000110101,
 | 
			
		||||
	},
 | 
			
		||||
	.p1_freq2 = {	/* 13 */
 | 
			
		||||
		.p1nmsb		= 0xd89d,		/* RFMD VCO bank 1 configuration from A series part */
 | 
			
		||||
	},
 | 
			
		||||
	.p1_freq3 = {	/* 14 */
 | 
			
		||||
		.reserved0	= 0b00000000,	/* RFMD VCO bank 1 configuration from A series part */
 | 
			
		||||
		.p1nlsb		= 0x89,
 | 
			
		||||
	},
 | 
			
		||||
	.p2_freq1 = {	/* 15 */
 | 
			
		||||
		.p2vcosel	= 0b00,			/* RFMD VCO bank 2 configuration from A series part */
 | 
			
		||||
		.p2presc	= 0b01,
 | 
			
		||||
		.p2lodiv	= 0b000,
 | 
			
		||||
		.p2n		= 0b000111101,
 | 
			
		||||
	},
 | 
			
		||||
	.p2_freq2 = {	/* 16 */
 | 
			
		||||
		.p2nmsb		= 0x89d8,		/* RFMD VCO bank 2 configuration from A series part */
 | 
			
		||||
	},
 | 
			
		||||
	.p2_freq3 = {	/* 17 */
 | 
			
		||||
		.reserved0	= 0b00000000,	/* RFMD VCO bank 2 configuration from A series part */
 | 
			
		||||
		.p2nlsb		= 0x9d,
 | 
			
		||||
	},
 | 
			
		||||
	.fn_ctrl = {	/* 18 */
 | 
			
		||||
		.reserved0	= 0b0,
 | 
			
		||||
		.tzps		= 0b0,
 | 
			
		||||
		.dmode		= 0b0,
 | 
			
		||||
		.fm			= 0b0,
 | 
			
		||||
		.dith		= 0b0,
 | 
			
		||||
		.mode		= 0b0,
 | 
			
		||||
		.phsalndly	= 0b10,
 | 
			
		||||
		.phsalngain	= 0b010,
 | 
			
		||||
		.phaln		= 0b1,
 | 
			
		||||
		.sdm		= 0b10,
 | 
			
		||||
		.dithr		= 0b0,
 | 
			
		||||
		.fnz		= 0b0,
 | 
			
		||||
	},
 | 
			
		||||
	.ext_mod = {	/* 19 */
 | 
			
		||||
		.reserved0	= 0b0000000000,
 | 
			
		||||
		.modstep	= 0b0000,
 | 
			
		||||
		.modsetup	= 0b00,
 | 
			
		||||
	},
 | 
			
		||||
	.fmod = {		/* 20 */
 | 
			
		||||
		.modulation	= 0x0000,
 | 
			
		||||
	},
 | 
			
		||||
	.sdi_ctrl = {	/* 21 */
 | 
			
		||||
		.reserved0	= 0b0,
 | 
			
		||||
		.reset		= 0b0,
 | 
			
		||||
		.reserved1	= 0b000000000,
 | 
			
		||||
		.addr		= 0b0,
 | 
			
		||||
		.fourwire	= 0b0,			/* Use three pin SPI mode */
 | 
			
		||||
		.mode		= 0b1,			/* Active PLL register bank 2, active mixer 2 */
 | 
			
		||||
		.enbl		= 0b0,			/* Part is initially disabled */
 | 
			
		||||
		.sipin		= 0b1,			/* Control MODE, ENBL from SPI bus */
 | 
			
		||||
	},
 | 
			
		||||
	.gpo = {		/* 22 */
 | 
			
		||||
		.lock		= 0b1,			/* Present LOCK signal on GPIO4/LD/DO, HackRF One test point P6 */
 | 
			
		||||
		.gate		= 0b1,			/* GPOs active even when part is disabled (ENBL=0) */
 | 
			
		||||
		.p1gpo		= 0b0000001,	/* Turn on GPO1 to turn *off* !ANT_BIAS (GPO numbering is one-based) */
 | 
			
		||||
		.p2gpo		= 0b0000001,	/* Turn on GPO1 to turn *off* !ANT_BIAS (GPO numbering is one-based) */
 | 
			
		||||
	},
 | 
			
		||||
	.t_vco = {	/* 23 */
 | 
			
		||||
		.reserved0	= 0b0000000,
 | 
			
		||||
		.curve_vco3	= 0b000,
 | 
			
		||||
		.curve_vco2	= 0b000,
 | 
			
		||||
		.curve_vco1	= 0b000,
 | 
			
		||||
	},
 | 
			
		||||
	.iqmod1 = {	/* 24 */
 | 
			
		||||
		.bufdc		= 0b11,
 | 
			
		||||
		.divbias	= 0b0,
 | 
			
		||||
		.calblk		= 0b0,
 | 
			
		||||
		.calnul		= 0b0,
 | 
			
		||||
		.calon		= 0b0,
 | 
			
		||||
		.lobias		= 0b10,
 | 
			
		||||
		.modbias	= 0b010,
 | 
			
		||||
		.modiv		= 0b0,
 | 
			
		||||
		.mod		= 0b0,
 | 
			
		||||
		.txlo		= 0b0,
 | 
			
		||||
		.bbgm		= 0b0,
 | 
			
		||||
		.ctrl		= 0b0,
 | 
			
		||||
	},
 | 
			
		||||
	.iqmod2 = {	/* 25 */
 | 
			
		||||
		.modbuf		= 0b11,
 | 
			
		||||
		.mod		= 0b11,
 | 
			
		||||
		.calatten	= 0b00,
 | 
			
		||||
		.rctune		= 0b000000,
 | 
			
		||||
		.bbatten	= 0b1111,
 | 
			
		||||
	},
 | 
			
		||||
	.iqmod3 = {	/* 26 */
 | 
			
		||||
		.reserved0	= 0b000,
 | 
			
		||||
		.dacen		= 0b0,
 | 
			
		||||
		.bufdacq	= 0b000000,
 | 
			
		||||
		.bufdaci	= 0b000000,
 | 
			
		||||
	},
 | 
			
		||||
	.iqmod4 = {	/* 27 */
 | 
			
		||||
		.bufbias2	= 0b11,
 | 
			
		||||
		.bufbias1	= 0b11,
 | 
			
		||||
		.moddacq	= 0b000000,
 | 
			
		||||
		.moddaci	= 0b000000,
 | 
			
		||||
	},
 | 
			
		||||
	.t_ctrl = {	/* 28 */
 | 
			
		||||
		.reserved0	= 0b00000,
 | 
			
		||||
		.v_test		= 0b0,
 | 
			
		||||
		.ldo_by		= 0b0,
 | 
			
		||||
		.ext_filt	= 0b0,
 | 
			
		||||
		.ref_sel	= 0b0,
 | 
			
		||||
		.filt_ctrl	= 0b00,
 | 
			
		||||
		.fc_en		= 0b0,
 | 
			
		||||
		.tbl_sel	= 0b00,
 | 
			
		||||
		.tc_en		= 0b00,
 | 
			
		||||
	},
 | 
			
		||||
	.dev_ctrl = {	/* 29 */
 | 
			
		||||
		.reserved0	= 0b0,
 | 
			
		||||
		.bypas		= 0b0,
 | 
			
		||||
		.ctclk		= 0b0,
 | 
			
		||||
		.dac		= 0b0,
 | 
			
		||||
		.cpd		= 0b0,
 | 
			
		||||
		.cpu		= 0b0,
 | 
			
		||||
		.rsmstopst	= 0b00000,
 | 
			
		||||
		.rsmst		= 0b0,
 | 
			
		||||
		.readsel	= 0b0001,
 | 
			
		||||
	},
 | 
			
		||||
	.test = {		/* 30 */
 | 
			
		||||
		.lfsrd			= 0b1,
 | 
			
		||||
		.rcbyp			= 0b0,
 | 
			
		||||
		.rgbyp			= 0b1,		/* RFMD recommended change: 0 -> 1 */
 | 
			
		||||
		.lfsrt			= 0b0,
 | 
			
		||||
		.lfsrgatetime	= 0b0000,
 | 
			
		||||
		.lfsrp			= 0b0,
 | 
			
		||||
		.lfsr			= 0b0,
 | 
			
		||||
		.tsel			= 0b00,
 | 
			
		||||
		.tmux			= 0b000,
 | 
			
		||||
		.ten			= 0b0,
 | 
			
		||||
	},
 | 
			
		||||
} };
 | 
			
		||||
 | 
			
		||||
class RFFC507x {
 | 
			
		||||
public:
 | 
			
		||||
	void init();
 | 
			
		||||
	void reset();
 | 
			
		||||
 | 
			
		||||
	void flush();
 | 
			
		||||
 | 
			
		||||
	void enable();
 | 
			
		||||
	void disable();
 | 
			
		||||
 | 
			
		||||
	void set_mixer_current(const uint8_t value);
 | 
			
		||||
	void set_frequency(const rf::Frequency lo_frequency);
 | 
			
		||||
	void set_gpo1(const bool new_value);
 | 
			
		||||
	
 | 
			
		||||
	reg_t read(const address_t reg_num);
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	spi::SPI _bus { };
 | 
			
		||||
 | 
			
		||||
	RegisterMap _map { default_hackrf_one };
 | 
			
		||||
	DirtyRegisters<Register, reg_count> _dirty { };
 | 
			
		||||
 | 
			
		||||
	void write(const address_t reg_num, const reg_t value);
 | 
			
		||||
 | 
			
		||||
	void write(const Register reg, const reg_t value);
 | 
			
		||||
	reg_t read(const Register reg);
 | 
			
		||||
 | 
			
		||||
	void flush_one(const Register reg);
 | 
			
		||||
 | 
			
		||||
	reg_t readback(const Readback readback);
 | 
			
		||||
 | 
			
		||||
	void init_for_best_performance();
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} /* rffc507x */
 | 
			
		||||
 | 
			
		||||
#endif/*__RFFC507X_H__*/
 | 
			
		||||
@@ -0,0 +1,109 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
 | 
			
		||||
 *
 | 
			
		||||
 * This file is part of PortaPack.
 | 
			
		||||
 *
 | 
			
		||||
 * 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 "rffc507x_spi.hpp"
 | 
			
		||||
 | 
			
		||||
#include "utility.hpp"
 | 
			
		||||
 | 
			
		||||
#include "hackrf_gpio.hpp"
 | 
			
		||||
using namespace hackrf::one;
 | 
			
		||||
 | 
			
		||||
namespace rffc507x {
 | 
			
		||||
namespace spi {
 | 
			
		||||
 | 
			
		||||
void SPI::init() {
 | 
			
		||||
	gpio_rffc5072_select.set();
 | 
			
		||||
	gpio_rffc5072_clock.clear();
 | 
			
		||||
 | 
			
		||||
	gpio_rffc5072_select.output();
 | 
			
		||||
	gpio_rffc5072_clock.output();
 | 
			
		||||
	gpio_rffc5072_data.input();
 | 
			
		||||
 | 
			
		||||
	gpio_rffc5072_data.clear();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline void SPI::select(const bool active) {
 | 
			
		||||
	gpio_rffc5072_select.write(!active);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline void SPI::direction_out() {
 | 
			
		||||
	gpio_rffc5072_data.output();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline void SPI::direction_in() {
 | 
			
		||||
	gpio_rffc5072_data.input();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline void SPI::write_bit(const bit_t value) {
 | 
			
		||||
	gpio_rffc5072_data.write(value);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline bit_t SPI::read_bit() {
 | 
			
		||||
	return gpio_rffc5072_data.read() & 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline bit_t SPI::transfer_bit(const bit_t bit_out) {
 | 
			
		||||
	gpio_rffc5072_clock.clear();
 | 
			
		||||
	write_bit(bit_out);
 | 
			
		||||
	const bit_t bit_in = read_bit();
 | 
			
		||||
	gpio_rffc5072_clock.set();
 | 
			
		||||
	return bit_in;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
data_t SPI::transfer_bits(const data_t data_out, const size_t count) {
 | 
			
		||||
	data_t data_in = 0;
 | 
			
		||||
	for(size_t i=0; i<count; i++) {
 | 
			
		||||
		data_in = (data_in << 1) | transfer_bit((data_out >> (count - i - 1)) & 1);
 | 
			
		||||
	}
 | 
			
		||||
	return data_in;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
data_t SPI::transfer_word(const Direction direction, const address_t address, const data_t data_out) {
 | 
			
		||||
	select(true);
 | 
			
		||||
 | 
			
		||||
	const data_t address_word =
 | 
			
		||||
		  ((direction == Direction::Read) ? (1 << 7) : 0)
 | 
			
		||||
		| (address & 0x7f)
 | 
			
		||||
		;
 | 
			
		||||
 | 
			
		||||
	direction_out();
 | 
			
		||||
	transfer_bits(address_word, 9);
 | 
			
		||||
 | 
			
		||||
	if( direction == Direction::Read ) {
 | 
			
		||||
		direction_in();
 | 
			
		||||
		transfer_bits(0, 2);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	const data_t data_in = transfer_bits(data_out, 16);
 | 
			
		||||
 | 
			
		||||
	if( direction == Direction::Write ) {
 | 
			
		||||
		direction_in();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	select(false);
 | 
			
		||||
 | 
			
		||||
	transfer_bits(0, 2);
 | 
			
		||||
 | 
			
		||||
	return data_in;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,70 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
 | 
			
		||||
 *
 | 
			
		||||
 * This file is part of PortaPack.
 | 
			
		||||
 *
 | 
			
		||||
 * 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 __RFFC507X_SPI_H__
 | 
			
		||||
#define __RFFC507X_SPI_H__
 | 
			
		||||
 | 
			
		||||
#include <cstdint>
 | 
			
		||||
#include <cstddef>
 | 
			
		||||
 | 
			
		||||
namespace rffc507x {
 | 
			
		||||
namespace spi {
 | 
			
		||||
 | 
			
		||||
using reg_t = uint16_t;
 | 
			
		||||
using address_t = uint8_t;
 | 
			
		||||
using bit_t = uint_fast8_t;
 | 
			
		||||
using data_t = uint32_t;
 | 
			
		||||
 | 
			
		||||
class SPI {
 | 
			
		||||
public:
 | 
			
		||||
	enum class Direction {
 | 
			
		||||
		Write = 0,
 | 
			
		||||
		Read = 1,
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	void init();
 | 
			
		||||
 | 
			
		||||
	reg_t read(const address_t address) {
 | 
			
		||||
		return transfer_word(Direction::Read, address, 0);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void write(const address_t address, const reg_t value) {
 | 
			
		||||
		transfer_word(Direction::Write, address, value);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	void select(const bool active);
 | 
			
		||||
 | 
			
		||||
	void direction_out();
 | 
			
		||||
	void direction_in();
 | 
			
		||||
 | 
			
		||||
	void write_bit(const bit_t value);
 | 
			
		||||
	bit_t read_bit();
 | 
			
		||||
 | 
			
		||||
	bit_t transfer_bit(const bit_t bit_out);
 | 
			
		||||
	data_t transfer_bits(const data_t data_out, const size_t count);
 | 
			
		||||
	data_t transfer_word(const Direction direction, const address_t address, const data_t data_out);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} /* spi */
 | 
			
		||||
} /* rffc507x */
 | 
			
		||||
 | 
			
		||||
#endif/*__RFFC507X_SPI_H__*/
 | 
			
		||||
							
								
								
									
										105
									
								
								Software/portapack-mayhem/firmware/application/hw/si5351.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										105
									
								
								Software/portapack-mayhem/firmware/application/hw/si5351.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,105 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
 | 
			
		||||
 *
 | 
			
		||||
 * This file is part of PortaPack.
 | 
			
		||||
 *
 | 
			
		||||
 * 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 "si5351.hpp"
 | 
			
		||||
 | 
			
		||||
#include "utility.hpp"
 | 
			
		||||
 | 
			
		||||
#include <cstdint>
 | 
			
		||||
#include <array>
 | 
			
		||||
 | 
			
		||||
namespace si5351 {
 | 
			
		||||
 | 
			
		||||
void Si5351::reset() {
 | 
			
		||||
	wait_for_device_ready();
 | 
			
		||||
 | 
			
		||||
	write_register(Register::InterruptStatusSticky, 0x00);
 | 
			
		||||
	write_register(Register::InterruptStatusMask, 0xf0);
 | 
			
		||||
 | 
			
		||||
	disable_output_mask(0xff);
 | 
			
		||||
	write_register(Register::OEBPinEnableControlMask, 0xff);
 | 
			
		||||
	write_register(Register::PLLInputSource, 0x00);
 | 
			
		||||
 | 
			
		||||
	set_clock_control({
 | 
			
		||||
		ClockControl::power_off(), ClockControl::power_off(),
 | 
			
		||||
		ClockControl::power_off(), ClockControl::power_off(),
 | 
			
		||||
		ClockControl::power_off(), ClockControl::power_off(),
 | 
			
		||||
		ClockControl::power_off(), ClockControl::power_off()
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	write(std::array<uint8_t, 70> { Register::CLK3_0DisableState });
 | 
			
		||||
 | 
			
		||||
	write(std::array<uint8_t, 14>{
 | 
			
		||||
		Register::SpreadSpectrumParameters_Base
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	write(std::array<uint8_t, 4>{
 | 
			
		||||
		Register::VCXOParameters_Base
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	write(std::array<uint8_t, 7>{
 | 
			
		||||
		Register::CLKInitialPhaseOffset_Base
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	write_register(Register::CrystalInternalLoadCapacitance, 0b11010010);
 | 
			
		||||
	write_register(Register::FanoutEnable, 0x00);
 | 
			
		||||
 | 
			
		||||
	reset_plls();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Si5351::regvalue_t Si5351::read_register(const uint8_t reg) {
 | 
			
		||||
	const std::array<uint8_t, 1> tx { reg };
 | 
			
		||||
	std::array<uint8_t, 1> rx { 0x00 };
 | 
			
		||||
	_bus.transmit(_address, tx.data(), tx.size());
 | 
			
		||||
	_bus.receive(_address, rx.data(), rx.size());
 | 
			
		||||
	return rx[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Si5351::set_ms_frequency(
 | 
			
		||||
	const size_t ms_number,
 | 
			
		||||
	const uint32_t frequency,
 | 
			
		||||
	const uint32_t vco_frequency,
 | 
			
		||||
	const size_t r_div
 | 
			
		||||
) {
 | 
			
		||||
	/* TODO: Factor out the VCO frequency, which should be an attribute held
 | 
			
		||||
	 * by the Si5351 object.
 | 
			
		||||
	 */
 | 
			
		||||
	const uint32_t a = vco_frequency / frequency;
 | 
			
		||||
	const uint32_t remainder = vco_frequency - (frequency * a);
 | 
			
		||||
	const uint32_t denom = gcd(remainder, frequency);
 | 
			
		||||
	const uint32_t b = remainder / denom;
 | 
			
		||||
	const uint32_t c = frequency / denom;
 | 
			
		||||
 | 
			
		||||
	/* TODO: Switch between integer and fractional modes depending on the
 | 
			
		||||
	 * values of a and b.
 | 
			
		||||
	 */
 | 
			
		||||
	const MultisynthFractional ms {
 | 
			
		||||
		.f_src = vco_frequency,
 | 
			
		||||
		.a = a,
 | 
			
		||||
		.b = b,
 | 
			
		||||
		.c = c,
 | 
			
		||||
		.r_div = r_div,
 | 
			
		||||
	};
 | 
			
		||||
	const auto regs = ms.reg(ms_number);
 | 
			
		||||
	write(regs);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} /* namespace si5351 */
 | 
			
		||||
							
								
								
									
										492
									
								
								Software/portapack-mayhem/firmware/application/hw/si5351.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										492
									
								
								Software/portapack-mayhem/firmware/application/hw/si5351.hpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,492 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
 | 
			
		||||
 *
 | 
			
		||||
 * This file is part of PortaPack.
 | 
			
		||||
 *
 | 
			
		||||
 * 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 __SI5351_H__
 | 
			
		||||
#define __SI5351_H__
 | 
			
		||||
 | 
			
		||||
#include <cstdint>
 | 
			
		||||
#include <array>
 | 
			
		||||
#include <algorithm>
 | 
			
		||||
 | 
			
		||||
#include "ch.h"
 | 
			
		||||
#include "hal.h"
 | 
			
		||||
 | 
			
		||||
#include "i2c_pp.hpp"
 | 
			
		||||
 | 
			
		||||
namespace si5351 {
 | 
			
		||||
 | 
			
		||||
using reg_t = uint8_t;
 | 
			
		||||
 | 
			
		||||
namespace Register {
 | 
			
		||||
	enum {
 | 
			
		||||
		DeviceStatus = 0,
 | 
			
		||||
		InterruptStatusSticky = 1,
 | 
			
		||||
		InterruptStatusMask = 2,
 | 
			
		||||
		OutputEnableControl = 3,
 | 
			
		||||
		OEBPinEnableControlMask = 9,
 | 
			
		||||
		PLLInputSource = 15,
 | 
			
		||||
		CLKControl_Base = 16,
 | 
			
		||||
		CLKControl0 = 16,
 | 
			
		||||
		CLKControl1 = 17,
 | 
			
		||||
		CLKControl2 = 18,
 | 
			
		||||
		CLKControl3 = 19,
 | 
			
		||||
		CLKControl4 = 20,
 | 
			
		||||
		CLKControl5 = 21,
 | 
			
		||||
		CLKControl6 = 22,
 | 
			
		||||
		CLKControl7 = 23,
 | 
			
		||||
		CLK3_0DisableState = 24,
 | 
			
		||||
		CLK7_4DisableState = 25,
 | 
			
		||||
		MultisynthNAParameters_Base = 26,
 | 
			
		||||
		MultisynthNBParameters_Base = 34,
 | 
			
		||||
		Multisynth0Parameters_Base = 42,
 | 
			
		||||
		Multisynth1Parameters_Base = 50,
 | 
			
		||||
		Multisynth2Parameters_Base = 58,
 | 
			
		||||
		Multisynth3Parameters_Base = 66,
 | 
			
		||||
		Multisynth4Parameters_Base = 74,
 | 
			
		||||
		Multisynth5Parameters_Base = 82,
 | 
			
		||||
		Multisynth6Parameters = 90,
 | 
			
		||||
		Multisynth7Parameters = 91,
 | 
			
		||||
		Clock6And7OutputDivider = 92,
 | 
			
		||||
		SpreadSpectrumParameters_Base = 149,
 | 
			
		||||
		VCXOParameters_Base = 162,
 | 
			
		||||
		CLKInitialPhaseOffset_Base = 165,
 | 
			
		||||
		PLLReset = 177,
 | 
			
		||||
		CrystalInternalLoadCapacitance = 183,
 | 
			
		||||
		FanoutEnable = 187,
 | 
			
		||||
	};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
namespace DeviceStatus {
 | 
			
		||||
	using Type = uint8_t;
 | 
			
		||||
 | 
			
		||||
	enum {
 | 
			
		||||
		REVID_Mask              = (0b11 << 0),
 | 
			
		||||
 | 
			
		||||
		LOS_Mask                = (1 << 4),
 | 
			
		||||
		LOS_ValidClockAtCLKIN   = (0 << 4),
 | 
			
		||||
		LOS_LossOfSignalAtCLKIN = (1 << 4),
 | 
			
		||||
 | 
			
		||||
		LOL_A_Mask              = (1 << 5),
 | 
			
		||||
		LOL_A_PLLALocked        = (0 << 5),
 | 
			
		||||
		LOL_A_PLLAUnlocked      = (1 << 5),
 | 
			
		||||
 | 
			
		||||
		LOL_B_Mask              = (1 << 6),
 | 
			
		||||
		LOL_B_PLLBLocked        = (0 << 6),
 | 
			
		||||
		LOL_B_PLLBUnlocked      = (1 << 6),
 | 
			
		||||
 | 
			
		||||
		SYS_INIT_Mask           = (1 << 7),
 | 
			
		||||
		SYS_INIT_Complete       = (0 << 7),
 | 
			
		||||
		SYS_INIT_Initializing   = (1 << 7),
 | 
			
		||||
	};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct ClockControl {
 | 
			
		||||
	enum ClockCurrentDrive {
 | 
			
		||||
		_2mA = 0b00,
 | 
			
		||||
		_4mA = 0b01,
 | 
			
		||||
		_6mA = 0b10,
 | 
			
		||||
		_8mA = 0b11,
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	enum ClockSource {
 | 
			
		||||
		Xtal = 0b00,
 | 
			
		||||
		CLKIN = 0b01,
 | 
			
		||||
		MS_Group = 0b10,
 | 
			
		||||
		MS_Self = 0b11,
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	enum ClockInvert {
 | 
			
		||||
		Normal = 0,
 | 
			
		||||
		Invert = 1,
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	enum MultiSynthSource {
 | 
			
		||||
		PLLA = 0,
 | 
			
		||||
		PLLB = 1,
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	enum MultiSynthMode {
 | 
			
		||||
		Fractional = 0,
 | 
			
		||||
		Integer = 1,
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	enum ClockPowerDown {
 | 
			
		||||
		Power_On = 0,
 | 
			
		||||
		Power_Off = 1,
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	reg_t CLK_IDRV : 2;
 | 
			
		||||
	reg_t CLK_SRC  : 2;
 | 
			
		||||
	reg_t CLK_INV  : 1;
 | 
			
		||||
	reg_t MS_SRC   : 1;
 | 
			
		||||
	reg_t MS_INT   : 1;
 | 
			
		||||
	reg_t CLK_PDN  : 1;
 | 
			
		||||
 | 
			
		||||
	constexpr ClockControl(
 | 
			
		||||
		ClockCurrentDrive clk_idrv,
 | 
			
		||||
		ClockSource clk_src,
 | 
			
		||||
		ClockInvert clk_inv,
 | 
			
		||||
		MultiSynthSource ms_src,
 | 
			
		||||
		MultiSynthMode ms_int,
 | 
			
		||||
		ClockPowerDown clk_pdn
 | 
			
		||||
	) : CLK_IDRV(clk_idrv),
 | 
			
		||||
		CLK_SRC(clk_src),
 | 
			
		||||
		CLK_INV(clk_inv),
 | 
			
		||||
		MS_SRC(ms_src),
 | 
			
		||||
		MS_INT(ms_int),
 | 
			
		||||
		CLK_PDN(clk_pdn)
 | 
			
		||||
	{
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ClockControl clk_src(const ClockSource value) const {
 | 
			
		||||
		auto result = *this;
 | 
			
		||||
		result.CLK_SRC = value;
 | 
			
		||||
		return result;
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	ClockControl ms_src(const MultiSynthSource value) const {
 | 
			
		||||
		auto result = *this;
 | 
			
		||||
		result.MS_SRC = value;
 | 
			
		||||
		return result;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ClockControl clk_pdn(const ClockPowerDown value) const {
 | 
			
		||||
		auto result = *this;
 | 
			
		||||
		result.CLK_PDN = value;
 | 
			
		||||
		return result;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	constexpr operator reg_t() {
 | 
			
		||||
		return *reinterpret_cast<reg_t*>(this);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	static constexpr ClockControl power_off() {
 | 
			
		||||
		return {
 | 
			
		||||
			ClockCurrentDrive::_2mA,
 | 
			
		||||
			ClockSource::Xtal,
 | 
			
		||||
			ClockInvert::Normal,
 | 
			
		||||
			MultiSynthSource::PLLA,
 | 
			
		||||
			MultiSynthMode::Fractional,
 | 
			
		||||
			ClockPowerDown::Power_Off,
 | 
			
		||||
		};
 | 
			
		||||
	}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static_assert(sizeof(ClockControl) == 1, "ClockControl size is not eight bits");
 | 
			
		||||
 | 
			
		||||
using ClockControls = std::array<ClockControl, 8>;
 | 
			
		||||
 | 
			
		||||
namespace CrystalInternalLoadCapacitance {
 | 
			
		||||
	using Type = uint8_t;
 | 
			
		||||
 | 
			
		||||
	enum {
 | 
			
		||||
		XTAL_CL_Mask      = (0b11 << 6),
 | 
			
		||||
		XTAL_CL_6pF       = (0b01 << 6),
 | 
			
		||||
		XTAL_CL_8pF       = (0b10 << 6),
 | 
			
		||||
		XTAL_CL_10pF      = (0b11 << 6),
 | 
			
		||||
	};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
namespace PLLInputSource {
 | 
			
		||||
	using Type = uint8_t;
 | 
			
		||||
 | 
			
		||||
	enum {
 | 
			
		||||
		PLLA_Source_Mask  = (1 << 2),
 | 
			
		||||
		PLLA_Source_XTAL  = (0 << 2),
 | 
			
		||||
		PLLA_Source_CLKIN = (1 << 2),
 | 
			
		||||
 | 
			
		||||
		PLLB_Source_Mask  = (1 << 3),
 | 
			
		||||
		PLLB_Source_XTAL  = (0 << 3),
 | 
			
		||||
		PLLB_Source_CLKIN = (1 << 3),
 | 
			
		||||
 | 
			
		||||
		CLKIN_Div_Mask    = (0b11 << 6),
 | 
			
		||||
		CLKIN_Div1        = (0b00 << 6),
 | 
			
		||||
		CLKIN_Div2        = (0b01 << 6),
 | 
			
		||||
		CLKIN_Div4        = (0b10 << 6),
 | 
			
		||||
		CLKIN_Div8        = (0b11 << 6),
 | 
			
		||||
	};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct Inputs {
 | 
			
		||||
	const uint32_t f_xtal;
 | 
			
		||||
	const uint32_t f_clkin;
 | 
			
		||||
	const uint32_t clkin_div;
 | 
			
		||||
 | 
			
		||||
	constexpr uint32_t f_clkin_out() const {
 | 
			
		||||
		return f_clkin / clkin_div;
 | 
			
		||||
	}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
using PLLReg = std::array<uint8_t, 9>;
 | 
			
		||||
 | 
			
		||||
struct PLL {
 | 
			
		||||
	const uint32_t f_in;
 | 
			
		||||
	const uint32_t a;
 | 
			
		||||
	const uint32_t b;
 | 
			
		||||
	const uint32_t c;
 | 
			
		||||
 | 
			
		||||
	constexpr uint32_t f_vco() const {
 | 
			
		||||
		return f_in * (a + (float)b / (float)c);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	constexpr uint32_t p1() const {
 | 
			
		||||
		return 128 * a + (uint32_t)(128 * (float)b / (float)c) - 512;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	constexpr uint32_t p2() const {
 | 
			
		||||
		return 128 * b - c * (uint32_t)(128 * (float)b / (float)c);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	constexpr uint32_t p3() const {
 | 
			
		||||
		return c;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	constexpr PLLReg reg(const uint8_t pll_n) const {
 | 
			
		||||
		return {
 | 
			
		||||
			uint8_t(26 + (pll_n * 8)),
 | 
			
		||||
			uint8_t((p3() >>  8) & 0xff),
 | 
			
		||||
			uint8_t((p3() >>  0) & 0xff),
 | 
			
		||||
			uint8_t((p1() >> 16) & 0x03),
 | 
			
		||||
			uint8_t((p1() >>  8) & 0xff),
 | 
			
		||||
			uint8_t((p1() >>  0) & 0xff),
 | 
			
		||||
			uint8_t(
 | 
			
		||||
				  (((p3() >> 16) & 0x0f) << 4)
 | 
			
		||||
				|  ((p2() >> 16) & 0x0f)
 | 
			
		||||
			),
 | 
			
		||||
			uint8_t((p2() >>  8) & 0xff),
 | 
			
		||||
			uint8_t((p2() >>  0) & 0xff),
 | 
			
		||||
		};
 | 
			
		||||
	}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
using MultisynthFractionalReg = std::array<uint8_t, 9>;
 | 
			
		||||
 | 
			
		||||
struct MultisynthFractional {
 | 
			
		||||
	const uint32_t f_src;
 | 
			
		||||
	const uint32_t a;
 | 
			
		||||
	const uint32_t b;
 | 
			
		||||
	const uint32_t c;
 | 
			
		||||
	const uint32_t r_div;
 | 
			
		||||
 | 
			
		||||
	constexpr uint32_t p1() const {
 | 
			
		||||
		return 128 * a + (uint32_t)(128 * (float)b / (float)c) - 512;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	constexpr uint32_t p2() const {
 | 
			
		||||
		return 128 * b - c * (uint32_t)(128 * (float)b / (float)c);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	constexpr uint32_t p3() const {
 | 
			
		||||
		return c;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	constexpr uint32_t f_out() const {
 | 
			
		||||
		return f_src / (a + (float)b / (float)c) / (1 << r_div);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	constexpr MultisynthFractionalReg reg(const uint8_t multisynth_n) const {
 | 
			
		||||
		return {
 | 
			
		||||
			uint8_t(42 + (multisynth_n * 8)),
 | 
			
		||||
			uint8_t((p3() >> 8) & 0xFF),
 | 
			
		||||
			uint8_t((p3() >> 0) & 0xFF),
 | 
			
		||||
			uint8_t((r_div << 4) | (0 << 2) | ((p1() >> 16) & 0x3)),
 | 
			
		||||
			uint8_t((p1() >> 8) & 0xFF),
 | 
			
		||||
			uint8_t((p1() >> 0) & 0xFF),
 | 
			
		||||
			uint8_t((((p3() >> 16) & 0xF) << 4) | (((p2() >> 16) & 0xF) << 0)),
 | 
			
		||||
			uint8_t((p2() >> 8) & 0xFF),
 | 
			
		||||
			uint8_t((p2() >> 0) & 0xFF)
 | 
			
		||||
		};
 | 
			
		||||
	}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct MultisynthInteger {
 | 
			
		||||
	const uint32_t f_src;
 | 
			
		||||
	const uint32_t a;
 | 
			
		||||
	const uint32_t r_div;
 | 
			
		||||
 | 
			
		||||
	constexpr uint8_t p1() const {
 | 
			
		||||
		return a;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	constexpr uint32_t f_out() const {
 | 
			
		||||
		return f_src / a / (1 << r_div);
 | 
			
		||||
	}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
using Multisynth6And7Reg = std::array<uint8_t, 4>;
 | 
			
		||||
 | 
			
		||||
constexpr Multisynth6And7Reg ms6_7_reg(
 | 
			
		||||
	const MultisynthInteger& ms6,
 | 
			
		||||
	const MultisynthInteger& ms7
 | 
			
		||||
) {
 | 
			
		||||
	return {
 | 
			
		||||
		Register::Multisynth6Parameters,
 | 
			
		||||
		uint8_t(ms6.p1() & 0xff),
 | 
			
		||||
		uint8_t(ms7.p1() & 0xff),
 | 
			
		||||
		uint8_t(((ms7.r_div & 7) << 4) | ((ms6.r_div & 7) << 0)),
 | 
			
		||||
	};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class Si5351 {
 | 
			
		||||
public:
 | 
			
		||||
	using regvalue_t = uint8_t;
 | 
			
		||||
 | 
			
		||||
	constexpr Si5351(I2C& bus, I2C::address_t address) :
 | 
			
		||||
		_clock_control({
 | 
			
		||||
			ClockControl::power_off(), ClockControl::power_off(),
 | 
			
		||||
			ClockControl::power_off(), ClockControl::power_off(),
 | 
			
		||||
			ClockControl::power_off(), ClockControl::power_off(),
 | 
			
		||||
			ClockControl::power_off(), ClockControl::power_off()
 | 
			
		||||
		}),
 | 
			
		||||
		_bus(bus),
 | 
			
		||||
		_address(address),
 | 
			
		||||
		_output_enable(0x00)
 | 
			
		||||
	{
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void reset();
 | 
			
		||||
 | 
			
		||||
	uint8_t device_status() {
 | 
			
		||||
		return read_register(Register::DeviceStatus);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void wait_for_device_ready() {
 | 
			
		||||
		while(device_status() & 0x80);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bool clkin_loss_of_signal() {
 | 
			
		||||
		return (device_status() >> 4) & 1;
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	void enable_fanout() {
 | 
			
		||||
		write_register(Register::FanoutEnable, 0b11010000);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void reset_plls() {
 | 
			
		||||
		// Datasheet recommends value 0xac, though the low nibble bits are not defined in AN619.
 | 
			
		||||
		write_register(Register::PLLReset, 0xac);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	regvalue_t read_register(const uint8_t reg);
 | 
			
		||||
 | 
			
		||||
	template<size_t N>
 | 
			
		||||
	void write(const std::array<uint8_t, N>& values) {
 | 
			
		||||
		_bus.transmit(_address, values.data(), values.size());
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void write_register(const uint8_t reg, const regvalue_t value) {
 | 
			
		||||
		write(std::array<uint8_t, 2>{
 | 
			
		||||
			reg, value
 | 
			
		||||
		});
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void write(const size_t ms_number, const MultisynthFractional& config) {
 | 
			
		||||
		write(config.reg(ms_number));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void set_ms_frequency(
 | 
			
		||||
		const size_t ms_number,
 | 
			
		||||
		const uint32_t frequency,
 | 
			
		||||
		const uint32_t vco_frequency,
 | 
			
		||||
		const size_t r_div
 | 
			
		||||
	);
 | 
			
		||||
 | 
			
		||||
	void set_crystal_internal_load_capacitance(const CrystalInternalLoadCapacitance::Type xtal_cl) {
 | 
			
		||||
		write_register(Register::CrystalInternalLoadCapacitance, xtal_cl);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void set_pll_input_sources(const PLLInputSource::Type value) {
 | 
			
		||||
		write_register(Register::PLLInputSource, value);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void enable_output_mask(const uint8_t mask) {
 | 
			
		||||
		_output_enable |= mask;
 | 
			
		||||
		update_output_enable_control();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void enable_output(const size_t n) {
 | 
			
		||||
		enable_output_mask(1 << n);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void disable_output_mask(const uint8_t mask) {
 | 
			
		||||
		_output_enable &= ~mask;
 | 
			
		||||
		update_output_enable_control();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void disable_output(const size_t n) {
 | 
			
		||||
		disable_output_mask(1 << n);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void set_clock_control(const ClockControls& clock_control) {
 | 
			
		||||
		_clock_control = clock_control;
 | 
			
		||||
		update_all_clock_control();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void set_clock_control(const size_t n, const ClockControl clock_control) {
 | 
			
		||||
		_clock_control[n] = clock_control;
 | 
			
		||||
		write_register(Register::CLKControl_Base + n, _clock_control[n]);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void enable_clock(const size_t n) {
 | 
			
		||||
		_clock_control[n].CLK_PDN = ClockControl::ClockPowerDown::Power_On;
 | 
			
		||||
		write_register(Register::CLKControl_Base + n, _clock_control[n]);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void disable_clock(const size_t n) {
 | 
			
		||||
		_clock_control[n].CLK_PDN = ClockControl::ClockPowerDown::Power_Off;
 | 
			
		||||
		write_register(Register::CLKControl_Base + n, _clock_control[n]);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	template<size_t N>
 | 
			
		||||
	void write_registers(const uint8_t reg, const std::array<uint8_t, N>& values) {
 | 
			
		||||
		std::array<uint8_t, N + 1> data;
 | 
			
		||||
		data[0] = reg;
 | 
			
		||||
		std::copy(values.cbegin(), values.cend(), data.begin() + 1);
 | 
			
		||||
		write(data);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	ClockControls _clock_control;
 | 
			
		||||
	I2C& _bus;
 | 
			
		||||
	const I2C::address_t _address;
 | 
			
		||||
	uint8_t _output_enable;
 | 
			
		||||
 | 
			
		||||
	void update_output_enable_control() {
 | 
			
		||||
		write_register(Register::OutputEnableControl, ~_output_enable);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void update_all_clock_control() {
 | 
			
		||||
		write_registers(Register::CLKControl_Base, std::array<reg_t, 8> { {
 | 
			
		||||
			_clock_control[0],
 | 
			
		||||
			_clock_control[1],
 | 
			
		||||
			_clock_control[2],
 | 
			
		||||
			_clock_control[3],
 | 
			
		||||
			_clock_control[4],
 | 
			
		||||
			_clock_control[5],
 | 
			
		||||
			_clock_control[6],
 | 
			
		||||
			_clock_control[7],
 | 
			
		||||
		} });
 | 
			
		||||
	}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif/*__SI5351_H__*/
 | 
			
		||||
@@ -0,0 +1,77 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
 | 
			
		||||
 *
 | 
			
		||||
 * This file is part of PortaPack.
 | 
			
		||||
 *
 | 
			
		||||
 * 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 __SPI_ARBITER_H__
 | 
			
		||||
#define __SPI_ARBITER_H__
 | 
			
		||||
 | 
			
		||||
#include <cstddef>
 | 
			
		||||
 | 
			
		||||
#include "spi_pp.hpp"
 | 
			
		||||
 | 
			
		||||
namespace spi {
 | 
			
		||||
namespace arbiter {
 | 
			
		||||
 | 
			
		||||
class Arbiter {
 | 
			
		||||
public:
 | 
			
		||||
	constexpr Arbiter(
 | 
			
		||||
		SPI& bus
 | 
			
		||||
	) : _bus(bus),
 | 
			
		||||
		_config(nullptr)
 | 
			
		||||
	{
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void transfer(const SPIConfig* const config, void* const data, const size_t count) {
 | 
			
		||||
		if( config != _config ) {
 | 
			
		||||
			_bus.stop();
 | 
			
		||||
			_bus.start(*config);
 | 
			
		||||
			_config = config;
 | 
			
		||||
		}
 | 
			
		||||
		_bus.transfer(data, count);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	SPI& _bus;
 | 
			
		||||
	const SPIConfig* _config;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class Target {
 | 
			
		||||
public:
 | 
			
		||||
	constexpr Target(
 | 
			
		||||
		Arbiter& arbiter,
 | 
			
		||||
		const SPIConfig& config
 | 
			
		||||
	) : _arbiter(arbiter),
 | 
			
		||||
		_config(config)
 | 
			
		||||
	{
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void transfer(void* const data, const size_t count) {
 | 
			
		||||
		_arbiter.transfer(&_config, data, count);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	Arbiter& _arbiter;
 | 
			
		||||
	const SPIConfig _config;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} /* arbiter */
 | 
			
		||||
} /* spi */
 | 
			
		||||
 | 
			
		||||
#endif/*__SPI_ARBITER_H__*/
 | 
			
		||||
							
								
								
									
										22
									
								
								Software/portapack-mayhem/firmware/application/hw/spi_pp.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								Software/portapack-mayhem/firmware/application/hw/spi_pp.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,22 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
 | 
			
		||||
 *
 | 
			
		||||
 * This file is part of PortaPack.
 | 
			
		||||
 *
 | 
			
		||||
 * 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 "spi_pp.hpp"
 | 
			
		||||
							
								
								
									
										56
									
								
								Software/portapack-mayhem/firmware/application/hw/spi_pp.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								Software/portapack-mayhem/firmware/application/hw/spi_pp.hpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,56 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
 | 
			
		||||
 *
 | 
			
		||||
 * This file is part of PortaPack.
 | 
			
		||||
 *
 | 
			
		||||
 * 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 __SPI_PP_H__
 | 
			
		||||
#define __SPI_PP_H__
 | 
			
		||||
 | 
			
		||||
#include <cstdint>
 | 
			
		||||
 | 
			
		||||
#include "ch.h"
 | 
			
		||||
#include "hal.h"
 | 
			
		||||
 | 
			
		||||
class SPI {
 | 
			
		||||
public:
 | 
			
		||||
	constexpr SPI(SPIDriver* const driver) :
 | 
			
		||||
		_driver(driver) {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void start(const SPIConfig& config) {
 | 
			
		||||
		spiStart(_driver, &config);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void stop() {
 | 
			
		||||
		spiStop(_driver);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void transfer(void* const data, const size_t count) {
 | 
			
		||||
		spiAcquireBus(_driver);
 | 
			
		||||
		spiSelect(_driver);
 | 
			
		||||
		spiExchange(_driver, count, data, data);
 | 
			
		||||
		spiUnselect(_driver);
 | 
			
		||||
		spiReleaseBus(_driver);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	SPIDriver* const _driver;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#endif/*__SPI_PP_H__*/
 | 
			
		||||
@@ -0,0 +1,86 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
 | 
			
		||||
 *
 | 
			
		||||
 * This file is part of PortaPack.
 | 
			
		||||
 *
 | 
			
		||||
 * 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 "touch.hpp"
 | 
			
		||||
 | 
			
		||||
#include <cstdint>
 | 
			
		||||
 | 
			
		||||
#include "adc.hpp"
 | 
			
		||||
#include "utility.hpp"
 | 
			
		||||
 | 
			
		||||
#include "hal.h"
 | 
			
		||||
using namespace lpc43xx;
 | 
			
		||||
 | 
			
		||||
#include "hackrf_hal.hpp"
 | 
			
		||||
using namespace hackrf::one;
 | 
			
		||||
 | 
			
		||||
#include "portapack_adc.hpp"
 | 
			
		||||
#include "portapack_shared_memory.hpp"
 | 
			
		||||
 | 
			
		||||
namespace touch {
 | 
			
		||||
namespace adc {
 | 
			
		||||
 | 
			
		||||
constexpr uint8_t adc0_sel =
 | 
			
		||||
	  (1 << portapack::adc0_touch_xp_input)
 | 
			
		||||
	| (1 << portapack::adc0_touch_xn_input)
 | 
			
		||||
	| (1 << portapack::adc0_touch_yp_input)
 | 
			
		||||
	| (1 << portapack::adc0_touch_yn_input)
 | 
			
		||||
	;
 | 
			
		||||
const auto adc0_interrupt_mask = flp2(adc0_sel);
 | 
			
		||||
 | 
			
		||||
constexpr lpc43xx::adc::CR adc0_cr {
 | 
			
		||||
	.sel = adc0_sel,
 | 
			
		||||
	.clkdiv = 49,		/* 400kHz sample rate, 2.5us/sample @ 200MHz PCLK */
 | 
			
		||||
	.resolution = 9,	/* Ten clocks */
 | 
			
		||||
	.edge = 0,
 | 
			
		||||
};
 | 
			
		||||
constexpr lpc43xx::adc::Config adc0_config {
 | 
			
		||||
	.cr = adc0_cr,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
void init() {
 | 
			
		||||
	adc0::clock_enable();
 | 
			
		||||
	adc0::interrupts_disable();
 | 
			
		||||
	adc0::power_up(adc0_config);
 | 
			
		||||
	adc0::interrupts_enable(adc0_interrupt_mask);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void start() {
 | 
			
		||||
	adc0::start_burst();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// static constexpr bool monitor_overruns_and_not_dones = false;
 | 
			
		||||
 | 
			
		||||
Samples get() {
 | 
			
		||||
	const auto xp_reg = LPC_ADC0->DR[portapack::adc0_touch_xp_input];
 | 
			
		||||
	const auto xn_reg = LPC_ADC0->DR[portapack::adc0_touch_xn_input];
 | 
			
		||||
	const auto yp_reg = LPC_ADC0->DR[portapack::adc0_touch_yp_input];
 | 
			
		||||
	const auto yn_reg = LPC_ADC0->DR[portapack::adc0_touch_yn_input];
 | 
			
		||||
	return {
 | 
			
		||||
		(xp_reg >> 6) & 0x3ff,
 | 
			
		||||
		(xn_reg >> 6) & 0x3ff,
 | 
			
		||||
		(yp_reg >> 6) & 0x3ff,
 | 
			
		||||
		(yn_reg >> 6) & 0x3ff,
 | 
			
		||||
	};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} /* namespace adc */
 | 
			
		||||
} /* namespace touch */
 | 
			
		||||
@@ -0,0 +1,38 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
 | 
			
		||||
 *
 | 
			
		||||
 * This file is part of PortaPack.
 | 
			
		||||
 *
 | 
			
		||||
 * 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 __TOUCH_ADC_H__
 | 
			
		||||
#define __TOUCH_ADC_H__
 | 
			
		||||
 | 
			
		||||
#include "touch.hpp"
 | 
			
		||||
 | 
			
		||||
namespace touch {
 | 
			
		||||
namespace adc {
 | 
			
		||||
 | 
			
		||||
void init();
 | 
			
		||||
void start();
 | 
			
		||||
 | 
			
		||||
Samples get();
 | 
			
		||||
 | 
			
		||||
} /* namespace adc */
 | 
			
		||||
} /* namespace touch */
 | 
			
		||||
 | 
			
		||||
#endif/*__TOUCH_ADC_H__*/
 | 
			
		||||
		Reference in New Issue
	
	Block a user