mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
bb739907a2
This (re-)introduces the `CC110X_DEFAULT_CHANNEL` preprocessor macro to set the default channel of the `cc110x` at compile time. The same macro has been used in the previous version of the driver, so some users might still expect it to work.
178 lines
4.8 KiB
C
178 lines
4.8 KiB
C
/*
|
|
* 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 for the "public" API of the CC1100/CC1101 driver
|
|
*
|
|
* @author Marian Buschsieweke <marian.buschsieweke@ovgu.de>
|
|
* @}
|
|
*/
|
|
|
|
#include <errno.h>
|
|
#include <string.h>
|
|
|
|
#include "cc110x.h"
|
|
#include "cc110x_internal.h"
|
|
|
|
#define ENABLE_DEBUG (0)
|
|
#include "debug.h"
|
|
|
|
int cc110x_setup(cc110x_t *dev, const cc110x_params_t *params)
|
|
{
|
|
if (!dev || !params) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* Zero out everything but RIOT's driver interface, which should be
|
|
* managed by RIOT
|
|
*/
|
|
memset((char *)dev + sizeof(netdev_t), 0x00,
|
|
sizeof(cc110x_t) - sizeof(netdev_t));
|
|
dev->params = *params;
|
|
dev->netdev.driver = &cc110x_driver;
|
|
dev->state = CC110X_STATE_OFF;
|
|
return 0;
|
|
}
|
|
|
|
int cc110x_apply_config(cc110x_t *dev, const cc110x_config_t *conf,
|
|
const cc110x_chanmap_t *chanmap, uint8_t channel)
|
|
{
|
|
DEBUG("[cc110x] Applying new configuration\n");
|
|
if (!dev || !chanmap) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
if ((channel >= CC110X_MAX_CHANNELS) || (chanmap->map[channel] == 0xff)) {
|
|
/* Channel out of range or not supported in current channel map */
|
|
return -ERANGE;
|
|
}
|
|
|
|
if (cc110x_acquire(dev) != SPI_OK) {
|
|
return -EIO;
|
|
}
|
|
|
|
gpio_irq_disable(dev->params.gdo0);
|
|
gpio_irq_disable(dev->params.gdo2);
|
|
|
|
/* Go to IDLE state to allow reconfiguration */
|
|
cc110x_cmd(dev, CC110X_STROBE_IDLE);
|
|
dev->state = CC110X_STATE_IDLE;
|
|
|
|
if (conf != NULL) {
|
|
/* Write all three base frequency configuration bytes in one burst */
|
|
cc110x_burst_write(dev, CC110X_REG_FREQ2, &conf->base_freq, 3);
|
|
|
|
cc110x_write(dev, CC110X_REG_FSCTRL1, conf->fsctrl1);
|
|
cc110x_write(dev, CC110X_REG_MDMCFG4, conf->mdmcfg4);
|
|
cc110x_write(dev, CC110X_REG_MDMCFG3, conf->mdmcfg3);
|
|
cc110x_write(dev, CC110X_REG_DEVIATN, conf->deviatn);
|
|
}
|
|
|
|
/* We only need to store the channel, cc110x_full_calibration() will tune it
|
|
* in after calibration.
|
|
*/
|
|
dev->channel = channel;
|
|
dev->channels = chanmap;
|
|
cc110x_release(dev);
|
|
|
|
/* prepare hopping will call cc110x_enter_rx_mode(), which restores the IRQs */
|
|
return cc110x_full_calibration(dev);
|
|
}
|
|
|
|
int cc110x_set_tx_power(cc110x_t *dev, cc110x_tx_power_t power)
|
|
{
|
|
DEBUG("[cc110x] Applying TX power setting at index %u\n", (unsigned)power);
|
|
if (!dev) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
if ((unsigned)power >= CC110X_TX_POWER_NUMOF) {
|
|
return -ERANGE;
|
|
}
|
|
|
|
if (cc110x_acquire(dev) != SPI_OK) {
|
|
return -EIO;
|
|
}
|
|
|
|
switch (dev->state) {
|
|
case CC110X_STATE_IDLE:
|
|
/* falls through */
|
|
case CC110X_STATE_RX_MODE:
|
|
break;
|
|
default:
|
|
cc110x_release(dev);
|
|
return -EAGAIN;
|
|
}
|
|
|
|
uint8_t frend0 = 0x10 | (uint8_t)power;
|
|
cc110x_write(dev, CC110X_REG_FREND0, frend0);
|
|
dev->tx_power = power;
|
|
cc110x_release(dev);
|
|
return 0;
|
|
}
|
|
|
|
int cc110x_set_channel(cc110x_t *dev, uint8_t channel)
|
|
{
|
|
DEBUG("[cc110x] Hopping to channel %i\n", (int)channel);
|
|
if (!dev) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (cc110x_acquire(dev) != SPI_OK) {
|
|
return -EIO;
|
|
}
|
|
|
|
if ((channel >= CC110X_MAX_CHANNELS) || (dev->channels->map[channel] == 0xff)) {
|
|
/* Channel out of range or not supported in current channel map */
|
|
cc110x_release(dev);
|
|
return -ERANGE;
|
|
}
|
|
|
|
switch (dev->state) {
|
|
case CC110X_STATE_IDLE:
|
|
/* falls through */
|
|
case CC110X_STATE_RX_MODE:
|
|
/* falls through */
|
|
case CC110X_STATE_FSTXON:
|
|
/* Above states are fine for hopping */
|
|
break;
|
|
default:
|
|
/* All other states do not allow hopping right now */
|
|
cc110x_release(dev);
|
|
return -EAGAIN;
|
|
}
|
|
|
|
/* Disable IRQs, as e.g. PLL indicator will go LOW in IDLE state */
|
|
gpio_irq_disable(dev->params.gdo0);
|
|
gpio_irq_disable(dev->params.gdo2);
|
|
|
|
/* Go to IDLE state to disable frequency synchronizer */
|
|
cc110x_cmd(dev, CC110X_STROBE_IDLE);
|
|
|
|
/* Upload new channel and corresponding calibration data */
|
|
cc110x_write(dev, CC110X_REG_CHANNR, dev->channels->map[channel]);
|
|
|
|
uint8_t caldata[] = {
|
|
dev->fscal.fscal3, dev->fscal.fscal2, dev->fscal.fscal1[channel]
|
|
};
|
|
cc110x_burst_write(dev, CC110X_REG_FSCAL3, caldata, sizeof(caldata));
|
|
|
|
/* Start listening on the new channel (restores IRQs) */
|
|
cc110x_enter_rx_mode(dev);
|
|
|
|
dev->channel = channel;
|
|
cc110x_release(dev);
|
|
|
|
dev->netdev.event_callback(&dev->netdev, NETDEV_EVENT_FHSS_CHANGE_CHANNEL);
|
|
return 0;
|
|
}
|