Beware that this post is related to an SDK in maintenance mode
More Info: Consider nRF Connect SDK for new designs
This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts
This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

How to set duty cycle of two pwm channel in opposite polarity inside gpio interrupt handler?

I am quite new with nordic environment .

My application is to start 2 pwm instance in opposite polarity (i.e 1 high polarity and another is low polarity with same duty cycle) when got interrupt in gpio pin.

I am able to create the same by using PWM_Library example in SDK .

Here is my code 

#define    Period    20000 //20ms
#define    PIN_IN    22
#define    PWM_PIN  (11UL)

int duty_1= 20;

static volatile bool ready_flag;            
static int gpio_flag =0;

APP_PWM_INSTANCE(PWM1,1);                   
APP_PWM_INSTANCE(PWM2,2);

void pwm_ready_callback(uint32_t pwm_id)    // PWM callback function
{
    ready_flag = true;
}

void in_pin_handler(nrf_drv_gpiote_pin_t pin, nrf_gpiote_polarity_t action)
{
   gpio_flag =1;
}

static void gpio_init(void)
{
    ret_code_t err_code;

    err_code = nrf_drv_gpiote_init();
    APP_ERROR_CHECK(err_code);

    nrf_drv_gpiote_in_config_t in_config = GPIOTE_CONFIG_IN_SENSE_LOTOHI(true);
    in_config.pull = NRF_GPIO_PIN_PULLDOWN;

    err_code = nrf_drv_gpiote_in_init(PIN_IN, &in_config, in_pin_handler);
    APP_ERROR_CHECK(err_code);

    nrf_drv_gpiote_in_event_enable(PIN_IN, true);
}

static void pwm_pin_init(void)
{
 ret_code_t err_code;

    app_pwm_config_t pwm1_cfg = APP_PWM_DEFAULT_CONFIG_1CH(Period, BSP_LED_0);
    app_pwm_config_t pwm2_cfg = APP_PWM_DEFAULT_CONFIG_1CH(Period, BSP_LED_1);

    
    pwm1_cfg.pin_polarity [0]= APP_PWM_POLARITY_ACTIVE_HIGH;
    pwm2_cfg.pin_polarity [0]= APP_PWM_POLARITY_ACTIVE_LOW;


    
    err_code = app_pwm_init(&PWM1,&pwm1_cfg,pwm_ready_callback);

    APP_ERROR_CHECK(err_code);

    err_code = app_pwm_init(&PWM2,&pwm2_cfg,pwm_ready_callback);

    APP_ERROR_CHECK(err_code);
    app_pwm_enable(&PWM1);
    app_pwm_enable(&PWM2);

} 

int main(void)
{
 bsp_board_init(BSP_INIT_LEDS);

    gpio_init();

    pwm_pin_init();

    uint32_t value;

  while(true)
  {

    if(gpio_flag == 1)
    {
      value = duty_1;
      gpio_flag =0;

      ready_flag = false;
      /* Set the duty cycle - keep trying until PWM is ready... */
      
      
      while ((app_pwm_channel_duty_set(&PWM1, 0, value))&(app_pwm_channel_duty_set(&PWM2, 0, value)) == NRF_ERROR_BUSY);
 
      /* ... or wait for callback. */
      while (!ready_flag);
      APP_ERROR_CHECK(app_pwm_channel_duty_set(&PWM1, 0, value));
      APP_ERROR_CHECK(app_pwm_channel_duty_set(&PWM2, 0, value));
   
     // nrf_delay_ms(25);
    }

    else
    {
     app_pwm_channel_duty_set(&PWM1, 0, 0);
     app_pwm_channel_duty_set(&PWM2, 0, 0);
    }
  }
}

This code is working properly but I want to set duty cycle inside the interrupt function not in main function .

So how to do so because I tried but without any success.

So guide me .

