tests/periph_gpio: add automatic GPIO test cmd

This adds the `auto_test <port> <pin> <port> <pin>` command.

Specify two GPIOs that are electrically connected e.g. via jumper or wire.

The command will automatically conduct several tests to ensure that the
GPIO implemenation behaves according to the specification.
This commit is contained in:
Benjamin Valentin 2020-08-12 23:50:12 +02:00
parent 2509c206ee
commit 53e292569d

View File

@ -27,6 +27,7 @@
#include "periph/gpio.h"
#define BENCH_RUNS_DEFAULT (1000UL * 100)
#define IRQ_TIMEOUT_US (1000UL)
#ifdef MODULE_PERIPH_GPIO_IRQ
static void cb(void *arg)
@ -246,6 +247,135 @@ static int cmd_toggle(int argc, char **argv)
return 0;
}
#ifdef MODULE_PERIPH_GPIO_IRQ
static void _test_cb(void *ctx)
{
mutex_unlock(ctx);
}
#endif
static int cmd_auto_test(int argc, char **argv)
{
if (argc < 5) {
printf("usage: %s <port> <pin> <port> <pin>\n", argv[0]);
return 1;
}
gpio_t pin_in = GPIO_PIN(atoi(argv[1]), atoi(argv[2]));
gpio_t pin_out = GPIO_PIN(atoi(argv[3]), atoi(argv[4]));
puts("[START]");
if (gpio_init(pin_in, GPIO_IN)) {
printf("Error to initialize GPIO_PIN(%s, %s)\n", argv[1], argv[2]);
return -1;
}
if (gpio_init(pin_out, GPIO_OUT)) {
printf("Error to initialize GPIO_PIN(%s, %s)\n", argv[3], argv[4]);
return -1;
}
/* test set HIGH */
gpio_set(pin_out);
if (gpio_read(pin_in) == 0) {
puts("gpio_set() or gpio_read() or failed");
return -1;
}
/* test set LOW */
gpio_clear(pin_out);
if (gpio_read(pin_in) != 0) {
puts("gpio_clear() or gpio_read() failed");
return -1;
}
#ifdef MODULE_PERIPH_GPIO_IRQ
mutex_t lock = MUTEX_INIT_LOCKED;
/* test rising interrupt */
if (gpio_init_int(pin_in, GPIO_IN, GPIO_RISING, _test_cb, &lock)) {
puts("setting rising interrupt failed");
return -1;
}
gpio_set(pin_out);
if (xtimer_mutex_lock_timeout(&lock, IRQ_TIMEOUT_US)) {
puts("rising interrupt timeout");
return -1;
}
gpio_clear(pin_out);
if (xtimer_mutex_lock_timeout(&lock, IRQ_TIMEOUT_US) == 0) {
puts("interrupt falsely generated on falling edge");
return -1;
}
/* test falling interrupt */
if (gpio_init_int(pin_in, GPIO_IN, GPIO_FALLING, _test_cb, &lock)) {
puts("setting falling interrupt failed");
return -1;
}
gpio_set(pin_out);
if (xtimer_mutex_lock_timeout(&lock, IRQ_TIMEOUT_US) == 0) {
puts("interrupt falsely generated on rising edge");
return -1;
}
gpio_clear(pin_out);
if (xtimer_mutex_lock_timeout(&lock, IRQ_TIMEOUT_US)) {
puts("rising interrupt timeout");
return -1;
}
/* test IRQ disable */
gpio_irq_disable(pin_in);
gpio_set(pin_out);
if (xtimer_mutex_lock_timeout(&lock, IRQ_TIMEOUT_US) == 0) {
puts("interrupt falsely generated on rising edge while disabled");
return -1;
}
gpio_clear(pin_out);
if (xtimer_mutex_lock_timeout(&lock, IRQ_TIMEOUT_US) == 0) {
puts("interrupt falsely generated while disabled");
return -1;
}
/* test IRQ enable */
gpio_irq_enable(pin_in);
if (xtimer_mutex_lock_timeout(&lock, IRQ_TIMEOUT_US) == 0) {
puts("interrupt falsely generated after being re-enabled");
return -1;
}
gpio_set(pin_out);
if (xtimer_mutex_lock_timeout(&lock, IRQ_TIMEOUT_US) == 0) {
puts("interrupt falsely generated on rising edge after re-enabled");
return -1;
}
gpio_clear(pin_out);
if (xtimer_mutex_lock_timeout(&lock, IRQ_TIMEOUT_US)) {
puts("interrupt not re-enabled");
return -1;
}
/* disable IRQ to avoid further interrupts */
gpio_irq_disable(pin_in);
#endif
puts("[SUCCESS]");
return 0;
}
static int bench(int argc, char **argv)
{
if (argc < 3) {
@ -285,6 +415,7 @@ static const shell_command_t shell_commands[] = {
{ "set", "set pin to HIGH", cmd_set },
{ "clear", "set pin to LOW", cmd_clear },
{ "toggle", "toggle pin", cmd_toggle },
{ "auto_test", "Run a series of automatic tests on two connected GPIOs", cmd_auto_test },
{ "bench", "run a set of predefined benchmarks", bench },
{ NULL, NULL, NULL }
};