diff --git a/Makefile.dep b/Makefile.dep index d7b84f0c26c1..98bcaf7da81e 100644 --- a/Makefile.dep +++ b/Makefile.dep @@ -981,8 +981,8 @@ ifneq (,$(filter periph_uart_nonblocking,$(USEMODULE))) FEATURES_REQUIRED += periph_uart endif -# Enable periph_gpio when periph_gpio_irq is enabled -ifneq (,$(filter periph_gpio_irq,$(USEMODULE))) +# Add periph_gpio when one or more of its submodules is used +ifneq (,$(filter periph_gpio_%,$(USEMODULE))) FEATURES_REQUIRED += periph_gpio endif diff --git a/drivers/include/periph/gpio_abc.h b/drivers/include/periph/gpio_abc.h new file mode 100644 index 000000000000..85e48dd64a57 --- /dev/null +++ b/drivers/include/periph/gpio_abc.h @@ -0,0 +1,291 @@ +/* + * Copyright 2019 Marian Buschsieweke + * + * 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_periph_gpio_abc GPIO Advanced Bitbanging Capabilities (ABC) + * @ingroup drivers_periph_gpio + * @brief GPIO extension for precisely timed GPIO accesses + * + * Description + * =========== + * + * This submodule extends @ref drivers_periph_gpio to allow precisely timed + * GPIO accesses with a sub-microsecond resolution. The goal of this extension + * is to provide a platform independent API that allows to implement bit banging + * protocols with high data rates and tight timing constraints (timing accuracy + * of 150ns) in a portable manner. + * + * Why is this Extension Needed + * ============================ + * + * The accuracy of 150ns cannot be be implemented with the default GPIO API, as + * the function calls add a significant overhead (in the order of hundred + * nanoseconds on ARM, in the order of microseconds on AVR). Therefore, the user + * would have to compensate this platform specific delay, making portable bit + * banging implementations with a timing accuracy of 150ns and better + * impossible. Each platform specific GPIO ABC implementation will therefore + * transparently compensate for the platform specific delay. + * + * Secondly, no platform independent way of sub microsecond delays is possible, + * so platform specific implementations under the umbrella of a common API are + * required. + * + * And lastly, as the overhead of a function call has the biggest impact on the + * frequency at which GPIO accesses can be performed, this overhead should not + * be paid more than needed. By combining the GPIO access with the high accuracy + * timing, the overhead of a function call needs to be paid only once. + * + * General Design Idea + * =================== + * + * The general idea of this API is to combine the GPIO interface + * (@ref drivers_periph_gpio) with a high resolution delay, which takes the + * overhead of calling the API into account and compensates it. Further, it is + * assumed that the duration of high and low pulses are known ahead of time - + * usually at compile time. Therefore the macro @ref GPIO_ABC_TICKS or the + * function @ref gpio_abc_ticks have to be used to translate the delay in + * nanoseconds into an opaque number of delay ticks, which has to be passed + * to the GPIO ABC API. It is further assumed that the relation between the + * ticks and the actual delay can be described by an linear function, with the + * y-intercept being the duration of a call to @ref gpio_set_for with zero ticks + * delay and the slope being the number of nanoseconds each tick takes. Each + * platform specific implementation will provide the parameters of this linear + * function, so that correct number of delay ticks can be calculated with + * @ref GPIO_ABC_TICKS or @ref gpio_abc_ticks. + * + * Accuracy + * ======== + * + * The accuracy of GPIO ABC depends on two aspects + * + * 1. On the correctness of the parameters for the delay calculation + * 2. On how much CPU time is spend between calls to GPIO ABC functions, as this + * time is not taken into account + * + * It is therefore mandatory that users of GPIO ABC do ***NOT*** perform any + * non-trival (in terms of CPU cycles) calculations *during* bit banging. + * + * Adding Support for GPIO ABC + * =========================== + * + * In order to add support for GPIO ABC to a platform, three things need to + * be provided: + * + * 1. Implementations of @ref gpio_set_for and @ref gpio_clear_for + * 2. The underlying clock frequency of the timing used as @ref GPIO_ABC_CLOCK, + * unless the CPU frequency is used. (If @ref GPIO_ABC_CLOCK is not defined + * in `cpu_gpio_abc.h`, it will be defined to @ref CLOCK_CORECLOCK.) + * 3. The overhead of a function call in cycles of @ref GPIO_ABC_CLOCK as + * `GPIO_ABC_OVERHEAD_CYCLES`, or in nanoseconds as + * @ref GPIO_ABC_OVERHEAD_NS. (If @ref GPIO_ABC_OVERHEAD_NS is not provided + * in `cpu_gpio_abc.h`, it will be calculated in this header based on + * @ref GPIO_ABC_CLOCK.) + * 4. The number of @ref GPIO_ABC_CLOCK cycles the duration of a call to + * @ref gpio_set_for or @ref gpio_clear_for is increased when the + * `ticks` argument is increased by one. (In other words: The length of one + * delay loop iteration in terms of @ref GPIO_ABC_CLOCK.) + * + * Implementation Hints + * -------------------- + * + * The implementations of @ref gpio_set_for and @ref gpio_clear_for have to + * inline the logic to set/clear the GPIO pin and the delay loop instead of + * calling @ref gpio_set and @ref gpio_clear. Otherwise the overhead of the + * function calls would prevent sending short pulses. The logic for + * setting/clearing the pin can (and should) be implemented in C. This logic + * is rather simple and therefore has little potential for compiler + * optimizations and, thus, will take about the same time independent of the + * compiler. If CPU cycles counting is used in the delay loop, inline assembly + * must be used, as even one CPU cycle more or less accumulates over each spin + * of the loop. On more sophisticated platforms with features like dynamic + * branch prediction, special care needs to be taken that each spin of the delay + * loop requires the same number of CPU cycles. + * + * If the delay loop uses a high precision hardware timer, a C implementation + * should be feasible. + * + * Determination of Overhead Cycles and Loop Cycles + * ------------------------------------------------ + * + * The test application in `tests/periph_gpio_abc` can be used to determine + * (or validate) the parameters. You need however to define @ref GPIO_ABC_CLOCK + * in advance, if it differs from the CPU core clock. + * + * You can verify the parameters with an oscilloscope or a logic analyzer (with + * at least 20 MHz sample rate) using that test application. This is highly + * encouraged on sophisticated platforms with features like dynamic branch + * prediction, when CPU cycle counting is used in the delay loop: The dynamic + * branch prediction will add ***significant*** jitter on the length of the + * pulses, if the delay loop is not implemented with care. + * + * @{ + * @file + * @brief GPIO Advanced Bitbanging Capabilities (ABC) + * + * @author Marian Buschsieweke + */ + +#ifndef PERIPH_GPIO_ABC_H +#define PERIPH_GPIO_ABC_H + +#include "board.h" +#include "cpu_gpio_abc.h" +#include "periph/gpio.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief The minimum accuracy a GPIO ABC implementation has to provide in nano seconds + */ +#define GPIO_ABC_MIN_ACCURACY (150U) + +#ifndef GPIO_ABC_CLOCK +/** + * @brief The frequency of the clock used for timing in the GPIO ABC + * implementation + * + * @details If not provided by the implementation, the CPU frequency is used + */ +#define GPIO_ABC_CLOCK CLOCK_CORECLOCK +#endif /* GPIO_ABC_CLOCK */ + +#ifndef GPIO_ABC_OVERHEAD_NS +/** + * @brief Overhead to compensate for in delay loop measured in nano seconds + * + * @details An GPIO ABC implementation has to either provide this macro, or + * provide the overhead in terms of `GPIO_ABC_CLOCK` via + * `GPIO_ABC_OVERHEAD_CYCLES` + */ +#define GPIO_ABC_OVERHEAD_NS ((1000000000ULL * GPIO_ABC_OVERHEAD_CYCLES + GPIO_ABC_CLOCK / 2) / GPIO_ABC_CLOCK) +#endif /* GPIO_ABC_MIN_ACCURACY */ + +/** + * @brief Length of one GPIO ABC "tick" (one delay loop spin) in nanoseconds + */ +#define GPIO_ABC_TICK_NS ((1000000000ULL * GPIO_ABC_TICK_CYCLES + GPIO_ABC_CLOCK / 2) / GPIO_ABC_CLOCK) + +/** + * @brief Minimum pulse length (in nanosecond) supported by the used GPIO ABC + * implementation + */ +#define GPIO_ABC_MIN_PULSE_LEN GPIO_ABC_OVERHEAD_NS + +/** + * @brief Expected accuracy of the timer in nanoseconds + * + * This assumes that the parameter for overhead and cycles per loop are 100% + * correct, but the desired delay would be achieved just in the middle of + * a delay loop iteration (worst case). The value is rounded up. + */ +#define GPIO_ABC_ACCURACY_NS ((GPIO_ABC_TICK_NS + 1) / 2) + +#if GPIO_ABC_ACCURACY_NS > GPIO_ABC_MIN_ACCURACY +#warning "The GPIO ABC implementation has an accuracy of less than 150ns" +#endif + +/** + * @brief Calculates the ticks parameter from a pulse length in nano seconds + * + * This macro can be used instead of @ref gpio_abc_ticks when the pulse length + * is a compile time constant. When doing so, please check against + * @ref GPIO_ABC_MIN_PULSE_LEN if the required pulse length is not too short + * for the GPIO ABC implementation. + */ +#define GPIO_ABC_TICKS(ns) \ + ((int)( \ + ((ns) < GPIO_ABC_OVERHEAD_NS) ? 0 : \ + ((uint64_t)(ns) - GPIO_ABC_OVERHEAD_NS) * GPIO_ABC_CLOCK / (1000000000ULL * GPIO_ABC_TICK_CYCLES)) \ + ) + +#ifndef GPIO_ABC_HAS_BEGIN +/** + * @brief Prepares the board for a bit banging transfer with GPIO ABC + * + * Depending on the GPIO ABC implementation, some preparation - such as setting + * up a high resolution timer - might be required before a bit banging transfer. + * This function does this if required and adds no overhead if not. + * + * @see gpio_abc_end + */ +static inline void gpio_abc_begin(void) +{ + /* Nothing to do */ +} +#endif /* GPIO_ABC_HAS_BEGIN */ + +#ifndef GPIO_ABC_HAS_END +/** + * @brief Function to call at the end of a GPIO ABC bit banging transfer + * + * Depending on the GPIO ABC implementation, some configuration done in + * @ref gpio_abc_begin needs to be undone after the GPIO ABC session, e.g. + * powering of a high resolution timer in order to reduce power consumption. + */ +static inline void gpio_abc_end(void) +{ + /* Nothing to do */ +} +#endif /* GPIO_ABC_HAS_END */ + +/** + * @brief Calculate the delay parameter for precise timing + * + * @param ns Intended pulse length in nano seconds + * + * @return The delay parameter to pass to @ref gpio_set_for and + * @ref gpio_clear_for + * @retval -1 The pulse length is too short to be feasible + * + * @details If the delay is a compile time constant, use @ref GPIO_ABC_DLEAY + * instead + */ +int gpio_abc_ticks(uint16_t ns); + +/** + * @brief Set the given pin to HIGH and wait for the given duration + * + * @param[in] pin The pin to set + * @param[in] delay Number of delay loop iterations calculated with + * @p gpio_abc_ticks + * + * @post The GPIO pin identified by @p pin has been set and afterwards a + * delay loop spun for @p delay iterations + * + * @details If @p delay is zero or negative, this function returns as soon as + * possible + * @warning Wrap calls to @ref gpio_set_for and @ref gpio_clear_for in a pair + * of @ref gpio_abc_begin and @ref gpio_abc_end + */ +void gpio_set_for(gpio_t pin, int delay); + +/** + * @brief Set the given pin to LOW and wait for the given duration + * + * @param[in] pin The pin to clear + * @param[in] delay Number of delay loop iterations calculated with + * @p gpio_abc_ticks + * + * @post The GPIO pin identified by @p pin has been cleared and afterwards a + * delay loop spun for @p delay iterations + * + * @details If @p delay is zero or negative, this function returns as soon as + * possible + * @warning Wrap calls to @ref gpio_set_for and @ref gpio_clear_for in a pair + * of @ref gpio_abc_begin and @ref gpio_abc_end + */ +void gpio_clear_for(gpio_t pin, int delay); + +#ifdef __cplusplus +} +#endif + +#endif /* PERIPH_GPIO_ABC_H */ +/** @} */ diff --git a/drivers/periph_common/gpio_abc.c b/drivers/periph_common/gpio_abc.c new file mode 100644 index 000000000000..a910e3d2212d --- /dev/null +++ b/drivers/periph_common/gpio_abc.c @@ -0,0 +1,27 @@ +/* + * Copyright 2019 Marian Buschsieweke + * + * 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. + */ + +#ifdef MODULE_PERIPH_GPIO_ABC +#include "periph/gpio_abc.h" + +int gpio_abc_ticks(uint16_t ns) +{ + if (ns + GPIO_ABC_MIN_ACCURACY < GPIO_ABC_OVERHEAD_NS) { + return -1; + } + + if (ns < GPIO_ABC_OVERHEAD_NS) { + return 0; + } + + return GPIO_ABC_TICKS(ns); +} + +#else /* MODULE_PERIPH_GPIO_ABC */ +typedef int dont_be_pedantic; +#endif /* MODULE_PERIPH_GPIO_ABC */ diff --git a/tests/periph_gpio_abc/Makefile b/tests/periph_gpio_abc/Makefile new file mode 100644 index 000000000000..c7d5c5b81d3f --- /dev/null +++ b/tests/periph_gpio_abc/Makefile @@ -0,0 +1,11 @@ +include ../Makefile.tests_common + +BOARD_INSUFFICIENT_MEMORY := arduino-duemilanove arduino-leonardo arduino-nano\ + arduino-uno + +FEATURES_REQUIRED = periph_gpio_abc + +USEMODULE += shell +USEMODULE += xtimer + +include $(RIOTBASE)/Makefile.include diff --git a/tests/periph_gpio_abc/main.c b/tests/periph_gpio_abc/main.c new file mode 100644 index 000000000000..0bcc79730a4d --- /dev/null +++ b/tests/periph_gpio_abc/main.c @@ -0,0 +1,346 @@ +/* + * Copyright 2019 Marian Buschsieweke + * + * 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 GPIO Advanced Bitbaning Capabilities (ABC) + * + * @author Marian Buschsieweke + * + * @} + */ + +#include +#include +#include +#include +#include + +#include "irq.h" +#include "periph/gpio_abc.h" +#include "shell.h" +#include "xtimer.h" + +#define N_LOOPS (100U) + +static int gpio_pin(int argc, char **argv); +static int gpio_abc(int argc, char **argv); +static int detect(int argc, char **argv); +static int params(int argc, char **argv); + +static const shell_command_t shell_commands[] = { + { "pin", "Select the pin to test", gpio_pin }, + { "abc", "Toggles the GPIO 100 times with the given nano-second delay", gpio_abc }, + { "detect", "Tries to detect the delay parameters", detect }, + { "params", "Prints/sets the delay parameters", params }, + { NULL, NULL, NULL } +}; + +static _Atomic gpio_t _gpio = GPIO_UNDEF; +static atomic_int _ticks = -1; +static char toggler_stack[THREAD_STACKSIZE_DEFAULT]; +static uint16_t gpio_abc_overhead_ns = GPIO_ABC_OVERHEAD_NS; +static unsigned gpio_abc_tick_cycles = GPIO_ABC_TICK_CYCLES; + +/** + * @brief Same as @ref gpio_abc_ticks, but with custom delay parameters + * + * @param nanosecs Length of the delay loop in nanoseconds + * @param overhead_ns Overhead of a call to @ref gpio_set_for in nanoseconds + * @param tick_cycles @ref GPIO_ABC_CLOCK cycles a delay loop spin takes + * + * This function allows to calculate delays with custom parameters, instead of + * the hard wired parameters provided by `cpu_gpio_abc.h` + */ +static int _gpio_abc_ticks(uint16_t nanosecs, uint16_t overhead_ns, unsigned tick_cycles) +{ + if (nanosecs + GPIO_ABC_MIN_ACCURACY < overhead_ns) { + return -1; + } + + if (nanosecs < overhead_ns) { + return 0; + } + + uint64_t result = nanosecs - overhead_ns; + result *= GPIO_ABC_CLOCK; + result /= 1000000000ULL * (uint64_t)tick_cycles; + + return (int)result; +} + +static void *toggler(void *arg) +{ + (void)arg; + while (1) { + gpio_t gpio = atomic_load(&_gpio); + int ticks = atomic_load(&_ticks); + + if ((gpio != GPIO_UNDEF) && (ticks != -1)) { + gpio_abc_begin(); + for (unsigned i = 0; i < N_LOOPS; i++) { + gpio_set_for(gpio, ticks); + gpio_clear_for(gpio, ticks); + } + gpio_abc_end(); + } + xtimer_usleep(100); + } + + return NULL; +} + +static int gpio_pin(int argc, char **argv) +{ + int port, pin; + gpio_t gpio; + + if (argc != 3) { + printf("Usage: %s \n", argv[0]); + return EXIT_FAILURE; + } + + if ((*argv[1] >= 'A') && (*argv[1] <= 'Z')) { + port = (int)*argv[1] - (int)'A'; + } + else if ((*argv[1] >= 'a') && (*argv[1] <= 'z')) { + port = (int)*argv[1] - (int)'a'; + } + else { + port = atoi(argv[1]); + } + + pin = atoi(argv[2]); + + gpio = GPIO_PIN(port, pin); + + if (gpio_init(gpio, GPIO_OUT)) { + printf("Error to initialize P%c%d / P%d.%d\n", + (char)('A' + port), pin, port, pin); + return EXIT_FAILURE; + } + + gpio_clear(gpio); + atomic_store(&_gpio, gpio); + + return EXIT_SUCCESS; +} + +static int gpio_abc(int argc, char **argv) +{ + if (argc != 2) { + printf("Usage: %s \n", argv[0]); + return EXIT_FAILURE; + } + + gpio_t gpio = atomic_load(&_gpio); + if (gpio == GPIO_UNDEF) { + puts("Run command \"pin\" first to select the pin to toggle"); + return EXIT_FAILURE; + } + + uint16_t duration = (uint16_t)atoi(argv[1]); + int ticks = _gpio_abc_ticks(duration, gpio_abc_overhead_ns, + gpio_abc_tick_cycles); + + if (ticks == -1) { + printf("A pulse length of %" PRIu16 " is too short for your board\n", + duration); + return EXIT_FAILURE; + } + + printf("Toggling now with pulse lengths of %" PRIu16 "ns (ticks = %d)\n", + duration, ticks); + printf("Expected pulse length: %uns\n", + (unsigned)(gpio_abc_overhead_ns + (1000000000ULL * gpio_abc_tick_cycles * (uint64_t)ticks) / GPIO_ABC_CLOCK)); + atomic_store(&_ticks, ticks); + + return EXIT_SUCCESS; +} + +static int detect(int argc, char **argv) +{ + static const uint32_t repetitions1 = 10000000; + static const uint32_t repetitions2 = 10000; + static const int ticks_array[] = { 33, 67, 100 }; + uint64_t tmp; + uint32_t start, stop, overhead_ns, tick_cycles[3]; + if (argc != 1) { + printf("Usage: %s\n", argv[0]); + return EXIT_FAILURE; + } + + gpio_t gpio = _gpio; + if (gpio == GPIO_UNDEF) { + puts("Run command \"pin\" first to select the pin to toggle"); + return EXIT_FAILURE; + } + + gpio_abc_begin(); + start = xtimer_now_usec(); + for (uint32_t i = 0; i < repetitions1; i++) { + gpio_set_for(gpio, 0); + gpio_clear_for(gpio, 0); + } + stop = xtimer_now_usec(); + gpio_abc_end(); + + tmp = (stop - start); + tmp *= NS_PER_US; + tmp = (tmp + repetitions1) / (2 * repetitions1); + overhead_ns = tmp; + + for (unsigned i = 0; i < ARRAY_SIZE(ticks_array); i++) { + gpio_abc_begin(); + start = xtimer_now_usec(); + int ticks = ticks_array[i]; + for (uint32_t j = 0; j < repetitions2; j++) { + gpio_set_for(gpio, ticks); + gpio_clear_for(gpio, ticks); + } + stop = xtimer_now_usec(); + gpio_abc_end(); + tmp = (stop - start); + tmp *= NS_PER_US; + tmp = (tmp + repetitions2) / (2 * repetitions2); + tmp -= overhead_ns; + tmp = (tmp * GPIO_ABC_CLOCK + 500000000ULL) / 1000000000ULL; + tmp = (tmp + ticks / 2) / ticks; + tick_cycles[i] = (int)tmp; + } + + uint32_t overhead_cycles = (overhead_ns * GPIO_ABC_CLOCK + 500000000ULL) + / 1000000000ULL; + puts("Detected values:"); + printf("GPIO_ABC_OVERHEAD_NS = %" PRIu32 "\n" + "GPIO_ABC_OVERHEAD_CYCLES = %" PRIu32 "\n", + overhead_ns, overhead_cycles); + printf("GPIO_ABC_TICK_CYCLES = %" PRIu32 "\n", tick_cycles[0]); + + if (gpio_abc_overhead_ns != overhead_ns) { + int32_t diff = (int32_t)overhead_ns - (int32_t)gpio_abc_overhead_ns; + printf("GPIO_ABC_OVERHEAD_NS to be adjusted by %" PRIi32 "ns\n", diff); + } + else { + puts("GPIO_ABC_OVERHEAD_NS is up to date"); + } + + if (gpio_abc_tick_cycles != tick_cycles[0]) { + int32_t diff = (int32_t)tick_cycles[0] - (int32_t)gpio_abc_tick_cycles; + printf("GPIO_ABC_TICK_CYCLES to be adjusted by %" PRIi32 "\n", diff); + } + else { + puts("GPIO_ABC_TICK_CYCLES is up to date"); + } + + if ((tick_cycles[0] != tick_cycles[1]) || (tick_cycles[1] != tick_cycles[2])) { + puts( + "Error: Delay does not increase exactly linear with the number of\n" + "ticks. Check the implementation of the delay loop in\n" + "gpio_set_for() and gpio_clear_for()\n" + ); + printf("(Results: 1: %" PRIu32 ", 2: %" PRIu32 ", 3: %" PRIu32 ")\n", + tick_cycles[0], tick_cycles[1], tick_cycles[2]); + return EXIT_SUCCESS; + } + + gpio_abc_overhead_ns = overhead_ns; + gpio_abc_tick_cycles = tick_cycles[0]; + + return EXIT_SUCCESS; +} + +static int params(int argc, char **argv) +{ + if ((argc > 3) || ((argc > 1) && !strcmp("-h", argv[1]))) { + printf("Usage: %s [GPIO_ABC_TICK_CYCLES [GPIO_ABC_OVERHEAD_NS]]\n", + argv[0]); + puts( + "When called without parameters, the current values for\n" + "GPIO_ABC_TICK_CYCLES and GPIO_ABC_OVERHEAD_NS are displayed.\n" + "When called with one parameter, GPIO_ABC_TICK_CYCLES is updated\n" + "to the given value.\n" + "When called with two parameters, GPIO_ABC_TICK_CYCLES is updated\n" + "to the value given in the first parameter and\n" + "GPIO_ABC_OVERHEAD_NS to the value given in the second\n" + ); + return EXIT_FAILURE; + } + + if (argc == 3) { + gpio_abc_overhead_ns = atoi(argv[2]); + } + + if (argc >= 2) { + gpio_abc_tick_cycles = atoi(argv[1]); + } + + printf("GPIO_ABC_OVERHEAD_NS = %" PRIu16 "\n", + gpio_abc_overhead_ns); + printf("GPIO_ABC_TICK_CYCLES = %u\n", gpio_abc_tick_cycles); + return EXIT_SUCCESS; +} + +int main(void) +{ + puts( + "GPIO Advanced Bitbanging Capabilities (ABC) Test\n" + "================================================\n" + "\n" + "Prerequisites\n" + "-------------\n" + "\n" + "- A digital oscilloscope or a logic analyzer with a sample rate of\n" + " at least 20 MHz (50ns resolution)\n" + "- A board with GPIO ABC support\n" + "\n" + "Detecting GPIO ABC Parameters\n" + "-----------------------------\n" + "\n" + "1. Select a GPIO pin to test using the \"pin\" command\n" + "2. Run the \"detect\" command and wait patiently (about ten seconds\n" + " on an ARM board)\n" + " ==> The automatically detected parameters are printed\n" + "\n" + "Testing\n" + "-------\n" + "\n" + "1. Connect the scope or the logic analyzer to an GPIO pin\n" + "2. Setup that pin using the \"pin\" command\n" + "3. Run \"abc\" with durations of your choosing and verify that the\n" + " durations match the one you specified\n" + ); + + printf("Shortest pulse: %uns\n", (unsigned)GPIO_ABC_MIN_PULSE_LEN); + printf("Accuracy(*): %uns\n", (unsigned)GPIO_ABC_ACCURACY_NS); + printf("CPU Clock: %u %03u %03uHz\n", + (unsigned)(CLOCK_CORECLOCK / 1000000U), + (unsigned)((CLOCK_CORECLOCK / 1000U) % 1000U), + (unsigned)(CLOCK_CORECLOCK % 1000000U)); + printf("GPIO ABC Clock: %u %03u %03uHz\n", + (unsigned)(GPIO_ABC_CLOCK / 1000000U), + (unsigned)((GPIO_ABC_CLOCK / 1000U) % 1000U), + (unsigned)(GPIO_ABC_CLOCK % 1000000U)); + printf("Tick Duration: %uns\n", + (unsigned)GPIO_ABC_TICK_NS); + puts("\n(*) Worst case accuracy if GPIO ABC parameters are perfect"); + + /* start toggling thread */ + thread_create(toggler_stack, sizeof(toggler_stack), + THREAD_PRIORITY_MAIN + 1, THREAD_CREATE_STACKTEST, + toggler, NULL, "toggler"); + + /* start the shell */ + char line_buf[SHELL_DEFAULT_BUFSIZE]; + shell_run(shell_commands, line_buf, SHELL_DEFAULT_BUFSIZE); + + return 0; +}