Merge pull request #10340 from maribu/cc110x_rewrite

drivers/cc110x: Complete rewrite from scratch
This commit is contained in:
Peter Kietzmann 2019-08-20 17:58:21 +02:00 committed by GitHub
commit 11b4bab101
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
51 changed files with 4127 additions and 2267 deletions

View File

@ -164,7 +164,7 @@ ifneq (,$(filter gnrc_netif,$(USEMODULE)))
endif endif
endif endif
ifneq (,$(filter ieee802154 nrfmin esp_now gnrc_sixloenc,$(USEMODULE))) ifneq (,$(filter ieee802154 nrfmin esp_now cc110x gnrc_sixloenc,$(USEMODULE)))
ifneq (,$(filter gnrc_ipv6, $(USEMODULE))) ifneq (,$(filter gnrc_ipv6, $(USEMODULE)))
USEMODULE += gnrc_sixlowpan USEMODULE += gnrc_sixlowpan
endif endif

View File

@ -1,7 +1,7 @@
include $(RIOTBOARD)/common/msba2/Makefile.dep include $(RIOTBOARD)/common/msba2/Makefile.dep
ifneq (,$(filter netdev_default gnrc_netdev_default,$(USEMODULE))) ifneq (,$(filter netdev_default gnrc_netdev_default,$(USEMODULE)))
USEMODULE += cc110x USEMODULE += cc1100
endif endif
ifneq (,$(filter saul_default,$(USEMODULE))) ifneq (,$(filter saul_default,$(USEMODULE)))

View File

@ -1,7 +1,7 @@
# add driver for CC1101 sub-gigahertz transceiver as default netdev
ifneq (,$(filter netdev_default gnrc_netdev_default,$(USEMODULE))) ifneq (,$(filter netdev_default gnrc_netdev_default,$(USEMODULE)))
USEMODULE += cc110x USEMODULE += cc1101
endif endif
# add support for LEDs and buttons as default saul devices # add support for LEDs and buttons as default saul devices
ifneq (,$(filter saul_default,$(USEMODULE))) ifneq (,$(filter saul_default,$(USEMODULE)))
USEMODULE += saul_gpio USEMODULE += saul_gpio

View File

@ -33,8 +33,8 @@ extern "C" {
#define CC110X_PARAM_SPI SPI_DEV(0) /**< SPI interface CC1101 is connected to */ #define CC110X_PARAM_SPI SPI_DEV(0) /**< SPI interface CC1101 is connected to */
#define CC110X_PARAM_CS GPIO_PIN(PORT_B, 12) /**< CS pin of CC1101 */ #define CC110X_PARAM_CS GPIO_PIN(PORT_B, 12) /**< CS pin of CC1101 */
#define CC110X_PARAM_GDO0 GPIO_PIN(PORT_C, 4) /**< GDO0 pin of CC1101 */ #define CC110X_PARAM_GDO0 GPIO_PIN(PORT_C, 4) /**< GDO0 pin of CC1101 */
#define CC110X_PARAM_GDO1 GPIO_PIN(PORT_A, 6) /**< GDO1 pin of CC1101 */
#define CC110X_PARAM_GDO2 GPIO_PIN(PORT_C, 5) /**< GDO2 pin of CC1101 */ #define CC110X_PARAM_GDO2 GPIO_PIN(PORT_C, 5) /**< GDO2 pin of CC1101 */
#define CC110X_PARAM_SPI_CLOCK SPI_CLK_1MHZ /**< SPI clock (reduced to work around hw bug) */
/** @} */ /** @} */
/** /**

View File

@ -81,16 +81,18 @@ ifneq (,$(filter bm%280,$(USEMODULE)))
USEMODULE += bmx280 USEMODULE += bmx280
endif endif
ifneq (,$(filter cc110x,$(USEMODULE))) ifneq (,$(filter cc110%,$(USEMODULE)))
USEMODULE += ieee802154 USEMODULE += cc110x
USEMODULE += cc1xxx_common
USEMODULE += luid USEMODULE += luid
USEMODULE += netif
USEMODULE += xtimer USEMODULE += xtimer
ifneq (,$(filter gnrc_netdev_default,$(USEMODULE)))
USEMODULE += gnrc_cc110x
endif
FEATURES_REQUIRED += periph_gpio FEATURES_REQUIRED += periph_gpio
FEATURES_REQUIRED += periph_gpio_irq FEATURES_REQUIRED += periph_gpio_irq
FEATURES_REQUIRED += periph_spi FEATURES_REQUIRED += periph_spi
ifneq (,$(filter gnrc_ipv6,$(USEMODULE)))
USEMODULE += gnrc_sixlowpan
endif
endif endif
ifneq (,$(filter cc2420,$(USEMODULE))) ifneq (,$(filter cc2420,$(USEMODULE)))

View File

@ -1,3 +1 @@
DIRS += gnrc_cc110x
include $(RIOTBASE)/Makefile.base include $(RIOTBASE)/Makefile.base

View File

@ -1,100 +0,0 @@
/*
* Copyright (C) 2013 INRIA
* 2015 Kaspar Schleiser <kaspar@schleiser.de>
*
* 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 TI Chipcon CC110x default settings
*
* @author Thomas Hillebrandt <hillebra@inf.fu-berlin.de>
* @author Heiko Will <hwill@inf.fu-berlin.de>
* @author Oliver Hahm <oliver.hahm@inria.fr>
* @author Kaspar Schleiser <kaspar@schleiser.de>
* @}
*/
#include "board.h"
#include "cc110x.h"
/**
* @brief PATABLE with available output powers
* @note If changed in size, adjust MAX_OUTPUT_POWER definition
* in CC110x interface
*/
const char cc110x_default_pa_table[8] = {
0x00, /*< -52 dBm */
0x0D, /*< -20 dBm */
0x34, /*< -10 dBm */
0x57, /*< - 5 dBm */
0x8E, /*< 0 dBm */
0x85, /*< + 5 dBm */
0xCC, /*< + 7 dBm */
0xC3 /*< +10 dBm */
};
const char cc110x_default_base_freq[3] = { 0x21, 0x71, 0x7F };
/**
* @brief cc110x default settings
*/
const char cc110x_default_conf[] = {
0x06, /* IOCFG2 */
0x2E, /* IOCFG1 */
/* some boards use cc110x' GDO0 as clock source, so for those, we allow
* overriding of the corresponding setting, e.g., in board.h */
#ifdef CC110X_IOCONF0_VAL
CC110X_IOCONF0_VAL,
#else
0x0E, /* IOCFG0 */
#endif
0x07, /* FIFOTHR */
0x9B, /* SYNC1 */
0xAD, /* SYNC0 */
0xFF, /* PKTLEN */
0x06, /* PKTCTRL1 */
0x45, /* PKTCTRL0 (variable packet length) */
0xFF, /* ADDR */
0x00, /* CHANNR */
0x0F, /* FSCTRL1 */
0x00, /* FSCTRL0 */
0x21, /* FREQ2 */
0x71, /* FREQ1 */
0x7A, /* FREQ0 */
0x7C, /* MDMCFG4 */
0x7A, /* MDMCFG3 */
0x06, /* MDMCFG2 */
0xC0, /* MDMCFG1 */
0xF8, /* MDMCFG0 */
0x44, /* DEVIATN */
0x07, /* MCSM2 */
0x03, /* MCSM1 */
0x18, /* MCSM0 */
0x16, /* FOCCFG */
0x6C, /* BSCFG */
0x45, /* AGCCTRL2 */
0x40, /* AGCCTRL1 */
0x91, /* AGCCTRL0 */
0x87, /* WOREVT1 */
0x6B, /* WOREVT0 */
0xF8, /* WORCTRL */
0x56, /* FREND1 */
0x17, /* FREND0 */
0xEA, /* FSCAL3 */
0x2A, /* FSCAL2 */
0x00, /* FSCAL1 */
0x1F, /* FSCAL0 */
0x00 /* padding to 4 bytes */
};
/**
* @brief The size of the configuration array for CC110X in bytes
* */
const uint8_t cc110x_default_conf_size = sizeof(cc110x_default_conf);

View File

@ -1,239 +0,0 @@
/*
* Copyright (C) 2014 Freie Universität Berlin
* 2015 Kaspar Schleiser <kaspar@schleiser.de>
*
* 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 netdev interface for cc110x
*
* @author Fabian Nack <nack@inf.fu-berlin.de>
* @author Kaspar Schleiser <kaspar@schleiser.de>
* @}
*/
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include "cc110x.h"
#include "cc110x-netdev.h"
#include "cc110x-internal.h"
#include "cc110x-interface.h"
#include "cc110x-defines.h"
#include "net/eui64.h"
#include "periph/gpio.h"
#include "net/netdev.h"
#include "net/gnrc/nettype.h"
#define ENABLE_DEBUG (0)
#include "debug.h"
static int _send(netdev_t *dev, const iolist_t *iolist)
{
DEBUG("%s:%u\n", __func__, __LINE__);
netdev_cc110x_t *netdev_cc110x = (netdev_cc110x_t *)dev;
cc110x_pkt_t *cc110x_pkt = iolist->iol_base;
return cc110x_send(&netdev_cc110x->cc110x, cc110x_pkt);
}
static int _recv(netdev_t *dev, void *buf, size_t len, void *info)
{
DEBUG("%s:%u\n", __func__, __LINE__);
cc110x_t *cc110x = &((netdev_cc110x_t*) dev)->cc110x;
cc110x_pkt_t *cc110x_pkt = &cc110x->pkt_buf.packet;
if (cc110x_pkt->length > len) {
return -ENOSPC;
}
memcpy(buf, (void*)cc110x_pkt, cc110x_pkt->length);
if (info != NULL) {
netdev_cc110x_rx_info_t *cc110x_info = info;
cc110x_info->rssi = (int16_t)cc110x->pkt_buf.rssi/2 - CC110X_RSSI_OFFSET;
cc110x_info->lqi = cc110x->pkt_buf.lqi;
}
return cc110x_pkt->length;
}
static inline int _get_iid(netdev_t *netdev, eui64_t *value, size_t max_len)
{
cc110x_t *cc110x = &((netdev_cc110x_t*) netdev)->cc110x;
uint8_t *eui64 = (uint8_t*) value;
if (max_len < sizeof(eui64_t)) {
return -EOVERFLOW;
}
/* make address compatible to https://tools.ietf.org/html/rfc6282#section-3.2.2*/
memset(eui64, 0, sizeof(eui64_t));
eui64[3] = 0xff;
eui64[4] = 0xfe;
eui64[7] = cc110x->radio_address;
return sizeof(eui64_t);
}
static int _get(netdev_t *dev, netopt_t opt, void *value, size_t value_len)
{
cc110x_t *cc110x = &((netdev_cc110x_t*) dev)->cc110x;
switch (opt) {
case NETOPT_DEVICE_TYPE:
assert(value_len == 2);
*((uint16_t *) value) = NETDEV_TYPE_CC110X;
return 2;
#ifdef MODULE_GNRC_NETIF
case NETOPT_PROTO:
assert(value_len == sizeof(gnrc_nettype_t));
*((gnrc_nettype_t *)value) = cc110x->proto;
return sizeof(gnrc_nettype_t);
#endif
case NETOPT_CHANNEL:
assert(value_len > 1);
*((uint16_t *)value) = (uint16_t)cc110x->radio_channel;
return sizeof(uint16_t);
case NETOPT_ADDRESS:
assert(value_len > 0);
*((uint8_t *)value) = cc110x->radio_address;
return sizeof(uint8_t);
case NETOPT_MAX_PDU_SIZE:
assert(value_len > 0);
*((uint16_t *)value) = CC110X_PACKET_LENGTH - CC110X_L2_HDR_SIZE;
return sizeof(uint16_t);
case NETOPT_IPV6_IID:
return _get_iid(dev, value, value_len);
case NETOPT_ADDR_LEN:
case NETOPT_SRC_LEN:
*((uint16_t *)value) = sizeof(cc110x->radio_address);
return sizeof(uint16_t);
default:
break;
}
return -ENOTSUP;
}
static int _set(netdev_t *dev, netopt_t opt, const void *value, size_t value_len)
{
cc110x_t *cc110x = &((netdev_cc110x_t*) dev)->cc110x;
switch (opt) {
case NETOPT_CHANNEL:
{
const uint16_t *arg = value;
uint8_t channel = (uint8_t)(*arg);
#if CC110X_MIN_CHANNR
if (channel < CC110X_MIN_CHANNR) {
return -EINVAL;
}
#endif /* CC110X_MIN_CHANNR */
if (channel > CC110X_MAX_CHANNR) {
return -EINVAL;
}
if (cc110x_set_channel(cc110x, channel) == -1) {
return -EINVAL;
}
return sizeof(uint16_t);
}
case NETOPT_ADDRESS:
if (value_len < 1) {
return -EINVAL;
}
if (!cc110x_set_address(cc110x, *(const uint8_t*)value)) {
return -EINVAL;
}
return sizeof(uint8_t);
#ifdef MODULE_GNRC_NETIF
case NETOPT_PROTO:
if (value_len != sizeof(gnrc_nettype_t)) {
return -EINVAL;
}
else {
cc110x->proto = (gnrc_nettype_t) value;
return sizeof(gnrc_nettype_t);
}
break;
#endif
default:
return -ENOTSUP;
}
return 0;
}
static void _netdev_cc110x_isr(void *arg)
{
netdev_t *netdev = (netdev_t*) arg;
netdev->event_callback(netdev, NETDEV_EVENT_ISR);
}
static void _netdev_cc110x_rx_callback(void *arg)
{
netdev_t *netdev = (netdev_t*) arg;
cc110x_t *cc110x = &((netdev_cc110x_t*) arg)->cc110x;
gpio_irq_disable(cc110x->params.gdo2);
netdev->event_callback(netdev, NETDEV_EVENT_RX_COMPLETE);
}
static void _isr(netdev_t *dev)
{
cc110x_t *cc110x = &((netdev_cc110x_t*) dev)->cc110x;
cc110x_isr_handler(cc110x, _netdev_cc110x_rx_callback, (void*)dev);
}
static int _init(netdev_t *dev)
{
DEBUG("%s:%u\n", __func__, __LINE__);
cc110x_t *cc110x = &((netdev_cc110x_t*) dev)->cc110x;
gpio_init_int(cc110x->params.gdo2, GPIO_IN, GPIO_BOTH,
&_netdev_cc110x_isr, (void*)dev);
gpio_set(cc110x->params.gdo2);
gpio_irq_disable(cc110x->params.gdo2);
/* Switch to RX mode */
cc110x_rd_set_mode(cc110x, RADIO_MODE_ON);
return 0;
}
const netdev_driver_t netdev_cc110x_driver = {
.send=_send,
.recv=_recv,
.init=_init,
.get=_get,
.set=_set,
.isr=_isr
};
int netdev_cc110x_setup(netdev_cc110x_t *netdev_cc110x, const cc110x_params_t *params)
{
DEBUG("netdev_cc110x_setup()\n");
netdev_cc110x->netdev.driver = &netdev_cc110x_driver;
/* set default protocol */
#ifdef MODULE_GNRC_NETIF
# ifdef MODULE_GNRC_SIXLOWPAN
netdev_cc110x->cc110x.proto = GNRC_NETTYPE_SIXLOWPAN;
# else
netdev_cc110x->cc110x.proto = GNRC_NETTYPE_UNDEF;
# endif
#endif
return cc110x_setup(&netdev_cc110x->cc110x, params);
}

View File

@ -1,327 +0,0 @@
/*
* Copyright (C) 2014 Freie Universität Berlin
* Copyright (C) 2013 INRIA
*
* 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 Functions for packet reception and transmission on cc110x devices
*
* @author Oliver Hahm <oliver.hahm@inria.fr>
* @author Fabian Nack <nack@inf.fu-berlin.de>
* @author Kaspar Schleiser <kaspar@schleiser.de>
* @}
*/
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include "cc110x.h"
#include "cc110x-spi.h"
#include "cc110x-internal.h"
#include "cc110x-interface.h"
#include "cc110x-defines.h"
#include "periph/gpio.h"
#include "irq.h"
#include "kernel_types.h"
#include "msg.h"
#include "cpu_conf.h"
#include "cpu.h"
#ifdef MODULE_OD
#include "od.h"
#endif
#include "log.h"
#define ENABLE_DEBUG (0)
#include "debug.h"
const char *cc110x_state_to_text(uint8_t state)
{
switch (state){
case RADIO_IDLE:
return "idle";
case RADIO_TX_BUSY:
return "tx busy";
case RADIO_RX:
return "rx";
case RADIO_RX_BUSY:
return "rx busy";
case RADIO_PWD:
return "pwd";
case RADIO_UNKNOWN:
return "unknown";
}
return "invalid";
}
static void _rx_abort(cc110x_t *dev)
{
gpio_irq_disable(dev->params.gdo2);
cc110x_strobe(dev, CC110X_SIDLE); /* Switch to IDLE (should already be)... */
cc110x_strobe(dev, CC110X_SFRX); /* ...for flushing the RX FIFO */
cc110x_switch_to_rx(dev);
}
static void _rx_start(cc110x_t *dev)
{
dev->radio_state = RADIO_RX_BUSY;
cc110x_pkt_buf_t *pkt_buf = &dev->pkt_buf;
pkt_buf->pos = 0;
gpio_irq_disable(dev->params.gdo2);
cc110x_write_reg(dev, CC110X_IOCFG2,
CC110X_GDO_HIGH_ON_RX_FIFO_FILLED_OR_PKT_END);
gpio_irq_enable(dev->params.gdo2);
}
static void _rx_read_data(cc110x_t *dev, void(*callback)(void*), void*arg)
{
int fifo = cc110x_get_reg_robust(dev, 0xfb);
if (fifo & RXFIFO_OVERFLOW) {
DEBUG("%s:%s:%u rx overflow\n", RIOT_FILE_RELATIVE, __func__, __LINE__);
_rx_abort(dev);
return;
}
if (!fifo) {
gpio_irq_enable(dev->params.gdo2);
return;
}
cc110x_pkt_buf_t *pkt_buf = &dev->pkt_buf;
if (!pkt_buf->pos) {
pkt_buf->pos = 1;
pkt_buf->packet.length = cc110x_read_reg(dev, CC110X_RXFIFO);
/* Possible packet received, RX -> IDLE (0.1 us) */
dev->cc110x_statistic.packets_in++;
}
int left = pkt_buf->packet.length+1 - pkt_buf->pos;
/* if the fifo doesn't contain the rest of the packet,
* leav at least one byte as per spec sheet. */
int to_read = (fifo < left) ? (fifo-1) : fifo;
if (to_read > left) {
to_read = left;
}
if (to_read) {
cc110x_readburst_reg(dev, CC110X_RXFIFO,
((char *)&pkt_buf->packet)+pkt_buf->pos, to_read);
pkt_buf->pos += to_read;
}
if (to_read == left) {
uint8_t status[2];
/* full packet received. */
/* Read the 2 appended status bytes (status[0] = RSSI, status[1] = LQI) */
cc110x_readburst_reg(dev, CC110X_RXFIFO, (char *)status, 2);
/* Store RSSI value of packet */
pkt_buf->rssi = status[I_RSSI];
/* Bit 0-6 of LQI indicates the link quality (LQI) */
pkt_buf->lqi = status[I_LQI] & LQI_EST;
/* MSB of LQI is the CRC_OK bit */
int crc_ok = (status[I_LQI] & CRC_OK) >> 7;
if (crc_ok) {
LOG_DEBUG("cc110x: received packet from=%u to=%u payload len=%u\n",
(unsigned)pkt_buf->packet.phy_src,
(unsigned)pkt_buf->packet.address,
pkt_buf->packet.length - CC110X_L2_HDR_SIZE);
/* let someone know that we've got a packet */
callback(arg);
cc110x_switch_to_rx(dev);
}
else {
DEBUG("%s:%s:%u crc-error\n", RIOT_FILE_RELATIVE, __func__, __LINE__);
dev->cc110x_statistic.packets_in_crc_fail++;
#if defined(MODULE_OD) && ENABLE_DEBUG
od_hex_dump(pkt_buf->packet.data,
pkt_buf->packet.length - CC110X_L2_HDR_SIZE,
OD_WIDTH_DEFAULT);
#endif
_rx_abort(dev);
}
}
}
static void _rx_continue(cc110x_t *dev, void(*callback)(void*), void*arg)
{
if (dev->radio_state != RADIO_RX_BUSY) {
DEBUG("%s:%s:%u _rx_continue in invalid state\n", RIOT_FILE_RELATIVE,
__func__, __LINE__);
_rx_abort(dev);
return;
}
gpio_irq_disable(dev->params.gdo2);
do {
_rx_read_data(dev, callback, arg);
} while (gpio_read(dev->params.gdo2));
}
static void _tx_abort(cc110x_t *dev)
{
cc110x_switch_to_rx(dev);
}
static void _tx_continue(cc110x_t *dev)
{
gpio_irq_disable(dev->params.gdo2);
cc110x_pkt_t *pkt = &dev->pkt_buf.packet;
int size = pkt->length + 1;
int left = size - dev->pkt_buf.pos;
if (!left) {
dev->cc110x_statistic.raw_packets_out++;
LOG_DEBUG("cc110x: packet successfully sent.\n");
cc110x_switch_to_rx(dev);
return;
}
int fifo = CC110X_FIFO_LENGTH - cc110x_get_reg_robust(dev, 0xfa);
if (fifo & TXFIFO_UNDERFLOW) {
DEBUG("%s:%s:%u tx underflow!\n", RIOT_FILE_RELATIVE, __func__, __LINE__);
_tx_abort(dev);
return;
}
if (!fifo) {
DEBUG("%s:%s:%u fifo full!?\n", RIOT_FILE_RELATIVE, __func__, __LINE__);
_tx_abort(dev);
return;
}
int to_send = left > fifo ? fifo : left;
/* Write packet into TX FIFO */
cc110x_writeburst_reg(dev, CC110X_TXFIFO, ((char *)pkt)+dev->pkt_buf.pos, to_send);
dev->pkt_buf.pos += to_send;
if (left == size) {
/* Switch to TX mode */
cc110x_strobe(dev, CC110X_STX);
}
if (to_send < left) {
/* set GDO2 to 0x2 -> will deassert at TX FIFO below threshold */
gpio_irq_enable(dev->params.gdo2);
cc110x_write_reg(dev, CC110X_IOCFG2,
CC110X_GDO_LOW_ON_TX_FIFO_BELOW_THRESHOLD);
}
else {
/* set GDO2 to 0x6 -> will deassert at packet end */
cc110x_write_reg(dev, CC110X_IOCFG2, CC110X_GDO_HIGH_ON_SYNC_WORD);
gpio_irq_enable(dev->params.gdo2);
}
}
void cc110x_isr_handler(cc110x_t *dev, void(*callback)(void*), void*arg)
{
switch (dev->radio_state) {
case RADIO_RX:
if (gpio_read(dev->params.gdo2)) {
_rx_start(dev);
}
else {
DEBUG("cc110x_isr_handler((): isr handled too slow?\n");
_rx_abort(dev);
}
break;
case RADIO_RX_BUSY:
_rx_continue(dev, callback, arg);
break;
case RADIO_TX_BUSY:
if (!gpio_read(dev->params.gdo2)) {
_tx_continue(dev);
}
else {
DEBUG("cc110x_isr_handler() RADIO_TX_BUSY + GDO2\n");
}
break;
default:
DEBUG("%s:%s:%u: unhandled mode\n", RIOT_FILE_RELATIVE,
__func__, __LINE__);
}
}
int cc110x_send(cc110x_t *dev, cc110x_pkt_t *packet)
{
DEBUG("cc110x: snd pkt to %u payload_length=%u\n",
(unsigned)packet->address,
(unsigned)packet->length - CC110X_L2_HDR_SIZE);
unsigned size;
switch (dev->radio_state) {
case RADIO_RX_BUSY:
case RADIO_TX_BUSY:
DEBUG("cc110x: invalid state for sending: %s\n",
cc110x_state_to_text(dev->radio_state));
return -EAGAIN;
}
/*
* Number of bytes to send is:
* length of phy payload (packet->length)
* + size of length field (1 byte)
*/
size = packet->length + 1;
if (size > CC110X_PACKET_LENGTH) {
DEBUG("%s:%s:%u trying to send oversized packet\n",
RIOT_FILE_RELATIVE, __func__, __LINE__);
return -ENOSPC;
}
/* set source address */
packet->phy_src = dev->radio_address;
/* Disable RX interrupt */
gpio_irq_disable(dev->params.gdo2);
dev->radio_state = RADIO_TX_BUSY;
#ifdef MODULE_CC110X_HOOKS
cc110x_hook_tx();
#endif
cc110x_write_reg(dev, CC110X_IOCFG2,
CC110X_GDO_LOW_ON_TX_FIFO_BELOW_THRESHOLD);
/* Put CC110x in IDLE mode to flush the FIFO */
cc110x_strobe(dev, CC110X_SIDLE);
/* Flush TX FIFO to be sure it is empty */
cc110x_strobe(dev, CC110X_SFTX);
memcpy((char*)&dev->pkt_buf.packet, packet, size);
dev->pkt_buf.pos = 0;
_tx_continue(dev);
return (int)size;
}

View File

@ -1,172 +0,0 @@
/*
* Copyright (C) 2014 Freie Universität Berlin
* Copyright (C) 2015 Kaspar Schleiser <kaspar@schleiser.de>
*
* 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 TI Chipcon CC110x spi driver
*
* @author Thomas Hillebrandt <hillebra@inf.fu-berlin.de>
* @author Heiko Will <hwill@inf.fu-berlin.de>
* @author Fabian Nack <nack@inf.fu-berlin.de>
* @author Joakim Gebart <joakim.gebart@eistec.se>
* @author Kaspar Schleiser <kaspar@schleiser.de>
* @}
*/
#include <stdio.h>
#include "cc110x.h"
#include "cc110x-spi.h"
#include "cc110x-internal.h"
#include "cc110x-defines.h"
#include "periph/gpio.h"
#include "periph/spi.h"
#include "xtimer.h"
#define SPI_CLK SPI_CLK_5MHZ
#define SPI_MODE SPI_MODE_0
/**********************************************************************
* CC110x spi access
**********************************************************************/
static inline void lock(cc110x_t *dev)
{
spi_acquire(dev->params.spi, dev->params.cs, SPI_MODE, SPI_CLK);
}
void cc110x_cs(cc110x_t *dev)
{
volatile int retry_count = 0;
/* Switch MISO/GDO1 to GPIO input mode */
#ifndef GPIO_READS_SPI_PINS
gpio_init(dev->params.gdo1, GPIO_IN);
#endif
/* CS to low */
gpio_clear(dev->params.cs);
/* Wait for SO to go low (voltage regulator
* has stabilized and the crystal is running) */
while (gpio_read(dev->params.gdo1)) {
/* Wait ~500us and try again */
xtimer_usleep(CS_SO_WAIT_TIME);
if (gpio_read(dev->params.gdo1)) {
retry_count++;
if (retry_count > CC110X_GDO1_LOW_RETRY) {
puts("[CC110X spi] fatal error\n");
break;
}
gpio_set(dev->params.cs);
gpio_clear(dev->params.cs);
}
}
/* Switch MISO/GDO1 to spi mode */
#ifndef GPIO_READS_SPI_PINS
spi_init_pins(dev->params.spi);
#endif
}
void cc110x_writeburst_reg(cc110x_t *dev, uint8_t addr, const char *src, uint8_t count)
{
lock(dev);
cc110x_cs(dev);
spi_transfer_regs(dev->params.spi, SPI_CS_UNDEF,
(addr | CC110X_WRITE_BURST), src, NULL, count);
gpio_set(dev->params.cs);
spi_release(dev->params.spi);
}
void cc110x_readburst_reg(cc110x_t *dev, uint8_t addr, char *buffer, uint8_t count)
{
int i = 0;
lock(dev);
cc110x_cs(dev);
spi_transfer_byte(dev->params.spi, SPI_CS_UNDEF, false,
(addr | CC110X_READ_BURST));
while (i < count) {
buffer[i] = (char)spi_transfer_byte(dev->params.spi, SPI_CS_UNDEF,
false, CC110X_NOBYTE);
i++;
}
gpio_set(dev->params.cs);
spi_release(dev->params.spi);
}
void cc110x_write_reg(cc110x_t *dev, uint8_t addr, uint8_t value)
{
lock(dev);
cc110x_cs(dev);
spi_transfer_reg(dev->params.spi, SPI_CS_UNDEF, addr, value);
gpio_set(dev->params.cs);
spi_release(dev->params.spi);
}
uint8_t cc110x_read_reg(cc110x_t *dev, uint8_t addr)
{
uint8_t result;
lock(dev);
cc110x_cs(dev);
result = spi_transfer_reg(dev->params.spi, SPI_CS_UNDEF,
(addr | CC110X_READ_SINGLE), CC110X_NOBYTE);
gpio_set(dev->params.cs);
spi_release(dev->params.spi);
return result;
}
uint8_t cc110x_read_status(cc110x_t *dev, uint8_t addr)
{
uint8_t result;
lock(dev);
cc110x_cs(dev);
result = spi_transfer_reg(dev->params.spi, SPI_CS_UNDEF,
(addr | CC110X_READ_BURST), CC110X_NOBYTE);
gpio_set(dev->params.cs);
spi_release(dev->params.spi);
return (uint8_t) result;
}
uint8_t cc110x_get_reg_robust(cc110x_t *dev, uint8_t addr)
{
uint8_t res1, res2;
lock(dev);
cc110x_cs(dev);
do {
res1 = spi_transfer_reg(dev->params.spi, SPI_CS_UNDEF,
(addr | CC110X_READ_BURST), CC110X_NOBYTE);
res2 = spi_transfer_reg(dev->params.spi, SPI_CS_UNDEF,
(addr | CC110X_READ_BURST), CC110X_NOBYTE);
} while (res1 != res2);
gpio_set(dev->params.cs);
spi_release(dev->params.spi);
return res1;
}
uint8_t cc110x_strobe(cc110x_t *dev, uint8_t c)
{
#ifdef CC110X_DONT_RESET
if (c == CC110X_SRES) {
return 0;
}
#endif
uint8_t result;
lock(dev);
cc110x_cs(dev);
result = spi_transfer_byte(dev->params.spi, SPI_CS_UNDEF, false, c);
gpio_set(dev->params.cs);
spi_release(dev->params.spi);
return result;
}

View File

@ -1,7 +1,5 @@
/* /*
* Copyright (C) 2014 Freie Universität Berlin * Copyright (C) 2018 Otto-von-Guericke-Universität Magdeburg
* Copyright (C) 2013 INRIA
* Copyright (C) 2015 Kaspar Schleiser <kaspar@schleiser.de>
* *
* This file is subject to the terms and conditions of the GNU Lesser * 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 * General Public License v2.1. See the file LICENSE in the top level
@ -11,261 +9,165 @@
/** /**
* @ingroup drivers_cc110x * @ingroup drivers_cc110x
* @{ * @{
* @file
* @brief Basic functionality of cc110x driver
* *
* @author Oliver Hahm <oliver.hahm@inria.fr> * @file
* @author Fabian Nack <nack@inf.fu-berlin.de> * @brief Implementation for the "public" API of the CC1100/CC1101 driver
* @author Kaspar Schleiser <kaspar@schleiser.de> *
* @author Marian Buschsieweke <marian.buschsieweke@ovgu.de>
* @} * @}
*/ */
#include "luid.h" #include <errno.h>
#include "board.h" #include <string.h>
#include "periph/gpio.h"
#include "periph/spi.h"
#include "xtimer.h"
#include "cpu.h"
#include "log.h"
#include "cc110x.h" #include "cc110x.h"
#include "cc110x-defaultsettings.h" #include "cc110x_internal.h"
#include "cc110x-defines.h"
#include "cc110x-interface.h"
#include "cc110x-internal.h"
#include "cc110x-spi.h"
#define ENABLE_DEBUG (0) #define ENABLE_DEBUG (0)
#include "debug.h" #include "debug.h"
/* Internal function prototypes */
#ifndef CC110X_DONT_RESET
static void _reset(cc110x_t *dev);
static void _power_up_reset(cc110x_t *dev);
#endif
int cc110x_setup(cc110x_t *dev, const cc110x_params_t *params) int cc110x_setup(cc110x_t *dev, const cc110x_params_t *params)
{ {
DEBUG("%s:%s:%u\n", RIOT_FILE_RELATIVE, __func__, __LINE__); if (!dev || !params) {
return -EINVAL;
#ifdef MODULE_CC110X_HOOKS }
cc110x_hooks_init();
#endif
/* 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->params = *params;
dev->netdev.driver = &cc110x_driver;
/* Configure chip-select */ dev->state = CC110X_STATE_OFF;
spi_init_cs(dev->params.spi, dev->params.cs);
/* Configure GDO1 */
gpio_init(dev->params.gdo1, GPIO_IN);
#ifndef CC110X_DONT_RESET
/* reset device*/
_power_up_reset(dev);
#endif
/* set default state */
dev->radio_state = RADIO_IDLE;
/* Write configuration to configuration registers */
cc110x_writeburst_reg(dev, 0x00, cc110x_default_conf, cc110x_default_conf_size);
/* Write PATABLE (power settings) */
cc110x_writeburst_reg(dev, CC110X_PATABLE, CC110X_DEFAULT_PATABLE, 8);
/* set base frequency */
cc110x_set_base_freq_raw(dev, CC110X_DEFAULT_FREQ);
/* Set default channel number */
cc110x_set_channel(dev, CC110X_DEFAULT_CHANNEL);
/* set default node id */
uint8_t addr;
luid_get(&addr, 1);
cc110x_set_address(dev, addr);
LOG_INFO("cc110x: initialized with address=%u and channel=%i\n",
(unsigned)dev->radio_address,
dev->radio_channel);
return 0; return 0;
} }
uint8_t cc110x_set_address(cc110x_t *dev, uint8_t address) int cc110x_apply_config(cc110x_t *dev, const cc110x_config_t *conf,
const cc110x_chanmap_t *chanmap)
{ {
DEBUG("%s:%s:%u setting address %u\n", RIOT_FILE_RELATIVE, __func__, DEBUG("[cc110x] Applying new configuration\n");
__LINE__, (unsigned)address); if (!dev || !chanmap) {
if (!(address < MIN_UID)) { return -EINVAL;
if (dev->radio_state != RADIO_UNKNOWN) {
cc110x_write_register(dev, CC110X_ADDR, address);
dev->radio_address = address;
return address;
}
} }
return 0; if (cc110x_acquire(dev) != SPI_OK) {
return -EIO;
} }
void cc110x_set_base_freq_raw(cc110x_t *dev, const char* freq_array) gpio_irq_disable(dev->params.gdo0);
{
#if ENABLE_DEBUG == 1
uint8_t _tmp[] = { freq_array[2], freq_array[1], freq_array[0], 0x00};
uint32_t *FREQ = (uint32_t*) _tmp;
DEBUG("cc110x_set_base_freq_raw(): setting base frequency to %uHz\n",
(26000000>>16) * (unsigned)(*FREQ));
#endif
cc110x_writeburst_reg(dev, CC110X_FREQ2, freq_array, 3);
}
void cc110x_set_monitor(cc110x_t *dev, uint8_t mode)
{
DEBUG("%s:%s:%u\n", RIOT_FILE_RELATIVE, __func__, __LINE__);
cc110x_write_register(dev, CC110X_PKTCTRL1, mode ? 0x04 : 0x06);
}
void cc110x_setup_rx_mode(cc110x_t *dev)
{
DEBUG("%s:%s:%u\n", RIOT_FILE_RELATIVE, __func__, __LINE__);
/* Stay in RX mode until end of packet */
cc110x_write_reg(dev, CC110X_MCSM2, 0x07);
cc110x_switch_to_rx(dev);
}
void cc110x_switch_to_rx(cc110x_t *dev)
{
DEBUG("%s:%s:%u\n", RIOT_FILE_RELATIVE, __func__, __LINE__);
#ifdef MODULE_CC110X_HOOKS
cc110x_hook_rx();
#endif
gpio_irq_disable(dev->params.gdo2); gpio_irq_disable(dev->params.gdo2);
/* flush RX fifo */ /* Go to IDLE state to allow reconfiguration */
cc110x_strobe(dev, CC110X_SIDLE); cc110x_cmd(dev, CC110X_STROBE_IDLE);
cc110x_strobe(dev, CC110X_SFRX); dev->state = CC110X_STATE_IDLE;
dev->radio_state = RADIO_RX; 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_reg(dev, CC110X_IOCFG2, CC110X_GDO_HIGH_ON_SYNC_WORD); cc110x_write(dev, CC110X_REG_FSCTRL1, conf->fsctrl1);
cc110x_strobe(dev, CC110X_SRX); cc110x_write(dev, CC110X_REG_MDMCFG4, conf->mdmcfg4);
cc110x_write(dev, CC110X_REG_MDMCFG3, conf->mdmcfg3);
gpio_irq_enable(dev->params.gdo2); cc110x_write(dev, CC110X_REG_DEVIATN, conf->deviatn);
} }
void cc110x_wakeup_from_rx(cc110x_t *dev) /* Set current channel to zero, as the new map might not support the current
* virtual channel number. cc110x_full_calibration() will tune in that
* channel after calibration.
*/
dev->channel = 0;
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)
{ {
if (dev->radio_state != RADIO_RX) { DEBUG("[cc110x] Applying TX power setting at index %u\n", (unsigned)power);
return; if (!dev) {
return -EINVAL;
} }
LOG_DEBUG("cc110x: switching to idle mode\n"); if ((unsigned)power >= CC110X_TX_POWER_NUMOF) {
return -ERANGE;
cc110x_strobe(dev, CC110X_SIDLE);
dev->radio_state = RADIO_IDLE;
} }
void cc110x_switch_to_pwd(cc110x_t *dev) if (cc110x_acquire(dev) != SPI_OK) {
{ return -EIO;
LOG_DEBUG("cc110x: switching to powerdown mode\n");
cc110x_wakeup_from_rx(dev);
cc110x_strobe(dev, CC110X_SPWD);
dev->radio_state = RADIO_PWD;
#ifdef MODULE_CC110X_HOOKS
cc110x_hook_off();
#endif
} }
#ifndef MODULE_CC110X_HOOKS switch (dev->state) {
int16_t cc110x_set_channel(cc110x_t *dev, uint8_t channr) case CC110X_STATE_IDLE:
{ /* falls through */
DEBUG("%s:%s:%u\n", RIOT_FILE_RELATIVE, __func__, __LINE__); case CC110X_STATE_RX_MODE:
if (channr > MAX_CHANNR) {
return -1;
}
cc110x_write_register(dev, CC110X_CHANNR, channr * 10);
dev->radio_channel = channr;
return channr;
}
#endif
#ifndef CC110X_DONT_RESET
static void _reset(cc110x_t *dev)
{
DEBUG("%s:%s:%u\n", RIOT_FILE_RELATIVE, __func__, __LINE__);
cc110x_wakeup_from_rx(dev);
cc110x_cs(dev);
cc110x_strobe(dev, CC110X_SRES);
xtimer_usleep(100);
}
static void _power_up_reset(cc110x_t *dev)
{
DEBUG("%s:%s:%u\n", RIOT_FILE_RELATIVE, __func__, __LINE__);
gpio_set(dev->params.cs);
gpio_clear(dev->params.cs);
gpio_set(dev->params.cs);
xtimer_usleep(RESET_WAIT_TIME);
_reset(dev);
}
#endif
void cc110x_write_register(cc110x_t *dev, uint8_t r, uint8_t value)
{
/* Save old radio state */
uint8_t old_state = dev->radio_state;
/* Wake up from RX (no effect if in other mode) */
cc110x_wakeup_from_rx(dev);
cc110x_write_reg(dev, r, value);
/* Have to put radio back to RX if old radio state
* was RX, otherwise no action is necessary */
if (old_state == RADIO_RX) {
cc110x_switch_to_rx(dev);
}
}
int cc110x_rd_set_mode(cc110x_t *dev, int mode)
{
DEBUG("%s:%s:%u\n", RIOT_FILE_RELATIVE, __func__, __LINE__);
int result;
/* Get current radio mode */
if ((dev->radio_state == RADIO_UNKNOWN) || (dev->radio_state == RADIO_PWD)) {
result = RADIO_MODE_OFF;
}
else {
result = RADIO_MODE_ON;
}
switch(mode) {
case RADIO_MODE_ON:
LOG_DEBUG("cc110x: switching to RX mode\n");
cc110x_setup_rx_mode(dev); /* Set chip to desired mode */
break; break;
case RADIO_MODE_OFF:
gpio_irq_disable(dev->params.gdo2); /* Disable interrupts */
cc110x_switch_to_pwd(dev); /* Set chip to power down mode */
break;
case RADIO_MODE_GET:
/* do nothing, just return current mode */
default: default:
/* do nothing */ cc110x_release(dev);
break; return -EAGAIN;
} }
/* Return previous mode */ uint8_t frend0 = 0x10 | (uint8_t)power;
return result; 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;
} }

