PWM single pulse generation

Hi there,

I'm trying to create a single pulse from PWM instance. Using SDK5 SDK, I could create only even number of pulses with a nrf_pwm_simple_playback().  When  playback_count = 1 or 2, I get two pulses. playback_count = 3, 4 I get four pulses. 5,6 I get 6 pulses so. 

from nrfx_pwm.c:

288 nrf_pwm_sequence_set(p_instance->p_registers, 0, p_sequence);
289 nrf_pwm_sequence_set(p_instance->p_registers, 1, p_sequence);
290 bool odd = (playback_count & 1);
291 nrf_pwm_loop_set(p_instance->p_registers,
292 (playback_count / 2) + (odd ? 1 : 0));

Is it okay just to create one pulse?

Then I tried to do directly via HAL, but couldn't get PWM to stop after SEQEND0 finishes:

#define first_pin PWM_PIN_A
#define second_pin PWM_PIN_B
#define PWM_CH0_DUTY 5
#define PWM_CH1_DUTY 5
#define PWM_CH2_DUTY 5
#define PWM_CH3_DUTY 5
uint16_t pwm_seq[4] = {PWM_CH0_DUTY, PWM_CH1_DUTY, PWM_CH2_DUTY, PWM_CH3_DUTY};

void demo8(void)
{
// 5us active cycle
NRF_PWM0->PSEL.OUT[0] = (first_pin << PWM_PSEL_OUT_PIN_Pos) | (PWM_PSEL_OUT_CONNECT_Connected << PWM_PSEL_OUT_CONNECT_Pos);
NRF_PWM0->PSEL.OUT[1] = (second_pin << PWM_PSEL_OUT_PIN_Pos) | (PWM_PSEL_OUT_CONNECT_Connected << PWM_PSEL_OUT_CONNECT_Pos);

NRF_PWM0->PSEL.OUT[2] = 0xFF;
NRF_PWM0->PSEL.OUT[3] = 0xFF;

NRF_PWM0->ENABLE = (PWM_ENABLE_ENABLE_Enabled << PWM_ENABLE_ENABLE_Pos);

//count up
NRF_PWM0->MODE = (PWM_MODE_UPDOWN_Up << PWM_MODE_UPDOWN_Pos);


//1MHZ
NRF_PWM0->PRESCALER = (PWM_PRESCALER_PRESCALER_DIV_16 << PWM_PRESCALER_PRESCALER_Pos);

//stop after 15us
NRF_PWM0->COUNTERTOP = (15 << PWM_COUNTERTOP_COUNTERTOP_Pos);

// no loop
NRF_PWM0->LOOP = (PWM_LOOP_CNT_Disabled << PWM_LOOP_CNT_Pos);
NRF_PWM0->DECODER = (PWM_DECODER_LOAD_Individual << PWM_DECODER_LOAD_Pos) | (PWM_DECODER_MODE_RefreshCount << PWM_DECODER_MODE_Pos);

// RAM address of this sequence
NRF_PWM0->SEQ[0].PTR = ((uint32_t)(pwm_seq) << PWM_SEQ_PTR_PTR_Pos);
// number of sequences. Each sequence is a 16-bit.
NRF_PWM0->SEQ[0].CNT = ((sizeof(pwm_seq) / sizeof(uint16_t)) << PWM_SEQ_CNT_CNT_Pos);


NRF_PWM0->SEQ[0].REFRESH = 0;

NRF_PWM0->SEQ[0].ENDDELAY = 0;
/* NRF_PWM0->SHORTS = PWM_SHORTS_SEQEND0_STOP_Enabled << PWM_SHORTS_SEQEND0_STOP_Pos ; */
/* delay(100); */
NRF_PWM0->TASKS_SEQSTART[0] = 1;
}

Is there a way that I could use PWM to create just one single pulse for each time I call TASKS_SEQSTART[0]? Thanks.

