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

Inverting PWM in nRF52840 pinn

Hello,

I want to output in one pin 400kHz and inverse of 400kHz in the other pin. I have somehow managed to output the the 400kHz but haven't managed to output its inverse.

Here is my code:

#include <stdio.h>
#include <string.h>
#include "nrf_drv_pwm.h"
#include "app_util_platform.h"
#include "app_error.h"
#include "boards.h"
#include "bsp.h"
#include "app_timer.h"
#include "nrf_drv_clock.h"
#include "nrf_delay.h" 
#include "nrf_log.h"
#include "nrf_log_ctrl.h"
#include "nrf_log_default_backends.h"


#define m_top     20 
#define m_step    1 

#define  led_external  26 
#define  led_reversed  27

static nrfx_pwm_t m_pwm0 = NRFX_PWM_INSTANCE(0);


uint16_t step = m_top / m_step;

static nrf_pwm_values_common_t sequence_values[m_step * 2]; 	

uint16_t value = 0;



static void pwm_common_init(void)
{

    for(int i = 0; i<m_step; i++)
    {
		
      value += step; 
      sequence_values[i] = value;
      sequence_values[m_step + i] = m_top - value; 
    }

    nrfx_pwm_config_t const config0 = 
    {
      .output_pins = 
      {
          led_external,
          led_reversed| NRFX_PWM_PIN_INVERTED,
          NRFX_PWM_PIN_NOT_USED, 
          NRFX_PWM_PIN_NOT_USED 
      },
      .irq_priority = APP_IRQ_PRIORITY_LOWEST, 
      .base_clock   = NRF_PWM_CLK_16MHz, 
      .count_mode   = NRF_PWM_MODE_UP, 
      .top_value    = m_top, 
      .load_mode    = NRF_PWM_LOAD_COMMON, 
      .step_mode    = NRF_PWM_STEP_AUTO

    };
    APP_ERROR_CHECK(nrfx_pwm_init(&m_pwm0, &config0, NULL));
}
static void pwm_play(void)
{
  nrf_pwm_sequence_t const seq0 = 
  {
    .values  = sequence_values, 
    .length           = NRF_PWM_VALUES_LENGTH(sequence_values), 
    .repeats          = 0, 
    .end_delay        = 0 
  
  };
  (void)nrfx_pwm_simple_playback(&m_pwm0, &seq0, 1, NRFX_PWM_FLAG_LOOP);

  }



static void log_init(void)
{
  ret_code_t err_code = NRF_LOG_INIT(NULL);
  APP_ERROR_CHECK(err_code);

  NRF_LOG_DEFAULT_BACKENDS_INIT();

  }





int main(void)
{
log_init();
bsp_board_init(BSP_INIT_LEDS);
pwm_common_init();
pwm_play();

  NRF_LOG_INFO("PWM application started!!");
   
}

I was looking into the previous answers but I wasn't able to still apply their solution into my program. From what I know so far is that "NRFX_PWM_PIN_INVERTED" only works in idle state and that "seq_values->channel_0 = duty_cycle | 0x8000" is a possible answer but I am not sure how to integrate that in my program. 

Asking for help/advice how to integrate 400kHz output in pin 0.26 and inverse 400kHz frequency in pin 0.27 of nRF52840 board thru modifying my code?