View File

@ -0,0 +1,141 @@
/*
* 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 <marian.buschsieweke@ovgu.de>
* @}
*/
#include <errno.h>
#include <stdint.h>
#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
*/
if (cc110x_acquire(dev) != SPI_OK) {
return -EIO;
}
} 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;
}
if (cc110x_acquire(dev) != SPI_OK) {
return -EIO;
}
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);
}

View File

@ -0,0 +1,74 @@
/*
* 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 Channel maps that translate "virtual" channels to "physical"
* channels.
*
* @author Marian Buschsieweke <marian.buschsieweke@ovgu.de>
* @}
*/
#include "cc110x.h"
#include "cc110x_internal.h"
const cc110x_chanmap_t cc110x_chanmap_433mhz_300khz = {
.map = {
0, /*< base + 0.000MHz = 433.225 MHz (up to 350 kHz wide channel) */
7, /*< base + 0.350MHz = 433.575 MHz (up to 350 kHz wide channel) */
14, /*< base + 0.700MHz = 433.925 MHz (up to 350 kHz wide channel) */
21, /*< base + 1.050MHz = 434.275 MHz (up to 350 kHz wide channel) */
28, /*< base + 1.400MHz = 434.625 MHz (only up to *330* kHz wide) */
255, /*< License free range (433.05 MHz - 434.79 MHz) exhausted :-( */
255, /*< License free range (433.05 MHz - 434.79 MHz) exhausted :-( */
255, /*< License free range (433.05 MHz - 434.79 MHz) exhausted :-( */
}
};
const cc110x_chanmap_t cc110x_chanmap_433mhz_50khz = {
.map = {
0, /*< base + 0.000MHz = 433.100 MHz (LDP433 Channel 2) */
4, /*< base + 0.200MHz = 433.300 MHz (LDP433 Channel 10)*/
8, /*< base + 0.400MHz = 433.500 MHz (LDP433 Channel 18) */
12, /*< base + 0.600MHz = 433.700 MHz (LDP433 Channel 26) */
16, /*< base + 0.800MHz = 433.900 MHz (LDP433 Channel 34) */
20, /*< base + 1.000MHz = 434.100 MHz (LDP433 Channel 42) */
24, /*< base + 1.200MHz = 434.300 MHz (LDP433 Channel 50) */
28, /*< base + 1.400MHz = 434.500 MHz (LDP433 Channel 58) */
}
};
const cc110x_chanmap_t cc110x_chanmap_433mhz_50khz_alt = {
.map = {
2, /*< base + 0.100MHz = 433.200 MHz (LDP433 Channel 6) */
6, /*< base + 0.300MHz = 433.400 MHz (LDP433 Channel 14)*/
10, /*< base + 0.500MHz = 433.600 MHz (LDP433 Channel 22) */
14, /*< base + 0.700MHz = 433.800 MHz (LDP433 Channel 30) */
18, /*< base + 0.900MHz = 434.000 MHz (LDP433 Channel 38) */
22, /*< base + 1.100MHz = 434.200 MHz (LDP433 Channel 46) */
26, /*< base + 1.300MHz = 434.400 MHz (LDP433 Channel 54) */
30, /*< base + 1.500MHz = 434.600 MHz (LDP433 Channel 62) */
}
};
const cc110x_chanmap_t cc110x_chanmap_868mhz_lora = {
.map = {
0, /*< base + 0.000MHz = 865.20 MHz = LoRa868 Channel 10 */
6, /*< base + 0.300MHz = 865.50 MHz = LoRa868 Channel 11 */
12, /*< base + 0.600MHz = 865.80 MHz = LoRa868 Channel 12 */
18, /*< base + 0.900MHz = 866.10 MHz = LoRa868 Channel 13 */
24, /*< base + 1.200MHz = 866.40 MHz = LoRa868 Channel 14 */
30, /*< base + 1.500MHz = 866.70 MHz = LoRa868 Channel 15 */
36, /*< base + 1.800MHz = 867.00 MHz = LoRa868 Channel 16 */
56, /*< base + 2.800MHz = 868.00 MHz = LoRa868 Channel 17 */
}
};

View File

@ -0,0 +1,115 @@
/*
* 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 Functions to communicate with the CC1100/CC1101 transceiver
*
* @author Marian Buschsieweke <marian.buschsieweke@ovgu.de>
* @}
*/
#include <errno.h>
#include "periph/gpio.h"
#include "periph/spi.h"
#include "xtimer.h"
#include "cc110x.h"
#include "cc110x_constants.h"
int cc110x_power_on(cc110x_t *dev)
{
gpio_t cs = dev->params.cs;
if (gpio_init(cs, GPIO_OUT)) {
return -EIO;
}
gpio_clear(cs);
xtimer_usleep(150);
gpio_set(cs);
spi_init_cs(dev->params.spi, dev->params.cs);
return 0;
}
uint8_t cc110x_read(cc110x_t *dev, uint8_t addr, uint8_t *dest)
{
uint8_t status;
addr |= CC110X_SINGLE_BYTE_READ;
spi_transfer_bytes(dev->params.spi, dev->params.cs, true,
&addr, &status, 1);
spi_transfer_bytes(dev->params.spi, dev->params.cs, false, NULL, dest, 1);
return status;
}
uint8_t cc110x_read_reliable(cc110x_t *dev, uint8_t addr, uint8_t *dest)
{
uint8_t status, tmp;
do {
cc110x_read(dev, addr, &tmp);
status = cc110x_read(dev, addr, dest);
} while (*dest != tmp);
return status;
}
uint8_t cc110x_write(cc110x_t *dev, uint8_t addr, uint8_t data)
{
uint8_t status;
addr |= CC110X_SINGLE_BYTE_WRITE;
spi_transfer_bytes(dev->params.spi, dev->params.cs, true,
&addr, &status, 1);
spi_transfer_bytes(dev->params.spi, dev->params.cs, false, &data, NULL, 1);
return status;
}
uint8_t cc110x_burst_read(cc110x_t *dev, uint8_t addr, void *dest, size_t len)
{
uint8_t status;
addr |= CC110X_BURST_READ;
spi_transfer_bytes(dev->params.spi, dev->params.cs, true,
&addr, &status, 1);
spi_transfer_bytes(dev->params.spi, dev->params.cs, false, NULL, dest, len);
return status;
}
uint8_t cc110x_burst_write(cc110x_t *dev, uint8_t addr,
const void *src, size_t len)
{
uint8_t status;
addr |= CC110X_BURST_WRITE;
spi_transfer_bytes(dev->params.spi, dev->params.cs, true,
&addr, &status, 1);
spi_transfer_bytes(dev->params.spi, dev->params.cs, false, src, NULL, len);
return status;
}
uint8_t cc110x_cmd(cc110x_t *dev, uint8_t cmd_strobe)
{
uint8_t status;
spi_transfer_bytes(dev->params.spi, dev->params.cs, false,
&cmd_strobe, &status, 1);
return status;
}
uint8_t cc110x_status(cc110x_t *dev)
{
uint8_t status;
do {
status = cc110x_cmd(dev, CC110X_STROBE_STATUS);
} while (status != cc110x_cmd(dev, CC110X_STROBE_STATUS));
return status;
}

View File

@ -0,0 +1,65 @@
/*
* 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 Transceiver configuration for different base bands, modulation
* rate and channel bandwidth.
*
* @author Marian Buschsieweke <marian.buschsieweke@ovgu.de>
* @}
*/
#include "cc110x.h"
#include "cc110x_internal.h"
const cc110x_config_t cc110x_config_433mhz_250kbps_300khz = {
/* 0x10A99A * 26MHz / 65536 = 433.2252 MHz (LPD433 Channel 7)
*
* This is 175 kHz above lower end of the license free range, thus up to 5
* 350 kHz wide channels can be used (see @ref cc110x_conf_t::deviatn).
*/
.base_freq = { 0x10, 0xA9, 0x9A },
/* Intermediate frequency: 0x0C * 26MHz / 1024 = 304.7kHz */
.fsctrl1 = 0x0C,
/* 541.67 kHz channel filter bandwidth */
.mdmcfg4 = 0x2D,
/* 250 kBaud symbol rate ==> 250 kbps data rate with GFSK */
.mdmcfg3 = 0x3B,
/* Deviation of +- 126.953 kHz ==> channel bandwidth about 300 kHz */
.deviatn = 0x62,
};
const cc110x_config_t cc110x_config_433mhz_38kbps_50khz = {
/* 0x10A85F * 26MHz / 65536 = 433.1002 MHz (LPD433 Channel 2) */
.base_freq = { 0x10, 0xA8, 0x5F },
/* Intermediate frequency: 0x06 * 26000kHz / 1024 = 152.3kHz */
.fsctrl1 = 0x06,
/* 101.5625 kHz channel filter bandwidth */
.mdmcfg4 = 0xCA,
/* 38.38 kBaud symbol rate ==> 38.38 kbps data rate with GFSK */
.mdmcfg3 = 0x83,
/* Deviation of +- 20.63 kHz ==> channel bandwidth about 50 kHz */
.deviatn = 0x35,
};
const cc110x_config_t cc110x_config_868mhz_250kbps_300khz = {
/* 0x2146e4 * 26MHz / 65536 = 865.1998 MHz (LoRa 868 Channel 10) */
.base_freq = { 0x21, 0x46, 0xE4 },
/* Intermediate frequency: 0x0C * 26MHz / 1024 = 304.7kHz */
.fsctrl1 = 0x0C,
/* 541.67 kHz channel filter bandwidth */
.mdmcfg4 = 0x2D,
/* 250 kBaud symbol rate ==> 250 kbps data rate with GFSK */
.mdmcfg3 = 0x3B,
/* Deviation of +- 126.953 kHz ==> channel bandwidth about 300 kHz */
.deviatn = 0x62,
};

View File

