kopia lustrzana https://github.com/cariboulabs/cariboulite
705 wiersze
29 KiB
C
705 wiersze
29 KiB
C
#ifndef ZF_LOG_LEVEL
|
|
#define ZF_LOG_LEVEL ZF_LOG_VERBOSE
|
|
#endif
|
|
|
|
#define ZF_LOG_DEF_SRCLOC ZF_LOG_SRCLOC_LONG
|
|
#define ZF_LOG_TAG "AT86RF215_Radio"
|
|
|
|
#include <stdint.h>
|
|
#include <math.h>
|
|
#include <string.h>
|
|
#include <stdbool.h>
|
|
#include <stdio.h>
|
|
#include "zf_log/zf_log.h"
|
|
#include "io_utils/io_utils.h"
|
|
#include "io_utils/io_utils_spi.h"
|
|
#include "at86rf215_radio.h"
|
|
#include "at86rf215_regs.h"
|
|
|
|
static const struct at86rf215_radio_regs RF09_regs = {
|
|
.RG_IRQS = 0x00,
|
|
.RG_IRQM = 0x100,
|
|
.RG_AUXS = 0x101,
|
|
.RG_STATE = 0x102,
|
|
.RG_CMD = 0x103,
|
|
.RG_CS = 0x104,
|
|
.RG_CCF0L = 0x105,
|
|
.RG_CCF0H = 0x106,
|
|
.RG_CNL = 0x107,
|
|
.RG_CNM = 0x108,
|
|
.RG_RXBWC = 0x109,
|
|
.RG_RXDFE = 0x10A,
|
|
.RG_AGCC = 0x10B,
|
|
.RG_AGCS = 0x10C,
|
|
.RG_RSSI = 0x10D,
|
|
.RG_EDC = 0x10E,
|
|
.RG_EDD = 0x10F,
|
|
.RG_EDV = 0x110,
|
|
.RG_RNDV = 0x111,
|
|
.RG_TXCUTC = 0x112,
|
|
.RG_TXDFE = 0x113,
|
|
.RG_PAC = 0x114,
|
|
.RG_PADFE = 0x116,
|
|
.RG_PLL = 0x121,
|
|
.RG_PLLCF = 0x122,
|
|
.RG_TXCI = 0x125,
|
|
.RG_TXCQ = 0x126,
|
|
.RG_TXDACI = 0x127,
|
|
.RG_TXDACQ = 0x128,
|
|
};
|
|
|
|
static const struct at86rf215_radio_regs RF24_regs = {
|
|
.RG_IRQS = 0x01,
|
|
.RG_IRQM = 0x200,
|
|
.RG_AUXS = 0x201,
|
|
.RG_STATE = 0x202,
|
|
.RG_CMD = 0x203,
|
|
.RG_CS = 0x204,
|
|
.RG_CCF0L = 0x205,
|
|
.RG_CCF0H = 0x206,
|
|
.RG_CNL = 0x207,
|
|
.RG_CNM = 0x208,
|
|
.RG_RXBWC = 0x209,
|
|
.RG_RXDFE = 0x20A,
|
|
.RG_AGCC = 0x20B,
|
|
.RG_AGCS = 0x20C,
|
|
.RG_RSSI = 0x20D,
|
|
.RG_EDC = 0x20E,
|
|
.RG_EDD = 0x20F,
|
|
.RG_EDV = 0x210,
|
|
.RG_RNDV = 0x211,
|
|
.RG_TXCUTC = 0x212,
|
|
.RG_TXDFE = 0x213,
|
|
.RG_PAC = 0x214,
|
|
.RG_PADFE = 0x216,
|
|
.RG_PLL = 0x221,
|
|
.RG_PLLCF = 0x222,
|
|
.RG_TXCI = 0x225,
|
|
.RG_TXCQ = 0x226,
|
|
.RG_TXDACI = 0x227,
|
|
.RG_TXDACQ = 0x228,
|
|
};
|
|
|
|
#define AT86RF215_REG_ADDR(c,r) \
|
|
(((c)==at86rf215_rf_channel_900mhz)?(RF09_regs.RG_##r):(RF24_regs.RG_##r))
|
|
|
|
|
|
//==================================================================================
|
|
void at86rf215_radio_setup_interrupt_mask(at86rf215_st* dev, at86rf215_rf_channel_en ch,
|
|
at86rf215_radio_irq_st* mask)
|
|
{
|
|
uint16_t reg_address = AT86RF215_REG_ADDR(ch, IRQM);
|
|
at86rf215_write_byte(dev, reg_address, *((uint8_t*)mask));
|
|
}
|
|
|
|
//==================================================================================
|
|
void at86rf215_radio_setup_external_settings(at86rf215_st* dev, at86rf215_rf_channel_en ch,
|
|
at86rf215_radio_external_ctrl_st* cfg)
|
|
{
|
|
// "RG_AUXS" RFn_AUXS – Transceiver Auxiliary Settings
|
|
uint16_t reg_address_aux = AT86RF215_REG_ADDR(ch, AUXS);
|
|
uint16_t reg_address_pad = AT86RF215_REG_ADDR(ch, PADFE);
|
|
|
|
uint8_t aux = 0;
|
|
aux |= (cfg->ext_lna_bypass_available&0x1) << 7;
|
|
aux |= (cfg->agc_backoff&0x3) << 5;
|
|
aux |= (cfg->analog_voltage_external&0x1) << 4;
|
|
aux |= (cfg->analog_voltage_enable_in_off&0x1) << 3;
|
|
aux |= (cfg->int_power_amplifier_voltage&0x3) << 0;
|
|
|
|
at86rf215_write_byte(dev, reg_address_aux, aux);
|
|
|
|
uint8_t pad = (cfg->fe_pad_configuration&0x3) << 6;
|
|
at86rf215_write_byte(dev, reg_address_pad, pad);
|
|
}
|
|
|
|
//==================================================================================
|
|
void at86rf215_radio_get_external_settings(at86rf215_st* dev, at86rf215_rf_channel_en ch,
|
|
at86rf215_radio_external_ctrl_st* cfg)
|
|
{
|
|
uint16_t reg_address_aux = AT86RF215_REG_ADDR(ch, AUXS);
|
|
uint16_t reg_address_pad = AT86RF215_REG_ADDR(ch, PADFE);
|
|
|
|
uint8_t aux = at86rf215_read_byte(dev, reg_address_aux);
|
|
uint8_t pad = at86rf215_read_byte(dev, reg_address_pad);
|
|
|
|
cfg->fe_pad_configuration = pad>>6;
|
|
cfg->ext_lna_bypass_available = (aux>>7) & 0x1;
|
|
cfg->agc_backoff = (aux>>5) & 0x3;
|
|
cfg->analog_voltage_external = (aux>>4) & 0x1;
|
|
cfg->analog_voltage_enable_in_off = (aux>>3) & 0x1;
|
|
cfg->analog_voltage_status = (aux>>2) & 0x1;
|
|
cfg->int_power_amplifier_voltage = (aux>>0) & 0x2;
|
|
}
|
|
|
|
//==================================================================================
|
|
at86rf215_radio_state_cmd_en at86rf215_radio_get_state(at86rf215_st* dev, at86rf215_rf_channel_en ch)
|
|
{
|
|
// "RG_STATE" RFn_STATE – Transceiver State
|
|
uint16_t reg_address = AT86RF215_REG_ADDR(ch, STATE);
|
|
uint8_t state = at86rf215_read_byte(dev, reg_address);
|
|
return state & 0x7;
|
|
}
|
|
|
|
//==================================================================================
|
|
void at86rf215_radio_set_state(at86rf215_st* dev, at86rf215_rf_channel_en ch, at86rf215_radio_state_cmd_en cmd)
|
|
{
|
|
// "RG_CMD" RFn_CMD – Transceiver Command
|
|
|
|
uint16_t reg_address = AT86RF215_REG_ADDR(ch, CMD);
|
|
at86rf215_write_byte(dev, reg_address, cmd & 0x7);
|
|
|
|
/*Errata #6: State Machine Command RFn_CMD=TRXOFF may not be succeeded
|
|
Description: If the current state is different from SLEEP, the execution of the command TRXOFF may fail.
|
|
Software workaround: Check state by reading register RFn_STATE Repeat the command RFn_CMD=TRXOFF
|
|
if the target state was not reached.
|
|
*/
|
|
if (cmd == at86rf215_radio_state_cmd_trx_off)
|
|
{
|
|
int retries = 5;
|
|
while (at86rf215_radio_get_state(dev, ch) != cmd && retries--)
|
|
{
|
|
io_utils_usleep(100);
|
|
at86rf215_write_byte(dev, reg_address, cmd & 0x7);
|
|
}
|
|
}
|
|
if (cmd == at86rf215_radio_state_cmd_tx_prep || cmd == at86rf215_radio_state_cmd_tx || cmd == at86rf215_radio_state_cmd_rx)
|
|
{
|
|
//if (ch == at86rf215_rf_channel_900mhz) event_node_wait_ready(&dev->events.lo_trx_ready_event);
|
|
//else if (ch == at86rf215_rf_channel_2400mhz) event_node_wait_ready(&dev->events.hi_trx_ready_event);
|
|
|
|
io_utils_usleep(1000);
|
|
if (dev->override_cal)
|
|
{
|
|
int i = ch == at86rf215_rf_channel_900mhz ? dev->cal.low_ch_i : dev->cal.hi_ch_i;
|
|
int q = ch == at86rf215_rf_channel_900mhz ? dev->cal.low_ch_q : dev->cal.hi_ch_q;
|
|
at86rf215_radio_set_tx_iq_calibration(dev, ch, i, q);
|
|
}
|
|
}
|
|
}
|
|
|
|
static double _fine_freq_starts[] = {0, 377e6, 754e6, 2366e6, 2550e6};
|
|
static double _fine_freq_pll_src[] = {0, 6.5e6, 13e6, 26e6};
|
|
//static int _fine_freq_ccf_min[] = {0, 126030, 126030, 85700};
|
|
//static int _fine_freq_ccf_max[] = {0, 1340967, 1340967, 296172};
|
|
|
|
//==================================================================================
|
|
int at86rf215_radio_get_good_channel(double wanted_frequency_hz, at86rf215_radio_channel_mode_en *mode,
|
|
at86rf215_rf_channel_en *ch)
|
|
{
|
|
if ( wanted_frequency_hz >= _fine_freq_starts[at86rf215_radio_channel_mode_fine_high]
|
|
&& wanted_frequency_hz < _fine_freq_starts[at86rf215_radio_channel_mode_fine_high+1])
|
|
{
|
|
*ch = at86rf215_rf_channel_2400mhz;
|
|
*mode = at86rf215_radio_channel_mode_fine_high;
|
|
}
|
|
else if ( wanted_frequency_hz >= _fine_freq_starts[at86rf215_radio_channel_mode_fine_mid]
|
|
&& wanted_frequency_hz < _fine_freq_starts[at86rf215_radio_channel_mode_fine_high] )
|
|
{
|
|
*ch = at86rf215_rf_channel_900mhz;
|
|
*mode = at86rf215_radio_channel_mode_fine_mid;
|
|
}
|
|
else if ( wanted_frequency_hz >= _fine_freq_starts[at86rf215_radio_channel_mode_fine_low]
|
|
&& wanted_frequency_hz < _fine_freq_starts[at86rf215_radio_channel_mode_fine_mid] )
|
|
{
|
|
*ch = at86rf215_rf_channel_900mhz;
|
|
*mode = at86rf215_radio_channel_mode_fine_low;
|
|
}
|
|
else
|
|
{
|
|
ZF_LOGE("the requested frequency %.1f Hz not supported", wanted_frequency_hz);
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
//==================================================================================
|
|
double at86rf215_radio_get_frequency( /*IN*/ at86rf215_radio_channel_mode_en mode,
|
|
/*IN*/ int channel_spacing_25khz_res,
|
|
/*IN*/ double wanted_frequency_hz,
|
|
/*OUT*/ int *center_freq_25khz_res,
|
|
/*OUT*/ int *channel_number)
|
|
{
|
|
// "RG_CS" RFn_CS – Channel Spacing
|
|
/*
|
|
The register configures the channel spacing with a resolution of 25kHz. The register CNM must be written to take over
|
|
the CS value. The CNM register must always be written last.
|
|
Example: A value of 0x08 needs to be set (8x25kHz) for a channel spacing of 200kHz.
|
|
*/
|
|
// "RG_CCF0L, RG_CCF0H" RFn_CCF0(L/H) – Channel Center Frequency F0 Low/High Byte
|
|
/*
|
|
The register configures the base value of the channel center frequency F0 (low / high byte) with a resolution of 25kHz.
|
|
The register CNM must be written to latch the CCFOL/H value. The CNM register must always be written last.
|
|
|
|
VALUES: The reset value (0xf8) of the channel register results in 902.2MHz for the sub-1GHz transceiver (RF09)
|
|
and 2402.2MHz for the 2.4GHz transceiver (RF24): CCF0=0x8cf8=36088; CS=200KHz, CN=0.
|
|
|
|
HOW:
|
|
F_rf09 = CCF0*25khz
|
|
F_rf24 = 1500000 + CCF0 * 25
|
|
F_rf09(default) = 36088 * 25 = 902,200 kHz
|
|
F_rf24(default) = 1500000 + 36088 * 25 = 2,402,200 kHz
|
|
*/
|
|
// "RG_CNL" RFn_CNL – Channel Number Low Byte
|
|
// "RG_CNM" RFn_CNM – Channel Mode and Channel Number High Bit
|
|
// in total the CNL/M contains 9 bits of channel and the LATCH (Mode)
|
|
/*
|
|
CNL - The register contains the transceiver channel number low byte CN[7:0]. The register CNM must be written to latch the
|
|
CNL value. The CNM register must always be written last.
|
|
|
|
CNM - The register configures the channel mode and channel number bit 9 CN[8]. A write operation to this register applies the
|
|
channel register CS, CCFOL, CCFOH and CNL values.
|
|
|
|
0x0 IEEE compliant channel scheme; f=(CCF0+CN*CS)*25kHz+f_offset ; (f_offset = 0Hz/1.5GHz)
|
|
0x1 Fine resolution (389.5-510.0)MHz with 99.182Hz channel stepping
|
|
0x2 Fine resolution (779-1020)MHz with 198.364Hz channel stepping
|
|
0x3 Fine resolution (2400-2483.5)MHz with 396.728Hz channel stepping
|
|
*/
|
|
|
|
|
|
if (mode == at86rf215_radio_channel_mode_ieee)
|
|
{
|
|
float f_offset = (wanted_frequency_hz>1500e6)?1500e6:0.0f;
|
|
*center_freq_25khz_res = (int)((wanted_frequency_hz - f_offset) / 25e3);
|
|
*channel_number = 0;
|
|
return f_offset + (*center_freq_25khz_res)*25e3;
|
|
}
|
|
|
|
int nchannel = (int)((wanted_frequency_hz - _fine_freq_starts[mode]) * ((float)(1<<16)) / _fine_freq_pll_src[mode]);
|
|
float actual_freq = (nchannel*_fine_freq_pll_src[mode]) / ((float)(1<<16)) + _fine_freq_starts[mode];
|
|
|
|
*center_freq_25khz_res = (nchannel >> 8) & 0xFFFF;
|
|
*channel_number = (nchannel >> 0) & 0xFF;
|
|
return actual_freq;
|
|
}
|
|
|
|
//==================================================================================
|
|
void at86rf215_radio_setup_channel(at86rf215_st* dev, at86rf215_rf_channel_en ch,
|
|
int channel_spacing_25khz_res,
|
|
int center_freq_25khz_res,
|
|
int channel_number,
|
|
at86rf215_radio_channel_mode_en mode)
|
|
{
|
|
|
|
uint16_t reg_address_spacing = AT86RF215_REG_ADDR(ch, CS);
|
|
uint8_t buf[5] = {0};
|
|
buf[0] = channel_spacing_25khz_res;
|
|
buf[1] = /*LOW*/ center_freq_25khz_res & 0xFF;
|
|
buf[2] = /*HIGH*/ (center_freq_25khz_res >> 8) & 0xFF;
|
|
buf[3] = /*LOW*/ channel_number & 0xFF;
|
|
buf[4] = /*HIGH + MODE*/ ((channel_number>>8)&0x01) | ((mode & 0x3)<<6);
|
|
at86rf215_write_buffer(dev, reg_address_spacing, buf, 5);
|
|
}
|
|
|
|
//==================================================================================
|
|
void at86rf215_radio_set_rx_bandwidth_sampling(at86rf215_st* dev, at86rf215_rf_channel_en ch,
|
|
at86rf215_radio_set_rx_bw_samp_st* cfg)
|
|
{
|
|
// "RG_RXBWC" RFn_RXBWC – Receiver Filter Bandwidth Control
|
|
/*
|
|
The register configures the receiver filter settings.
|
|
- Bit 5 – RXBWC.IFI: IF Inversion
|
|
A value of one configures the receiver to implement the inverted-sign IF frequency.
|
|
Use default setting for normal operation.
|
|
- Bit 4 – RXBWC.IFS: IF Shift
|
|
A value of one configures the receiver to shift the IF frequency by factor of 1.25.
|
|
This is useful to place the image frequency according to channel scheme.
|
|
- Bit 3:0 – RXBWC.BW: Receiver Bandwidth
|
|
The sub-register controls the receiver filter bandwidth settings.
|
|
*/
|
|
// "RG_RXDFE" RFn_RXDFE – Receiver Digital Frontend
|
|
/*
|
|
The register configures the receiver digital frontend
|
|
Bit 7:5 – RXDFE.RCUT: RX filter relative cut-off frequency
|
|
Bit 3:0 – RXDFE.SR: RX Sample Rate
|
|
*/
|
|
|
|
uint16_t reg_address_bw = AT86RF215_REG_ADDR(ch, RXBWC);
|
|
|
|
uint8_t buf[2] = {0};
|
|
buf[0] |= (cfg->inverter_sign_if & 0x1)<<5;
|
|
buf[0] |= (cfg->shift_if_freq & 0x1)<<4;
|
|
buf[0] |= (cfg->bw & 0xF);
|
|
buf[1] |= (cfg->fcut & 0x7) << 5;
|
|
buf[1] |= (cfg->fs & 0xF);
|
|
at86rf215_write_buffer(dev, reg_address_bw, buf, 2);
|
|
}
|
|
|
|
//==================================================================================
|
|
void at86rf215_radio_get_rx_bandwidth_sampling(at86rf215_st* dev, at86rf215_rf_channel_en ch,
|
|
at86rf215_radio_set_rx_bw_samp_st* cfg)
|
|
{
|
|
uint16_t reg_address_bw = AT86RF215_REG_ADDR(ch, RXBWC);
|
|
uint8_t buf[2] = {0};
|
|
at86rf215_read_buffer(dev, reg_address_bw, buf, 2);
|
|
|
|
cfg->inverter_sign_if = (buf[0]>>5) & 0x1;
|
|
cfg->shift_if_freq = (buf[0]>>4) & 0x1;
|
|
cfg->bw = (buf[0]>>0) & 0xF;
|
|
cfg->fcut = (buf[1]>>5) & 0x7;
|
|
cfg->fs = (buf[1]>>0) & 0xF;
|
|
}
|
|
|
|
//==================================================================================
|
|
void at86rf215_radio_setup_agc(at86rf215_st* dev, at86rf215_rf_channel_en ch,
|
|
at86rf215_radio_agc_ctrl_st *agc_ctrl)
|
|
{
|
|
// "RG_AGCC, RG_AGCS" RFn_AGCC – Receiver AGC Control 0 / RFn_AGCS – Receiver AGCG
|
|
/*
|
|
RFn_AGCC – Receiver AGC Control 0:
|
|
The register controls the transceiver automatic gain control (AGC).
|
|
Bit 6 – AGCC.AGCI: AGC Input (0 - filterred, 1 - unfiltered, faster operation)
|
|
Bit 5:4 – AGCC.AVGS: AGC Average Time in Number of Samples
|
|
0x0 8 samples
|
|
0x1 16 samples
|
|
0x2 32 samples
|
|
0x3 64 samples
|
|
Bit 3 – AGCC.RST: AGC Reset - resets the AGC and sets the maximum receiver gain.
|
|
Bit 2 – AGCC.FRZS: AGC Freeze Status - A value of one indicates that the AGC is on hold.
|
|
Bit 1 – AGCC.FRZC: AGC Freeze Control - A value of one forces the AGC to freeze to its current value.
|
|
Bit 0 – AGCC.EN: AGC Enable - a value of zero allows a manual setting of the RX gain control by sub-register AGCS.GCW
|
|
|
|
RFn_AGCS – Receiver AGCG:
|
|
The register controls the AGC and the receiver gain.
|
|
Bit 7:5 – AGCS.TGT: AGC Target Level - sets the AGC target level relative to ADC full scale.
|
|
0x0 Target=-21dB
|
|
0x1 Target=-24dB
|
|
0x2 Target=-27dB
|
|
0x3 Target=-30dB
|
|
0x4 Target=-33dB
|
|
0x5 Target=-36dB
|
|
0x6 Target=-39dB
|
|
0x7 Target=-42dB
|
|
Bit 4:0 – AGCS.GCW: RX Gain Control Word
|
|
If AGCC_EN is set to 1, a read of bit AGCS.GCW indicates the current receiver gain setting. If AGCC_EN is set to 0, a
|
|
write access to GCW manually sets the receiver gain. An integer value of 23 indicates the maximum receiver gain; each
|
|
integer step changes the gain by 3dB.
|
|
*/
|
|
|
|
uint16_t reg_address_agc = AT86RF215_REG_ADDR(ch, AGCC);
|
|
uint8_t buf[2] = {0};
|
|
|
|
buf[0] |= (agc_ctrl->agc_measure_source_not_filtered & 0x1)<<6;
|
|
buf[0] |= (agc_ctrl->avg & 0x3)<<4;
|
|
buf[0] |= (agc_ctrl->reset_cmd & 0x1) << 3;
|
|
buf[0] |= (agc_ctrl->freeze_cmd & 0x1) << 1;
|
|
buf[0] |= (agc_ctrl->enable_cmd & 0x1);
|
|
buf[1] |= (agc_ctrl->att & 0x7) << 5;
|
|
buf[1] |= (agc_ctrl->gain_control_word & 0x1F);
|
|
at86rf215_write_buffer(dev, reg_address_agc, buf, 2);
|
|
}
|
|
|
|
//==================================================================================
|
|
void at86rf215_radio_get_agc(at86rf215_st* dev, at86rf215_rf_channel_en ch,
|
|
at86rf215_radio_agc_ctrl_st *agc_ctrl)
|
|
{
|
|
uint16_t reg_address_agc = AT86RF215_REG_ADDR(ch, AGCC);
|
|
uint8_t buf[2] = {0};
|
|
|
|
at86rf215_read_buffer(dev, reg_address_agc, buf, 2);
|
|
|
|
agc_ctrl->agc_measure_source_not_filtered = (buf[0] >> 6);
|
|
agc_ctrl->avg = (buf[0] >> 4) & 0x1;
|
|
agc_ctrl->reset_cmd = (buf[0] >> 3) & 0x3;
|
|
agc_ctrl->freeze_status = (buf[0] >> 2) & 0x1;
|
|
agc_ctrl->freeze_cmd = (buf[0] >> 1) & 0x1;
|
|
agc_ctrl->enable_cmd = (buf[0] >> 0) & 0x1;
|
|
agc_ctrl->att = (buf[1] >> 5) & 0x7;
|
|
agc_ctrl->gain_control_word = (buf[1] >> 0) & 0x1F;
|
|
}
|
|
|
|
//==================================================================================
|
|
float at86rf215_radio_get_rssi_dbm(at86rf215_st* dev, at86rf215_rf_channel_en ch)
|
|
{
|
|
// "RG_RSSI" RFn_RSSI – Received Signal Strength Indicator
|
|
/*
|
|
Bit 7:0 – RSSI.RSSI: Received Signal Strength Indicator
|
|
The value reflects the received signal power in dBm. A value of 127 indicates that the RSSI value is invalid. The register value presents a signed
|
|
integer value (value range -127...+4) using twos complement representation.
|
|
*/
|
|
uint16_t reg_address = AT86RF215_REG_ADDR(ch, RSSI);
|
|
uint8_t v = at86rf215_read_byte(dev, reg_address);
|
|
int8_t *sv = (int8_t *)&v;
|
|
return (float)(*sv);
|
|
}
|
|
|
|
//==================================================================================
|
|
void at86rf215_radio_setup_energy_detection(at86rf215_st* dev, at86rf215_rf_channel_en ch,
|
|
at86rf215_radio_energy_detection_st* ed)
|
|
{
|
|
/*
|
|
"RG_EDC" "RG_EDD" "RG_EDV"
|
|
RFn_EDC – Energy Detection Configuration
|
|
Bit 1:0 – EDC.EDM: Energy Detection Mode (at86rf215_radio_energy_detection_mode_en)
|
|
RFn_EDD – Receiver Energy Detection Averaging Duration
|
|
Bit 7:2 – EDD.DF: Receiver energy detection duration factor - T[μs]=DF*DTB
|
|
Bit 1:0 – EDD.DTB: Receiver energy detection average duration time basis (RSSI averaging basis)
|
|
0x0 2μs
|
|
0x1 8μs
|
|
0x2 32μs
|
|
0x3 128μs
|
|
*/
|
|
uint16_t reg_address_edc = AT86RF215_REG_ADDR(ch, EDC);
|
|
uint8_t buf[2] = {0};
|
|
|
|
buf[0] |= (ed->mode & 0x3)<<0;
|
|
|
|
// find the closest usec number
|
|
int df = ed->average_duration_us / 2;
|
|
int dtb = 0;
|
|
if (df > 63)
|
|
{
|
|
df = ed->average_duration_us / 8;
|
|
dtb = 1;
|
|
if (df > 63)
|
|
{
|
|
df = ed->average_duration_us / 32;
|
|
dtb = 2;
|
|
if (df > 63)
|
|
{
|
|
df = ed->average_duration_us / 128;
|
|
dtb = 3;
|
|
if (df > 63)
|
|
{
|
|
df = 63;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
buf[1] |= (df & 0x3F) << 2;
|
|
buf[1] |= (dtb & 0x3);
|
|
|
|
at86rf215_write_buffer(dev, reg_address_edc, buf, 2);
|
|
}
|
|
|
|
//==================================================================================
|
|
void at86rf215_radio_get_energy_detection(at86rf215_st* dev, at86rf215_rf_channel_en ch,
|
|
at86rf215_radio_energy_detection_st* ed)
|
|
{
|
|
uint16_t reg_address_edc = AT86RF215_REG_ADDR(ch, EDC);
|
|
uint8_t buf[3] = {0};
|
|
|
|
at86rf215_read_buffer(dev, reg_address_edc, buf, 3);
|
|
|
|
ed->mode = (buf[0]) & 0x3;
|
|
buf[0] |= (ed->mode & 0x3)<<0;
|
|
|
|
float dtb = 0.0f;
|
|
switch (buf[1] & 0x3)
|
|
{
|
|
case 0: dtb = 2.0f; break;
|
|
case 1: dtb = 8.0f; break;
|
|
case 2: dtb = 32.0f; break;
|
|
case 3: dtb = 64.0f; break;
|
|
default: dtb = 2.0f; break;
|
|
}
|
|
float df = (buf[1] >> 2) & 0x3;
|
|
ed->average_duration_us = df * dtb;
|
|
ed->energy_detection_value = (float)(*(int8_t*)(&buf[2]));
|
|
}
|
|
|
|
//==================================================================================
|
|
uint8_t at86rf215_radio_get_random_value(at86rf215_st* dev, at86rf215_rf_channel_en ch)
|
|
{
|
|
// "RG_RNDV" RFn_RNDV – Random Value - contains 8bit random value
|
|
uint16_t reg_address = AT86RF215_REG_ADDR(ch, RNDV);
|
|
return at86rf215_read_byte(dev, reg_address);
|
|
}
|
|
|
|
//==================================================================================
|
|
void at86rf215_radio_setup_tx_ctrl(at86rf215_st* dev, at86rf215_rf_channel_en ch,
|
|
at86rf215_radio_tx_ctrl_st* cfg)
|
|
{
|
|
/*
|
|
RFn_TXCUTC – Transmitter Filter Cutoff Control and PA Ramp Time
|
|
Bit 7:6 – TXCUTC.PARAMP: Power Amplifier Ramp Time
|
|
RF_PARAMP4U 0x0 tpa_ramp = 4μs
|
|
RF_PARAMP8U 0x1 tpa_ramp = 8μs
|
|
RF_PARAMP16U 0x2 tpa_ramp = 16μs
|
|
RF_PARAMP32U 0x3 tpa_ramp = 32μs
|
|
Bit 3:0 – TXCUTC.LPFCUT: Transmitter cut-off frequency
|
|
RF_FLC80KHZ 0x0 fLPFCUT = 80kHz
|
|
RF_FLC100KHZ 0x1 fLPFCUT = 100kHz
|
|
RF_FLC125KHZ 0x2 fLPFCUT = 125kHz
|
|
RF_FLC160KHZ 0x3 fLPFCUT = 160kHz
|
|
RF_FLC200KHZ 0x4 fLPFCUT = 200kHz
|
|
RF_FLC250KHZ 0x5 fLPFCUT = 250kHz
|
|
RF_FLC315KHZ 0x6 fLPFCUT = 315kHz
|
|
RF_FLC400KHZ 0x7 fLPFCUT = 400kHz
|
|
RF_FLC500KHZ 0x8 fLPFCUT = 500kHz
|
|
RF_FLC625KHZ 0x9 fLPFCUT = 625kHz
|
|
RF_FLC800KHZ 0xA fLPFCUT = 800kHz
|
|
RF_FLC1000KHZ 0xB fLPFCUT = 1000kHz
|
|
*/
|
|
|
|
/*
|
|
RFn_TXDFE – Transmitter TX Digital Frontend
|
|
Bit 7:5 – TXDFE.RCUT: TX filter relative to the cut-off frequency
|
|
0x0 fCUT=0.25 *fS/2
|
|
0x1 fCUT=0.375*fS/2
|
|
0x2 fCUT=0.5 *fS/2
|
|
0x3 fCUT=0.75 *fS/2
|
|
0x4 fCUT=1.00 *fS/2
|
|
Bit 4 – TXDFE.DM: Direct Modulation
|
|
If enabled (1) the transmitter direct modulation is enabled - available for FSK and
|
|
OQPSK (OQPSKC0.FCHIP=0;1). Direct modulation must also be enabled at the BBCx registers (FSKDM.EN and OQPSKC0.DM).
|
|
Bit 3:0 – TXDFE.SR: TX Sample Rate
|
|
0x1 fS=4000kHz
|
|
0x2 fS=2000kHz
|
|
0x3 fS=(4000/3)kHz
|
|
0x4 fS=1000kHz
|
|
0x5 fS=800kHz
|
|
0x6 fS=(2000/3)kHz
|
|
0x8 fS=500kHz
|
|
0xA fS=400kHz
|
|
*/
|
|
|
|
/*
|
|
RFn_PAC – Transmitter Power Amplifier Control
|
|
Bit 6:5 – PAC.PACUR: Power Amplifier Current Control - PACUR configures the power amplifier DC current (useful for low power settings).
|
|
0x0 Power amplifier current reduction by about 22mA (3dB reduction of max. small signal gain)
|
|
0x1 Power amplifier current reduction by about 18mA (2dB reduction of max. small signal gain)
|
|
0x2 Power amplifier current reduction by about 11mA (1dB reduction of max. small signal gain)
|
|
0x3 No power amplifier current reduction (max. transmit small signal gain)
|
|
Bit 4:0 – PAC.TXPWR: Transmitter Output Power
|
|
Maximum output power is TXPWR=31, minimum output power is TXPWR=0. The output power can be set by about 1dB step resolution.
|
|
*/
|
|
|
|
uint16_t reg_address_txcut = AT86RF215_REG_ADDR(ch, TXCUTC);
|
|
uint8_t buf[3] = {0};
|
|
|
|
buf[0] |= (cfg->pa_ramping_time & 0x3) << 6;
|
|
buf[0] |= (cfg->analog_bw & 0xF);
|
|
buf[1] |= (cfg->digital_bw & 0x7) << 5;
|
|
buf[1] |= (cfg->direct_modulation & 0x1) << 4;
|
|
buf[1] |= (cfg->fs & 0xF);
|
|
buf[2] |= (cfg->current_reduction & 0x3) << 5;
|
|
buf[2] |= (cfg->tx_power & 0x1F);
|
|
|
|
at86rf215_write_buffer(dev, reg_address_txcut, buf, 3);
|
|
}
|
|
|
|
//==================================================================================
|
|
void at86rf215_radio_get_tx_ctrl(at86rf215_st* dev, at86rf215_rf_channel_en ch,
|
|
at86rf215_radio_tx_ctrl_st* cfg)
|
|
{
|
|
uint16_t reg_address_txcut = AT86RF215_REG_ADDR(ch, TXCUTC);
|
|
uint8_t buf[3] = {0};
|
|
at86rf215_read_buffer(dev, reg_address_txcut, buf, 3);
|
|
|
|
cfg->pa_ramping_time = (buf[0] >> 6) & 0x3;
|
|
cfg->analog_bw = (buf[0] >> 0) & 0xF;
|
|
cfg->digital_bw = (buf[1] >> 5) & 0x7;
|
|
cfg->direct_modulation = (buf[1] >> 4) & 0x1;
|
|
cfg->fs = (buf[1] >> 0) & 0xF;
|
|
cfg->current_reduction = (buf[2] >> 5) & 0x3;
|
|
cfg->tx_power = (buf[2] >> 0) & 0x1F;
|
|
}
|
|
|
|
//==================================================================================
|
|
void at86rf215_radio_setup_pll_ctrl(at86rf215_st* dev, at86rf215_rf_channel_en ch,
|
|
at86rf215_radio_pll_ctrl_st* cfg)
|
|
{
|
|
uint16_t reg_address_pll = AT86RF215_REG_ADDR(ch, PLL);
|
|
uint8_t buf[2] = {0};
|
|
|
|
buf[0] |= (cfg->loop_bw & 0x3) << 4;
|
|
//buf[0] |= (cfg->pll_locked & 0x1) << 1; <- status
|
|
buf[1] |= (cfg->pll_center_freq & 0x3F) << 0;
|
|
|
|
at86rf215_write_buffer(dev, reg_address_pll, buf, 2);
|
|
}
|
|
|
|
//==================================================================================
|
|
void at86rf215_radio_get_pll_ctrl(at86rf215_st* dev, at86rf215_rf_channel_en ch,
|
|
at86rf215_radio_pll_ctrl_st* cfg)
|
|
{
|
|
uint16_t reg_address_pll = AT86RF215_REG_ADDR(ch, PLL);
|
|
uint8_t buf[2] = {0};
|
|
|
|
at86rf215_read_buffer(dev, reg_address_pll, buf, 2);
|
|
|
|
cfg->loop_bw = (buf[0] >> 4) & 0x3;
|
|
cfg->pll_locked = (buf[0] >> 1) & 0x1;
|
|
cfg->pll_center_freq = (buf[1] >> 0) & 0x3F;
|
|
}
|
|
|
|
//==================================================================================
|
|
void at86rf215_radio_set_tx_iq_calibration(at86rf215_st* dev, at86rf215_rf_channel_en ch,
|
|
int cal_i, int cal_q)
|
|
{
|
|
uint16_t reg_address_cal_i = AT86RF215_REG_ADDR(ch, TXCI);
|
|
|
|
uint8_t buf[2] = {0};
|
|
buf[0] = cal_i & 0x3F;
|
|
buf[1] = cal_q & 0x3F;
|
|
at86rf215_write_buffer(dev, reg_address_cal_i, buf, 2);
|
|
|
|
// RFn_TXCI – Transmit calibration I path
|
|
// The register contains information about the TX LO leakage calibration value of the transmit I path. At the transition
|
|
// process from state TRXOFF to TX the calibration is started automatically
|
|
|
|
// RFn_TXCQ – Transmit calibration Q path
|
|
// The register contains information about the TX LO leakage calibration value of the transmit Q path. At the transition
|
|
// process from state TRXOFF to TX the calibration is started automatically.
|
|
}
|
|
|
|
//==================================================================================
|
|
void at86rf215_radio_get_tx_iq_calibration(at86rf215_st* dev, at86rf215_rf_channel_en ch,
|
|
int *cal_i, int *cal_q)
|
|
{
|
|
uint16_t reg_address_cal_i = AT86RF215_REG_ADDR(ch, TXCI);
|
|
uint16_t reg_address_cal_q = AT86RF215_REG_ADDR(ch, TXCQ);
|
|
uint8_t buf[2] = {0};
|
|
|
|
at86rf215_read_buffer(dev, reg_address_cal_i, buf, 2);
|
|
|
|
*cal_i = buf[0] & 0x3F;
|
|
*cal_q = buf[1] & 0x3F;
|
|
}
|
|
|
|
//==================================================================================
|
|
void at86rf215_radio_set_tx_dac_input_iq(at86rf215_st* dev, at86rf215_rf_channel_en ch,
|
|
int enable_dac_i_dc, int dac_i_val,
|
|
int enable_dac_q_dc, int dac_q_val)
|
|
{
|
|
uint16_t reg_address_dac_i = AT86RF215_REG_ADDR(ch, TXDACI);
|
|
|
|
uint8_t buf[2] = {0};
|
|
buf[0] = ((enable_dac_i_dc & 0x1)<<7) | (dac_i_val & 0x7F);
|
|
buf[1] = ((enable_dac_q_dc & 0x1)<<7) | (dac_q_val & 0x7F);
|
|
at86rf215_write_buffer(dev, reg_address_dac_i, buf, 2);
|
|
|
|
// RFn_TXDACI – In-phase input value for TXDAC - Input I value can be applied at TXDAC (DC measurement)
|
|
// Bit 7 – TXDACI.ENTXDACID: Enable input to TXDAC
|
|
// Bit 6:0 – TXDACI.TXDACID: Input to TXDAC data
|
|
|
|
// RFn_TXDACQ – Quadrature-phase input value for TXDAC - Input Q value can be applied at TXDAC (DC measurement)
|
|
// Bit 7 – TXDACQ.ENTXDACQD: Enable input to TXDAC
|
|
// Bit 6:0 – TXDACQ.TXDACQD: Input to TXDAC data
|
|
|
|
/*
|
|
PAGE 221 in datasheet!
|
|
The AT86RF215 comprises a DAC (digital to analog converter) value overwrite functionality. Each transceiver contains
|
|
two transmitter DACs (for the in-phase and quadrature-phase signal) in order to transmit IQ signals. Both DAC values
|
|
can be overwritten separately by register settings. This feature is useful to transmit an LO carrier which is necessary for
|
|
certain certifications.
|
|
*/
|
|
}
|
|
|
|
//==================================================================================
|
|
void at86rf215_radio_get_tx_dac_input_iq(at86rf215_st* dev, at86rf215_rf_channel_en ch,
|
|
int *enable_dac_i_dc, int *dac_i_val,
|
|
int *enable_dac_q_dc, int *dac_q_val)
|
|
{
|
|
uint16_t reg_address_dac_i = AT86RF215_REG_ADDR(ch, TXDACI);
|
|
|
|
uint8_t buf[2] = {0};
|
|
at86rf215_read_buffer(dev, reg_address_dac_i, buf, 2);
|
|
|
|
*enable_dac_i_dc = (buf[0] >> 7) & 0x1;
|
|
*enable_dac_q_dc = (buf[1] >> 7) & 0x1;
|
|
|
|
*dac_i_val = (buf[0]) & 0x3F;
|
|
*dac_q_val = (buf[1]) & 0x3F;
|
|
} |