diff --git a/tests/periph_spi/Makefile b/tests/periph_spi/Makefile index 7f3aae67c4..70ea313087 100644 --- a/tests/periph_spi/Makefile +++ b/tests/periph_spi/Makefile @@ -3,6 +3,7 @@ include ../Makefile.tests_common FEATURES_REQUIRED = periph_spi +USEMODULE += xtimer USEMODULE += shell USEMODULE += shell_commands diff --git a/tests/periph_spi/main.c b/tests/periph_spi/main.c index b251ef946b..6bf46d92f1 100644 --- a/tests/periph_spi/main.c +++ b/tests/periph_spi/main.c @@ -24,307 +24,402 @@ #include #include -#include "board.h" +#include "xtimer.h" #include "shell.h" #include "periph/spi.h" -#include "periph/gpio.h" -enum { - READ = 0, - WRITE, - INIT -} rw; +/** + * @brief Some parameters used for benchmarking + */ +#define BENCH_REDOS (1000) +#define BENCH_SMALL (2) +#define BENCH_LARGE (100) +#define BENCH_PAYLOAD ('b') +#define BENCH_REGADDR (0x23) -static int spi_dev = -1; -static gpio_t spi_cs = -1; -static int spi_mode_int = -1; -static spi_conf_t spi_mode = -1; -static int spi_speed_int = -1; -static spi_speed_t spi_speed = -1; +#define BUF_SIZE (512U) -/* 0 for slave, 1 for master, -1 for not initialized */ -static int spi_master = -1; -static int port = -1; -static int pin = -1; +/** + * @brief Benchmark buffers + */ +static uint8_t bench_wbuf[BENCH_LARGE]; +static uint8_t bench_rbuf[BENCH_LARGE]; -static char buffer[256]; /* temporary buffer */ -static char rx_buffer[256]; /* global receive buffer */ -static int rx_counter = 0; +/** + * @brief Generic buffer used for receiving + */ +static uint8_t buf[BUF_SIZE]; -static volatile int state; -static char* mem = "Hello Master! abcdefghijklmnopqrstuvwxyz 0123456789 " - "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; +static struct { + spi_t dev; + spi_mode_t mode; + spi_clk_t clk; + spi_cs_t cs; +} spiconf; -int parse_spi_dev(int argc, char **argv) +void print_bytes(char* title, uint8_t* data, size_t len) { - /* reset default values */ - spi_dev = SPI_0; - spi_mode = SPI_CONF_FIRST_RISING; - spi_speed = SPI_SPEED_1MHZ; - - if (argc < 4) { - printf("usage: %s [mode [speed]]\n", argv[0]); - puts(" DEV is the SPI device to use:"); - for (int i = 0; i < SPI_NUMOF; i++) { - printf(" %i - SPI_%i\n", i, i); - } - puts(" cs port: port to use as the chip select line"); - puts(" cs pin: pin to use on th given port as cs line"); - puts(" mode: must be one of the following options (* marks " - "default value):"); - puts(" *0 - POL:0, PHASE:0 - ON FIRST RISING EDGE"); - puts(" 1 - POL:0, PHASE:1 - ON SECOND RISING EDGE"); - puts(" 2 - POL:1, PHASE:0 - ON FIRST FALLING EDGE"); - puts(" 3 - POL:1, PHASE:1 - on second falling edge"); - puts(" speed: must be one of the following options (only used " - "in master mode):"); - puts(" 0 - 100 KHz"); - puts(" 1 - 400 KHz"); - puts(" *2 - 1 MHz"); - puts(" 3 - 5 MHz"); - puts(" 4 - 10 MHz\n"); - return -4; + printf("%4s\n", title); + for (size_t i = 0; i < len; i++) { + printf(" %2i ", (int)i); } - spi_dev = atoi(argv[1]); - if (spi_dev < 0 || spi_dev >= SPI_NUMOF) { - puts("error: invalid DEV value given"); - return -1; + printf("\n "); + for (size_t i = 0; i < len; i++) { + printf(" 0x%02x", (int)data[i]); } - port = atoi(argv[2]); - pin = atoi(argv[3]); - spi_cs = GPIO_PIN(port,pin); - if (argc >= 5) { - spi_mode_int = argv[4][0] - '0'; - if (spi_mode_int < 0 || spi_mode_int > 3) { - puts("error: invalid MODE value given"); - return -2; - } else { - switch (spi_mode_int) { - case 0: - spi_mode = SPI_CONF_FIRST_RISING; - break; - case 1: - spi_mode = SPI_CONF_SECOND_RISING; - break; - case 2: - spi_mode = SPI_CONF_FIRST_FALLING; - break; - case 3: - spi_mode = SPI_CONF_SECOND_FALLING; - break; - } - } - } - if (argc >= 6) { - spi_speed_int = argv[5][0] - '0'; - if (spi_speed_int < 0 || spi_speed_int > 4) { - puts("error: invalid SPEED value given"); - return -3; - } else { - switch (spi_speed_int) { - case 0: - spi_speed = SPI_SPEED_100KHZ; - break; - case 1: - spi_speed = SPI_SPEED_400KHZ; - break; - case 2: - spi_speed = SPI_SPEED_1MHZ; - break; - case 3: - spi_speed = SPI_SPEED_5MHZ; - break; - case 4: - spi_speed = SPI_SPEED_10MHZ; - break; - } - } - } - return 0; -} - -void print_bytes(char* title, char* chars, int length) -{ - printf("%4s", title); - for (int i = 0; i < length; i++) { - printf(" %2i ", i); - } - printf("\n "); - for (int i = 0; i < length; i++) { - printf(" 0x%02x", (int)chars[i]); - } - printf("\n "); - for (int i = 0; i < length; i++) { - if (chars[i] < ' ' || chars[i] > '~') { + printf("\n "); + for (size_t i = 0; i < len; i++) { + if (data[i] < ' ' || data[i] > '~') { printf(" ?? "); } else { - printf(" %c ", chars[i]); + printf(" %c ", (char)data[i]); } } printf("\n\n"); } -void slave_on_cs(void *arg) +int cmd_init(int argc, char **argv) { - (void)arg; + int dev, mode, clk, port, pin, tmp; - spi_transmission_begin(spi_dev, 'F'); - state = 0; - rw = INIT; -} - -char slave_on_data(char data) -{ - rx_buffer[rx_counter] = data; - rx_counter++; - if (rx_counter >= 256) { - rx_counter = 0; + if (argc < 5) { + printf("usage: %s \n", argv[0]); + puts("\tdev:"); + for (int i = 0; i < (int)SPI_NUMOF; i++) { + printf("\t\t%i: SPI_DEV(%i)\n", i, i); + } + puts("\tmode:"); + puts("\t\t0: POL:0, PHASE:0 - on first rising edge"); + puts("\t\t1: POL:0, PHASE:1 - on second rising edge"); + puts("\t\t2: POL:1, PHASE:0 - on first falling edge"); + puts("\t\t3: POL:1, PHASE:1 - on second falling edge"); + puts("\tclk:"); + puts("\t\t0: 100 KHz"); + puts("\t\t1: 400 KHz"); + puts("\t\t2: 1 MHz"); + puts("\t\t3: 5 MHz"); + puts("\t\t4: 10 MHz"); + puts("\tcs port:"); + puts("\t\tPort of the CS pin, set to -1 for hardware chip select"); + puts("\tcs pin:"); + puts("\t\tPin used for chip select. If hardware chip select is enabled,\n" + "\t\tthis value specifies the internal HWCS line"); + return 1; } - switch (rw) { - case READ: - return mem[state++]; - case WRITE: - mem[state++] = data; - return 'o'; - case INIT: - if (data == ' ') { - rw = READ; - return mem[state++]; - } else if (data & 0x80) { - rw = WRITE; - state = (data & 0x7f); - return 'W'; - } else { - rw = READ; - state = data; - return mem[state++]; - } + /* parse the given SPI device */ + dev = atoi(argv[1]); + if (dev < 0 || dev >= SPI_NUMOF) { + puts("error: invalid SPI device specified"); + return 1; } - return 'e'; -} + spiconf.dev = SPI_DEV(dev); -int cmd_init_master(int argc, char **argv) -{ - int res; - spi_master = -1; - if (parse_spi_dev(argc, argv) < 0) { - return 1; + /* parse the SPI mode */ + mode = atoi(argv[2]); + switch (mode) { + case 0: spiconf.mode = SPI_MODE_0; break; + case 1: spiconf.mode = SPI_MODE_1; break; + case 2: spiconf.mode = SPI_MODE_2; break; + case 3: spiconf.mode = SPI_MODE_3; break; + default: + puts("error: invalid SPI mode specified"); + return 1; } - spi_acquire(spi_dev); - res = spi_init_master(spi_dev, spi_mode, spi_speed); - spi_release(spi_dev); - if (res < 0) { - printf("spi_init_master: error initializing SPI_%i device (code %i)\n", - spi_dev, res); - return 1; - } - res = gpio_init(spi_cs, GPIO_OUT); - if (res < 0){ - printf("gpio_init: error initializing GPIO_PIN(%i, %i) as CS line (code %i)\n", - port, pin, res); - return 1; - } - gpio_set(spi_cs); - spi_master = 1; - printf("SPI_%i successfully initialized as master, cs: GPIO_PIN(%i, %i), mode: %i, speed: %i\n", - spi_dev, port, pin, spi_mode_int, spi_speed_int); - return 0; -} -int cmd_init_slave(int argc, char **argv) -{ - int res; - spi_master = -1; - if (parse_spi_dev(argc, argv) < 0) { + /* parse the targeted clock speed */ + clk = atoi(argv[3]); + switch (clk) { + case 0: spiconf.clk = SPI_CLK_100KHZ; break; + case 1: spiconf.clk = SPI_CLK_400KHZ; break; + case 2: spiconf.clk = SPI_CLK_1MHZ; break; + case 3: spiconf.clk = SPI_CLK_5MHZ; break; + case 4: spiconf.clk = SPI_CLK_10MHZ; break; + default: + puts("error: invalid bus speed specified"); + return 1; + } + + /* parse chip select port and pin */ + port = atoi(argv[4]); + pin = atoi(argv[5]); + if (pin < 0 || port < -1) { + puts("error: invalid CS port/pin combination specified"); + } + if (port == -1) { /* hardware chip select line */ + spiconf.cs = SPI_HWCS(pin); + } + else { + spiconf.cs = (spi_cs_t)GPIO_PIN(port, pin); + } + + /* test setup */ + tmp = spi_init_cs(spiconf.dev, spiconf.cs); + if (tmp != SPI_OK) { + puts("error: unable to initialize the given chip select line"); return 1; } - spi_acquire(spi_dev); - res = spi_init_slave(spi_dev, spi_mode, slave_on_data); - spi_release(spi_dev); - if (res < 0) { - printf("spi_init_slave: error initializing SPI_%i device (code: %i)\n", - spi_dev, res); + tmp = spi_acquire(spiconf.dev, spiconf.cs, spiconf.mode, spiconf.clk); + if (tmp == SPI_NOMODE) { + puts("error: given SPI mode is not supported"); return 1; } - res = gpio_init_int(spi_cs, GPIO_IN, GPIO_FALLING, slave_on_cs, 0); - if (res < 0){ - printf("gpio_init_int: error initializing GPIO_PIN(%i, %i) as CS line (code %i)\n", - port, pin, res); + else if (tmp == SPI_NOCLK) { + puts("error: targeted clock speed is not supported"); return 1; } - spi_master = 0; - printf("SPI_%i successfully initialized as slave, cs: GPIO_PIN(%i, %i), mode: %i\n", - spi_dev, port, pin, spi_mode_int); + else if (tmp != SPI_OK) { + puts("error: unable to acquire bus with given parameters"); + return 1; + } + spi_release(spiconf.dev); + + printf("SPI_DEV(%i) initialized: mode: %i, clk: %i, cs_port: %i, cs_pin: %i\n", + dev, mode, clk, port, pin); + return 0; } int cmd_transfer(int argc, char **argv) { - int res; - char *hello = "Hello"; - - if (spi_master != 1) { - puts("error: node is not initialized as master, please do so first"); - return 1; - } + size_t len; if (argc < 2) { - puts("No data to transfer given, will transfer 'Hello' to device"); - } - else { - hello = argv[1]; - } - - /* do the actual data transfer */ - spi_acquire(spi_dev); - gpio_clear(spi_cs); - res = spi_transfer_bytes(spi_dev, hello, buffer, strlen(hello)); - gpio_set(spi_cs); - spi_release(spi_dev); - - /* look at the results */ - if (res < 0) { - printf("error: unable to transfer data to slave (code: %i)\n", res); + printf("usage: %s \n", argv[0]); return 1; } - else { - printf("Transfered %i bytes:\n", res); - print_bytes("MOSI", hello, res); - print_bytes("MISO", buffer, res); - return 0; + + if (spiconf.dev == SPI_UNDEF) { + puts("error: SPI is not initialized, please initialize bus first"); + return 1; } + + /* get bus access */ + if (spi_acquire(spiconf.dev, spiconf.cs, + spiconf.mode, spiconf.clk) != SPI_OK) { + puts("error: unable to acquire the SPI bus"); + return 1; + } + + /* transfer data */ + len = strlen(argv[1]); + memset(buf, 0, sizeof(buf)); + spi_transfer_bytes(spiconf.dev, spiconf.cs, false, argv[1], buf, len); + + /* release the bus */ + spi_release(spiconf.dev); + + /* print results */ + print_bytes("Sent bytes", (uint8_t *)argv[1], len); + print_bytes("Received bytes", buf, len); + + return 0; } -int cmd_print(int argc, char **argv) +int cmd_bench(int argc, char **argv) { - if (spi_master != 0) { - puts("error: node is not initialized as slave"); + uint32_t start, stop; + uint32_t sum = 0; + uint8_t in; + uint8_t out = (uint8_t)BENCH_PAYLOAD; + + if (spiconf.dev == SPI_UNDEF) { + puts("error: SPI is not initialized, please initialize bus first"); return 1; } - else { - printf("Received %i bytes:\n", rx_counter); - print_bytes("MOSI", rx_buffer, rx_counter); + + /* prepare buffer */ + memset(bench_wbuf, BENCH_PAYLOAD, BENCH_LARGE); + + /* get access to the bus */ + if (spi_acquire(spiconf.dev, spiconf.cs, + spiconf.mode, spiconf.clk) != SPI_OK) { + puts("error: unable to acquire the SPI bus"); + return 1; } - rx_counter = 0; - memset(&rx_buffer, 0, 256); + + puts("### Running some benchmarks, all values in [us] ###\n"); + + /* 1 - write 1000 times 1 byte */ + start = xtimer_now_usec(); + for (int i = 0; i < BENCH_REDOS; i++) { + in = spi_transfer_byte(spiconf.dev, spiconf.cs, false, out); + (void)in; + } + stop = xtimer_now_usec(); + printf(" 1 - write %i times %i byte:", BENCH_REDOS, 1); + printf("\t\t\t%i\n", (int)(stop - start)); + sum += (stop - start); + + /* 2 - write 1000 times 2 byte */ + start = xtimer_now_usec(); + for (int i = 0; i < BENCH_REDOS; i++) { + spi_transfer_bytes(spiconf.dev, spiconf.cs, false, + bench_wbuf, NULL, BENCH_SMALL); + } + stop = xtimer_now_usec(); + printf(" 2 - write %i times %i byte:", BENCH_REDOS, BENCH_SMALL); + printf("\t\t\t%i\n", (int)(stop - start)); + sum += (stop - start); + + /* 3 - write 1000 times 100 byte */ + start = xtimer_now_usec(); + for (int i = 0; i < BENCH_REDOS; i++) { + spi_transfer_bytes(spiconf.dev, spiconf.cs, false, + bench_wbuf, NULL, BENCH_LARGE); + } + stop = xtimer_now_usec(); + printf(" 3 - write %i times %i byte:", BENCH_REDOS, BENCH_LARGE); + printf("\t\t%i\n", (int)(stop - start)); + sum += (stop - start); + + /* 4 - write 1000 times 1 byte to register */ + start = xtimer_now_usec(); + for (int i = 0; i < BENCH_REDOS; i++) { + in = spi_transfer_reg(spiconf.dev, spiconf.cs, BENCH_REGADDR, out); + (void)in; + } + stop = xtimer_now_usec(); + printf(" 4 - write %i times %i byte to register:", BENCH_REDOS, 1); + printf("\t%i\n", (int)(stop - start)); + sum += (stop - start); + + /* 5 - write 1000 times 2 byte to register */ + start = xtimer_now_usec(); + for (int i = 0; i < BENCH_REDOS; i++) { + spi_transfer_regs(spiconf.dev, spiconf.cs, BENCH_REGADDR, + bench_wbuf, NULL, BENCH_SMALL); + } + stop = xtimer_now_usec(); + printf(" 5 - write %i times %i byte to register:", BENCH_REDOS, BENCH_SMALL); + printf("\t%i\n", (int)(stop - start)); + sum += (stop - start); + + /* 6 - write 1000 times 100 byte to register */ + start = xtimer_now_usec(); + for (int i = 0; i < BENCH_REDOS; i++) { + spi_transfer_regs(spiconf.dev, spiconf.cs, BENCH_REGADDR, + bench_wbuf, NULL, BENCH_LARGE); + } + stop = xtimer_now_usec(); + printf(" 6 - write %i times %i byte to register:", BENCH_REDOS, BENCH_LARGE); + printf("\t%i\n", (int)(stop - start)); + sum += (stop - start); + + /* 7 - read 1000 times 2 byte */ + start = xtimer_now_usec(); + for (int i = 0; i < BENCH_REDOS; i++) { + spi_transfer_bytes(spiconf.dev, spiconf.cs, false, + NULL, bench_rbuf, BENCH_SMALL); + } + stop = xtimer_now_usec(); + printf(" 7 - read %i times %i byte:", BENCH_REDOS, BENCH_SMALL); + printf("\t\t\t%i\n", (int)(stop - start)); + sum += (stop - start); + + /* 8 - read 1000 times 100 byte */ + start = xtimer_now_usec(); + for (int i = 0; i < BENCH_REDOS; i++) { + spi_transfer_bytes(spiconf.dev, spiconf.cs, false, + NULL, bench_rbuf, BENCH_LARGE); + } + stop = xtimer_now_usec(); + printf(" 8 - read %i times %i byte:", BENCH_REDOS, BENCH_LARGE); + printf("\t\t\t%i\n", (int)(stop - start)); + sum += (stop - start); + + /* 9 - read 1000 times 2 byte from register */ + start = xtimer_now_usec(); + for (int i = 0; i < BENCH_REDOS; i++) { + spi_transfer_regs(spiconf.dev, spiconf.cs, BENCH_REGADDR, + NULL, bench_rbuf, BENCH_SMALL); + } + stop = xtimer_now_usec(); + printf(" 9 - read %i times %i byte from register:", BENCH_REDOS, BENCH_SMALL); + printf("\t%i\n", (int)(stop - start)); + sum += (stop - start); + + /* 10 - read 1000 times 100 byte from register */ + start = xtimer_now_usec(); + for (int i = 0; i < BENCH_REDOS; i++) { + spi_transfer_regs(spiconf.dev, spiconf.cs, BENCH_REGADDR, + NULL, bench_rbuf, BENCH_LARGE); + } + stop = xtimer_now_usec(); + printf("10 - read %i times %i byte from register:", BENCH_REDOS, BENCH_LARGE); + printf("\t%i\n", (int)(stop - start)); + sum += (stop - start); + + /* 11 - transfer 1000 times 2 byte */ + start = xtimer_now_usec(); + for (int i = 0; i < BENCH_REDOS; i++) { + spi_transfer_bytes(spiconf.dev, spiconf.cs, false, + bench_wbuf, bench_rbuf, BENCH_SMALL); + } + stop = xtimer_now_usec(); + printf("11 - transfer %i times %i byte:", BENCH_REDOS, BENCH_SMALL); + printf("\t\t%i\n", (int)(stop - start)); + sum += (stop - start); + + /* 12 - transfer 1000 times 100 byte */ + start = xtimer_now_usec(); + for (int i = 0; i < BENCH_REDOS; i++) { + spi_transfer_bytes(spiconf.dev, spiconf.cs, false, + bench_wbuf, bench_rbuf, BENCH_LARGE); + } + stop = xtimer_now_usec(); + printf("12 - transfer %i times %i byte:", BENCH_REDOS, BENCH_LARGE); + printf("\t\t%i\n", (int)(stop - start)); + sum += (stop - start); + + /* 13 - transfer 1000 times 2 byte from/to register */ + start = xtimer_now_usec(); + for (int i = 0; i < BENCH_REDOS; i++) { + spi_transfer_regs(spiconf.dev, spiconf.cs, BENCH_REGADDR, + bench_wbuf, bench_rbuf, BENCH_SMALL); + } + stop = xtimer_now_usec(); + printf("13 - transfer %i times %i byte to register:", BENCH_REDOS, BENCH_SMALL); + printf("\t%i\n", (int)(stop - start)); + sum += (stop - start); + + /* 14 - transfer 1000 times 100 byte from/to register */ + start = xtimer_now_usec(); + for (int i = 0; i < BENCH_REDOS; i++) { + spi_transfer_regs(spiconf.dev, spiconf.cs, BENCH_REGADDR, + bench_wbuf, bench_rbuf, BENCH_LARGE); + } + stop = xtimer_now_usec(); + printf("14 - transfer %i times %i byte to register:", BENCH_REDOS, BENCH_LARGE); + printf("\t%i\n", (int)(stop - start)); + sum += (stop - start); + + printf("-- - SUM:\t\t\t\t\t%i\n", (int)sum); + + spi_release(spiconf.dev); + puts("\n### All runs complete ###"); + return 0; } static const shell_command_t shell_commands[] = { - { "init_master", "Initialize node as SPI master", cmd_init_master }, - { "init_slave", "Initialize node as SPI slave", cmd_init_slave }, - { "send", "Transfer string to slave (only in master mode)", cmd_transfer }, - { "print_rx", "Print the received string (only in slave mode)", cmd_print }, + { "init", "Setup a particular SPI configuration", cmd_init }, + { "send", "Transfer string to slave", cmd_transfer }, + { "bench", "Runs some benchmarks", cmd_bench }, { NULL, NULL, NULL } }; int main(void) { - puts("\nRIOT low-level SPI driver test"); - puts("This application enables you to test a platforms SPI driver implementation."); - puts("Enter 'help' to get started\n"); + puts("Manual SPI peripheral driver test"); + puts("Refer to the README.md file for more information.\n"); + + printf("There are %i SPI devices configured for your platform.\n", + (int)SPI_NUMOF); + + /* reset local SPI configuration */ + spiconf.dev = SPI_UNDEF; /* run the shell */ char line_buf[SHELL_DEFAULT_BUFSIZE];