Parents
  • Hi,

    I notice you are using app_pwm, which is not actually the PWM hardware peripheral instance, but instead app_pwm is using several ppi, gpiote and timer to create a PWM signal:
    https://infocenter.nordicsemi.com/topic/sdk_nrf5_v17.0.2/lib_pwm.html 

    You may get into trouble updating the PWM in such case since it will depend on the interrupt priority of those peripherals.

    I instead suggest that you use the PWM hardware peripheral, which have no such dependencies.

    You can find an example here that show how you can setup the PWM peripheral:
    https://devzone.nordicsemi.com/f/nordic-q-a/39314/pwm-update_function/152602#152602

    You can find an example below where pwm channel 1 is inverted of pwm channel 0:

    /cfs-file/__key/support-attachments/beef5d1b77644c448dabff31668f3a47-e7e4528908a64f45bf120ad5a23ef4b4/25464.main.c 

    The above main.c is a modification of \nRF5_SDK_17.0.2_d674dde\examples\peripheral\pwm_driver

  • Hello, Kenneth

    I tried using pwm hardware peripheral but It's not working can You please guide me where is the fault in my code??

    #define OUTPUT_PIN LED_1
    #define OUTPUT_PIN2 LED_2
    
    #define    PIN_IN    22
    #define    PWM_PIN   11
    
    
    unsigned int duty = 4000 ;
    static volatile bool  gpio_flag;
      
    
    static int16_t buf[] = {10000}; // Inverse polarity (bit 15), 10000us duty cycle
    static nrf_drv_pwm_t m_pwm0 = NRF_DRV_PWM_INSTANCE(0);
    
    
    
    // Declare variables holding PWM sequence values. In this example only one channel is used 
    nrf_pwm_values_individual_t seq_values[] = {0, 0, 0, 0};
    nrf_pwm_sequence_t const seq =
    {
        .values.p_individual = seq_values,
        .length          = NRF_PWM_VALUES_LENGTH(seq_values),
        .repeats         = 0,
        .end_delay       = 0
    };
    
    void in_pin_handler(nrf_drv_gpiote_pin_t pin, nrf_gpiote_polarity_t action)
    {
       gpio_flag = true;
    }
    
    static void gpio_init(void)
    {
        ret_code_t err_code;
    
        err_code = nrf_drv_gpiote_init();
        APP_ERROR_CHECK(err_code);
    
        nrf_drv_gpiote_in_config_t in_config = GPIOTE_CONFIG_IN_SENSE_LOTOHI(true);
        in_config.pull = NRF_GPIO_PIN_PULLDOWN;
    
        err_code = nrf_drv_gpiote_in_init(PIN_IN, &in_config, in_pin_handler);
        APP_ERROR_CHECK(err_code);
    
        nrf_drv_gpiote_in_event_enable(PIN_IN, true);
    }
    
    
    
    // Set duty cycle between 0 and 100%
    void pwm_update_duty_cycle(uint16_t duty_cycle)
    {
        
       
        {
            seq_values->channel_0 = duty_cycle ;
            seq_values->channel_1 = duty_cycle | 0x8000;
        }
        
        
        nrf_drv_pwm_simple_playback(&m_pwm0, &seq, 1, NRF_DRV_PWM_FLAG_LOOP);
    }
    
    static void pwm_init(void)
    {
        nrf_drv_pwm_config_t const config0 =
        {
            .output_pins =
            {
                OUTPUT_PIN, // channel 0
                OUTPUT_PIN2,             // channel 1
                NRF_DRV_PWM_PIN_NOT_USED,             // channel 2
                NRF_DRV_PWM_PIN_NOT_USED,             // channel 3
            },
            .irq_priority = APP_IRQ_PRIORITY_LOWEST,
            .base_clock   = NRF_PWM_CLK_1MHz,
            .count_mode   = NRF_PWM_MODE_UP,
            .top_value    = 10000,
            .load_mode    = NRF_PWM_LOAD_INDIVIDUAL,
            .step_mode    = NRF_PWM_STEP_AUTO
        };
        // Init PWM without error handler
        APP_ERROR_CHECK(nrf_drv_pwm_init(&m_pwm0, &config0, NULL));
        
    }
    
    static void zcd_init(void)
    {
    
     // Configure PWM_PIN as output, and set it to 0
      NRF_GPIO->DIRSET = (1 << PWM_PIN);
      NRF_GPIO->OUTCLR = (1 << PWM_PIN);
      
      
      NRF_PWM0->PRESCALER   = PWM_PRESCALER_PRESCALER_DIV_16; // 1 us
      NRF_PWM0->PSEL.OUT[0] = PWM_PIN;
      NRF_PWM0->MODE        = (PWM_MODE_UPDOWN_Up << PWM_MODE_UPDOWN_Pos);
      NRF_PWM0->DECODER     = (PWM_DECODER_LOAD_Individual       << PWM_DECODER_LOAD_Pos) | 
                              (PWM_DECODER_MODE_RefreshCount << PWM_DECODER_MODE_Pos);
      NRF_PWM0->LOOP        = (PWM_LOOP_CNT_Disabled << PWM_LOOP_CNT_Pos);
      
      NRF_PWM0->COUNTERTOP = 20000; // 20ms period
      
      
      NRF_PWM0->SEQ[0].CNT = ((sizeof(buf) / sizeof(uint16_t)) << PWM_SEQ_CNT_CNT_Pos);
      NRF_PWM0->SEQ[0].ENDDELAY = 0;
      NRF_PWM0->SEQ[0].PTR = (uint32_t)&buf[0];
      NRF_PWM0->SEQ[0].REFRESH = 0;
      NRF_PWM0->SHORTS = 0;
      
      NRF_PWM0->ENABLE = 1;
      NRF_PWM0->TASKS_SEQSTART[0] = 1;
    }
    
    int main(void)
    {
    
      // Start accurate HFCLK (XOSC)
      NRF_CLOCK->TASKS_HFCLKSTART = 1;
      while (NRF_CLOCK->EVENTS_HFCLKSTARTED == 0) ;
      NRF_CLOCK->EVENTS_HFCLKSTARTED = 0;
       
        bsp_board_init(BSP_INIT_LEDS);
        zcd_init();
        gpio_init();
        pwm_init();
        gpio_flag = false;
        
    
        while (1)
        {
            //if (gpio_flag == true)
            {
              gpio_flag = false;
              unsigned int val = duty;
              pwm_update_duty_cycle(val);
            }
        }
    }
    

    Mainly my zcd_init() function is not working . where I created an 50% duty cycle signal of 20 ms period and given to my gpio interrupt pin 22.

  • Please don't mix direct hardware access to the PWM peripheral and at the same time also use the driver to access the PWM peripheral. Then you very likely will end up in a bad state or configuration.

    Kenneth

  • Hello ,Kenneth 

    Please guide me to solve this problem .

    My simple aim to control two pwm channel in opposite duty cycle from interrupt and interrupt signal is also an square wave signal with 50% duty cycle and 20ms period.

  • Hello

    Again by doing (duty | 0x8000) channel 1 output is opposite to channel 0 but with different duty cycle .

    That is if duty cycle of  channel 0 is 40 then duty cycle become 60 .which is not required .

    My question was about opposite polarity with same duty cycle.

  • I thought you wanted 50% duty cycle, if you want different duty cycle then maybe you can consider using a second pwm instance for the second channel? If you start them from the CPU there will be a 16MHz period between the two, will that be a problem? 

    Kenneth

Reply
  • I thought you wanted 50% duty cycle, if you want different duty cycle then maybe you can consider using a second pwm instance for the second channel? If you start them from the CPU there will be a 16MHz period between the two, will that be a problem? 

    Kenneth

Children
Related