Parents
  • Hi there,

    Can you show me the code when you use nrf_pwm_simple_playback(), did you also add the flag  NRFX_PWM_FLAG_STOP ?

    regards

    Jared 

  • void demo5(void) {

    nrf_drv_pwm_config_t const config0 =
    {
    .output_pins =
    {
    PWM_PIN_A | NRF_DRV_PWM_PIN_INVERTED, // channel 0
    0xFF,
    PWM_PIN_C, // channel 0
    0xFF,
    },
    .irq_priority = APP_IRQ_PRIORITY_LOWEST,
    .base_clock = NRF_PWM_CLK_1MHz,
    .count_mode = NRF_PWM_MODE_UP,
    .top_value = TOP_VALUE,
    .load_mode = NRF_PWM_LOAD_GROUPED,
    .step_mode = NRF_PWM_STEP_AUTO
    };
    APP_ERROR_CHECK(nrf_drv_pwm_init(&m_pwm0, &config0, NULL)); // changes the pins to the levels set in output_pins

    nrf_drv_pwm_config_t const config1 =

    {
    .output_pins =
    {
    PWM_PIN_B | NRF_DRV_PWM_PIN_INVERTED, // channel 0
    0xFF,
    PWM_PIN_D | NRF_DRV_PWM_PIN_INVERTED, // channel 0
    0xFF,
    },
    .irq_priority = APP_IRQ_PRIORITY_LOWEST,
    .base_clock = NRF_PWM_CLK_1MHz,
    .count_mode = NRF_PWM_MODE_UP,
    .top_value = TOP_VALUE,
    .load_mode = NRF_PWM_LOAD_GROUPED,
    .step_mode = NRF_PWM_STEP_AUTO
    };

    /* APP_ERROR_CHECK(nrf_drv_pwm_init(&m_pwm1, &config1, NULL)); // changes the pins */

    nrf_drv_timer_config_t const config2 =
    {
    .frequency = NRF_TIMER_FREQ_1MHz,\
    .mode = NRF_TIMER_MODE_TIMER,\
    .bit_width = TIMER_BITMODE_BITMODE_16Bit,\
    .interrupt_priority = NRFX_TIMER_DEFAULT_CONFIG_IRQ_PRIORITY, \
    .p_context = NULL \
    };

    APP_ERROR_CHECK(nrf_drv_timer_init(&m_tim1, &config2, NULL));

    APP_ERROR_CHECK(nrf_drv_ppi_init());
    APP_ERROR_CHECK(nrf_drv_ppi_channel_alloc(&m_ppi_channel1));
    APP_ERROR_CHECK(nrf_drv_ppi_channel_alloc(&m_ppi_channel2));

    nrf_drv_ppi_channel_assign(m_ppi_channel1,
    nrf_drv_timer_event_address_get(&m_tim1, NRF_TIMER_EVENT_COMPARE0),
    nrf_drv_pwm_task_address_get(&m_pwm0, NRF_PWM_TASK_SEQSTART0));

    nrf_drv_ppi_channel_assign(m_ppi_channel1,
    nrf_drv_timer_event_address_get(&m_tim1, NRF_TIMER_EVENT_COMPARE1),
    nrf_drv_pwm_task_address_get(&m_pwm1, NRF_PWM_TASK_SEQSTART0));


    // Enable both configured PPI channels
    APP_ERROR_CHECK(nrf_drv_ppi_channel_enable(m_ppi_channel1));
    APP_ERROR_CHECK(nrf_drv_ppi_channel_enable(m_ppi_channel2));

    m_demo4a_seq_values.group_0 = 5; //group 0 inverted
    m_demo4a_seq_values.group_1 = 5|0x8000;
    /* m_demo4a_seq_values.group_1 = 5; */



    m_demo4b_seq_values.group_0 = 4|0x8000; //group 0 inverted
    m_demo4b_seq_values.group_1 = 4;

    /* (void)nrf_drv_pwm_simple_playback(&m_pwm0, &m_demo4a_seq, 1, */
    /* NRF_DRV_PWM_FLAG_LOOP); */

    /* (void)nrf_drv_pwm_simple_playback(&m_pwm1, &m_demo4b_seq, 1, */
    /* NRF_DRV_PWM_FLAG_LOOP); */

    (void)nrf_drv_pwm_simple_playback(&m_pwm0, &m_demo4a_seq, 1,
    NRF_DRV_PWM_FLAG_START_VIA_TASK | NRFX_PWM_FLAG_STOP);

    /* (void)nrf_drv_pwm_simple_playback(&m_pwm1, &m_demo4b_seq, 1, */
    /* NRF_DRV_PWM_FLAG_START_VIA_TASK | NRFX_PWM_FLAG_STOP); */

    // note this must be true: CH1_VAL > CH0_VAL since we stop at the last CC compare count
    nrf_drv_timer_compare
    (&m_tim1, NRF_TIMER_CC_CHANNEL0, timer_ch0_val, false);
    nrf_drv_timer_extended_compare
    (&m_tim1, NRF_TIMER_CC_CHANNEL1, timer_ch1_val, NRF_TIMER_SHORT_COMPARE1_STOP_MASK, false);

    //this will run one time and launch a pulse?
    /* nrf_drv_timer_enable(&m_tim1); */
    NRF_PWM0->TASKS_SEQSTART[0] = 1;
    }

