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

PPI does not work with PWM

URGENT

First of all, I have to mention that I dont understand PPI.

I have application which uses PWM to control a pump which is connected to the nRF52. To the nRF52 is a sensor connected, which gives me a frequency.

To measure this frequency I use this example:

devzone.nordicsemi.com/.../

Now when I add this code to my application, then the pwm wont work anymore. Why?

What does PPI have to do with PWM?

Here is my setup of the pwm:

APP_PWM_INSTANCE(PWM1,0);

static void PWMinit(void)
{
 app_pwm_config_t pwm1_cfg = APP_PWM_DEFAULT_CONFIG_1CH(9000,27);
 pwm1_cfg.pin_polarity[0] = APP_PWM_POLARITY_ACTIVE_HIGH;
 app_pwm_init(&PWM1, &pwm1_cfg, pwm_ready_callback);
 app_pwm_enable(&PWM1);
}

And here the frequency measuring part:

static void timer_init()
{
 NRF_TIMER1->TASKS_STOP = 1;
 NRF_TIMER1->MODE = TIMER_MODE_MODE_Timer;
 NRF_TIMER1->PRESCALER = 8;  // Fhck / 2^8 
 NRF_TIMER1->CC[0] = 62500;  // 62500 - 1s

 NRF_TIMER1->BITMODE = (TIMER_BITMODE_BITMODE_16Bit << TIMER_BITMODE_BITMODE_Pos);   

 NRF_TIMER1->TASKS_CLEAR = 1;
 NRF_TIMER1->INTENSET = (TIMER_INTENSET_COMPARE0_Enabled << TIMER_INTENSET_COMPARE0_Pos);

 NRF_TIMER1->EVENTS_COMPARE[0] = 0;
}

static void counter_init()
{
 NRF_TIMER2->TASKS_STOP = 1; 
 NRF_TIMER2->MODE = TIMER_MODE_MODE_Counter;
 NRF_TIMER2->BITMODE = (TIMER_BITMODE_BITMODE_24Bit << TIMER_BITMODE_BITMODE_Pos);
 NRF_TIMER2->TASKS_CLEAR = 1;
 NRF_TIMER2->EVENTS_COMPARE[0] = 0;
}

static void gpiote_init(uint32_t pin)
{
 NRF_GPIOTE->CONFIG[0]   =   0x01 << 0;                              // MODE: Event
 NRF_GPIOTE->CONFIG[0]   |=  pin << 8;                               // Pin number
 NRF_GPIOTE->CONFIG[0]   |=  GPIOTE_CONFIG_POLARITY_LoToHi  << 16;   // Event rising edge    
}

static void ppi_timer_stop_counter_init()
{
 NRF_PPI->CHEN |= 1 << 0;
 *(&(NRF_PPI->CH0_EEP)) = (uint32_t)&NRF_TIMER1->EVENTS_COMPARE[0];
 *(&(NRF_PPI->CH0_TEP)) = (uint32_t)&NRF_TIMER2->TASKS_STOP;
 NRF_PPI->CHENSET |= 1 << 0;
}

static void ppi_gpiote_counter_init()
{
 NRF_PPI->CHEN |= 1 << 1;
 *(&(NRF_PPI->CH1_EEP)) = (uint32_t)&NRF_GPIOTE->EVENTS_IN[0];
 *(&(NRF_PPI->CH1_TEP)) = (uint32_t)&NRF_TIMER2->TASKS_COUNT;
 NRF_PPI->CHENSET |= 1 << 1;
}

void TIMER1_IRQHandler(void) 
{
  if (NRF_TIMER1->EVENTS_COMPARE[0] != 0)
  {
    NRF_TIMER1->EVENTS_COMPARE[0] = 0;
    NRF_TIMER2->TASKS_CAPTURE[0] = 1;

   frequency = NRF_TIMER2->CC[0];

    NRF_TIMER1->TASKS_CLEAR = 1;
    NRF_TIMER2->TASKS_CLEAR = 1;    

    NRF_TIMER2->TASKS_START = 1;            
  }
}

Is it possible that they use the same PPI channel? How can I choose the channel of PWM PPI?

Any hints?

On what I have to pay attention when I use PWM and PPI?

UPDATE

I think the problem is not in the PPI. I changed all of them. And the same for the timers.

What else can I do?

The problem is between PWMinit() and gpiote_init functions.

But this is strange. Because in the gpiote_init function I only choose a mode,pin and polaritity.

  • First I just want to point out that since you are using nRF52, there is an entire PWM peripheral for you to use, so you don't have to use the software PWM library.

    The PWM library generates a PWM waveform using a combination of a Timer instance and the PPI.

    One thing I notice is that you are creating the APP PWM instance using Timer0. Are you using a SoftDevice with this? The SoftDevice reserves the use of Timer0, so creating the App PWM instance with Timer0 is sure to cause issue.

    Another thing is I see is that in your ppi_timer_stop_counter_init() and ppi_gpiote_counter_init(), you are assigning tasks and events to specific PPI channels. As mentioned before, the App PWM library use PPI. If you initialize PPI with your function after you initialize the App PWM library, you might have overwritten the PPI configurations the App PWM library uses to work. Just as you have guessed.

  • So to avoid that potential PPI channel conflicts, you should use the PPI driver to allocate PPI channels. Read more about it here infocenter.nordicsemi.com/index.jsp

    The SDKs also come with a PPI example for your reference.

  • The problem is probably that you are using GPIOTE channel 0 (NRF_GPIOTE->CONFIG[0]). The pwm library (app_pwm) will use the ppi and gpiote drivers which will dynamically allocate ppi channels and gpiote channels starting from the lowest available channel. If you use 1 channel pwm, then at least gpiote channel 0 will be occupied. The solution is to use the ppi and gpiote drivers instead of manipulating the registers directly, or to use the highest channels (gpiote channel 7 and ppi channel 18 if using SoftDevice) instead.

    As Võ said nRF52 has a PWM peripheral, so you can use this instead of the pwm library. The pwm driver (nrf_drv_pwm) uses the pwm peripheral. Here is a simple example of how to set a single duty cycle with the driver.

  • First, thank you very much for the support! No, I'm not using a soft device. I will now work with the pwm peripheral. In the example I cant see pwm_stop and start functions like it was in the software pwm library. If I want to stop the pwm, should I just set the dutycycle to 0 %?

    In the example, there are 3 PWM instances. But they are not at the same time activated. Its always just one at the same time.

    But I need 6 PWM at the same time. They can have the same frequency, but 6 different dutycycle on 6 different outputs. Is this possible?

    But I remember on the software pwm library, I can have 2 output for one instance.

  • Got it! 4 PWM's with one Instance. This is great! So I only need 2 Instanes for 6 PWM's.

Related