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; +}