#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 #include #include #include #include #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, ¢er_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, ¢er_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); }