Parents
  • Hi, 

    What you have experienced is true.Please see the solution from a similar case: Invert PWM PIN, PWM, PWM intialize .

  • Please see the solution from a similar case: Invert PWM PIN, PWM, PWM intialize .

    Yeah, that's where I got the "seq_values->channel_0 = duty_cycle | 0x8000" idea but I dont know how to add that to my program

  • Sorry, I wasn't being very clear. To get differential (or inverse or antiphase or full-bridge, all same thing) output don't use NRF_PWM_LOAD_COMMON but instead GROUPED or WAVEFORM or SINGLE as these each use a separate value for each pin. This requires a bigger buffer of course; the inverted value has the "| 0x8000" whereas the non-inverted output does not. Have a look at section 47.2 Decoder with Easy DMA

  • GROUPED or WAVEFORM or SINGLE

    Which one of them should I use? and how could I integrate them to my code? what changes will I make?

  • Try this, not tested:

    With "step" as the number of discrete PWM changes, Change
    static nrf_pwm_values_common_t sequence_values[m_step * 2]; 	
    to
    static nrf_pwm_values_common_t sequence_values[step * 2]; 	
    
    static void pwm_common_init(void)
    {
    Change
        for(int i = 0; i<m_step; i++)
    to
        for(int i = 0; i<step*2; i+=2)
        {
    Change
          value += step; 
          sequence_values[i] = value;
          sequence_values[i+1] = value;
    to
          value += m_step; 
          sequence_values[i]   = value;
          sequence_values[i+1] = value | 0x8000; 
        }
    
        nrfx_pwm_config_t const config0 = 
        {
          .output_pins = 
          {
    Change
              led_external,
              led_reversed| NRFX_PWM_PIN_INVERTED,
              NRFX_PWM_PIN_NOT_USED, 
              NRFX_PWM_PIN_NOT_USED 
    To
              led_external,
              NRFX_PWM_PIN_NOT_USED, 
              led_reversed| NRFX_PWM_PIN_INVERTED,
              NRFX_PWM_PIN_NOT_USED 
          },
          .irq_priority = APP_IRQ_PRIORITY_LOWEST, 
          .base_clock   = NRF_PWM_CLK_16MHz, 
          .count_mode   = NRF_PWM_MODE_UP, 
          .top_value    = m_top, 
    Change
          .load_mode    = NRF_PWM_LOAD_COMMON, 
    To
          .load_mode    = NRF_PWM_LOAD_GROUPED, 
          .step_mode    = NRF_PWM_STEP_AUTO
    
        };
        APP_ERROR_CHECK(nrfx_pwm_init(&m_pwm0, &config0, NULL));
    }

    Not totally sure what waveform you want, but these changes allow a PWM output and inverted PWM output ramping from low duty cycle to max duty cycle. The pins order changes because of the mapping for the different modes; GROUPED uses 2 entries per step controlling 2 pins each.

    In the code you posted it looks like you used m_step erroneously instead of step

  • Thank you so far it's working..a bit better, I can see the inverted PWM. Is there a possible way to get constant 50% duty cycle? The program is somehow making the pwm "glitchy"

  • Hi,

    I made one example that shows how to get 50% with GROUPED mode. Suggest you refer to nrf52840 ps section PWM and nRF52 nRF5 SDK example pwm_driver to learn how to use PWM peripheral on nRF52 devices. 

    #include <stdio.h>
    #include <string.h>
    #include "nrf_drv_pwm.h"
    #include "app_util_platform.h"
    #include "app_error.h"
    #include "boards.h"
    #include "bsp.h"
    #include "app_timer.h"
    #include "nrf_drv_clock.h"
    #include "nrf_delay.h" 
    #include "nrf_log.h"
    #include "nrf_log_ctrl.h"
    #include "nrf_log_default_backends.h"
    
    
    #define m_top     20 
    #define m_step    1 
    
    #define  led_external  26 
    #define  led_reversed  27|NRFX_PWM_PIN_INVERTED
    
    static nrfx_pwm_t m_pwm0 = NRFX_PWM_INSTANCE(0);
    
    
    uint16_t step = m_top / m_step;
    
    static nrf_pwm_values_grouped_t sequence_values[]=    {
            { 0x8000, 0 },
            { 0, 0x8000 }
        };	
    
    
    uint16_t value = 0;
    
    
    static void pwm_common_init(void)
    {
        nrfx_pwm_config_t const config0 = 
        {
          .output_pins = 
          {
              led_external,
              NRFX_PWM_PIN_NOT_USED, 
              led_reversed| NRFX_PWM_PIN_INVERTED,
              NRFX_PWM_PIN_NOT_USED 
          },
          .irq_priority = APP_IRQ_PRIORITY_LOWEST, 
          .base_clock   = NRF_PWM_CLK_16MHz, 
          .count_mode   = NRF_PWM_MODE_UP, 
          .top_value    = m_top, 
          .load_mode    = NRF_PWM_LOAD_GROUPED, 
          .step_mode    = NRF_PWM_STEP_AUTO
    
        };
        APP_ERROR_CHECK(nrfx_pwm_init(&m_pwm0, &config0, NULL));
    }
    static void pwm_play(void)
    {
      nrf_pwm_sequence_t const seq0 = 
      {
        .values.p_grouped  = sequence_values, 
        .length           = NRF_PWM_VALUES_LENGTH(sequence_values), 
        .repeats          = 0, 
        .end_delay        = 0 
      
      };
      (void)nrfx_pwm_simple_playback(&m_pwm0, &seq0, 1, NRFX_PWM_FLAG_LOOP);
    
      }
    
    
    
    static void log_init(void)
    {
      ret_code_t err_code = NRF_LOG_INIT(NULL);
      APP_ERROR_CHECK(err_code);
    
      NRF_LOG_DEFAULT_BACKENDS_INIT();
    
      }
    
    
    
    
    
    int main(void)
    {
    log_init();
    bsp_board_init(BSP_INIT_LEDS);
    pwm_common_init();
    pwm_play();
    
      NRF_LOG_INFO("PWM application started!!");
       
    }

