23#include <etl/binary.h>
24#include <etl/vector.h>
34static uint8_t tx_bb09_pdu[AT86RF215_MAX_PDU];
35static uint8_t tx_bb24_pdu[AT86RF215_MAX_PDU];
42static constexpr std::array<float, 8> fsk_mod_idxs = {
43 0.375f, 0.5f, 0.75f, 1.0f, 1.25f, 1.5f, 1.75f, 2.0f};
50static constexpr std::array<float, 4> fsk_bt = {0.5f, 1.0f, 1.5f, 2.0f};
57 if (h->rst_gpio_dev) {
59 rst_gpio_dev->
set(enable);
93 size_t tx_len,
size_t rx_len)
96 spi_dev->
read(out, in, tx_len, rx_len);
105 spi_dev->
write(in, len);
111 uint8_t rf24_irqs, uint8_t bbc0_irqs,
114 radio *r =
reinterpret_cast<radio *
>(h->user_dev1);
116 if (bbc0_irqs & bit<uint8_t, 1>()) {
117 at86rf215_rx_frame(h, AT86RF215_RF09,
rx_msg.
pdu,
125 int ret = at86rf215_get_edv(h, AT86RF215_RF09, &
rx_msg.
info.
rssi);
130 ret = r->m_rxq.put_isr(
rx_msg);
137 at86rf215_rx(h, AT86RF215_RF09, 1000);
141 if (bbc1_irqs & bit<uint8_t, 1>()) {
142 at86rf215_rx_frame(h, AT86RF215_RF24,
rx_msg.
pdu,
150 int ret = at86rf215_get_edv(h, AT86RF215_RF24, &
rx_msg.
info.
rssi);
155 ret = r->m_rxq.put_isr(
rx_msg);
162 at86rf215_rx(h, AT86RF215_RF24, 1000);
174static at86rf215_radio_t
179 return AT86RF215_RF09;
181 return AT86RF215_RF24;
183 throw inval_arg_exception(__FILE__, __LINE__);
199 : m_frame_len{p.uhf_len, p.sband_len},
202 m_en_ext_clk(cnf.en_ext_clk),
203 m_tx_mixer_params(p.tx_mixer_params),
204 m_rx_mixer_params(p.rx_mixer_params),
205 m_iface_enabled{false, false},
206 m_rffe09(p.rffe09_params, cnf.
rffe09_io, pwr),
207 m_rffe24(p.rffe24_params, cnf.
rffe24_io, pwr),
208 m_chrono(cnf.chrono),
211 m_req_clk_src(p.clk),
231 return at86rf215_irq_callback(&r.m_at86);
251 at86rf215_enable(
true);
264radio::enable_uhf(
bool yes)
270 if (
enabled() ==
false && yes ==
false) {
279 ret = at86rf215_set_cmd(&m_at86, AT86RF215_CMD_RF_TXPREP, AT86RF215_RF09);
281 ret = at86rf215_set_trxoff(&m_at86, AT86RF215_RF09, 20);
283 ret += at86rf215_radio_irq_clear(&m_at86, AT86RF215_RF09);
285 throw radio_exception(__FILE__, __LINE__);
287 m_rffe09.enable(yes);
291 m_rffe09.enable(
false);
293 std::rethrow_exception(std::current_exception());
298radio::enable_sband(
bool yes)
304 if (
enabled() ==
false && yes ==
false) {
305 m_rffe24.enable(
false);
313 ret = at86rf215_set_cmd(&m_at86, AT86RF215_CMD_RF_TXPREP, AT86RF215_RF24);
315 ret = at86rf215_set_trxoff(&m_at86, AT86RF215_RF24, 20);
317 ret += at86rf215_radio_irq_clear(&m_at86, AT86RF215_RF24);
319 throw radio_exception(__FILE__, __LINE__);
321 m_rffe24.enable(yes);
325 m_rffe24.enable(
false);
327 std::rethrow_exception(std::current_exception());
345 if (yes &&
enabled() ==
false) {
375 at86rf215_conn_check(&m_at86) == AT86RF215_OK;
395 const at86rf215_radio_t r = iface_to_at86_radio(iface);
399 return m_rffe09.enable(
false);
402 return m_rffe24.enable(
false);
409 at86rf215_set_trxoff(&m_at86, r, 20);
424 return enabled() && m_iface_enabled[
static_cast<uint8_t
>(iface)];
439 return m_rffe09.direction();
442 return m_rffe24.direction();
462 if (m_rffe09.frequency_valid(d, freq) ==
false) {
466 m_rffe09.set_direction(d);
467 struct at86rf215_radio_conf cnf = {.cm = AT86RF215_CM_FINE_RES_04,
468 .lbw = AT86RF215_PLL_LBW_DEFAULT};
469 ret = at86rf215_radio_conf(&m_at86, AT86RF215_RF09, &cnf);
474 ret = at86rf215_set_freq(&m_at86, AT86RF215_RF09, freq);
480 if (m_rffe24.frequency_valid(d, freq) ==
false) {
484 uint64_t lo = lo_freq(freq, d);
485 m_rffe24.set_direction(d, lo);
486 struct at86rf215_radio_conf cnf = {.cm = AT86RF215_CM_FINE_RES_24,
487 .lbw = AT86RF215_PLL_LBW_DEFAULT};
489 ret = at86rf215_radio_conf(&m_at86, AT86RF215_RF24, &cnf);
494 ret = at86rf215_set_freq(&m_at86, AT86RF215_RF24, if_freq(freq, d));
534 const at86rf215_radio_t r = iface_to_at86_radio(iface);
535 gain = etl::clamp(gain, 0.0f, 31.0f);
536 at86rf215_set_pac(&m_at86, r, AT86RF215_PACUR_NO_RED, std::round(gain));
555 const at86rf215_radio_t r = iface_to_at86_radio(iface);
557 at86rf215_agc_conf agc_cnf = {.enable = 0,
560 .avgs = AT86RF215_AVGS_8,
563 .tgt = AT86RF215_TGT_M21};
573 etl::clamp<uint8_t>(std::lroundf(gain.
gain1.
gain / 3.0f), 0U, 23U);
575 int ret = at86rf215_set_agc(&m_at86, r, &agc_cnf);
577 throw radio_exception(__FILE__, __LINE__);
583 m_rffe09.set_rx_gain(gain);
586 m_rffe24.set_rx_gain(gain);
589 throw radio_exception(__FILE__, __LINE__);
605 return x >= p.
freq.first && x <= p.
freq.second;
624 for (
const auto &i : m_rx_mixer_params) {
625 if (freq_in_range(i, rf_freq)) {
629 if (rf_freq < m_rx_mixer_params[0].freq.first) {
630 return m_rx_mixer_params[0].lo;
632 return m_rx_mixer_params[1].lo;
635 for (
const auto &i : m_tx_mixer_params) {
636 if (freq_in_range(i, rf_freq)) {
640 if (rf_freq < m_tx_mixer_params[0].freq.first) {
641 return m_tx_mixer_params[0].lo;
643 return m_tx_mixer_params[1].lo;
646 throw radio_exception(__FILE__, __LINE__);
663 uint64_t res = rf_freq + lo_freq(rf_freq, d);
664 if (res > 0xFFFFFFFF) {
665 throw radio_exception(__FILE__, __LINE__);
680static std::pair<at86rf215_fsk_midx_t, at86rf215_fsk_midxs_t>
681tune_fsk_mod_idx(
float mod_idx)
683 float diff = std::fabs(mod_idx - fsk_mod_idxs[0] * 7.0f / 8.0f);
686 for (uint8_t i = 0; i < fsk_mod_idxs.size(); i++) {
687 float scaling = 7.0f / 8.0f;
688 for (uint8_t j = 0; j < 4; j++) {
689 const auto x = std::fabs(mod_idx - fsk_mod_idxs[i] * scaling);
695 scaling += 1.0f / 8.0f;
698 return {
static_cast<at86rf215_fsk_midx_t
>(mod),
699 static_cast<at86rf215_fsk_midxs_t
>(s)};
709static at86rf215_fsk_bt_t
712 float diff = std::fabs(bt - fsk_bt[0]);
714 for (uint8_t i = 1; i < fsk_bt.size(); i++) {
715 const float x = std::fabs(bt - fsk_bt[i]);
721 return static_cast<at86rf215_fsk_bt_t
>(idx);
733 const at86rf215_radio_t r = iface_to_at86_radio(iface);
736 at86rf215_set_trxoff(&m_at86, r, 20);
739 at86rf215_set_cmd(&m_at86, AT86RF215_CMD_RF_TXPREP, r);
742 m_chrono.delay_us(pll_ch_sw_delay_us);
748 int ret = at86rf215_set_radio_irq_mask(&m_at86, r, TX_RF_IRQM);
749 ret += at86rf215_set_bbc_irq_mask(&m_at86, r, TX_BB_IRQM);
755 auto mod_idx = tune_fsk_mod_idx(conf.
fsk.
mod_idx);
758 struct at86rf215_bb_conf at86_bb;
762 at86_bb.fcst = AT86RF215_FCS_16;
763 at86_bb.pt = AT86RF215_BB_MRFSK;
764 at86_bb.fsk.mord = AT86RF215_2FSK;
765 at86_bb.fsk.midx = mod_idx.first;
766 at86_bb.fsk.midxs = mod_idx.second;
768 at86_bb.fsk.srate = conf.
fsk.
rate;
770 at86_bb.fsk.rxo = AT86RF215_FSK_RXO_DISABLED;
771 at86_bb.fsk.pdtm = 0;
772 at86_bb.fsk.rxpto = 1;
775 at86_bb.fsk.fecs = AT86RF215_FSK_FEC_RSC;
776 at86_bb.fsk.fecie = 0;
777 at86_bb.fsk.sfd_threshold = 15;
778 at86_bb.fsk.preamble_threshold = 8;
779 at86_bb.fsk.sfdq = 1;
780 at86_bb.fsk.sfd32 = 1;
781 at86_bb.fsk.rawrbit = 1;
782 at86_bb.fsk.csfd1 = AT86RF215_SFD_UNCODED_RAW;
783 at86_bb.fsk.csfd0 = AT86RF215_SFD_UNCODED_RAW;
786 at86_bb.fsk.sfd0 =
static_cast<uint16_t
>(etl::reverse_bits(conf.
sync));
787 at86_bb.fsk.sfd1 =
static_cast<uint16_t
>(etl::reverse_bits(conf.
sync) >> 16);
790 at86_bb.fsk.preemphasis = 0;
793 ret = at86rf215_bb_conf(&m_at86, r, &at86_bb);
794 ret += at86rf215_set_mode(&m_at86, AT86RF215_RF_MODE_BBRF);
795 ret += at86rf215_set_cmd(&m_at86, AT86RF215_CMD_RF_TXPREP, r);
796 ret += at86rf215_radio_irq_clear(&m_at86, r);
797 ret += at86rf215_bb_enable(&m_at86, r, 1);
820 const at86rf215_radio_t r = iface_to_at86_radio(iface);
821 uint8_t *tx_pdu = iface ==
interface::UHF ? tx_bb09_pdu : tx_bb24_pdu;
823 memcpy(tx_pdu, b,
tx_len(iface));
825 int ret = at86rf215_tx_frame(&m_at86, r, tx_pdu,
tx_len(iface), 2000);
827 if (ret == -AT86RF215_TIMEOUT) {
847 const at86rf215_radio_t r = iface_to_at86_radio(iface);
850 at86rf215_set_trxoff(&m_at86, r, 20);
852 at86rf215_set_cmd(&m_at86, AT86RF215_CMD_RF_TXPREP, r);
855 m_chrono.delay_us(pll_ch_sw_delay_us);
861 int ret = at86rf215_set_radio_irq_mask(&m_at86, r, RX_RF_IRQM);
862 ret += at86rf215_set_bbc_irq_mask(&m_at86, r, RX_BB_IRQM);
867 set_rx_gain(iface, conf.
gain);
877 auto mod_idx = tune_fsk_mod_idx(conf.
fsk.
mod_idx);
880 struct at86rf215_bb_conf at86_bb;
884 at86_bb.fcst = AT86RF215_FCS_16;
885 at86_bb.pt = AT86RF215_BB_MRFSK;
886 at86_bb.fsk.mord = AT86RF215_2FSK;
887 at86_bb.fsk.midx = mod_idx.first;
888 at86_bb.fsk.midxs = mod_idx.second;
890 at86_bb.fsk.srate = conf.
fsk.
rate;
892 at86_bb.fsk.rxo = AT86RF215_FSK_RXO_DISABLED;
893 at86_bb.fsk.pdtm = 0;
894 at86_bb.fsk.rxpto = 1;
897 at86_bb.fsk.fecs = AT86RF215_FSK_FEC_RSC;
898 at86_bb.fsk.fecie = 0;
899 at86_bb.fsk.sfd_threshold = 12;
900 at86_bb.fsk.preamble_threshold = 4;
901 at86_bb.fsk.sfdq = 0;
902 at86_bb.fsk.sfd32 = 1;
903 at86_bb.fsk.rawrbit = 1;
904 at86_bb.fsk.csfd1 = AT86RF215_SFD_UNCODED_RAW;
905 at86_bb.fsk.csfd0 = AT86RF215_SFD_UNCODED_RAW;
908 at86_bb.fsk.sfd0 =
static_cast<uint16_t
>(etl::reverse_bits(conf.
sync));
909 at86_bb.fsk.sfd1 =
static_cast<uint16_t
>(etl::reverse_bits(conf.
sync) >> 16);
912 at86_bb.fsk.preemphasis = 0;
916 at86_bb.fsk.fskrrxf =
rx_len(iface);
918 ret = at86rf215_bb_conf(&m_at86, r, &at86_bb);
919 ret += at86rf215_set_mode(&m_at86, AT86RF215_RF_MODE_BBRF);
920 ret += at86rf215_radio_irq_clear(&m_at86, r);
921 ret += at86rf215_bb_enable(&m_at86, r, 1);
922 ret += at86rf215_rx(&m_at86, r, 1000);
937 return m_rffe24.mixer_lock();
970 return m_rxq.get(&msg, timeout_ms);
982 return m_frame_len[iface_idx(iface)].tx;
994 return m_frame_len[iface_idx(iface)].rx;
1006 m_at86.spi_dev = &m_spi;
1007 m_at86.cs_gpio_dev = &m_spi.
cs();
1008 m_at86.rst_gpio_dev = &m_nrst;
1010 m_at86.user_dev0 = &m_chrono;
1015 m_at86.user_dev1 =
this;
1024radio::at86rf215_enable(
bool enable)
1032 m_at86.clk_drv = AT86RF215_RF_DRVCLKO4;
1033 m_at86.clko_os = AT86RF215_RF_CLKO_26_MHZ;
1038 m_at86.pad_drv = AT86RF215_RF_DRV8;
1039 m_at86.rf_femode_09 = AT86RF215_RF_FEMODE2;
1040 m_at86.rf_femode_24 = AT86RF215_RF_FEMODE2;
1050 m_chrono.
delay_us(clk_rfsw_delay_us);
1051 ret = at86rf215_init(&m_at86);
1054 m_en_ext_clk.
set(
false);
1055 m_chrono.
delay_us(clk_rfsw_delay_us);
1056 ret = at86rf215_init(&m_at86);
1063 m_en_ext_clk.set(
false);
1064 m_chrono.delay_us(clk_rfsw_delay_us);
1066 ret = at86rf215_init(&m_at86);
1074 throw radio_exception(__FILE__, __LINE__);
1077 at86rf215_set_trxoff(&m_at86, AT86RF215_RF09, 20);
1078 at86rf215_set_trxoff(&m_at86, AT86RF215_RF24, 20);
1079 at86rf215_irq_clear(&m_at86);
1096 m_req_clk_src = src;
1120 at86rf215_pll_ls_t status = AT86RF215_PLL_UNLOCKED;
1123 at86rf215_get_pll_ls(&m_at86, &status, iface_to_at86_radio(
interface::UHF));
1124 return status == AT86RF215_PLL_LOCKED;
1127 at86rf215_get_pll_ls(&m_at86, &status,
1129 return status == AT86RF215_PLL_LOCKED;
1132 return AT86RF215_PLL_UNLOCKED;
1149 m_rx_drop = callback;
1153radio::iface_idx(interface iface)
const
1158 return static_cast<uint32_t
>(iface);
comms::lib::radio & radio()
Returns a reference to the radio subsystem.
static board & get_instance()
Gets a reference to the single instance of the Board interface class.
Chrono device abstraction.
virtual int64_t time_ms()=0
Retrieves the current time in milliseconds.
virtual void delay_us(size_t us)=0
Delays the execution of the active task for at least us microseconds.
virtual void set_raw(bool s)=0
Sets the physical output of the pin.
virtual void set(bool s)=0
Sets the logical output of the pin. For example, if the pin has been configured as active low,...
Message queue device abstraction.
virtual gpio & cs()
Returns a GPIO control handle for the CS of the SPI. If this is performed automatically by the hardwa...
virtual void read(uint8_t *rx, const uint8_t *tx, size_t tx_len, size_t rx_len)=0
Performs an SPI read operation.
virtual void write(const uint8_t *tx, size_t len)=0
Performs an SPI write operation.
Generic exception indicating an invalid argument.
Exception indicating PLL lock status issue.
Manages power supplies and monitors subsystem status.
at86rf215_fsk_srate_t rate
FSK data rate.
float excess_bw
FSK 3-dB bandwidth of the shaping filter.
IO configuration that is necessary for the radio to operate.
Configuration parameters for the RF mixer.
std::pair< uint32_t, uint32_t > freq
Initialization parameters of the radio class.
RX configuration parameters.
fsk_conf fsk
FSK parameters.
uint32_t sync
The synchronization word.
rf_frontend::filter filter
Hardware filter selection.
uint32_t freq
The desired RX frequency to set.
rf_frontend::rx_gain_params gain
Gain settings to set.
uint8_t preamble_len
The length of the preamble in bytes.
uint32_t len
The frame length.
The RX message accompanied by its metadata.
uint8_t pdu[AT86RF215_MAX_PDU]
Buffer to hold the received frame.
uint32_t sync
The synchronization word.
fsk_conf fsk
FSK parameters.
uint32_t freq
The desired RX frequency to set.
uint8_t preamble_len
The length of the preamble in bytes.
Exception indicating a generic exception of the radio subsystem.
void rx_async(interface iface, const rx_conf &conf)
Sets the radio in reception mode asynchronously.
rf_frontend::dir direction(interface iface) const
Fetches the current direction (RX/TX) of the specific interface.
static int trx_irq_handler()
The IRQ handler of the AT86RF215 IRQ.
void tx(interface iface, const uint8_t *b)
Performs a TX in blocking mode.
void register_rx_drop_callback(etl::delegate< void(interface)> callback)
Registers a callback for the RX drop message event.
void set_frequency(interface iface, rf_frontend::dir d, uint32_t freq)
Sets the frequency and the direction.
interface
Radio interface identifier.
bool mixer_lock()
Gets the status of the RF mixer lock.
clk_src
PLL Reference Clock identifier.
@ INTERNAL
Internal Crystal Oscillator.
@ EXTERNAL
External Reference Clock (TCXO).
uint32_t frequency(interface iface, rf_frontend::dir d) const
Returns the actual frequency that the hardware reports.
uint32_t rx_len(interface iface) const
Retrieves the configured RX frame size for a specific interface.
radio(const params &p, const io_conf &cnf, power &pwr, bsp::imsgq< rx_msg > &rx_msgq)
Construct a new radio::radio object.
void set_tx_conf(interface iface, const tx_conf &conf)
Performs the configuration of a specific interface for TX.
void stop(interface iface, size_t timeout_ms=1000)
Sets the FSM of the AT86RF215 interface to TRXOFF.
bool get_pll_ls(interface iface)
Checks the PLL lock status of the AT86RF215 transceiver for the specified radio interface.
void enable(bool yes=true)
Enable/disable the radio chain.
clk_src get_clk_src() const
Retrieves the current clock source of the AT86RF215 PLL.
void set_tx_gain(interface iface, float gain)
Sets the TX gain of the AT86RF215.
int recv_msg(rx_msg &msg, size_t timeout_ms)
Gets an available message from the receive message queue.
uint32_t tx_len(interface iface) const
Retrieves the configured TX frame size for a specific interface.
bool enabled() const
Checks if the radio transceiver is in an operational state. This means that their powesr sources are ...
void set_clk_src(clk_src src)
Sets the clock source for the AT86RF215 PLL.
RF-frontend for the UHF interface.
void enable(bool set=true)
RF-frontend for the S-Band interface.
RX gain settings for the two different gain stages. Gain0 stage corresponds to the first stage (close...
union satnogs::comms::lib::rf_frontend::rx_gain_params::@215011014006014333052114215072331261330071377362 gain1
float gain
Fixed gain value in dB [-6, 29.5].
virtual void set_filter(filter f)
Generic timeout exception.
Exception indicating an invalid frequency.
int at86rf215_set_seln(struct at86rf215 *h, uint8_t enable)
int at86rf215_irq_user_callback(struct at86rf215 *h, uint8_t rf09_irqs, uint8_t rf24_irqs, uint8_t bbc0_irqs, uint8_t bbc1_irqs)
void at86rf215_delay_us(struct at86rf215 *h, uint32_t us)
int at86rf215_spi_read(struct at86rf215 *h, uint8_t *out, const uint8_t *in, size_t tx_len, size_t rx_len)
size_t at86rf215_get_time_ms(struct at86rf215 *h)
int at86rf215_spi_write(struct at86rf215 *h, const uint8_t *in, size_t len)
int at86rf215_set_rstn(struct at86rf215 *h, uint8_t enable)
scl::rf_frontend09::io_conf rffe09_io
scl::rf_frontend24::io_conf rffe24_io