diff --git a/Makefile.dep b/Makefile.dep index c4d9b80450..afc3d5d61b 100644 --- a/Makefile.dep +++ b/Makefile.dep @@ -164,7 +164,7 @@ ifneq (,$(filter gnrc_netif,$(USEMODULE))) 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))) USEMODULE += gnrc_sixlowpan endif diff --git a/boards/msba2/Makefile.dep b/boards/msba2/Makefile.dep index 04ee7ff3ea..b0ec516ac2 100644 --- a/boards/msba2/Makefile.dep +++ b/boards/msba2/Makefile.dep @@ -1,7 +1,7 @@ include $(RIOTBOARD)/common/msba2/Makefile.dep ifneq (,$(filter netdev_default gnrc_netdev_default,$(USEMODULE))) - USEMODULE += cc110x + USEMODULE += cc1100 endif ifneq (,$(filter saul_default,$(USEMODULE))) diff --git a/boards/msbiot/Makefile.dep b/boards/msbiot/Makefile.dep index be08a394e3..c1c23ead69 100644 --- a/boards/msbiot/Makefile.dep +++ b/boards/msbiot/Makefile.dep @@ -1,7 +1,7 @@ -# add driver for CC1101 sub-gigahertz transceiver as default netdev ifneq (,$(filter netdev_default gnrc_netdev_default,$(USEMODULE))) - USEMODULE += cc110x + USEMODULE += cc1101 endif + # add support for LEDs and buttons as default saul devices ifneq (,$(filter saul_default,$(USEMODULE))) USEMODULE += saul_gpio diff --git a/boards/msbiot/include/board.h b/boards/msbiot/include/board.h index 4af33f6f32..55d08dcec1 100644 --- a/boards/msbiot/include/board.h +++ b/boards/msbiot/include/board.h @@ -30,11 +30,11 @@ extern "C" { * @name Configure connected CC1101 (radio) device * @{ */ -#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_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_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_GDO0 GPIO_PIN(PORT_C, 4) /**< GDO0 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) */ /** @} */ /** diff --git a/drivers/Makefile.dep b/drivers/Makefile.dep index 3d893f6017..a04ba5f491 100644 --- a/drivers/Makefile.dep +++ b/drivers/Makefile.dep @@ -81,16 +81,18 @@ ifneq (,$(filter bm%280,$(USEMODULE))) USEMODULE += bmx280 endif -ifneq (,$(filter cc110x,$(USEMODULE))) - USEMODULE += ieee802154 +ifneq (,$(filter cc110%,$(USEMODULE))) + USEMODULE += cc110x + USEMODULE += cc1xxx_common USEMODULE += luid + USEMODULE += netif USEMODULE += xtimer - ifneq (,$(filter gnrc_netdev_default,$(USEMODULE))) - USEMODULE += gnrc_cc110x - endif FEATURES_REQUIRED += periph_gpio FEATURES_REQUIRED += periph_gpio_irq FEATURES_REQUIRED += periph_spi + ifneq (,$(filter gnrc_ipv6,$(USEMODULE))) + USEMODULE += gnrc_sixlowpan + endif endif ifneq (,$(filter cc2420,$(USEMODULE))) diff --git a/drivers/cc110x/Makefile b/drivers/cc110x/Makefile index a6588efa9c..48422e909a 100644 --- a/drivers/cc110x/Makefile +++ b/drivers/cc110x/Makefile @@ -1,3 +1 @@ -DIRS += gnrc_cc110x - include $(RIOTBASE)/Makefile.base diff --git a/drivers/cc110x/cc110x-defaultsettings.c b/drivers/cc110x/cc110x-defaultsettings.c deleted file mode 100644 index aded9ece1f..0000000000 --- a/drivers/cc110x/cc110x-defaultsettings.c +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright (C) 2013 INRIA - * 2015 Kaspar Schleiser - * - * 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 - * @author Heiko Will - * @author Oliver Hahm - * @author Kaspar Schleiser - * @} - */ - -#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); diff --git a/drivers/cc110x/cc110x-netdev.c b/drivers/cc110x/cc110x-netdev.c deleted file mode 100644 index b840389e22..0000000000 --- a/drivers/cc110x/cc110x-netdev.c +++ /dev/null @@ -1,239 +0,0 @@ -/* - * Copyright (C) 2014 Freie Universität Berlin - * 2015 Kaspar Schleiser - * - * 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 - * @author Kaspar Schleiser - * @} - */ - -#include -#include -#include -#include - -#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); -} diff --git a/drivers/cc110x/cc110x-rxtx.c b/drivers/cc110x/cc110x-rxtx.c deleted file mode 100644 index 04cc8df5c4..0000000000 --- a/drivers/cc110x/cc110x-rxtx.c +++ /dev/null @@ -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 - * @author Fabian Nack - * @author Kaspar Schleiser - * @} - */ - -#include -#include -#include - -#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; -} diff --git a/drivers/cc110x/cc110x-spi.c b/drivers/cc110x/cc110x-spi.c deleted file mode 100644 index abb1920f12..0000000000 --- a/drivers/cc110x/cc110x-spi.c +++ /dev/null @@ -1,172 +0,0 @@ -/* - * Copyright (C) 2014 Freie Universität Berlin - * Copyright (C) 2015 Kaspar Schleiser - * - * 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 - * @author Heiko Will - * @author Fabian Nack - * @author Joakim Gebart - * @author Kaspar Schleiser - * @} - */ - -#include - -#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; -} diff --git a/drivers/cc110x/cc110x.c b/drivers/cc110x/cc110x.c index 8230f94873..a60323aaaa 100644 --- a/drivers/cc110x/cc110x.c +++ b/drivers/cc110x/cc110x.c @@ -1,7 +1,5 @@ /* - * Copyright (C) 2014 Freie Universität Berlin - * Copyright (C) 2013 INRIA - * Copyright (C) 2015 Kaspar Schleiser + * 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 @@ -9,263 +7,167 @@ */ /** - * @ingroup drivers_cc110x + * @ingroup drivers_cc110x * @{ - * @file - * @brief Basic functionality of cc110x driver * - * @author Oliver Hahm - * @author Fabian Nack - * @author Kaspar Schleiser + * @file + * @brief Implementation for the "public" API of the CC1100/CC1101 driver + * + * @author Marian Buschsieweke * @} */ -#include "luid.h" -#include "board.h" -#include "periph/gpio.h" -#include "periph/spi.h" -#include "xtimer.h" -#include "cpu.h" -#include "log.h" +#include +#include #include "cc110x.h" -#include "cc110x-defaultsettings.h" -#include "cc110x-defines.h" -#include "cc110x-interface.h" -#include "cc110x-internal.h" -#include "cc110x-spi.h" +#include "cc110x_internal.h" #define ENABLE_DEBUG (0) #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) { - DEBUG("%s:%s:%u\n", RIOT_FILE_RELATIVE, __func__, __LINE__); - -#ifdef MODULE_CC110X_HOOKS - cc110x_hooks_init(); -#endif - - dev->params = *params; - - /* Configure chip-select */ - 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; -} - -uint8_t cc110x_set_address(cc110x_t *dev, uint8_t address) -{ - DEBUG("%s:%s:%u setting address %u\n", RIOT_FILE_RELATIVE, __func__, - __LINE__, (unsigned)address); - if (!(address < MIN_UID)) { - if (dev->radio_state != RADIO_UNKNOWN) { - cc110x_write_register(dev, CC110X_ADDR, address); - dev->radio_address = address; - return address; - } + if (!dev || !params) { + return -EINVAL; } + /* Zero out everything but RIOT's driver interface, which should be + * managed by RIOT + */ + memset((char *)dev + sizeof(netdev_t), 0x00, + sizeof(cc110x_t) - sizeof(netdev_t)); + dev->params = *params; + dev->netdev.driver = &cc110x_driver; + dev->state = CC110X_STATE_OFF; return 0; } -void cc110x_set_base_freq_raw(cc110x_t *dev, const char* freq_array) +int cc110x_apply_config(cc110x_t *dev, const cc110x_config_t *conf, + const cc110x_chanmap_t *chanmap) { -#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] Applying new configuration\n"); + if (!dev || !chanmap) { + return -EINVAL; + } - 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 + if (cc110x_acquire(dev) != SPI_OK) { + return -EIO; + } + gpio_irq_disable(dev->params.gdo0); gpio_irq_disable(dev->params.gdo2); - /* flush RX fifo */ - cc110x_strobe(dev, CC110X_SIDLE); - cc110x_strobe(dev, CC110X_SFRX); + /* Go to IDLE state to allow reconfiguration */ + cc110x_cmd(dev, CC110X_STROBE_IDLE); + 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_strobe(dev, CC110X_SRX); - - gpio_irq_enable(dev->params.gdo2); -} - -void cc110x_wakeup_from_rx(cc110x_t *dev) -{ - if (dev->radio_state != RADIO_RX) { - return; + cc110x_write(dev, CC110X_REG_FSCTRL1, conf->fsctrl1); + cc110x_write(dev, CC110X_REG_MDMCFG4, conf->mdmcfg4); + cc110x_write(dev, CC110X_REG_MDMCFG3, conf->mdmcfg3); + cc110x_write(dev, CC110X_REG_DEVIATN, conf->deviatn); } - 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); - dev->radio_state = RADIO_IDLE; + /* prepare hopping will call cc110x_enter_rx_mode(), which restores the IRQs */ + 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"); - cc110x_wakeup_from_rx(dev); - cc110x_strobe(dev, CC110X_SPWD); - dev->radio_state = RADIO_PWD; - -#ifdef MODULE_CC110X_HOOKS - cc110x_hook_off(); -#endif -} - -#ifndef MODULE_CC110X_HOOKS -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; + DEBUG("[cc110x] Applying TX power setting at index %u\n", (unsigned)power); + if (!dev) { + return -EINVAL; } - cc110x_write_register(dev, CC110X_CHANNR, channr * 10); - dev->radio_channel = channr; - - return channr; -} -#endif - -#ifndef CC110X_DONT_RESET -static void _reset(cc110x_t *dev) -{ - DEBUG("%s:%s:%u\n", RIOT_FILE_RELATIVE, __func__, __LINE__); - cc110x_wakeup_from_rx(dev); - cc110x_cs(dev); - cc110x_strobe(dev, CC110X_SRES); - xtimer_usleep(100); -} - -static void _power_up_reset(cc110x_t *dev) -{ - DEBUG("%s:%s:%u\n", RIOT_FILE_RELATIVE, __func__, __LINE__); - gpio_set(dev->params.cs); - gpio_clear(dev->params.cs); - gpio_set(dev->params.cs); - xtimer_usleep(RESET_WAIT_TIME); - _reset(dev); -} -#endif - -void cc110x_write_register(cc110x_t *dev, uint8_t r, uint8_t value) -{ - /* Save old radio state */ - uint8_t old_state = dev->radio_state; - - /* Wake up from RX (no effect if in other mode) */ - cc110x_wakeup_from_rx(dev); - cc110x_write_reg(dev, r, value); - - /* Have to put radio back to RX if old radio state - * was RX, otherwise no action is necessary */ - if (old_state == RADIO_RX) { - cc110x_switch_to_rx(dev); - } -} - -int cc110x_rd_set_mode(cc110x_t *dev, int mode) -{ - DEBUG("%s:%s:%u\n", RIOT_FILE_RELATIVE, __func__, __LINE__); - - int result; - - /* Get current radio mode */ - if ((dev->radio_state == RADIO_UNKNOWN) || (dev->radio_state == RADIO_PWD)) { - result = RADIO_MODE_OFF; - } - else { - result = RADIO_MODE_ON; + if ((unsigned)power >= CC110X_TX_POWER_NUMOF) { + return -ERANGE; } - switch(mode) { - case RADIO_MODE_ON: - LOG_DEBUG("cc110x: switching to RX mode\n"); - cc110x_setup_rx_mode(dev); /* Set chip to desired mode */ + if (cc110x_acquire(dev) != SPI_OK) { + return -EIO; + } + + switch (dev->state) { + case CC110X_STATE_IDLE: + /* falls through */ + case CC110X_STATE_RX_MODE: 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: - /* do nothing */ - break; + cc110x_release(dev); + return -EAGAIN; } - /* Return previous mode */ - return result; + uint8_t frend0 = 0x10 | (uint8_t)power; + cc110x_write(dev, CC110X_REG_FREND0, frend0); + dev->tx_power = power; + cc110x_release(dev); + return 0; +} + +int cc110x_set_channel(cc110x_t *dev, uint8_t channel) +{ + DEBUG("[cc110x] Hopping to channel %i\n", (int)channel); + if (!dev) { + return -EINVAL; + } + + if (cc110x_acquire(dev) != SPI_OK) { + return -EIO; + } + + if ((channel >= CC110X_MAX_CHANNELS) || (dev->channels->map[channel] == 0xff)) { + /* Channel out of range or not supported in current channel map */ + cc110x_release(dev); + return -ERANGE; + } + + switch (dev->state) { + case CC110X_STATE_IDLE: + /* falls through */ + case CC110X_STATE_RX_MODE: + /* falls through */ + case CC110X_STATE_FSTXON: + /* Above states are fine for hopping */ + break; + default: + /* All other states do not allow hopping right now */ + cc110x_release(dev); + return -EAGAIN; + } + + /* Disable IRQs, as e.g. PLL indicator will go LOW in IDLE state */ + gpio_irq_disable(dev->params.gdo0); + gpio_irq_disable(dev->params.gdo2); + + /* Go to IDLE state to disable frequency synchronizer */ + cc110x_cmd(dev, CC110X_STROBE_IDLE); + + /* Upload new channel and corresponding calibration data */ + cc110x_write(dev, CC110X_REG_CHANNR, dev->channels->map[channel]); + + uint8_t caldata[] = { + dev->fscal.fscal3, dev->fscal.fscal2, dev->fscal.fscal1[channel] + }; + cc110x_burst_write(dev, CC110X_REG_FSCAL3, caldata, sizeof(caldata)); + + /* Start listening on the new channel (restores IRQs) */ + cc110x_enter_rx_mode(dev); + + dev->channel = channel; + cc110x_release(dev); + + dev->netdev.event_callback(&dev->netdev, NETDEV_EVENT_FHSS_CHANGE_CHANNEL); + return 0; } diff --git a/drivers/cc110x/cc110x_calibration.c b/drivers/cc110x/cc110x_calibration.c new file mode 100644 index 0000000000..f530a067c6 --- /dev/null +++ b/drivers/cc110x/cc110x_calibration.c @@ -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 + * @} + */ + +#include +#include + +#include "periph/gpio.h" +#include "xtimer.h" + +#include "cc110x.h" +#include "cc110x_internal.h" + +#define ENABLE_DEBUG (0) +#include "debug.h" + +/** + * @brief Read the calibration data from the transceiver and store it + * + * @param dev Device descriptor of the transceiver + * + * @pre @p dev is acquired using @p cc110x_acquire + */ +static inline void get_calibration_data(cc110x_t *dev) +{ + char caldata[3]; + + cc110x_burst_read(dev, CC110X_REG_FSCAL3, caldata, sizeof(caldata)); + dev->fscal.fscal3 = caldata[0]; + dev->fscal.fscal2 = caldata[1]; + dev->fscal.fscal1[dev->channel] = caldata[2]; +} + +int cc110x_recalibrate(cc110x_t *dev) +{ + /* Sadly we cannot use GDO0 to check for calibration, as it only + * provides output in RX/TX state. But after successful manual + * calibration, the device returns to IDLE state. Thus, we keep + * calibrating until IDLE state is reached + */ + do { + /* Start calibration */ + cc110x_cmd(dev, CC110X_STROBE_CALIBRATE); + /* Release SPI interface to give other threads a chance to use it */ + cc110x_release(dev); + /* Manual calibration take 735 micro seconds (see Table 34 on page + * 54 in the date sheet). We'll wait 750 to be sure + */ + xtimer_usleep(750); + + /* Re-acquire SPI interface in order to check if calibration + * succeeded + */ + 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); +} diff --git a/drivers/cc110x/cc110x_chanmaps.c b/drivers/cc110x/cc110x_chanmaps.c new file mode 100644 index 0000000000..49c82bb3ae --- /dev/null +++ b/drivers/cc110x/cc110x_chanmaps.c @@ -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 + * @} + */ + +#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 */ + } +}; diff --git a/drivers/cc110x/cc110x_communication.c b/drivers/cc110x/cc110x_communication.c new file mode 100644 index 0000000000..a6f3134504 --- /dev/null +++ b/drivers/cc110x/cc110x_communication.c @@ -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 + * @} + */ + +#include +#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; +} diff --git a/drivers/cc110x/cc110x_configs.c b/drivers/cc110x/cc110x_configs.c new file mode 100644 index 0000000000..540806e018 --- /dev/null +++ b/drivers/cc110x/cc110x_configs.c @@ -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 + * @} + */ + +#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, +}; diff --git a/drivers/cc110x/cc110x_netdev.c b/drivers/cc110x/cc110x_netdev.c new file mode 100644 index 0000000000..1f34ce9dac --- /dev/null +++ b/drivers/cc110x/cc110x_netdev.c @@ -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 + * @} + */ + +#include +#include + +#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; + } +} diff --git a/drivers/cc110x/cc110x_patables.c b/drivers/cc110x/cc110x_patables.c new file mode 100644 index 0000000000..8d1863b1c8 --- /dev/null +++ b/drivers/cc110x/cc110x_patables.c @@ -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 + * @} + */ + +#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 */ + } +}; diff --git a/drivers/cc110x/cc110x_rx_tx.c b/drivers/cc110x/cc110x_rx_tx.c new file mode 100644 index 0000000000..523bb882d1 --- /dev/null +++ b/drivers/cc110x/cc110x_rx_tx.c @@ -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 + * @} + */ + +#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); + } +} diff --git a/drivers/cc110x/cc110x_settings.c b/drivers/cc110x/cc110x_settings.c new file mode 100644 index 0000000000..715fd68ec7 --- /dev/null +++ b/drivers/cc110x/cc110x_settings.c @@ -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 + * @} + */ + +#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 }; diff --git a/drivers/cc110x/gnrc_cc110x/Makefile b/drivers/cc110x/gnrc_cc110x/Makefile deleted file mode 100644 index 48422e909a..0000000000 --- a/drivers/cc110x/gnrc_cc110x/Makefile +++ /dev/null @@ -1 +0,0 @@ -include $(RIOTBASE)/Makefile.base diff --git a/drivers/cc110x/gnrc_cc110x/gnrc_cc110x.c b/drivers/cc110x/gnrc_cc110x/gnrc_cc110x.c deleted file mode 100644 index 38e786583e..0000000000 --- a/drivers/cc110x/gnrc_cc110x/gnrc_cc110x.c +++ /dev/null @@ -1,200 +0,0 @@ -/* - * Copyright (C) 2015 Kaspar Schleiser - * - * 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 - -#include - -#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); -} diff --git a/drivers/cc110x/include/cc110x-defaultsettings.h b/drivers/cc110x/include/cc110x-defaultsettings.h deleted file mode 100644 index 1c9764d051..0000000000 --- a/drivers/cc110x/include/cc110x-defaultsettings.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (C) 2015 Kaspar Schleiser - * - * 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 - */ -#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 */ -/** @} */ diff --git a/drivers/cc110x/include/cc110x-defines.h b/drivers/cc110x/include/cc110x-defines.h deleted file mode 100644 index 6eac9fafb2..0000000000 --- a/drivers/cc110x/include/cc110x-defines.h +++ /dev/null @@ -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 - * @author Heiko Will - * @author Oliver Hahm - */ - -#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 */ -/** @} */ diff --git a/drivers/cc110x/include/cc110x-interface.h b/drivers/cc110x/include/cc110x-interface.h deleted file mode 100644 index 4e77b94789..0000000000 --- a/drivers/cc110x/include/cc110x-interface.h +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (C) 2015 Kaspar Schleiser - * 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 - * @author Kaspar Schleiser - */ - -#ifndef CC110X_INTERFACE_H -#define CC110X_INTERFACE_H - -#include -#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 */ -/** @} */ diff --git a/drivers/cc110x/include/cc110x-internal.h b/drivers/cc110x/include/cc110x-internal.h deleted file mode 100644 index 1c5cc5ba6a..0000000000 --- a/drivers/cc110x/include/cc110x-internal.h +++ /dev/null @@ -1,212 +0,0 @@ -/* - * Copyright (C) 2015 Kaspar Schleiser - * 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 - * @author Kaspar Schleiser - */ - -#ifndef CC110X_INTERNAL_H -#define CC110X_INTERNAL_H - -#include - -#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 - * - *
----------------------------------------------------
-|        |         |         |       |            |
-| 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
-
-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 */ -/** @} */ diff --git a/drivers/cc110x/include/cc110x-netdev.h b/drivers/cc110x/include/cc110x-netdev.h deleted file mode 100644 index 2292662b83..0000000000 --- a/drivers/cc110x/include/cc110x-netdev.h +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (C) 2014 Freie Universität Berlin - * 2015 Kaspar Schleiser - * - * 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 - * @author Kaspar Schleiser - */ - -#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 */ -/** @} */ diff --git a/drivers/cc110x/include/cc110x-spi.h b/drivers/cc110x/include/cc110x-spi.h deleted file mode 100644 index 0b39c89c91..0000000000 --- a/drivers/cc110x/include/cc110x-spi.h +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright (C) 2014 Freie Universität Berlin - * Copyright (C) 2013 INRIA - * Copyright (C) 2015 Kaspar Schleiser - * - * 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 - * @author Fabian Nack - * @author Kaspar Schleiser - */ - -#ifndef CC110X_SPI_H -#define CC110X_SPI_H - -#include - -#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 */ -/** @} */ diff --git a/drivers/cc110x/include/cc110x_calibration.h b/drivers/cc110x/include/cc110x_calibration.h new file mode 100644 index 0000000000..250e47f826 --- /dev/null +++ b/drivers/cc110x/include/cc110x_calibration.h @@ -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 + * @} + */ + +#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 */ +/** @} */ diff --git a/drivers/cc110x/include/cc110x_communication.h b/drivers/cc110x/include/cc110x_communication.h new file mode 100644 index 0000000000..40e5e1cc27 --- /dev/null +++ b/drivers/cc110x/include/cc110x_communication.h @@ -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 + * @} + */ + +#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 */ +/** @} */ diff --git a/drivers/cc110x/include/cc110x_constants.h b/drivers/cc110x/include/cc110x_constants.h new file mode 100644 index 0000000000..2fbecc9382 --- /dev/null +++ b/drivers/cc110x/include/cc110x_constants.h @@ -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 + * @} + */ + +#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 */ +/** @} */ diff --git a/drivers/cc110x/include/cc110x_internal.h b/drivers/cc110x/include/cc110x_internal.h new file mode 100644 index 0000000000..ca11d9b42a --- /dev/null +++ b/drivers/cc110x/include/cc110x_internal.h @@ -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 + * @} + */ + +#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 */ +/** @} */ diff --git a/drivers/cc110x/include/cc110x_netdev.h b/drivers/cc110x/include/cc110x_netdev.h new file mode 100644 index 0000000000..88d8bb0de1 --- /dev/null +++ b/drivers/cc110x/include/cc110x_netdev.h @@ -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 + * @} + */ + +#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 */ +/** @} */ diff --git a/drivers/cc110x/include/cc110x_params.h b/drivers/cc110x/include/cc110x_params.h index e9f0130c2a..bcead87fb7 100644 --- a/drivers/cc110x/include/cc110x_params.h +++ b/drivers/cc110x/include/cc110x_params.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2017 Marian Buschsieweke + * 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 @@ -20,6 +20,7 @@ #define CC110X_PARAMS_H #include "board.h" +#include "cc110x_settings.h" #ifdef __cplusplus extern "C" { @@ -43,32 +44,51 @@ extern "C" { #define CC110X_PARAM_GDO0 GPIO_PIN(0, 27) #endif -#ifndef CC110X_PARAM_GDO1 -#define CC110X_PARAM_GDO1 GPIO_PIN(1, 23) -#endif - #ifndef CC110X_PARAM_GDO2 #define CC110X_PARAM_GDO2 GPIO_PIN(0, 28) #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 #define CC110X_PARAMS { \ - .spi = CC110X_PARAM_SPI, \ - .cs = CC110X_PARAM_CS, \ - .gdo0 = CC110X_PARAM_GDO0, \ - .gdo1 = CC110X_PARAM_GDO1, \ - .gdo2 = CC110X_PARAM_GDO2, \ - } + .spi = CC110X_PARAM_SPI, \ + .spi_clk = CC110X_PARAM_SPI_CLOCK, \ + .cs = CC110X_PARAM_CS, \ + .gdo0 = CC110X_PARAM_GDO0, \ + .gdo2 = CC110X_PARAM_GDO2, \ + .l2addr = CC110X_PARAM_L2ADDR, \ + .patable = CC110X_PARAM_PATABLE, \ + .config = CC110X_PARAM_CONFIG, \ + .channels = CC110X_PARAM_CHANNELS, \ +} #endif /** @} */ - /** * @name CC110X configuration * @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 }; diff --git a/drivers/cc110x/include/cc110x_rx_tx.h b/drivers/cc110x/include/cc110x_rx_tx.h new file mode 100644 index 0000000000..554af05f50 --- /dev/null +++ b/drivers/cc110x/include/cc110x_rx_tx.h @@ -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 + * @} + */ + +#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 */ +/** @} */ diff --git a/drivers/cc110x/include/cc110x_settings.h b/drivers/cc110x/include/cc110x_settings.h new file mode 100644 index 0000000000..29238a1a43 --- /dev/null +++ b/drivers/cc110x/include/cc110x_settings.h @@ -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 + * @} + */ + +#ifndef CC110X_SETTINGS_H +#define CC110X_SETTINGS_H + +#include + +#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 */ +/** @} */ diff --git a/drivers/cc110x/include/gnrc_netif_cc110x.h b/drivers/cc110x/include/gnrc_netif_cc110x.h deleted file mode 100644 index dc1d43c211..0000000000 --- a/drivers/cc110x/include/gnrc_netif_cc110x.h +++ /dev/null @@ -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 - */ -#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 */ -/** @} */ diff --git a/drivers/cc1xxx_common/gnrc_netif_cc1xxx.c b/drivers/cc1xxx_common/gnrc_netif_cc1xxx.c index 2c55bc11b6..0c4498427e 100644 --- a/drivers/cc1xxx_common/gnrc_netif_cc1xxx.c +++ b/drivers/cc1xxx_common/gnrc_netif_cc1xxx.c @@ -86,7 +86,7 @@ static gnrc_pktsnip_t *cc1xxx_adpt_recv(gnrc_netif_t *netif) return NULL; } 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->lqi = rx_info.lqi; 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 */ LL_APPEND(payload, hdr); +#ifdef MODULE_NETSTATS_L2 + netif->stats.rx_count++; + netif->stats.rx_bytes += pktlen; +#endif + return payload; } @@ -122,6 +127,9 @@ static int cc1xxx_adpt_send(gnrc_netif_t *netif, gnrc_pktsnip_t *pkt) if (netif_hdr->flags & BCAST) { l2hdr.dest_addr = CC1XXX_BCAST_ADDR; DEBUG("[cc1xxx-gnrc] send: preparing to send broadcast\n"); +#ifdef MODULE_NETSTATS_L2 + netif->stats.tx_mcast_count++; +#endif } else { /* 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]; DEBUG("[cc1xxx-gnrc] send: preparing to send unicast %02x --> %02x\n", (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 */ diff --git a/drivers/include/cc110x.h b/drivers/include/cc110x.h index f915f8bfc0..261f6306cf 100644 --- a/drivers/include/cc110x.h +++ b/drivers/include/cc110x.h @@ -1,132 +1,629 @@ /* - * Copyright (C) 2014 Freie Universität Berlin + * Copyright (C) 2013 INRIA + * 2014 Freie Universität Berlin * 2015 Kaspar Schleiser + * 2018,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. + * 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. */ /** - * @defgroup drivers_cc110x CC1100/CC1101 radio driver + * @defgroup drivers_cc110x CC1100/CC1100e/CC1101 Sub-GHz transceiver driver * @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___`. (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 - * @brief Public interface for cc110x driver + * @brief Interface definition for the CC1100/CC1101 driver + * + * @author Marian Buschsieweke + * @author Oliver Hahm + * @author Fabian Nack * @author Kaspar Schleiser */ #ifndef CC110X_H #define CC110X_H +#include + +#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 extern "C" { #endif -#include "periph/spi.h" -#include "periph/gpio.h" -#include "cc110x-internal.h" -#include "net/gnrc/nettype.h" +/** + * @brief Length of a layer 2 frame + * + * 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 { - 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 */ - 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_PAYLOAD_SIZE (CC110X_MAX_FRAME_SIZE - CC1XXX_HEADER_SIZE) + +/** + * @brief Maximum number of channels supported by the driver + */ +#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; /** - * @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 { - cc110x_params_t params; /**< cc110x IO configuration */ - - cc110x_statistic_t cc110x_statistic; /**< Statistic values for - debugging */ - - uint8_t radio_state; /**< Radio state */ - uint8_t radio_channel; /**< current Radio channel */ - uint8_t radio_address; /**< current Radio address */ - - cc110x_pkt_buf_t pkt_buf; /**< RX/TX buffer */ - void (*isr_cb)(cc110x_t *dev, void* arg); /**< isr callback */ - void *isr_cb_arg; /**< isr callback argument */ -#ifdef MODULE_GNRC_NETIF - gnrc_nettype_t proto; /**< protocol the radio expects */ -#endif -}; +typedef struct __attribute__((packed)) { + uint8_t len; /**< Length of the frame in bytes */ + /** + * @brief The payload data of the frame + */ + uint8_t data[CC110X_MAX_FRAME_SIZE]; + /** + * @brief Index of the next @ref cc110x_framebuf_t::data element to transfer + * + * In RX mode: Index of the next @ref cc110x_framebuf_t::data element to store + * data read from the RX-FIFO into. + * + * In TX mode: Index of the next @ref cc110x_framebuf_t::data element to write + * to the TX-FIFO. + */ + 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 + * + * Blocking during TX within the driver prevents the upper layers from + * 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 * - * @param[in] dev device struct to set up - * @param[in] params struct holding parameters + * @ref netdev_driver_t::init can be used after this call to initialize the + * transceiver. * - * @return always succeeds + * @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); /** - * @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[in] channr guess what + * @param dev Device descriptor of the transceiver + * @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 - * @return -1 on error + * @retval 0 Success + * @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[in] packet ptr to packet to be sent + * @param dev Device descriptor of the transceiver * - * @return size of packet on success - * @return <0 on error + * @retval 0 Success + * @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[in] address new address + * @param dev Device descriptor of the transceiver + * @param power Output power to apply * - * @return address set on success - * @return 0 on error + * @retval 0 Success + * @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); - - -/** - * @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); +int cc110x_set_tx_power(cc110x_t *dev, cc110x_tx_power_t power); #ifdef __cplusplus } diff --git a/examples/default/Makefile b/examples/default/Makefile index 87bca54625..79e53da8eb 100644 --- a/examples/default/Makefile +++ b/examples/default/Makefile @@ -38,10 +38,10 @@ USEMODULE += ps USEMODULE += saul_default BOARD_PROVIDES_NETIF := acd52832 airfy-beacon b-l072z-lrwan1 cc2538dk fox \ - iotlab-m3 iotlab-a8-m3 lobaro-lorabox lsn50 mulle microbit native nrf51dk \ - nrf51dongle nrf52dk nrf52840dk nrf52840-mdk nrf6310 nucleo-f767zi \ - openmote-cc2538 pba-d-01-kw2x remote-pa remote-reva samr21-xpro \ - spark-core telosb yunjia-nrf51822 z1 + iotlab-m3 iotlab-a8-m3 lobaro-lorabox lsn50 mulle microbit msba2 \ + native nrf51dk nrf51dongle nrf52dk nrf52840dk nrf52840-mdk nrf6310 \ + nucleo-f767zi openmote-cc2538 pba-d-01-kw2x remote-pa remote-reva \ + samr21-xpro spark-core telosb yunjia-nrf51822 z1 ifneq (,$(filter $(BOARD),$(BOARD_PROVIDES_NETIF))) # Use modules for networking @@ -71,15 +71,10 @@ endif include $(RIOTBASE)/Makefile.include # Set a custom channel if needed -ifneq (,$(filter cc110x,$(USEMODULE))) # radio is cc110x sub-GHz - DEFAULT_CHANNEL ?= 0 - CFLAGS += -DCC110X_DEFAULT_CHANNEL=$(DEFAULT_CHANNEL) -else - ifneq (,$(filter at86rf212b,$(USEMODULE))) # radio is IEEE 802.15.4 sub-GHz - DEFAULT_CHANNEL ?= 5 - CFLAGS += -DIEEE802154_DEFAULT_SUBGHZ_CHANNEL=$(DEFAULT_CHANNEL) - else # radio is IEEE 802.15.4 2.4 GHz - DEFAULT_CHANNEL ?= 26 - CFLAGS += -DIEEE802154_DEFAULT_CHANNEL=$(DEFAULT_CHANNEL) - endif +ifneq (,$(filter at86rf212b,$(USEMODULE))) # radio is IEEE 802.15.4 sub-GHz + DEFAULT_CHANNEL ?= 5 + CFLAGS += -DIEEE802154_DEFAULT_SUBGHZ_CHANNEL=$(DEFAULT_CHANNEL) +else # radio is IEEE 802.15.4 2.4 GHz + DEFAULT_CHANNEL ?= 26 + CFLAGS += -DIEEE802154_DEFAULT_CHANNEL=$(DEFAULT_CHANNEL) endif diff --git a/examples/gnrc_border_router/Makefile b/examples/gnrc_border_router/Makefile index ff06c25ed7..3d0c46d952 100644 --- a/examples/gnrc_border_router/Makefile +++ b/examples/gnrc_border_router/Makefile @@ -116,15 +116,10 @@ host-tools: $(Q)env -u CC -u CFLAGS make -C $(RIOTTOOLS) # Set a custom channel if needed -ifneq (,$(filter cc110x,$(USEMODULE))) # radio is cc110x sub-GHz - DEFAULT_CHANNEL ?= 0 - CFLAGS += -DCC110X_DEFAULT_CHANNEL=$(DEFAULT_CHANNEL) -else - ifneq (,$(filter at86rf212b,$(USEMODULE))) # radio is IEEE 802.15.4 sub-GHz - DEFAULT_CHANNEL ?= 5 - CFLAGS += -DIEEE802154_DEFAULT_SUBGHZ_CHANNEL=$(DEFAULT_CHANNEL) - else # radio is IEEE 802.15.4 2.4 GHz - DEFAULT_CHANNEL ?= 26 - CFLAGS += -DIEEE802154_DEFAULT_CHANNEL=$(DEFAULT_CHANNEL) - endif +ifneq (,$(filter at86rf212b,$(USEMODULE))) # radio is IEEE 802.15.4 sub-GHz + DEFAULT_CHANNEL ?= 5 + CFLAGS += -DIEEE802154_DEFAULT_SUBGHZ_CHANNEL=$(DEFAULT_CHANNEL) +else # radio is IEEE 802.15.4 2.4 GHz + DEFAULT_CHANNEL ?= 26 + CFLAGS += -DIEEE802154_DEFAULT_CHANNEL=$(DEFAULT_CHANNEL) endif diff --git a/examples/gnrc_minimal/Makefile b/examples/gnrc_minimal/Makefile index 18d8ddc40c..7d7a1cf805 100644 --- a/examples/gnrc_minimal/Makefile +++ b/examples/gnrc_minimal/Makefile @@ -36,15 +36,10 @@ QUIET ?= 1 include $(RIOTBASE)/Makefile.include # Set a custom channel if needed -ifneq (,$(filter cc110x,$(USEMODULE))) # radio is cc110x sub-GHz - DEFAULT_CHANNEL ?= 0 - CFLAGS += -DCC110X_DEFAULT_CHANNEL=$(DEFAULT_CHANNEL) -else - ifneq (,$(filter at86rf212b,$(USEMODULE))) # radio is IEEE 802.15.4 sub-GHz - DEFAULT_CHANNEL ?= 5 - CFLAGS += -DIEEE802154_DEFAULT_SUBGHZ_CHANNEL=$(DEFAULT_CHANNEL) - else # radio is IEEE 802.15.4 2.4 GHz - DEFAULT_CHANNEL ?= 26 - CFLAGS += -DIEEE802154_DEFAULT_CHANNEL=$(DEFAULT_CHANNEL) - endif +ifneq (,$(filter at86rf212b,$(USEMODULE))) # radio is IEEE 802.15.4 sub-GHz + DEFAULT_CHANNEL ?= 5 + CFLAGS += -DIEEE802154_DEFAULT_SUBGHZ_CHANNEL=$(DEFAULT_CHANNEL) +else # radio is IEEE 802.15.4 2.4 GHz + DEFAULT_CHANNEL ?= 26 + CFLAGS += -DIEEE802154_DEFAULT_CHANNEL=$(DEFAULT_CHANNEL) endif diff --git a/examples/gnrc_networking/Makefile b/examples/gnrc_networking/Makefile index 25c2dc858d..57ca39821a 100644 --- a/examples/gnrc_networking/Makefile +++ b/examples/gnrc_networking/Makefile @@ -65,15 +65,10 @@ QUIET ?= 1 include $(RIOTBASE)/Makefile.include # Set a custom channel if needed -ifneq (,$(filter cc110x,$(USEMODULE))) # radio is cc110x sub-GHz - DEFAULT_CHANNEL ?= 0 - CFLAGS += -DCC110X_DEFAULT_CHANNEL=$(DEFAULT_CHANNEL) -else - ifneq (,$(filter at86rf212b,$(USEMODULE))) # radio is IEEE 802.15.4 sub-GHz - DEFAULT_CHANNEL ?= 5 - CFLAGS += -DIEEE802154_DEFAULT_SUBGHZ_CHANNEL=$(DEFAULT_CHANNEL) - else # radio is IEEE 802.15.4 2.4 GHz - DEFAULT_CHANNEL ?= 26 - CFLAGS += -DIEEE802154_DEFAULT_CHANNEL=$(DEFAULT_CHANNEL) - endif +ifneq (,$(filter at86rf212b,$(USEMODULE))) # radio is IEEE 802.15.4 sub-GHz + DEFAULT_CHANNEL ?= 5 + CFLAGS += -DIEEE802154_DEFAULT_SUBGHZ_CHANNEL=$(DEFAULT_CHANNEL) +else # radio is IEEE 802.15.4 2.4 GHz + DEFAULT_CHANNEL ?= 26 + CFLAGS += -DIEEE802154_DEFAULT_CHANNEL=$(DEFAULT_CHANNEL) endif diff --git a/examples/nanocoap_server/Makefile b/examples/nanocoap_server/Makefile index 511f3b65be..8b908cb32b 100644 --- a/examples/nanocoap_server/Makefile +++ b/examples/nanocoap_server/Makefile @@ -61,15 +61,10 @@ QUIET ?= 1 include $(RIOTBASE)/Makefile.include # Set a custom channel if needed -ifneq (,$(filter cc110x,$(USEMODULE))) # radio is cc110x sub-GHz - DEFAULT_CHANNEL ?= 0 - CFLAGS += -DCC110X_DEFAULT_CHANNEL=$(DEFAULT_CHANNEL) -else - ifneq (,$(filter at86rf212b,$(USEMODULE))) # radio is IEEE 802.15.4 sub-GHz - DEFAULT_CHANNEL ?= 5 - CFLAGS += -DIEEE802154_DEFAULT_SUBGHZ_CHANNEL=$(DEFAULT_CHANNEL) - else # radio is IEEE 802.15.4 2.4 GHz - DEFAULT_CHANNEL ?= 26 - CFLAGS += -DIEEE802154_DEFAULT_CHANNEL=$(DEFAULT_CHANNEL) - endif +ifneq (,$(filter at86rf212b,$(USEMODULE))) # radio is IEEE 802.15.4 sub-GHz + DEFAULT_CHANNEL ?= 5 + CFLAGS += -DIEEE802154_DEFAULT_SUBGHZ_CHANNEL=$(DEFAULT_CHANNEL) +else # radio is IEEE 802.15.4 2.4 GHz + DEFAULT_CHANNEL ?= 26 + CFLAGS += -DIEEE802154_DEFAULT_CHANNEL=$(DEFAULT_CHANNEL) endif diff --git a/makefiles/pseudomodules.inc.mk b/makefiles/pseudomodules.inc.mk index 47ee91cff6..72542fb28f 100644 --- a/makefiles/pseudomodules.inc.mk +++ b/makefiles/pseudomodules.inc.mk @@ -97,6 +97,11 @@ PSEUDOMODULES += adc121c # full featured version of CCS811 driver as pseudo module 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 PSEUDOMODULES += sx1272 PSEUDOMODULES += sx1276 diff --git a/sys/auto_init/auto_init.c b/sys/auto_init/auto_init.c index 874ce21a8e..9585445687 100644 --- a/sys/auto_init/auto_init.c +++ b/sys/auto_init/auto_init.c @@ -206,6 +206,11 @@ void auto_init(void) auto_init_mrf24j40(); #endif +#ifdef MODULE_CC110X + extern void auto_init_cc110x(void); + auto_init_cc110x(); +#endif + #ifdef MODULE_CC2420 extern void auto_init_cc2420(void); auto_init_cc2420(); @@ -248,11 +253,6 @@ void auto_init(void) auto_init_slipdev(); #endif -#ifdef MODULE_CC110X - extern void auto_init_cc110x(void); - auto_init_cc110x(); -#endif - #ifdef MODULE_CC2538_RF extern void auto_init_cc2538_rf(void); auto_init_cc2538_rf(); diff --git a/sys/auto_init/netif/auto_init_cc110x.c b/sys/auto_init/netif/auto_init_cc110x.c index 7a0e9df67e..42645c6c84 100644 --- a/sys/auto_init/netif/auto_init_cc110x.c +++ b/sys/auto_init/netif/auto_init_cc110x.c @@ -1,5 +1,6 @@ /* * Copyright (C) 2015 Kaspar Schleiser + * 2016 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 @@ -7,62 +8,78 @@ * */ -/* - * @ingroup sys_auto_init_gnrc_netif +/** + * @ingroup sys_auto_init_gnrc_netif * @{ * * @file - * @brief Auto initialization for cc110x network interfaces + * @brief Auto initialization for cc110x network interfaces * - * @author Kaspar Schleiser + * @author Kaspar Schleiser + * @author Hauke Petersen */ #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 "cc1xxx_common.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 Define stack parameters for the MAC layer thread - * @{ + * @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_MAC_STACKSIZE (THREAD_STACKSIZE_DEFAULT + DEBUG_EXTRA_STACKSIZE) -#ifndef CC110X_MAC_PRIO -#define CC110X_MAC_PRIO (GNRC_NETIF_PRIO) +#define CC110X_EXTRA_STACKSIZE ((GNRC_NETIF_MSG_QUEUE_SIZE - 8) * sizeof(msg_t)) #endif -#define CC110X_NUM ARRAY_SIZE(cc110x_params) +/** + * @brief Calculate the stack size for the MAC layer thread(s) + */ +#define CC110X_MAC_STACKSIZE (THREAD_STACKSIZE_DEFAULT + \ + CC110X_EXTRA_STACKSIZE + \ + DEBUG_EXTRA_STACKSIZE) +#ifndef CC110X_MAC_PRIO +/** + * @brief The priority of the MAC layer thread + */ +#define CC110X_MAC_PRIO (GNRC_NETIF_PRIO) +#endif -static netdev_cc110x_t cc110x_devs[CC110X_NUM]; -static char _stacks[CC110X_NUM][CC110X_MAC_STACKSIZE]; +/** + * @brief Calculate the number of configured CC1100/CC1101 transceivers + */ +#define CC110X_NUM ARRAY_SIZE(cc110x_params) + +/** + * @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) { 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); - int res = netdev_cc110x_setup(&cc110x_devs[i], p); - if (res < 0) { - LOG_ERROR("[auto_init_netif] error initializing cc110x #%u\n", i); - } - else { - gnrc_netif_cc110x_create(_stacks[i], CC110X_MAC_STACKSIZE, - CC110X_MAC_PRIO, "cc110x", - (netdev_t *)&cc110x_devs[i]); - } + cc110x_setup(&_cc110x_devs[i], &cc110x_params[i]); + gnrc_netif_cc1xxx_create(stacks[i], CC110X_MAC_STACKSIZE, CC110X_MAC_PRIO, + "cc110x", (netdev_t *)&_cc110x_devs[i]); } } + #else typedef int dont_be_pedantic; #endif /* MODULE_CC110X */ - /** @} */ diff --git a/tests/driver_cc110x/Makefile b/tests/driver_cc110x/Makefile new file mode 100644 index 0000000000..d2f9afbc71 --- /dev/null +++ b/tests/driver_cc110x/Makefile @@ -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 diff --git a/tests/driver_cc110x/main.c b/tests/driver_cc110x/main.c new file mode 100644 index 0000000000..5e3ff85d62 --- /dev/null +++ b/tests/driver_cc110x/main.c @@ -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 + * + * @} + */ + +#include +#include + +#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; +} diff --git a/tests/driver_cc110x/sc_cc110x.c b/tests/driver_cc110x/sc_cc110x.c new file mode 100644 index 0000000000..5cf55d652f --- /dev/null +++ b/tests/driver_cc110x/sc_cc110x.c @@ -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 +#include +#include +#include +#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; +} diff --git a/tests/gnrc_udp/Makefile b/tests/gnrc_udp/Makefile index 16d201ced2..b4d10df9b2 100644 --- a/tests/gnrc_udp/Makefile +++ b/tests/gnrc_udp/Makefile @@ -31,15 +31,10 @@ USEMODULE += netstats_ipv6 include $(RIOTBASE)/Makefile.include # Set a custom channel if needed -ifneq (,$(filter cc110x,$(USEMODULE))) # radio is cc110x sub-GHz - DEFAULT_CHANNEL ?= 0 - CFLAGS += -DCC110X_DEFAULT_CHANNEL=$(DEFAULT_CHANNEL) -else - ifneq (,$(filter at86rf212b,$(USEMODULE))) # radio is IEEE 802.15.4 sub-GHz - DEFAULT_CHANNEL ?= 5 - CFLAGS += -DIEEE802154_DEFAULT_SUBGHZ_CHANNEL=$(DEFAULT_CHANNEL) - else # radio is IEEE 802.15.4 2.4 GHz - DEFAULT_CHANNEL ?= 26 - CFLAGS += -DIEEE802154_DEFAULT_CHANNEL=$(DEFAULT_CHANNEL) - endif +ifneq (,$(filter at86rf212b,$(USEMODULE))) # radio is IEEE 802.15.4 sub-GHz + DEFAULT_CHANNEL ?= 5 + CFLAGS += -DIEEE802154_DEFAULT_SUBGHZ_CHANNEL=$(DEFAULT_CHANNEL) +else # radio is IEEE 802.15.4 2.4 GHz + DEFAULT_CHANNEL ?= 26 + CFLAGS += -DIEEE802154_DEFAULT_CHANNEL=$(DEFAULT_CHANNEL) endif diff --git a/tests/netstats_l2/Makefile b/tests/netstats_l2/Makefile index 1f31841b63..627fa54d29 100644 --- a/tests/netstats_l2/Makefile +++ b/tests/netstats_l2/Makefile @@ -2,7 +2,7 @@ include ../Makefile.tests_common 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 \ - yunjia-nrf51822 \ + yunjia-nrf51822 msba2 \ esp32-mh-et-live-minikit esp32-olimex-evb \ esp32-wemos-lolin-d32-pro esp32-wroom-32 esp32-wrover-kit