@ -0,0 +1,623 @@
/*
* 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 RIOT's netdev_driver API for the CC1100/CC1101
* transceiver
*
* @author Marian Buschsieweke <marian.buschsieweke@ovgu.de>
* @}
*/
#include <errno.h>
#include <string.h>
#include "assert.h"
#include "iolist.h"
#include "irq.h"
#include "luid.h"
#include "mutex.h"
#include "net/eui64.h"
#include "net/netdev.h"
#include "xtimer.h"
#include "cc110x.h"
#include "cc110x_internal.h"
#define ENABLE_DEBUG (0)
#include "debug.h"
static int cc110x_init(netdev_t *netdev);
static int cc110x_recv(netdev_t *netdev, void *buf, size_t len, void *info);
static int cc110x_send(netdev_t *netdev, const iolist_t *iolist);
static int cc110x_get(netdev_t *netdev, netopt_t opt,
void *val, size_t max_len);
static int cc110x_set(netdev_t *netdev, netopt_t opt,
const void *val, size_t len);
/**
* @brief A lookup table to convert from dBm value to the best matching
* @ref cc110x_tx_power_t value
*/
static const int8_t tx_power_from_dbm[] = {
[CC110X_TX_POWER_MINUS_30_DBM] = -25,
[CC110X_TX_POWER_MINUS_20_DBM] = -17,
[CC110X_TX_POWER_MINUS_15_DBM] = -12,
[CC110X_TX_POWER_MINUS_10_DBM] = -5,
[CC110X_TX_POWER_0_DBM] = 3,
[CC110X_TX_POWER_PLUS_5_DBM] = 6,
[CC110X_TX_POWER_PLUS_7_DBM] = 9,
};
/**
* @brief A lookup table to convert an @ref cc110x_tx_power_t value to dBm
*/
static const int8_t dbm_from_tx_power[] = {
[CC110X_TX_POWER_MINUS_30_DBM] = -30,
[CC110X_TX_POWER_MINUS_20_DBM] = -20,
[CC110X_TX_POWER_MINUS_15_DBM] = -15,
[CC110X_TX_POWER_MINUS_10_DBM] = -10,
[CC110X_TX_POWER_0_DBM] = 0,
[CC110X_TX_POWER_PLUS_5_DBM] = 5,
[CC110X_TX_POWER_PLUS_7_DBM] = 7,
[CC110X_TX_POWER_PLUS_10_DBM] = 10,
};
const netdev_driver_t cc110x_driver = {
.init = cc110x_init,
.recv = cc110x_recv,
.send = cc110x_send,
.isr = cc110x_isr,
.get = cc110x_get,
.set = cc110x_set,
};
void cc110x_on_gdo(void *_dev)
{
cc110x_t *dev = _dev;
if ((dev->state & 0x07) == CC110X_STATE_TX_MODE) {
/* Unlock mutex to unblock netdev thread */
mutex_unlock(&dev->isr_signal);
}
else {
dev->netdev.event_callback(&dev->netdev, NETDEV_EVENT_ISR);
}
}
static int identify_device(cc110x_t *dev)
{
uint8_t partnum, version, status;
int is_ready;
cc110x_state_t state;
cc110x_read(dev, CC110X_REG_VERSION, &version);
/* Retrieving the status is reliable for non-transient status */
status = cc110x_read(dev, CC110X_REG_PARTNUM, &partnum);
state = cc110x_state_from_status(status);
/* Most significant bit should be zero, otherwise chip is not ready */
is_ready = cc110x_is_ready_from_status(status);
DEBUG("[cc110x] PARTNUM = %i, VERSION = %i, STATUS = 0x%02x, READY = %i\n",
(int)partnum, (int)version, (int)status, is_ready);
if ((state != CC110X_STATE_IDLE) || (!is_ready)) {
DEBUG("[cc110x] IC not ready or in invalid state\n");
return -1;
}
/* Source: https://e2e.ti.com/support/wireless-connectivity/other-wireless/f/667/t/370643 */
if (partnum != 0) {
DEBUG("[cc110x] Device not a CC110x transceiver\n");
return -1;
}
switch (version) {
case 3:
DEBUG("[cc110x] Detected CC1100 transceiver\n");
/* RSSI offset is 78dBm @ 868MHz & 250kBaud.
* Depends on the symbol rate and base band and ranges from
* 74dBm to 79dBm.
*/
dev->rssi_offset = 78;
return 0;
case 5:
DEBUG("[cc110x] Detected CC1100E transceiver\n");
/* RSSI offset is 79 dBm @ 250kbps & 250 kbps.
* Depends on base band and symbol rate and ranges from
* 75dBm to 79dBm
*/
dev->rssi_offset = 79;
return 0;
case 4:
/* falls through */
case 14:
/* falls through */
case 20:
/* RSSI offset for the CC1101 is independent of symbol rate and
* base 74 dBm
*/
dev->rssi_offset = 74;
DEBUG("[cc110x] Detected CC1101 transceiver\n");
return 0;
default:
DEBUG("[cc110x] Device not a CC110x transceiver\n");
return -1;
}
}
static int check_config(cc110x_t *dev)
{
char buf[CC110X_CONF_SIZE];
/* Verify content of main config registers */
cc110x_burst_read(dev, CC110X_CONF_START, buf, sizeof(buf));
if (memcmp(buf, cc110x_conf, sizeof(buf))) {
DEBUG("[cc110x] ERROR: Verification of main config registers failed\n"
" Check SPI wiring or reduce SPI clock\n");
return -1;
}
/* Verify content of "magic number" config registers */
cc110x_burst_read(dev, CC110X_REG_TEST2, buf,
sizeof(cc110x_magic_registers));
if (memcmp(buf, cc110x_magic_registers, sizeof(cc110x_magic_registers))) {
DEBUG("[cc110x] ERROR: Verification of \"magic\" registers failed\n"
" Check SPI wiring or reduce SPI clock\n");
return -1;
}
/* Verify content of PA_TABLE */
cc110x_burst_read(dev, CC110X_MULTIREG_PATABLE, buf, CC110X_PATABLE_LEN);
if (memcmp(buf, dev->params.patable->data, CC110X_PATABLE_LEN)) {
DEBUG("[cc110x] ERROR: Verification of PA_TABLE failed\n"
" Check SPI wiring or reduce SPI clock\n");
return -1;
}
DEBUG("[cc110x] Content of configuration registers verified\n");
return 0;
}
static int check_gdo_pins(cc110x_t *dev)
{
/* GPIOs connected to GDOs are not yet configured, so we do this now.
* This configuration is just temporarily for testing, as gpio_init_int()
* will reconfigure them later again. We do not want to set up the
* interrupts before validating the transceiver, so we effectively configure
* the GPIOs twice.
*/
if (gpio_init(dev->params.gdo2, GPIO_IN)) {
DEBUG("[cc110x] Configuring GDO2 failed");
return -1;
}
if (gpio_init(dev->params.gdo0, GPIO_IN)) {
DEBUG("[cc110x] Configuring GDO0 failed");
return -1;
}
/* Validate that GDO2 responds to configuration updates */
cc110x_write(dev, CC110X_REG_IOCFG2, CC110X_GDO_CONSTANT_HIGH);
if (!gpio_read(dev->params.gdo2)) {
DEBUG("[cc110x] GDO2 does not respond (check wiring!)\n");
return -1;
}
cc110x_write(dev, CC110X_REG_IOCFG2, CC110X_GDO_CONSTANT_LOW);
if (gpio_read(dev->params.gdo2)) {
DEBUG("[cc110x] GDO2 does not respond (check wiring!)\n");
return -1;
}
/* Validate that GDO0 responds to configuration updates */
cc110x_write(dev, CC110X_REG_IOCFG0, CC110X_GDO_CONSTANT_HIGH);
if (!gpio_read(dev->params.gdo0)) {
DEBUG("[cc110x] GDO0 does not respond (check wiring!)\n");
return -1;
}
cc110x_write(dev, CC110X_REG_IOCFG0, CC110X_GDO_CONSTANT_LOW);
if (gpio_read(dev->params.gdo0)) {
DEBUG("[cc110x] GDO0 does not respond (check wiring!)\n");
return -1;
}
/* Restore default GDO2 & GDO0 config */
cc110x_write(dev, CC110X_REG_IOCFG2, cc110x_conf[CC110X_REG_IOCFG2]);
cc110x_write(dev, CC110X_REG_IOCFG0, cc110x_conf[CC110X_REG_IOCFG0]);
return 0;
}
static int cc110x_init(netdev_t *netdev)
{
cc110x_t *dev = (cc110x_t *)netdev;
/* Use locked mutex to block thread on TX and un-block from ISR */
mutex_init(&dev->isr_signal);
mutex_lock(&dev->isr_signal);
/* Make sure the crystal is stable and the chip ready. This is needed as
* the reset is done via an SPI command, but the SPI interface must not be
* used unless the chip is ready according to the data sheet. After the
* reset, a second call to cc110x_power_on() is needed to finally have
* the transceiver in a known state and ready for SPI communication.
*/
if (cc110x_power_on(dev)) {
DEBUG("[cc110x] netdev_driver_t::init(): Failed to pull CS pin low\n");
return -EIO;
}
if (cc110x_acquire(dev) != SPI_OK) {
DEBUG("[cc110x] netdev_driver_t::init(): Failed to setup/acquire SPI "
"interface\n");
return -EIO;
}
/* Performing a reset of the transceiver to get it in a known state */
cc110x_cmd(dev, CC110X_STROBE_RESET);
cc110x_release(dev);
/* Again, make sure the crystal is stable and the chip ready */
if (cc110x_power_on(dev)) {
DEBUG("[cc110x] netdev_driver_t::init(): Failed to pull CS pin low "
"after reset\n");
return -EIO;
}
if (cc110x_acquire(dev) != SPI_OK) {
DEBUG("[cc110x] netdev_driver_t::init(): Failed to setup/acquire SPI "
"interface after reset\n");
return -EIO;
}
if (identify_device(dev)) {
DEBUG("[cc110x] netdev_driver_t::init(): Device identification failed\n");
cc110x_release(dev);
return -ENOTSUP;
}
/* Upload the main configuration */
cc110x_burst_write(dev, CC110X_CONF_START, cc110x_conf, CC110X_CONF_SIZE);
/* Set TX power to match uploaded configuration */
dev->tx_power = CC110X_TX_POWER_0_DBM;
/* Upload the poorly documented magic numbers obtained via SmartRF Studio */
cc110x_burst_write(dev, CC110X_REG_TEST2, cc110x_magic_registers,
sizeof(cc110x_magic_registers));
/* Setup the selected PA_TABLE */
cc110x_burst_write(dev, CC110X_MULTIREG_PATABLE,
dev->params.patable->data, CC110X_PATABLE_LEN);
/* Verify main config, magic numbers and PA_TABLE correctly uploaded */
if (check_config(dev)) {
cc110x_release(dev);
return -EIO;
}
/* Verify that pins GDO2 and GDO0 are correctly connected */
if (check_gdo_pins(dev)) {
cc110x_release(dev);
return -EIO;
}
/* Setup the layer 2 address, but do not accept CC110X_L2ADDR_AUTO (which
* has the value 0x00 and is used for broadcast)
*/
dev->addr = dev->params.l2addr;
while (dev->addr == CC110X_L2ADDR_AUTO) {
luid_get(&dev->addr, 1);
}
cc110x_write(dev, CC110X_REG_ADDR, dev->addr);
/* Setup interrupt on GDO0 */
if (gpio_init_int(dev->params.gdo0, GPIO_IN, GPIO_BOTH,
cc110x_on_gdo, dev)) {
cc110x_release(dev);
DEBUG("[cc110x] netdev_driver_t::init(): Failed to setup interrupt on "
"GDO0 pin\n");
return -EIO;
}
/* Setup interrupt on GDO2 */
if (gpio_init_int(dev->params.gdo2, GPIO_IN, GPIO_BOTH,
cc110x_on_gdo, dev)) {
gpio_irq_disable(dev->params.gdo0);
cc110x_release(dev);
DEBUG("[cc110x] netdev_driver_t::init(): Failed to setup interrupt on "
"GDO2 pin\n");
return -EIO;
}
/* Update the state of the driver/transceiver */
dev->state = CC110X_STATE_IDLE;
cc110x_release(dev);
int retval; /*< Store return value to be able to pass through error code */
/* Apply configuration (if non-NULL) and channel map, which also calls
* cc110x_full_calibration
*/
retval = cc110x_apply_config(dev, dev->params.config, dev->params.channels);
if (retval) {
gpio_irq_disable(dev->params.gdo0);
gpio_irq_disable(dev->params.gdo2);
DEBUG("[cc110x] netdev_driver_t::init(): cc110x_apply_config() "
"failed\n");
/* Pass through received error code */
return retval;
}
else {
DEBUG("[cc110x] netdev_driver_t::init(): Success\n");
}
return 0;
}
static int cc110x_recv(netdev_t *netdev, void *buf, size_t len, void *info)
{
cc110x_t *dev = (cc110x_t *)netdev;
/* Call to cc110x_enter_rx_mode() will clear dev->buf.len, so back up it first */
int size = dev->buf.len;
if (cc110x_acquire(dev) != SPI_OK) {
DEBUG("[cc110x] netdev_driver_t::recv(): cc110x_acquire() "
"failed\n");
return -EIO;
}
/* Copy RX info on last frame (if requested) */
if (info != NULL) {
*((cc1xxx_rx_info_t *)info) = dev->rx_info;
}
if (!buf) {
/* Get the size of the frame; if len > 0 then also drop the frame */
if (len > 0) {
/* Drop frame requested */
cc110x_enter_rx_mode(dev);
}
cc110x_release(dev);
return size;
}
if (len < (size_t)size) {
/* Drop frame and return -ENOBUFS */
cc110x_enter_rx_mode(dev);
cc110x_release(dev);
return -ENOBUFS;
}
memcpy(buf, dev->buf.data, (size_t)size);
cc110x_enter_rx_mode(dev);
cc110x_release(dev);
return size;
}
static int cc110x_send(netdev_t *netdev, const iolist_t *iolist)
{
cc110x_t *dev = (cc110x_t *)netdev;
/* assert that cc110x_send was called with valid parameters */
assert(netdev && iolist && (iolist->iol_len == sizeof(cc1xxx_l2hdr_t)));
if (cc110x_acquire(dev) != SPI_OK) {
DEBUG("[cc110x] netdev_driver_t::send(): cc110x_acquire() failed\n");
return -1;
}
switch (dev->state) {
case CC110X_STATE_FSTXON:
/* falls through */
case CC110X_STATE_RX_MODE:
break;
case CC110X_STATE_RECEIVING:
cc110x_release(dev);
DEBUG("[cc110x] netdev_driver_t::send(): Refusing to send while "
"receiving a frame\n");
return -EBUSY;
default:
cc110x_release(dev);
DEBUG("[cc110x] netdev_driver_t::send(): Driver state %i prevents "
"sending\n", (int)dev->state);
return -1;
}
/* Copy data to send into frame buffer */
size_t size = sizeof(cc1xxx_l2hdr_t);
memcpy(dev->buf.data, iolist->iol_base, sizeof(cc1xxx_l2hdr_t));
for (const iolist_t *iol = iolist->iol_next; iol; iol = iol->iol_next) {
if (iol->iol_len) {
if (size + iol->iol_len > CC110X_MAX_FRAME_SIZE) {
cc110x_release(dev);
DEBUG("[cc110x] netdev_driver_t::send(): Frame size of %uB "
"exceeds maximum supported size of %uB\n",
(unsigned)(size + iol->iol_len),
(unsigned)CC110X_MAX_FRAME_SIZE);
return -1;
}
memcpy(dev->buf.data + size, iol->iol_base, iol->iol_len);
size += iol->iol_len;
}
}
dev->buf.len = (uint8_t)size;
/* Disable IRQs, as GDO configuration will be changed now */
gpio_irq_disable(dev->params.gdo0);
gpio_irq_disable(dev->params.gdo2);
/* Fill the TX FIFO: First write the length, then the frame */
dev->buf.pos = (size > CC110X_FIFO_SIZE - 1) ? CC110X_FIFO_SIZE - 1 : size;
/* cc110x_framebuf_t has the same memory layout as the device expects */
cc110x_burst_write(dev, CC110X_MULTIREG_FIFO,
&dev->buf, dev->buf.pos + 1);
/* Go to TX */
cc110x_cmd(dev, CC110X_STROBE_TX);
/* Configure GDO2 and update state */
if (dev->buf.pos < dev->buf.len) {
/* We need to keep feeding TX FIFO */
cc110x_write(dev, CC110X_REG_IOCFG2, CC110X_GDO_ON_TX_DATA);
dev->state = CC110X_STATE_TX_MODE;
}
else {
/* All data in TX FIFO, just waiting for transceiver to finish */
cc110x_write(dev, CC110X_REG_IOCFG2, CC110X_GDO_CONSTANT_LOW);
dev->state = CC110X_STATE_TX_COMPLETING;
}
cc110x_release(dev);
/* Restore IRQs */
gpio_irq_enable(dev->params.gdo0);
gpio_irq_enable(dev->params.gdo2);
while ((dev->state & 0x07) == CC110X_STATE_TX_MODE) {
/* Block until mutex is unlocked from ISR */
mutex_lock(&dev->isr_signal);
cc110x_isr(&dev->netdev);
}
return (int)size;
}
/**
* @brief Generate an IPv6 interface identifier for a CC110X transceiver
*
* @param dev Transceiver to create the IPv6 interface identifier (IID)
* @param iid Store the generated IID here
*
* @return Returns the size of @ref eui64_t to confirm with the API
* in @ref netdev_driver_t::get
*/
static int cc110x_get_iid(cc110x_t *dev, eui64_t *iid)
{
static const eui64_t empty_iid = {
.uint8 = { 0x00, 0x00, 0x00, 0xff, 0xfe, 0x00, 0x00, 0x00 }
};
*iid = empty_iid;
iid->uint8[7] = dev->addr;
return sizeof(eui64_t);
}
static int cc110x_get(netdev_t *netdev, netopt_t opt,
void *val, size_t max_len)
{
cc110x_t *dev = (cc110x_t *)netdev;
switch (opt) {
case NETOPT_DEVICE_TYPE:
assert(max_len == sizeof(uint16_t));
*((uint16_t *)val) = NETDEV_TYPE_CC110X;
return sizeof(uint16_t);
case NETOPT_PROTO:
assert(max_len == sizeof(gnrc_nettype_t));
*((gnrc_nettype_t *)val) = CC110X_DEFAULT_PROTOCOL;
return sizeof(gnrc_nettype_t);
case NETOPT_MAX_PACKET_SIZE:
assert(max_len == sizeof(uint16_t));
*((uint16_t *)val) = CC110X_MAX_FRAME_SIZE - sizeof(cc1xxx_l2hdr_t);
return sizeof(uint16_t);
case NETOPT_ADDR_LEN:
/* falls through */
case NETOPT_SRC_LEN:
assert(max_len == sizeof(uint16_t));
*((uint16_t *)val) = CC1XXX_ADDR_SIZE;
return sizeof(uint16_t);
case NETOPT_ADDRESS:
assert(max_len >= CC1XXX_ADDR_SIZE);
*((uint8_t *)val) = dev->addr;
return CC1XXX_ADDR_SIZE;
case NETOPT_IPV6_IID:
if (max_len < sizeof(eui64_t)) {
return -EOVERFLOW;
}
return cc110x_get_iid(dev, val);
case NETOPT_CHANNEL:
assert(max_len == sizeof(uint16_t));
*((uint16_t *)val) = dev->channel;
return sizeof(uint16_t);
case NETOPT_TX_POWER:
assert(max_len == sizeof(uint16_t));
*((uint16_t *)val) = dbm_from_tx_power[dev->tx_power];
return sizeof(uint16_t);
default:
return -ENOTSUP;
}
}
/**
* @brief Set the given address as the device's layer 2 address
*
* @param dev Device descripter of the transceiver
* @param addr Address to set
*/
static int cc110x_set_addr(cc110x_t *dev, uint8_t addr)
{
if (cc110x_acquire(dev) != SPI_OK) {
return -EIO;
}
dev->addr = addr;
cc110x_write(dev, CC110X_REG_ADDR, addr);
cc110x_release(dev);
return 1;
}
static int cc110x_set(netdev_t *netdev, netopt_t opt,
const void *val, size_t len)
{
(void)len;
cc110x_t *dev = (cc110x_t *)netdev;
switch (opt) {
case NETOPT_ADDRESS:
assert(len == CC1XXX_ADDR_SIZE);
return cc110x_set_addr(dev, *((uint8_t *)val));
case NETOPT_CHANNEL:
{
assert(len == sizeof(uint16_t));
int retval;
uint16_t channel = *((uint16_t *)val);
if (channel >= CC110X_MAX_CHANNELS) {
return -EINVAL;
}
if ((retval = cc110x_set_channel(dev, (uint8_t)channel))) {
return retval;
}
}
return sizeof(uint16_t);
case NETOPT_TX_POWER:
{
assert(len == sizeof(int16_t));
int16_t dbm = *((int16_t *)val);
cc110x_tx_power_t power = CC110X_TX_POWER_MINUS_30_DBM;
for ( ; power < CC110X_TX_POWER_PLUS_10_DBM; power++) {
if ((int16_t)tx_power_from_dbm[power] >= dbm) {
break;
}
}
if (cc110x_set_tx_power(dev, power)) {
return -EINVAL;
}
}
return sizeof(uint16_t);
default:
return -ENOTSUP;
}
}

View File

@ -0,0 +1,67 @@
/*
* 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 Default configuration of the transceiver output power for the
* different frequency bands
*
* See Section "24 Output Power Programming" on page 59 in the data sheet for
* details.
*
* @author Marian Buschsieweke <marian.buschsieweke@ovgu.de>
* @}
*/
#include "cc110x.h"
#include "cc110x_internal.h"
const cc110x_patable_t cc110x_patable_433mhz = {
.data = {
/* Settings for 433 MHz */
0x12, /*< -30 dBm */
0x0E, /*< -20 dBm */
0x1D, /*< -15 dBm */
0x34, /*< -10 dBm */
0x60, /*< 0 dBm */
0x84, /*< 5 dBm */
0xC8, /*< 7 dBm */
0xC0, /*< 10 dBm */
}
};
const cc110x_patable_t cc110x_patable_868mhz = {
.data = {
/* Settings for 868 MHz */
0x03, /*< -30 dBm */
0x0F, /*< -20 dBm */
0x1E, /*< -15 dBm */
0x27, /*< -10 dBm */
0x50, /*< 0 dBm */
0x81, /*< 5 dBm */
0xCB, /*< 7 dBm */
0xC2, /*< 10 dBm */
}
};
const cc110x_patable_t cc110x_patable_915mhz = {
.data = {
/* Settings for 915 MHz */
0x03, /*< -30 dBm */
0x0E, /*< -20 dBm */
0x1E, /*< -15 dBm */
0x27, /*< -10 dBm */
0x8E, /*< 0 dBm */
0xCD, /*< 5 dBm */
0xC7, /*< 7 dBm */
0xC0, /*< 10 dBm */
}
};

View File

@ -0,0 +1,326 @@
/*
* 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 Functions to manage sending/receiving frames with the CC110x
*
* @author Marian Buschsieweke <marian.buschsieweke@ovgu.de>
* @}
*/
#include "xtimer.h"
#include "cc110x.h"
#include "cc110x_internal.h"
#define ENABLE_DEBUG (0)
#include "debug.h"
/* Use NETDEV_EVENT_ISR to indicate that no event needs to be passed to upper
* layer at end of ISR, as ISR will never need this event
*/
#define NETDEV_NO_EVENT NETDEV_EVENT_ISR
void cc110x_enter_rx_mode(cc110x_t *dev)
{
DEBUG("[cc110x] Going to RX\n");
/* bring device to IDLE state and flush FIFOs (just in case) */
gpio_irq_disable(dev->params.gdo0);
gpio_irq_disable(dev->params.gdo2);
cc110x_cmd(dev, CC110X_STROBE_IDLE);
cc110x_cmd(dev, CC110X_STROBE_FLUSH_RX);
cc110x_cmd(dev, CC110X_STROBE_FLUSH_TX);
dev->buf.pos = dev->buf.len = 0;
/* Apply GDO2 config and go to RX */
cc110x_write(dev, CC110X_REG_IOCFG2, CC110X_GDO_ON_RX_DATA);
cc110x_write(dev, CC110X_REG_IOCFG0, CC110X_GDO_ON_TRANSMISSION);
cc110x_cmd(dev, CC110X_STROBE_RX);
dev->state = CC110X_STATE_RX_MODE;
gpio_irq_enable(dev->params.gdo2);
gpio_irq_enable(dev->params.gdo0);
}
/**
* @brief Function to run when frame is fully received
*
* @param dev Device descriptor of the transceiver
*
* Intended to be called from @ref cc110x_rx_continue
*/
static netdev_event_t cc110x_rx_done(cc110x_t *dev)
{
uint8_t lqi_crc;
int8_t rssi;
cc110x_read(dev, CC110X_REG_LQI, &lqi_crc);
cc110x_read(dev, CC110X_REG_RSSI, (uint8_t *)&rssi);
/* CRC_OK bit is most significant bit, see page 92 in the data sheet */
if (!(lqi_crc & 0x80)) {
DEBUG("[cc110x] ISR: CRC error, dropping frame\n");
/* Drop frame and go back to RX */
cc110x_enter_rx_mode(dev);
return NETDEV_EVENT_CRC_ERROR;
}
/* Copy all but the CRC_OK bit */
dev->rx_info.lqi = (uint8_t)lqi_crc & 0x7f;
/* Use the formula in section 17.3 on page 44 in the data sheet to obtain
* the correct RSSI value in dBm.
*/
dev->rx_info.rssi = (int16_t)(rssi / 2) - (int16_t)dev->rssi_offset;
/* Transceiver has automatically gone to IDLE. We keep it in IDLE until
* upper layer fetched the frame
*/
dev->state = CC110X_STATE_FRAME_READY;
return NETDEV_EVENT_RX_COMPLETE;
}
/**
* @brief Read a chunk of data from the RX-FIFO
*
* @param dev Device descriptor of the transceiver
*
* This function should be called from the ISR when data in the RX-FIFO is
* available or the last byte of the frame was received
*/
static netdev_event_t cc110x_rx_continue(cc110x_t *dev)
{
uint8_t in_fifo;
netdev_event_t retval = NETDEV_NO_EVENT;
while (gpio_read(dev->params.gdo2)) {
cc110x_read_reliable(dev, CC110X_REG_RXBYTES, &in_fifo);
if (in_fifo & 0x80) {
/* RXFIFO_OVERFLOW bit is set (see RXBYTES on page 94) */
DEBUG("[cc110x] ISR: RX-FIFO overflown, ISR too slow\n");
/* Drop frame and go to RX */
cc110x_enter_rx_mode(dev);
return NETDEV_EVENT_RX_TIMEOUT;
}
if (!in_fifo) {
/* GDO2 will be high when data is present *or* at end of packet */
break;
}
/* Handle first read from RX FIFO differently from subsequent reads, as
* in first reads the Length Field is read as well
*/
if (!dev->buf.len) {
if (in_fifo < sizeof(cc1xxx_l2hdr_t) + 1) {
/* At least a frame header + Length Field (1B) is expected */
DEBUG("[cc110x] ISR: Incoming frame smaller than header "
"--> drop\n");
cc110x_enter_rx_mode(dev);
/* Not exactly CRC, but incorrect CRC indicates a broken frame*/
return NETDEV_EVENT_CRC_ERROR;
}
cc110x_burst_read(dev, CC110X_MULTIREG_FIFO, &dev->buf,
in_fifo - 1);
/* Update read position in payload, that is number of bytes read
* minus the Length Filed and minus the byte left in the FIFO to not
* trigger a silicon bug
*/
dev->buf.pos = in_fifo - 2;
retval = NETDEV_EVENT_RX_STARTED;
}
else {
/* Prevent overflow of buffer */
if (dev->buf.pos + in_fifo > CC110X_MAX_FRAME_SIZE) {
DEBUG("[cc110x] ISR: Incoming frame exceeds maximum size\n");
cc110x_enter_rx_mode(dev);
/* Not exactly CRC, but incorrect CRC indicates a broken frame */
return NETDEV_EVENT_CRC_ERROR;
}
if (dev->buf.pos + in_fifo < dev->buf.len) {
/* Frame not fully received yet, keeping one byte in RX FIFO
* to prevent triggering a silicon bug
*/
in_fifo--;
}
/* Continue reading data */
cc110x_burst_read(dev, CC110X_MULTIREG_FIFO,
dev->buf.data + dev->buf.pos, in_fifo);
dev->buf.pos += in_fifo;
}
}
if (dev->buf.pos > dev->buf.len) {
DEBUG("[cc110x] ISR: Incoming frame larger than Length Field "
"--> drop\n");
cc110x_enter_rx_mode(dev);
/* Not exactly CRC, but incorrect CRC indicates a broken frame */
return NETDEV_EVENT_CRC_ERROR;
}
if (!gpio_read(dev->params.gdo0)) {
/* GDO0 is low when transmission is over ==> RX complete or corrupt
frame */
if (dev->buf.pos == dev->buf.len) {
return cc110x_rx_done(dev);
}
else {
DEBUG("[cc110x] ISR: Incoming frame smaller than Length Field "
"--> drop\n");
cc110x_enter_rx_mode(dev);
/* Not exactly CRC, but incorrect CRC indicates a broken frame */
return NETDEV_EVENT_CRC_ERROR;
}
}
return retval;
}
/**
* @brief Function to run when frame is fully send
*
* @param dev Device descriptor of the transceiver
*/
static netdev_event_t cc110x_tx_done(cc110x_t *dev)
{
uint8_t status = cc110x_status(dev);
cc110x_state_t state = cc110x_state_from_status(status);
switch (state){
case CC110X_STATE_SETTLING:
case CC110X_STATE_CALIBRATE:
case CC110X_STATE_TX_MODE:
/* TX still in progress, or hasn't even started yet */
return NETDEV_NO_EVENT;
case CC110X_STATE_IDLE:
cc110x_enter_rx_mode(dev);
return NETDEV_EVENT_TX_COMPLETE;
case CC110X_STATE_TXFIFO_UNDERFLOW:
DEBUG("[cc110x] ISR: TX FIFO underflown.\n");
break;
default:
DEBUG("[cc110x] ISR: Unknown state during TX.\n");
break;
}
cc110x_enter_rx_mode(dev);
/* TX timeout is the only TX-related error event known to RIOT */
return NETDEV_EVENT_TX_TIMEOUT;
}
/**
* @brief Refill the TX-FIFO
*
* @param dev Device descriptor of the transceiver
*/
static netdev_event_t cc110x_tx_continue(cc110x_t *dev)
{
uint8_t in_fifo;
cc110x_read_reliable(dev, CC110X_REG_TXBYTES, &in_fifo);
/* most significant bit indicates TXFIFO underflow, see page 94 in the
* data sheet
*/
if (in_fifo & 0x80) {
DEBUG("[cc110x] ISR: ERROR: TX-FIFO underflown, ISR too slow\n");
/* Abort: Flush TX and go back to RX */
cc110x_cmd(dev, CC110X_STROBE_IDLE);
cc110x_cmd(dev, CC110X_STROBE_FLUSH_TX);
cc110x_enter_rx_mode(dev);
return NETDEV_EVENT_TX_TIMEOUT;
}
uint8_t to_write = CC110X_FIFO_SIZE - in_fifo;
if (to_write == 0) {
/* ISR came to early, nothing to do yet */
return NETDEV_NO_EVENT;
}
uint8_t left = dev->buf.len - dev->buf.pos;
to_write = (left < to_write) ? left : to_write;
cc110x_burst_write(dev, CC110X_MULTIREG_FIFO,
dev->buf.data + dev->buf.pos, to_write);
dev->buf.pos += to_write;
if (dev->buf.pos == dev->buf.len) {
/* All data send to the transceiver, now waiting for transceiver to
* complete transmission
*/
dev->state = CC110X_STATE_TX_COMPLETING;
/* Disable GDO2, as we do not need to further feed TX FIFO */
cc110x_write(dev, CC110X_REG_IOCFG2, CC110X_GDO_CONSTANT_LOW);
}
return NETDEV_NO_EVENT;
}
void cc110x_isr(netdev_t *netdev)
{
cc110x_t *dev = (cc110x_t *)netdev;
/* We don't want to create events while device descriptor is acquired, to
* prevent a dead lock. (Currently e.g. on NETDEV_EVENT_RX_COMPLETE the
* upper layer will immediately call netdev_driver_t::recv(), which in
* turn wants to operate on the device descriptor. We could rely on this
* behaviour by skipping cc110x_acquire() there, but the driver would break
* when upper layer behaviour is changed. By moving the event notification
* at the end of the ISR (end after cc110x_release()), the driver becomes
* agnostic to which behaviour the upper layer implements.)
*/
netdev_event_t post_isr_event = NETDEV_NO_EVENT;
if (cc110x_acquire(dev) != SPI_OK) {
DEBUG("[cc110x] ISR: CRITICAL ERROR: Couldn't acquire device\n");
return;
}
/* Disable IRQs in a coarse manner, instead of doing so any time the
* IOCFGx configuration registers are changed. (This should be less
* bug prone.)
*/
gpio_irq_disable(dev->params.gdo0);
gpio_irq_disable(dev->params.gdo2);
switch (dev->state) {
case CC110X_STATE_RX_MODE:
if (gpio_read(dev->params.gdo0) || gpio_read(dev->params.gdo2)) {
dev->state = CC110X_STATE_RECEIVING;
dev->buf.pos = dev->buf.len = 0;
}
break;
case CC110X_STATE_RECEIVING:
post_isr_event = cc110x_rx_continue(dev);
break;
case CC110X_STATE_TX_MODE:
post_isr_event = cc110x_tx_continue(dev);
break;
case CC110X_STATE_TX_COMPLETING:
post_isr_event = cc110x_tx_done(dev);
break;
default:
DEBUG("[cc110x] ISR: CRITICAL ERROR: No interrupt expected "
"for current state\n");
/* Go back to RX and pray that solved the problem */
cc110x_enter_rx_mode(dev);
}
/* Re-enable IRQs again, unless device state */
gpio_irq_enable(dev->params.gdo0);
gpio_irq_enable(dev->params.gdo2);
cc110x_release(dev);
/* Pass event to uper layer, if needed */
if (post_isr_event != NETDEV_NO_EVENT) {
dev->netdev.event_callback(&dev->netdev, post_isr_event);
}
}