Reply
  • void demo5(void) {

    nrf_drv_pwm_config_t const config0 =
    {
    .output_pins =
    {
    PWM_PIN_A | NRF_DRV_PWM_PIN_INVERTED, // channel 0
    0xFF,
    PWM_PIN_C, // channel 0
    0xFF,
    },
    .irq_priority = APP_IRQ_PRIORITY_LOWEST,
    .base_clock = NRF_PWM_CLK_1MHz,
    .count_mode = NRF_PWM_MODE_UP,
    .top_value = TOP_VALUE,
    .load_mode = NRF_PWM_LOAD_GROUPED,
    .step_mode = NRF_PWM_STEP_AUTO
    };
    APP_ERROR_CHECK(nrf_drv_pwm_init(&m_pwm0, &config0, NULL)); // changes the pins to the levels set in output_pins

    nrf_drv_pwm_config_t const config1 =

    {
    .output_pins =
    {
    PWM_PIN_B | NRF_DRV_PWM_PIN_INVERTED, // channel 0
    0xFF,
    PWM_PIN_D | NRF_DRV_PWM_PIN_INVERTED, // channel 0
    0xFF,
    },
    .irq_priority = APP_IRQ_PRIORITY_LOWEST,
    .base_clock = NRF_PWM_CLK_1MHz,
    .count_mode = NRF_PWM_MODE_UP,
    .top_value = TOP_VALUE,
    .load_mode = NRF_PWM_LOAD_GROUPED,
    .step_mode = NRF_PWM_STEP_AUTO
    };

    /* APP_ERROR_CHECK(nrf_drv_pwm_init(&m_pwm1, &config1, NULL)); // changes the pins */

    nrf_drv_timer_config_t const config2 =
    {
    .frequency = NRF_TIMER_FREQ_1MHz,\
    .mode = NRF_TIMER_MODE_TIMER,\
    .bit_width = TIMER_BITMODE_BITMODE_16Bit,\
    .interrupt_priority = NRFX_TIMER_DEFAULT_CONFIG_IRQ_PRIORITY, \
    .p_context = NULL \
    };

    APP_ERROR_CHECK(nrf_drv_timer_init(&m_tim1, &config2, NULL));

    APP_ERROR_CHECK(nrf_drv_ppi_init());
    APP_ERROR_CHECK(nrf_drv_ppi_channel_alloc(&m_ppi_channel1));
    APP_ERROR_CHECK(nrf_drv_ppi_channel_alloc(&m_ppi_channel2));

    nrf_drv_ppi_channel_assign(m_ppi_channel1,
    nrf_drv_timer_event_address_get(&m_tim1, NRF_TIMER_EVENT_COMPARE0),
    nrf_drv_pwm_task_address_get(&m_pwm0, NRF_PWM_TASK_SEQSTART0));

    nrf_drv_ppi_channel_assign(m_ppi_channel1,
    nrf_drv_timer_event_address_get(&m_tim1, NRF_TIMER_EVENT_COMPARE1),
    nrf_drv_pwm_task_address_get(&m_pwm1, NRF_PWM_TASK_SEQSTART0));


    // Enable both configured PPI channels
    APP_ERROR_CHECK(nrf_drv_ppi_channel_enable(m_ppi_channel1));
    APP_ERROR_CHECK(nrf_drv_ppi_channel_enable(m_ppi_channel2));

    m_demo4a_seq_values.group_0 = 5; //group 0 inverted
    m_demo4a_seq_values.group_1 = 5|0x8000;
    /* m_demo4a_seq_values.group_1 = 5; */



    m_demo4b_seq_values.group_0 = 4|0x8000; //group 0 inverted
    m_demo4b_seq_values.group_1 = 4;

    /* (void)nrf_drv_pwm_simple_playback(&m_pwm0, &m_demo4a_seq, 1, */
    /* NRF_DRV_PWM_FLAG_LOOP); */

    /* (void)nrf_drv_pwm_simple_playback(&m_pwm1, &m_demo4b_seq, 1, */
    /* NRF_DRV_PWM_FLAG_LOOP); */

    (void)nrf_drv_pwm_simple_playback(&m_pwm0, &m_demo4a_seq, 1,
    NRF_DRV_PWM_FLAG_START_VIA_TASK | NRFX_PWM_FLAG_STOP);

    /* (void)nrf_drv_pwm_simple_playback(&m_pwm1, &m_demo4b_seq, 1, */
    /* NRF_DRV_PWM_FLAG_START_VIA_TASK | NRFX_PWM_FLAG_STOP); */

    // note this must be true: CH1_VAL > CH0_VAL since we stop at the last CC compare count
    nrf_drv_timer_compare
    (&m_tim1, NRF_TIMER_CC_CHANNEL0, timer_ch0_val, false);
    nrf_drv_timer_extended_compare
    (&m_tim1, NRF_TIMER_CC_CHANNEL1, timer_ch1_val, NRF_TIMER_SHORT_COMPARE1_STOP_MASK, false);

    //this will run one time and launch a pulse?
    /* nrf_drv_timer_enable(&m_tim1); */
    NRF_PWM0->TASKS_SEQSTART[0] = 1;
    }

