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

Issue with PPI on PWM (nrf52)

Hello,

I'm trying to use PPI over PWM to, at the end of one single PWM period, disable the PWM and switch one GPIO (later, I'll use it for more complex task, but I start with this).

I wrote this (partial) code below (base on SDK & some example I found here) but it does not work. The PWM continues, and the GPIO stays clear (here led is active with the GPIO at 0). Any idea of what I'm doing wrong ? Did I missed something ?

Thanks in advance,

Julien


static nrf_ppi_channel_t m_ppi_channel1;

static void ppi_init(void)
{
    uint32_t err_code = NRF_SUCCESS;
    volatile int toto;

    err_code = nrf_drv_ppi_init();
    APP_ERROR_CHECK(err_code);

    /* Configure 1st available PPI channel to stop PWM when the first PWM period is finished,
     */
    err_code = nrf_drv_ppi_channel_alloc(&m_ppi_channel1);
    APP_ERROR_CHECK(err_code);
    err_code = nrf_drv_ppi_channel_assign(m_ppi_channel1,
                                          nrf_drv_pwm_event_address_get(&m_pwm0,
                                                                         NRF_PWM_EVENT_PWMPERIODEND),
                                         nrf_drv_pwm_task_address_get(&m_pwm0,
                                                                      NRF_PWM_TASK_STOP));
    APP_ERROR_CHECK(err_code);

    nrfx_gpiote_out_config_t config = GPIOTE_CONFIG_OUT_TASK_TOGGLE(true);
    err_code = nrf_drv_gpiote_out_init(REAR_LED, &config);
    APP_ERROR_CHECK(err_code);

    uint32_t gpiote_task_addr = nrf_drv_gpiote_set_task_addr_get(REAR_LED);

    // 2nd task activated from the event NRF_PWM_EVENT_PWMPERIODEND, the GPIO READ_LED is set.
    err_code = nrf_drv_ppi_channel_fork_assign(m_ppi_channel1, gpiote_task_addr);

    // Enable configured PPI channel
    err_code = nrf_drv_ppi_channel_enable(m_ppi_channel1);
    APP_ERROR_CHECK(err_code);

    nrf_drv_gpiote_out_task_enable(REAR_LED);
}