View File

@ -0,0 +1,330 @@
/*
* 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 On-chip settings for the TI CC1100/CC1101 transceiver
*
* @author Marian Buschsieweke <marian.buschsieweke@ovgu.de>
* @}
*/
#include "cc110x.h"
#include "cc110x_internal.h"
const char cc110x_conf[CC110X_CONF_SIZE] = {
/*
* IOCFG2; default: 0x29 (CHIP_RDYn)
* Invert GDO2: off,
* GDO2: Go high when RX data should be read
*
* Why not default?
* GDO2 will be used to be notified about FIFO events (e.g. refilling TX
* FIFO is needed during transmission, reading from RX FIFO is needed
* during reception)
*/
CC110X_GDO_ON_RX_DATA,
/*
* IOCFG1; default: 0x2E (3-state)
* Invert GDO1: off,
* GDO1: 3-state (required when SPI interface is shared with other devices)
*/
0x2E,
/*
* IOCFG0; default: 0x3F (CLK_XOSC/192)
* Invert GDO0: off,
* GDO0: Go high on PLL in lock
*
* Why not default?
* GDO0 will be used to be notified when a packet is coming while in RX
* mode (will go high during transmission) and when sending is completed
* while in TX (remains high during transmission and will go back low when
* done).
*/
CC110X_GDO_ON_TRANSMISSION,
/*
* FIFOTHR; default: 0x07
* TEST1 = 0x31 and TEST2 = 0x88 when waking up from SLEEP,
* 0dB RX attenuation,
* threshold for FIFOs: TX FIFO = 33, RX FIFO = 32
*/
0x07,
/*
* SYNC1, SYNC0; defaults: 0xD3, 0x91
* Use 0xD3,0x91 as sync word
*/
0xD3, /*< SYNC1 */
0x91, /*< SYNC0 */
/*
* PKTLEN; default: 0xFF
* Packet length in bytes in fixed length mode, else maximum length
*/
0xff,
/*
* PKTCTRL1; default: 0x04
* PQT: Accept all sync words, regardless of preamble quality
* CRC_AUTOFLUSH: Do not auto-flush RX FIFO on incorrect CRC
* APPEND_STATUS: Do not add 2 bytes of status information in RX FIFO
* ADDR_CHK: Filter incoming frames in hardware by address: Only frames
* with destination address 0x00 (broadcast) or with with the
* layer-2 address of the transceiver are accepted.
*
* Why not default?
* - The RSSI, LQI and CRC info are also available via status registers.
* Thus, it is not worth to sacrifice two bytes of RX FIFO for it.
* - Hardware address filtering could reduce the number IRQs generated
* (e.g. a huge frame is dropped before it fully received) which reduces
* the system's load. Thus, it is enabled.
*/
0x02,
/*
* PKTCTRL0; default: 0x45
* Data whitening enabled, use RX/TX FIFOs, CRC enabled,
* variable packet length
*/
0x45,
/*
* ADDR; default: 0x00
* Address will overwritten later
*
* Why not default?
* 0x00 is used as broadcast address. Using it would increase chance to
* receive message during device initialization and thus power consumption.
*/
0xFF,
/*
* CHANNR; default: 0x00
* Channel number 0 by default
*/
0x00,
/*
* FSCTRL1; default: 0x0C
* Intermediate frequency: 0x0C * 26MHz / 1024 = 304.7kHz
*
* Why not defaults?
* See MDMCFG4, MDMCFG3
*/
0x0C,
/*
* FSCTRL0; default: 0x00
* Frequency offset to base frequency: 0kHz
*/
0x00,
/*
* FREQ2, FREQ1, FREQ0; defaults: 0x1E, 0xC4, 0xEC
* 0x2146E4 * 26MHz / 65536 = 865.1998 MHz (LoRa Channel 10)
*
* Why not defaults?
* Default is 800.000 MHz, which is not in a license free frequency band.
* Using LoRa channel 10 instead.
*/
0x21, /*< FREQ2 */
0x46, /*< FREQ1 */
0xE4, /*< FREQ0 */
/*
* MDMCFG4, MDMCFG3; defaults: 0x8C, 0x22
* 541.67 kHz channel filter bandwidth,
* 249.94 kBaud symbol rate
*
* Why not defaults?
* Using 250 kBaud (==> 250kBit/s when 2-FSK/GFSK/ASK) to compete with
* 802.15.4 in data rate.
* Using settings listed in Table 7 (pages 12ff in the data sheet):
* - 250 kBaud
* - GFSK modulation
* - 304 kHz IF frequency
* - 540 kHz channel filter bandwidth
* - 127 kHz deviation
*/
0x2D, /*< MDMCFG4 */
0x3B, /*< MDMCFG3 */
/*
* MDMCFG2; default: 0x02
* DC blocking filter on,
* GFSK modulation,
* no manchester code,
* Enable RX when 30 bits of the 32 bits sync word are received correctly
*
* Why not default?
* Default expects all 16 bits of a two byte sync word to be correctly
* received. The data sheet recommends to expect 30 bits of a four byte
* sync word to be correctly received instead (see section 15 on page 37),
* so we go for this.
*
* Using GFSK instead of 2-FSK reduces the modulated spectrum width and is
* suggested in Table 7 of the datasheet, see MDMCFG4, MDMCFG3
*/
0x13,
/*
* MDMCFG1, MDMCFG0; defaults: 0x22, 0xF8
* FEC disabled,
* 4 preamble bytes,
* 49.99 kHz distance between channel centre frequencies (closest to 50kHz)
*
* Why not defaults?
* This driver uses an translation layer between physical channels (with
* 50 kHz distance) and "virtual" channel numbers as seen outside of the
* driver. This allows to set the frequency in a range of 12.75 MHz with
* a resolution of 50kHz - this seems to allow to configure all desired
* channel layouts.
*/
0x20, /*< MDMCFG1 */
0xF8, /*< MDMCFG0 */
/*
* DEVIATN; default: 0x47
* Deviation of frequency to encode data: +- 126.953kHz in 2-FSK/4-FSK/GFSK
*
* Why not default?
* Higher deviation required for reliable operation at 250 kbps data rate.
*/
0x62,
/*
* MCSM2; default: 0x07
* No direct RX termination on RSSI measurement (only for ASK/OOK),
* on RX timeout check for sync word and ignore PQI,
* no RX timeout
*/
0x07,
/*
* MCSM1; default: 0x30
* CCA: Enter TX even when channel is detected busy
* go to idle after packet received,
* go to idle after packet sent
*
* Why not default?
* By default the transceiver refuses to enter TX when the channel is
* detected busy. While this is desired, checking if TX was successfully
* entered is too slow and generated interrupts on the GDO2 pin are easily
* missed. However, reading the carrier sense value in the PKTSTATUS
* registers allows to implement this feature in software in a faster
* way. In addition to not missing GDO2 interrupts, this allows the send
* function to give feedback right away when the channel is busy.
*/
0x00,
/*
* MCSM0; default: 0x04
* Auto calibration: Disabled, driver has to manage
* delay to allow supply voltage to stabilize: 149-155 microseconds
* pin radio control option is off,
* oscillator is off in sleep state
*
* Why not default?
* Using 149µs-155µs instead of the default 37µs-39µs as PO_TIMEOUT is
* encouraged by the data sheet for robust operation
*/
0x08,
/*
* FOCCFG; default: 0x36
* Freeze frequency offset compensation etc until CS high: yes
* frequency compensation loop gain before sync word: 3K
* frequency compensation loop gain after sync word: K/2
* saturation point for frequency offset compensation: 25% channel bandwidth
* (incompatible with ASK/OOK)
*/
0x36,
/*
* BSCFG; default: 0x6C
* Clock recovery feedback loop integral gain before sync word: 2K_i,
* clock recovery feedback loop proportional gain before sync word: 3K_p,
* clock recovery feedback loop integral gain after sync word: K_i/2
* clock recovery feedback loop proportional gain after sync word: K_p,
* data rate offset compensation: Disabled
*/
0x6C,
/*
* AGCCTRL2; default: 0x03
* Maximum allowable DVGA gain: No limitation, maximum DVGA gain can be used
* Maximum allowable LNA + LNA2 gain: No limitation
* target amplitude from channel filter: 33 dB (default)
*/
0x03,
/*
* AGCCTRL1; default: 0x40
* LNA priority: Decrease LNA gain first, start decreasing LNA2 gain when
* LNA gain reached minimum
* Relative carrier sense threshold: Disabled
* Absolute carrier sense threshold: At MAGN_TARGET
*/
0x40,
/*
* AGCCTRL0; default: 0x91
* HYST_LEVEL: Medium hysteresis, medium asymmetric dead zone, medium gain
* Adjust gain after how many channel filter samples: After 16 samples
* Freeze AGC gain: Never; perform gain adjustments as required
* FILTER_LENGTH:
* - 16 channel filter samples when 2-FSK, 4-FSK, or MSK is used
* - 8 dB decision baundry when OOK/ASK is used
*/
0x91,
/*
* WOREVT1, WOREVT0, WORCTRL; defaults: 0x87, 0x6B, 0xF8
* Event0 Timeout: 1.000 seconds
* RC_PD: 1 (The datasheet is quite cryptic regarding this setting)
* Event1 Timeout: 1.333ms - 1.385ms (depending on crystal frequency)
* RC oscillator calibration: Enabled
* WOR_RES: 0 (Relevant for Event0 resolution and maximum timeout)
*/
0x87, /*< WOREVT1 */
0x6B, /*< WOREVT0 */
0xF8, /*< WORCTRL */
/*
* FREND1; default: 0x56
* LNA_CURRENT: 0b01
* LNA2MIX_CURRENT: 0b01
* LODIV_BUF_CURRENT_RW: 0b01
* MIX_CURRENT: 0b10
*/
0x56,
/*
* FREND0; default: 0x10
* LODIV_BUF_CURRENT_TX: 0b01
* Index in PA_POWER table (in 0..7, default is 0): 4 (0dBm)
*
* Why not default:
* Use a reasonable TX power level instead of the lowest.
*/
0x14,
/*
* FSCAL3, FSCAL2, FSCAL1, FSCAL0; defaults: 0xA9, 0x0A, 0x20, 0x0d
* These values store calibration date of the CC1100/CC1101 transceiver.
* Once the transceiver performs a calibration, those registers are updated
* with the new calibration data. In a "stable" environment (e.g. constant
* channel/frequency, stable humidity, temperature, supply voltage etc.)
* the obtained values could be written to the transceiver and calibration
* could be turned off completely.
*
* Fast channel hopping could be performed by obtaining the FSCAL1
* calibration data for each channel and storing it in the MCU's RAM.
* The other calibration values is frequency independent according to the
* data sheet, but depends on temperature etc.
*
* Once the FSCAL1 values for each channel are stored, the calibration can
* be disabled and the stored FSCAL1 data can be uploaded for each channel
* hop. A re-calibration from time to time is suggested to cope with changes
* in the environment, e.g. in temperature or supply voltage.
*
* Why not defaults?
* Using "magic" values obtained with SmartRF Studio software for 868 MHz
* band.
*/
0xEA, /*< FSCAL3: charge pump current calibration, frequency independent */
0x2A, /*< FSCAL2: VCO current calibration, frequency independent */
0x00, /*< FSCAL1: VCO capacitance calibration, frequency dependent */
0x1F, /*< FSCAL0: "Magic number", use SmartRF Studio to obtain */
/*
* RCCTRL1, RCCTRL0; defaults: 0x41, 0x00
* RC oscillator configuration, no explanation given in data sheet.
*/
0x41, /*< RCCTRL1 */
0x00, /*< RCCTRL0 */
};
const char cc110x_magic_registers[3] = { 0x88, 0x31, 0x09 };

View File

@ -1 +0,0 @@
include $(RIOTBASE)/Makefile.base

View File

@ -1,200 +0,0 @@
/*
* Copyright (C) 2015 Kaspar Schleiser <kaspar@schleiser.de>
*
* 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.
*/
#include <assert.h>
#include <sys/uio.h>
#include "net/netdev.h"
#include "net/gnrc.h"
#include "cc110x.h"
#include "cc110x-netdev.h"
#include "net/gnrc/netif.h"
#include "od.h"
#define ENABLE_DEBUG (0)
#include "debug.h"
static int _send(gnrc_netif_t *netif, gnrc_pktsnip_t *pkt)
{
cc110x_pkt_t cc110x_pkt;
netdev_t *dev = netif->dev;
netdev_cc110x_t *netdev_cc110x = (netdev_cc110x_t *) dev;
cc110x_t *cc110x = &netdev_cc110x->cc110x;
assert(pkt != NULL);
assert(dev->driver == &netdev_cc110x_driver);
gnrc_netif_hdr_t *netif_hdr;
gnrc_pktsnip_t *payload;
payload = pkt->next;
if (pkt->type != GNRC_NETTYPE_NETIF) {
DEBUG("gnrc_cc110x: First header was not generic netif header\n");
gnrc_pktbuf_release(pkt);
return -EBADMSG;
}
netif_hdr = (gnrc_netif_hdr_t *) pkt->data;
/* set up header */
if (netif_hdr->src_l2addr_len == 1) {
uint8_t *_src_addr = gnrc_netif_hdr_get_src_addr(netif_hdr);
cc110x_pkt.phy_src = *_src_addr;
}
else {
cc110x_pkt.phy_src = cc110x->radio_address;
}
if (netif_hdr->flags & (GNRC_NETIF_HDR_FLAGS_BROADCAST |
GNRC_NETIF_HDR_FLAGS_MULTICAST)) {
cc110x_pkt.address = 0;
}
else {
uint8_t *_dst_addr = gnrc_netif_hdr_get_dst_addr(netif_hdr);
cc110x_pkt.address = _dst_addr[netif_hdr->dst_l2addr_len-1];
}
switch (payload->type) {
#ifdef MODULE_GNRC_SIXLOWPAN
case GNRC_NETTYPE_SIXLOWPAN:
cc110x_pkt.flags = 1;
break;
#endif
default:
cc110x_pkt.flags = 0;
}
iolist_t iolist = {
.iol_base = (char *)&cc110x_pkt,
.iol_len = sizeof(cc110x_pkt_t)
};
unsigned payload_len = 0;
uint8_t *pos = cc110x_pkt.data;
while (payload) {
payload_len += payload->size;
if (payload_len > CC110X_MAX_DATA_LENGTH) {
DEBUG("gnrc_cc110x: payload length exceeds maximum"
"(%u>%u)\n", payload_len, CC110X_MAX_DATA_LENGTH);
gnrc_pktbuf_release(pkt);
return -EBADMSG;
}
memcpy(pos, payload->data, payload->size);
pos += payload->size;
payload = payload->next;
}
/* pkt has been copied into cc110x_pkt, we're done with it. */
gnrc_pktbuf_release(pkt);
cc110x_pkt.length = (uint8_t) payload_len + CC110X_HEADER_LENGTH;
DEBUG("gnrc_cc110x: sending packet from %02x to %02x with payload "
"length %u\n",
(unsigned)cc110x_pkt.phy_src,
(unsigned)cc110x_pkt.address,
(unsigned)payload_len);
#if defined(MODULE_OD) && ENABLE_DEBUG
od_hex_dump(cc110x_pkt.data, payload_len, OD_WIDTH_DEFAULT);
#endif
return dev->driver->send(dev, &iolist);
}
static gnrc_pktsnip_t *_recv(gnrc_netif_t *netif)
{
netdev_t *dev = netif->dev;
cc110x_t *cc110x = &((netdev_cc110x_t*) dev)->cc110x;
cc110x_pkt_t *cc110x_pkt = &cc110x->pkt_buf.packet;
int payload_length = cc110x_pkt->length - CC110X_HEADER_LENGTH;
int nettype;
int addr_len;
switch (cc110x_pkt->flags) {
#ifdef MODULE_GNRC_SIXLOWPAN
case 1:
addr_len = 8;
nettype = GNRC_NETTYPE_SIXLOWPAN;
break;
#endif
default:
addr_len = 1;
nettype = GNRC_NETTYPE_UNDEF;
}
/* copy packet payload into pktbuf */
gnrc_pktsnip_t *pkt = gnrc_pktbuf_add(NULL, cc110x_pkt->data,
payload_length, nettype);
if(!pkt) {
DEBUG("gnrc_cc110x: _recv: cannot allocate pktsnip.\n");
return NULL;
}
gnrc_pktsnip_t *netif_hdr;
netif_hdr = gnrc_pktbuf_add(NULL, NULL,
sizeof(gnrc_netif_hdr_t) + 2*addr_len,
GNRC_NETTYPE_NETIF);
if (netif_hdr == NULL) {
DEBUG("gnrc_cc110x: no space left in packet buffer\n");
gnrc_pktbuf_release(pkt);
return NULL;
}
gnrc_netif_hdr_init(netif_hdr->data, addr_len, addr_len);
if (addr_len == 8) {
uint64_t src_addr = cc110x_pkt->phy_src;
uint64_t dst_addr = cc110x_pkt->address;
gnrc_netif_hdr_set_src_addr(netif_hdr->data, (uint8_t*)&src_addr, addr_len);
gnrc_netif_hdr_set_dst_addr(netif_hdr->data, (uint8_t*)&dst_addr, addr_len);
}
else {
gnrc_netif_hdr_set_src_addr(netif_hdr->data, (uint8_t*)&cc110x_pkt->phy_src, addr_len);
gnrc_netif_hdr_set_dst_addr(netif_hdr->data, (uint8_t*)&cc110x_pkt->address, addr_len);
}
gnrc_netif_hdr_set_netif(netif_hdr->data, netif);
((gnrc_netif_hdr_t *)netif_hdr->data)->lqi = cc110x->pkt_buf.lqi;
((gnrc_netif_hdr_t *)netif_hdr->data)->rssi = cc110x->pkt_buf.rssi;
DEBUG("gnrc_cc110x: received packet from %02x of length %u\n",
(unsigned)cc110x_pkt->phy_src,
(unsigned)cc110x_pkt->length-CC110X_HEADER_LENGTH);
#if defined(MODULE_OD) && ENABLE_DEBUG
od_hex_dump(cc110x_pkt->data, payload_length, OD_WIDTH_DEFAULT);
#endif
pkt->next = netif_hdr;
return pkt;
}
static const gnrc_netif_ops_t _cc110x_ops = {
.send = _send,
.recv = _recv,
.get = gnrc_netif_get_from_netdev,
.set = gnrc_netif_set_from_netdev,
};
gnrc_netif_t *gnrc_netif_cc110x_create(char *stack, int stacksize, char priority,
char *name, netdev_t *dev)
{
return gnrc_netif_create(stack, stacksize, priority, name, dev,
&_cc110x_ops);
}

View File

@ -1,46 +0,0 @@
/*
* Copyright (C) 2015 Kaspar Schleiser <kaspar@schleiser.de>
*
* 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 cc110x default settings override
*
* By setting either CC110X_DEFAULT_PATABLE or CC110X_DEFAULT_FREQ in board.h,
* it is possible to override the default pa table or base frequency registers
* on a per-device basis.
*
* @author Kaspar Schleiser <kaspar@schleiser.de>
*/
#ifndef CC110X_DEFAULTSETTINGS_H
#define CC110X_DEFAULTSETTINGS_H
#include "board.h"
#ifdef __cplusplus
extern "C" {
#endif
#ifndef CC110X_DEFAULT_PATABLE
#define CC110X_DEFAULT_PATABLE cc110x_default_pa_table
extern const char cc110x_default_pa_table[8];
#endif
#ifndef CC110X_DEFAULT_FREQ
#define CC110X_DEFAULT_FREQ cc110x_default_base_freq
extern const char cc110x_default_base_freq[3];
#endif
#ifdef __cplusplus
}
#endif
#endif /* CC110X_DEFAULTSETTINGS_H */
/** @} */

View File

@ -1,252 +0,0 @@
/*
* Copyright (C) 2008 Freie Universität Berlin
* Copyright (C) 2013 INRIA
*
* 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 Driver internal constants for CC110x chip configuration
*
* @author Thomas Hillebrandt <hillebra@inf.fu-berlin.de>
* @author Heiko Will <hwill@inf.fu-berlin.de>
* @author Oliver Hahm <oliver.hahm@inria.fr>
*/
#ifndef CC110X_DEFINES_H
#define CC110X_DEFINES_H
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Variable packet length PKTCTRL0 bit configuration
*
* If variable packet length is configured in PKTCTRL0 the
* first byte after the synch word determines the packet length.
*/
#define VARIABLE_PKTLEN (0x01)
/**
* @brief RSSI calculation offset.
*
* The cc1101 has 74 as a RSSI offset. The CC1100E has a
* slightly larger offset of 75 to 79 (not implemented here).
* With those devices we thus get a slightly optimistic result.
*/
#define CC110X_RSSI_OFFSET (74)
/**
* @name Bitmasks for reading out status register values
* @{
*/
/**
* @brief Bitmask (=10000000) for reading CRC_OK.
*
* If CRC_OK == 1: CRC for received data OK (or CRC disabled).
* If CRC_OK == 0: CRC error in received data.
*/
#define CRC_OK (0x80)
/**
* @brief Bitmask (=01111111) for reading LQI_EST.
*
* The Link Quality Indicator estimates how easily a received signal can be demodulated.
*/
#define LQI_EST (0x7F)
#define I_RSSI (0x00) /**< Index 0 contains RSSI information (from optionally appended packet status bytes). */
#define I_LQI (0x01) /**< Index 1 contains LQI & CRC_OK information (from optionally appended packet status bytes). */
#define MARC_STATE (0x1F) /**< Bitmask (=00011111) for reading MARC_STATE in MARCSTATE status register. */
#define PKTSTATUS_CS (0x40) /**< Bitmask (=01000000) for reading CS (Carrier Sense) in PKTSTATUS status register. */
#define PKTSTATUS_PQT_REACHED (0x20) /**< Bitmask (=00100000) for reading PQT_REACHED (Preamble Quality reached) in PKTSTATUS status register. */
#define PKTSTATUS_CCA (0x10) /**< Bitmask (=00010000) for reading CCA (clear channel assessment) in PKTSTATUS status register. */
#define PKTSTATUS_SFD (0x08) /**< Bitmask (=00001000) for reading SFD (Sync word found) in PKTSTATUS status register. */
#define PKTSTATUS_GDO2 (0x04) /**< Bitmask (=00000100) for reading GDO2 (current value on GDO2 pin) in PKTSTATUS status register. */
#define PKTSTATUS_GDO1 (0x02) /**< Bitmask (=00000010) for reading GDO1 (current value on GDO1 pin) in PKTSTATUS status register. */
#define PKTSTATUS_GDO0 (0x01) /**< Bitmask (=00000001) for reading GDO0 (current value on GDO0 pin) in PKTSTATUS status register. */
#define TXFIFO_UNDERFLOW (0x80) /**< Bitmask (=10000000) for reading TXFIFO_UNDERFLOW in TXBYTES status register. */
#define BYTES_IN_TXFIFO (0x7F) /**< Bitmask (=01111111) for reading NUM_TXBYTES in TXBYTES status register. */
#define RXFIFO_OVERFLOW (0x80) /**< Bitmask (=10000000) for reading RXFIFO_OVERFLOW in RXBYTES status register. */
#define BYTES_IN_RXFIFO (0x7F) /**< Bitmask (=01111111) for reading NUM_RXBYTES in RXBYTES status register. */
/** @} */
/**
* @name Bitmasks for reading out configuration register values
* @{
*/
#define PKT_LENGTH_CONFIG (0x03) /**< Bitmask (=00000011) for reading LENGTH_CONFIG in PKTCTRL0 configuration register. */
/** @} */
/**
* @name Definitions to support burst/single access
* @{
*/
#define CC110X_WRITE_BURST (0x40) /**< Offset for burst write. */
#define CC110X_READ_SINGLE (0x80) /**< Offset for read single byte. */
#define CC110X_READ_BURST (0xC0) /**< Offset for read burst. */
#define CC110X_NOBYTE (0xFF) /**< No command (for reading). */
/** @} */
/**
* @name Configuration Registers (47x)
* @{
*/
#define CC110X_IOCFG2 (0x00) /**< GDO2 output pin configuration */
#define CC110X_IOCFG1 (0x01) /**< GDO1 output pin configuration */
#define CC110X_IOCFG0 (0x02) /**< GDO0 output pin configuration */
#define CC110X_FIFOTHR (0x03) /**< RX FIFO and TX FIFO thresholds */
#define CC110X_SYNC1 (0x04) /**< Sync word, high byte */
#define CC110X_SYNC0 (0x05) /**< Sync word, low byte */
#define CC110X_PKTLEN (0x06) /**< Packet length */
#define CC110X_PKTCTRL1 (0x07) /**< Packet automation control */
#define CC110X_PKTCTRL0 (0x08) /**< Packet automation control */
#define CC110X_ADDR (0x09) /**< Device address */
#define CC110X_CHANNR (0x0A) /**< Channel number */
#define CC110X_FSCTRL1 (0x0B) /**< Frequency synthesizer control */
#define CC110X_FSCTRL0 (0x0C) /**< Frequency synthesizer control */
#define CC110X_FREQ2 (0x0D) /**< Frequency control word, high byte */
#define CC110X_FREQ1 (0x0E) /**< Frequency control word, middle byte */
#define CC110X_FREQ0 (0x0F) /**< Frequency control word, low byte */
#define CC110X_MDMCFG4 (0x10) /**< Modem configuration */
#define CC110X_MDMCFG3 (0x11) /**< Modem configuration */
#define CC110X_MDMCFG2 (0x12) /**< Modem configuration */
#define CC110X_MDMCFG1 (0x13) /**< Modem configuration */
#define CC110X_MDMCFG0 (0x14) /**< Modem configuration */
#define CC110X_DEVIATN (0x15) /**< Modem deviation setting */
#define CC110X_MCSM2 (0x16) /**< Main Radio Control State Machine configuration */
#define CC110X_MCSM1 (0x17) /**< Main Radio Control State Machine configuration */
#define CC110X_MCSM0 (0x18) /**< Main Radio Control State Machine configuration */
#define CC110X_FOCCFG (0x19) /**< Frequency Offset Compensation configuration */
#define CC110X_BSCFG (0x1A) /**< Bit Synchronization configuration */
#define CC110X_AGCCTRL2 (0x1B) /**< AGC control */
#define CC110X_AGCCTRL1 (0x1C) /**< AGC control */
#define CC110X_AGCCTRL0 (0x1D) /**< AGC control */
#define CC110X_WOREVT1 (0x1E) /**< High byte Event 0 timeout */
#define CC110X_WOREVT0 (0x1F) /**< Low byte Event 0 timeout */
#define CC110X_WORCTRL (0x20) /**< Wake On Radio control */
#define CC110X_FREND1 (0x21) /**< Front end RX configuration */
#define CC110X_FREND0 (0x22) /**< Front end TX configuration */
#define CC110X_FSCAL3 (0x23) /**< Frequency synthesizer calibration */
#define CC110X_FSCAL2 (0x24) /**< Frequency synthesizer calibration */
#define CC110X_FSCAL1 (0x25) /**< Frequency synthesizer calibration */
#define CC110X_FSCAL0 (0x26) /**< Frequency synthesizer calibration */
#define CC110X_RCCTRL1 (0x27) /**< RC oscillator configuration */
#define CC110X_RCCTRL0 (0x28) /**< RC oscillator configuration */
#define CC110X_FSTEST (0x29) /**< Frequency synthesizer calibration control */
#define CC110X_PTEST (0x2A) /**< Production test */
#define CC110X_AGCTEST (0x2B) /**< AGC test */
#define CC110X_TEST2 (0x2C) /**< Various test settings */
#define CC110X_TEST1 (0x2D) /**< Various test settings */
#define CC110X_TEST0 (0x2E) /**< Various test settings */
/** @} */
/**
* @name Strobe commands (14x)
* @{
*/
#define CC110X_SRES (0x30) /**< Reset chip. */
/**
* @brief Enable and calibrate frequency synthesizer (if MCSM0.FS_AUTOCAL=1).
*
* If in RX/TX: Go to a wait state where only the synthesizer is running (for quick RX / TX turnaround).
*/
#define CC110X_SFSTXON (0x31)
#define CC110X_SXOFF (0x32) /**< Turn off crystal oscillator. */
#define CC110X_SCAL (0x33) /**< Calibrate frequency synthesizer and turn it off (enables quick start). */
#define CC110X_SRX (0x34) /**< Enable RX. Perform calibration first if coming from IDLE and MCSM0.FS_AUTOCAL=1. */
/**
* In IDLE state: Enable TX. Perform calibration first if MCSM0.FS_AUTOCAL=1.
* If in RX state and CCA is enabled: Only go to TX if channel is clear.
*/
#define CC110X_STX (0x35)
#define CC110X_SIDLE (0x36) /**< Exit RX / TX, turn off frequency synthesizer and exit WOR mode if applicable. */
#define CC110X_SAFC (0x37) /**< Perform AFC adjustment of the frequency synthesizer */
#define CC110X_SWOR (0x38) /**< Start automatic RX polling sequence (Wake-on-Radio) */
#define CC110X_SPWD (0x39) /**< Enter power down mode when CSn goes high. */
#define CC110X_SFRX (0x3A) /**< Flush the RX FIFO buffer (CC110X should be in IDLE state). */
#define CC110X_SFTX (0x3B) /**< Flush the TX FIFO buffer (CC110X should be in IDLE state). */
#define CC110X_SWORRST (0x3C) /**< Reset real time clock. */
#define CC110X_SNOP (0x3D) /**< No operation. May be used to pad strobe commands to two bytes for simpler software. */
/** @} */
/**
* @name Status registers (12x)
* @{
*/
#define CC110X_PARTNUM (0x30) /**< Part number of CC110X. */
#define CC110X_VERSION (0x31) /**< Current version number. */
#define CC110X_FREQEST (0x32) /**< Frequency Offset Estimate. */
#define CC110X_LQI (0x33) /**< Demodulator estimate for Link Quality. */
#define CC110X_RSSI (0x34) /**< Received signal strength indication. */
#define CC110X_MARCSTATE (0x35) /**< Control state machine state. */
#define CC110X_WORTIME1 (0x36) /**< High byte of WOR timer. */
#define CC110X_WORTIME0 (0x37) /**< Low byte of WOR timer. */
#define CC110X_PKTSTATUS (0x38) /**< Current GDOx status and packet status. */
#define CC110X_VCO_VC_DAC (0x39) /**< Current setting from PLL calibration module. */
#define CC110X_TXBYTES (0x3A) /**< Underflow and number of bytes in the TX FIFO. */
#define CC110X_RXBYTES (0x3B) /**< Overflow and number of bytes in the RX FIFO. */
/** @} */
/**
* @name Multi byte registers
* @{
*/
/**
* @brief Register for eight user selected output power settings.
*
* 3-bit FREND0.PA_POWER value selects the PATABLE entry to use.
*/
#define CC110X_PATABLE (0x3E)
#define CC110X_TXFIFO (0x3F) /**< TX FIFO: Write operations write to the TX FIFO (SB: +0x00; BURST: +0x40) */
#define CC110X_RXFIFO (0x3F) /**< RX FIFO: Read operations read from the RX FIFO (SB: +0x80; BURST: +0xC0) */
/** @} */
/**
* @name GDO configuration values
*
* Values that can be written to the GDO0, GDO1 and GDO2 configuration registers
* @{
*/
/** @brief GDO goes high when RX FIFO is filled at or above threshold */
#define CC110X_GDO_HIGH_ON_RX_FIFO_ABOVE_THRESHOLD (0x00)
/**
* @brief GDO goes high when RX FIFO is filled at or above threshold or when
* packet is fully received
*/
#define CC110X_GDO_HIGH_ON_RX_FIFO_FILLED_OR_PKT_END (0x01)
/** @brief GDO goes low when TX FIFO is filled less than threshold */
#define CC110X_GDO_LOW_ON_TX_FIFO_BELOW_THRESHOLD (0x02)
/** @brief GDO goes low when TX FIFO becomes empty */
#define CC110X_GDO_LOW_ON_TX_FIFO_EMPTY (0x03)
/** @brief GDO goes high when RX FIFO overflows */
#define CC110X_GDO_HIGH_ON_RX_FIFO_OVERFLOW (0x04)
/** @brief GDO goes high when TX FIFO underflows */
#define CC110X_GDO_HIGH_ON_TX_FIFO_UNDERFLOW (0x05)
/**
* @brief GDO goes high when sync word was just received until the packet is
* fully received, or when sync word has been send until packet is fully
* send
*/
#define CC110X_GDO_HIGH_ON_SYNC_WORD (0x06)
/**
* @brief GDO goes high when a packet is received and CRC is correct.
* Goes back to low when first byte of RX fifo has been read
*/
#define CC110X_GDO_HIGH_ON_PACKET_RECEIVED (0x07)
/** @} */
#ifdef __cplusplus
}
#endif
#endif /* CC110X_DEFINES_H */
/** @} */

