PPI and timers


Hello
I am learning how to connect timers with gpio via PPI, i working on nrf52832dk.

So far I have connected the RTC 0 alarm channel to the LED no.3 (pin no.19) and its state changes every time the timer is called.

I tried to do the same with timer 1 and LED4 (pin no.20) , unfortunately this connection won't work.

I am enclosing the code that theoretically should work, can anyone check this problem?


#include <zephyr.h>
#include <debug/ppi_trace.h>
#include <drivers/counter.h>
#include <hal/nrf_rtc.h>
#include <hal/nrf_clock.h>
#include <hal/nrf_timer.h>
#include <nrfx_timer.h>
#include <device.h>
#include <logging/log.h>

LOG_MODULE_REGISTER(app);

#define ALARM_PERIOD_US 1000 * 1000
#define RTC NRF_RTC2
#define TIMER NRF_TIMER1

#define RTC_LABEL DT_LABEL(DT_NODELABEL(rtc2))
#define TIMER_LABEL DT_LABEL(DT_NODELABEL(timer1))

static void alarm_callback(const struct device *dev, uint8_t chan_id, uint32_t ticks,
						   void *user_data);

static void timer_callback(const struct device *dev, uint8_t chan_id, uint32_t ticks,
						   void *user_data);

static struct counter_alarm_cfg RTC_alarm_cfg = {
	.callback = alarm_callback,
	.flags = COUNTER_ALARM_CFG_ABSOLUTE,
};

static struct counter_alarm_cfg TIM0_alarm_cfg = {
	.callback = timer_callback,
	.flags = 0, //?
};

static void ppi_trace_pin_setup(uint32_t pin, uint32_t evt)
{
	void *handle;

	handle = ppi_trace_config(pin, evt);
	__ASSERT(handle != NULL,
			 "Failed to initialize trace pin, no PPI or GPIOTE resources?");

	ppi_trace_enable(handle);
}


static void ppi_trace_setup(void)
{
	ppi_trace_pin_setup(19,
						nrf_rtc_event_address_get(RTC, NRF_RTC_EVENT_COMPARE_0));
	

	ppi_trace_pin_setup(20, nrf_timer_event_address_get(TIMER, NRF_TIMER_EVENT_COMPARE1));

	LOG_INF("PPI trace setup done.");
}

static void alarm_callback(const struct device *dev, uint8_t chan_id,
						   uint32_t ticks, void *user_data)
{
	int err;
	uint32_t alarm_cnt = (uint32_t)user_data + 1;

	RTC_alarm_cfg.ticks = ticks + counter_us_to_ticks(dev, ALARM_PERIOD_US);
	RTC_alarm_cfg.user_data = (void *)alarm_cnt;
	LOG_INF("alarm_callback");
	err = counter_set_channel_alarm(dev, 0, &RTC_alarm_cfg);
	__ASSERT_NO_MSG(err == 0);
	(void)err;
}

static void timer_callback(const struct device *dev, uint8_t chan_id,
						   uint32_t ticks, void *user_data)
{
	int err;
	uint32_t alarm_cnt = (uint32_t)user_data + 1;

	TIM0_alarm_cfg.ticks = counter_us_to_ticks(dev, ALARM_PERIOD_US/2);
	TIM0_alarm_cfg.user_data = (void *)alarm_cnt;
	LOG_INF("timer_callback %d ",TIM0_alarm_cfg.ticks);
	err = counter_set_channel_alarm(dev, 1, &TIM0_alarm_cfg);
	__ASSERT_NO_MSG(err == 0);
	(void)err;
}

static void counter_setup(void)
{
	{
		int err;
		const struct device *dev = device_get_binding(RTC_LABEL);

		__ASSERT(dev, "Sample cannot run on this board.");

		RTC_alarm_cfg.ticks = counter_us_to_ticks(dev, ALARM_PERIOD_US);
		err = counter_set_channel_alarm(dev, 0, &RTC_alarm_cfg);
		__ASSERT_NO_MSG(err == 0);

		err = counter_start(dev);
		__ASSERT_NO_MSG(err == 0);
	}
	{
		int err;
		const struct device *dev = device_get_binding(TIMER_LABEL);

		__ASSERT(dev, "Sample cannot run on this board.");

		TIM0_alarm_cfg.ticks = counter_us_to_ticks(dev, ALARM_PERIOD_US);

		err = counter_set_channel_alarm(dev, 1, &TIM0_alarm_cfg);
		__ASSERT_NO_MSG(err == 0);

		 err = counter_start(dev);
		__ASSERT_NO_MSG(err == 0);
	}
}

void main(void)
{
	ppi_trace_setup();
	counter_setup();

	while (1)
	{
		k_msleep(1000);
	}
}

Parents Reply
  • Hi 

    You can look at the implementation of counter_nrfx_timer.c for details. 

    If you look here you can see that CC register 0 is used to store the top value, and CC register 1 is used every time you want to read out the current state of the timer in the read() function. 

    The TIMER module does not allow you to read out the state of the timer without running a capture operation, and in order to do a capture you need to have a free CC register to store the capture value, which is why CC1 is assigned to this task only. 

    This leaves only two more free CC registers (CC2 and CC3) to store custom values, and the CC_TO_ID and ID_TO_CC macros are used to convert between CC number and channel ID. 

    Best regards
    Torbjørn

Children
No Data
Related