diff --git a/drivers/at86rf215/at86rf215.c b/drivers/at86rf215/at86rf215.c index f962d77ff7..2e0822dd6b 100644 --- a/drivers/at86rf215/at86rf215.c +++ b/drivers/at86rf215/at86rf215.c @@ -125,7 +125,7 @@ if (!IS_ACTIVE(CONFIG_AT86RF215_USE_CLOCK_OUTPUT)){ } /* allow to configure board-specific trim */ #ifdef CONFIG_AT86RF215_TRIM_VAL - at86rf215_reg_write(dev, RG_RF_XOC, CONFIG_AT86RF215_TRIM_VAL | XOC_FS_MASK); + at86rf215_set_trim(dev, CONFIG_AT86RF215_TRIM_VAL); #endif /* enable TXFE & RXFE IRQ */ diff --git a/drivers/at86rf215/at86rf215_getset.c b/drivers/at86rf215/at86rf215_getset.c index 473139a72c..7a1f65f13a 100644 --- a/drivers/at86rf215/at86rf215_getset.c +++ b/drivers/at86rf215/at86rf215_getset.c @@ -89,6 +89,18 @@ uint8_t at86rf215_get_chan(const at86rf215_t *dev) return at86rf215_reg_read16(dev, dev->RF->RG_CNL); } +void at86rf215_set_trim(at86rf215_t *dev, uint8_t trim) +{ + assert(trim <= 0xF); + at86rf215_reg_write(dev, RG_RF_XOC, trim | XOC_FS_MASK); +} + +void at86rf215_set_clock_output(at86rf215_t *dev, + at86rf215_clko_cur_t cur, at86rf215_clko_freq_t freq) +{ + at86rf215_reg_write(dev, RG_RF_CLKO, cur | freq); +} + void at86rf215_set_chan(at86rf215_t *dev, uint16_t channel) { at86rf215_await_state_end(dev, RF_STATE_TX); diff --git a/drivers/include/at86rf215.h b/drivers/include/at86rf215.h index 022d23cdba..d9ef609a4d 100644 --- a/drivers/include/at86rf215.h +++ b/drivers/include/at86rf215.h @@ -272,6 +272,33 @@ enum { AT86RF215_MODE_MR_FSK }; +/** + * @name Clock Output Driver Strength + * @{ + */ +typedef enum { + AT86RF215_CLKO_2mA = 0 << 3, /**< 2 mA */ + AT86RF215_CLKO_4mA = 1 << 3, /**< 4 mA */ + AT86RF215_CLKO_6mA = 2 << 3, /**< 6 mA */ + AT86RF215_CLKO_8mA = 3 << 3, /**< 8 mA */ +} at86rf215_clko_cur_t; +/** @} */ + +/** + * @name Clock Output Frequency + * @{ + */ +typedef enum { + AT86RF215_CLKO_OFF = 0, /**< Clock Output Disabled */ + AT86RF215_CLKO_26_MHz, /**< 26 MHz */ + AT86RF215_CLKO_32_MHz, /**< 32 MHz */ + AT86RF215_CLKO_16_MHz, /**< 16 MHz */ + AT86RF215_CLKO_8_MHz, /**< 8 MHz */ + AT86RF215_CLKO_4_MHz, /**< 4 MHz */ + AT86RF215_CLKO_2_MHz, /**< 2 MHz */ + AT86RF215_CLKO_1_MHz, /**< 1 MHz */ +} at86rf215_clko_freq_t; + /** * @name Internal device option flags * @{ @@ -526,6 +553,42 @@ int8_t at86rf215_get_ed_level(at86rf215_t *dev); */ void at86rf215_set_option(at86rf215_t *dev, uint16_t option, bool state); +/** + * @brief Set crystal oscillator trim value. + * + * An internal capacitance array is connected to the + * crystal oscillator pins TCXO and XTAL2. + * + * Each increment of the trim value adds 0.3pF capacitance + * to the oscillator circuit. + * + * To trim a board, enable the clock output with + * @ref at86rf215_set_clock_output and connect a frequency + * counter to the clock output pin. + * Then adjust the trim value until it the measured frequency + * closely matches the configured output frequency. + * + * It is recommended to use a 26 MHz output frequency for the + * test as this is the raw frequency of the external oscillator. + * + * The resulting trim value must then be stored in a persistent + * memory area of the board to be set via @ref CONFIG_AT86RF215_TRIM_VAL + * + * @param[in] dev device to configure + * @param[in] trim trim value + */ +void at86rf215_set_trim(at86rf215_t *dev, uint8_t trim); + +/** + * @brief Configure the Clock Output pin + * + * @param[in] dev device to configure + * @param[in] cur Clock output current + * @param[in] freq Clock output frequency + */ +void at86rf215_set_clock_output(at86rf215_t *dev, + at86rf215_clko_cur_t cur, at86rf215_clko_freq_t freq); + /** * @brief Convenience function for simply sending data * diff --git a/drivers/include/net/netdev.h b/drivers/include/net/netdev.h index 00043cdcd7..21dd89c317 100644 --- a/drivers/include/net/netdev.h +++ b/drivers/include/net/netdev.h @@ -297,6 +297,11 @@ typedef enum { } netdev_type_t; /** @} */ +/** + * @brief Will match any device index + */ +#define NETDEV_INDEX_ANY (0xFF) + /** * @brief Structure to hold driver state * diff --git a/sys/include/net/eui_provider.h b/sys/include/net/eui_provider.h index cccd03c19d..87282ae007 100644 --- a/sys/include/net/eui_provider.h +++ b/sys/include/net/eui_provider.h @@ -103,11 +103,6 @@ extern "C" { #endif -/** - * @brief Will match any device index - */ -#define NETDEV_INDEX_ANY (0xFF) - /** * @brief Function for providing a EUI-48 to a device * diff --git a/sys/include/net/gnrc/netif.h b/sys/include/net/gnrc/netif.h index 070b504fb5..e2a3e510c2 100644 --- a/sys/include/net/gnrc/netif.h +++ b/sys/include/net/gnrc/netif.h @@ -566,6 +566,19 @@ int gnrc_netif_get_from_netdev(gnrc_netif_t *netif, gnrc_netapi_opt_t *opt); int gnrc_netif_set_from_netdev(gnrc_netif_t *netif, const gnrc_netapi_opt_t *opt); +/** + * @brief Gets an interface by the netdev type (and index) + * + * @pre The netdev has been registered with @ref netdev_register + * + * @param[in] type driver type of the netdev, can be @ref NETDEV_ANY + * @param[in] index index of the netdev, can be @ref NETDEV_INDEX_ANY + * + * @return The network interface that has a netdev of matching type and index + * NULL if no matching interface could be found + */ +gnrc_netif_t *gnrc_netif_get_by_type(netdev_type_t type, uint8_t index); + /** * @brief Converts a hardware address to a human readable string. * diff --git a/sys/net/gnrc/netif/gnrc_netif.c b/sys/net/gnrc/netif/gnrc_netif.c index f9fe3e4154..6e33d5b199 100644 --- a/sys/net/gnrc/netif/gnrc_netif.c +++ b/sys/net/gnrc/netif/gnrc_netif.c @@ -117,6 +117,31 @@ gnrc_netif_t *gnrc_netif_iter(const gnrc_netif_t *prev) return (gnrc_netif_t*) netif_iter((netif_t*) prev); } +gnrc_netif_t *gnrc_netif_get_by_type(netdev_type_t type, uint8_t index) +{ + gnrc_netif_t *netif = NULL; + while ((netif = gnrc_netif_iter(netif))) { + +#ifdef MODULE_NETDEV_REGISTER + if (netif->dev->type != type && type != NETDEV_ANY) { + continue; + } + + if (netif->dev->index != index && index != NETDEV_INDEX_ANY) { + continue; + } +#else + (void)type; + (void)index; + assert(index == 0); +#endif + + return netif; + } + + return NULL; +} + int gnrc_netif_get_from_netdev(gnrc_netif_t *netif, gnrc_netapi_opt_t *opt) { int res = -ENOTSUP; diff --git a/tests/driver_at86rf215/main.c b/tests/driver_at86rf215/main.c index c558eeb2de..78accc58ab 100644 --- a/tests/driver_at86rf215/main.c +++ b/tests/driver_at86rf215/main.c @@ -20,13 +20,16 @@ #include #include "at86rf215.h" +#include "at86rf215_internal.h" #include "thread.h" #include "shell.h" #include "shell_commands.h" #include "sys/bus.h" #include "net/gnrc/pktdump.h" +#include "net/gnrc/netif.h" #include "net/gnrc.h" +#include "od.h" static char batmon_stack[THREAD_STACKSIZE_MAIN]; @@ -74,8 +77,126 @@ static int cmd_enable_batmon(int argc, char **argv) return res; } +static int cmd_set_trim(int argc, char **argv) +{ + if (argc < 2) { + printf("usage: %s \n", argv[0]); + return 1; + } + + uint8_t trim = atoi(argv[1]); + + if (trim > 0xF) { + puts("Trim value out of range"); + return 1; + } + + gnrc_netif_t *netif = gnrc_netif_get_by_type(NETDEV_AT86RF215, 0); + + if (netif == NULL) { + puts("No at86rf215 radio found"); + return 1; + } + + at86rf215_t* dev = (at86rf215_t*)netif->dev; + + printf("setting trim to %u fF\n", 300U * trim); + at86rf215_set_trim(dev, trim); + + return 0; +} + +static int cmd_set_clock_out(int argc, char **argv) +{ + const char *keys[] = { + [AT86RF215_CLKO_OFF] = "off", + [AT86RF215_CLKO_26_MHz] = "26", + [AT86RF215_CLKO_32_MHz] = "32", + [AT86RF215_CLKO_16_MHz] = "16", + [AT86RF215_CLKO_8_MHz] = "8", + [AT86RF215_CLKO_4_MHz] = "4", + [AT86RF215_CLKO_2_MHz] = "2", + [AT86RF215_CLKO_1_MHz] = "1", + }; + + at86rf215_clko_freq_t freq = AT86RF215_CLKO_26_MHz; + + if (argc > 1) { + unsigned tmp = 0xFF; + for (unsigned i = 0; i < ARRAY_SIZE(keys); ++i) { + if (strcmp(argv[1], keys[i]) == 0) { + tmp = i; + break; + } + } + + if (tmp == 0xFF) { + printf("usage: %s [freq in MHz | off]\n", argv[0]); + printf("valid frequencies: off"); + for (unsigned i = 1; i < ARRAY_SIZE(keys); ++i) { + printf(", %s MHz", keys[i]); + } + puts(""); + return 1; + } + + freq = tmp; + } + + + gnrc_netif_t *netif = gnrc_netif_get_by_type(NETDEV_AT86RF215, 0); + + if (netif == NULL) { + puts("No at86rf215 radio found"); + return 1; + } + + at86rf215_t *dev = (at86rf215_t *)netif->dev; + + printf("Clock output set to %s %s\n", keys[freq], freq ? "MHz" : ""); + at86rf215_set_clock_output(dev, AT86RF215_CLKO_4mA, freq); + + return 0; +} + +static int cmd_get_random(int argc, char **argv) +{ + uint8_t values; + uint8_t buffer[256]; + + if (argc > 1) { + values = atoi(argv[1]); + } + else { + values = 16; + } + + if (values == 0) { + printf("usage: %s [num]\n", argv[0]); + return 1; + } + + gnrc_netif_t *netif = gnrc_netif_get_by_type(NETDEV_AT86RF215, 0); + + if (netif == NULL) { + puts("No at86rf215 radio found"); + return 1; + } + + at86rf215_t *dev = (at86rf215_t *)netif->dev; + + at86rf215_get_random(dev, buffer, values); + + od_hex_dump(buffer, values, 0); + + return 0; +} + static const shell_command_t shell_commands[] = { { "batmon", "Enable the battery monitor", cmd_enable_batmon }, + { "set_trim", "at86rf215: Set the trim value of the crystal oscillator", cmd_set_trim }, + { "set_clko", "at86rf215: Configure the Clock Output pin", cmd_set_clock_out }, + { "get_random", "at86rf215: Get random values from the radio", cmd_get_random }, { NULL, NULL, NULL } };