View File

@ -1,66 +0,0 @@
/*
* Copyright (C) 2015 Kaspar Schleiser <kaspar@schleiser.de>
* Copyright (C) 2014 Freie Universität Berlin
* Copyright (C) 2013 INRIA
*
* 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 internal declarations for cc110x driver
*
* @author Oliver Hahm <oliver.hahm@inria.fr>
* @author Kaspar Schleiser <kaspar@schleiser.de>
*/
#ifndef CC110X_INTERFACE_H
#define CC110X_INTERFACE_H
#include <stdint.h>
#include "cc110x.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @name cc110x raw low-level interface
* @internal
* @{
*/
char *cc110x_get_marc_state(cc110x_t *dev);
const char *cc110x_state_to_text(uint8_t state);
int cc110x_rd_set_mode(cc110x_t *dev, int mode);
uint8_t cc110x_get_buffer_pos(cc110x_t *dev);
void cc110x_isr_handler(cc110x_t *dev, void(*callback)(void*), void*arg);
void cc110x_set_base_freq_raw(cc110x_t *dev, const char* freq_array);
void cc110x_setup_rx_mode(cc110x_t *dev);
void cc110x_switch_to_pwd(cc110x_t *dev);
void cc110x_switch_to_rx(cc110x_t *dev);
void cc110x_wakeup_from_rx(cc110x_t *dev);
void cc110x_write_register(cc110x_t *dev, uint8_t r, uint8_t value);
extern const char cc110x_default_conf[];
extern const uint8_t cc110x_default_conf_size;
extern const uint8_t cc110x_pa_table[];
#ifdef MODULE_CC110X_HOOKS
void cc110x_hooks_init(void);
void cc110x_hook_rx(void);
void cc110x_hook_tx(void);
void cc110x_hook_off(void);
#endif
/* @} */
#ifdef __cplusplus
}
#endif
#endif /* CC110X_INTERFACE_H */
/** @} */

View File

@ -1,212 +0,0 @@
/*
* Copyright (C) 2015 Kaspar Schleiser <kaspar@schleiser.de>
* Copyright (C) 2014 Freie Universität Berlin
* Copyright (C) 2013 INRIA
*
* 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 Data structures and variables for the cc110x driver interface
*
* @author Oliver Hahm <oliver.hahm@inria.fr>
* @author Kaspar Schleiser <kaspar@schleiser.de>
*/
#ifndef CC110X_INTERNAL_H
#define CC110X_INTERNAL_H
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
#define CC110X_RXBUF_SIZE (2)
#define CC110X_FIFO_LENGTH (64)
#define CC110X_MAX_DATA_LENGTH (58+64)
#define CC110X_HEADER_LENGTH (3) /**< Header covers SRC, DST and
FLAGS */
#define CC110X_BROADCAST_ADDRESS (0x00) /**< CC110X broadcast address */
#define MIN_UID (0x01) /**< Minimum UID of a node is
1 */
#define MAX_UID (0xFF) /**< Maximum UID of a node is
255 */
#define MIN_CHANNR (0) /**< Minimum channel number */
#define MAX_CHANNR (24) /**< Maximum channel number */
#define CC110X_PACKET_LENGTH (0xFF) /**< max packet length = 255b */
#define CC110X_L2_HDR_SIZE (3) /**< Layer 2 header size */
#define CC110X_SYNC_WORD_TX_TIME (90000) /**< loop count (max. timeout ~15ms)
to wait for sync word to be
transmitted (GDO2 from low to
high) */
#define RESET_WAIT_TIME (610) /**< Reset wait time (in reset
procedure) */
#define IDLE_TO_RX_TIME (122) /**< Time chip needs to go to RX */
#define CS_SO_WAIT_TIME (488) /**< Time to wait for SO to go low
after CS */
#define CC110X_GDO1_LOW_RETRY (100) /**< Max. retries for SO to go low
after CS */
#ifndef CC110X_DEFAULT_CHANNEL
#define CC110X_DEFAULT_CHANNEL (0) /**< The default channel number */
#endif
#define CC110X_MIN_CHANNR (0) /**< lowest possible channel number */
#define CC110X_MAX_CHANNR (0) /**< highest possible channel number */
/**
* @name State values for state machine
* @{
*/
enum {
RADIO_UNKNOWN,
RADIO_IDLE,
RADIO_TX_BUSY,
RADIO_RX,
RADIO_RX_BUSY,
RADIO_PWD,
};
/** @} */
/**
* @brief array holding cc110x register values
*/
extern char cc110x_conf[];
/**
* @brief CC110X layer 0 protocol
*
* <pre>
---------------------------------------------------
| | | | | |
| Length | Address | PhySrc | Flags | Data |
| | | | | |
---------------------------------------------------
1 byte 1 byte 1 byte 1 byte <= 251 bytes
Flags:
Bit | Meaning
--------------------
7:4 | -
3:1 | Protocol
0 | Identification
</pre>
Notes:
\li length & address are given by CC110X
\li Identification is increased is used to scan duplicates. It must be increased
for each new packet and kept for packet retransmissions.
*/
typedef struct __attribute__((packed))
{
uint8_t length; /**< Length of the packet (without length byte) */
uint8_t address; /**< Destination address */
uint8_t phy_src; /**< Source address (physical source) */
uint8_t flags; /**< Flags */
uint8_t data[CC110X_MAX_DATA_LENGTH]; /**< Data (high layer protocol) */
} cc110x_pkt_t;
/**
* @brief struct holding cc110x packet + metadata
*/
typedef struct {
uint8_t rssi; /**< RSSI value */
uint8_t lqi; /**< link quality indicator */
uint8_t pos; /**< I have no clue. */
cc110x_pkt_t packet; /**< whole packet */
} cc110x_pkt_buf_t;
/**
* @brief enum for holding cc110x radio on/off state */
enum cc110x_radio_mode {
RADIO_MODE_GET = -1, /**< leave mode unchanged */
RADIO_MODE_OFF = 0, /**< turn radio off */
RADIO_MODE_ON = 1 /**< turn radio on */
};
/**
* @brief CC110x register configuration
*/
typedef struct {
uint8_t _IOCFG2; /**< GDO2 output pin configuration */
uint8_t _IOCFG1; /**< GDO1 output pin configuration */
uint8_t _IOCFG0; /**< GDO0 output pin configuration */
uint8_t _FIFOTHR; /**< RX FIFO and TX FIFO thresholds */
uint8_t _SYNC1; /**< Sync word, high byte */
uint8_t _SYNC0; /**< Sync word, low byte */
uint8_t _PKTLEN; /**< Packet length */
uint8_t _PKTCTRL1; /**< Packet automation control */
uint8_t _PKTCTRL0; /**< Packet automation control */
uint8_t _ADDR; /**< Device address */
uint8_t _CHANNR; /**< Channel number */
uint8_t _FSCTRL1; /**< Frequency synthesizer control */
uint8_t _FSCTRL0; /**< Frequency synthesizer control */
uint8_t _FREQ2; /**< Frequency control word, high byte */
uint8_t _FREQ1; /**< Frequency control word, middle byte */
uint8_t _FREQ0; /**< Frequency control word, low byte */
uint8_t _MDMCFG4; /**< Modem configuration */
uint8_t _MDMCFG3; /**< Modem configuration */
uint8_t _MDMCFG2; /**< Modem configuration */
uint8_t _MDMCFG1; /**< Modem configuration */
uint8_t _MDMCFG0; /**< Modem configuration */
uint8_t _DEVIATN; /**< Modem deviation setting */
uint8_t _MCSM2; /**< Main Radio Control State Machine configuration */
uint8_t _MCSM1; /**< Main Radio Control State Machine configuration */
uint8_t _MCSM0; /**< Main Radio Control State Machine configuration */
uint8_t _FOCCFG; /**< Frequency Offset Compensation configuration */
uint8_t _BSCFG; /**< Bit Synchronization configuration */
uint8_t _AGCCTRL2; /**< AGC control */
uint8_t _AGCCTRL1; /**< AGC control */
uint8_t _AGCCTRL0; /**< AGC control */
uint8_t _WOREVT1; /**< High byte Event 0 timeout */
uint8_t _WOREVT0; /**< Low byte Event 0 timeout */
uint8_t _WORCTRL; /**< Wake On Radio control */
uint8_t _FREND1; /**< Front end RX configuration */
uint8_t _FREND0; /**< Front end TX configuration */
uint8_t _FSCAL3; /**< Frequency synthesizer calibration */
uint8_t _FSCAL2; /**< Frequency synthesizer calibration */
uint8_t _FSCAL1; /**< Frequency synthesizer calibration */
uint8_t _FSCAL0; /**< Frequency synthesizer calibration */
} cc110x_reg_t;
/**
* @brief CC110x radio configuration
*/
typedef struct {
cc110x_reg_t reg_cfg; /**< CC110X register configuration */
uint8_t pa_power; /**< Output power setting */
} cc110x_cfg_t;
/**
* @brief Radio Control Flags
*/
typedef struct {
uint8_t _RSSI; /**< The RSSI value of last received packet */
uint8_t _LQI; /**< The LQI value of the last received packet */
} cc110x_flags_t;
/**
* @brief Statistic interface for debugging
*/
typedef struct cc110x_statistic {
uint32_t packets_in; /**< total nr of packets received */
uint32_t packets_in_crc_fail; /**< dropped because of invalid crc */
uint32_t packets_in_while_tx; /**< receive while tx */
uint32_t raw_packets_out; /**< packets sent */
} cc110x_statistic_t;
#ifdef __cplusplus
}
#endif
#endif /* CC110X_INTERNAL_H */
/** @} */

View File

@ -1,67 +0,0 @@
/*
* Copyright (C) 2014 Freie Universität Berlin
* 2015 Kaspar Schleiser <kaspar@schleiser.de>
*
* 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 Variables for the cc110x netdev interface
*
* @author Fabian Nack <nack@inf.fu-berlin.de>
* @author Kaspar Schleiser <kaspar@schleiser.de>
*/
#ifndef CC110X_NETDEV_H
#define CC110X_NETDEV_H
#include "periph/gpio.h"
#include "periph/spi.h"
#include "net/netdev.h"
#include "cc110x.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Implementation of netdev_driver_t for CC110X device
*/
extern const netdev_driver_t netdev_cc110x_driver;
/**
* @brief cc110x netdev struct
*/
typedef struct netdev_cc110x {
netdev_t netdev; /**< writing obious */
cc110x_t cc110x; /**< documentation here */
} netdev_cc110x_t;
/**
* @brief Received packet status information for cc110x radios
*/
typedef struct netdev_radio_rx_info netdev_cc110x_rx_info_t;
/**
* @brief netdev <-> cc110x glue code initialization function
*
* @param[out] netdev_cc110x ptr to netdev_cc110x struct ti initialize
* @param[in] params cc110x IO parameter struct to use
*
* @return 0 on success
* @return -1 on error
*/
int netdev_cc110x_setup(netdev_cc110x_t *netdev_cc110x, const cc110x_params_t *params);
#ifdef __cplusplus
}
#endif
#endif /* CC110X_NETDEV_H */
/** @} */

View File

@ -1,116 +0,0 @@
/*
* Copyright (C) 2014 Freie Universität Berlin
* Copyright (C) 2013 INRIA
* Copyright (C) 2015 Kaspar Schleiser <kaspar@schleiser.de>
*
* 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 CC110X SPI functions
*
* @author Oliver Hahm <oliver.hahm@inria.fr>
* @author Fabian Nack <nack@inf.fu-berlin.de>
* @author Kaspar Schleiser <kaspar@schleiser.de>
*/
#ifndef CC110X_SPI_H
#define CC110X_SPI_H
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Write a set of bytes using burst mode (if available)
*
* @param dev Device to work on
* @param addr Destination register
* @param buffer Data to be written
* @param count Size of data
*/
void cc110x_writeburst_reg(cc110x_t *dev, uint8_t addr, const char *buffer, uint8_t count);
/**
* @brief Read a set of bytes using burst mode (if available)
*
* @param dev Device to work on
* @param addr Source register
* @param buffer Buffer to store read data
* @param count Size of data to be read
*/
void cc110x_readburst_reg(cc110x_t *dev, uint8_t addr, char *buffer, uint8_t count);
/**
* @brief Write one byte to a register
*
* @param dev Device to work on
* @param addr Destinatoin register
* @param value New value
*/
void cc110x_write_reg(cc110x_t *dev, uint8_t addr, uint8_t value);
/**
* @brief Read a byte from register
*
* @param dev Device to work on
* @param addr Source register
*
* @return Read state and value of register
*/
uint8_t cc110x_read_reg(cc110x_t *dev, uint8_t addr);
/**
* @brief Read a byte from register, robust version
*
* Datasheet states some registered should be read twice until
* it returns the same value.
*
* @param dev Device to work on
* @param addr Source register
*
* @return Read state and value of register
*/
uint8_t cc110x_get_reg_robust(cc110x_t *dev, uint8_t addr);
/**
* @brief Read state of a register
*
* @param dev Device to work on
* @param addr Source register
*
* @return State of register
*/
uint8_t cc110x_read_status(cc110x_t *dev, uint8_t addr);
/**
* @brief Sends a command strobe
*
* @param dev Device to work on
* @param c Command code
*
* @return Command response
*/
uint8_t cc110x_strobe(cc110x_t *dev, uint8_t c);
/**
* @brief Pull CS to low and wait for CC110x stabilization
*
* @param dev Device to work on
*/
void cc110x_cs(cc110x_t *dev);
#ifdef __cplusplus
}
#endif
#endif /* CC110X_SPI_H */
/** @} */

View File

@ -0,0 +1,49 @@
/*
* 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 Calibration related functions of the CC110x transceiver driver
*
* @author Marian Buschsieweke <marian.buschsieweke@ovgu.de>
* @}
*/
#ifndef CC110X_CALIBRATION_H
#define CC110X_CALIBRATION_H
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Perform a recalibration of the transceiver
*
* @param dev The device descriptor of the transceiver
*
* @retval 0 Success
* @retval -EIO Failed
*
* @pre @p dev has been acquired using @ref cc110x_acquire
* @pre Transceiver is in IDLE state
* @post On success @p dev is still acquired, the caller has to release
* it. On failure the SPI bus is **already** **released**
* @post Transceiver is again in IDLE state, calibration has been
* performed and calibration data has been backed up on MCU.
*/
int cc110x_recalibrate(cc110x_t *dev);
#ifdef __cplusplus
}
#endif
#endif /* CC110X_CALIBRATION_H */
/** @} */

View File

@ -0,0 +1,213 @@
/*
* 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 Functions to communicate with the CC1100/CC1101 transceiver
*
* @author Marian Buschsieweke <marian.buschsieweke@ovgu.de>
* @}
*/
#ifndef CC110X_COMMUNICATION_H
#define CC110X_COMMUNICATION_H
#include "periph/gpio.h"
#include "periph/spi.h"
#include "cc110x.h"
#include "cc110x_constants.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Acquire the SPI interface of the transceiver and configure it
*
* @retval SPI_OK Success
* @retval SPI_NOMODE SPI mode 0 not supported by MCU
* @retval SPI_NOCLK SPI clock given in @ref cc110x_params_t is not supported
*
* @pre @ref cc110x_power_on has be called before calling this function.
* (Only needed *once* when the driver initializes.)
*/
static inline int cc110x_acquire(cc110x_t *dev)
{
return spi_acquire(dev->params.spi, dev->params.cs, SPI_MODE_0,
dev->params.spi_clk);
}
/**
* @brief Release the SPI interface of the transceiver
*/
static inline void cc110x_release(cc110x_t *dev)
{
spi_release(dev->params.spi);
}
/**
* @brief Read a single configuration/status register from the transceiver
*
* @param dev Device descriptor of the transceiver to read the register from
* @param addr Address of the register to read
* @param dest Where to store the received register content
*
* @return The received status byte
*
* @pre @p dest points to one byte of writeable memory
* @warning Race condition: SPI access to status registers can occur while
* their content is changed, resulting in corrupted data being
* retrieved. @ref cc110x_read_reliable provides reliable access
* to status registers and should be used to read the TXBYTES,
* RXBYTES, MARCSTATE, LQI, RSSI, WORTIME1 and WORTIME0 status
* registers. (See Silicon Errata from 2015 at pages 4ff.)
* (In IDLE state LQI and RSSI can be read safely using this
* function.)
* @warning The received status byte is occasionally corrupted. (See
* Silicon Errata from 2015 at pages 4ff.) Use @ref cc110x_status
* To get the status byte in a reliable way.
*/
uint8_t cc110x_read(cc110x_t *dev, uint8_t addr, uint8_t *dest);
/**
* @brief Read a single status register from the transceiver reliable
*
* This function has more overhead than @ref cc110x_read, but it is the only
* reliable way to access frequently updated status registers.
*
* @param dev Device descriptor of the transceiver to read the register from
* @param addr Address of the register to read
* @param dest Where to store the received register content
*
* @return The received status byte
*
* @pre @p dest points to one byte of writeable memory
* @warning The received status byte is occasionally corrupted. (See
* Silicon Errata from 2015 at pages 4ff.) Use @ref cc110x_status
* To get the status byte in a reliable way.
*/
uint8_t cc110x_read_reliable(cc110x_t *dev, uint8_t addr, uint8_t *dest);
/**
* @brief Write to a single configuration register on the transceiver
*
* @param dev Device descriptor of the transceiver to write byte to
* @param addr Address of the register to write to
* @param data Data to write
*
* @return The received status byte
*
* @pre @p addr <= 0x2e (@ref CC110X_REG_TEST0)
* @warning Writing to status registers is impossible (==> precondition)
* @warning The received status byte is occasionally corrupted. (See
* Silicon Errata from 2015 at pages 4ff.) Use @ref cc110x_status
* To get the status byte in a reliable way.
*/
uint8_t cc110x_write(cc110x_t *dev, uint8_t addr, uint8_t data);
/**
* @brief Burst-read a bunch of configuration registers from the transceiver
*
* @param dev Device descriptor of the transceiver to read from
* @param addr Address to start reading from
* @param dest Destination buffer to store the received data to
* @param len Number of bytes to read starting from @p addr
*
* @return The received status byte
*
* @pre @p dest points to a pre-allocated buffer of >= @p len bytes
* @pre @p addr + @p len <= 0x2e (@ref CC110X_REG_TEST0)
* @warning Burst read access from status registers is impossible
* (==> second precondition)
* @warning The received status byte is occasionally corrupted. (See
* Silicon Errata from 2015 at pages 4ff.) Use @ref cc110x_status
* To get the status byte in a reliable way.
*/
uint8_t cc110x_burst_read(cc110x_t *dev, uint8_t addr, void *dest, size_t len);
/**
* @brief Burst-write to a bunch of configuration registers on the transceiver
*
* @param dev Device descriptor of the transceiver to write
* @param addr Address to start writing to
* @param src Buffer holding the configuration to write
* @param len Number of registers to write to
*
* @return The received status byte
*
* @pre @p src points to @p len bytes of readable memory
* @pre @p addr + @p len <= 0x2e (@ref CC110X_REG_TEST0)
* @warning Writes to status registers is impossible
* (==> second precondition)
* @warning The received status byte is occasionally corrupted. (See
* Silicon Errata from 2015 at pages 4ff.) Use @ref cc110x_status
* to get the status byte in a reliable way.
*/
uint8_t cc110x_burst_write(cc110x_t *dev, uint8_t addr,
const void *src, size_t len);
/**
* @brief Send a command to the transceiver
*
* @param dev Device descriptor of the transceiver to send the command to
* @param cmd_strobe Command to send
*
* @return The received status byte
*
* @warning The received status byte is occasionally corrupted. (See
* Silicon Errata from 2015 at pages 4ff.) Use
* @ref cc110x_status to get the status byte in a reliable
* way.
*/
uint8_t cc110x_cmd(cc110x_t *dev, uint8_t cmd_strobe);
/**
* @brief Get the transceivers status byte in a reliable way
*
* @param dev Device descriptor of the transceiver to get the status from
*
* @return The received status byte
*/
uint8_t cc110x_status(cc110x_t *dev);
/**
* @brief Wakes up the transceiver from "Sleep" or "Crystal oscillator off"
* state and waits until the crystal has stabilized
*
* Thus function clears the CS pin, which triggers a transition from the "Sleep"
* or "Crystal oscillator off" states (see Figure 13 on page 28 in the data
* sheet).
*
* If the crystal was off (only in above mentioned states), the MCU must wait
* for the transceiver to become ready *before* any SPI transfer is initiated.
* In all other states, CS pin can be pulled low and SPI transfer can start
* right away (see section 10 on page 29 in the data sheet). This driver will
* never disable the transceivers crystal, so this function has to be called
* only once when the transceiver is powered on.
*
* The transceiver will signal that it is available by pulling the MISO pin
* low (section 10 on page 29 in the data sheet), which does not take longer
* than 150 microseconds (see Table 22 on page 30 in the data sheet). Instead
* of messing with the SPI interface, this driver simply waits for this upper
* bound, as suggested in the note below Table 22 on page 30 in the data sheet.
*
* @retval 0 Success
* @retval -EIO Couldn't pull the CS pin down (@ref cc110x_params_t::cs)
*/
int cc110x_power_on(cc110x_t *dev);
#ifdef __cplusplus
}
#endif
#endif /* CC110X_COMMUNICATION_H */
/** @} */

View File

