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

Timer, GPIOTE, PPI problem

I'm using an nrf52832 development board and am trying to implement a simple toggling of an output pin using a timer and PPI.
Below is the code I’m using; however, the pin doesn’t toggle but stays high. Any help appreciated.

#define PIN 30

NRF_TIMER1->MODE = 0;  //timer mode

NRF_TIMER1->BITMODE = 0;  //16 bit timer width

NRF_TIMER1->PRESCALER = 4;  //16MHz / 2^4 = 1MHz

NRF_TIMER1->SHORTS = 1;  //timer cleared at CC[0]

NRF_TIMER1->CC[0] = 1000;  //.001 secs

NRF_GPIOTE->CONFIG[0] = (3 | PIN << 8 | 3 << 16 | 1 << 20);  //task mode, PIN selected, toggle, PIN high initially

//Configure PPI channel 0 to toggle PIN on every Timer 1 COMPARE[0] match

NRF_PPI->CH[0].EEP = (uint32_t)&NRF_TIMER1->EVENTS_COMPARE[0];

NRF_PPI->CH[0].TEP = (uint32_t)&NRF_GPIOTE->TASKS_OUT[0];

NRF_PPI->CHENSET = 1;  //enable PPI channel 0

NRF_TIMER1->TASKS_START = 1;

