Merge pull request #10340 from maribu/cc110x_rewrite
drivers/cc110x: Complete rewrite from scratch
This commit is contained in:
commit
11b4bab101
@ -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
|
||||||
|
|||||||
@ -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)))
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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) */
|
||||||
/** @} */
|
/** @} */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -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)))
|
||||||
|
|||||||
@ -1,3 +1 @@
|
|||||||
DIRS += gnrc_cc110x
|
|
||||||
|
|
||||||
include $(RIOTBASE)/Makefile.base
|
include $(RIOTBASE)/Makefile.base
|
||||||
|
|||||||
@ -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);
|
|
||||||
@ -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);
|
|
||||||
}
|
|
||||||
@ -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;
|
|
||||||
}
|
|
||||||
@ -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;
|
|
||||||
}
|
|
||||||
@ -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)
|
|
||||||
{
|
|
||||||
#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.gdo0);
|
||||||
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)
|
|
||||||
{
|
|
||||||
if (dev->radio_state != RADIO_RX) {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG_DEBUG("cc110x: switching to idle mode\n");
|
/* 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);
|
||||||
|
|
||||||
cc110x_strobe(dev, CC110X_SIDLE);
|
/* prepare hopping will call cc110x_enter_rx_mode(), which restores the IRQs */
|
||||||
dev->radio_state = RADIO_IDLE;
|
return cc110x_full_calibration(dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
void cc110x_switch_to_pwd(cc110x_t *dev)
|
int cc110x_set_tx_power(cc110x_t *dev, cc110x_tx_power_t power)
|
||||||
{
|
{
|
||||||
LOG_DEBUG("cc110x: switching to powerdown mode\n");
|
DEBUG("[cc110x] Applying TX power setting at index %u\n", (unsigned)power);
|
||||||
cc110x_wakeup_from_rx(dev);
|
if (!dev) {
|
||||||
cc110x_strobe(dev, CC110X_SPWD);
|
return -EINVAL;
|
||||||
dev->radio_state = RADIO_PWD;
|
|
||||||
|
|
||||||
#ifdef MODULE_CC110X_HOOKS
|
|
||||||
cc110x_hook_off();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifndef MODULE_CC110X_HOOKS
|
|
||||||
int16_t cc110x_set_channel(cc110x_t *dev, uint8_t channr)
|
|
||||||
{
|
|
||||||
DEBUG("%s:%s:%u\n", RIOT_FILE_RELATIVE, __func__, __LINE__);
|
|
||||||
|
|
||||||
if (channr > MAX_CHANNR) {
|
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cc110x_write_register(dev, CC110X_CHANNR, channr * 10);
|
if ((unsigned)power >= CC110X_TX_POWER_NUMOF) {
|
||||||
dev->radio_channel = channr;
|
return -ERANGE;
|
||||||
|
|
||||||
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) {
|
if (cc110x_acquire(dev) != SPI_OK) {
|
||||||
case RADIO_MODE_ON:
|
return -EIO;
|
||||||
LOG_DEBUG("cc110x: switching to RX mode\n");
|
}
|
||||||
cc110x_setup_rx_mode(dev); /* Set chip to desired mode */
|
|
||||||
|
switch (dev->state) {
|
||||||
|
case CC110X_STATE_IDLE:
|
||||||
|
/* falls through */
|
||||||
|
case CC110X_STATE_RX_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;
|
||||||
}
|
}
|
||||||
|
|||||||
141
drivers/cc110x/cc110x_calibration.c
Normal file
141
drivers/cc110x/cc110x_calibration.c
Normal 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);
|
||||||
|
}
|
||||||
74
drivers/cc110x/cc110x_chanmaps.c
Normal file
74
drivers/cc110x/cc110x_chanmaps.c
Normal 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 */
|
||||||
|
}
|
||||||
|
};
|
||||||
115
drivers/cc110x/cc110x_communication.c
Normal file
115
drivers/cc110x/cc110x_communication.c
Normal 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;
|
||||||
|
}
|
||||||
65
drivers/cc110x/cc110x_configs.c
Normal file
65
drivers/cc110x/cc110x_configs.c
Normal 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,
|
||||||
|
};
|
||||||
623
drivers/cc110x/cc110x_netdev.c
Normal file
623
drivers/cc110x/cc110x_netdev.c
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
67
drivers/cc110x/cc110x_patables.c
Normal file
67
drivers/cc110x/cc110x_patables.c
Normal 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 */
|
||||||
|
}
|
||||||
|
};
|
||||||
326
drivers/cc110x/cc110x_rx_tx.c
Normal file
326
drivers/cc110x/cc110x_rx_tx.c
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
330
drivers/cc110x/cc110x_settings.c
Normal file
330
drivers/cc110x/cc110x_settings.c
Normal 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 };
|
||||||
@ -1 +0,0 @@
|
|||||||
include $(RIOTBASE)/Makefile.base
|
|
||||||
@ -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);
|
|
||||||
}
|
|
||||||
@ -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 */
|
|
||||||
/** @} */
|
|
||||||
@ -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 */
|
|
||||||
/** @} */
|
|
||||||
@ -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 */
|
|
||||||
/** @} */
|
|
||||||
@ -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 */
|
|
||||||
/** @} */
|
|
||||||
@ -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 */
|
|
||||||
/** @} */
|
|
||||||
@ -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 */
|
|
||||||
/** @} */
|
|
||||||
49
drivers/cc110x/include/cc110x_calibration.h
Normal file
49
drivers/cc110x/include/cc110x_calibration.h
Normal 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 */
|
||||||
|
/** @} */
|
||||||
213
drivers/cc110x/include/cc110x_communication.h
Normal file
213
drivers/cc110x/include/cc110x_communication.h
Normal 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 */
|
||||||
|
/** @} */
|
||||||
522
drivers/cc110x/include/cc110x_constants.h
Normal file
522
drivers/cc110x/include/cc110x_constants.h
Normal 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 */
|
||||||
|
/** @} */
|
||||||
83
drivers/cc110x/include/cc110x_internal.h
Normal file
83
drivers/cc110x/include/cc110x_internal.h
Normal 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 */
|
||||||
|
/** @} */
|
||||||
47
drivers/cc110x/include/cc110x_netdev.h
Normal file
47
drivers/cc110x/include/cc110x_netdev.h
Normal 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 */
|
||||||
|
/** @} */
|
||||||
@ -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
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
47
drivers/cc110x/include/cc110x_rx_tx.h
Normal file
47
drivers/cc110x/include/cc110x_rx_tx.h
Normal 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 */
|
||||||
|
/** @} */
|
||||||
170
drivers/cc110x/include/cc110x_settings.h
Normal file
170
drivers/cc110x/include/cc110x_settings.h
Normal 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 */
|
||||||
|
/** @} */
|
||||||
@ -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 */
|
|
||||||
/** @} */
|
|
||||||
@ -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 */
|
||||||
|
|||||||
@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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,15 +71,10 @@ 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
|
ifneq (,$(filter at86rf212b,$(USEMODULE))) # radio is IEEE 802.15.4 sub-GHz
|
||||||
DEFAULT_CHANNEL ?= 0
|
|
||||||
CFLAGS += -DCC110X_DEFAULT_CHANNEL=$(DEFAULT_CHANNEL)
|
|
||||||
else
|
|
||||||
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)
|
||||||
else # radio is IEEE 802.15.4 2.4 GHz
|
else # radio is IEEE 802.15.4 2.4 GHz
|
||||||
DEFAULT_CHANNEL ?= 26
|
DEFAULT_CHANNEL ?= 26
|
||||||
CFLAGS += -DIEEE802154_DEFAULT_CHANNEL=$(DEFAULT_CHANNEL)
|
CFLAGS += -DIEEE802154_DEFAULT_CHANNEL=$(DEFAULT_CHANNEL)
|
||||||
endif
|
|
||||||
endif
|
endif
|
||||||
|
|||||||
@ -116,15 +116,10 @@ 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
|
ifneq (,$(filter at86rf212b,$(USEMODULE))) # radio is IEEE 802.15.4 sub-GHz
|
||||||
DEFAULT_CHANNEL ?= 0
|
|
||||||
CFLAGS += -DCC110X_DEFAULT_CHANNEL=$(DEFAULT_CHANNEL)
|
|
||||||
else
|
|
||||||
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)
|
||||||
else # radio is IEEE 802.15.4 2.4 GHz
|
else # radio is IEEE 802.15.4 2.4 GHz
|
||||||
DEFAULT_CHANNEL ?= 26
|
DEFAULT_CHANNEL ?= 26
|
||||||
CFLAGS += -DIEEE802154_DEFAULT_CHANNEL=$(DEFAULT_CHANNEL)
|
CFLAGS += -DIEEE802154_DEFAULT_CHANNEL=$(DEFAULT_CHANNEL)
|
||||||
endif
|
|
||||||
endif
|
endif
|
||||||
|
|||||||
@ -36,15 +36,10 @@ 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
|
ifneq (,$(filter at86rf212b,$(USEMODULE))) # radio is IEEE 802.15.4 sub-GHz
|
||||||
DEFAULT_CHANNEL ?= 0
|
|
||||||
CFLAGS += -DCC110X_DEFAULT_CHANNEL=$(DEFAULT_CHANNEL)
|
|
||||||
else
|
|
||||||
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)
|
||||||
else # radio is IEEE 802.15.4 2.4 GHz
|
else # radio is IEEE 802.15.4 2.4 GHz
|
||||||
DEFAULT_CHANNEL ?= 26
|
DEFAULT_CHANNEL ?= 26
|
||||||
CFLAGS += -DIEEE802154_DEFAULT_CHANNEL=$(DEFAULT_CHANNEL)
|
CFLAGS += -DIEEE802154_DEFAULT_CHANNEL=$(DEFAULT_CHANNEL)
|
||||||
endif
|
|
||||||
endif
|
endif
|
||||||
|
|||||||
@ -65,15 +65,10 @@ 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
|
ifneq (,$(filter at86rf212b,$(USEMODULE))) # radio is IEEE 802.15.4 sub-GHz
|
||||||
DEFAULT_CHANNEL ?= 0
|
|
||||||
CFLAGS += -DCC110X_DEFAULT_CHANNEL=$(DEFAULT_CHANNEL)
|
|
||||||
else
|
|
||||||
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)
|
||||||
else # radio is IEEE 802.15.4 2.4 GHz
|
else # radio is IEEE 802.15.4 2.4 GHz
|
||||||
DEFAULT_CHANNEL ?= 26
|
DEFAULT_CHANNEL ?= 26
|
||||||
CFLAGS += -DIEEE802154_DEFAULT_CHANNEL=$(DEFAULT_CHANNEL)
|
CFLAGS += -DIEEE802154_DEFAULT_CHANNEL=$(DEFAULT_CHANNEL)
|
||||||
endif
|
|
||||||
endif
|
endif
|
||||||
|
|||||||
@ -61,15 +61,10 @@ 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
|
ifneq (,$(filter at86rf212b,$(USEMODULE))) # radio is IEEE 802.15.4 sub-GHz
|
||||||
DEFAULT_CHANNEL ?= 0
|
|
||||||
CFLAGS += -DCC110X_DEFAULT_CHANNEL=$(DEFAULT_CHANNEL)
|
|
||||||
else
|
|
||||||
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)
|
||||||
else # radio is IEEE 802.15.4 2.4 GHz
|
else # radio is IEEE 802.15.4 2.4 GHz
|
||||||
DEFAULT_CHANNEL ?= 26
|
DEFAULT_CHANNEL ?= 26
|
||||||
CFLAGS += -DIEEE802154_DEFAULT_CHANNEL=$(DEFAULT_CHANNEL)
|
CFLAGS += -DIEEE802154_DEFAULT_CHANNEL=$(DEFAULT_CHANNEL)
|
||||||
endif
|
|
||||||
endif
|
endif
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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();
|
||||||
|
|||||||
@ -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 */
|
||||||
|
|
||||||
/** @} */
|
/** @} */
|
||||||
|
|||||||
57
tests/driver_cc110x/Makefile
Normal file
57
tests/driver_cc110x/Makefile
Normal 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
134
tests/driver_cc110x/main.c
Normal 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;
|
||||||
|
}
|
||||||
208
tests/driver_cc110x/sc_cc110x.c
Normal file
208
tests/driver_cc110x/sc_cc110x.c
Normal 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;
|
||||||
|
}
|
||||||
@ -31,15 +31,10 @@ 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
|
ifneq (,$(filter at86rf212b,$(USEMODULE))) # radio is IEEE 802.15.4 sub-GHz
|
||||||
DEFAULT_CHANNEL ?= 0
|
|
||||||
CFLAGS += -DCC110X_DEFAULT_CHANNEL=$(DEFAULT_CHANNEL)
|
|
||||||
else
|
|
||||||
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)
|
||||||
else # radio is IEEE 802.15.4 2.4 GHz
|
else # radio is IEEE 802.15.4 2.4 GHz
|
||||||
DEFAULT_CHANNEL ?= 26
|
DEFAULT_CHANNEL ?= 26
|
||||||
CFLAGS += -DIEEE802154_DEFAULT_CHANNEL=$(DEFAULT_CHANNEL)
|
CFLAGS += -DIEEE802154_DEFAULT_CHANNEL=$(DEFAULT_CHANNEL)
|
||||||
endif
|
|
||||||
endif
|
endif
|
||||||
|
|||||||
@ -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
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user