@ -0,0 +1,522 @@
/*
* 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 Constants for the CC1100/CC1101 driver
*
* @author Marian Buschsieweke <marian.buschsieweke@ovgu.de>
* @}
*/
#ifndef CC110X_CONSTANTS_H
#define CC110X_CONSTANTS_H
#ifdef __cplusplus
extern "C" {
#endif
/**
* @name Command strobes of the CC1100/CC1101 transceiver
*
* See Table 42 on page 67 in the data sheet. Only values relevant to the
* driver are listed.
*
* @{
*/
/**
* @brief Reset chip (SRES)
*/
#define CC110X_STROBE_RESET 0x30
/**
* @brief Calibrate frequency synthesizer and turn it off (SCAL)
*/
#define CC110X_STROBE_CALIBRATE 0x33
/**
* @brief Go to RX state (SRX)
*
* Requires frequency calibration first
*/
#define CC110X_STROBE_RX 0x34
/**
* @brief Go to TX state (STX)
*
* Requires frequency calibration first
*/
#define CC110X_STROBE_TX 0x35
/**
* @brief Go to IDLE state (SIDLE)
*/
#define CC110X_STROBE_IDLE 0x36
/**
* @brief Go to power down state once CS goes high (SPWD)
*/
#define CC110X_STROBE_OFF 0x39
/**
* @brief Flush RX fifo (SFRX)
*
* Only valid in IDLE or in RXFIO_OVERFLOW states
*/
#define CC110X_STROBE_FLUSH_RX 0x3A
/**
* @brief Flush TX fifo (SFTX)
*
* Only valid in IDLE or in TXFIO_OVERFLOW states
*/
#define CC110X_STROBE_FLUSH_TX 0x3B
/**
* @brief Get the status byte (SNOP)
*/
#define CC110X_STROBE_STATUS 0x3D
/** @} */
/**
* @name Access modifies for accessing configuration/status registers
*
* See Table 45 on pages 69ff in the data sheet. These modifies need to be
* xor'ed with the address of the register.
*
* @{
*/
/**
* @brief Access modifier to write a single byte to a configuration register
*
* | read bit (`0x80`) | burst access bit (`0x40`) |
* |-------------------|---------------------------|
* | `0` (= write) | `0` (= no burst access) |
*/
#define CC110X_SINGLE_BYTE_WRITE 0x00
/**
* @brief Access modifier to write multiple bytes at once to configuration
* registers
*
* | read bit (`0x80`) | burst access bit (`0x40`) |
* |-------------------|---------------------------|
* | `0` (= write) | `1` (= burst access) |
*/
#define CC110X_BURST_WRITE 0x40
/**
* @brief Access modifier to read a single byte from a configuration register
*
* | read bit (`0x80`) | burst access bit (`0x40`) |
* |-------------------|---------------------------|
* | `1` (= read) | `0` (= no burst access) |
*/
#define CC110X_SINGLE_BYTE_READ 0x80
/**
* @brief Access modifier to read multiple bytes at once from configuration
* registers
*
* | read bit (`0x80`) | burst access bit (`0x40`) |
* |-------------------|---------------------------|
* | `1` (= read) | `1` (= burst access) |
*/
#define CC110X_BURST_READ 0xC0
/** @} */
/**
* @name "Multi byte registers" of the CC1100/CC1101 transceiver
*
* See Table 45 on pages 69ff in the data sheet. These multi byte registers
* have a special semantics, which is documented for each multi byte register
*
* @{
*/
/**
* @brief Access to the PATABLE as multi byte register
*
* It is most convenient to read/write the whole 8 bytes of the PATABLE using a
* burst access. The first single byte access after the CS pin is pulled low
* will read from / write to the first byte, the second access the second byte,
* and so on. As @ref cc110x_read and @ref cc110x_write pull the CS pin high
* after the access, all but the first byte are only accessible using burst
* access in this driver.
*/
#define CC110X_MULTIREG_PATABLE 0x3E
/**
* @brief Access to the TX and RX FIFO as multi byte register
*
* A single byte read using @ref cc110x_read from the FIFO multi byte register
* will retrieve and remove the next byte from the RX FIFO. A burst of *n* bytes
* using @ref cc110x_burst_read will retrieve and remove the next *n* bytes.
*
* A single byte write using @ref cc110x_write will push one byte of data into
* the TX FIFO. A multi byte write of *n* byte using @ref cc110x_burst_write
* will push *n* bytes into the TX FIFO.
*
* @warning Reading the last byte from the RX-FIFO results in data corruption,
* unless the whole frame was received. Thus, read all but the last
* byte from the FIFO until the whole frame was received.
*/
#define CC110X_MULTIREG_FIFO 0x3F
/** @} */
/**
* @name Configuration registers of the CC1100/CC1101 transceiver
*
* See Table 43 on pages 68ff in the data sheet. Only values relevant to the
* driver are listed.
*
* @{
*/
/**
* @brief First configuration register on the transceiver, used for burst
* access to the whole configuration
*/
#define CC110X_CONF_START 0x00
/**
* @brief GDO2 output pin configuration
*/
#define CC110X_REG_IOCFG2 0x00
/**
* @brief GDO1 output pin configuration
*/
#define CC110X_REG_IOCFG1 0x01
/**
* @brief GDO0 output pin configuration
*/
#define CC110X_REG_IOCFG0 0x02
/**
* @brief Device address
*/
#define CC110X_REG_ADDR 0x09
/**
* @brief Channel number
*/
#define CC110X_REG_CHANNR 0x0A
/**
* @brief Intermediate frequency to use
*/
#define CC110X_REG_FSCTRL1 0x0B
/**
* @brief Frequency control word, high byte
*/
#define CC110X_REG_FREQ2 0x0D
/**
* @brief Frequency control word, middle byte
*/
#define CC110X_REG_FREQ1 0x0E
/**
* @brief Frequency control word, low byte
*/
#define CC110X_REG_FREQ0 0x0F
/**
* @brief Modem configuration (channel filter bandwidth and data rate)
*/
#define CC110X_REG_MDMCFG4 0x10
/**
* @brief Modem configuration (data rate)
*/
#define CC110X_REG_MDMCFG3 0x11
/**
* @brief Modem deviation setting
*/
#define CC110X_REG_DEVIATN 0x15
/**
* @brief Front End TX Configuration
*
* Least three significant bits contain the current PA power setting.
*/
#define CC110X_REG_FREND0 0x22
/**
* @brief Charge pump current calibration
*
* This value depends on the environment (e.g. temperature, supply voltage,
* etc.), but not on the frequency. Thus, this value does not become obsolete
* when changing the channel.
*/
#define CC110X_REG_FSCAL3 0x23
/**
* @brief VCO current calibration
*
* This value depends on the environment (e.g. temperature, supply voltage,
* etc.), but not on the frequency. Thus, this value does not become obsolete
* when changing the channel.
*/
#define CC110X_REG_FSCAL2 0x24
/**
* @brief VCO capacitance calibration
*
* This value is frequency depended. Thus, for fast channel hopping it has to
* be obtained for each channel (by performing a calibration on that channel
* and reading it out). Than the stored calibration data can be written to the
* register when changing the channel.
*/
#define CC110X_REG_FSCAL1 0x25
/**
* @brief Undocumented frequency calibration value
*
* For fast channel hopping this value can be ignored (see page 64ff in the
* data sheet) - so it has to be frequency independent.
*/
#define CC110X_REG_FSCAL0 0x26
/**
* @brief Unlock the temperature sensor by writing 0xBF to it
*
* Intended for production test, but who would complain about getting an
* temperature sensor for free :-)
*
* @see @ref CC110X_GDO0_ANALOG_TEMPERATURE for details
*/
#define CC110X_REG_PTEST 0x2A
/**
* @brief Magic value obtained with SmartRF Studio software
*/
#define CC110X_REG_TEST2 0x2C
/**
* @brief Magic value obtained with SmartRF Studio software
*/
#define CC110X_REG_TEST1 0x2D
/**
* @brief Magic value obtained with SmartRF Studio software
*/
#define CC110X_REG_TEST0 0x2E
/** @} */
/**
* @name Status registers of the CC1100/CC1101 transceiver
*
* See Table 43 on pages 68ff in the data sheet. Only values relevant to the
* driver are listed.
*
* @warning The burst access bit of these registers has to be set to distinguish
* between command strobes and status registers. Thus, no burst access
* to status registers is possible.
*
* @{
*/
/**
* @brief Part number
*
* @warning Not accessible using burst reads
*/
#define CC110X_REG_PARTNUM (0x30 | 0x40)
/**
* @brief Version
*
* @warning Not accessible using burst reads
*/
#define CC110X_REG_VERSION (0x31 | 0x40)
/**
* @brief Estimated link quality
*
* @warning Not accessible using burst reads
*/
#define CC110X_REG_LQI (0x33 | 0x40)
/**
* @brief Received signal strength indication
*
* @warning Not accessible using burst reads
*/
#define CC110X_REG_RSSI (0x34 | 0x40)
/**
* @brief Packet status, GDOx status
*
* @warning Not accessible using burst reads
*/
#define CC110X_REG_PKTSTATUS (0x38 | 0x40)
/**
* @brief Number of bytes in the TX FIFO
*
* @warning Not accessible using burst reads
* @warning The received value could be corrupted when reading it while it is
* updated. Reading it out twice until both reads return the same
* value is the suggested workaround for this hardware bug.
*/
#define CC110X_REG_TXBYTES (0x3A | 0x40)
/**
* @brief Number of bytes available in the RX FIFO
*
* @warning Not accessible using burst reads
* @warning The received value could be corrupted when reading it while it is
* updated. Reading it out twice until both reads return the same
* value is the suggested workaround for this hardware bug.
*/
#define CC110X_REG_RXBYTES (0x3B | 0x40)
/** @} */
/**
* @name Possible values for the IOCFG2, IOCFG1, and IOCFG0 configuration registers
*
* See Table 41 on page 62 in the data sheet. Only values relevant to the
* driver are listed.
*
* @{
*/
/**
* @brief GDOx goes HIGH when data has to be read from RX FIFO or when a packet
* is fully received
*
* Depends on the threshold set for the RX-FIFO in the FIFOTHR configuration
* register
*/
#define CC110X_GDO_ON_RX_DATA 0x01
/**
* @brief GDOx goes LOW when data should be written to the TX FIFO
*
* Depends on the threshold set for the TX-FIFO in the FIFOTHR configuration
* register
*/
#define CC110X_GDO_ON_TX_DATA 0x02
/**
* @brief GDOx goes HIGH when a packet is received/send and back LOW when the
* transmission is completed/aborted (e.g. wrong destination address)
*/
#define CC110X_GDO_ON_TRANSMISSION 0x06
/**
* @brief GDOx goes HIGH when channel is clear for sending
*
* Depends on the CCA_MODE setting in the MCSM1 configuration register
*/
#define CC110X_GDO_ON_CHANNEL_CLEAR 0x09
/**
* @brief GDOx goes HIGH when PLL is in lock
*
*/
#define CC110X_GDO_ON_PLL_IN_LOCK 0x0A
/**
* @brief GDOx remains constantly LOW
*/
#define CC110X_GDO_CONSTANT_LOW 0x2F
/**
* @brief GDOx remains constantly HIGH
*/
#define CC110X_GDO_CONSTANT_HIGH 0x6F
/**
* @brief Repurpose GDO0 as analog temperature sensor in IDLE state
*
* This only works with GDO0 and only in IDLE state! Additionally, 0xBF has
* to be written to configuration register PTEST when in IDLE state. Before
* leaving IDLE state, PTEST should be restored to 0x7F.
*/
#define CC110X_GDO0_ANALOG_TEMPERATURE 0x80
/** @} */
/**
* @name Bitmasks to access entries in the PKTSTATUS status register
*
* See page 94 in the data sheet.
*
* @{
*/
/**
* @brief Bitmask to get the GDO0 state from the PKTSTATUS status register value
*/
#define CC110X_PKTSTATUS_GDO0 0x01
/**
* @brief Bitmask to get the GDO2 state from the PKTSTATUS status register value
*/
#define CC110X_PKTSTATUS_GDO2 0x04
/**
* @brief Bitmask to get the SFD bit from the PKTSTATUS status register value
* which is set while receiving a frame
*/
#define CC110X_PKTSTATUS_RECEIVING 0x08
/**
* @brief Bitmask to get the CCA bit from the PKTSTATUS status register value
*/
#define CC110X_PKTSTATUS_CCA 0x10
/**
* @brief Bitmask to get the Carrier Sense bit from the PKTSTATUS status
* register value
*/
#define CC110X_PKTSTATUS_CS 0x40
/** @} */
/**
* @name Values to write into the PTEST configuration register
*
* See page 91 in the data sheet. Only the two documented values are specified.
*
* @{
*/
/**
* @brief Default value of the PTEST configuration register.
*/
#define CC110X_PTEST_DEFAULT 0x7F
/**
* @brief Value to write in PTEST when reading the temperature.
*
* @see CC110X_GDO0_ANALOG_TEMPERATURE
*/
#define CC110X_PTEST_TEMPERATURE 0xBF
/** @} */
/**
* @brief Size of the RX and TX FIFO
*/
#define CC110X_FIFO_SIZE 64
#ifdef __cplusplus
}
#endif
#endif /* CC110X_CONSTANTS_H */
/** @} */

View File

@ -0,0 +1,83 @@
/*
* 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 Internal functions of the CC110x transceiver driver
*
* @author Marian Buschsieweke <marian.buschsieweke@ovgu.de>
* @}
*/
#ifndef CC110X_INTERNAL_H
#define CC110X_INTERNAL_H
#include "cc110x_calibration.h"
#include "cc110x_communication.h"
#include "cc110x_constants.h"
#include "cc110x_netdev.h"
#include "cc110x_rx_tx.h"
#include "cc110x_settings.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Extract the device state from the status register value
*
* @param status Contents of the CC110x's status register
* @return The state encoded in @p status
*
* The status register contains the device state at the bits 1 - 3
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* Format of the status byte
* 0 1 2 3 4 5 6 7
* +-+-+-+-+-+-+-+-+
* |R|STATE| FIFO |
* +-+-+-+-+-+-+-+-+
*
* R = Chip Ready bit (0 = ready, 1 = power and crystal are not yet stable)
* STATE = The device state
* FIFO = Number of bytes available in RX FIFO or (in TX mode) number of free
* bytes
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* Note: The FIFO has a size of 64 bytes. If more than 15 bytes are available
* for reading in the FIFO (or more than 15 bytes can be written in TX), the
* value will still show 15. This driver never uses this information, but
* accesses a dedicated register for that.
*/
static inline cc110x_state_t cc110x_state_from_status(uint8_t status)
{
return (cc110x_state_t)((status >> 4) & 0x7);
}
/**
* @brief Figure out of the transceiver is ready or still powering up
* @param status Contents of the CC110x's status register
* @retval 1 Transceiver is ready
* @retval 0 *NOT* ready, still powering up
*
* @see cc110x_state_from_status
*/
static inline int cc110x_is_ready_from_status(uint8_t status)
{
return (status & 0x80) ? 0: 1;
}
#ifdef __cplusplus
}
#endif
#endif /* CC110X_INTERNAL_H */
/** @} */

View File

@ -0,0 +1,47 @@
/*
* 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 Functions related to the netdev interface of the CC110x driver
*
* @author Marian Buschsieweke <marian.buschsieweke@ovgu.de>
* @}
*/
#ifndef CC110X_NETDEV_H
#define CC110X_NETDEV_H
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief RIOT's API to interact with the CC1100/CC1101 driver
*/
extern const netdev_driver_t cc110x_driver;
/**
* @brief Interrupt handler to call on both edges of the GDO0 and GDO2 pins
*
* @param dev The device descriptor of the transceiver
*
* This interrupt handler requests that the cc110x ISR is called in thread
* context
*/
void cc110x_on_gdo(void *dev);
#ifdef __cplusplus
}
#endif
#endif /* CC110X_NETDEV_H */
/** @} */

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2017 Marian Buschsieweke <marian.buschsieweke@ovgu.de> * Copyright (C) 2018 Otto-von-Guericke-Universität Magdeburg
* *
* This file is subject to the terms and conditions of the GNU Lesser * 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 * General Public License v2.1. See the file LICENSE in the top level
@ -20,6 +20,7 @@
#define CC110X_PARAMS_H #define CC110X_PARAMS_H
#include "board.h" #include "board.h"
#include "cc110x_settings.h"
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
@ -43,32 +44,51 @@ extern "C" {
#define CC110X_PARAM_GDO0 GPIO_PIN(0, 27) #define CC110X_PARAM_GDO0 GPIO_PIN(0, 27)
#endif #endif
#ifndef CC110X_PARAM_GDO1
#define CC110X_PARAM_GDO1 GPIO_PIN(1, 23)
#endif
#ifndef CC110X_PARAM_GDO2 #ifndef CC110X_PARAM_GDO2
#define CC110X_PARAM_GDO2 GPIO_PIN(0, 28) #define CC110X_PARAM_GDO2 GPIO_PIN(0, 28)
#endif #endif
#ifndef CC110X_PARAM_SPI_CLOCK
#define CC110X_PARAM_SPI_CLOCK SPI_CLK_5MHZ
#endif
#ifndef CC110X_PARAM_L2ADDR
#define CC110X_PARAM_L2ADDR CC110X_L2ADDR_AUTO
#endif
#ifndef CC110X_PARAM_PATABLE
#define CC110X_PARAM_PATABLE (&cc110x_patable_868mhz)
#endif
#ifndef CC110X_PARAM_CONFIG
#define CC110X_PARAM_CONFIG NULL
#endif
#ifndef CC110X_PARAM_CHANNELS
#define CC110X_PARAM_CHANNELS (&cc110x_chanmap_868mhz_lora)
#endif
#ifndef CC110X_PARAMS #ifndef CC110X_PARAMS
#define CC110X_PARAMS { \ #define CC110X_PARAMS { \
.spi = CC110X_PARAM_SPI, \ .spi = CC110X_PARAM_SPI, \
.spi_clk = CC110X_PARAM_SPI_CLOCK, \
.cs = CC110X_PARAM_CS, \ .cs = CC110X_PARAM_CS, \
.gdo0 = CC110X_PARAM_GDO0, \ .gdo0 = CC110X_PARAM_GDO0, \
.gdo1 = CC110X_PARAM_GDO1, \
.gdo2 = CC110X_PARAM_GDO2, \ .gdo2 = CC110X_PARAM_GDO2, \
.l2addr = CC110X_PARAM_L2ADDR, \
.patable = CC110X_PARAM_PATABLE, \
.config = CC110X_PARAM_CONFIG, \
.channels = CC110X_PARAM_CHANNELS, \
} }
#endif #endif
/** @} */ /** @} */
/** /**
* @name CC110X configuration * @name CC110X configuration
* @brief Specifies the SPI bus and GPIOs connected to the CC110X transceiver * @brief Specifies the SPI bus and GPIOs connected to the CC110X transceiver
*/ */
const cc110x_params_t cc110x_params[] = { static const cc110x_params_t cc110x_params[] = {
CC110X_PARAMS CC110X_PARAMS
}; };

View File

@ -0,0 +1,47 @@
/*
* 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 Functions to related to RX/TX of the CC110x transceiver driver
*
* @author Marian Buschsieweke <marian.buschsieweke@ovgu.de>
* @}
*/
#ifndef CC110X_RX_TX_H
#define CC110X_RX_TX_H
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief ISR to be called via @ref netdev_driver_t::isr
*/
void cc110x_isr(netdev_t *dev);
/**
* @brief Bring transceiver into RX mode
*
* @param dev The device descriptor of the transceiver
*
* @pre @p dev has been acquired using @ref cc110x_acquire
* @post @p dev is still acquired, the caller has to release it
*/
void cc110x_enter_rx_mode(cc110x_t *dev);
#ifdef __cplusplus
}
#endif
#endif /* CC110X_RX_TX_H */
/** @} */

View File

@ -0,0 +1,170 @@
/*
* 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 Default settings of the TI CC1100/CC1101 transceiver
*
* @author Marian Buschsieweke <marian.buschsieweke@ovgu.de>
* @}
*/
#ifndef CC110X_SETTINGS_H
#define CC110X_SETTINGS_H
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief The size of the configuration array for CC110X in bytes
*/
#define CC110X_CONF_SIZE 41
/**
* @brief Length of the PATABLE
*/
#define CC110X_PATABLE_LEN 8
/**
* @brief Configuration register values for CC1100/CC1101 transceivers
*/
extern const char cc110x_conf[CC110X_CONF_SIZE];
/**
* @brief Magic numbers to write to the TEST2, TEST1 and TEST0 configuration
* registers
*
* Those registers are poorly documented. The data sheet refers to SmartRF
* Studio to obtain the correct magic numbers. Those values were identical
* for the 433MHz, 868MHz and 915MHz band at GFSK, 250kBaud and 127kHz
* deviation. Maybe by luck? So when adding a new base band or adjusting the
* transceiver configuration, those numbers should be checked again with the
* SmartRF Studio
*/
extern const char cc110x_magic_registers[3];
/**
* @name Configuration data that specify the 8 available output power levels
*
* Source: Table 39 in the data sheet on page 60.
*
* | Output Power [dBm] | Setting 433 MHz | Setting 868 MHz | Setting 915 MHz |
* |--------------------|-----------------|-----------------|-----------------|
* | -30 | 0x12 | 0x03 | 0x03 |
* | -20 | 0x0E | 0x0F | 0x0E |
* | -15 | 0x1D | 0x1E | 0x1E |
* | -10 | 0x34 | 0x27 | 0x27 |
* | 0 | 0x60 | 0x50 | 0x8E |
* | 5 | 0x84 | 0x81 | 0xCD |
* | 7 | 0xC8 | 0xCB | 0xC7 |
* | 10 | 0xC0 | 0xC2 | 0xC0 |
*
* @{
*/
/**
* @brief PATABLE values for the 433 MHz band
*
* Source: Table 39 in the data sheet on page 60.
*/
extern const cc110x_patable_t cc110x_patable_433mhz;
/**
* @brief PATABLE values for the 868 MHz band
*
* @note This is the default PATABLE. There is no need to upload this table
* unless switching between different PATABLEs is required.
*
* Source: Table 39 in the data sheet on page 60.
*/
extern const cc110x_patable_t cc110x_patable_868mhz;
/**
* @brief PATABLE values for the 915 MHz band
*
* Source: Table 39 in the data sheet on page 60.
*/
extern const cc110x_patable_t cc110x_patable_915mhz;
/** @} */
/**
* @name Channel mappings for CC110x transceivers
*
* @{
*/
/**
* @brief Channel map for 50 kHz channels with a distance of 200 kHz in the
* 433 MHz band
*
* This channel mapping assumes @ref cc110x_config_433mhz_38kbps_50khz is
* used as configuration. @ref cc110x_chanmap_433mhz_50khz_alt is an alternative
* channel map that does not overlap with this map. (This means devices with
* the alternative channel map cannot communicate with devices using this one,
* but also no interference is expected.)
*/
extern const cc110x_chanmap_t cc110x_chanmap_433mhz_50khz;
/**
* @brief Alternative channel map for 50 kHz channels with a distance of 200 kHz
* in the 433 MHz band
* @see cc110x_chanmap_433mhz_50khz
*
* This channel mapping assumes @ref cc110x_config_433mhz_38kbps_50khz is
* used as configuration.
*/
extern const cc110x_chanmap_t cc110x_chanmap_433mhz_50khz_alt;
/**
* @brief Channel map for 5 non-overlapping 300 kHz channels in the 433 MHz band
*
* This channel mapping assumes @ref cc110x_config_433mhz_250kbps_300khz is
* used as configuration. (Note: The distance between channels is in fact
* 350 kHz instead of 300. This increased distance allows to cover the full
* license free range and reduce the possibility of interference of adjacent
* channels. Reducing the distance to 300kHz would still allow only 5 channels.)
*/
extern const cc110x_chanmap_t cc110x_chanmap_433mhz_300khz;
/**
* @brief Channel map for LoRa 868MHz channels 10 to 17 (available as 0 to 7).
*
* This channel mapping assumes @ref cc110x_config_868mhz_250kbps_300khz is used
* as configuration.
*/
extern const cc110x_chanmap_t cc110x_chanmap_868mhz_lora;
/** @} */
/**
* @name Base frequency, channel bandwidth and data rate configurations for CC110x transceivers
*
* @{
*/
/**
* @brief CC110x configuration: 433MHz band, 38.4kbps data rate, 50 kHz channel
* width
*/
extern const cc110x_config_t cc110x_config_433mhz_38kbps_50khz;
/**
* @brief CC110x configuration: 433MHz band, 250kbps data rate, 300 kHz channel
* width
*/
extern const cc110x_config_t cc110x_config_433mhz_250kbps_300khz;
/**
* @brief CC110x configuration: 868MHz band, 250kbps data rate, 300 kHz channel
* width
*/
extern const cc110x_config_t cc110x_config_868mhz_250kbps_300khz;
/** @} */
#ifdef __cplusplus
}
#endif
#endif /* CC110X_SETTINGS_H */
/** @} */

View File

@ -1,35 +0,0 @@
/*
* Copyright (C) 2017 Freie Universität Berlin
*
* 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 net_gnrc_netif
* @{
*
* @file
* @brief CC110x adaption for @ref net_gnrc_netif
*
* @author Martine Lenders <m.lenders@fu-berlin.de>
*/
#ifndef GNRC_NETIF_CC110X_H
#define GNRC_NETIF_CC110X_H
#include "net/gnrc/netif.h"
#ifdef __cplusplus
extern "C" {
#endif
gnrc_netif_t *gnrc_netif_cc110x_create(char *stack, int stacksize, char priority,
char *name, netdev_t *dev);
#ifdef __cplusplus
}
#endif
#endif /* GNRC_NETIF_CC110X_H */
/** @} */

View File

@ -86,7 +86,7 @@ static gnrc_pktsnip_t *cc1xxx_adpt_recv(gnrc_netif_t *netif)
return NULL; return NULL;
} }
netif_hdr = (gnrc_netif_hdr_t *)hdr->data; netif_hdr = (gnrc_netif_hdr_t *)hdr->data;
netif_hdr->if_pid = netif->pid; gnrc_netif_hdr_set_netif(netif_hdr, netif);
netif_hdr->rssi = rx_info.rssi; netif_hdr->rssi = rx_info.rssi;
netif_hdr->lqi = rx_info.lqi; netif_hdr->lqi = rx_info.lqi;
if (l2hdr.dest_addr == CC1XXX_BCAST_ADDR) { if (l2hdr.dest_addr == CC1XXX_BCAST_ADDR) {
@ -98,6 +98,11 @@ static gnrc_pktsnip_t *cc1xxx_adpt_recv(gnrc_netif_t *netif)
/* and append the netif header */ /* and append the netif header */
LL_APPEND(payload, hdr); LL_APPEND(payload, hdr);
#ifdef MODULE_NETSTATS_L2
netif->stats.rx_count++;
netif->stats.rx_bytes += pktlen;
#endif
return payload; return payload;
} }
@ -122,6 +127,9 @@ static int cc1xxx_adpt_send(gnrc_netif_t *netif, gnrc_pktsnip_t *pkt)
if (netif_hdr->flags & BCAST) { if (netif_hdr->flags & BCAST) {
l2hdr.dest_addr = CC1XXX_BCAST_ADDR; l2hdr.dest_addr = CC1XXX_BCAST_ADDR;
DEBUG("[cc1xxx-gnrc] send: preparing to send broadcast\n"); DEBUG("[cc1xxx-gnrc] send: preparing to send broadcast\n");
#ifdef MODULE_NETSTATS_L2
netif->stats.tx_mcast_count++;
#endif
} }
else { else {
/* check that destination address is valid */ /* check that destination address is valid */
@ -130,6 +138,9 @@ static int cc1xxx_adpt_send(gnrc_netif_t *netif, gnrc_pktsnip_t *pkt)
l2hdr.dest_addr = addr[0]; l2hdr.dest_addr = addr[0];
DEBUG("[cc1xxx-gnrc] send: preparing to send unicast %02x --> %02x\n", DEBUG("[cc1xxx-gnrc] send: preparing to send unicast %02x --> %02x\n",
(int)l2hdr.src_addr, (int)l2hdr.dest_addr); (int)l2hdr.src_addr, (int)l2hdr.dest_addr);
#ifdef MODULE_NETSTATS_L2
netif->stats.tx_unicast_count++;
#endif
} }
/* now let's send out the stuff */ /* now let's send out the stuff */

View File

