From 7394b4584d6b1689839b8ebbd994492fa061c615 Mon Sep 17 00:00:00 2001 From: cuiziwei Date: Wed, 8 Feb 2023 11:38:09 +0800 Subject: [PATCH] testing/drivertest: add pwm driver test Signed-off-by: cuiziwei --- testing/drivertest/Makefile | 4 + testing/drivertest/drivertest_pwm.c | 316 ++++++++++++++++++++++++++++ 2 files changed, 320 insertions(+) create mode 100644 testing/drivertest/drivertest_pwm.c diff --git a/testing/drivertest/Makefile b/testing/drivertest/Makefile index a1e735a6c..acf6370e6 100644 --- a/testing/drivertest/Makefile +++ b/testing/drivertest/Makefile @@ -39,5 +39,9 @@ MAINSRC += drivertest_timer.c PROGNAME += cmocka_driver_timer endif +ifneq ($(CONFIG_PWM),) +MAINSRC += drivertest_pwm.c +PROGNAME += cmocka_driver_pwm +endif include $(APPDIR)/Application.mk diff --git a/testing/drivertest/drivertest_pwm.c b/testing/drivertest/drivertest_pwm.c new file mode 100644 index 000000000..78d5c6d31 --- /dev/null +++ b/testing/drivertest/drivertest_pwm.c @@ -0,0 +1,316 @@ +/**************************************************************************** + * apps/testing/drivertest/drivertest_pwm.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define PWM_DEFAULT_COUNT 0 +#define PWM_DEFAULT_DURATION 5 +#define PWM_DEFAULT_DUTY 50 +#define PWM_DEFAULT_FREQUENCY 100 +#define PWM_DEFAULT_DEVPATH "/dev/pwm0" + +#define OPTARG_TO_VALUE(value, type, base) \ + do \ + { \ + FAR char *ptr; \ + value = (type)strtoul(optarg, &ptr, base); \ + if (*ptr != '\0') \ + { \ + printf("Parameter error: -%c %s\n", ch, optarg); \ + pwm_help(argv[0], pwm_state, EXIT_FAILURE); \ + } \ + } while (0) + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +struct pwm_state_s +{ + char devpath[PATH_MAX]; + uint8_t duty; + uint32_t freq; +#ifdef CONFIG_PWM_PULSECOUNT + uint32_t count; +#endif + int duration; +}; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: pwm_help + ****************************************************************************/ + +static void pwm_help(FAR const char *progname, + FAR struct pwm_state_s *pwm_state, int exitcode) +{ + printf("Usage: %s" + " -p -f -d -n -t \n", + progname); + printf("\nArguments are \"sticky\". " + "For example, once the PWM frequency is\n"); + printf("specified, that frequency will be re-used until it is changed.\n"); + printf("\n\"sticky\" OPTIONS include:\n"); + printf(" [-p devpath] selects the PWM device. " + "Default: %s Current: %s\n", PWM_DEFAULT_DEVPATH, + pwm_state->devpath); + printf(" [-f frequency] selects the pulse frequency. " + "Default: %d Hz Current: %" PRIu32 " Hz\n", + PWM_DEFAULT_FREQUENCY, pwm_state->freq); + printf(" [-d duty] selects the pulse duty as a percentage. " + "Default: %d %% Current: %d %%\n", + PWM_DEFAULT_DUTY, pwm_state->duty); + +#ifdef CONFIG_PWM_PULSECOUNT + printf(" [-n count] selects the pulse count. " + "Default: %d Current: %" PRIx32 "\n", + PWM_DEFAULT_COUNT, pwm_state->count); +#endif + printf(" [-t duration] is the duration of the pulse train in seconds. " + "Default: %d Current: %d\n", + PWM_DEFAULT_DURATION, pwm_state->duration); + printf(" [-h] shows this message and exits\n"); + + exit(exitcode); +} + +/**************************************************************************** + * Name: parse_commandline + ****************************************************************************/ + +static void parse_commandline(FAR struct pwm_state_s *pwm_state, int argc, + FAR char **argv) +{ + int ch; + int converted; + + while ((ch = getopt(argc, argv, "p:d:n:f:t:h")) != ERROR) + { + switch (ch) + { + case 'p': + strncpy(pwm_state->devpath, optarg, sizeof(pwm_state->devpath)); + pwm_state->devpath[sizeof(pwm_state->devpath) - 1] = '\0'; + break; + case 'd': + OPTARG_TO_VALUE(converted, uint8_t, 10); + if (converted < 0 || converted > 100) + { + printf("Duty out of range: %d\n", converted); + pwm_help(argv[0], pwm_state, EXIT_FAILURE); + } + + pwm_state->duty = (uint8_t)converted; + break; +#ifdef CONFIG_PWM_PULSECOUNT + case 'n': + OPTARG_TO_VALUE(converted, uint32_t, 10); + if (converted < 0) + { + printf("Count must be non-negative: %ld\n", converted); + pwm_help(argv[0], pwm_state, EXIT_FAILURE); + } + + pwm_state->count = (uint32_t)value; + break; +#endif + case 'f': + OPTARG_TO_VALUE(converted, uint32_t, 10); + if (converted < 0) + { + printf("Frequency out of range: %d\n", converted); + pwm_help(argv[0], pwm_state, EXIT_FAILURE); + } + + pwm_state->freq = (uint32_t)converted; + break; + case 't': + OPTARG_TO_VALUE(converted, int, 10); + if (converted < 1 || converted > INT_MAX) + { + printf("Duty out of range: %d\n", converted); + pwm_help(argv[0], pwm_state, EXIT_FAILURE); + } + + pwm_state->duration = (int)converted; + break; + case 'h': + pwm_help(argv[0], pwm_state, EXIT_FAILURE); + break; + case '?': + printf("Unsupported option: %s\n", optarg); + pwm_help(argv[0], pwm_state, EXIT_FAILURE); + break; + } + } + + printf("devname = %s\n" + "duty = %d\n" + "frenquency = %" PRIu32 "\n" + "duration = %d\n", + pwm_state->devpath, + pwm_state->duty, + pwm_state->freq, + pwm_state->duration); + +#ifdef CONFIG_PWM_PULSECOUNT + printf("count = %" PRIu32 "\n", pwm_state->count); +#endif +} + +/**************************************************************************** + * Name: test_case_pwm + ****************************************************************************/ + +static void test_case_pwm(FAR void **state) +{ + int fd; + int ret; + struct pwm_info_s info; + FAR struct pwm_state_s *pwm_state; + pwm_state = (FAR struct pwm_state_s *)*state; + + /* Open the PWM device for reading */ + + fd = open(pwm_state->devpath, O_RDONLY); + assert_true(fd > 0); + + /* Configure the characteristics of the pulse train */ + + memset(&info, 0, sizeof(info)); + + info.frequency = pwm_state->freq; + info.duty = b16divi(uitoub16(pwm_state->duty), 100); + +#ifdef CONFIG_PWM_MULTICHAN + for (i = 0; i < CONFIG_PWM_NCHANNELS; i++) + { + info.channels[i].channel = i + 1; + info.channels[i].duty = info.duty; + } +#endif + +#ifdef CONFIG_PWM_PULSECOUNT + info.count = pwm_state.count; +#endif + + ret = ioctl(fd, PWMIOC_SETCHARACTERISTICS, + (unsigned long)((uintptr_t)&info)); + assert_return_code(ret, OK); + + /* Then start the pulse train. Since the driver was opened in blocking + * mode, this call will block if the count value is greater than zero. + */ + + ret = ioctl(fd, PWMIOC_START, 0); + assert_return_code(ret, OK); + + /* It a non-zero count was not specified, then wait for the selected + * duration, then stop the PWM output. + */ + +#ifdef CONFIG_PWM_PULSECOUNT + if (info.count == 0) +#endif + { + /* Wait for the specified duration */ + + sleep(pwm_state->duration); + + /* Then stop the pulse train */ + + ret = ioctl(fd, PWMIOC_STOP, 0); + assert_return_code(ret, OK); + } + + close(fd); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: main + ****************************************************************************/ + +int main(int argc, FAR char *argv[]) +{ + /* Initialize the state data */ + + struct pwm_state_s pwm_state = + { + .devpath = PWM_DEFAULT_DEVPATH, + .duty = PWM_DEFAULT_DUTY, + .freq = PWM_DEFAULT_FREQUENCY, + .duration = PWM_DEFAULT_DURATION, +#ifdef CONFIG_PWM_PULSECOUNT + .count = PWM_DEFAULT_COUNT, +#endif + }; + + parse_commandline(&pwm_state, argc, argv); + + const struct CMUnitTest tests[] = + { + cmocka_unit_test_prestate(test_case_pwm, &pwm_state) + }; + + return cmocka_run_group_tests(tests, NULL, NULL); +}