NCS 2.9.1 - NRF5340 DK
I've developed the gppi one_to_one example to try and create an asymmetric pulse that I can control with the Timer compare registers. It boggles my mind why this code doesn't produce a 25% duty cycle pwm as I have configured.
#include <nrfx_example.h>
#include <helpers/nrfx_gppi.h>
#include <nrfx_timer.h>
#include <nrfx_gpiote.h>
#define NRFX_LOG_MODULE EXAMPLE
#define NRFX_EXAMPLE_CONFIG_LOG_ENABLED 1
#define NRFX_EXAMPLE_CONFIG_LOG_LEVEL 3
#include <nrfx_log.h>
/**
* @defgroup nrfx_gppi_one_to_one_example One-to-one GPPI example
* @{
* @ingroup nrfx_gppi_examples
*
* @brief Example showing basic functionality of a nrfx_gppi helper.
*
* @details Application initializes nrfx_gpiote, nrfx_timer drivers and nrfx_gppi helper in a way that
* TIMER compare event is set up to be forwarded via PPI/DPPI to GPIOTE and toggle a pin.
*/
/** @brief Symbol specifying timer instance to be used. */
#define TIMER_INST_IDX 0
/** @brief Symbol specifying time in milliseconds to wait for handler execution. */
#define TIME_TO_WAIT_MS 20UL
/** @brief Symbol specifying GPIOTE instance to be used. */
#define GPIOTE_INST_IDX 0
/** @brief Symbol specifying ouput pin associated with the task. */
#define OUTPUT_PIN 7
static uint32_t pulse;
static uint32_t period_len;
/**
* @brief Function for handling TIMER driver events.
*
* @param[in] event_type Timer event.
* @param[in] p_context General purpose parameter set during initialization of the timer.
* This parameter can be used to pass additional information to the handler
* function for example the timer ID.
*/
static void timer_handler(nrf_timer_event_t event_type, void * p_context)
{
switch(event_type)
{
case NRF_TIMER_EVENT_COMPARE0:
NRFX_LOG_INFO("Compare 0");
break;
case NRF_TIMER_EVENT_COMPARE1:
NRFX_LOG_INFO("Compare 1");
break;
default:
break;
}
}
/**
* @brief Function for application main entry.
*
* @return Nothing.
*/
int main(void)
{
nrfx_err_t status;
(void)status;
uint8_t out_channel;
uint8_t gppi_channel_hi;
uint8_t gppi_channel_lo;
#if defined(__ZEPHYR__)
IRQ_CONNECT(NRFX_IRQ_NUMBER_GET(NRF_TIMER_INST_GET(TIMER_INST_IDX)), IRQ_PRIO_LOWEST,
NRFX_TIMER_INST_HANDLER_GET(TIMER_INST_IDX), 0, 0);
IRQ_CONNECT(NRFX_IRQ_NUMBER_GET(NRF_GPIOTE_INST_GET(GPIOTE_INST_IDX)), IRQ_PRIO_LOWEST,
NRFX_GPIOTE_INST_HANDLER_GET(GPIOTE_INST_IDX), 0, 0);
#endif
NRFX_EXAMPLE_LOG_INIT();
NRFX_LOG_INFO("Starting nrfx_gppi basic one-to-one example.");
NRFX_EXAMPLE_LOG_PROCESS();
nrfx_gpiote_t const gpiote_inst = NRFX_GPIOTE_INSTANCE(GPIOTE_INST_IDX);
status = nrfx_gpiote_init(&gpiote_inst, NRFX_GPIOTE_DEFAULT_CONFIG_IRQ_PRIORITY);
NRFX_ASSERT(status == NRFX_SUCCESS);
NRFX_LOG_INFO("GPIOTE status: %s",
nrfx_gpiote_init_check(&gpiote_inst) ? "initialized" : "not initialized");
status = nrfx_gpiote_channel_alloc(&gpiote_inst, &out_channel);
NRFX_ASSERT(status == NRFX_SUCCESS);
/*
* Initialize output pin. The SET task will turn the LED on,
* CLR will turn it off and OUT will toggle it.
*/
static const nrfx_gpiote_output_config_t output_config =
{
.drive = NRF_GPIO_PIN_S0S1,
.input_connect = NRF_GPIO_PIN_INPUT_DISCONNECT,
.pull = NRF_GPIO_PIN_PULLDOWN,
};
const nrfx_gpiote_task_config_t task_config_hi =
{
.task_ch = out_channel,
.polarity = NRF_GPIOTE_POLARITY_TOGGLE,
.init_val = NRF_GPIOTE_INITIAL_VALUE_HIGH,
};
status = nrfx_gpiote_output_configure(&gpiote_inst, OUTPUT_PIN, &output_config, &task_config_hi);
NRFX_ASSERT(status == NRFX_SUCCESS);
nrfx_gpiote_out_task_enable(&gpiote_inst, OUTPUT_PIN);
nrfx_timer_t timer_inst = NRFX_TIMER_INSTANCE(TIMER_INST_IDX);
uint32_t base_frequency = NRF_TIMER_BASE_FREQUENCY_GET(timer_inst.p_reg);
nrfx_timer_config_t timer_config = NRFX_TIMER_DEFAULT_CONFIG(base_frequency);
timer_config.bit_width = NRF_TIMER_BIT_WIDTH_32;
timer_config.p_context = "Some context";
status = nrfx_timer_init(&timer_inst, &timer_config, timer_handler);
NRFX_ASSERT(status == NRFX_SUCCESS);
nrfx_timer_clear(&timer_inst);
/* Creating variable desired_ticks to store the output of nrfx_timer_ms_to_ticks function. */
pulse = nrfx_timer_ms_to_ticks(&timer_inst, 10);
period_len = nrfx_timer_ms_to_ticks(&timer_inst, 40);
NRFX_LOG_INFO("Period Length: %lu ms", TIME_TO_WAIT_MS*2);
/*
* Setting the timer channel NRF_TIMER_CC_CHANNEL0 in the extended compare mode to clear
* the timer and to trigger an interrupt if the internal counter register is equal to
* desired_ticks.
*/
nrfx_timer_compare(&timer_inst, NRF_TIMER_CC_CHANNEL0, pulse, true);
nrfx_timer_extended_compare(&timer_inst, NRF_TIMER_CC_CHANNEL1, period_len,
NRF_TIMER_SHORT_COMPARE1_CLEAR_MASK, true);
status = nrfx_gppi_channel_alloc(&gppi_channel_hi);
NRFX_ASSERT(status == NRFX_SUCCESS);
status = nrfx_gppi_channel_alloc(&gppi_channel_lo);
NRFX_ASSERT(status == NRFX_SUCCESS);
/*
* Configure endpoints of the channel so that the input timer event is connected with the output
* pin OUT task. This means that each time the timer interrupt occurs, the LED pin will be toggled.
*/
nrfx_gppi_channel_endpoints_setup(gppi_channel_lo,
nrfx_timer_compare_event_address_get(&timer_inst, NRF_TIMER_CC_CHANNEL0),
nrfx_gpiote_out_task_address_get(&gpiote_inst, OUTPUT_PIN));
nrfx_gppi_channel_endpoints_setup(gppi_channel_hi,
nrfx_timer_compare_event_address_get(&timer_inst, NRF_TIMER_CC_CHANNEL1),
nrfx_gpiote_out_task_address_get(&gpiote_inst, OUTPUT_PIN));
nrfx_gppi_channels_enable(BIT(gppi_channel_hi));
nrfx_gppi_channels_enable(BIT(gppi_channel_lo));
nrfx_timer_enable(&timer_inst);
NRFX_LOG_INFO("Timer status: %s", nrfx_timer_is_enabled(&timer_inst) ? "enabled" : "disabled");
while (1)
{
NRFX_EXAMPLE_LOG_PROCESS();
}
}
/** @} */
Instead it only produces a square wave as if it's ignoring the additional toggle gppi channel I've configured - even though I can see the timer interrupt gets handled for both CC events. Do I have this configured correctly?