mirror of
https://github.com/portapack-mayhem/mayhem-firmware.git
synced 2025-08-13 23:27:40 +00:00
Formatted code (#1007)
* Updated style * Updated files * fixed new line * Updated spacing * File fix WIP * Updated to clang 13 * updated comment style * Removed old comment code
This commit is contained in:
@@ -24,16 +24,16 @@
|
||||
#include "utility.hpp"
|
||||
|
||||
bool Debounce::feed(const uint8_t bit) {
|
||||
history_ = (history_ << 1) | (bit & 1);
|
||||
history_ = (history_ << 1) | (bit & 1);
|
||||
|
||||
if( history_ == 0b00001111 ) {
|
||||
state_ = 1;
|
||||
return true;
|
||||
}
|
||||
if( history_ == 0b11110000 ) {
|
||||
state_ = 0;
|
||||
return true;
|
||||
}
|
||||
if (history_ == 0b00001111) {
|
||||
state_ = 1;
|
||||
return true;
|
||||
}
|
||||
if (history_ == 0b11110000) {
|
||||
state_ = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
|
@@ -25,16 +25,16 @@
|
||||
#include <cstdint>
|
||||
|
||||
class Debounce {
|
||||
public:
|
||||
bool feed(const uint8_t bit);
|
||||
|
||||
uint8_t state() const {
|
||||
return state_;
|
||||
}
|
||||
public:
|
||||
bool feed(const uint8_t bit);
|
||||
|
||||
private:
|
||||
uint8_t history_ { 0 };
|
||||
uint8_t state_ { 0 };
|
||||
uint8_t state() const {
|
||||
return state_;
|
||||
}
|
||||
|
||||
private:
|
||||
uint8_t history_{0};
|
||||
uint8_t state_{0};
|
||||
};
|
||||
|
||||
#endif/*__DEBOUNCE_H__*/
|
||||
#endif /*__DEBOUNCE_H__*/
|
||||
|
@@ -24,32 +24,31 @@
|
||||
#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
|
||||
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;
|
||||
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];
|
||||
return transition_map[state & 0xf];
|
||||
}
|
||||
|
@@ -25,14 +25,13 @@
|
||||
#include <cstdint>
|
||||
|
||||
class Encoder {
|
||||
public:
|
||||
int_fast8_t update(
|
||||
const uint_fast8_t phase_0,
|
||||
const uint_fast8_t phase_1
|
||||
);
|
||||
public:
|
||||
int_fast8_t update(
|
||||
const uint_fast8_t phase_0,
|
||||
const uint_fast8_t phase_1);
|
||||
|
||||
private:
|
||||
uint_fast8_t state { 0 };
|
||||
private:
|
||||
uint_fast8_t state{0};
|
||||
};
|
||||
|
||||
#endif/*__ENCODER_H__*/
|
||||
#endif /*__ENCODER_H__*/
|
||||
|
@@ -38,14 +38,13 @@ namespace lna {
|
||||
|
||||
using namespace max283x::lna;
|
||||
|
||||
constexpr std::array<uint8_t, 8> lookup_8db_steps {
|
||||
0b111, 0b011, 0b110, 0b010,
|
||||
0b100, 0b000, 0b000, 0b000
|
||||
};
|
||||
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];
|
||||
const auto db_sat = gain_db_range.clip(db);
|
||||
return lna::lookup_8db_steps[(db_sat >> 3) & 7];
|
||||
}
|
||||
|
||||
} /* namespace lna */
|
||||
@@ -55,8 +54,8 @@ namespace vga {
|
||||
using namespace max283x::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;
|
||||
const auto db_sat = gain_db_range.clip(db);
|
||||
return ((db_sat >> 1) & 0b11111) ^ 0b11111;
|
||||
}
|
||||
|
||||
} /* namespace vga */
|
||||
@@ -66,11 +65,11 @@ namespace tx {
|
||||
using namespace max283x::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;
|
||||
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 */
|
||||
@@ -80,10 +79,10 @@ namespace filter {
|
||||
using namespace max283x::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();
|
||||
/* 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 */
|
||||
@@ -98,36 +97,36 @@ constexpr uint32_t reference_frequency = max283x_reference_f;
|
||||
constexpr uint32_t pll_factor = 1.0 / (4.0 / 3.0 / reference_frequency) + 0.5;
|
||||
|
||||
void MAX2837::init() {
|
||||
set_mode(Mode::Shutdown);
|
||||
set_mode(Mode::Shutdown);
|
||||
|
||||
gpio_max283x_enable.output();
|
||||
gpio_max2837_rxenable.output();
|
||||
gpio_max2837_txenable.output();
|
||||
gpio_max283x_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.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.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.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.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.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_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.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.spi_en.EN_SPI = 1; /* enable chip functions when ENABLE pin set */
|
||||
|
||||
_map.r.lo_gen.LOGEN_2GM = 0;
|
||||
_map.r.lo_gen.LOGEN_2GM = 0;
|
||||
|
||||
#if 0
|
||||
_map.r.rxrf_1.LNA_EN = 1;
|
||||
@@ -141,184 +140,188 @@ void MAX2837::init() {
|
||||
_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 */
|
||||
_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();
|
||||
_dirty.set();
|
||||
flush();
|
||||
|
||||
set_mode(Mode::Standby);
|
||||
set_mode(Mode::Standby);
|
||||
}
|
||||
|
||||
enum class Mask {
|
||||
Enable = 0b001,
|
||||
RxEnable = 0b010,
|
||||
TxEnable = 0b100,
|
||||
Shutdown = 0b000,
|
||||
Standby = Enable,
|
||||
Receive = Enable | RxEnable,
|
||||
Transmit = Enable | TxEnable,
|
||||
Enable = 0b001,
|
||||
RxEnable = 0b010,
|
||||
TxEnable = 0b100,
|
||||
Shutdown = 0b000,
|
||||
Standby = Enable,
|
||||
Receive = Enable | RxEnable,
|
||||
Transmit = Enable | TxEnable,
|
||||
};
|
||||
|
||||
Mask mode_mask(const Mode mode) {
|
||||
switch (mode) {
|
||||
case Mode::Standby: return Mask::Standby;
|
||||
case Mode::Receive: return Mask::Receive;
|
||||
case Mode::Transmit: return Mask::Transmit;
|
||||
default: return Mask::Shutdown;
|
||||
}
|
||||
switch (mode) {
|
||||
case Mode::Standby:
|
||||
return Mask::Standby;
|
||||
case Mode::Receive:
|
||||
return Mask::Receive;
|
||||
case Mode::Transmit:
|
||||
return Mask::Transmit;
|
||||
default:
|
||||
return Mask::Shutdown;
|
||||
}
|
||||
}
|
||||
|
||||
void MAX2837::set_mode(const Mode mode) {
|
||||
Mask mask = mode_mask(mode);
|
||||
gpio_max283x_enable.write(toUType(mask) & toUType(Mask::Enable));
|
||||
gpio_max2837_rxenable.write(toUType(mask) & toUType(Mask::RxEnable));
|
||||
gpio_max2837_txenable.write(toUType(mask) & toUType(Mask::TxEnable));
|
||||
Mask mask = mode_mask(mode);
|
||||
gpio_max283x_enable.write(toUType(mask) & toUType(Mask::Enable));
|
||||
gpio_max2837_rxenable.write(toUType(mask) & toUType(Mask::RxEnable));
|
||||
gpio_max2837_txenable.write(toUType(mask) & toUType(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();
|
||||
}
|
||||
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);
|
||||
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);
|
||||
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;
|
||||
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);
|
||||
write(toUType(reg), value);
|
||||
}
|
||||
|
||||
reg_t MAX2837::read(const Register reg) {
|
||||
return read(toUType(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();
|
||||
_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();
|
||||
_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();
|
||||
_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();
|
||||
_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;
|
||||
/* 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;
|
||||
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_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();
|
||||
_map.r.syn_fr_div_1.SYN_FRDIV_9_0 = (div_q20 & 0x3ff);
|
||||
_dirty[Register::SYN_FR_DIV_1] = 1;
|
||||
flush();
|
||||
|
||||
return true;
|
||||
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();
|
||||
_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();
|
||||
_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();
|
||||
_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();
|
||||
_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);
|
||||
if (!_map.r.rx_top.ts_en) {
|
||||
_map.r.rx_top.ts_en = 1;
|
||||
flush_one(Register::RX_TOP);
|
||||
|
||||
chThdSleepMilliseconds(1);
|
||||
}
|
||||
chThdSleepMilliseconds(1);
|
||||
}
|
||||
|
||||
_map.r.rx_top.ts_adc_trigger = 1;
|
||||
flush_one(Register::RX_TOP);
|
||||
_map.r.rx_top.ts_adc_trigger = 1;
|
||||
flush_one(Register::RX_TOP);
|
||||
|
||||
halPolledDelay(ticks_for_temperature_sense_adc_conversion);
|
||||
halPolledDelay(ticks_for_temperature_sense_adc_conversion);
|
||||
|
||||
const auto value = read(Register::TEMP_SENSE);
|
||||
const auto value = read(Register::TEMP_SENSE);
|
||||
|
||||
_map.r.rx_top.ts_adc_trigger = 0;
|
||||
flush_one(Register::RX_TOP);
|
||||
_map.r.rx_top.ts_adc_trigger = 0;
|
||||
flush_one(Register::RX_TOP);
|
||||
|
||||
return value;
|
||||
return value;
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace max2837
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -37,14 +37,13 @@ namespace lna {
|
||||
|
||||
using namespace max283x::lna;
|
||||
|
||||
constexpr std::array<uint8_t, 8> lookup_8db_steps {
|
||||
0b11, 0b11, 0b10, 0b10,
|
||||
0b01, 0b00, 0b00, 0b00
|
||||
};
|
||||
constexpr std::array<uint8_t, 8> lookup_8db_steps{
|
||||
0b11, 0b11, 0b10, 0b10,
|
||||
0b01, 0b00, 0b00, 0b00};
|
||||
|
||||
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];
|
||||
const auto db_sat = gain_db_range.clip(db);
|
||||
return lna::lookup_8db_steps[(db_sat >> 3) & 7];
|
||||
}
|
||||
|
||||
} /* namespace lna */
|
||||
@@ -53,11 +52,11 @@ namespace vga {
|
||||
|
||||
using namespace max283x::vga;
|
||||
|
||||
constexpr range_t<int8_t> gain_db_range_internal { 0, 63 };
|
||||
constexpr range_t<int8_t> gain_db_range_internal{0, 63};
|
||||
|
||||
static uint_fast8_t gain_ordinal(const int8_t db) {
|
||||
const auto db_sat = gain_db_range_internal.clip(db);
|
||||
return (db_sat & 0b111111) ^ 0b111111;
|
||||
const auto db_sat = gain_db_range_internal.clip(db);
|
||||
return (db_sat & 0b111111) ^ 0b111111;
|
||||
}
|
||||
|
||||
} /* namespace vga */
|
||||
@@ -67,8 +66,8 @@ namespace tx {
|
||||
using namespace max283x::tx;
|
||||
|
||||
static uint_fast8_t gain_ordinal(const int8_t db) {
|
||||
const auto db_sat = gain_db_range.clip(db);
|
||||
return 47 - db_sat;
|
||||
const auto db_sat = gain_db_range.clip(db);
|
||||
return 47 - db_sat;
|
||||
}
|
||||
|
||||
} /* namespace tx */
|
||||
@@ -78,10 +77,10 @@ namespace filter {
|
||||
using namespace max283x::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();
|
||||
/* 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 */
|
||||
@@ -99,114 +98,118 @@ static int_fast8_t requested_rx_lna_gain = 0;
|
||||
static int_fast8_t requested_rx_vga_gain = 0;
|
||||
|
||||
void MAX2839::init() {
|
||||
set_mode(Mode::Shutdown);
|
||||
set_mode(Mode::Shutdown);
|
||||
|
||||
gpio_max283x_enable.output();
|
||||
gpio_max2839_rxtx.output();
|
||||
gpio_max283x_enable.output();
|
||||
gpio_max2839_rxtx.output();
|
||||
|
||||
_map.r.rxrf_1.MIMOmode = 1; /* enable RXINB */
|
||||
_map.r.rxrf_1.MIMOmode = 1; /* enable RXINB */
|
||||
|
||||
_map.r.pa_drv.TXVGA_GAIN_SPI_EN = 1;
|
||||
_map.r.tx_gain.TXVGA_GAIN_SPI = 0x00;
|
||||
_map.r.pa_drv.TXVGA_GAIN_SPI_EN = 1;
|
||||
_map.r.tx_gain.TXVGA_GAIN_SPI = 0x00;
|
||||
|
||||
_map.r.hpfsm_3.HPC_STOP = 1; /* 1kHz */
|
||||
_map.r.hpfsm_3.HPC_STOP = 1; /* 1kHz */
|
||||
|
||||
_map.r.rxrf_2.LNAgain_SPI_EN = 1; /* control LNA gain from SPI */
|
||||
_map.r.lpf_vga_1.L = 0b000;
|
||||
_map.r.lpf_vga_2.L = 0b000;
|
||||
_map.r.rxrf_2.LNAgain_SPI_EN = 1; /* control LNA gain from SPI */
|
||||
_map.r.lpf_vga_1.L = 0b000;
|
||||
_map.r.lpf_vga_2.L = 0b000;
|
||||
|
||||
_map.r.rx_top_1.VGAgain_SPI_EN = 1; /* control VGA gain from SPI */
|
||||
_map.r.lpf_vga_1.VGA = 0b000000;
|
||||
_map.r.lpf_vga_2.VGA = 0b010101;
|
||||
_map.r.rx_top_1.VGAgain_SPI_EN = 1; /* control VGA gain from SPI */
|
||||
_map.r.lpf_vga_1.VGA = 0b000000;
|
||||
_map.r.lpf_vga_2.VGA = 0b010101;
|
||||
|
||||
_map.r.lpf_vga_2.BUFF_VCM = 0b11; /* maximum RX output common-mode voltage */
|
||||
_map.r.lpf_vga_2.BUFF_VCM = 0b11; /* maximum RX output common-mode voltage */
|
||||
|
||||
_map.r.lpf_vga_1.ModeCtrl = 0b01; /* Rx LPF */
|
||||
_map.r.lpf.FT = 0b0000; /* 1.75 MHz LPF */
|
||||
_map.r.lpf_vga_1.ModeCtrl = 0b01; /* Rx LPF */
|
||||
_map.r.lpf.FT = 0b0000; /* 1.75 MHz LPF */
|
||||
|
||||
_map.r.spi_en.EN_SPI = 1; /* enable chip functions when ENABLE pin set */
|
||||
_map.r.spi_en.EN_SPI = 1; /* enable chip functions when ENABLE pin set */
|
||||
|
||||
_map.r.lo_gen.LOGEN_2GM = 0;
|
||||
_map.r.lo_gen.LOGEN_2GM = 0;
|
||||
|
||||
_map.r.rssi_vga.RSSI_MODE = 1; /* RSSI independent of RXHP */
|
||||
_map.r.rssi_vga.RSSI_MODE = 1; /* RSSI independent of RXHP */
|
||||
|
||||
/*
|
||||
* There are two LNA band settings, but we only use one of them.
|
||||
* Switching to the other one doesn't make the overall spectrum any
|
||||
* flatter but adds a surprise step in the middle.
|
||||
*/
|
||||
_map.r.rxrf_1.LNAband = 0; /* 2.3 - 2.5GHz */
|
||||
/*
|
||||
* There are two LNA band settings, but we only use one of them.
|
||||
* Switching to the other one doesn't make the overall spectrum any
|
||||
* flatter but adds a surprise step in the middle.
|
||||
*/
|
||||
_map.r.rxrf_1.LNAband = 0; /* 2.3 - 2.5GHz */
|
||||
|
||||
_dirty.set();
|
||||
flush();
|
||||
_dirty.set();
|
||||
flush();
|
||||
|
||||
set_mode(Mode::Standby);
|
||||
set_mode(Mode::Standby);
|
||||
}
|
||||
|
||||
enum class Mask {
|
||||
Enable = 0b01,
|
||||
RxTx = 0b10,
|
||||
Shutdown = 0b00,
|
||||
Standby = RxTx,
|
||||
Receive = Enable | RxTx,
|
||||
Transmit = Enable,
|
||||
Enable = 0b01,
|
||||
RxTx = 0b10,
|
||||
Shutdown = 0b00,
|
||||
Standby = RxTx,
|
||||
Receive = Enable | RxTx,
|
||||
Transmit = Enable,
|
||||
};
|
||||
|
||||
Mask mode_mask(const Mode mode) {
|
||||
switch (mode) {
|
||||
case Mode::Standby: return Mask::Standby;
|
||||
case Mode::Receive: return Mask::Receive;
|
||||
case Mode::Transmit: return Mask::Transmit;
|
||||
default: return Mask::Shutdown;
|
||||
}
|
||||
switch (mode) {
|
||||
case Mode::Standby:
|
||||
return Mask::Standby;
|
||||
case Mode::Receive:
|
||||
return Mask::Receive;
|
||||
case Mode::Transmit:
|
||||
return Mask::Transmit;
|
||||
default:
|
||||
return Mask::Shutdown;
|
||||
}
|
||||
}
|
||||
|
||||
void MAX2839::set_mode(const Mode mode) {
|
||||
Mask mask = mode_mask(mode);
|
||||
gpio_max283x_enable.write(toUType(mask) & toUType(Mask::Enable));
|
||||
gpio_max2839_rxtx.write(toUType(mask) & toUType(Mask::RxTx));
|
||||
Mask mask = mode_mask(mode);
|
||||
gpio_max283x_enable.write(toUType(mask) & toUType(Mask::Enable));
|
||||
gpio_max2839_rxtx.write(toUType(mask) & toUType(Mask::RxTx));
|
||||
}
|
||||
|
||||
void MAX2839::flush() {
|
||||
if( _dirty ) {
|
||||
for(size_t n=0; n<reg_count; n++) {
|
||||
if( _dirty[n] ) {
|
||||
write(n, _map.w[n]);
|
||||
}
|
||||
}
|
||||
_dirty.clear();
|
||||
}
|
||||
if (_dirty) {
|
||||
for (size_t n = 0; n < reg_count; n++) {
|
||||
if (_dirty[n]) {
|
||||
write(n, _map.w[n]);
|
||||
}
|
||||
}
|
||||
_dirty.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void MAX2839::flush_one(const Register reg) {
|
||||
const auto reg_num = toUType(reg);
|
||||
write(reg_num, _map.w[reg_num]);
|
||||
_dirty.clear(reg_num);
|
||||
const auto reg_num = toUType(reg);
|
||||
write(reg_num, _map.w[reg_num]);
|
||||
_dirty.clear(reg_num);
|
||||
}
|
||||
|
||||
void MAX2839::write(const address_t reg_num, const reg_t value) {
|
||||
uint16_t t = (0U << 15) | (reg_num << 10) | (value & 0x3ffU);
|
||||
_target.transfer(&t, 1);
|
||||
uint16_t t = (0U << 15) | (reg_num << 10) | (value & 0x3ffU);
|
||||
_target.transfer(&t, 1);
|
||||
}
|
||||
|
||||
reg_t MAX2839::read(const address_t reg_num) {
|
||||
uint16_t t = (1U << 15) | (reg_num << 10);
|
||||
_target.transfer(&t, 1U);
|
||||
return t & 0x3ffU;
|
||||
uint16_t t = (1U << 15) | (reg_num << 10);
|
||||
_target.transfer(&t, 1U);
|
||||
return t & 0x3ffU;
|
||||
}
|
||||
|
||||
void MAX2839::write(const Register reg, const reg_t value) {
|
||||
write(toUType(reg), value);
|
||||
write(toUType(reg), value);
|
||||
}
|
||||
|
||||
reg_t MAX2839::read(const Register reg) {
|
||||
return read(toUType(reg));
|
||||
return read(toUType(reg));
|
||||
}
|
||||
|
||||
void MAX2839::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();
|
||||
_map.r.tx_gain.TXVGA_GAIN_SPI = tx::gain_ordinal(db);
|
||||
_dirty[Register::TX_GAIN] = 1;
|
||||
flush();
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -215,140 +218,140 @@ void MAX2839::set_tx_vga_gain(const int_fast8_t db) {
|
||||
* user experience.
|
||||
*/
|
||||
void MAX2839::configure_rx_gain() {
|
||||
/* Apply MAX2837 restrictions to requested gain settings. */
|
||||
int_fast8_t lna_gain = lna::gain_db_range.clip(requested_rx_lna_gain);
|
||||
lna_gain &= 0x38;
|
||||
int_fast8_t vga_gain = vga::gain_db_range.clip(requested_rx_vga_gain);
|
||||
vga_gain &= 0x3e;
|
||||
/* Apply MAX2837 restrictions to requested gain settings. */
|
||||
int_fast8_t lna_gain = lna::gain_db_range.clip(requested_rx_lna_gain);
|
||||
lna_gain &= 0x38;
|
||||
int_fast8_t vga_gain = vga::gain_db_range.clip(requested_rx_vga_gain);
|
||||
vga_gain &= 0x3e;
|
||||
|
||||
/*
|
||||
* MAX2839 has lower full-scale RX output voltage than MAX2837, so we
|
||||
* adjust the VGA (baseband) gain to compensate.
|
||||
*/
|
||||
vga_gain += 3;
|
||||
/*
|
||||
* MAX2839 has lower full-scale RX output voltage than MAX2837, so we
|
||||
* adjust the VGA (baseband) gain to compensate.
|
||||
*/
|
||||
vga_gain += 3;
|
||||
|
||||
/*
|
||||
* If that adjustment puts VGA gain out of range, use LNA gain to
|
||||
* compensate. MAX2839 VGA gain can be any number from 0 through 63.
|
||||
*/
|
||||
if (vga_gain > 63) {
|
||||
if (lna_gain <= 32) {
|
||||
vga_gain -= 8;
|
||||
lna_gain += 8;
|
||||
} else {
|
||||
vga_gain = 63;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* If that adjustment puts VGA gain out of range, use LNA gain to
|
||||
* compensate. MAX2839 VGA gain can be any number from 0 through 63.
|
||||
*/
|
||||
if (vga_gain > 63) {
|
||||
if (lna_gain <= 32) {
|
||||
vga_gain -= 8;
|
||||
lna_gain += 8;
|
||||
} else {
|
||||
vga_gain = 63;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* MAX2839 lacks max-24 dB (16 dB) and max-40 dB (0 dB) LNA gain
|
||||
* settings, so we use VGA gain to compensate.
|
||||
*/
|
||||
if (lna_gain == 0) {
|
||||
lna_gain = 8;
|
||||
vga_gain = (vga_gain >= 8) ? vga_gain - 8 : 0;
|
||||
}
|
||||
if (lna_gain == 16) {
|
||||
if (vga_gain > 32) {
|
||||
vga_gain -= 8;
|
||||
lna_gain += 8;
|
||||
} else {
|
||||
vga_gain += 8;
|
||||
lna_gain -= 8;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* MAX2839 lacks max-24 dB (16 dB) and max-40 dB (0 dB) LNA gain
|
||||
* settings, so we use VGA gain to compensate.
|
||||
*/
|
||||
if (lna_gain == 0) {
|
||||
lna_gain = 8;
|
||||
vga_gain = (vga_gain >= 8) ? vga_gain - 8 : 0;
|
||||
}
|
||||
if (lna_gain == 16) {
|
||||
if (vga_gain > 32) {
|
||||
vga_gain -= 8;
|
||||
lna_gain += 8;
|
||||
} else {
|
||||
vga_gain += 8;
|
||||
lna_gain -= 8;
|
||||
}
|
||||
}
|
||||
|
||||
_map.r.lpf_vga_2.L = lna::gain_ordinal(lna_gain);
|
||||
_dirty[Register::RXRF_2] = 1;
|
||||
_map.r.lpf_vga_2.VGA = vga::gain_ordinal(vga_gain);
|
||||
_dirty[Register::LPF_VGA_2] = 1;
|
||||
flush();
|
||||
_map.r.lpf_vga_2.L = lna::gain_ordinal(lna_gain);
|
||||
_dirty[Register::RXRF_2] = 1;
|
||||
_map.r.lpf_vga_2.VGA = vga::gain_ordinal(vga_gain);
|
||||
_dirty[Register::LPF_VGA_2] = 1;
|
||||
flush();
|
||||
}
|
||||
|
||||
void MAX2839::set_lna_gain(const int_fast8_t db) {
|
||||
requested_rx_lna_gain = db;
|
||||
configure_rx_gain();
|
||||
requested_rx_lna_gain = db;
|
||||
configure_rx_gain();
|
||||
}
|
||||
|
||||
void MAX2839::set_vga_gain(const int_fast8_t db) {
|
||||
requested_rx_vga_gain = db;
|
||||
configure_rx_gain();
|
||||
requested_rx_vga_gain = db;
|
||||
configure_rx_gain();
|
||||
}
|
||||
|
||||
void MAX2839::set_lpf_rf_bandwidth(const uint32_t bandwidth_minimum) {
|
||||
_map.r.lpf.FT = filter::bandwidth_ordinal(bandwidth_minimum);
|
||||
_dirty[Register::LPF] = 1;
|
||||
flush();
|
||||
_map.r.lpf.FT = filter::bandwidth_ordinal(bandwidth_minimum);
|
||||
_dirty[Register::LPF] = 1;
|
||||
flush();
|
||||
}
|
||||
|
||||
bool MAX2839::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 */
|
||||
} else if( lo::band[1].contains(lo_frequency) ) {
|
||||
_map.r.syn_int_div.LOGEN_BSW = 0b01; /* 2400 - 2499.99MHz */
|
||||
} else if( lo::band[2].contains(lo_frequency) ) {
|
||||
_map.r.syn_int_div.LOGEN_BSW = 0b10; /* 2500 - 2599.99MHz */
|
||||
} else if( lo::band[3].contains(lo_frequency) ) {
|
||||
_map.r.syn_int_div.LOGEN_BSW = 0b11; /* 2600 - 2700Hz */
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
_dirty[Register::SYN_INT_DIV] = 1;
|
||||
/* 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 */
|
||||
} else if (lo::band[1].contains(lo_frequency)) {
|
||||
_map.r.syn_int_div.LOGEN_BSW = 0b01; /* 2400 - 2499.99MHz */
|
||||
} else if (lo::band[2].contains(lo_frequency)) {
|
||||
_map.r.syn_int_div.LOGEN_BSW = 0b10; /* 2500 - 2599.99MHz */
|
||||
} else if (lo::band[3].contains(lo_frequency)) {
|
||||
_map.r.syn_int_div.LOGEN_BSW = 0b11; /* 2600 - 2700Hz */
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
_dirty[Register::SYN_INT_DIV] = 1;
|
||||
|
||||
const uint64_t div_q20 = (lo_frequency * (1 << 20)) / pll_factor;
|
||||
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_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();
|
||||
_map.r.syn_fr_div_1.SYN_FRDIV_9_0 = (div_q20 & 0x3ff);
|
||||
_dirty[Register::SYN_FR_DIV_1] = 1;
|
||||
flush();
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void MAX2839::set_rx_lo_iq_calibration(const size_t v) {
|
||||
_map.r.rxrf_2.RX_IQERR_SPI_EN = 1;
|
||||
_dirty[Register::RXRF_2] = 1;
|
||||
_map.r.rxrf_1.iqerr_trim = v;
|
||||
_dirty[Register::RXRF_1] = 1;
|
||||
flush();
|
||||
_map.r.rxrf_2.RX_IQERR_SPI_EN = 1;
|
||||
_dirty[Register::RXRF_2] = 1;
|
||||
_map.r.rxrf_1.iqerr_trim = v;
|
||||
_dirty[Register::RXRF_1] = 1;
|
||||
flush();
|
||||
}
|
||||
|
||||
void MAX2839::set_rx_buff_vcm(const size_t v) {
|
||||
_map.r.lpf_vga_2.BUFF_VCM = v;
|
||||
_dirty[Register::LPF_VGA_2] = 1;
|
||||
flush();
|
||||
_map.r.lpf_vga_2.BUFF_VCM = v;
|
||||
_dirty[Register::LPF_VGA_2] = 1;
|
||||
flush();
|
||||
}
|
||||
|
||||
reg_t MAX2839::temp_sense() {
|
||||
if( !_map.r.rx_top_2.ts_en ) {
|
||||
_map.r.rx_top_2.ts_en = 1;
|
||||
flush_one(Register::RX_TOP_2);
|
||||
if (!_map.r.rx_top_2.ts_en) {
|
||||
_map.r.rx_top_2.ts_en = 1;
|
||||
flush_one(Register::RX_TOP_2);
|
||||
|
||||
chThdSleepMilliseconds(1);
|
||||
}
|
||||
chThdSleepMilliseconds(1);
|
||||
}
|
||||
|
||||
_map.r.rx_top_2.ts_adc_trigger = 1;
|
||||
flush_one(Register::RX_TOP_2);
|
||||
_map.r.rx_top_2.ts_adc_trigger = 1;
|
||||
flush_one(Register::RX_TOP_2);
|
||||
|
||||
halPolledDelay(ticks_for_temperature_sense_adc_conversion);
|
||||
halPolledDelay(ticks_for_temperature_sense_adc_conversion);
|
||||
|
||||
/*
|
||||
* Things look very similar to MAX2837, so this probably works, but the
|
||||
* MAX2839 data sheet does not describe the TEMP_SENSE register contents.
|
||||
*/
|
||||
const auto value = read(Register::TEMP_SENSE);
|
||||
/*
|
||||
* Things look very similar to MAX2837, so this probably works, but the
|
||||
* MAX2839 data sheet does not describe the TEMP_SENSE register contents.
|
||||
*/
|
||||
const auto value = read(Register::TEMP_SENSE);
|
||||
|
||||
_map.r.rx_top_2.ts_adc_trigger = 0;
|
||||
flush_one(Register::RX_TOP_2);
|
||||
_map.r.rx_top_2.ts_adc_trigger = 0;
|
||||
flush_one(Register::RX_TOP_2);
|
||||
|
||||
return value;
|
||||
return value;
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace max2839
|
File diff suppressed because it is too large
Load Diff
@@ -30,12 +30,12 @@ namespace max283x {
|
||||
|
||||
namespace lo {
|
||||
|
||||
constexpr std::array<rf::FrequencyRange, 4> band { {
|
||||
{ 2300000000, 2400000000 },
|
||||
{ 2400000000, 2500000000 },
|
||||
{ 2500000000, 2600000000 },
|
||||
{ 2600000000, 2700000000 },
|
||||
} };
|
||||
constexpr std::array<rf::FrequencyRange, 4> band{{
|
||||
{2300000000, 2400000000},
|
||||
{2400000000, 2500000000},
|
||||
{2500000000, 2600000000},
|
||||
{2600000000, 2700000000},
|
||||
}};
|
||||
|
||||
} /* namespace lo */
|
||||
|
||||
@@ -43,13 +43,13 @@ constexpr std::array<rf::FrequencyRange, 4> band { {
|
||||
|
||||
namespace lna {
|
||||
|
||||
constexpr range_t<int8_t> gain_db_range { 0, 40 };
|
||||
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 },
|
||||
} };
|
||||
constexpr std::array<rf::FrequencyRange, 2> band{{
|
||||
{2300000000, 2500000000},
|
||||
{2500000000, 2700000000},
|
||||
}};
|
||||
|
||||
} /* namespace lna */
|
||||
|
||||
@@ -57,7 +57,7 @@ constexpr std::array<rf::FrequencyRange, 2> band { {
|
||||
|
||||
namespace vga {
|
||||
|
||||
constexpr range_t<int8_t> gain_db_range { 0, 62 };
|
||||
constexpr range_t<int8_t> gain_db_range{0, 62};
|
||||
constexpr int8_t gain_db_step = 2;
|
||||
|
||||
} /* namespace vga */
|
||||
@@ -66,32 +66,32 @@ constexpr int8_t gain_db_step = 2;
|
||||
|
||||
namespace tx {
|
||||
|
||||
constexpr range_t<int8_t> gain_db_range { 0, 47 };
|
||||
constexpr range_t<int8_t> gain_db_range{0, 47};
|
||||
constexpr int8_t gain_db_step = 1;
|
||||
}
|
||||
} // namespace tx
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
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 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];
|
||||
@@ -102,37 +102,37 @@ constexpr auto bandwidth_maximum = bandwidths[bandwidths.size() - 1];
|
||||
/*************************************************************************/
|
||||
|
||||
enum Mode {
|
||||
Shutdown,
|
||||
Standby,
|
||||
Receive,
|
||||
Transmit,
|
||||
Shutdown,
|
||||
Standby,
|
||||
Receive,
|
||||
Transmit,
|
||||
};
|
||||
|
||||
using reg_t = uint16_t;
|
||||
using address_t = uint8_t;
|
||||
|
||||
class MAX283x {
|
||||
public:
|
||||
virtual ~MAX283x() = default;
|
||||
public:
|
||||
virtual ~MAX283x() = default;
|
||||
|
||||
virtual void init();
|
||||
virtual void set_mode(const Mode mode);
|
||||
virtual void init();
|
||||
virtual void set_mode(const Mode mode);
|
||||
|
||||
virtual void set_tx_vga_gain(const int_fast8_t db);
|
||||
virtual void set_lna_gain(const int_fast8_t db);
|
||||
virtual void set_vga_gain(const int_fast8_t db);
|
||||
virtual void set_lpf_rf_bandwidth(const uint32_t bandwidth_minimum);
|
||||
virtual void set_tx_vga_gain(const int_fast8_t db);
|
||||
virtual void set_lna_gain(const int_fast8_t db);
|
||||
virtual void set_vga_gain(const int_fast8_t db);
|
||||
virtual void set_lpf_rf_bandwidth(const uint32_t bandwidth_minimum);
|
||||
|
||||
virtual bool set_frequency(const rf::Frequency lo_frequency);
|
||||
virtual bool set_frequency(const rf::Frequency lo_frequency);
|
||||
|
||||
virtual void set_rx_lo_iq_calibration(const size_t v);
|
||||
virtual void set_rx_buff_vcm(const size_t v);
|
||||
virtual void set_rx_lo_iq_calibration(const size_t v);
|
||||
virtual void set_rx_buff_vcm(const size_t v);
|
||||
|
||||
virtual reg_t temp_sense();
|
||||
virtual reg_t temp_sense();
|
||||
|
||||
virtual reg_t read(const address_t reg_num);
|
||||
virtual reg_t read(const address_t reg_num);
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace max283x
|
||||
|
||||
#endif/*__MAX283X_H__*/
|
||||
#endif /*__MAX283X_H__*/
|
@@ -29,8 +29,8 @@
|
||||
namespace max5864 {
|
||||
|
||||
void MAX5864::set_mode(const Mode mode) {
|
||||
std::array<uint8_t, 1> command { toUType(mode) };
|
||||
_target.transfer(command.data(), command.size());
|
||||
std::array<uint8_t, 1> command{toUType(mode)};
|
||||
_target.transfer(command.data(), command.size());
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace max5864
|
||||
|
@@ -27,33 +27,32 @@
|
||||
namespace max5864 {
|
||||
|
||||
enum class Mode : uint8_t {
|
||||
Shutdown = 0x00,
|
||||
Idle = 0x01,
|
||||
Receive = 0x02,
|
||||
Transmit = 0x03,
|
||||
Transceiver = 0x04,
|
||||
Standby = 0x05,
|
||||
Shutdown = 0x00,
|
||||
Idle = 0x01,
|
||||
Receive = 0x02,
|
||||
Transmit = 0x03,
|
||||
Transceiver = 0x04,
|
||||
Standby = 0x05,
|
||||
};
|
||||
|
||||
class MAX5864 {
|
||||
public:
|
||||
constexpr MAX5864(
|
||||
spi::arbiter::Target& target
|
||||
) : _target(target)
|
||||
{
|
||||
}
|
||||
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 init() {
|
||||
/* Shut down explicitly, as there is no other reset mechanism. */
|
||||
set_mode(Mode::Shutdown);
|
||||
}
|
||||
|
||||
void set_mode(const Mode mode);
|
||||
void set_mode(const Mode mode);
|
||||
|
||||
private:
|
||||
spi::arbiter::Target& _target;
|
||||
private:
|
||||
spi::arbiter::Target& _target;
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace max5864
|
||||
|
||||
#endif/*__MAX5864_H__*/
|
||||
#endif /*__MAX5864_H__*/
|
||||
|
@@ -51,7 +51,7 @@ constexpr auto reference_frequency = rffc5072_reference_f;
|
||||
|
||||
namespace vco {
|
||||
|
||||
constexpr rf::FrequencyRange range { 2700000000, 5400000000 };
|
||||
constexpr rf::FrequencyRange range{2700000000, 5400000000};
|
||||
|
||||
} /* namespace vco */
|
||||
|
||||
@@ -63,24 +63,24 @@ 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 };
|
||||
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;
|
||||
}
|
||||
/* 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;
|
||||
return lo_divider_log2;
|
||||
}
|
||||
|
||||
} /* namespace lo */
|
||||
@@ -96,42 +96,40 @@ 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
|
||||
;
|
||||
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;
|
||||
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;
|
||||
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 rf::Frequency vco_frequency = lo_frequency * lo_divider;
|
||||
|
||||
const size_t prescaler_divider_log2 = prescaler::divider_log2(vco_frequency);
|
||||
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;
|
||||
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,
|
||||
};
|
||||
}
|
||||
return {
|
||||
lo_divider_log2,
|
||||
prescaler_divider_log2,
|
||||
n_divider_q24,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
/* Readback values, RFFC5072 rev A:
|
||||
@@ -146,136 +144,136 @@ struct SynthConfig {
|
||||
*/
|
||||
|
||||
void RFFC507x::init() {
|
||||
gpio_rffc5072_resetx.set();
|
||||
gpio_rffc5072_resetx.output();
|
||||
reset();
|
||||
gpio_rffc5072_resetx.set();
|
||||
gpio_rffc5072_resetx.output();
|
||||
reset();
|
||||
|
||||
_bus.init();
|
||||
_bus.init();
|
||||
|
||||
_dirty.set();
|
||||
flush();
|
||||
_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);
|
||||
/* 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();
|
||||
}
|
||||
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);
|
||||
_bus.write(reg_num, value);
|
||||
}
|
||||
|
||||
spi::reg_t RFFC507x::read(const address_t reg_num) {
|
||||
return _bus.read(reg_num);
|
||||
return _bus.read(reg_num);
|
||||
}
|
||||
|
||||
void RFFC507x::write(const Register reg, const spi::reg_t value) {
|
||||
write(toUType(reg), value);
|
||||
write(toUType(reg), value);
|
||||
}
|
||||
|
||||
spi::reg_t RFFC507x::read(const Register reg) {
|
||||
return read(toUType(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);
|
||||
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);
|
||||
_map.r.sdi_ctrl.enbl = 1;
|
||||
flush_one(Register::SDI_CTRL);
|
||||
|
||||
/* TODO: Reset PLLCPL after CT_CAL? */
|
||||
/* 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.
|
||||
*/
|
||||
/* 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);
|
||||
_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);
|
||||
/* 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);
|
||||
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);
|
||||
/* 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();
|
||||
_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;
|
||||
}
|
||||
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);
|
||||
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);
|
||||
/* 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);
|
||||
return read(Register::READBACK);
|
||||
}
|
||||
|
||||
} /* namespace rffc507x */
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -30,80 +30,78 @@ namespace rffc507x {
|
||||
namespace spi {
|
||||
|
||||
void SPI::init() {
|
||||
gpio_rffc5072_select.set();
|
||||
gpio_rffc5072_clock.clear();
|
||||
gpio_rffc5072_select.set();
|
||||
gpio_rffc5072_clock.clear();
|
||||
|
||||
gpio_rffc5072_select.output();
|
||||
gpio_rffc5072_clock.output();
|
||||
gpio_rffc5072_data.input();
|
||||
gpio_rffc5072_select.output();
|
||||
gpio_rffc5072_clock.output();
|
||||
gpio_rffc5072_data.input();
|
||||
|
||||
gpio_rffc5072_data.clear();
|
||||
gpio_rffc5072_data.clear();
|
||||
}
|
||||
|
||||
inline void SPI::select(const bool active) {
|
||||
gpio_rffc5072_select.write(!active);
|
||||
gpio_rffc5072_select.write(!active);
|
||||
}
|
||||
|
||||
inline void SPI::direction_out() {
|
||||
gpio_rffc5072_data.output();
|
||||
gpio_rffc5072_data.output();
|
||||
}
|
||||
|
||||
inline void SPI::direction_in() {
|
||||
gpio_rffc5072_data.input();
|
||||
gpio_rffc5072_data.input();
|
||||
}
|
||||
|
||||
inline void SPI::write_bit(const bit_t value) {
|
||||
gpio_rffc5072_data.write(value);
|
||||
gpio_rffc5072_data.write(value);
|
||||
}
|
||||
|
||||
inline bit_t SPI::read_bit() {
|
||||
return gpio_rffc5072_data.read() & 1;
|
||||
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;
|
||||
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 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);
|
||||
select(true);
|
||||
|
||||
const data_t address_word =
|
||||
((direction == Direction::Read) ? (1 << 7) : 0)
|
||||
| (address & 0x7f)
|
||||
;
|
||||
const data_t address_word =
|
||||
((direction == Direction::Read) ? (1 << 7) : 0) | (address & 0x7f);
|
||||
|
||||
direction_out();
|
||||
transfer_bits(address_word, 9);
|
||||
direction_out();
|
||||
transfer_bits(address_word, 9);
|
||||
|
||||
if( direction == Direction::Read ) {
|
||||
direction_in();
|
||||
transfer_bits(0, 2);
|
||||
}
|
||||
if (direction == Direction::Read) {
|
||||
direction_in();
|
||||
transfer_bits(0, 2);
|
||||
}
|
||||
|
||||
const data_t data_in = transfer_bits(data_out, 16);
|
||||
const data_t data_in = transfer_bits(data_out, 16);
|
||||
|
||||
if( direction == Direction::Write ) {
|
||||
direction_in();
|
||||
}
|
||||
if (direction == Direction::Write) {
|
||||
direction_in();
|
||||
}
|
||||
|
||||
select(false);
|
||||
select(false);
|
||||
|
||||
transfer_bits(0, 2);
|
||||
transfer_bits(0, 2);
|
||||
|
||||
return data_in;
|
||||
return data_in;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
} // namespace spi
|
||||
} // namespace rffc507x
|
||||
|
@@ -34,37 +34,37 @@ using bit_t = uint_fast8_t;
|
||||
using data_t = uint32_t;
|
||||
|
||||
class SPI {
|
||||
public:
|
||||
enum class Direction {
|
||||
Write = 0,
|
||||
Read = 1,
|
||||
};
|
||||
public:
|
||||
enum class Direction {
|
||||
Write = 0,
|
||||
Read = 1,
|
||||
};
|
||||
|
||||
void init();
|
||||
void init();
|
||||
|
||||
reg_t read(const address_t address) {
|
||||
return transfer_word(Direction::Read, address, 0);
|
||||
}
|
||||
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);
|
||||
}
|
||||
void write(const address_t address, const reg_t value) {
|
||||
transfer_word(Direction::Write, address, value);
|
||||
}
|
||||
|
||||
private:
|
||||
void select(const bool active);
|
||||
private:
|
||||
void select(const bool active);
|
||||
|
||||
void direction_out();
|
||||
void direction_in();
|
||||
void direction_out();
|
||||
void direction_in();
|
||||
|
||||
void write_bit(const bit_t value);
|
||||
bit_t read_bit();
|
||||
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);
|
||||
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 */
|
||||
} // namespace spi
|
||||
} // namespace rffc507x
|
||||
|
||||
#endif/*__RFFC507X_SPI_H__*/
|
||||
#endif /*__RFFC507X_SPI_H__*/
|
||||
|
@@ -29,77 +29,71 @@
|
||||
namespace si5351 {
|
||||
|
||||
void Si5351::reset() {
|
||||
wait_for_device_ready();
|
||||
wait_for_device_ready();
|
||||
|
||||
write_register(Register::InterruptStatusSticky, 0x00);
|
||||
write_register(Register::InterruptStatusMask, 0xf0);
|
||||
write_register(Register::InterruptStatusSticky, 0x00);
|
||||
write_register(Register::InterruptStatusMask, 0xf0);
|
||||
|
||||
disable_output_mask(0xff);
|
||||
write_register(Register::OEBPinEnableControlMask, 0xff);
|
||||
write_register(Register::PLLInputSource, 0x00);
|
||||
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()
|
||||
});
|
||||
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, 70>{Register::CLK3_0DisableState});
|
||||
|
||||
write(std::array<uint8_t, 14>{
|
||||
Register::SpreadSpectrumParameters_Base
|
||||
});
|
||||
write(std::array<uint8_t, 14>{
|
||||
Register::SpreadSpectrumParameters_Base});
|
||||
|
||||
write(std::array<uint8_t, 4>{
|
||||
Register::VCXOParameters_Base
|
||||
});
|
||||
write(std::array<uint8_t, 4>{
|
||||
Register::VCXOParameters_Base});
|
||||
|
||||
write(std::array<uint8_t, 7>{
|
||||
Register::CLKInitialPhaseOffset_Base
|
||||
});
|
||||
write(std::array<uint8_t, 7>{
|
||||
Register::CLKInitialPhaseOffset_Base});
|
||||
|
||||
write_register(Register::CrystalInternalLoadCapacitance, 0b11010010);
|
||||
write_register(Register::FanoutEnable, 0x00);
|
||||
write_register(Register::CrystalInternalLoadCapacitance, 0b11010010);
|
||||
write_register(Register::FanoutEnable, 0x00);
|
||||
|
||||
reset_plls();
|
||||
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];
|
||||
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;
|
||||
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);
|
||||
/* 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 */
|
||||
|
@@ -36,158 +36,157 @@ 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,
|
||||
};
|
||||
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;
|
||||
using Type = uint8_t;
|
||||
|
||||
enum {
|
||||
REVID_Mask = (0b11 << 0),
|
||||
enum {
|
||||
REVID_Mask = (0b11 << 0),
|
||||
|
||||
LOS_Mask = (1 << 4),
|
||||
LOS_ValidClockAtCLKIN = (0 << 4),
|
||||
LOS_LossOfSignalAtCLKIN = (1 << 4),
|
||||
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_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),
|
||||
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),
|
||||
};
|
||||
}
|
||||
SYS_INIT_Mask = (1 << 7),
|
||||
SYS_INIT_Complete = (0 << 7),
|
||||
SYS_INIT_Initializing = (1 << 7),
|
||||
};
|
||||
} // namespace DeviceStatus
|
||||
|
||||
struct ClockControl {
|
||||
enum ClockCurrentDrive {
|
||||
_2mA = 0b00,
|
||||
_4mA = 0b01,
|
||||
_6mA = 0b10,
|
||||
_8mA = 0b11,
|
||||
};
|
||||
enum ClockCurrentDrive {
|
||||
_2mA = 0b00,
|
||||
_4mA = 0b01,
|
||||
_6mA = 0b10,
|
||||
_8mA = 0b11,
|
||||
};
|
||||
|
||||
enum ClockSource {
|
||||
Xtal = 0b00,
|
||||
CLKIN = 0b01,
|
||||
MS_Group = 0b10,
|
||||
MS_Self = 0b11,
|
||||
};
|
||||
enum ClockSource {
|
||||
Xtal = 0b00,
|
||||
CLKIN = 0b01,
|
||||
MS_Group = 0b10,
|
||||
MS_Self = 0b11,
|
||||
};
|
||||
|
||||
enum ClockInvert {
|
||||
Normal = 0,
|
||||
Invert = 1,
|
||||
};
|
||||
enum ClockInvert {
|
||||
Normal = 0,
|
||||
Invert = 1,
|
||||
};
|
||||
|
||||
enum MultiSynthSource {
|
||||
PLLA = 0,
|
||||
PLLB = 1,
|
||||
};
|
||||
enum MultiSynthSource {
|
||||
PLLA = 0,
|
||||
PLLB = 1,
|
||||
};
|
||||
|
||||
enum MultiSynthMode {
|
||||
Fractional = 0,
|
||||
Integer = 1,
|
||||
};
|
||||
enum MultiSynthMode {
|
||||
Fractional = 0,
|
||||
Integer = 1,
|
||||
};
|
||||
|
||||
enum ClockPowerDown {
|
||||
Power_On = 0,
|
||||
Power_Off = 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;
|
||||
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)
|
||||
{
|
||||
}
|
||||
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_src(const ClockSource value) const {
|
||||
auto result = *this;
|
||||
result.CLK_SRC = value;
|
||||
return result;
|
||||
}
|
||||
|
||||
ClockControl clk_pdn(const ClockPowerDown value) const {
|
||||
auto result = *this;
|
||||
result.CLK_PDN = value;
|
||||
return result;
|
||||
}
|
||||
ClockControl ms_src(const MultiSynthSource value) const {
|
||||
auto result = *this;
|
||||
result.MS_SRC = value;
|
||||
return result;
|
||||
}
|
||||
|
||||
constexpr operator reg_t() {
|
||||
return *reinterpret_cast<reg_t*>(this);
|
||||
}
|
||||
ClockControl clk_pdn(const ClockPowerDown value) const {
|
||||
auto result = *this;
|
||||
result.CLK_PDN = value;
|
||||
return result;
|
||||
}
|
||||
|
||||
static constexpr ClockControl power_off() {
|
||||
return {
|
||||
ClockCurrentDrive::_2mA,
|
||||
ClockSource::Xtal,
|
||||
ClockInvert::Normal,
|
||||
MultiSynthSource::PLLA,
|
||||
MultiSynthMode::Fractional,
|
||||
ClockPowerDown::Power_Off,
|
||||
};
|
||||
}
|
||||
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");
|
||||
@@ -195,302 +194,294 @@ static_assert(sizeof(ClockControl) == 1, "ClockControl size is not eight bits");
|
||||
using ClockControls = std::array<ClockControl, 8>;
|
||||
|
||||
namespace CrystalInternalLoadCapacitance {
|
||||
using Type = uint8_t;
|
||||
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),
|
||||
};
|
||||
}
|
||||
enum {
|
||||
XTAL_CL_Mask = (0b11 << 6),
|
||||
XTAL_CL_6pF = (0b01 << 6),
|
||||
XTAL_CL_8pF = (0b10 << 6),
|
||||
XTAL_CL_10pF = (0b11 << 6),
|
||||
};
|
||||
} // namespace CrystalInternalLoadCapacitance
|
||||
|
||||
namespace PLLInputSource {
|
||||
using Type = uint8_t;
|
||||
using Type = uint8_t;
|
||||
|
||||
enum {
|
||||
PLLA_Source_Mask = (1 << 2),
|
||||
PLLA_Source_XTAL = (0 << 2),
|
||||
PLLA_Source_CLKIN = (1 << 2),
|
||||
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),
|
||||
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),
|
||||
};
|
||||
}
|
||||
CLKIN_Div_Mask = (0b11 << 6),
|
||||
CLKIN_Div1 = (0b00 << 6),
|
||||
CLKIN_Div2 = (0b01 << 6),
|
||||
CLKIN_Div4 = (0b10 << 6),
|
||||
CLKIN_Div8 = (0b11 << 6),
|
||||
};
|
||||
} // namespace PLLInputSource
|
||||
|
||||
struct Inputs {
|
||||
const uint32_t f_xtal;
|
||||
const uint32_t f_clkin;
|
||||
const uint32_t clkin_div;
|
||||
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;
|
||||
}
|
||||
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;
|
||||
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 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 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 p2() const {
|
||||
return 128 * b - c * (uint32_t)(128 * (float)b / (float)c);
|
||||
}
|
||||
|
||||
constexpr uint32_t p3() const {
|
||||
return 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),
|
||||
};
|
||||
}
|
||||
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;
|
||||
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 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 p2() const {
|
||||
return 128 * b - c * (uint32_t)(128 * (float)b / (float)c);
|
||||
}
|
||||
|
||||
constexpr uint32_t p3() const {
|
||||
return 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 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)
|
||||
};
|
||||
}
|
||||
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;
|
||||
const uint32_t f_src;
|
||||
const uint32_t a;
|
||||
const uint32_t r_div;
|
||||
|
||||
constexpr uint8_t p1() const {
|
||||
return a;
|
||||
}
|
||||
constexpr uint8_t p1() const {
|
||||
return a;
|
||||
}
|
||||
|
||||
constexpr uint32_t f_out() const {
|
||||
return f_src / a / (1 << r_div);
|
||||
}
|
||||
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)),
|
||||
};
|
||||
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;
|
||||
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)
|
||||
{
|
||||
}
|
||||
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();
|
||||
void reset();
|
||||
|
||||
uint8_t device_status() {
|
||||
return read_register(Register::DeviceStatus);
|
||||
}
|
||||
uint8_t device_status() {
|
||||
return read_register(Register::DeviceStatus);
|
||||
}
|
||||
|
||||
void wait_for_device_ready() {
|
||||
while(device_status() & 0x80);
|
||||
}
|
||||
void wait_for_device_ready() {
|
||||
while (device_status() & 0x80)
|
||||
;
|
||||
}
|
||||
|
||||
bool plla_loss_of_signal() {
|
||||
return (device_status() >> 5) & 1;
|
||||
}
|
||||
bool plla_loss_of_signal() {
|
||||
return (device_status() >> 5) & 1;
|
||||
}
|
||||
|
||||
bool clkin_loss_of_signal() {
|
||||
return (device_status() >> 4) & 1;
|
||||
}
|
||||
|
||||
void enable_fanout() {
|
||||
write_register(Register::FanoutEnable, 0b11010000);
|
||||
}
|
||||
bool clkin_loss_of_signal() {
|
||||
return (device_status() >> 4) & 1;
|
||||
}
|
||||
|
||||
void reset_plls() {
|
||||
// Datasheet recommends value 0xac, though the low nibble bits are not defined in AN619.
|
||||
write_register(Register::PLLReset, 0xac);
|
||||
}
|
||||
void enable_fanout() {
|
||||
write_register(Register::FanoutEnable, 0b11010000);
|
||||
}
|
||||
|
||||
regvalue_t read_register(const uint8_t reg);
|
||||
void reset_plls() {
|
||||
// Datasheet recommends value 0xac, though the low nibble bits are not defined in AN619.
|
||||
write_register(Register::PLLReset, 0xac);
|
||||
}
|
||||
|
||||
template<size_t N>
|
||||
void write(const std::array<uint8_t, N>& values) {
|
||||
_bus.transmit(_address, values.data(), values.size());
|
||||
}
|
||||
regvalue_t read_register(const uint8_t reg);
|
||||
|
||||
void write_register(const uint8_t reg, const regvalue_t value) {
|
||||
write(std::array<uint8_t, 2>{
|
||||
reg, value
|
||||
});
|
||||
}
|
||||
template <size_t N>
|
||||
void write(const std::array<uint8_t, N>& values) {
|
||||
_bus.transmit(_address, values.data(), values.size());
|
||||
}
|
||||
|
||||
void write(const size_t ms_number, const MultisynthFractional& config) {
|
||||
write(config.reg(ms_number));
|
||||
}
|
||||
void write_register(const uint8_t reg, const regvalue_t value) {
|
||||
write(std::array<uint8_t, 2>{
|
||||
reg, value});
|
||||
}
|
||||
|
||||
void set_ms_frequency(
|
||||
const size_t ms_number,
|
||||
const uint32_t frequency,
|
||||
const uint32_t vco_frequency,
|
||||
const size_t r_div
|
||||
);
|
||||
void write(const size_t ms_number, const MultisynthFractional& config) {
|
||||
write(config.reg(ms_number));
|
||||
}
|
||||
|
||||
void set_crystal_internal_load_capacitance(const CrystalInternalLoadCapacitance::Type xtal_cl) {
|
||||
write_register(Register::CrystalInternalLoadCapacitance, xtal_cl);
|
||||
}
|
||||
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_pll_input_sources(const PLLInputSource::Type value) {
|
||||
write_register(Register::PLLInputSource, value);
|
||||
}
|
||||
void set_crystal_internal_load_capacitance(const CrystalInternalLoadCapacitance::Type xtal_cl) {
|
||||
write_register(Register::CrystalInternalLoadCapacitance, xtal_cl);
|
||||
}
|
||||
|
||||
void enable_output_mask(const uint8_t mask) {
|
||||
_output_enable |= mask;
|
||||
update_output_enable_control();
|
||||
}
|
||||
void set_pll_input_sources(const PLLInputSource::Type value) {
|
||||
write_register(Register::PLLInputSource, value);
|
||||
}
|
||||
|
||||
void enable_output(const size_t n) {
|
||||
enable_output_mask(1 << n);
|
||||
}
|
||||
void enable_output_mask(const uint8_t mask) {
|
||||
_output_enable |= mask;
|
||||
update_output_enable_control();
|
||||
}
|
||||
|
||||
void disable_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(const size_t n) {
|
||||
disable_output_mask(1 << n);
|
||||
}
|
||||
void disable_output_mask(const uint8_t mask) {
|
||||
_output_enable &= ~mask;
|
||||
update_output_enable_control();
|
||||
}
|
||||
|
||||
void set_clock_control(const ClockControls& clock_control) {
|
||||
_clock_control = clock_control;
|
||||
update_all_clock_control();
|
||||
}
|
||||
void disable_output(const size_t n) {
|
||||
disable_output_mask(1 << n);
|
||||
}
|
||||
|
||||
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 set_clock_control(const ClockControls& clock_control) {
|
||||
_clock_control = clock_control;
|
||||
update_all_clock_control();
|
||||
}
|
||||
|
||||
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 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 disable_clock(const size_t n) {
|
||||
_clock_control[n].CLK_PDN = ClockControl::ClockPowerDown::Power_Off;
|
||||
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]);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
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]);
|
||||
}
|
||||
|
||||
private:
|
||||
ClockControls _clock_control;
|
||||
I2C& _bus;
|
||||
const I2C::address_t _address;
|
||||
uint8_t _output_enable;
|
||||
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);
|
||||
}
|
||||
|
||||
void update_output_enable_control() {
|
||||
write_register(Register::OutputEnableControl, ~_output_enable);
|
||||
}
|
||||
private:
|
||||
ClockControls _clock_control;
|
||||
I2C& _bus;
|
||||
const I2C::address_t _address;
|
||||
uint8_t _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],
|
||||
} });
|
||||
}
|
||||
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],
|
||||
}});
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace si5351
|
||||
|
||||
#endif/*__SI5351_H__*/
|
||||
#endif /*__SI5351_H__*/
|
||||
|
@@ -30,48 +30,46 @@ namespace spi {
|
||||
namespace arbiter {
|
||||
|
||||
class Arbiter {
|
||||
public:
|
||||
constexpr Arbiter(
|
||||
SPI& bus
|
||||
) : _bus(bus),
|
||||
_config(nullptr)
|
||||
{
|
||||
}
|
||||
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);
|
||||
}
|
||||
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;
|
||||
private:
|
||||
SPI& _bus;
|
||||
const SPIConfig* _config;
|
||||
};
|
||||
|
||||
class Target {
|
||||
public:
|
||||
constexpr Target(
|
||||
Arbiter& arbiter,
|
||||
const SPIConfig& config
|
||||
) : _arbiter(arbiter),
|
||||
_config(config)
|
||||
{
|
||||
}
|
||||
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);
|
||||
}
|
||||
void transfer(void* const data, const size_t count) {
|
||||
_arbiter.transfer(&_config, data, count);
|
||||
}
|
||||
|
||||
private:
|
||||
Arbiter& _arbiter;
|
||||
const SPIConfig _config;
|
||||
private:
|
||||
Arbiter& _arbiter;
|
||||
const SPIConfig _config;
|
||||
};
|
||||
|
||||
} /* arbiter */
|
||||
} /* spi */
|
||||
} // namespace arbiter
|
||||
} // namespace spi
|
||||
|
||||
#endif/*__SPI_ARBITER_H__*/
|
||||
#endif /*__SPI_ARBITER_H__*/
|
||||
|
@@ -28,29 +28,29 @@
|
||||
#include "hal.h"
|
||||
|
||||
class SPI {
|
||||
public:
|
||||
constexpr SPI(SPIDriver* const driver) :
|
||||
_driver(driver) {
|
||||
}
|
||||
public:
|
||||
constexpr SPI(SPIDriver* const driver)
|
||||
: _driver(driver) {
|
||||
}
|
||||
|
||||
void start(const SPIConfig& config) {
|
||||
spiStart(_driver, &config);
|
||||
}
|
||||
void start(const SPIConfig& config) {
|
||||
spiStart(_driver, &config);
|
||||
}
|
||||
|
||||
void stop() {
|
||||
spiStop(_driver);
|
||||
}
|
||||
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);
|
||||
}
|
||||
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;
|
||||
private:
|
||||
SPIDriver* const _driver;
|
||||
};
|
||||
|
||||
#endif/*__SPI_PP_H__*/
|
||||
#endif /*__SPI_PP_H__*/
|
||||
|
@@ -39,47 +39,43 @@ 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)
|
||||
;
|
||||
(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::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,
|
||||
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);
|
||||
adc0::clock_enable();
|
||||
adc0::interrupts_disable();
|
||||
adc0::power_up(adc0_config);
|
||||
adc0::interrupts_enable(adc0_interrupt_mask);
|
||||
}
|
||||
|
||||
void start() {
|
||||
adc0::start_burst();
|
||||
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,
|
||||
};
|
||||
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 */
|
||||
|
@@ -35,4 +35,4 @@ Samples get();
|
||||
} /* namespace adc */
|
||||
} /* namespace touch */
|
||||
|
||||
#endif/*__TOUCH_ADC_H__*/
|
||||
#endif /*__TOUCH_ADC_H__*/
|
||||
|
Reference in New Issue
Block a user