Children
  • I couldn't get mode NRF_PWM_LOAD_GROUPED to work in either HAL or NRF's nrfx_pwm_* driver. I can get it work with NRF_PWM_LOAD_WAVE_FORM. I wonder if it's mode dependent. Here is a code it that works.

    void demo9(void) {

    /* #define NRFX_PWM_DEFAULT_CONFIG \ */
    /* { \ */
    /* .output_pins = { NRFX_PWM_DEFAULT_CONFIG_OUT0_PIN, \ */
    /* NRFX_PWM_DEFAULT_CONFIG_OUT1_PIN, \ */
    /* NRFX_PWM_DEFAULT_CONFIG_OUT2_PIN, \ */
    /* NRFX_PWM_DEFAULT_CONFIG_OUT3_PIN }, \ */
    /* .irq_priority = NRFX_PWM_DEFAULT_CONFIG_IRQ_PRIORITY, \ */
    /* .base_clock = (nrf_pwm_clk_t)NRFX_PWM_DEFAULT_CONFIG_BASE_CLOCK, \ */
    /* .count_mode = (nrf_pwm_mode_t)NRFX_PWM_DEFAULT_CONFIG_COUNT_MODE, \ */
    /* .top_value = NRFX_PWM_DEFAULT_CONFIG_TOP_VALUE, \ */
    /* .load_mode = (nrf_pwm_dec_load_t)NRFX_PWM_DEFAULT_CONFIG_LOAD_MODE, \ */
    /* .step_mode = (nrf_pwm_dec_step_t)NRFX_PWM_DEFAULT_CONFIG_STEP_MODE, \ */
    /* } */

    nrf_drv_pwm_config_t const config0 =
    {
    .output_pins =
    {
    PWM_PIN_A,
    PWM_PIN_B,
    0xFF,
    0xFF,
    },
    .irq_priority = APP_IRQ_PRIORITY_LOWEST,
    .base_clock = NRF_PWM_CLK_1MHz,
    .count_mode = NRF_PWM_MODE_UP,
    .top_value = TOP_VALUE, //unused
    .load_mode = NRF_PWM_LOAD_WAVE_FORM,
    .step_mode = NRF_PWM_STEP_AUTO
    };
    APP_ERROR_CHECK(nrf_drv_pwm_init(&m_pwm0, &config0, NULL));
    m_demo9_seq_values.channel_0 = 10;
    m_demo9_seq_values.channel_1 = 10;
    m_demo9_seq_values.channel_2 = 0;
    m_demo9_seq_values.counter_top = 20;
    /* nrf_drv_pwm_simple_playback(&m_pwm0, &m_demo9_seq, 1, */
    /* NRF_DRV_PWM_FLAG_START_VIA_TASK | NRFX_PWM_FLAG_STOP); */

    NRF_PWM0->SHORTS = 0;
    nrf_drv_pwm_simple_playback(&m_pwm0, &m_demo9_seq, 1, NRFX_PWM_FLAG_STOP);
    /* NRF_PWM0->SHORTS = PWM_SHORTS_SEQEND0_STOP_Enabled << PWM_SHORTS_SEQEND0_STOP_Pos; */

    /* NRF_PWM0->TASKS_SEQSTART[0] = 1; */
    }

  • Hi there,

    I'm happy that found a solution,

    I tried playing with the PWM driver example in the SDK and I modified demo 3 such that playback_count parameter of nrf_drv_pwm_simple_playback() was set to 0 instead of 3 and it resulted in the sequence being played only once instead of 3 times. Note that this is with the load_mode being COMMON. Try to do the same and see if you're able to repeat the results,

    regards

    Jared

  • Jared, do you mean this example:

    From the API https://infocenter.nordicsemi.com/index.jsp?topic=%2Fcom.nordic.infocenter.sdk5.v15.2.0%2Fgroup__nrf__drv__pwm.html

    [in] playback_count Number of playbacks to be performed (must not be 0).

    Also if playback_count is zero, why would it play one?

  • Hi,

    Yes, that example, and 0 was a typo, I meant 1,

    regards

    Jared

Related