Reply
  • Hi,

    I made one example that shows how to get 50% with GROUPED mode. Suggest you refer to nrf52840 ps section PWM and nRF52 nRF5 SDK example pwm_driver to learn how to use PWM peripheral on nRF52 devices. 

    #include <stdio.h>
    #include <string.h>
    #include "nrf_drv_pwm.h"
    #include "app_util_platform.h"
    #include "app_error.h"
    #include "boards.h"
    #include "bsp.h"
    #include "app_timer.h"
    #include "nrf_drv_clock.h"
    #include "nrf_delay.h" 
    #include "nrf_log.h"
    #include "nrf_log_ctrl.h"
    #include "nrf_log_default_backends.h"
    
    
    #define m_top     20 
    #define m_step    1 
    
    #define  led_external  26 
    #define  led_reversed  27|NRFX_PWM_PIN_INVERTED
    
    static nrfx_pwm_t m_pwm0 = NRFX_PWM_INSTANCE(0);
    
    
    uint16_t step = m_top / m_step;
    
    static nrf_pwm_values_grouped_t sequence_values[]=    {
            { 0x8000, 0 },
            { 0, 0x8000 }
        };	
    
    
    uint16_t value = 0;
    
    
    static void pwm_common_init(void)
    {
        nrfx_pwm_config_t const config0 = 
        {
          .output_pins = 
          {
              led_external,
              NRFX_PWM_PIN_NOT_USED, 
              led_reversed| NRFX_PWM_PIN_INVERTED,
              NRFX_PWM_PIN_NOT_USED 
          },
          .irq_priority = APP_IRQ_PRIORITY_LOWEST, 
          .base_clock   = NRF_PWM_CLK_16MHz, 
          .count_mode   = NRF_PWM_MODE_UP, 
          .top_value    = m_top, 
          .load_mode    = NRF_PWM_LOAD_GROUPED, 
          .step_mode    = NRF_PWM_STEP_AUTO
    
        };
        APP_ERROR_CHECK(nrfx_pwm_init(&m_pwm0, &config0, NULL));
    }
    static void pwm_play(void)
    {
      nrf_pwm_sequence_t const seq0 = 
      {
        .values.p_grouped  = sequence_values, 
        .length           = NRF_PWM_VALUES_LENGTH(sequence_values), 
        .repeats          = 0, 
        .end_delay        = 0 
      
      };
      (void)nrfx_pwm_simple_playback(&m_pwm0, &seq0, 1, NRFX_PWM_FLAG_LOOP);
    
      }
    
    
    
    static void log_init(void)
    {
      ret_code_t err_code = NRF_LOG_INIT(NULL);
      APP_ERROR_CHECK(err_code);
    
      NRF_LOG_DEFAULT_BACKENDS_INIT();
    
      }
    
    
    
    
    
    int main(void)
    {
    log_init();
    bsp_board_init(BSP_INIT_LEDS);
    pwm_common_init();
    pwm_play();
    
      NRF_LOG_INFO("PWM application started!!");
       
    }

