mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
ff727f8f90
Use a custom expect() that just spins in an endless loop instead of panicking. The test isn't run automatically anyway, as it requires connecting two GPIOs with jumper wires; but when run manually it is helpful to not kill RIOT to also get the stdio output of the exact point where the test fails (e.g. USB CDC ACM doesn't like panic()).
825 lines
31 KiB
C
825 lines
31 KiB
C
/*
|
|
* Copyright (C) 2021 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 Peripheral GPIO Low-Level API
|
|
*
|
|
* @author Marian Buschsieweke <marian.buschsieweke@ovgu.de>
|
|
*
|
|
* @}
|
|
*/
|
|
|
|
#include <errno.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
|
|
#include "irq.h"
|
|
#include "mutex.h"
|
|
#include "periph/gpio_ll.h"
|
|
#include "periph/gpio_ll_irq.h"
|
|
#include "timex.h"
|
|
#include "ztimer.h"
|
|
|
|
#ifndef LOW_ROM
|
|
#define LOW_ROM 0
|
|
#endif
|
|
|
|
static const char *noyes[] = { "no", "yes" };
|
|
static gpio_port_t port_out = GPIO_PORT(PORT_OUT);
|
|
static gpio_port_t port_in = GPIO_PORT(PORT_IN);
|
|
|
|
static const uint64_t mutex_timeout = US_PER_MS;
|
|
|
|
static void puts_optional(const char *str)
|
|
{
|
|
if (!LOW_ROM) {
|
|
puts(str);
|
|
}
|
|
}
|
|
|
|
#if LOW_ROM
|
|
#define printf_optional(...) (void)0
|
|
#else
|
|
#define printf_optional(...) printf(__VA_ARGS__)
|
|
#endif
|
|
|
|
/* a custom expect that keeps the CPU alive makes debugging easier with
|
|
* stdio that requires RIOT to remain alive, e.g. USB CDC ACM */
|
|
static void expect_impl(int val, unsigned line)
|
|
{
|
|
if (!val) {
|
|
printf("expect failed at line %u\n", line);
|
|
fflush(stdout);
|
|
while(1) {}
|
|
}
|
|
}
|
|
|
|
#define expect(x) expect_impl((int)(x), __LINE__)
|
|
|
|
static void print_cabling(unsigned port1, unsigned pin1,
|
|
unsigned port2, unsigned pin2)
|
|
{
|
|
printf(" P%u.%u (P%c%u) -- P%u.%u (P%c%u)\n",
|
|
port1, pin1, 'A' + (char)port1, pin1,
|
|
port2, pin2, 'A' + (char)port2, pin2);
|
|
}
|
|
|
|
static void print_details(void)
|
|
{
|
|
puts_optional("Test / Hardware Details:\n"
|
|
"========================\n"
|
|
"Cabling:\n"
|
|
"(INPUT -- OUTPUT)");
|
|
print_cabling(PORT_IN, PIN_IN_0, PORT_OUT, PIN_OUT_0);
|
|
print_cabling(PORT_IN, PIN_IN_1, PORT_OUT, PIN_OUT_1);
|
|
printf("Number of pull resistor values supported: %u\n", GPIO_PULL_NUMOF);
|
|
printf("Number of drive strengths supported: %u\n", GPIO_DRIVE_NUMOF);
|
|
printf("Number of slew rates supported: %u\n", GPIO_SLEW_NUMOF);
|
|
puts("Valid GPIO ports:");
|
|
|
|
/* 64 GPIO ports ought to be enough for anybody */
|
|
for (uint_fast8_t i = 0; i < 64; i++) {
|
|
if (is_gpio_port_num_valid(i)) {
|
|
printf("- PORT %" PRIuFAST8 " (PORT %c)\n", i, 'A' + i);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void test_gpio_port_pack(void)
|
|
{
|
|
char stacked;
|
|
|
|
puts_optional("\n"
|
|
"Testing gpio_port_pack_addr()\n"
|
|
"=============================\n");
|
|
|
|
expect(gpio_port_unpack_addr(gpio_port_pack_addr(&stacked)) == &stacked);
|
|
expect(gpio_port_unpack_addr(gpio_port_pack_addr(noyes)) == noyes);
|
|
void *tmp = malloc(1);
|
|
expect(tmp);
|
|
expect(gpio_port_unpack_addr(gpio_port_pack_addr(tmp)) == tmp);
|
|
free(tmp);
|
|
expect(gpio_port_unpack_addr(port_in) == NULL);
|
|
expect(gpio_port_unpack_addr(port_out) == NULL);
|
|
puts_optional("All OK");
|
|
}
|
|
|
|
static void test_gpio_ll_init(void)
|
|
{
|
|
bool is_supported;
|
|
puts_optional("\n"
|
|
"Testing gpip_ng_init()\n"
|
|
"======================\n"
|
|
"\n"
|
|
"Testing is_gpio_port_num_valid() is true for PORT_OUT and "
|
|
"PORT_IN:");
|
|
expect(is_gpio_port_num_valid(PORT_IN));
|
|
expect(is_gpio_port_num_valid(PORT_OUT));
|
|
|
|
puts_optional("\nTesting input configurations for PIN_IN_0:");
|
|
is_supported = (0 == gpio_ll_init(port_in, PIN_IN_0, &gpio_ll_in_pu));
|
|
printf_optional("Support for input with pull up: %s\n",
|
|
noyes[is_supported]);
|
|
if (is_supported) {
|
|
gpio_conf_t conf;
|
|
gpio_ll_query_conf(&conf, port_in, PIN_IN_0);
|
|
gpio_ll_print_conf(&conf);
|
|
puts_optional("");
|
|
expect((conf.state == GPIO_INPUT) && (conf.pull == GPIO_PULL_UP));
|
|
}
|
|
|
|
is_supported = (0 == gpio_ll_init(port_in, PIN_IN_0, &gpio_ll_in_pd));
|
|
printf_optional("Support for input with pull down: %s\n",
|
|
noyes[is_supported]);
|
|
if (is_supported) {
|
|
gpio_conf_t conf;
|
|
gpio_ll_query_conf(&conf, port_in, PIN_IN_0);
|
|
gpio_ll_print_conf(&conf);
|
|
puts_optional("");
|
|
expect((conf.state == GPIO_INPUT) && (conf.pull == GPIO_PULL_DOWN));
|
|
}
|
|
|
|
is_supported = (0 == gpio_ll_init(port_in, PIN_IN_0, &gpio_ll_in_pk));
|
|
printf_optional("Support for input with pull to bus level: %s\n",
|
|
noyes[is_supported]);
|
|
if (is_supported) {
|
|
gpio_conf_t conf;
|
|
gpio_ll_query_conf(&conf, port_in, PIN_IN_0);
|
|
gpio_ll_print_conf(&conf);
|
|
puts_optional("");
|
|
expect((conf.state == GPIO_INPUT) && (conf.pull == GPIO_PULL_KEEP));
|
|
}
|
|
|
|
is_supported = (0 == gpio_ll_init(port_in, PIN_IN_0, &gpio_ll_in));
|
|
printf_optional("Support for floating input (no pull resistors): %s\n",
|
|
noyes[is_supported]);
|
|
{
|
|
gpio_conf_t conf;
|
|
gpio_ll_query_conf(&conf, port_in, PIN_IN_0);
|
|
gpio_ll_print_conf(&conf);
|
|
puts_optional("");
|
|
expect((conf.state == GPIO_INPUT) && (conf.pull == GPIO_FLOATING));
|
|
}
|
|
/* Support for floating inputs is mandatory */
|
|
expect(is_supported);
|
|
|
|
puts_optional("\nTesting output configurations for PIN_OUT_0:");
|
|
{
|
|
gpio_conf_t conf = {
|
|
.state = GPIO_OUTPUT_PUSH_PULL,
|
|
.initial_value = false,
|
|
};
|
|
is_supported = (0 == gpio_ll_init(port_out, PIN_OUT_0, &conf));
|
|
}
|
|
printf_optional("Support for output (push-pull) with initial value of "
|
|
"LOW: %s\n",
|
|
noyes[is_supported]);
|
|
|
|
if (is_supported) {
|
|
gpio_conf_t conf;
|
|
gpio_ll_query_conf(&conf, port_out, PIN_OUT_0);
|
|
gpio_ll_print_conf(&conf);
|
|
puts_optional("");
|
|
expect((conf.state == GPIO_OUTPUT_PUSH_PULL) && !conf.initial_value);
|
|
ztimer_sleep(ZTIMER_USEC, US_PER_MS);
|
|
is_supported = !(gpio_ll_read(port_in) & (1ULL << PIN_IN_0));
|
|
printf_optional("Output is indeed LOW: %s\n", noyes[is_supported]);
|
|
expect(is_supported);
|
|
gpio_ll_set(port_out, (1ULL<< PIN_OUT_0));
|
|
gpio_ll_query_conf(&conf, port_out, PIN_OUT_0);
|
|
gpio_ll_print_conf(&conf);
|
|
puts_optional("");
|
|
ztimer_sleep(ZTIMER_USEC, US_PER_MS);
|
|
is_supported = (gpio_ll_read(port_in) & (1ULL << PIN_IN_0));
|
|
printf_optional("Output can be pushed HIGH: %s\n", noyes[is_supported]);
|
|
expect(is_supported);
|
|
gpio_ll_query_conf(&conf, port_out, PIN_OUT_0);
|
|
expect((conf.state == GPIO_OUTPUT_PUSH_PULL) && conf.initial_value);
|
|
}
|
|
|
|
{
|
|
gpio_conf_t conf = {
|
|
.state = GPIO_OUTPUT_PUSH_PULL,
|
|
.initial_value = true,
|
|
};
|
|
is_supported = (0 == gpio_ll_init(port_out, PIN_OUT_0, &conf));
|
|
}
|
|
printf_optional("Support for output (push-pull) with initial value of "
|
|
"HIGH: %s\n",
|
|
noyes[is_supported]);
|
|
|
|
if (is_supported) {
|
|
gpio_conf_t conf;
|
|
gpio_ll_query_conf(&conf, port_out, PIN_OUT_0);
|
|
gpio_ll_print_conf(&conf);
|
|
puts_optional("");
|
|
expect((conf.state == GPIO_OUTPUT_PUSH_PULL) && conf.initial_value);
|
|
ztimer_sleep(ZTIMER_USEC, US_PER_MS);
|
|
is_supported = (gpio_ll_read(port_in) & (1ULL << PIN_IN_0));
|
|
printf_optional("Output is indeed HIGH: %s\n", noyes[is_supported]);
|
|
expect(is_supported);
|
|
}
|
|
|
|
{
|
|
gpio_conf_t conf = {
|
|
.state = GPIO_OUTPUT_OPEN_DRAIN,
|
|
.initial_value = false,
|
|
.pull = GPIO_PULL_UP
|
|
};
|
|
is_supported = (0 == gpio_ll_init(port_out, PIN_OUT_0, &conf));
|
|
}
|
|
printf_optional("Support for output (open drain with pull up) with initial "
|
|
"value of LOW: %s\n",
|
|
noyes[is_supported]);
|
|
|
|
if (is_supported) {
|
|
gpio_conf_t conf;
|
|
gpio_ll_query_conf(&conf, port_out, PIN_OUT_0);
|
|
gpio_ll_print_conf(&conf);
|
|
puts_optional("");
|
|
expect((conf.state == GPIO_OUTPUT_OPEN_DRAIN) && !conf.initial_value
|
|
&& (conf.pull == GPIO_PULL_UP));
|
|
ztimer_sleep(ZTIMER_USEC, US_PER_MS);
|
|
is_supported = !(gpio_ll_read(port_in) & (1ULL << PIN_IN_0));
|
|
printf_optional("Output is indeed LOW: %s\n", noyes[is_supported]);
|
|
expect(is_supported);
|
|
}
|
|
|
|
{
|
|
gpio_conf_t conf = {
|
|
.state = GPIO_OUTPUT_OPEN_DRAIN,
|
|
.initial_value = true,
|
|
.pull = GPIO_PULL_UP
|
|
};
|
|
is_supported = (0 == gpio_ll_init(port_out, PIN_OUT_0, &conf));
|
|
}
|
|
printf_optional("Support for output (open drain with pull up) with initial "
|
|
"value of HIGH: %s\n",
|
|
noyes[is_supported]);
|
|
|
|
if (is_supported) {
|
|
gpio_conf_t conf;
|
|
gpio_ll_query_conf(&conf, port_out, PIN_OUT_0);
|
|
gpio_ll_print_conf(&conf);
|
|
puts_optional("");
|
|
expect((conf.state == GPIO_OUTPUT_OPEN_DRAIN) && conf.initial_value
|
|
&& (conf.pull == GPIO_PULL_UP));
|
|
ztimer_sleep(ZTIMER_USEC, US_PER_MS);
|
|
is_supported = !!(gpio_ll_read(port_in) & (1ULL << PIN_IN_0));
|
|
printf_optional("Output is indeed HIGH: %s\n", noyes[is_supported]);
|
|
expect(is_supported);
|
|
}
|
|
|
|
{
|
|
gpio_conf_t conf = {
|
|
.state = GPIO_OUTPUT_OPEN_DRAIN,
|
|
.initial_value = false,
|
|
.pull = GPIO_FLOATING,
|
|
};
|
|
is_supported = (0 == gpio_ll_init(port_out, PIN_OUT_0, &conf));
|
|
}
|
|
printf_optional("Support for output (open drain) with initial value of "
|
|
"LOW: %s\n",
|
|
noyes[is_supported]);
|
|
|
|
if (is_supported) {
|
|
gpio_conf_t conf;
|
|
gpio_ll_query_conf(&conf, port_out, PIN_OUT_0);
|
|
gpio_ll_print_conf(&conf);
|
|
puts_optional("");
|
|
expect((conf.state == GPIO_OUTPUT_OPEN_DRAIN) && !conf.initial_value
|
|
&& (conf.pull == GPIO_FLOATING));
|
|
ztimer_sleep(ZTIMER_USEC, US_PER_MS);
|
|
is_supported = !(gpio_ll_read(port_in) & (1ULL << PIN_IN_0));
|
|
printf_optional("Output is indeed LOW: %s\n", noyes[is_supported]);
|
|
expect(is_supported);
|
|
}
|
|
|
|
{
|
|
gpio_conf_t conf = {
|
|
.state = GPIO_OUTPUT_OPEN_DRAIN,
|
|
.initial_value = true,
|
|
.pull = GPIO_FLOATING,
|
|
};
|
|
is_supported = (0 == gpio_ll_init(port_out, PIN_OUT_0, &conf));
|
|
}
|
|
printf_optional("Support for output (open drain) with initial value of "
|
|
"HIGH: %s\n",
|
|
noyes[is_supported]);
|
|
|
|
if (is_supported) {
|
|
gpio_conf_t conf;
|
|
gpio_ll_query_conf(&conf, port_out, PIN_OUT_0);
|
|
gpio_ll_print_conf(&conf);
|
|
puts_optional("");
|
|
expect((conf.state == GPIO_OUTPUT_OPEN_DRAIN) && conf.initial_value
|
|
&& (conf.pull == GPIO_FLOATING));
|
|
is_supported = (0 == gpio_ll_init(port_in, PIN_IN_0, &gpio_ll_in_pd));
|
|
if (is_supported) {
|
|
gpio_conf_t conf;
|
|
gpio_ll_query_conf(&conf, port_in, PIN_IN_0);
|
|
gpio_ll_print_conf(&conf);
|
|
puts_optional("");
|
|
ztimer_sleep(ZTIMER_USEC, US_PER_MS);
|
|
is_supported = !(gpio_ll_read(port_in) & (1ULL << PIN_IN_0));
|
|
printf_optional("Output can indeed be pulled LOW: %s\n",
|
|
noyes[is_supported]);
|
|
expect(is_supported);
|
|
}
|
|
else {
|
|
puts_optional("WARN: Cannot enable pull down of PIN_IN_0 to verify "
|
|
"correct Open Drain behavior");
|
|
}
|
|
is_supported = (0 == gpio_ll_init(port_in, PIN_IN_0, &gpio_ll_in_pu));
|
|
if (is_supported) {
|
|
gpio_conf_t conf;
|
|
gpio_ll_query_conf(&conf, port_in, PIN_IN_0);
|
|
gpio_ll_print_conf(&conf);
|
|
puts_optional("");
|
|
ztimer_sleep(ZTIMER_USEC, US_PER_MS);
|
|
is_supported = (gpio_ll_read(port_in) & (1ULL << PIN_IN_0));
|
|
printf_optional("Output can indeed be pulled HIGH: %s\n",
|
|
noyes[is_supported]);
|
|
expect(is_supported);
|
|
}
|
|
else {
|
|
puts_optional("WARN: Cannot enable pull up of PIN_IN_0 to verify "
|
|
"correct Open Drain behavior");
|
|
}
|
|
}
|
|
|
|
{
|
|
gpio_conf_t conf = {
|
|
.state = GPIO_OUTPUT_OPEN_SOURCE,
|
|
.initial_value = false,
|
|
.pull = GPIO_FLOATING,
|
|
};
|
|
is_supported = (0 == gpio_ll_init(port_out, PIN_OUT_0, &conf));
|
|
}
|
|
printf_optional("Support for output (open source) with initial value of "
|
|
"LOW: %s\n",
|
|
noyes[is_supported]);
|
|
|
|
if (is_supported) {
|
|
gpio_conf_t conf;
|
|
gpio_ll_query_conf(&conf, port_out, PIN_OUT_0);
|
|
gpio_ll_print_conf(&conf);
|
|
puts_optional("");
|
|
expect((conf.state == GPIO_OUTPUT_OPEN_SOURCE) && !conf.initial_value
|
|
&& (conf.pull == GPIO_FLOATING));
|
|
is_supported = (0 == gpio_ll_init(port_in, PIN_IN_0, &gpio_ll_in_pd));
|
|
if (is_supported) {
|
|
gpio_conf_t conf;
|
|
gpio_ll_query_conf(&conf, port_in, PIN_IN_0);
|
|
gpio_ll_print_conf(&conf);
|
|
puts_optional("");
|
|
ztimer_sleep(ZTIMER_USEC, US_PER_MS);
|
|
is_supported = !(gpio_ll_read(port_in) & (1ULL << PIN_IN_0));
|
|
printf_optional("Output can indeed be pulled LOW: %s\n",
|
|
noyes[is_supported]);
|
|
expect(is_supported);
|
|
}
|
|
else {
|
|
puts_optional("WARN: Cannot enable pull down of PIN_IN_0 to verify "
|
|
"correct Open Source behavior");
|
|
}
|
|
is_supported = (0 == gpio_ll_init(port_in, PIN_IN_0, &gpio_ll_in_pu));
|
|
if (is_supported) {
|
|
gpio_conf_t conf;
|
|
gpio_ll_query_conf(&conf, port_in, PIN_IN_0);
|
|
gpio_ll_print_conf(&conf);
|
|
puts_optional("");
|
|
ztimer_sleep(ZTIMER_USEC, US_PER_MS);
|
|
is_supported = (gpio_ll_read(port_in) & (1ULL << PIN_IN_0));
|
|
printf_optional("Output can indeed be pulled HIGH: %s\n",
|
|
noyes[is_supported]);
|
|
expect(is_supported);
|
|
}
|
|
else {
|
|
puts_optional("WARN: Cannot enable pull up of PIN_IN_0 to verify "
|
|
"correct Open Source behavior");
|
|
}
|
|
}
|
|
|
|
expect(0 == gpio_ll_init(port_in, PIN_IN_0, &gpio_ll_in));
|
|
|
|
{
|
|
gpio_conf_t conf = {
|
|
.state = GPIO_OUTPUT_OPEN_SOURCE,
|
|
.initial_value = true,
|
|
.pull = GPIO_FLOATING,
|
|
};
|
|
is_supported = (0 == gpio_ll_init(port_out, PIN_OUT_0, &conf));
|
|
}
|
|
printf_optional("Support for output (open source) with initial value of "
|
|
"HIGH: %s\n",
|
|
noyes[is_supported]);
|
|
|
|
if (is_supported) {
|
|
gpio_conf_t conf;
|
|
gpio_ll_query_conf(&conf, port_out, PIN_OUT_0);
|
|
gpio_ll_print_conf(&conf);
|
|
puts_optional("");
|
|
expect((conf.state == GPIO_OUTPUT_OPEN_SOURCE) && conf.initial_value
|
|
&& (conf.pull == GPIO_FLOATING));
|
|
ztimer_sleep(ZTIMER_USEC, US_PER_MS);
|
|
is_supported = (gpio_ll_read(port_in) & (1ULL << PIN_IN_0));
|
|
printf_optional("Output is indeed HIGH: %s\n", noyes[is_supported]);
|
|
expect(is_supported);
|
|
}
|
|
|
|
{
|
|
gpio_conf_t conf = {
|
|
.state = GPIO_OUTPUT_OPEN_SOURCE,
|
|
.initial_value = true,
|
|
.pull = GPIO_PULL_DOWN,
|
|
};
|
|
is_supported = (0 == gpio_ll_init(port_out, PIN_OUT_0, &conf));
|
|
}
|
|
printf_optional("Support for output (open source with pull up) with initial "
|
|
"value of HIGH: %s\n",
|
|
noyes[is_supported]);
|
|
|
|
if (is_supported) {
|
|
ztimer_sleep(ZTIMER_USEC, US_PER_MS);
|
|
is_supported = (gpio_ll_read(port_in) & (1ULL << PIN_IN_0));
|
|
printf_optional("Output is indeed HIGH: %s\n", noyes[is_supported]);
|
|
expect(is_supported);
|
|
}
|
|
|
|
{
|
|
gpio_conf_t conf = {
|
|
.state = GPIO_OUTPUT_OPEN_SOURCE,
|
|
.initial_value = false,
|
|
.pull = GPIO_PULL_DOWN,
|
|
};
|
|
is_supported = (0 == gpio_ll_init(port_out, PIN_OUT_0, &conf));
|
|
}
|
|
printf_optional("Support for output (open source with pull up) with initial "
|
|
"value of LOW: %s\n",
|
|
noyes[is_supported]);
|
|
|
|
if (is_supported) {
|
|
ztimer_sleep(ZTIMER_USEC, US_PER_MS);
|
|
is_supported = !(gpio_ll_read(port_in) & (1ULL << PIN_IN_0));
|
|
printf_optional("Output is indeed LOW: %s\n", noyes[is_supported]);
|
|
expect(is_supported);
|
|
}
|
|
|
|
{
|
|
gpio_conf_t conf = {
|
|
.state = GPIO_DISCONNECT,
|
|
};
|
|
is_supported = (0 == gpio_ll_init(port_out, PIN_OUT_0, &conf));
|
|
}
|
|
printf_optional("Support for disconnecting GPIO: %s\n", noyes[is_supported]);
|
|
/* This is mandatory */
|
|
expect(is_supported);
|
|
|
|
is_supported = (0 == gpio_ll_init(port_in, PIN_IN_0, &gpio_ll_in_pd));
|
|
if (is_supported) {
|
|
ztimer_sleep(ZTIMER_USEC, US_PER_MS);
|
|
is_supported = !(gpio_ll_read(port_in) & (1ULL << PIN_IN_0));
|
|
printf_optional("Output can indeed be pulled LOW: %s\n",
|
|
noyes[is_supported]);
|
|
expect(is_supported);
|
|
}
|
|
else {
|
|
puts_optional("WARN: Cannot enable pull down of PIN_IN_0 to verify "
|
|
"correct disabled behavior");
|
|
}
|
|
is_supported = (0 == gpio_ll_init(port_in, PIN_IN_0, &gpio_ll_in_pu));
|
|
if (is_supported) {
|
|
ztimer_sleep(ZTIMER_USEC, US_PER_MS);
|
|
is_supported = (gpio_ll_read(port_in) & (1ULL << PIN_IN_0));
|
|
printf_optional("Output can indeed be pulled HIGH: %s\n",
|
|
noyes[is_supported]);
|
|
expect(is_supported);
|
|
}
|
|
else {
|
|
puts_optional("WARN: Cannot enable pull up of PIN_IN_0 to verify "
|
|
"correct disabled behavior");
|
|
}
|
|
}
|
|
|
|
static void test_input_output(void)
|
|
{
|
|
puts_optional("\n"
|
|
"Testing Reading/Writing GPIO Ports\n"
|
|
"==================================\n");
|
|
|
|
expect(0 == gpio_ll_init(port_in, PIN_IN_0, &gpio_ll_in));
|
|
expect(0 == gpio_ll_init(port_in, PIN_IN_1, &gpio_ll_in));
|
|
expect(0 == gpio_ll_init(port_out, PIN_OUT_0, &gpio_ll_out));
|
|
expect(0 == gpio_ll_init(port_out, PIN_OUT_1, &gpio_ll_out));
|
|
|
|
uword_t mask_in_0 = (1UL << PIN_IN_0);
|
|
uword_t mask_in_1 = (1UL << PIN_IN_1);
|
|
uword_t mask_in_both = mask_in_0 | mask_in_1;
|
|
uword_t mask_out_both = (1UL << PIN_OUT_0) | (1UL << PIN_OUT_1);
|
|
|
|
puts_optional("testing initial value of 0 after init");
|
|
ztimer_sleep(ZTIMER_USEC, US_PER_MS);
|
|
expect(0x00 == (gpio_ll_read(port_in) & mask_in_both));
|
|
puts_optional("...OK");
|
|
|
|
puts_optional("testing setting both outputs_optional simultaneously");
|
|
gpio_ll_set(port_out, (1UL << PIN_OUT_0) | (1UL << PIN_OUT_1));
|
|
ztimer_sleep(ZTIMER_USEC, US_PER_MS);
|
|
expect(mask_in_both == (gpio_ll_read(port_in) & mask_in_both));
|
|
puts_optional("...OK");
|
|
|
|
puts_optional("testing clearing both outputs_optional simultaneously");
|
|
gpio_ll_clear(port_out, (1UL << PIN_OUT_0) | (1UL << PIN_OUT_1));
|
|
ztimer_sleep(ZTIMER_USEC, US_PER_MS);
|
|
expect(0x00 == (gpio_ll_read(port_in) & mask_in_both));
|
|
puts_optional("...OK");
|
|
|
|
puts_optional("testing toggling first output (0 --> 1)");
|
|
gpio_ll_toggle(port_out, 1UL << PIN_OUT_0);
|
|
ztimer_sleep(ZTIMER_USEC, US_PER_MS);
|
|
expect(mask_in_0 == (gpio_ll_read(port_in) & mask_in_both));
|
|
puts_optional("...OK");
|
|
|
|
puts_optional("testing toggling first output (1 --> 0)");
|
|
gpio_ll_toggle(port_out, 1UL << PIN_OUT_0);
|
|
ztimer_sleep(ZTIMER_USEC, US_PER_MS);
|
|
expect(0x00 == (gpio_ll_read(port_in) & mask_in_both));
|
|
puts_optional("...OK");
|
|
|
|
puts_optional("testing toggling second output (0 --> 1)");
|
|
gpio_ll_toggle(port_out, 1UL << PIN_OUT_1);
|
|
ztimer_sleep(ZTIMER_USEC, US_PER_MS);
|
|
expect(mask_in_1 == (gpio_ll_read(port_in) & mask_in_both));
|
|
puts_optional("...OK");
|
|
|
|
puts_optional("testing toggling second output (1 --> 0)");
|
|
gpio_ll_toggle(port_out, 1UL << PIN_OUT_1);
|
|
ztimer_sleep(ZTIMER_USEC, US_PER_MS);
|
|
expect(0x00 == (gpio_ll_read(port_in) & mask_in_both));
|
|
puts_optional("...OK");
|
|
|
|
puts_optional("testing setting first output and clearing second with "
|
|
"write");
|
|
/* Preventing side-effects on output GPIO port due to changes on unrelated
|
|
* pins between the calls to gpio_ll_prepare_write() and gpio_ll_write()
|
|
* by disabling IRQs. Better safe than sorry. */
|
|
unsigned irq_state = irq_disable();
|
|
gpio_ll_write(port_out, gpio_ll_prepare_write(port_out, mask_out_both,
|
|
1UL << PIN_OUT_0));
|
|
irq_restore(irq_state);
|
|
ztimer_sleep(ZTIMER_USEC, US_PER_MS);
|
|
expect(mask_in_0 == (gpio_ll_read(port_in) & mask_in_both));
|
|
puts_optional("...OK");
|
|
|
|
irq_state = irq_disable();
|
|
gpio_ll_write(port_out, gpio_ll_prepare_write(port_out, mask_out_both,
|
|
1UL << PIN_OUT_1));
|
|
irq_restore(irq_state);
|
|
puts_optional("testing setting second output and clearing first with "
|
|
"write");
|
|
expect(mask_in_1 == (gpio_ll_read(port_in) & mask_in_both));
|
|
puts_optional("...OK");
|
|
|
|
puts_optional("All input/output operations worked as expected");
|
|
}
|
|
|
|
static void irq_edge_cb(void *mut)
|
|
{
|
|
mutex_unlock(mut);
|
|
}
|
|
|
|
static void test_irq_edge(void)
|
|
{
|
|
mutex_t irq_mut = MUTEX_INIT_LOCKED;
|
|
|
|
puts_optional("Testing rising edge on PIN_IN_0");
|
|
expect(0 == gpio_ll_irq(port_in, PIN_IN_0, GPIO_TRIGGER_EDGE_RISING,
|
|
irq_edge_cb, &irq_mut));
|
|
/* test for spurious IRQ */
|
|
expect(-ECANCELED == ztimer_mutex_lock_timeout(ZTIMER_USEC, &irq_mut,
|
|
mutex_timeout));
|
|
gpio_ll_set(port_out, 1UL << PIN_OUT_0);
|
|
/* test for IRQ on rising edge */
|
|
expect(0 == ztimer_mutex_lock_timeout(ZTIMER_USEC, &irq_mut,
|
|
mutex_timeout));
|
|
/* test for spurious IRQ */
|
|
expect(-ECANCELED == ztimer_mutex_lock_timeout(ZTIMER_USEC, &irq_mut,
|
|
mutex_timeout));
|
|
gpio_ll_clear(port_out, 1UL << PIN_OUT_0);
|
|
/* test for no IRQ on falling edge */
|
|
expect(-ECANCELED == ztimer_mutex_lock_timeout(ZTIMER_USEC, &irq_mut,
|
|
mutex_timeout));
|
|
puts_optional("... OK");
|
|
|
|
puts_optional("Testing falling edge on PIN_IN_0");
|
|
expect(0 == gpio_ll_irq(port_in, PIN_IN_0, GPIO_TRIGGER_EDGE_FALLING,
|
|
irq_edge_cb, &irq_mut));
|
|
/* test for spurious IRQ */
|
|
expect(-ECANCELED == ztimer_mutex_lock_timeout(ZTIMER_USEC, &irq_mut,
|
|
mutex_timeout));
|
|
gpio_ll_set(port_out, 1UL << PIN_OUT_0);
|
|
/* test for no IRQ on rising edge */
|
|
expect(-ECANCELED == ztimer_mutex_lock_timeout(ZTIMER_USEC, &irq_mut,
|
|
mutex_timeout));
|
|
gpio_ll_clear(port_out, 1UL << PIN_OUT_0);
|
|
/* test for IRQ on falling edge */
|
|
expect(0 == ztimer_mutex_lock_timeout(ZTIMER_USEC, &irq_mut,
|
|
mutex_timeout));
|
|
/* test for spurious IRQ */
|
|
expect(-ECANCELED == ztimer_mutex_lock_timeout(ZTIMER_USEC, &irq_mut,
|
|
mutex_timeout));
|
|
puts_optional("... OK");
|
|
|
|
puts_optional("Testing both edges on PIN_IN_0");
|
|
expect(0 == gpio_ll_irq(port_in, PIN_IN_0, GPIO_TRIGGER_EDGE_BOTH,
|
|
irq_edge_cb, &irq_mut));
|
|
/* test for spurious IRQ */
|
|
expect(-ECANCELED == ztimer_mutex_lock_timeout(ZTIMER_USEC, &irq_mut,
|
|
mutex_timeout));
|
|
gpio_ll_set(port_out, 1UL << PIN_OUT_0);
|
|
/* test for IRQ on rising edge */
|
|
expect(0 == ztimer_mutex_lock_timeout(ZTIMER_USEC, &irq_mut,
|
|
mutex_timeout));
|
|
/* test for spurious IRQ */
|
|
expect(-ECANCELED == ztimer_mutex_lock_timeout(ZTIMER_USEC, &irq_mut,
|
|
mutex_timeout));
|
|
gpio_ll_clear(port_out, 1UL << PIN_OUT_0);
|
|
/* test for IRQ on falling edge */
|
|
expect(0 == ztimer_mutex_lock_timeout(ZTIMER_USEC, &irq_mut,
|
|
mutex_timeout));
|
|
/* test for spurious IRQ */
|
|
expect(-ECANCELED == ztimer_mutex_lock_timeout(ZTIMER_USEC, &irq_mut,
|
|
mutex_timeout));
|
|
puts_optional("... OK");
|
|
|
|
puts_optional("Testing masking of IRQs (still both edges on PIN_IN_0)");
|
|
gpio_ll_irq_mask(port_in, PIN_IN_0);
|
|
gpio_ll_set(port_out, 1UL << PIN_OUT_0);
|
|
/* test for no IRQ while masked */
|
|
expect(-ECANCELED == ztimer_mutex_lock_timeout(ZTIMER_USEC, &irq_mut,
|
|
mutex_timeout));
|
|
gpio_ll_irq_unmask_and_clear(port_in, PIN_IN_0);
|
|
/* test for IRQ of past event is cleared */
|
|
expect(-ECANCELED == ztimer_mutex_lock_timeout(ZTIMER_USEC, &irq_mut,
|
|
mutex_timeout));
|
|
/* testing for normal behavior after unmasked */
|
|
gpio_ll_clear(port_out, 1UL << PIN_OUT_0);
|
|
expect(0 == ztimer_mutex_lock_timeout(ZTIMER_USEC, &irq_mut,
|
|
mutex_timeout));
|
|
/* test for spurious IRQ */
|
|
expect(-ECANCELED == ztimer_mutex_lock_timeout(ZTIMER_USEC, &irq_mut,
|
|
mutex_timeout));
|
|
#if MODULE_PERIPH_GPIO_LL_IRQ_UNMASK
|
|
gpio_ll_irq_mask(port_in, PIN_IN_0);
|
|
gpio_ll_set(port_out, 1UL << PIN_OUT_0);
|
|
/* test for no IRQ while masked */
|
|
expect(-ECANCELED == ztimer_mutex_lock_timeout(ZTIMER_USEC, &irq_mut,
|
|
mutex_timeout));
|
|
gpio_ll_irq_unmask(port_in, PIN_IN_0);
|
|
/* test for IRQ of past event */
|
|
expect(0 == ztimer_mutex_lock_timeout(ZTIMER_USEC, &irq_mut,
|
|
mutex_timeout));
|
|
/* test for spurious IRQ */
|
|
expect(-ECANCELED == ztimer_mutex_lock_timeout(ZTIMER_USEC, &irq_mut,
|
|
mutex_timeout));
|
|
#endif
|
|
puts_optional("... OK");
|
|
gpio_ll_irq_off(port_in, PIN_IN_0);
|
|
}
|
|
|
|
struct mutex_counter {
|
|
mutex_t mutex;
|
|
unsigned counter;
|
|
};
|
|
|
|
__attribute__((unused))
|
|
static void irq_level_cb(void *_arg)
|
|
{
|
|
struct mutex_counter *arg = _arg;
|
|
|
|
if (!arg->counter) {
|
|
gpio_ll_toggle(port_out, 1UL << PIN_OUT_0);
|
|
mutex_unlock(&arg->mutex);
|
|
}
|
|
else {
|
|
arg->counter--;
|
|
}
|
|
}
|
|
|
|
static void test_irq_level(void)
|
|
{
|
|
struct mutex_counter arg = { .mutex = MUTEX_INIT_LOCKED, .counter = 10 };
|
|
|
|
if (IS_USED(MODULE_PERIPH_GPIO_LL_IRQ_LEVEL_TRIGGERED_HIGH)) {
|
|
puts_optional("Testing level-triggered on HIGH on PIN_IN_0 (when input "
|
|
"is LOW when setting up IRQ)");
|
|
gpio_ll_clear(port_out, 1UL << PIN_OUT_0);
|
|
expect(0 == gpio_ll_irq(port_in, PIN_IN_0, GPIO_TRIGGER_LEVEL_HIGH,
|
|
irq_level_cb, &arg));
|
|
/* test for spurious IRQ */
|
|
expect(-ECANCELED == ztimer_mutex_lock_timeout(ZTIMER_USEC, &arg.mutex,
|
|
mutex_timeout));
|
|
/* test for 10 level triggered IRQs on high */
|
|
gpio_ll_set(port_out, 1UL << PIN_OUT_0);
|
|
expect(0 == ztimer_mutex_lock_timeout(ZTIMER_USEC, &arg.mutex,
|
|
mutex_timeout));
|
|
/* test for spurious IRQ */
|
|
expect(-ECANCELED == ztimer_mutex_lock_timeout(ZTIMER_USEC, &arg.mutex,
|
|
mutex_timeout));
|
|
gpio_ll_irq_off(port_in, PIN_IN_0);
|
|
puts_optional("... OK");
|
|
|
|
puts_optional("Testing level-triggered on HIGH on PIN_IN_0 (when input "
|
|
"is HIGH when setting up IRQ)");
|
|
gpio_ll_set(port_out, 1UL << PIN_OUT_0);
|
|
expect(0 == gpio_ll_irq(port_in, PIN_IN_0, GPIO_TRIGGER_LEVEL_HIGH,
|
|
irq_level_cb, &arg));
|
|
/* test for 10 level triggered IRQs */
|
|
expect(0 == ztimer_mutex_lock_timeout(ZTIMER_USEC, &arg.mutex,
|
|
mutex_timeout));
|
|
/* test for spurious IRQ */
|
|
expect(-ECANCELED == ztimer_mutex_lock_timeout(ZTIMER_USEC, &arg.mutex,
|
|
mutex_timeout));
|
|
gpio_ll_irq_off(port_in, PIN_IN_0);
|
|
puts_optional("... OK");
|
|
}
|
|
|
|
if (IS_USED(MODULE_PERIPH_GPIO_LL_IRQ_LEVEL_TRIGGERED_LOW)) {
|
|
puts_optional("Testing level-triggered on LOW on PIN_IN_0 (when input "
|
|
"is HIGH when setting up IRQ)");
|
|
gpio_ll_set(port_out, 1UL << PIN_OUT_0);
|
|
arg.counter = 10;
|
|
expect(0 == gpio_ll_irq(port_in, PIN_IN_0, GPIO_TRIGGER_LEVEL_LOW,
|
|
irq_level_cb, &arg));
|
|
/* test for spurious IRQ */
|
|
expect(-ECANCELED == ztimer_mutex_lock_timeout(ZTIMER_USEC, &arg.mutex,
|
|
mutex_timeout));
|
|
/* test for 10 level triggered IRQs on low */
|
|
gpio_ll_clear(port_out, 1UL << PIN_OUT_0);
|
|
expect(0 == ztimer_mutex_lock_timeout(ZTIMER_USEC, &arg.mutex,
|
|
mutex_timeout));
|
|
/* test for spurious IRQ */
|
|
expect(-ECANCELED == ztimer_mutex_lock_timeout(ZTIMER_USEC, &arg.mutex,
|
|
mutex_timeout));
|
|
gpio_ll_irq_off(port_in, PIN_IN_0);
|
|
puts_optional("... OK");
|
|
|
|
puts_optional("Testing level-triggered on LOW on PIN_IN_0 (when input "
|
|
"is LOW when setting up IRQ)");
|
|
gpio_ll_clear(port_out, 1UL << PIN_OUT_0);
|
|
arg.counter = 10;
|
|
expect(0 == gpio_ll_irq(port_in, PIN_IN_0, GPIO_TRIGGER_LEVEL_LOW,
|
|
irq_level_cb, &arg));
|
|
/* test for 10 level triggered IRQs */
|
|
expect(0 == ztimer_mutex_lock_timeout(ZTIMER_USEC, &arg.mutex,
|
|
mutex_timeout));
|
|
/* test for spurious IRQ */
|
|
expect(-ECANCELED == ztimer_mutex_lock_timeout(ZTIMER_USEC, &arg.mutex,
|
|
mutex_timeout));
|
|
gpio_ll_irq_off(port_in, PIN_IN_0);
|
|
puts_optional("... OK");
|
|
}
|
|
}
|
|
|
|
static void test_irq(void)
|
|
{
|
|
if (IS_USED(MODULE_PERIPH_GPIO_LL_IRQ)) {
|
|
puts_optional("\n"
|
|
"Testing External IRQs\n"
|
|
"=====================\n");
|
|
|
|
expect(0 == gpio_ll_init(port_in, PIN_IN_0, &gpio_ll_in));
|
|
expect(0 == gpio_ll_init(port_out, PIN_OUT_0, &gpio_ll_out));
|
|
|
|
test_irq_edge();
|
|
test_irq_level();
|
|
}
|
|
}
|
|
|
|
int main(void)
|
|
{
|
|
print_details();
|
|
test_gpio_port_pack();
|
|
test_gpio_ll_init();
|
|
test_input_output();
|
|
if (IS_USED(MODULE_PERIPH_GPIO_LL_IRQ)) {
|
|
test_irq();
|
|
}
|
|
|
|
/* if no expect() didn't blow up until now, the test is passed */
|
|
|
|
puts("\n\nTEST SUCCEEDED");
|
|
return 0;
|
|
}
|