Parents Reply Children
  • Thanks, Edvin. I'm looking at it now and will let you know.

  • Your code works. I'll compare it to mine.

    Thanks for the help

  • Edvin,

    The difference between my implementation and yours is that you have two timer compare events for each output pin. One for driving the pin high the other for driving it low.

    My understanding of the GPIOTE->CONFIG[n] toggle polarity setting was that the pin is toggled (driven high if low or low if high) with TASK_OUT[n]. That's why I have 

    NRF_PPI->CH[0].EEP = (uint32_t)&NRF_TIMER1->EVENTS_COMPARE[0];

    NRF_PPI->CH[0].TEP = (uint32_t)&NRF_GPIOTE->TASKS_OUT[0];

    However, it doesn't appear that the toggle mode works in this way. The TASKS_OUT[0] task triggered by the EVENTS_COMPARE[0] event doesn't toggle the pin. Can you explain?

    The following modification to my code based on yours works:

    NRF_TIMER1->MODE = 0;  //timer mode

    NRF_TIMER1->BITMODE = 0;  //16 bit timer width

    NRF_TIMER1->PRESCALER = 4;  //16MHz / 2^4 = 1MHz

    NRF_TIMER1->SHORTS = 2;  //timer cleared at CC[1]

    NRF_TIMER1->CC[0] = 1000;  //.001 secs

    NRF_TIMER1->CC[1] = 2000;  //.002 secs

    NRF_GPIOTE->CONFIG[0] = (3 | (PIN << 8) | (3 << 16) | (1 << 20));  //task mode, PIN selected, toggle, PIN high initially

    //Configure PPI channels 0 and 1 to drive PIN low on every Timer 1 COMPARE[0] match and high on every Timer 1 COMPARE[1] match

    NRF_PPI->CH[0].EEP = (uint32_t)&NRF_TIMER1->EVENTS_COMPARE[0];

    NRF_PPI->CH[0].TEP = (uint32_t)&NRF_GPIOTE->TASKS_CLR[0];

    NRF_PPI->CH[1].EEP = (uint32_t)&NRF_TIMER1->EVENTS_COMPARE[1];

    NRF_PPI->CH[1].TEP = (uint32_t)&NRF_GPIOTE->TASKS_SET[0];

    NRF_PPI->CHENSET = (1 << 1) | (1 << 0);  //enable PPI channel 0

    NRF_TIMER1->TASKS_START = 1;

  • Hello,

    Sorry for the late reply.

    I did some testing. Modified your code to use the macros instead of just numbers, so that they are a bit more easy to read. I am having difficulties reproducing what you are seeing.

    Can you test the attached main.c file? It is just modified from the project that I attached earlier.

    /* Copyright (c) 2009 Nordic Semiconductor. All Rights Reserved.
     *
     * The information contained herein is property of Nordic Semiconductor ASA.
     * Terms and conditions of usage are described in detail in NORDIC
     * SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT.
     *
     * Licensees are granted free, non-transferable use of the information. NO
     * WARRANTY of ANY KIND is provided. This heading must NOT be removed from
     * the file.
     *
     */
    
    #include "nrf.h"
    #include <stdbool.h>
    #include <stdint.h>
    #include "bsp.h"
    #include "nrf_gpio.h"
    #include "nrf_delay.h"
    #include "math.h"
    
    // Peripheral channel assignments
    //#define PWM0_GPIOTE_CH      0
    //#define PWM0_PPI_CH_A       0
    #define PWM0_TIMER_CC_NUM   0
    
    
    // TIMER3 reload value. The PWM frequency equals '16000000 / TIMER_RELOAD'
    #define TIMER_RELOAD        1000
    // The timer CC register used to reset the timer. Be aware that not all timers in the nRF52 have 6 CC registers.
    #define TIMER_RELOAD_CC_NUM 0
    
    #define PIN                 30
    
    
    // This function initializes timer 3 with the following configuration:
    // 24-bit, base frequency 16 MHz, auto clear on COMPARE5 match (CC5 = TIMER_RELOAD)
    //void timer_init()
    //{
    //    NRF_TIMER3->BITMODE                 = TIMER_BITMODE_BITMODE_24Bit << TIMER_BITMODE_BITMODE_Pos;
    //    NRF_TIMER3->PRESCALER               = 0;
    //    NRF_TIMER3->SHORTS                  = TIMER_SHORTS_COMPARE0_CLEAR_Msk << TIMER_RELOAD_CC_NUM;
    //    NRF_TIMER3->MODE                    = TIMER_MODE_MODE_Timer << TIMER_MODE_MODE_Pos;
    //    NRF_TIMER3->CC[TIMER_RELOAD_CC_NUM] = TIMER_RELOAD;    
    //}
    void timer_init()
    {
        NRF_TIMER1->BITMODE                 = TIMER_BITMODE_BITMODE_16Bit << TIMER_BITMODE_BITMODE_Pos;     // 0<<0; = 0 
        NRF_TIMER1->PRESCALER               = 4;                                                            // previously 0;
        NRF_TIMER1->SHORTS                  = TIMER_SHORTS_COMPARE0_CLEAR_Msk << TIMER_RELOAD_CC_NUM;       // 1<<0; = 1
        NRF_TIMER1->MODE                    = TIMER_MODE_MODE_Timer << TIMER_MODE_MODE_Pos;                 // 0<<0; = 0
        NRF_TIMER1->CC[TIMER_RELOAD_CC_NUM] = TIMER_RELOAD;                                                 // 1000;
    }
    
    
    //// Starts TIMER3
    //void timer_start()
    //{
    //    NRF_TIMER3->TASKS_START = 1;
    //}
    
    // Starts TIMER1
    void timer_start()
    {
        NRF_TIMER1->TASKS_START = 1;
    }
    
    
    // This function sets up TIMER3, the PPI and the GPIOTE modules to configure a single PWM channel
    // Timer CC num, PPI channel nums and GPIOTE channel num is defined at the top of this file
    
    void pwm0_init(uint32_t pinselect)
    {  
        NRF_GPIOTE->CONFIG[0] = GPIOTE_CONFIG_MODE_Task << GPIOTE_CONFIG_MODE_Pos |                             // 3<<0
                                             GPIOTE_CONFIG_POLARITY_Toggle << GPIOTE_CONFIG_POLARITY_Pos |      // 3<<16
                                             PIN << GPIOTE_CONFIG_PSEL_Pos |                                    // 30<<8
                                             GPIOTE_CONFIG_OUTINIT_High << GPIOTE_CONFIG_OUTINIT_Pos;           // 1<<20
    
        NRF_PPI->CH[0].EEP = (uint32_t)&NRF_TIMER1->EVENTS_COMPARE[TIMER_RELOAD_CC_NUM];
        NRF_PPI->CH[0].TEP = (uint32_t)&NRF_GPIOTE->TASKS_OUT[0];
        
        NRF_PPI->CHENSET               = (1 << 0);
    }
    
    //void pwm0_init(uint32_t pinselect)
    //{  
    //    NRF_GPIOTE->CONFIG[PWM0_GPIOTE_CH] = GPIOTE_CONFIG_MODE_Task << GPIOTE_CONFIG_MODE_Pos | 
    //                                         GPIOTE_CONFIG_POLARITY_Toggle << GPIOTE_CONFIG_POLARITY_Pos | 
    //                                         pinselect << GPIOTE_CONFIG_PSEL_Pos | 
    //                                         GPIOTE_CONFIG_OUTINIT_High << GPIOTE_CONFIG_OUTINIT_Pos;
    
    //    NRF_PPI->CH[PWM0_PPI_CH_A].EEP = (uint32_t)&NRF_TIMER1->EVENTS_COMPARE[TIMER_RELOAD_CC_NUM];
    //    NRF_PPI->CH[PWM0_PPI_CH_A].TEP = (uint32_t)&NRF_GPIOTE->TASKS_OUT[PWM0_GPIOTE_CH];
    //    
    //    NRF_PPI->CHENSET               = (1 << PWM0_PPI_CH_A);
    //}
    
    
    
    // Utility function for providing sin values, and converting them to integers.
    // input values in the range [0 - input_max] will be converted to 0-360 degrees (0-2*PI).
    // output values will be scaled to the range [output_min - output_max].
    
    
    
    int main(void)
    {
        // Initialize the timer
        timer_init();
        
        // Initialize PWM channel
        pwm0_init(LED_1);
            
        // Start the timer
        timer_start();
    
        while (true)
        {
    
        }
    }
    

    BR,

    Edvin

Related