@ -1,132 +1,629 @@
/* /*
* Copyright (C) 2014 Freie Universität Berlin * Copyright (C) 2013 INRIA
* 2014 Freie Universität Berlin
* 2015 Kaspar Schleiser <kaspar@schleiser.de> * 2015 Kaspar Schleiser <kaspar@schleiser.de>
* 2018,2019 Otto-von-Guericke-Universität Magdeburg
* *
* This file is subject to the terms and conditions of the GNU Lesser * This file is subject to the terms and conditions of the GNU Lesser General
* General Public License v2.1. See the file LICENSE in the top level * Public License v2.1. See the file LICENSE in the top level directory for more
* directory for more details. * details.
*/ */
/** /**
* @defgroup drivers_cc110x CC1100/CC1101 radio driver * @defgroup drivers_cc110x CC1100/CC1100e/CC1101 Sub-GHz transceiver driver
* @ingroup drivers_netdev * @ingroup drivers_netdev
* @brief Driver for TI CC1100/CC1101 radios *
* This module contains the driver for the TI CC1100/CC110e/CC1101 Sub-GHz
* transceivers.
*
* @warning How the CC1100/CC1101 operate can be configured quite sophistically.
* This has the drawback, that configurations breaking laws and rules
* are complete possible. Please make sure that the configured output
* power, duty cycle, frequency range, etc. conform to the rules,
* standards and laws that apply in your use case.
*
*
* Supported Hardware and how to obtain
* ====================================
*
* This driver has been developed for the CC1101 and the older CC1100
* transceiver and tested for both. However, it should work with the CC1100e
* as well - but this has *NOT* been tested at all.
*
* It is suggested to go for the CC1101 when considering to buy one of the
* supported transceivers. The easiest way is to obtain CC1101 break out boards
* with a complete antenna circuit & antenna that can be connected via jumper
* wires using an 8 pin DIP pin header. These are sold in various flavours start
* from less than 2 at quantity one at your favourite Far East store. Beware
* that while the CC1101 chip can operate a various base frequencies, the
* antenna circuit will only work for a single frequency band. Most break out
* boards will operate at 433 MHz, which is license free in many countries (but
* verify that for your country before buying!). EU citizens might prefer the
* 868 MHz band over the 433 MHz, as more license free bandwidth is available
* in the 868 MHz band in the EU. (But when deploying only a few dozens of
* devices, the 433 MHz band is also fine for EU citizens.) US citizens should
* go for the 900 MHz band (as 868 MHz is not license free in the USA), which
* even contains more bandwidth than the 868 MHz band. (However, the 900 MHz
* band has not been tested, as using it would be illegal in the EU.)
*
*
* Packet Format
* =============
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* 0 1 2 3
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Preamble (4 bytes, handled by hardware, see MDMCFG1) |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Sync Word (4 bytes, handled by hardware, see MDMCFG2) |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Length Field | Destination | Source | Payload...
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* ... (see Length Field) | CRC (handled by hardware) |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* | Field | Description |
* |--------------|------------------------------------------------------------|
* | Preamble | 4 bytes, handled by hardware |
* | Sync Word | 4 bytes, handled by hardware |
* | Length Field | Handled by software & hardware, length of payload + 2 |
* | Destination | Handled by software & hardware, destination MAC address |
* | Source | Handled by software only, source MAC address |
* | Payload | Handled by software only, the payload to send |
* | CRC | 2 bytes, handled by hardware |
*
* The Length Field contains the length of the driver supplied data in bytes,
* not counting the Length Field. Thus, it contains the length of the payload
* plus the length of the Destination and Source address.
*
*
* Layer-2 Addresses
* -----------------
*
* The layer 2 addresses of the CC110x transceivers is a single byte long and
* the special value `0x00` for the destination address is used in broadcast
* transmissions. The transceiver is configured by this driver to ignore all
* packets unless the destination address matches the address of the transceiver
* or the destination address is `0x00`.
*
* Please note that the layer 2 address by default is derived from the CPU ID.
* Due to the birthday paradox with only 20 devices the probability of a
* collision is already bigger than 50%. Thus, manual address assignment is
* supported by defining `C110X_PARAM_L2ADDR`.
*
*
* Base Band, Data Rate, Channel Bandwidth and Channel Map Configuration
* =====================================================================
*
* This driver allows to configure the base band, the data rate and the channel
* bandwidth using an @ref cc110x_config_t data structure. Default
* configurations are supplied and name using the following scheme:
* `cc110x_config_<BASE-BAND>_<DATA-RATE>_<CHANNEL-BANDWIDTH>`. (E.g.
* @ref cc110x_config_868mhz_250kbps_300khz is the default configuration used by
* the MSB-A2 and the MSB-IoT boards.)
*
* Using the @ref cc110x_chanmap_t data structure the channel layout can be
* defined. This map contains 8 entries, each defines the offset from the base
* frequency defined in the @ref cc110x_config_t data structure for each
* channel in steps of 50kHz. E.g. @ref cc110x_chanmap_868mhz_lora provides
* the LoRa channels 10 to 17 in the 868MHz band. (The RIOT channel numbers
* will always start from 0, and currently only up to eight channels are
* supported. A special value of 255 as offset from the base frequency in the
* channel map is used mark the channel as disabled. This can be used if less
* than 8 non-overlapping channels are possible in the license free band.)
*
* Please note that the channel map (@ref cc110x_chanmap_t) must match the
* base configuration (@ref cc110x_config_t), as the channel map is relative
* to the configured base frequency. Also, the distance between the channels
* in the channel map should match the channel bandwidth of the configuration,
* as otherwise channels could overlap.
*
* Both configuration and matching channel map can be applied using
* @ref cc110x_apply_config. Please consider this as a slow operation, as the
* transceiver needs to be calibrated for each channel in the channel map.
*
*
* Calibration of the Frequency Generator
* ======================================
*
* The CC110x transceivers use a voltage controlled oscillator (VCO) and a
* phase locked loop (PLL) for frequency generation. However, they need to be
* calibrated to work correctly with the given supply voltage and the current
* temperature. The driver will perform this calibration during startup, but
* when the supply voltage or the temperature is not stable, a recalibration is
* required whenever the supply voltage of temperature has changed too much since
* the last calibration. This can be done by calling
* @ref cc110x_full_calibration. It is left to the application developer to
* perform this calibration when needed. During a test of about 2 hours of
* operation in an in-door environment with a stable temperature the CC1101 has
* worked reliable without any calibration at all (except for the automatic
* calibration at start up). So there are use cases which do not require any
* recalibration at all.
*
*
* Troubleshooting
* ===============
*
* The Driver Does Not Initialize Properly
* ---------------------------------------
* Set `ENABLE_DEBUG` in `cc110x_netdev.c` to `1` to get debug output, which
* will likely tell you what is going wrong. There are basically two things
* that can fail:
*
* Upon initialization the driver will read out the part number and version of
* the transceiver. If those do not match the ones expected for the CC1100,
* CC1100E, or the CC1101 the driver will refuse to initialize. If this fails,
* most likely incorrect values are read out and the SPI communication does not
* work correctly. However, future revisions of the CC110X transceivers might
* be produced and might have different values for the part number or version.
* If this should happen and they remain compatible with the driver, their
* part number & revision needs to be added to the driver.
*
* After uploading the configuration, the driver will read back the
* configuration to verify it. If the SPI communication is not reliable (e.g.
* sporadically bits flip), this will fail from time to time. E.g. on the
* MSB-IoT boards this is the case when the SPI interface operates at a clock of
* 5MHz, but it becomes reliable when clocked at 1MHz.
*
* The Driver Initializes, but Communication Is Impossible
* -------------------------------------------------------
* If two transceivers are too close to each other and TX power is at maximum,
* the signal is just too strong to be received correctly. Reducing TX power
* or increasing the distance (about half a meter should be fine) will solve
* this issue.
*
* While the chips can operate at any base frequency offered by the driver,
* the circuit the chip is connected to and the antenna are build for a single
* base band. Check if your configuration matches the frequency range the
* board is build for. E.g. most break out boards operate at 433MHz, but there
* are also boards for 868MHz.
*
* @{ * @{
*
* @file * @file
* @brief Public interface for cc110x driver * @brief Interface definition for the CC1100/CC1101 driver
*
* @author Marian Buschsieweke <marian.buschsieweke@ovgu.de>
* @author Oliver Hahm <oliver.hahm@inria.fr>
* @author Fabian Nack <nack@inf.fu-berlin.de>
* @author Kaspar Schleiser <kaspar@schleiser.de> * @author Kaspar Schleiser <kaspar@schleiser.de>
*/ */
#ifndef CC110X_H #ifndef CC110X_H
#define CC110X_H #define CC110X_H
#include <stdint.h>
#include "cc1xxx_common.h"
#include "mutex.h"
#include "net/gnrc/nettype.h"
#include "net/netdev.h"
#include "periph/adc.h"
#include "periph/gpio.h"
#include "periph/spi.h"
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
#include "periph/spi.h" /**
#include "periph/gpio.h" * @brief Length of a layer 2 frame
#include "cc110x-internal.h" *
#include "net/gnrc/nettype.h" * This does not include the preamble, sync word, CRC field, and length field.
*/
#define CC110X_MAX_FRAME_SIZE 0xFF
/** /**
* @brief Struct for holding cc110x IO parameters * @brief Maximum (layer 2) payload size supported by the driver
*/ */
typedef struct cc110x_params { #define CC110X_MAX_PAYLOAD_SIZE (CC110X_MAX_FRAME_SIZE - CC1XXX_HEADER_SIZE)
spi_t spi; /**< SPI bus the CC110x is connected to */
gpio_t cs; /**< GPIO connected to the chip select pin of the CC110x */ /**
gpio_t gdo0; /**< GPIO connected to the GDO0 pin of the CC110x */ * @brief Maximum number of channels supported by the driver
gpio_t gdo1; /**< GPIO connected to the GDO1 pin of the CC110x */ */
gpio_t gdo2; /**< GPIO connected to the GDO2 pin of the CC110x */ #define CC110X_MAX_CHANNELS 8
/**
* @brief Special value to indicate that layer 2 address should be derived
* from the CPU-ID
*/
#define CC110X_L2ADDR_AUTO 0x00
/**
* @brief Default protocol for data that is coming in
*/
#ifdef MODULE_GNRC_SIXLOWPAN
#define CC110X_DEFAULT_PROTOCOL (GNRC_NETTYPE_SIXLOWPAN)
#else
#define CC110X_DEFAULT_PROTOCOL (GNRC_NETTYPE_UNDEF)
#endif
/**
* @brief The state of the CC1100/CC1101 transceiver
*
* The three least significant bytes match the representation of the matching
* transceiver state given in the status byte of the hardware. See Table 32 on
* page 31 in the data sheet for the possible states in the status byte.
*/
typedef enum {
CC110X_STATE_IDLE = 0b00000000, /**< IDLE state */
/**
* @brief Frame received, waiting for upper layer to retrieve it
*
* Transceiver is in IDLE state.
*/
CC110X_STATE_FRAME_READY = 0b00001000,
/**
* @brief Frame received, waiting for upper layer to retrieve it
*
* Transceiver is in SLEEP state. There is no matching representation in the
* status byte, as reading the status byte will power up the transceiver in
* bring it in the IDLE state. Thus, we set the three least significant bits
* to the IDLE state
*/
CC110X_STATE_OFF = 0b00010000,
CC110X_STATE_RX_MODE = 0b00000001, /**< Listening for frames */
/**
* @brief Receiving a frame just now
*
* Transceiver is in RX state.
*/
CC110X_STATE_RECEIVING = 0b00001001,
CC110X_STATE_TX_MODE = 0b00000010, /**< Transmit mode */
/**
* @brief Waiting for transceiver to complete outgoing transmission
*
* Transceiver is in TX state
*/
CC110X_STATE_TX_COMPLETING = 0b00001010,
CC110X_STATE_FSTXON = 0b00000011, /**< Fast TX ready */
CC110X_STATE_CALIBRATE = 0b00000100, /**< Device is calibrating */
CC110X_STATE_SETTLING = 0b00000101, /**< PLL is settling */
CC110X_STATE_RXFIFO_OVERFLOW = 0b00000110, /**< RX FIFO overflown */
CC110X_STATE_TXFIFO_UNDERFLOW = 0b00000111, /**< TX FIFO underflown */
} cc110x_state_t;
/**
* @brief Enumeration over the possible TX power settings the driver offers
*/
typedef enum {
CC110X_TX_POWER_MINUS_30_DBM, /**< -30 dBm */
CC110X_TX_POWER_MINUS_20_DBM, /**< -20 dBm */
CC110X_TX_POWER_MINUS_15_DBM, /**< -15 dBm */
CC110X_TX_POWER_MINUS_10_DBM, /**< -10 dBm */
CC110X_TX_POWER_0_DBM, /**< 0 dBm */
CC110X_TX_POWER_PLUS_5_DBM, /**< 5 dBm */
CC110X_TX_POWER_PLUS_7_DBM, /**< 7 dBm */
CC110X_TX_POWER_PLUS_10_DBM, /**< 1 dBm */
CC110X_TX_POWER_NUMOF, /**< Number of TX power options */
} cc110x_tx_power_t;
/**
* @brief Structure that holds the PATABLE, which allows to configure the
* 8 available output power levels using a magic number for each level.
*
* See Section "24 Output Power Programming" on page 59ff in the data sheet.
* The values suggested in Table 39 on page 60 in the data sheet are already
* available by this driver, but will only be linked in (8 bytes of ROM size)
* when they are referenced.
*
* @see cc110x_patable_433mhz
* @see cc110x_patable_868mhz
* @see cc110x_patable_915mhz
*/
typedef struct {
uint8_t data[8]; /**< Magic number to store in the configuration register */
} cc110x_patable_t;
/**
* @brief Configuration of the transceiver to use
*
* @warning Two transceivers with different configurations will be unable
* to communicate.
*
* The data uploaded into configuration registers are stored in
* @ref cc110x_conf. Most of them cannot be changed, as the driver relies on
* their values. However, the base frequency, the symbol rate (which equals
* the bit rate for the chosen modulation and error correction) and the
* channel bandwidth can be configured using this data structure.
*
* Please note that while the CC1100/CC1101 chip is compatible with a huge
* frequency range (300 MHz - 928 MHz), the complete circuit is optimized to
* a narrow frequency band. So make sure the configured base frequency is within
* that frequency band that is compatible with that circuit. (Most break out
* board will operate at the 433 MHz band. In the EU the 868 MHz band would be
* more interesting, but 433 MHz is license free as well. In the USA the 915 MHz
* band is license free.
*
* Please verify that the driver is configured in a way that allows legal
* operation according to rules and laws that apply for you.
*/
typedef struct {
uint8_t base_freq[3]; /**< Base frequency to use */
/**
* @brief FSCTRL1 configuration register value that affects the
* intermediate frequency of the transceiver to use
* @note The 4 most significant bits have to be 0.
*
* Assuming a 26 MHz crystal the IF is calculated as follows (in kHz):
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* double intermediate_frequency = 26000 / 1024 * fsctrl1;
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
uint8_t fsctrl1;
/**
* @brief MDMCFG4 configuration register value that affects channel filter
* bandwidth and the data rate
*
* See page 76 in the data sheet.
*
* Assuming a 26 MHz crystal the channel filter bandwidth is calculated
* as follows (in kHz):
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* uint8_t exponent = mdmcfg4 >> 6;
* uint8_t mantissa = (mdmcfg4 >> 4) & 0x03;
* double bandwidth = 26000.0 / (8 * (4 + mantissa) * (1L << exponent));
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
uint8_t mdmcfg4;
/**
* @brief MDMCFG3 configuration register value that affects the data rate
*
* @see cc110x_config_t::mdmcfg4
*
* See page 76 in the data sheet.
*
* Assuming a 26 MHz crystal the symbol rate of the transceiver is calculated
* as fallows (in kBaud):
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* uint8_t exponent = mdmcfg4 & 0x0f;
* int32_t mantissa = mdmcfg3;
* double baudrate = (256 + mantissa) * 26000.0 / (1L << (28 - exponent));
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
uint8_t mdmcfg3;
/**
* @brief DEVIANT configuration register that affects the amount by which
* the radio frequency is shifted in FSK/GFSK modulation
*
* @see cc110x_config_t::mdmcfg4
*
* See page 79 in the data sheet.
*
* In an ideal world the channel bandwidth would be twice the channel
* deviation. In the real world the used channel bandwidth is higher.
* Assuming a 26 MHz crystal and GFSK modulation (the driver will configure
* the transceiver to use GFSK) the deviation
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* uint8_t exponent = (deviatn >> 4) & 0x07;
* int32_t mantissa = deviatn & 0x07;
* double deviation = (8 + mantissa) * 26000.0 / (1L << (17 - exponent));
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* For reliable operation at high symbol rates, the deviation has to be
* increased as well.
*/
uint8_t deviatn;
} cc110x_config_t;
/**
* @brief Structure to hold mapping between virtual and physical channel numbers
*
* This driver will provide "virtual" channel numbers 0 to 7, which will be
* translated to "physical" channel numbers before being send to the
* transceiver. This is used to overcome the following limitations:
*
* - The transceiver does not support channel maps with varying distance between
* channels. However, e.g. the LoRa channels 10 - 16 in the 868 MHz band have
* a distance of 300 kHz, but channel 16 and 17 have a distance of 1 MHz.
* - The transceiver does not supports channel distances higher than 405.46 kHz.
*
* This mapping overcomes both limitations be using 50kHz physical channel
* spacing and use the map to translate to the correct physical channel. This
* also allows to keep the same MDMCFG1 and MDMCFG0 configuration register
* values for all channel layouts. Finally, different channel sets can be
* used by different groups of IoT device in the same environment to limit
* collisions between those groups - assuming that enough non-overlapping
* channels are available.
*
* The "virtual" channel (the channel number presented to RIOT) will be used
* as index in @ref cc110x_chanmap_t::map, the value in there will give the
* corresponding "physical" channel number, or 255 if this virtual channel
* number is not available.
*/
typedef struct {
uint8_t map[CC110X_MAX_CHANNELS]; /**< "Physical" channel numbers */
} cc110x_chanmap_t;
/**
* @brief Structure holding all parameter for driver initialization
*/
typedef struct {
const cc110x_patable_t *patable; /**< Pointer to the PATABLE to use */
/**
* @brief Pointer to the configuration of the base frequency, data rate and
* channel bandwidth; or `NULL` to keep the default.
*/
const cc110x_config_t *config;
const cc110x_chanmap_t *channels; /**< Pointer to the default channel map */
spi_t spi; /**< SPI bus connected to the device */
spi_clk_t spi_clk; /**< SPI clock to use (max 6.5 MHz) */
spi_cs_t cs; /**< GPIO pin connected to chip select */
gpio_t gdo0; /**< GPIO pin connected to GDO0 */
gpio_t gdo2; /**< GPIO pin connected to GDO2 */
/**
* @brief Layer-2 address to use or `CC110X_L2ADDR_AUTO` to derive it from
* the CPU ID
*/
uint8_t l2addr;
} cc110x_params_t; } cc110x_params_t;
/** /**
* @brief Forward declaration * @brief Structure holding the calibration data of the frequency synthesizer
*/ */
typedef struct cc110x cc110x_t; typedef struct {
/**
* @brief VCO capacitance calibration, which depends on the frequency and,
* thus, has to be stored for each channel
*/
char fscal1[CC110X_MAX_CHANNELS];
char fscal2; /**< VCO current calibration, independent of channel */
char fscal3; /**< charge pump current calibration, independent of channel */
} cc110x_fs_calibration_t;
/** /**
* @brief Struct for holding cc110x device state * @brief Buffer to temporary store incoming/outgoing packet
*
* The CC1100/CC1101 transceiver's FIFO sadly is only 64 bytes in size. To
* support frames bigger than that, chunks of the frame have to be
* transferred between the MCU and the CC1100/CC1101 transceiver while the
* frame is in transit.
*/ */
struct cc110x { typedef struct __attribute__((packed)) {
cc110x_params_t params; /**< cc110x IO configuration */ uint8_t len; /**< Length of the frame in bytes */
/**
cc110x_statistic_t cc110x_statistic; /**< Statistic values for * @brief The payload data of the frame
debugging */ */
uint8_t data[CC110X_MAX_FRAME_SIZE];
uint8_t radio_state; /**< Radio state */ /**
uint8_t radio_channel; /**< current Radio channel */ * @brief Index of the next @ref cc110x_framebuf_t::data element to transfer
uint8_t radio_address; /**< current Radio address */ *
* In RX mode: Index of the next @ref cc110x_framebuf_t::data element to store
cc110x_pkt_buf_t pkt_buf; /**< RX/TX buffer */ * data read from the RX-FIFO into.
void (*isr_cb)(cc110x_t *dev, void* arg); /**< isr callback */ *
void *isr_cb_arg; /**< isr callback argument */ * In TX mode: Index of the next @ref cc110x_framebuf_t::data element to write
#ifdef MODULE_GNRC_NETIF * to the TX-FIFO.
gnrc_nettype_t proto; /**< protocol the radio expects */ */
#endif uint8_t pos;
}; } cc110x_framebuf_t;
/** /**
* @brief Setup cc110x device parameters * @brief Device descriptor for CC1100/CC1101 transceivers
*/
typedef struct {
netdev_t netdev; /**< RIOT's interface to this driver */
uint8_t addr; /**< Layer 2 address of this device */
/* Keep above in sync with cc1xx_t members, as they must overlap! */
cc110x_state_t state; /**< State of the transceiver */
cc110x_tx_power_t tx_power; /**< TX power of the receiver */
uint8_t channel; /**< Currently tuned (virtual) channel */
/* Struct packing: addr, state, tx_power and channel add up to 32 bit */
const cc110x_chanmap_t *channels; /**< Pointer to the channel map to use. */
cc110x_params_t params; /**< Configuration of the driver */
cc110x_framebuf_t buf; /**< Temporary frame buffer */
/**
* @brief RSSI and LQI of the last received frame
*/
cc1xxx_rx_info_t rx_info;
/**
* @brief Frequency synthesizer calibration data
*/
cc110x_fs_calibration_t fscal;
/**
* @brief Use mutex to block during TX and unblock from ISR when ISR
* needs to be handled from thread-context
* *
* @param[in] dev device struct to set up * Blocking during TX within the driver prevents the upper layers from
* @param[in] params struct holding parameters * calling @ref netdev_driver_t::send while already transmitting a frame.
*/
mutex_t isr_signal;
uint8_t rssi_offset; /**< dBm to subtract from raw RSSI data */
} cc110x_t;
/**
* @brief Setup the CC1100/CC1101 driver, but perform no initialization
* *
* @return always succeeds * @ref netdev_driver_t::init can be used after this call to initialize the
* transceiver.
*
* @param dev Device descriptor to use
* @param params Parameter of the device to setup
*
* @retval 0 Device successfully set up
* @retval -EINVAL @p dev or @p params is `NULL`, or @p params is invalid
*/ */
int cc110x_setup(cc110x_t *dev, const cc110x_params_t *params); int cc110x_setup(cc110x_t *dev, const cc110x_params_t *params);
/** /**
* @brief Set cc110x channel number * @brief Apply the given configuration and the given channel map and performs
* a recalibration
* *
* @param[in] dev device to work on * @param dev Device descriptor of the transceiver
* @param[in] channr guess what * @param conf Configuration to apply or `NULL` to only change channel map
* @param chanmap Channel map to apply (must be compatible with @p conf)
* *
* @return nr of set channel on success * @retval 0 Success
* @return -1 on error * @retval -EINVAL Called with invalid argument
* @retval -EIO Communication with the transceiver failed
*
* @pre The application developer checked in the documentation that the channel
* map in @p chanmap is compatible with the configuration in @p conf
*
* Because the configuration (potentially) changes the channel bandwidth, the
* old channel map is rendered invalid. This API therefore asks for both to make
* sure an application developer does not forget to update the channel map.
* Because the old calibration data is also rendered invalid,
* @ref cc110x_full_calibration is called to update it.
*/ */
int16_t cc110x_set_channel(cc110x_t *dev, uint8_t channr); int cc110x_apply_config(cc110x_t *dev, const cc110x_config_t *conf,
const cc110x_chanmap_t *chanmap);
/** /**
* @brief Send raw cc110x packet * @brief Perform a calibration of the frequency generator for each supported
* channel
* *
* @param[in] dev Device to send on * @param dev Device descriptor of the transceiver
* @param[in] packet ptr to packet to be sent
* *
* @return size of packet on success * @retval 0 Success
* @return <0 on error * @retval -EINVAL Called with invalid argument
* @retval -EAGAIN Current state prevents deliberate calibration
* @retval -EIO Communication with the transceiver failed
*
* Tunes in each supported channel and calibrates the transceiver. The
* calibration data is stored so that @ref cc110x_set_channel can skip the
* calibration phase and use the stored calibration data instead.
*/ */
int cc110x_send(cc110x_t *dev, cc110x_pkt_t *packet); int cc110x_full_calibration(cc110x_t *dev);
/** /**
* @brief Set cc110x radio address * @brief Hops to the specified channel
* *
* @param[in] dev device to query * @param dev Device descriptor of the transceiver
* @param channel Channel to hop to
* *
* @return nr of currently set address * @retval 0 Success
* @retval -EINVAL Called with `NULL` as @p dev
* @retval -ERANGE Channel out of range or not supported by channel map
* @retval -EAGAIN Currently in a state that does not allow hopping, e.g.
* sending/receiving a packet, calibrating or handling
* transmission errors
* @retval -EIO Communication with the transceiver failed
*
* This function implements the fact channel hopping approach outlined in
* section 28.2 on page 64 in the data sheet, which skips the calibration phase
* by storing the calibration date for each channel in the driver.
*/ */
uint8_t cc110x_get_address(cc110x_t *dev); int cc110x_set_channel(cc110x_t *dev, uint8_t channel);
/** /**
* @brief Set cc110x radio address * @brief Set the TX power to the specified value
* *
* @param[in] dev device to work on * @param dev Device descriptor of the transceiver
* @param[in] address new address * @param power Output power to apply
* *
* @return address set on success * @retval 0 Success
* @return 0 on error * @retval -EINVAL Called with `NULL` as @p dev
* @retval -ERANGE Called with an invalid value for @p power
* @retval -EAGAIN Changing the TX power is in the current state not possible
* @retval -EIO Communication with the transceiver failed
*/ */
uint8_t cc110x_set_address(cc110x_t *dev, uint8_t address); int cc110x_set_tx_power(cc110x_t *dev, cc110x_tx_power_t power);
/**
* @brief Set cc110x monitor mode setting
*
* @param[in] dev device to work on
* @param[in] mode mode to set (0 or 1)
*/
void cc110x_set_monitor(cc110x_t *dev, uint8_t mode);
#ifdef __cplusplus #ifdef __cplusplus
} }

View File

@ -38,10 +38,10 @@ USEMODULE += ps
USEMODULE += saul_default USEMODULE += saul_default
BOARD_PROVIDES_NETIF := acd52832 airfy-beacon b-l072z-lrwan1 cc2538dk fox \ BOARD_PROVIDES_NETIF := acd52832 airfy-beacon b-l072z-lrwan1 cc2538dk fox \
iotlab-m3 iotlab-a8-m3 lobaro-lorabox lsn50 mulle microbit native nrf51dk \ iotlab-m3 iotlab-a8-m3 lobaro-lorabox lsn50 mulle microbit msba2 \
nrf51dongle nrf52dk nrf52840dk nrf52840-mdk nrf6310 nucleo-f767zi \ native nrf51dk nrf51dongle nrf52dk nrf52840dk nrf52840-mdk nrf6310 \
openmote-cc2538 pba-d-01-kw2x remote-pa remote-reva samr21-xpro \ nucleo-f767zi openmote-cc2538 pba-d-01-kw2x remote-pa remote-reva \
spark-core telosb yunjia-nrf51822 z1 samr21-xpro spark-core telosb yunjia-nrf51822 z1
ifneq (,$(filter $(BOARD),$(BOARD_PROVIDES_NETIF))) ifneq (,$(filter $(BOARD),$(BOARD_PROVIDES_NETIF)))
# Use modules for networking # Use modules for networking
@ -71,10 +71,6 @@ endif
include $(RIOTBASE)/Makefile.include include $(RIOTBASE)/Makefile.include
# Set a custom channel if needed # Set a custom channel if needed
ifneq (,$(filter cc110x,$(USEMODULE))) # radio is cc110x sub-GHz
DEFAULT_CHANNEL ?= 0
CFLAGS += -DCC110X_DEFAULT_CHANNEL=$(DEFAULT_CHANNEL)
else
ifneq (,$(filter at86rf212b,$(USEMODULE))) # radio is IEEE 802.15.4 sub-GHz ifneq (,$(filter at86rf212b,$(USEMODULE))) # radio is IEEE 802.15.4 sub-GHz
DEFAULT_CHANNEL ?= 5 DEFAULT_CHANNEL ?= 5
CFLAGS += -DIEEE802154_DEFAULT_SUBGHZ_CHANNEL=$(DEFAULT_CHANNEL) CFLAGS += -DIEEE802154_DEFAULT_SUBGHZ_CHANNEL=$(DEFAULT_CHANNEL)
@ -82,4 +78,3 @@ else
DEFAULT_CHANNEL ?= 26 DEFAULT_CHANNEL ?= 26
CFLAGS += -DIEEE802154_DEFAULT_CHANNEL=$(DEFAULT_CHANNEL) CFLAGS += -DIEEE802154_DEFAULT_CHANNEL=$(DEFAULT_CHANNEL)
endif endif
endif

View File

