This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

How to continuously toggle pin using a timer?

I want to accomplish something very simple: use a timer to output a continuous square wave on a certain pin, at 30-60kHz or more. It doesn't have to be PWM or anything fancy, just a fixed frequency would do.

I cobbled together some code from examples that seems to initialize GPIOTE and PPI in a way that should accomplish this, as follows:

// GPIOTE init
nrf_gpio_cfg_output(PIN);
// Configure GPIOTE channel 0 to toggle the PWM pin state
nrf_gpiote_task_configure(0, PIN, NRF_GPIOTE_POLARITY_TOGGLE, NRF_GPIOTE_INITIAL_VALUE_LOW);

// PPI init
// Configure PPI channel 0 to toggle PIN_LED_PWMCLK on every TIMER2 COMPARE[0] match
NRF_PPI->CH[0].EEP = (uint32_t)&NRF_TIMER2->EVENTS_COMPARE[0];
NRF_PPI->CH[0].TEP = (uint32_t)&NRF_GPIOTE->TASKS_OUT[0];

NRF_PPI->CHEN = (PPI_CHEN_CH0_Enabled << PPI_CHEN_CH0_Pos);

// Timer init
NRF_TIMER2->MODE = TIMER_MODE_MODE_Timer;
NRF_TIMER2->TASKS_CLEAR = 1;
NRF_TIMER2->PRESCALER = 1;
NRF_TIMER2->BITMODE = TIMER_BITMODE_BITMODE_08Bit;

NRF_TIMER2->CC[0] = 255;
NRF_TIMER2->CC[1] = 0;
NRF_TIMER2->CC[2] = 0;

NRF_TIMER2->SHORTS = (TIMER_SHORTS_COMPARE0_CLEAR_Enabled << TIMER_SHORTS_COMPARE0_CLEAR_Pos);

NRF_TIMER2->TASKS_START = 1;

Unfortunately this simply doesn't toggle the output pin. What is wrong with my code?

I was expecting this to toggle at 16MHz / (prescaler 1 * count 256) = 62kHz.

Unlike some of the PWM examples I've seen, this only uses the shortcut to reset the counter; there are no interrupts (as they wouldn't be a reliable way to reset the counter at this frequency).

Also unlike the PWM examples, this only uses CC[0] and not the other registers.

Environment: modified version of ble_app_uart_c example, SDK 12.1.0, S130 softdevice, nRF51 DK, armgcc on Ubuntu. The code above is inserted in main() after the initialization of everything (after the call to nus_c_init).

Related