cariboulabs-cariboulite/software/libcariboulite/src/at86rf215/at86rf215.c

771 wiersze
31 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_Main"
#include <stdint.h>
#include <math.h>
#include <string.h>
#include <stdbool.h>
#include <stdio.h>
#include "zf_log/zf_log.h"
#include "at86rf215.h"
#include "io_utils/io_utils.h"
#include "io_utils/io_utils_spi.h"
#include "at86rf215_radio.h"
#include "at86rf215_regs.h"
//===================================================================
int at86rf215_write_buffer(at86rf215_st* dev, uint16_t addr, uint8_t *buffer, uint8_t size )
{
// a maximal possible chunk size - 256 + 2(addr)
uint8_t chunk_tx[258] = {0};
uint8_t chunk_rx[258] = {0};
chunk_tx[0] = ((addr >> 8) & 0x3F) | 0x80;
chunk_tx[1] = addr & 0xFF;
memcpy(chunk_tx + 2, buffer, size);
return io_utils_spi_transmit(dev->io_spi, dev->io_spi_handle, chunk_tx, chunk_rx, size + 2, io_utils_spi_read_write);
}
//===================================================================
int at86rf215_read_buffer(at86rf215_st* dev, uint16_t addr, uint8_t *buffer, uint8_t size)
{
// a maximal possible chunk size - 256 + 2(addr)
uint8_t chunk_tx[258] = {0};
uint8_t chunk_rx[258] = {0};
chunk_tx[0] = (addr >> 8) & 0x3F;
chunk_tx[1] = addr & 0xFF;
int ret = io_utils_spi_transmit(dev->io_spi, dev->io_spi_handle, chunk_tx, chunk_rx, size + 2, io_utils_spi_read_write);
if (ret == 0)
{
memcpy(buffer, chunk_rx + 2, size);
}
return ret;
}
//===================================================================
int at86rf215_write_byte(at86rf215_st* dev, uint16_t addr, uint8_t val )
{
uint8_t chunk_tx[3] = {0};
uint8_t chunk_rx[3] = {0};
chunk_tx[0] = ((addr >> 8) & 0x3F) | 0x80;
chunk_tx[1] = addr & 0xFF;
chunk_tx[2] = val;
return io_utils_spi_transmit(dev->io_spi, dev->io_spi_handle,
chunk_tx, chunk_rx, 3, io_utils_spi_read_write);
}
//===================================================================
int at86rf215_read_byte(at86rf215_st* dev, uint16_t addr)
{
uint8_t chunk_tx[3] = {0};
uint8_t chunk_rx[3] = {0};
chunk_tx[0] = (addr >> 8) & 0x3F;
chunk_tx[1] = addr & 0xFF;
/*printf("TX: ");
for (int i = 0; i < 3; i ++)
printf(" 0x%02X ", chunk_tx[i]);
printf("\n");*/
int ret = io_utils_spi_transmit(dev->io_spi, dev->io_spi_handle,
chunk_tx, chunk_rx, 3, io_utils_spi_read_write);
if (ret < 0)
{
return ret;
}
/*printf("RX: ");
for (int i = 0; i < 3; i ++)
printf(" 0x%02X ", chunk_rx[i]);
printf("\n");*/
return chunk_rx[2];
}
//===================================================================
#define NUM_CAL_STEPS 5
void swap(int *p,int *q)
{
int t;
t=*p;
*p=*q;
*q=t;
}
void sort(int a[],int n)
{
int i,j,temp;
for(i = 0;i < n-1;i++) {
for(j = 0;j < n-i-1;j++) {
if(a[j] > a[j+1])
swap(&a[j],&a[j+1]);
}
}
}
int median(int a[], int n)
{
if (n==0) return 0;
sort(a,n);
return a[(n+1)/2-1];
}
//===================================================================
int at86rf215_calibrate_device(at86rf215_st* dev, at86rf215_rf_channel_en ch, int* i_val, int* q_val)
{
int cal_i[NUM_CAL_STEPS] = {0};
int cal_q[NUM_CAL_STEPS] = {0};
bool override_flag = dev->override_cal;
dev->override_cal = false;
ZF_LOGD("Calibration of modem channel %d...", ch);
for (int i = 0; i < NUM_CAL_STEPS; i ++)
{
at86rf215_radio_set_state(dev, ch, at86rf215_radio_state_cmd_trx_off);
io_utils_usleep(2000);
at86rf215_radio_set_state(dev, ch, at86rf215_radio_state_cmd_tx_prep);
io_utils_usleep(10000);
at86rf215_radio_get_tx_iq_calibration(dev, ch, &cal_i[i], &cal_q[i]);
//printf("[%d,%d], ", cal_i[i], cal_q[i]);
}
// medians
int cal_i_med = median(cal_i, NUM_CAL_STEPS);
int cal_q_med = median(cal_q, NUM_CAL_STEPS);
ZF_LOGD("Calibration Results of the modem: I=%d, Q=%d", cal_i_med, cal_q_med);
if (i_val) *i_val = cal_i_med;
if (q_val) *q_val = cal_q_med;
if (ch == at86rf215_rf_channel_900mhz)
{
dev->cal.low_ch_i = cal_i_med;
dev->cal.low_ch_q = cal_q_med;
}
if (ch == at86rf215_rf_channel_2400mhz)
{
dev->cal.hi_ch_i = cal_i_med;
dev->cal.hi_ch_q = cal_q_med;
}
dev->override_cal = override_flag;
return 0;
}
//===================================================================
int at86rf215_init(at86rf215_st* dev,
io_utils_spi_st* io_spi)
{
if (dev == NULL)
{
ZF_LOGE("dev is NULL");
return -1;
}
dev->io_spi = io_spi;
ZF_LOGD("configuring reset and irq pins");
// Configure GPIO pins
io_utils_setup_gpio(dev->reset_pin, io_utils_dir_output, io_utils_pull_off);
io_utils_setup_gpio(dev->irq_pin, io_utils_dir_input, io_utils_pull_up);
// set to known state
io_utils_write_gpio(dev->reset_pin, 1);
ZF_LOGD("Adding chip definition to io_utils_spi");
io_utils_hard_spi_st hard_dev_modem = { .spi_dev_id = dev->spi_dev, .spi_dev_channel = dev->spi_channel, };
dev->io_spi_handle = io_utils_spi_add_chip(dev->io_spi, dev->cs_pin, 4000000, 0, 0,
io_utils_spi_chip_type_modem,
&hard_dev_modem);
// Setup the interrupts after clearing the register one time
at86rf215_irq_st irq = {0};
at86rf215_get_irqs(dev, &irq, 0);
dev->num_interrupts = 0;
if (io_utils_setup_interrupt(dev->irq_pin, at86rf215_interrupt_handler, dev) < 0)
{
ZF_LOGE("interrupt registration for irq_pin (%d) failed", dev->irq_pin);
//io_utils_setup_gpio(dev->reset_pin, io_utils_dir_input, io_utils_pull_up);
io_utils_setup_gpio(dev->irq_pin, io_utils_dir_input, io_utils_pull_up);
io_utils_spi_remove_chip(dev->io_spi, dev->io_spi_handle);
return -1;
}
// Initialize events
event_node_init(&dev->events.lo_trx_ready_event);
event_node_init(&dev->events.lo_energy_measure_event);
event_node_init(&dev->events.hi_trx_ready_event);
event_node_init(&dev->events.hi_energy_measure_event);
// Get chip type
uint8_t pn = 0, vn = 0;
at86rf215_get_versions(dev, &pn, &vn);
ZF_LOGD("Modem identity: Version: %02X, Product: %02X", vn, pn);
// calibrate TXPREP
at86rf215_calibrate_device(dev, at86rf215_rf_channel_900mhz, &dev->cal.low_ch_i, &dev->cal.low_ch_q);
at86rf215_calibrate_device(dev, at86rf215_rf_channel_2400mhz, &dev->cal.hi_ch_i, &dev->cal.hi_ch_q);
dev->override_cal = true;
dev->initialized = 1;
return 0;
}
//===================================================================
int at86rf215_close(at86rf215_st* dev)
{
if (dev == NULL)
{
ZF_LOGE("device pointer NULL");
return -1;
}
if (!dev->initialized)
{
ZF_LOGE("device not initialized");
return 0;
}
dev->initialized = 0;
event_node_close(&dev->events.lo_trx_ready_event);
event_node_close(&dev->events.lo_energy_measure_event);
event_node_close(&dev->events.hi_trx_ready_event);
event_node_close(&dev->events.hi_energy_measure_event);
//io_utils_setup_gpio(dev->reset_pin, io_utils_dir_input, io_utils_pull_up);
io_utils_setup_gpio(dev->irq_pin, io_utils_dir_input, io_utils_pull_up);
// Release the SPI device
io_utils_spi_remove_chip(dev->io_spi, dev->io_spi_handle);
ZF_LOGD("device release completed");
return 0;
}
//===================================================================
void at86rf215_reset(at86rf215_st* dev)
{
io_utils_write_gpio(dev->reset_pin, 0);
io_utils_usleep(300);
io_utils_write_gpio(dev->reset_pin, 1);
}
//===================================================================
void at86rf215_chip_reset_with_spi(at86rf215_st* dev)
{
uint8_t val = 0x7;
at86rf215_write_byte(dev, REG_RF_RST, val);
}
//===================================================================
void at86rf215_get_versions(at86rf215_st* dev, uint8_t *pn, uint8_t *vn)
{
if (pn) *pn = at86rf215_read_byte(dev, REG_RF_PN);
if (vn) *vn = at86rf215_read_byte(dev, REG_RF_VN);
}
//===================================================================
int at86rf215_print_version(at86rf215_st* dev)
{
uint8_t pn = 0, vn = 0;
at86rf215_get_versions(dev, &pn, &vn);
//at86rf215_get_versions(dev, &pn, &vn);
if (pn == at86rf215_pn_at86rf215) // 0x34
{
ZF_LOGD("MODEM Version: AT86RF215 (with basebands), version: %02x", vn);
}
else if (pn == at86rf215_pn_at86rf215iq) // 0x35
{
ZF_LOGD("MODEM Version: AT86RF215IQ (without basebands), version: %02x", vn);
}
else
{
ZF_LOGW("MODEM Version: not AT86RF215 IQ capable modem (product number: 0x%02x, versions %02x)", pn, vn);
}
return pn;
}
//===================================================================
int at86rf215_write_fifo(at86rf215_st* dev, uint8_t *buffer, uint8_t size )
{
return at86rf215_write_buffer(dev, 0, buffer, size);
}
//===================================================================
int at86rf215_read_fifo(at86rf215_st* dev, uint8_t *buffer, uint8_t size )
{
return at86rf215_read_buffer(dev, 0, buffer, size);
}
//===================================================================
void at86rf215_set_clock_output(at86rf215_st* dev,
at86rf215_drive_current_en drv_level,
at86rf215_clock_out_freq_en clock_val)
{
uint8_t val = ( (drv_level&0x03)<<3 ) | (clock_val&0x07);
at86rf215_write_byte(dev, REG_RF_CLKO, val);
}
//===================================================================
void at86rf215_setup_rf_irq(at86rf215_st* dev, uint8_t active_low,
uint8_t show_masked_irq,
at86rf215_drive_current_en drive)
{
uint8_t val = 0;
val |= (show_masked_irq&0x1)<<3;
val |= (active_low&0x1)<<2;
val |= (drive&0x3);
at86rf215_write_byte(dev, REG_RF_CFG, val);
}
//===================================================================
static void at86rf215_print_radio_irq(at86rf215_radio_irq_st* irq)
{
printf(" IQ_if_sync_fail: %d\n", irq->IQ_if_sync_fail);
printf(" trx_error: %d\n", irq->trx_error);
printf(" battery_low: %d\n", irq->battery_low);
printf(" energy_detection_complete: %d\n", irq->energy_detection_complete);
printf(" trx_ready: %d\n", irq->trx_ready);
printf(" wake_up_por: %d\n", irq->wake_up_por);
}
//===================================================================
static void at86rf215_print_bb_irq(at86rf215_baseband_irq_st* irq)
{
printf(" frame_buffer_level: %d\n", irq->frame_buffer_level);
printf(" agc_release: %d\n", irq->agc_release);
printf(" agc_hold: %d\n", irq->agc_hold);
printf(" frame_tx_complete: %d\n", irq->frame_tx_complete);
printf(" frame_rx_match_extended: %d\n", irq->frame_rx_match_extended);
printf(" frame_rx_address_match: %d\n", irq->frame_rx_address_match);
printf(" frame_rx_complete: %d\n", irq->frame_rx_complete);
printf(" frame_rx_started: %d\n", irq->frame_rx_started);
}
//===================================================================
void at86rf215_get_irqs(at86rf215_st* dev, at86rf215_irq_st* irq, int verbose)
{
at86rf215_read_buffer(dev, REG_RF09_IRQS, (uint8_t*)irq, 4);
if (verbose)
{
printf("IRQ Status:\n");
printf(" Radio09:\n"); at86rf215_print_radio_irq(&irq->radio09);
printf(" Radio24:\n"); at86rf215_print_radio_irq(&irq->radio24);
printf(" Baseband09:\n"); at86rf215_print_bb_irq(&irq->bb0);
printf(" Baseband24:\n"); at86rf215_print_bb_irq(&irq->bb1);
}
}
//===================================================================
void at86rf215_set_xo_trim(at86rf215_st* dev, uint8_t fast_start, float cap_trim)
{
if (cap_trim < 0.0f) cap_trim = 0.0f;
if (cap_trim > 4.5f) cap_trim = 4.5f;
uint8_t trim_val = ((uint8_t)((cap_trim+0.1f)/0.3f))&0xF;
trim_val |= (fast_start&0x1)<<4;
at86rf215_write_byte(dev, REG_RF_XOC, trim_val);
}
//===================================================================
void at86rf215_get_iq_if_cfg(at86rf215_st* dev, at86rf215_iq_interface_config_st* cfg, int verbose)
{
uint8_t data[3] = {0};
at86rf215_read_buffer(dev, REG_RF_IQIFC0, data, 3);
cfg->loopback_enable = (data[0]>>7)&0x01;
cfg->synchronization_failed = (data[0]>>6)&0x01;
cfg->drv_strength = (data[0]>>4)&0x03;
uint8_t cmv = (data[0]>>2)&0x03;
cfg->common_mode_voltage = ((data[0]>>1)&0x01)?at86rf215_iq_common_mode_v_ieee1596_1v2:cmv;
cfg->tx_control_with_iq_if = (data[0]>>0)&0x01;
cfg->in_failsafe_mode = (data[1]>>7)&0x01;
uint8_t chip_mode = (data[1]>>4)&0x07;
switch (chip_mode)
{
case 0x00:
cfg->radio09_mode = at86rf215_baseband_mode;
cfg->radio24_mode = at86rf215_baseband_mode;
break;
case 0x01:
cfg->radio09_mode = at86rf215_iq_if_mode;
cfg->radio24_mode = at86rf215_iq_if_mode;
break;
case 0x04:
cfg->radio09_mode = at86rf215_iq_if_mode;
cfg->radio24_mode = at86rf215_baseband_mode;
break;
case 0x05:
cfg->radio09_mode = at86rf215_baseband_mode;
cfg->radio24_mode = at86rf215_iq_if_mode;
break;
default:
ZF_LOGE("I/Q chipmode invalid: %d", chip_mode);
break;
}
cfg->clock_skew = (data[1]>>0)&0x03;
cfg->synchronized_incoming_iq = (data[2]>>7)&0x01;
if (verbose)
{
printf("Current I/Q interface settings:\n");
printf(" Loopback (RX => TX): %s\n", cfg->loopback_enable?"enabled":"disabled");
printf(" Drive strength: %d mA\n", cfg->drv_strength+1);
if (cfg->common_mode_voltage == at86rf215_iq_common_mode_v_ieee1596_1v2)
{
printf(" Common mode voltage: %.1f V\n", 1.2f);
}
else
{
printf(" Common mode voltage: %d mV\n", (cfg->common_mode_voltage+1) * 50 + 100);
}
printf(" I/Q interface for sub-GHz: %s\n", cfg->radio09_mode==at86rf215_iq_if_mode?"enabled":"diabled");
printf(" I/Q interface for 2.4-GHz: %s\n", cfg->radio24_mode==at86rf215_iq_if_mode?"enabled":"diabled");
printf(" I/Q Clock <=> Data skew: %.3f ns\n", cfg->clock_skew+1.906f);
printf(" Status 'Sync Failure': %d\n", cfg->synchronization_failed);
printf(" Status 'Failsafe mode': %d\n", cfg->in_failsafe_mode);
printf(" Status 'Is synchronized to incoming I/Q': %d\n", cfg->synchronized_incoming_iq);
}
}
//===================================================================
void at86rf215_setup_iq_if(at86rf215_st* dev, at86rf215_iq_interface_config_st* cfg)
{
uint8_t data[2] = {0};
data[0] |= (cfg->loopback_enable&0x01) << 7;
data[0] |= (cfg->drv_strength&0x03) << 4;
if (cfg->common_mode_voltage == at86rf215_iq_common_mode_v_ieee1596_1v2)
{
data[0] |= 1<<1;
}
else
{
data[0] |= (cfg->common_mode_voltage & 0x3) << 2;
}
data[0] |= (cfg->tx_control_with_iq_if & 0x01) << 0;
if (cfg->radio09_mode == at86rf215_iq_if_mode && cfg->radio24_mode == at86rf215_iq_if_mode)
{
data[1] |= 0x01 << 4;
}
else if (cfg->radio09_mode == at86rf215_iq_if_mode && cfg->radio24_mode == at86rf215_baseband_mode)
{
data[1] |= 0x04 << 4;
}
else if (cfg->radio09_mode == at86rf215_baseband_mode && cfg->radio24_mode == at86rf215_iq_if_mode)
{
data[1] |= 0x05 << 4;
}
data[1] |= (cfg->clock_skew & 0x03) << 0;
at86rf215_write_buffer(dev, REG_RF_IQIFC0, data, 2);
}
//===================================================================
double at86rf215_check_freq (at86rf215_st* dev, at86rf215_rf_channel_en ch, uint64_t freq_hz )
{
at86rf215_radio_channel_mode_en mode = 0;
at86rf215_rf_channel_en req_ch = 0;
if (at86rf215_radio_get_good_channel(freq_hz, &mode, &req_ch) < 0 || req_ch != ch)
{
ZF_LOGE("the requested channel or frequency not supported");
return -1;
}
int center_freq_25khz_res = 0;
int channel_number = 0;
double actual_freq = at86rf215_radio_get_frequency(mode, 1, freq_hz, &center_freq_25khz_res, &channel_number);
return actual_freq;
}
//===================================================================
int64_t at86rf215_setup_channel ( at86rf215_st* dev, at86rf215_rf_channel_en ch, uint64_t freq_hz )
{
if (dev->initialized == 0)
{
ZF_LOGE("device not initialized");
return -1;
}
at86rf215_radio_channel_mode_en mode = 0;
at86rf215_rf_channel_en req_ch = 0;
if (at86rf215_radio_get_good_channel(freq_hz, &mode, &req_ch) < 0 || req_ch != ch)
{
ZF_LOGE("the requested channel or frequency not supported");
return -1;
}
int center_freq_25khz_res = 0;
int channel_number = 0;
double actual_freq = at86rf215_radio_get_frequency(mode, 1, freq_hz, &center_freq_25khz_res, &channel_number);
at86rf215_radio_setup_channel(dev, ch, 1, center_freq_25khz_res, channel_number, mode);
return (int64_t)actual_freq;
}
//===================================================================
void at86rf215_setup_iq_radio_transmit (at86rf215_st* dev, at86rf215_rf_channel_en radio)
{
/*
It is assumed, that the radio has been reset before and is in State TRXOFF. All interrupts in register RFn_IRQS should be enabled (RFn_IRQM=0x3f).
*/
// 1. Set TRXOFF mode
// 2. Enable all interrupts in 09,_24_IRQS
// 3. Enable I/Q radio mode - setting IQIFC1.CHPM=1 at AT86RF215
// 4. Configure the Transmitter Frontend:
// Set the transmitter analog frontend sub-registers TXCUTC.LPFCUT and TXCUTC.PARAMP
// Set the transmitter digital frontend sub-registers TXDFE.SR and TXDFE.RCUT
// 5. Configure the channel parameters, see section "Channel Configuration" on page 62 and transmit power
// 6. Optional: Perform ED measurement, see section "Energy Measurement" on page 56. The following steps are recommended:
// Configure the measurement period, see register RFn_EDD.
// Switch to State RX.
// Start and finish a measurement:
// For single and continuous ED modes a measurement starts if the mode is written to sub-register EDC.EDM.
// The completion of the measurement is indicated by the interrupt IRQS.EDC. The resulting ED value can be read from register RFn_EDV.
// For the automatic mode, a measurement starts by setting bit AGCC.FRZC=1. After the completion of the measurement period, the ED value can be read from register RFn_EDV.
// 7. Switch to State TXPREP; interrupt IRQS.TRXRDY is issued.
// 8. To start the actual transmission, there are two possibilities, depending on the setting of sub-register IQIFC0.EEC:
// IQIFC0.EEC=0 => Enable the radio transmitter by writing command TX to the register RFn_CMD via SPI.
// IQIFC0.EEC=1 => The transmitter is activated automatically with the TX start signal embedded in I_DATA[0],
// 9. To finish a transmission depends on the setting of bit IQIFC0.EEC:
// IQIFC0.EEC=0 => To leave the State TX, write command TXPREP to the register RFn_CMD. Reaching State TXPREP is indicated by the interrupt IRQS.TRXRDY.
// IQIFC0.EEC=1 => If the bit I_DATA[0] is set to 0 (see Figure 6-4 on page 47) the ramp down process of the PA is started automatically. After ramp down the transmitter switches back to State TXPREP.
}
//===================================================================
void at86rf215_setup_iq_radio_receive (at86rf215_st* dev, at86rf215_rf_channel_en radio, uint64_t freq_hz,
int iqloopback, at86rf215_iq_clock_data_skew_en skew)
{
/*
It is assumed, that
1. the radio has been reset before and is in State TRXOFF.
2. All interrupts in register RFn_IRQS should be enabled (RFn_IRQM=0x3f).
*/
// 1. Set TRXOFF mode
at86rf215_radio_set_state(dev, radio, at86rf215_radio_state_cmd_trx_off);
// 2. Enable all radio interrupts in 09,_24_IRQS
at86rf215_radio_irq_st int_mask = {
.wake_up_por = 1,
.trx_ready = 1,
.energy_detection_complete = 1,
.battery_low = 1,
.trx_error = 1,
.IQ_if_sync_fail = 1,
.res = 0,
};
at86rf215_radio_setup_interrupt_mask(dev, radio, &int_mask);
// 3. Enable I/Q radio mode - setting IQIFC1.CHPM=1 at AT86RF215 (in AT86RF215IQ it is the only choice)
at86rf215_iq_interface_config_st iq_if_config =
{
.loopback_enable = iqloopback,
.drv_strength = at86rf215_iq_drive_current_4ma,
.common_mode_voltage = at86rf215_iq_common_mode_v_ieee1596_1v2,
.tx_control_with_iq_if = false,
.radio09_mode = at86rf215_iq_if_mode,
.radio24_mode = at86rf215_iq_if_mode,
.clock_skew = skew,
};
at86rf215_setup_iq_if(dev, &iq_if_config);
// 4. Configure the Receiving Frontend:
// Set the receiver analog frontend sub-registers RXBWC.BW and RXBWC.IFS,
// Set the receiver digital frontend sub-registers RXDFE.SR and RXDFE.RCUT
// Set the AGC registers RFn_AGCC and RFn_AGCS
at86rf215_radio_set_rx_bw_samp_st rx_bw_samp_cfg =
{
.inverter_sign_if = 0, // A value of one configures the receiver to implement the inverted-sign IF freq.
.shift_if_freq = 0, // A value of one configures the receiver to shift the IF frequency by factor of 1.25.
.bw = at86rf215_radio_rx_bw_BW2000KHZ_IF2000KHZ,
// The sub-register controls the receiver filter bandwidth settings.
.fcut = at86rf215_radio_rx_f_cut_half_fs,
// RX filter relative cut-off frequency
.fs = at86rf215_radio_rx_sample_rate_4000khz,
// RX Sample Rate
};
at86rf215_radio_set_rx_bandwidth_sampling(dev, radio, &rx_bw_samp_cfg);
at86rf215_radio_agc_ctrl_st agc_ctrl =
{
// commands
.agc_measure_source_not_filtered = 0, // AGC Input (0 - filterred, 1 - unfiltered, faster operation)
.avg = at86rf215_radio_agc_averaging_8, // AGC Average Time in Number of Samples
.reset_cmd = 0, // AGC Reset - resets the AGC and sets the maximum receiver gain.
.freeze_cmd = 0, // AGC Freeze Control - A value of one forces the AGC to
// freeze to its current value.
.enable_cmd = 1, // AGC Enable - a value of zero allows a manual setting of
// the RX gain control by sub-register AGCS.GCW
.att = at86rf215_radio_agc_relative_atten_21_db,// AGC Target Level - sets the AGC target level relative to ADC full scale.
.gain_control_word = 0, // 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.
.freeze_status = 0, // AGC Freeze Status - A value of one indicates that the AGC is on hold.
};
at86rf215_radio_setup_agc(dev, radio, &agc_ctrl);
// 5. Configure the channel parameters, see section "Channel Configuration" on page 62 and transmit power
at86rf215_setup_channel (dev, radio, freq_hz);
// 6. Switch to State TXPREP; interrupt IRQS.TRXRDY is issued.
// TXD and TXCLK are activated as shown in Figure 4-12 on page 26.
// What? Why TX?
// 7. Prepare the external baseband for reception of I/Q samples
// The FPGA was born prepared (;
// 8. Enable the radio receiver by writing command RX to the register RFn_CMD.
at86rf215_radio_set_state(dev, radio, at86rf215_radio_state_cmd_rx);
// 9. To prevent the AGC from switching its gain during reception, it is recommended to set AGCC.FRZC=1
// after reception of the preamble, the AGC has to be released after finishing reception by setting AGCC.FRZC=0.
// at86rf215_radio_setup_agc(dev, radio, &agc_ctrl);
}
//===================================================================
void at86rf215_stop_iq_radio_receive (at86rf215_st* dev, at86rf215_rf_channel_en radio)
{
at86rf215_radio_set_state(dev, radio, at86rf215_radio_state_cmd_trx_off);
at86rf215_iq_interface_config_st iq_if_config =
{
.loopback_enable = 0,
.drv_strength = at86rf215_iq_drive_current_2ma,
.common_mode_voltage = at86rf215_iq_common_mode_v_ieee1596_1v2,
.tx_control_with_iq_if = false,
.radio09_mode = at86rf215_baseband_mode,
.radio24_mode = at86rf215_baseband_mode,
.clock_skew = at86rf215_iq_clock_data_skew_4_906ns,
};
at86rf215_setup_iq_if(dev, &iq_if_config);
}
//===================================================================
void at86rf215_setup_iq_radio_continues_tx (at86rf215_st* dev, at86rf215_rf_channel_en ch)
{
// This is useful for application / production tests as well as certification tests.
// Using this mode, the transceiver acts as a continuous transmitter
// 1. Prior to transmission, the AT86RF215 must be in state TXPREP, see section "State Machine" on page 33.
// 2. The continuous transmission is enabled if the sub-register PC.CTX is set to 1
// 3. A frame transmission, started by CMD.CMD=TX with enabled continuous transmit mode (PC.CTX),
// transmits synchronization header (SHR), PHY header (PHR) and repeatedly PHY payload (PSDU).
// 4. The current PHY settings are used
// 5. the length of the PHY payload is configured by the concatenation of the registers BBCn_TXFLH and BBCn_TXFLL.
// If the sub-register PC.TXAFCS is set to 1, the last PHY payload octets are replaced by the calculated FCS
// 6. The transmission proceeds as long as the sub-register PC.CTX remains 1. If the sub-register PC.CTX is set
// to 0, the transmission stops once the current PSDU transmission is completed
}
void at86rf215_setup_iq_radio_dac_value_override_no_freq (at86rf215_st* dev,
at86rf215_rf_channel_en ch,
uint8_t tx_power)
{
at86rf215_radio_state_cmd_en state = at86rf215_radio_get_state(dev, ch);
if (state != at86rf215_radio_state_cmd_trx_off)
{
at86rf215_radio_set_state(dev, ch, at86rf215_radio_state_cmd_trx_off);
}
at86rf215_radio_set_state(dev, ch, at86rf215_radio_state_cmd_tx_prep);
at86rf215_radio_tx_ctrl_st tx_config =
{
.pa_ramping_time = at86rf215_radio_tx_pa_ramp_32usec,
.current_reduction = at86rf215_radio_pa_current_reduction_0ma,
.tx_power = tx_power,
.analog_bw = at86rf215_radio_tx_cut_off_80khz,
.digital_bw = at86rf215_radio_rx_f_cut_half_fs,
.fs = at86rf215_radio_rx_sample_rate_4000khz,
.direct_modulation = 0,
};
at86rf215_radio_setup_tx_ctrl(dev, ch, &tx_config);
at86rf215_radio_set_tx_dac_input_iq(dev, ch, 1, 0x7E, 1, 0x3F);
at86rf215_radio_set_state(dev, ch, at86rf215_radio_state_cmd_tx);
}
void at86rf215_setup_iq_radio_dac_value_override (at86rf215_st* dev,
at86rf215_rf_channel_en ch,
uint32_t freq_hz,
uint8_t tx_power)
{
// The AT86RF215 comprises a DAC (digital to analog converter) value overwrite functionality.
// Each transceiver contains two transmitter DACs in order to transmit IQ signals. This feature is useful to
// transmit an LO carrier which is necessary for certain certifications.
// If the sub-register TXDACI.ENTXDACID is set to 1, the digital input value of the in-phase signal DAC is
// overwritten with the value of the sub-register TXDACI.TXDACID. The same with TXDACQ.ENTXDACQD and TXDACQ.TXDACQD.
// Both TXDACI.TXDACID and TXDACQ.TXDACQD contain 7-bit binary values in the range from 0x00 to 0x7E (dec. 126).
// 0x3F (63) is zero, 0x00 is the minimal signal, and 0x7E is the maximal signal.
// If the transceiver is in state TX and both DAC values are overwritten, the transceiver transmits an LO
// carrier of the frequency selected by the "Frequency Synthesizer (PLL)" on page 62.
// To start a continuous transmission of a LO carrier, the transmitter is started as described in
// (at86rf215_setup_iq_radio_continues_tx) Frame Based Continuous Transmission.
// Alternatively, the transmitter can be started using chip mode 1 if sub-register IQIFC1.CHPM is set to 0x01.
// (this is the case of I/Q over LVDS) In this case the transmitter is started by command TX.
at86rf215_radio_state_cmd_en state = at86rf215_radio_get_state(dev, ch);
if (state != at86rf215_radio_state_cmd_trx_off)
{
at86rf215_radio_set_state(dev, ch, at86rf215_radio_state_cmd_trx_off);
}
at86rf215_radio_set_state(dev, ch, at86rf215_radio_state_cmd_tx_prep);
at86rf215_radio_tx_ctrl_st tx_config =
{
.pa_ramping_time = at86rf215_radio_tx_pa_ramp_32usec,
.current_reduction = at86rf215_radio_pa_current_reduction_0ma,
.tx_power = tx_power,
.analog_bw = at86rf215_radio_tx_cut_off_80khz,
.digital_bw = at86rf215_radio_rx_f_cut_half_fs,
.fs = at86rf215_radio_rx_sample_rate_4000khz,
.direct_modulation = 0,
};
at86rf215_radio_setup_tx_ctrl(dev, ch, &tx_config);
at86rf215_radio_external_ctrl_st aux_cfg =
{
.ext_lna_bypass_available = 0,
.agc_backoff = 0,
.analog_voltage_external = 0,
.analog_voltage_enable_in_off = 0,
.int_power_amplifier_voltage = 2,
.fe_pad_configuration = 1,
};
at86rf215_radio_setup_external_settings(dev, ch, &aux_cfg);
at86rf215_iq_interface_config_st iq_if_config =
{
.loopback_enable = 0,
.drv_strength = at86rf215_iq_drive_current_2ma,
.common_mode_voltage = at86rf215_iq_common_mode_v_ieee1596_1v2,
.tx_control_with_iq_if = false,
.radio09_mode = at86rf215_iq_if_mode,
.radio24_mode = at86rf215_iq_if_mode,
.clock_skew = at86rf215_iq_clock_data_skew_4_906ns,
};
at86rf215_setup_iq_if(dev, &iq_if_config);
at86rf215_radio_set_tx_dac_input_iq(dev, ch, 1, 0x7E, 1, 0x3F);
at86rf215_setup_channel (dev, ch, freq_hz);
at86rf215_radio_set_state(dev, ch, at86rf215_radio_state_cmd_tx);
}