Children
  • 50% with GROUPED mode

    If I needed 80% duty cycle, is that still possible in the program? or do I have to change something like the GROUPED mode into another example Individual?

  • The decoder load mode decides how a sequence is read from ram and spread to the compare register, so it is not related to duty cycle. The duty cycle is decided by compare value in sequence and top value. 

    You want to run 400KHZ 80% duty PWM on Pin26, and reverse output on Pin27, one solution is:

    NRF_PWM_CLK_4MHz //4M/10=400KHZ
    sequence_values[]={2, 2|0x8000,0,10}; // (10-2)/10=80%
    NRF_PWM_LOAD_WAVE_FORM//(See nrf52840 ps section 6.17.1)
    NRF_PWM_MODE_UP //(See nrf52840 ps section 6.17.1)

    Here are the codes with the above settings:

    #include <stdio.h>
    #include <string.h>
    #include "nrf_drv_pwm.h"
    #include "app_util_platform.h"
    #include "app_error.h"
    #include "boards.h"
    #include "bsp.h"
    #include "app_timer.h"
    #include "nrf_drv_clock.h"
    #include "nrf_delay.h" 
    #include "nrf_log.h"
    #include "nrf_log_ctrl.h"
    #include "nrf_log_default_backends.h"
    
    #define  led_external  26 
    #define  led_reversed  27
    
    static nrfx_pwm_t m_pwm0 = NRFX_PWM_INSTANCE(0);
    
    
    
    static nrf_pwm_values_wave_form_t sequence_values[]={2, 2|0x8000,0,10};	
    
    
    uint16_t value = 0;
    
    
    static void pwm_waveform_init(void)
    {
        nrfx_pwm_config_t const config0 = 
        {
          .output_pins = 
          {
              led_external,
              led_reversed,
              NRFX_PWM_PIN_NOT_USED,   
              NRFX_PWM_PIN_NOT_USED 
          },
          .irq_priority = APP_IRQ_PRIORITY_LOWEST, 
          .base_clock   = NRF_PWM_CLK_4MHz, 
          .count_mode   = NRF_PWM_MODE_UP,  
          .load_mode    = NRF_PWM_LOAD_WAVE_FORM, 
          .step_mode    = NRF_PWM_STEP_AUTO
    
        };
        APP_ERROR_CHECK(nrfx_pwm_init(&m_pwm0, &config0, NULL));
    }
    static void pwm_play(void)
    {
      nrf_pwm_sequence_t const seq0 = 
      {
        .values.p_wave_form  = sequence_values, 
        .length           = NRF_PWM_VALUES_LENGTH(sequence_values), 
        .repeats          = 1, 
        .end_delay        = 0 
      
      };
      (void)nrfx_pwm_simple_playback(&m_pwm0, &seq0, 1, NRFX_PWM_FLAG_LOOP);
    
      }
    
    
    
    static void log_init(void)
    {
      ret_code_t err_code = NRF_LOG_INIT(NULL);
      APP_ERROR_CHECK(err_code);
    
      NRF_LOG_DEFAULT_BACKENDS_INIT();
    
      }
    
    
    
    
    
    int main(void)
    {
    log_init();
    bsp_board_init(BSP_INIT_LEDS);
    pwm_waveform_init();
    pwm_play();
    NRF_LOG_INFO("PWM application started!!");
       
    }

Related