/* * Copyright (C) 2018 Otto-von-Guericke-Universität Magdeburg * * This file is subject to the terms and conditions of the GNU Lesser * General Public License v2.1. See the file LICENSE in the top level * directory for more details. */ /** * @ingroup drivers_cc110x * @{ * * @file * @brief Implementation of the manual calibration facility of the * CC1100/CC1101 driver * * @author Marian Buschsieweke * @} */ #include #include #include "periph/gpio.h" #include "xtimer.h" #include "cc110x.h" #include "cc110x_internal.h" #define ENABLE_DEBUG 0 #include "debug.h" /** * @brief Read the calibration data from the transceiver and store it * * @param dev Device descriptor of the transceiver * * @pre @p dev is acquired using @p cc110x_acquire */ static inline void get_calibration_data(cc110x_t *dev) { char caldata[3]; cc110x_burst_read(dev, CC110X_REG_FSCAL3, caldata, sizeof(caldata)); dev->fscal.fscal3 = caldata[0]; dev->fscal.fscal2 = caldata[1]; dev->fscal.fscal1[dev->channel] = caldata[2]; } int cc110x_recalibrate(cc110x_t *dev) { /* Sadly we cannot use GDO0 to check for calibration, as it only * provides output in RX/TX state. But after successful manual * calibration, the device returns to IDLE state. Thus, we keep * calibrating until IDLE state is reached */ do { /* Start calibration */ cc110x_cmd(dev, CC110X_STROBE_CALIBRATE); /* Release SPI interface to give other threads a chance to use it */ cc110x_release(dev); /* Manual calibration take 735 micro seconds (see Table 34 on page * 54 in the date sheet). We'll wait 750 to be sure */ xtimer_usleep(750); /* Re-acquire SPI interface in order to check if calibration * succeeded */ cc110x_acquire(dev); } while (cc110x_state_from_status(cc110x_status(dev)) != CC110X_STATE_IDLE); get_calibration_data(dev); return 0; } int cc110x_full_calibration(cc110x_t *dev) { DEBUG("[cc110x] Obtaining calibration data for fast channel hopping\n"); if (!dev) { return -EINVAL; } cc110x_acquire(dev); switch (dev->state) { case CC110X_STATE_IDLE: /* falls through */ case CC110X_STATE_RX_MODE: /* falls through */ case CC110X_STATE_FSTXON: /* Current state is fine for deliberate calibration */ break; default: /* Current state prevents deliberate calibration */ cc110x_release(dev); return -EAGAIN; } uint8_t old_channel = dev->channel; /* Disable interrupts on GDO pins */ gpio_irq_disable(dev->params.gdo0); gpio_irq_disable(dev->params.gdo2); /* While waiting for calibration to be done, another thread could * be scheduled. Setting the state should prevent other threads from * messing around with the driver */ dev->state = CC110X_STATE_CALIBRATE; /* Go to IDLE to allow setting the channel */ cc110x_cmd(dev, CC110X_STROBE_IDLE); for (dev->channel = 0; dev->channel < CC110X_MAX_CHANNELS; dev->channel++) { uint8_t phy_chan = dev->channels->map[dev->channel]; if (phy_chan == 0xff) { /* Channel not supported by channel map */ continue; } /* Set the channel to calibrate for fast hopping */ cc110x_write(dev, CC110X_REG_CHANNR, phy_chan); if (cc110x_recalibrate(dev)) { /* cc110x_recalibrate() release device on error */ return -EIO; } } /* Update device to reflect current transceiver state */ dev->state = CC110X_STATE_IDLE; cc110x_release(dev); /* Hop back to old channel, IRQs are restored by cc110x_set_channel */ return cc110x_set_channel(dev, old_channel); }