@ -116,10 +116,6 @@ host-tools:
$(Q)env -u CC -u CFLAGS make -C $(RIOTTOOLS) $(Q)env -u CC -u CFLAGS make -C $(RIOTTOOLS)
# Set a custom channel if needed # Set a custom channel if needed
ifneq (,$(filter cc110x,$(USEMODULE))) # radio is cc110x sub-GHz
DEFAULT_CHANNEL ?= 0
CFLAGS += -DCC110X_DEFAULT_CHANNEL=$(DEFAULT_CHANNEL)
else
ifneq (,$(filter at86rf212b,$(USEMODULE))) # radio is IEEE 802.15.4 sub-GHz ifneq (,$(filter at86rf212b,$(USEMODULE))) # radio is IEEE 802.15.4 sub-GHz
DEFAULT_CHANNEL ?= 5 DEFAULT_CHANNEL ?= 5
CFLAGS += -DIEEE802154_DEFAULT_SUBGHZ_CHANNEL=$(DEFAULT_CHANNEL) CFLAGS += -DIEEE802154_DEFAULT_SUBGHZ_CHANNEL=$(DEFAULT_CHANNEL)
@ -127,4 +123,3 @@ else
DEFAULT_CHANNEL ?= 26 DEFAULT_CHANNEL ?= 26
CFLAGS += -DIEEE802154_DEFAULT_CHANNEL=$(DEFAULT_CHANNEL) CFLAGS += -DIEEE802154_DEFAULT_CHANNEL=$(DEFAULT_CHANNEL)
endif endif
endif

View File

@ -36,10 +36,6 @@ QUIET ?= 1
include $(RIOTBASE)/Makefile.include include $(RIOTBASE)/Makefile.include
# Set a custom channel if needed # Set a custom channel if needed
ifneq (,$(filter cc110x,$(USEMODULE))) # radio is cc110x sub-GHz
DEFAULT_CHANNEL ?= 0
CFLAGS += -DCC110X_DEFAULT_CHANNEL=$(DEFAULT_CHANNEL)
else
ifneq (,$(filter at86rf212b,$(USEMODULE))) # radio is IEEE 802.15.4 sub-GHz ifneq (,$(filter at86rf212b,$(USEMODULE))) # radio is IEEE 802.15.4 sub-GHz
DEFAULT_CHANNEL ?= 5 DEFAULT_CHANNEL ?= 5
CFLAGS += -DIEEE802154_DEFAULT_SUBGHZ_CHANNEL=$(DEFAULT_CHANNEL) CFLAGS += -DIEEE802154_DEFAULT_SUBGHZ_CHANNEL=$(DEFAULT_CHANNEL)
@ -47,4 +43,3 @@ else
DEFAULT_CHANNEL ?= 26 DEFAULT_CHANNEL ?= 26
CFLAGS += -DIEEE802154_DEFAULT_CHANNEL=$(DEFAULT_CHANNEL) CFLAGS += -DIEEE802154_DEFAULT_CHANNEL=$(DEFAULT_CHANNEL)
endif endif
endif

View File

@ -65,10 +65,6 @@ QUIET ?= 1
include $(RIOTBASE)/Makefile.include include $(RIOTBASE)/Makefile.include
# Set a custom channel if needed # Set a custom channel if needed
ifneq (,$(filter cc110x,$(USEMODULE))) # radio is cc110x sub-GHz
DEFAULT_CHANNEL ?= 0
CFLAGS += -DCC110X_DEFAULT_CHANNEL=$(DEFAULT_CHANNEL)
else
ifneq (,$(filter at86rf212b,$(USEMODULE))) # radio is IEEE 802.15.4 sub-GHz ifneq (,$(filter at86rf212b,$(USEMODULE))) # radio is IEEE 802.15.4 sub-GHz
DEFAULT_CHANNEL ?= 5 DEFAULT_CHANNEL ?= 5
CFLAGS += -DIEEE802154_DEFAULT_SUBGHZ_CHANNEL=$(DEFAULT_CHANNEL) CFLAGS += -DIEEE802154_DEFAULT_SUBGHZ_CHANNEL=$(DEFAULT_CHANNEL)
@ -76,4 +72,3 @@ else
DEFAULT_CHANNEL ?= 26 DEFAULT_CHANNEL ?= 26
CFLAGS += -DIEEE802154_DEFAULT_CHANNEL=$(DEFAULT_CHANNEL) CFLAGS += -DIEEE802154_DEFAULT_CHANNEL=$(DEFAULT_CHANNEL)
endif endif
endif

View File

@ -61,10 +61,6 @@ QUIET ?= 1
include $(RIOTBASE)/Makefile.include include $(RIOTBASE)/Makefile.include
# Set a custom channel if needed # Set a custom channel if needed
ifneq (,$(filter cc110x,$(USEMODULE))) # radio is cc110x sub-GHz
DEFAULT_CHANNEL ?= 0
CFLAGS += -DCC110X_DEFAULT_CHANNEL=$(DEFAULT_CHANNEL)
else
ifneq (,$(filter at86rf212b,$(USEMODULE))) # radio is IEEE 802.15.4 sub-GHz ifneq (,$(filter at86rf212b,$(USEMODULE))) # radio is IEEE 802.15.4 sub-GHz
DEFAULT_CHANNEL ?= 5 DEFAULT_CHANNEL ?= 5
CFLAGS += -DIEEE802154_DEFAULT_SUBGHZ_CHANNEL=$(DEFAULT_CHANNEL) CFLAGS += -DIEEE802154_DEFAULT_SUBGHZ_CHANNEL=$(DEFAULT_CHANNEL)
@ -72,4 +68,3 @@ else
DEFAULT_CHANNEL ?= 26 DEFAULT_CHANNEL ?= 26
CFLAGS += -DIEEE802154_DEFAULT_CHANNEL=$(DEFAULT_CHANNEL) CFLAGS += -DIEEE802154_DEFAULT_CHANNEL=$(DEFAULT_CHANNEL)
endif endif
endif

View File

@ -97,6 +97,11 @@ PSEUDOMODULES += adc121c
# full featured version of CCS811 driver as pseudo module # full featured version of CCS811 driver as pseudo module
PSEUDOMODULES += ccs811_full PSEUDOMODULES += ccs811_full
# include variants of CC110X drivers as pseudo modules
PSEUDOMODULES += cc1100
PSEUDOMODULES += cc1100e
PSEUDOMODULES += cc1101
# include variants of SX127X drivers as pseudo modules # include variants of SX127X drivers as pseudo modules
PSEUDOMODULES += sx1272 PSEUDOMODULES += sx1272
PSEUDOMODULES += sx1276 PSEUDOMODULES += sx1276

View File

@ -206,6 +206,11 @@ void auto_init(void)
auto_init_mrf24j40(); auto_init_mrf24j40();
#endif #endif
#ifdef MODULE_CC110X
extern void auto_init_cc110x(void);
auto_init_cc110x();
#endif
#ifdef MODULE_CC2420 #ifdef MODULE_CC2420
extern void auto_init_cc2420(void); extern void auto_init_cc2420(void);
auto_init_cc2420(); auto_init_cc2420();
@ -248,11 +253,6 @@ void auto_init(void)
auto_init_slipdev(); auto_init_slipdev();
#endif #endif
#ifdef MODULE_CC110X
extern void auto_init_cc110x(void);
auto_init_cc110x();
#endif
#ifdef MODULE_CC2538_RF #ifdef MODULE_CC2538_RF
extern void auto_init_cc2538_rf(void); extern void auto_init_cc2538_rf(void);
auto_init_cc2538_rf(); auto_init_cc2538_rf();

View File

@ -1,5 +1,6 @@
/* /*
* Copyright (C) 2015 Kaspar Schleiser <kaspar@schleiser.de> * Copyright (C) 2015 Kaspar Schleiser <kaspar@schleiser.de>
* 2016 Freie Universität Berlin
* *
* This file is subject to the terms and conditions of the GNU Lesser * 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 * General Public License v2.1. See the file LICENSE in the top level
@ -7,7 +8,7 @@
* *
*/ */
/* /**
* @ingroup sys_auto_init_gnrc_netif * @ingroup sys_auto_init_gnrc_netif
* @{ * @{
* *
@ -15,54 +16,70 @@
* @brief Auto initialization for cc110x network interfaces * @brief Auto initialization for cc110x network interfaces
* *
* @author Kaspar Schleiser <kaspar@schleiser.de> * @author Kaspar Schleiser <kaspar@schleiser.de>
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
*/ */
#ifdef MODULE_CC110X #ifdef MODULE_CC110X
#include "log.h"
#include "debug.h"
#include "board.h"
#include "gnrc_netif_cc110x.h"
#include "cc110x-netdev.h"
#include "net/gnrc.h"
#include "cc110x.h" #include "cc110x.h"
#include "cc1xxx_common.h"
#include "cc110x_params.h" #include "cc110x_params.h"
#include "log.h"
#include "msg.h"
#include "net/gnrc/netif/conf.h" /* <- GNRC_NETIF_MSG_QUEUE_SIZE */
#define ENABLE_DEBUG (0)
#include "debug.h"
#ifndef CC110X_EXTRA_STACKSIZE
/**
* @brief Additional stack size required by the driver
*
* With increasing of GNRC_NETIF_MSG_QUEUE_SIZE the required stack size
* increases as well. A queue size of 8 messages works with default stack size,
* so we increase the stack by `sizeof(msg_t)` for each additional element
*/
#define CC110X_EXTRA_STACKSIZE ((GNRC_NETIF_MSG_QUEUE_SIZE - 8) * sizeof(msg_t))
#endif
/** /**
* @brief Define stack parameters for the MAC layer thread * @brief Calculate the stack size for the MAC layer thread(s)
* @{
*/ */
#define CC110X_MAC_STACKSIZE (THREAD_STACKSIZE_DEFAULT + DEBUG_EXTRA_STACKSIZE) #define CC110X_MAC_STACKSIZE (THREAD_STACKSIZE_DEFAULT + \
CC110X_EXTRA_STACKSIZE + \
DEBUG_EXTRA_STACKSIZE)
#ifndef CC110X_MAC_PRIO #ifndef CC110X_MAC_PRIO
/**
* @brief The priority of the MAC layer thread
*/
#define CC110X_MAC_PRIO (GNRC_NETIF_PRIO) #define CC110X_MAC_PRIO (GNRC_NETIF_PRIO)
#endif #endif
/**
* @brief Calculate the number of configured CC1100/CC1101 transceivers
*/
#define CC110X_NUM ARRAY_SIZE(cc110x_params) #define CC110X_NUM ARRAY_SIZE(cc110x_params)
static netdev_cc110x_t cc110x_devs[CC110X_NUM]; /**
static char _stacks[CC110X_NUM][CC110X_MAC_STACKSIZE]; * @brief Statically allocate memory for device descriptors
*/
cc110x_t _cc110x_devs[CC110X_NUM];
/**
* @brief Statically allocate memory for the MAC layer thread(s)
*/
static char stacks[CC110X_NUM][CC110X_MAC_STACKSIZE];
void auto_init_cc110x(void) void auto_init_cc110x(void)
{ {
for (unsigned i = 0; i < CC110X_NUM; i++) { for (unsigned i = 0; i < CC110X_NUM; i++) {
const cc110x_params_t *p = &cc110x_params[i];
LOG_DEBUG("[auto_init_netif] initializing cc110x #%u\n", i); LOG_DEBUG("[auto_init_netif] initializing cc110x #%u\n", i);
int res = netdev_cc110x_setup(&cc110x_devs[i], p); cc110x_setup(&_cc110x_devs[i], &cc110x_params[i]);
if (res < 0) { gnrc_netif_cc1xxx_create(stacks[i], CC110X_MAC_STACKSIZE, CC110X_MAC_PRIO,
LOG_ERROR("[auto_init_netif] error initializing cc110x #%u\n", i); "cc110x", (netdev_t *)&_cc110x_devs[i]);
}
else {
gnrc_netif_cc110x_create(_stacks[i], CC110X_MAC_STACKSIZE,
CC110X_MAC_PRIO, "cc110x",
(netdev_t *)&cc110x_devs[i]);
}
} }
} }
#else #else
typedef int dont_be_pedantic; typedef int dont_be_pedantic;
#endif /* MODULE_CC110X */ #endif /* MODULE_CC110X */
/** @} */ /** @} */

View File

@ -0,0 +1,57 @@
BOARD ?= msba2
include ../Makefile.tests_common
DEVICE ?= cc1100 # The MSB-A2 uses the CC1100. New boards use CC1101
BOARD_INSUFFICIENT_MEMORY += arduino-duemilanove
BOARD_INSUFFICIENT_MEMORY += arduino-leonardo
BOARD_INSUFFICIENT_MEMORY += arduino-mega2560
BOARD_INSUFFICIENT_MEMORY += arduino-nano
BOARD_INSUFFICIENT_MEMORY += arduino-uno
BOARD_INSUFFICIENT_MEMORY += blackpill
BOARD_INSUFFICIENT_MEMORY += bluepill
BOARD_INSUFFICIENT_MEMORY += i-nucleo-lrwan1
BOARD_INSUFFICIENT_MEMORY += mega-xplained
BOARD_INSUFFICIENT_MEMORY += nucleo-f031k6
BOARD_INSUFFICIENT_MEMORY += nucleo-f042k6
BOARD_INSUFFICIENT_MEMORY += nucleo-f072rb
BOARD_INSUFFICIENT_MEMORY += nucleo-f302r8
BOARD_INSUFFICIENT_MEMORY += nucleo-f303k8
BOARD_INSUFFICIENT_MEMORY += nucleo-f334r8
BOARD_INSUFFICIENT_MEMORY += nucleo-l031k6
BOARD_INSUFFICIENT_MEMORY += nucleo-l053r8
BOARD_INSUFFICIENT_MEMORY += saml10-xpro
BOARD_INSUFFICIENT_MEMORY += saml11-xpro
BOARD_INSUFFICIENT_MEMORY += stm32f0discovery
BOARD_INSUFFICIENT_MEMORY += stm32l0538-disco
BOARD_INSUFFICIENT_MEMORY += waspmote-pro
# stdlib.h for msp430 does not provide EXIT_FAILURE and EXIT_SUCCESS
BOARD_BLACKLIST += msb-430 msb-430h telosb wsn430-v1_3b wsn430-v1_4 z1
# This test will rely on a human interacting with the shell, so we better add
# the shell and some commands
USEMODULE += shell
USEMODULE += shell_commands
# Add the device driver
USEMODULE += $(DEVICE)
# Add a network stack and auto init of the network devices
USEMODULE += gnrc
USEMODULE += auto_init_gnrc_netif
USEMODULE += gnrc_ipv6_router_default
# checking current state of network threads can be useful for testing/debuggig
USEMODULE += ps
# txtsnd can be useful to test lower layers
USEMODULE += gnrc_txtsnd
USEMODULE += auto_init_gnrc_netif
# Activate ICMPv6 error messages
USEMODULE += gnrc_icmpv6_error
# This application dumps received packets to STDIO using the pktdump module
USEMODULE += gnrc_pktdump
# Additional networking modules that can be dropped if not needed
USEMODULE += gnrc_icmpv6_echo
# Some statistics could also be helpful
USEMODULE += netstats_l2
USEMODULE += netstats_ipv6
include $(RIOTBASE)/Makefile.include

134
tests/driver_cc110x/main.c Normal file
View File

@ -0,0 +1,134 @@
/*
* Copyright (C) 2019 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 tests
* @{
*
* @file
* @brief Test application for the CC110x driver
*
* @author Marian Buschsieweke <marian.buschsieweke@ovgu.de>
*
* @}
*/
#include <stdio.h>
#include <string.h>
#include "shell.h"
#include "shell_commands.h"
#include "net/gnrc/pktdump.h"
#include "net/gnrc.h"
#define MAIN_QUEUE_SIZE (8)
static int sc_dump(int argc, char **argv);
int sc_cc110x(int argc, char **argv);
static const shell_command_t shell_commands[] = {
{ "dump", "Enable/disable dumping of frames", sc_dump },
{ "cc110x", "Print the low level state of an CC110x device", sc_cc110x },
{ NULL, NULL, NULL }
};
static msg_t _main_msg_queue[MAIN_QUEUE_SIZE];
static gnrc_netreg_entry_t dump;
static int sc_dump(int argc, char **argv)
{
static int is_enabled = 0;
if (argc == 1) {
if (is_enabled) {
puts("Currently dumping packets");
}
else {
puts("Currently NOT dumping packets");
}
return 0;
}
else if (argc == 2) {
int new_state = 0;
if (!strcmp("y", argv[1])) {
new_state = 1;
}
else if (!strcmp("n", argv[1])) {
new_state = 0;
}
else {
printf("Usage: %s [y/n]\n", argv[0]);
return 0;
}
if (new_state == is_enabled) {
// Nothing to do;
return 0;
}
if (new_state) {
if (gnrc_netreg_register(GNRC_NETTYPE_SIXLOWPAN, &dump)) {
puts("Failed to register packet dumping");
}
}
else {
gnrc_netreg_unregister(GNRC_NETTYPE_SIXLOWPAN, &dump);
}
is_enabled = new_state;
return 0;
}
printf("Usage: %s [y/n]\n", argv[0]);
return 0;
}
int main(void)
{
/* we need a message queue for the thread running the shell in order to
* receive potentially fast incoming networking packets */
msg_init_queue(_main_msg_queue, MAIN_QUEUE_SIZE);
gnrc_netreg_entry_init_pid(&dump, GNRC_NETREG_DEMUX_CTX_ALL,
gnrc_pktdump_pid);
puts("cc110x driver test application\n"
"==============================\n"
"\n"
"Use the shell and two boards equipped with an CC1100/CC1101\n"
"transceiver to test the driver. Common testing tasks:\n"
"\n"
"- Using \"ifconfig\":\n"
" - Check the information stated for plausibility/correctness\n"
" - Try to get/set parameters like TX power, channel, address, ...\n"
" - BEWARE: With short communication distances (<=1m) for boards\n"
" with high gain antennas a high TX power may result in packet\n"
" loss: The incoming signal can only be demodulated when the\n"
" input signal is at most +10 dBm on the CC1101.\n"
" - Check the statistics for correctness/plausibility (after\n"
" sending frames using \"txtsnd\" or \"ping6\")\n"
"- Using \"ping6\":\n"
" - Does the other device respond to the ping?\n"
" - Does the measured RSSI increase when the nodes are closer\n"
" together?\n"
" - Try to increase the size of the pings, so that the TX/RX FIFO\n"
" needs to be filled/drain more than once per frame. The TX/RX\n"
" FIFO can hold 64 bytes\n"
" - Try to increase the size of the pings in order to trigger L2\n"
" fragmentation. The driver supports frames of up to 255 bytes\n"
"- Using \"txtsnd\":\n"
" - Turn on packet dumping using the command \"dump y\" on node A\n"
" - Send both unicast and broadcast frame from node B to A\n"
"- Using \"cc110x\":\n"
" - This tool will print low level details for all CC110x devices\n"
" attached\n"
" - This will be mostly useful for debugging, not for testing\n");
char line_buf[SHELL_DEFAULT_BUFSIZE];
shell_run(shell_commands, line_buf, SHELL_DEFAULT_BUFSIZE);
return 0;
}

View File

@ -0,0 +1,208 @@
/*
* Copyright (C) 2019 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.
*/
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "cc110x.h"
#include "cc110x_internal.h"
#include "cc110x_params.h"
#define CC110X_NUM (sizeof(cc110x_params) / sizeof(cc110x_params[0]))
extern cc110x_t _cc110x_devs[CC110X_NUM];
static const char *pa_settings[] = {
"-30",
"-20",
"-15",
"-10",
"0",
"+5",
"+7",
"+10",
};
static const char *state2s(cc110x_state_t state)
{
switch (state) {
case CC110X_STATE_IDLE:
return "IDLE";
case CC110X_STATE_FRAME_READY:
return "Frame ready";
case CC110X_STATE_OFF:
return "Off";
case CC110X_STATE_RX_MODE:
return "RX mode";
case CC110X_STATE_RECEIVING:
return "RX mode and currently receiving frame";
case CC110X_STATE_TX_MODE:
return "TX";
case CC110X_STATE_TX_COMPLETING:
return "TX completing";
case CC110X_STATE_FSTXON:
return "Fast TX on";
case CC110X_STATE_CALIBRATE:
return "Calibrating";
case CC110X_STATE_SETTLING:
return "Settling";
case CC110X_STATE_RXFIFO_OVERFLOW:
return "RX FIFO overflow";
case CC110X_STATE_TXFIFO_UNDERFLOW:
return "TX FIFO underflow";
}
return "Unknown";
}
static const char *gdoconf2s(uint8_t conf)
{
switch (conf) {
case CC110X_GDO_ON_RX_DATA:
return "High when frame received or RX FIFO needs draining";
case CC110X_GDO_ON_TX_DATA:
return "Low when TX FIFO needs refilling";
case CC110X_GDO_ON_TRANSMISSION:
return "High while frame is incoming / outgoing";
case CC110X_GDO_ON_CHANNEL_CLEAR:
return "High when channel is clear";
case CC110X_GDO_ON_PLL_IN_LOCK:
return "High when frequency generator is on and PLL is in lock";
case CC110X_GDO_CONSTANT_LOW:
return "Constant low";
case CC110X_GDO_CONSTANT_HIGH:
return "Constant high";
case CC110X_GDO0_ANALOG_TEMPERATURE:
return "Analog output of the temperature sensor (GDO0 only)";
}
return "Unknown";
}
static inline const char *b2s(int boolean)
{
return (boolean) ? "1" : "0";
}
static void print_state(cc110x_t *dev)
{
uint8_t status;
uint8_t pktstatus;
int8_t rssi_raw;
uint8_t iocfg2;
uint8_t iocfg0;
uint8_t txbytes;
uint8_t rxbytes;
uint8_t hwaddr;
uint8_t frend0;
uint8_t physical_channel;
uint8_t virtual_channel;
/* Get all required data and release device */
if (cc110x_acquire(dev) != SPI_OK) {
puts("Failed to acquire CC1100/CC1101 transceiver");
return;
}
/* Reading out the RSSI changes it, as SPI communication seems to generate
* some noise. Reading the RSSI out first yields up to 20 dBm lower
* values... (E.g. about -100dBm instead of about -80dBm with no
* other sources of Sub-GHz RF)
*/
status = cc110x_read_reliable(dev, CC110X_REG_RSSI, (uint8_t *)&rssi_raw);
cc110x_read_reliable(dev, CC110X_REG_PKTSTATUS, &pktstatus);
cc110x_read(dev, CC110X_REG_IOCFG2, &iocfg2);
cc110x_read(dev, CC110X_REG_IOCFG0, &iocfg0);
cc110x_read(dev, CC110X_REG_ADDR, &hwaddr);
cc110x_read(dev, CC110X_REG_FREND0, &frend0);
cc110x_read(dev, CC110X_REG_CHANNR, &physical_channel);
virtual_channel = dev->channel;
cc110x_read_reliable(dev, CC110X_REG_TXBYTES, &txbytes);
cc110x_read_reliable(dev, CC110X_REG_RXBYTES, &rxbytes);
cc110x_state_t sw_state = dev->state;
cc110x_release(dev);
/* Parse obtained raw data */
cc110x_state_t hw_state = (status >> 4) & 0x03;
int ready = !(status & 0x80);
int rssi = ((int)rssi_raw / 2) - (int)dev->rssi_offset;
int gdo0 = pktstatus & CC110X_PKTSTATUS_GDO0;
int gdo2 = pktstatus & CC110X_PKTSTATUS_GDO2;
int recv = pktstatus & CC110X_PKTSTATUS_RECEIVING;
int cca = pktstatus & CC110X_PKTSTATUS_CCA;
int cs = pktstatus & CC110X_PKTSTATUS_CS;
const char *pa = pa_settings[frend0 & 0x07];
/* Print all information */
if (!ready) {
puts(" CRITICAL: Crystal has not stabilized yet!");
}
printf(" GDO0: %s (%s)\n", b2s(gdo0), gdoconf2s(iocfg0));
printf(" GDO2: %s (%s)\n", b2s(gdo2), gdoconf2s(iocfg2));
printf(" Receiving: %s, CCA: %s, CS: %s\n", b2s(recv), b2s(cca), b2s(cs));
printf(" RSSI: %i.%sdBm, TX power: %sdBm\n",
rssi, (rssi_raw & 1) ? "5" : "0", pa);
printf(" L2 addr: %02x, physical channel: %u, virtual channel: %u\n",
(int)hwaddr, (unsigned)physical_channel, (unsigned)virtual_channel);
if (dev->channels->map[virtual_channel] != physical_channel) {
puts(" WARNING: Physical channel does not match channel map");
}
printf(" Driver state: %s, transceiver state: %s\n",
state2s(sw_state), state2s(hw_state));
if (hw_state != (sw_state & 0x07)) {
puts(" WARNING: Transceiver and device state don't match!");
}
if (0x80 & txbytes) {
puts(" TX FIFO: Underflown!");
}
else {
printf(" TX FIFO: %iB\n", (int)txbytes);
}
if (0x80 & rxbytes) {
puts(" RX FIFO: Overflown!");
}
else {
printf(" RX FIFO: %iB\n", (int)rxbytes);
}
}
int sc_cc110x(int argc, char **argv)
{
switch (argc) {
case 1:
for (unsigned i = 0; i < CC110X_NUM; i++){
printf("CC110x #%u:\n", i);
print_state(&_cc110x_devs[i]);
}
break;
case 2:
if ((!strcmp(argv[1], "-h")) || (!strcmp(argv[1], "--help"))) {
printf("Usage: %s [NUM]\n"
"\n"
"Prints the status of the CC1100/CC1101 transceiver "
"identified by NUM, or of\n"
"all available CC110x transceivers if no argument is "
"given\n", argv[0]);
}
else {
unsigned pos = atoi(argv[1]);
if (pos >= CC110X_NUM) {
puts("No such transceiver");
return EXIT_FAILURE;
}
printf("CC110x #%u:\n", pos);
print_state(&_cc110x_devs[pos]);
}
break;
default:
printf("Usage: %s [NUM]\n", argv[0]);
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}

View File

@ -31,10 +31,6 @@ USEMODULE += netstats_ipv6
include $(RIOTBASE)/Makefile.include include $(RIOTBASE)/Makefile.include
# Set a custom channel if needed # Set a custom channel if needed
ifneq (,$(filter cc110x,$(USEMODULE))) # radio is cc110x sub-GHz
DEFAULT_CHANNEL ?= 0
CFLAGS += -DCC110X_DEFAULT_CHANNEL=$(DEFAULT_CHANNEL)
else
ifneq (,$(filter at86rf212b,$(USEMODULE))) # radio is IEEE 802.15.4 sub-GHz ifneq (,$(filter at86rf212b,$(USEMODULE))) # radio is IEEE 802.15.4 sub-GHz
DEFAULT_CHANNEL ?= 5 DEFAULT_CHANNEL ?= 5
CFLAGS += -DIEEE802154_DEFAULT_SUBGHZ_CHANNEL=$(DEFAULT_CHANNEL) CFLAGS += -DIEEE802154_DEFAULT_SUBGHZ_CHANNEL=$(DEFAULT_CHANNEL)
@ -42,4 +38,3 @@ else
DEFAULT_CHANNEL ?= 26 DEFAULT_CHANNEL ?= 26
CFLAGS += -DIEEE802154_DEFAULT_CHANNEL=$(DEFAULT_CHANNEL) CFLAGS += -DIEEE802154_DEFAULT_CHANNEL=$(DEFAULT_CHANNEL)
endif endif
endif

View File

@ -2,7 +2,7 @@ include ../Makefile.tests_common
BOARD_PROVIDES_NETIF := airfy-beacon fox iotlab-m3 mulle native nrf51dk nrf51dongle \ BOARD_PROVIDES_NETIF := airfy-beacon fox iotlab-m3 mulle native nrf51dk nrf51dongle \
nrf6310 pba-d-01-kw2x samd21-xpro saml21-xpro samr21-xpro spark-core \ nrf6310 pba-d-01-kw2x samd21-xpro saml21-xpro samr21-xpro spark-core \
yunjia-nrf51822 \ yunjia-nrf51822 msba2 \
esp32-mh-et-live-minikit esp32-olimex-evb \ esp32-mh-et-live-minikit esp32-olimex-evb \
esp32-wemos-lolin-d32-pro esp32-wroom-32 esp32-wrover-kit esp32-wemos-lolin-d32-pro esp32-wroom-32 esp32-wrover-kit