void LED_init(void)
{
  cc_drv_gpio_output(REAR_LED);
  // For test, activate READ lead
  cc_drv_gpio_clear(REAR_LED);

  {
    nrf_drv_pwm_config_t const config0 =
    {
      .output_pins =
      {
        RED,          // channel 0
        GREEN,          // channel 1
        BLUE,         // channel 2
        NRF_DRV_PWM_PIN_NOT_USED,   // channel 3
      },
      .irq_priority = APP_IRQ_PRIORITY_LOW,
      .base_clock   = NRF_PWM_CLK_1MHz,
      .count_mode   = NRF_PWM_MODE_UP,
      .top_value    = PWM_PERIOD,
      .load_mode    = NRF_PWM_LOAD_INDIVIDUAL,
      .step_mode    = NRF_PWM_STEP_AUTO
    };

    APP_ERROR_CHECK(nrf_drv_pwm_init(&m_pwm0, &config0, 0)); // No call back

  ppi_init();

  {
    nrf_pwm_sequence_t const seq0 = { .values.p_individual = &seq0_values,
                                      .length =
                                        NRF_PWM_VALUES_LENGTH(seq0_values),
                                      .repeats = 0,
                                      .end_delay = 0 };

    APP_ERROR_CHECK(nrf_drv_pwm_simple_playback(
      &m_pwm0, &seq0, 1,
      NRF_DRV_PWM_FLAG_LOOP | NRF_DRV_PWM_FLAG_SIGNAL_END_SEQ0));
  }



  cc_set_pwm_3ch_duty_cycle_(50, 50, 50, LOW);

}

typedef enum {
  PWM_POLARITY_HIGH = 0x8000,
  PWM_POLARITY_LOW = 0,
} PWM_POLARITY;

static CC_UINT16 choose_polarity(bool polarity)
{
  if (polarity)
    return PWM_POLARITY_HIGH;
  return PWM_POLARITY_LOW;
}


static CC_UINT16 get_pwm_percentage(CC_UCHAR value)
{
  return ((value * PWM_PERIOD) / 100);
}

void cc_set_pwm_3ch_duty_cycle_(CC_UCHAR pin0_dc, CC_UCHAR pin1_dc,
                               CC_UCHAR pin2_dc, bool polarity)
{
  seq0_values.channel_0 =
    get_pwm_percentage(pin0_dc) | choose_polarity(polarity);
  seq0_values.channel_1 =
    get_pwm_percentage(pin1_dc) | choose_polarity(polarity);
  seq0_values.channel_2 =
    get_pwm_percentage(pin2_dc) | choose_polarity(polarity);
  seq0_values.channel_3 = 0;
}

Parents
  • Hi Julien

    Do you happen to have a diagram showing what you are trying to achieve with the different pins?

    Setting and clearing a pin is the same as setting duty cycle to 100 or 0% through the PWM module, so couldn't you just assign REAR_LED to the 4th PWM pin and set the buffer value to 0 or PWM_PERIOD (100%)?

    Best regards
    Torbjørn

  • Thank you Torbjørn for your answer.

    In fact this is just the beginning of what I need (and I'm very sad I can not make it work).

    Anyway your suggestion may be useful, but I think I'll still need PPI even if I don't use it for the clearing of the pin.

    Let me explain my full purpose here :

    I want to control 3 RGB leds using 3 GPIO to select the RGB led, and 3 GPIO in PWM mode for the 3 colors RGB. I need time sharing (1/3 of the time for each leds) to set whatever color on each leds.

    I first tried an obvious interrupt driven solution, where at each PWM period I switched leds GPIO & PWM values. But this does not  work due to the softdevice BLE scan as it uses higher priority interrupt and the time sharing is lost (there are many glitches on leds)

    So I'm now focusing on using only PPI to do the time sharing, my main idea is to use 3 PWM modules to control alternatively each leds during one period only, and after each period turn off itslef and activate the next PWM module - and also set/clear appropriate led GPIO. I still don't know if it's possible to do that, and I was first trying to turn off one PWM after a PWM period, and also clear a GPIO, using PPI.

    I'm not sure I'm clear enough ?

  • Dear Torbjørn ,

    I just found an other solution without PPI : 

    I keep the PWM on [RED, GREEN, BLUE] pins, with a sequence or 3 values on each channels corresponding of the RGB of the 3 leds.

    And I apply an other PWM on pins [LEFT_LED, RIGHT_LED, REAR_LED] with also 3 values on each channels so that only one led is active at each pwm period.

    I still don't know how to start precisely the 2 PWMs at the same time...

    And It's not perfect, I'll need to check why (I still have visible glitches)

    Regards,

    Julien

Reply
  • Dear Torbjørn ,

    I just found an other solution without PPI : 

    I keep the PWM on [RED, GREEN, BLUE] pins, with a sequence or 3 values on each channels corresponding of the RGB of the 3 leds.

    And I apply an other PWM on pins [LEFT_LED, RIGHT_LED, REAR_LED] with also 3 values on each channels so that only one led is active at each pwm period.

    I still don't know how to start precisely the 2 PWMs at the same time...

    And It's not perfect, I'll need to check why (I still have visible glitches)

    Regards,

    Julien

Children
  • Hi Julien

    What if you put RED, LEFT_RED, GREEN and RIGHT_GREEN on one PWM controller, and BLUE and REAR_BLUE on the second?

    Would that fix the issue of starting the PWM's at the same time?

    It is possible to do some PPI trickery in order to start the two PWM controllers at exactly the same time, but since you have to bypass the driver to do it it is not the cleanest solution. 

    If you experience glitches, would you be able to measure the lines with a scope or logic analyzer in order to see what they look like?

    Best regards
    Torbjørn

Related