|
| 1 | +/* |
| 2 | + * Copyright (c) 2025, Nordic Semiconductor ASA |
| 3 | + * |
| 4 | + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause |
| 5 | + */ |
| 6 | + |
| 7 | +#include <zephyr/kernel.h> |
| 8 | +#include <zephyr/ztest.h> |
| 9 | +#include <nrfx_pwm.h> |
| 10 | +#include <nrfx_timer.h> |
| 11 | +#include <helpers/nrfx_gppi.h> |
| 12 | + |
| 13 | +#define PWM_OUTPUT_PIN NRF_DT_GPIOS_TO_PSEL(DT_NODELABEL(led2), gpios) |
| 14 | + |
| 15 | +#define SLEEP_TIME_MS 500 |
| 16 | + |
| 17 | +struct pwm_events_fixture { |
| 18 | + nrfx_pwm_t *pwm; |
| 19 | + nrf_pwm_sequence_t *pwm_sequence; |
| 20 | + nrfx_timer_t *test_timer; |
| 21 | + uint32_t timer_task_address; |
| 22 | + uint32_t domain_id; |
| 23 | + nrfx_gppi_handle_t gppi_handle; |
| 24 | +}; |
| 25 | + |
| 26 | +static void configure_pwm(nrfx_pwm_t *pwm) |
| 27 | +{ |
| 28 | + nrfx_pwm_config_t pwm_config = |
| 29 | + NRFX_PWM_DEFAULT_CONFIG(PWM_OUTPUT_PIN, NRF_PWM_PIN_NOT_CONNECTED, |
| 30 | + NRF_PWM_PIN_NOT_CONNECTED, NRF_PWM_PIN_NOT_CONNECTED); |
| 31 | + |
| 32 | + pwm_config.count_mode = NRF_PWM_MODE_UP_AND_DOWN; |
| 33 | + |
| 34 | + zassert_ok(nrfx_pwm_init(pwm, &pwm_config, NULL, NULL), "NRFX PWM init failed\n"); |
| 35 | +} |
| 36 | + |
| 37 | +static uint32_t configure_test_timer(nrfx_timer_t *timer) |
| 38 | +{ |
| 39 | + uint32_t base_frequency = NRF_TIMER_BASE_FREQUENCY_GET(timer->p_reg); |
| 40 | + nrfx_timer_config_t timer_config = NRFX_TIMER_DEFAULT_CONFIG(base_frequency); |
| 41 | + |
| 42 | + timer_config.bit_width = NRF_TIMER_BIT_WIDTH_16; |
| 43 | + timer_config.mode = NRF_TIMER_MODE_COUNTER; |
| 44 | + |
| 45 | + TC_PRINT("Timer base frequency: %d Hz\n", base_frequency); |
| 46 | + |
| 47 | + zassert_ok(nrfx_timer_init(timer, &timer_config, NULL), "Timer init failed\n"); |
| 48 | + nrfx_timer_enable(timer); |
| 49 | + |
| 50 | + return nrfx_timer_task_address_get(timer, NRF_TIMER_TASK_COUNT); |
| 51 | +} |
| 52 | + |
| 53 | +static void setup_dppi_connection(nrfx_gppi_handle_t *gppi_handle, uint32_t domain_id, |
| 54 | + uint32_t timer_task_address, uint32_t pwm_event_address) |
| 55 | +{ |
| 56 | + zassert_ok(nrfx_gppi_conn_alloc(pwm_event_address, timer_task_address, gppi_handle), |
| 57 | + "Failed to allocate DPPI connection\n"); |
| 58 | + nrfx_gppi_conn_enable(*gppi_handle); |
| 59 | +} |
| 60 | + |
| 61 | +static void clear_dppi_connection(nrfx_gppi_handle_t *gppi_handle, uint32_t domain_id, |
| 62 | + uint32_t timer_task_address, uint32_t pwm_event_address) |
| 63 | +{ |
| 64 | + nrfx_gppi_conn_disable(*gppi_handle); |
| 65 | + nrfx_gppi_conn_free(pwm_event_address, timer_task_address, *gppi_handle); |
| 66 | +} |
| 67 | + |
| 68 | +static void run_pwm_event_test_case(struct pwm_events_fixture *fixture, |
| 69 | + nrf_pwm_event_t tested_pwm_event, |
| 70 | + uint32_t expected_triggers_count, char *event_name) |
| 71 | +{ |
| 72 | + uint32_t pwm_event_address; |
| 73 | + uint32_t timer_cc_before, timer_cc_after; |
| 74 | + |
| 75 | + pwm_event_address = nrf_pwm_event_address_get(fixture->pwm->p_reg, tested_pwm_event); |
| 76 | + setup_dppi_connection(&fixture->gppi_handle, fixture->domain_id, |
| 77 | + fixture->timer_task_address, pwm_event_address); |
| 78 | + |
| 79 | + nrf_pwm_event_clear(fixture->pwm->p_reg, tested_pwm_event); |
| 80 | + timer_cc_before = nrfx_timer_capture(fixture->test_timer, NRF_TIMER_CC_CHANNEL0); |
| 81 | + nrfx_pwm_simple_playback(fixture->pwm, fixture->pwm_sequence, 1, NRFX_PWM_FLAG_STOP); |
| 82 | + k_msleep(SLEEP_TIME_MS); |
| 83 | + timer_cc_after = nrfx_timer_capture(fixture->test_timer, NRF_TIMER_CC_CHANNEL0); |
| 84 | + |
| 85 | + TC_PRINT("PWM %s events count: %d\n", event_name, timer_cc_after - timer_cc_before); |
| 86 | + zassert_equal(timer_cc_after - timer_cc_before, expected_triggers_count, |
| 87 | + "PWM %s event triggered count != %u\n", event_name, expected_triggers_count); |
| 88 | + |
| 89 | + nrf_pwm_event_clear(fixture->pwm->p_reg, tested_pwm_event); |
| 90 | + clear_dppi_connection(&fixture->gppi_handle, fixture->domain_id, |
| 91 | + fixture->timer_task_address, pwm_event_address); |
| 92 | +} |
| 93 | + |
| 94 | +ZTEST_F(pwm_events, test_pwm_stop_event) |
| 95 | +{ |
| 96 | + run_pwm_event_test_case(fixture, NRF_PWM_EVENT_STOPPED, 1, "STOP"); |
| 97 | +} |
| 98 | + |
| 99 | +ZTEST_F(pwm_events, test_pwm_seqstarted_event) |
| 100 | +{ |
| 101 | + run_pwm_event_test_case(fixture, NRF_PWM_EVENT_SEQSTARTED1, 1, "SEQSTARTED1"); |
| 102 | +} |
| 103 | + |
| 104 | +ZTEST_F(pwm_events, test_pwm_seqend_event) |
| 105 | +{ |
| 106 | + run_pwm_event_test_case(fixture, NRF_PWM_EVENT_SEQEND1, 1, "SEQEND1"); |
| 107 | +} |
| 108 | + |
| 109 | +ZTEST_F(pwm_events, test_pwm_periodend_event) |
| 110 | +{ |
| 111 | + run_pwm_event_test_case(fixture, NRF_PWM_EVENT_PWMPERIODEND, fixture->pwm_sequence->length, |
| 112 | + "PWMPERIODEND"); |
| 113 | +} |
| 114 | + |
| 115 | +ZTEST_F(pwm_events, test_pwm_loopsdone_event) |
| 116 | +{ |
| 117 | + run_pwm_event_test_case(fixture, NRF_PWM_EVENT_LOOPSDONE, 1, "LOOPSDONE"); |
| 118 | +} |
| 119 | + |
| 120 | +static void *test_setup(void) |
| 121 | +{ |
| 122 | + static struct pwm_events_fixture fixture; |
| 123 | + static nrfx_timer_t test_timer = NRFX_TIMER_INSTANCE(DT_REG_ADDR(DT_NODELABEL(tst_timer))); |
| 124 | + static nrfx_pwm_t pwm = NRFX_PWM_INSTANCE(DT_REG_ADDR(DT_NODELABEL(dut_pwm))); |
| 125 | + static nrf_pwm_values_common_t pwm_duty_cycle_values[] = {0x500, 0x600}; |
| 126 | + static nrf_pwm_sequence_t pwm_sequence = {.values = {pwm_duty_cycle_values}, |
| 127 | + .length = ARRAY_SIZE(pwm_duty_cycle_values), |
| 128 | + .repeats = 0, |
| 129 | + .end_delay = 0}; |
| 130 | + |
| 131 | + fixture.pwm = &pwm; |
| 132 | + fixture.pwm_sequence = &pwm_sequence; |
| 133 | + configure_pwm(&pwm); |
| 134 | + |
| 135 | + fixture.test_timer = &test_timer; |
| 136 | + fixture.timer_task_address = configure_test_timer(&test_timer); |
| 137 | + |
| 138 | + fixture.domain_id = nrfx_gppi_domain_id_get((uint32_t)test_timer.p_reg); |
| 139 | + |
| 140 | + return &fixture; |
| 141 | +} |
| 142 | + |
| 143 | +ZTEST_SUITE(pwm_events, NULL, test_setup, NULL, NULL, NULL); |
0 commit comments