mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
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;
|
|
}
|