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

PWM Differential output

Would you please tell me how to use the nrf52832 PWM interface to implement differential output to spkr?

Here is my PWM interface configuration code:

NRF_PWM0->PSEL.OUT[0] =   (PWM_IO << PWM_PSEL_OUT_PIN_Pos) | (PWM_PSEL_OUT_CONNECT_Connected << PWM_PSEL_OUT_CONNECT_Pos);
NRF_PWM0->PSEL.OUT[1] =   (0 << PWM_PSEL_OUT_PIN_Pos) | (PWM_PSEL_OUT_CONNECT_Disconnected << PWM_PSEL_OUT_CONNECT_Pos);
NRF_PWM0->PSEL.OUT[2] =   (1 << PWM_PSEL_OUT_PIN_Pos) | (PWM_PSEL_OUT_CONNECT_Disconnected << PWM_PSEL_OUT_CONNECT_Pos);
NRF_PWM0->PSEL.OUT[3] =   (2 << PWM_PSEL_OUT_PIN_Pos) | (PWM_PSEL_OUT_CONNECT_Disconnected << PWM_PSEL_OUT_CONNECT_Pos);

NRF_PWM0->ENABLE = (PWM_ENABLE_ENABLE_Enabled << PWM_ENABLE_ENABLE_Pos);
NRF_PWM0->MODE = (PWM_MODE_UPDOWN_Up << PWM_MODE_UPDOWN_Pos);
NRF_PWM0->PRESCALER = (PWM_PRESCALER_PRESCALER_DIV_1 << PWM_PRESCALER_PRESCALER_Pos);
NRF_PWM0->COUNTERTOP = (256/*16000*/ << PWM_COUNTERTOP_COUNTERTOP_Pos);
NRF_PWM0->DECODER =   (PWM_DECODER_LOAD_Common << PWM_DECODER_LOAD_Pos) | (PWM_DECODER_MODE_RefreshCount << PWM_DECODER_MODE_Pos);

NRF_PWM0->SHORTS = (PWM_SHORTS_LOOPSDONE_SEQSTART0_Disabled << PWM_SHORTS_LOOPSDONE_SEQSTART0_Pos);

Here is my PWM audio output code:

	NRF_PWM0->SEQ[0].PTR = ((uint32_t)(pwmdata) << PWM_SEQ_PTR_PTR_Pos);
	NRF_PWM0->SEQ[0].CNT = ((sizeof(pwmdata) / sizeof(uint16_t)) << PWM_SEQ_CNT_CNT_Pos);
	NRF_PWM0->SEQ[0].REFRESH = REFRESHBD;
	NRF_PWM0->SEQ[0].ENDDELAY = REFRESHBD * ((HALFLOOPPERIOD / REFRESHBD) - ((sizeof(pwmdata) / sizeof(uint16_t))));
	
	NRF_PWM0->LOOP = (0 << PWM_LOOP_CNT_Pos);
	NRF_PWM0->TASKS_SEQSTART[0] = 1;

Is it possible to achieve PWM differential output?

Thanks!

Parents
  • Hello

    First I would recommend you use the pwm_driver module as it simplifies the use of the PWM hardware through predefined function calls.

    A differential PWM can be achieved by using two outputs reading separate sequences. Page 498 of the nRF52832 product specification shows grouped mode can be used. When a sequence is played the outputs will be fed with alternating duty cycles from the sequence, output 1 will be fed duty cycles number 1, 3, 5, 7 etc. and output 2 will be fed with duty cycles number 2, 4, 6, 8 etc.

    The register description on page 498 of the nRF52832 product specification shows that the MSB of the compare value read from RAM is a polarity bit. If the duty cycles are equal for both outputs, but with the MSB of the duty cycles fed to one of the outputs set, the output will be differential.

    A simple example of a differential PWM signal would be.

    #define PWM_CH0_DUTY 16000 //50% duty cycle
    #define PWM_CH1_DUTY PWM_CH0_DUTY | (1<<15) //50% duty cycle, polarity bit set
    

    With PWM configuration

    static void PWM_config(void)
    {
    	uint16_t pwm_seq0[2] = {PWM_CH0_DUTY, PWM_CH1_DUTY};
    	NRF_PWM0->PSEL.OUT[0] = (mypin << PWM_PSEL_OUT_PIN_Pos) | (PWM_PSEL_OUT_CONNECT_Connected << PWM_PSEL_OUT_CONNECT_Pos);
    	NRF_PWM0->PSEL.OUT[2] = (mypin2 << PWM_PSEL_OUT_PIN_Pos) | (PWM_PSEL_OUT_CONNECT_Connected << PWM_PSEL_OUT_CONNECT_Pos);
    	NRF_PWM0->ENABLE = (PWM_ENABLE_ENABLE_Enabled << PWM_ENABLE_ENABLE_Pos);
    	NRF_PWM0->MODE = (PWM_MODE_UPDOWN_Up << PWM_MODE_UPDOWN_Pos);
    	NRF_PWM0->PRESCALER = (PWM_PRESCALER_PRESCALER_DIV_128 << PWM_PRESCALER_PRESCALER_Pos);
    	NRF_PWM0->COUNTERTOP = (32000 << PWM_COUNTERTOP_COUNTERTOP_Pos);
    	NRF_PWM0->LOOP = (1 << PWM_LOOP_CNT_Pos);
    	NRF_PWM0->DECODER = (PWM_DECODER_LOAD_Grouped << PWM_DECODER_LOAD_Pos) | (PWM_DECODER_MODE_RefreshCount << PWM_DECODER_MODE_Pos);
    	NRF_PWM0->SEQ[0].PTR = ((uint32_t)(pwm_seq0) << PWM_SEQ_PTR_PTR_Pos);
    	NRF_PWM0->SEQ[0].CNT = ((sizeof(pwm_seq0) / sizeof(uint16_t)) << PWM_SEQ_CNT_CNT_Pos);
    	NRF_PWM0->SEQ[0].REFRESH = 0;
    	NRF_PWM0->SEQ[0].ENDDELAY = 2;
    }
    

    This example simply blinks two leds with 50% duty cycle, where one led is inverted compared to the other.

    For driving a speaker your sequence would be based on the audio you are going to play. For continuous play you could create two sequences, and use interrupts to determine when to update and run the two sequences in an alternating fashion.

    Best regards

    Jørn Frøysa

Reply
  • Hello

    First I would recommend you use the pwm_driver module as it simplifies the use of the PWM hardware through predefined function calls.

    A differential PWM can be achieved by using two outputs reading separate sequences. Page 498 of the nRF52832 product specification shows grouped mode can be used. When a sequence is played the outputs will be fed with alternating duty cycles from the sequence, output 1 will be fed duty cycles number 1, 3, 5, 7 etc. and output 2 will be fed with duty cycles number 2, 4, 6, 8 etc.

    The register description on page 498 of the nRF52832 product specification shows that the MSB of the compare value read from RAM is a polarity bit. If the duty cycles are equal for both outputs, but with the MSB of the duty cycles fed to one of the outputs set, the output will be differential.

    A simple example of a differential PWM signal would be.

    #define PWM_CH0_DUTY 16000 //50% duty cycle
    #define PWM_CH1_DUTY PWM_CH0_DUTY | (1<<15) //50% duty cycle, polarity bit set
    

    With PWM configuration

    static void PWM_config(void)
    {
    	uint16_t pwm_seq0[2] = {PWM_CH0_DUTY, PWM_CH1_DUTY};
    	NRF_PWM0->PSEL.OUT[0] = (mypin << PWM_PSEL_OUT_PIN_Pos) | (PWM_PSEL_OUT_CONNECT_Connected << PWM_PSEL_OUT_CONNECT_Pos);
    	NRF_PWM0->PSEL.OUT[2] = (mypin2 << PWM_PSEL_OUT_PIN_Pos) | (PWM_PSEL_OUT_CONNECT_Connected << PWM_PSEL_OUT_CONNECT_Pos);
    	NRF_PWM0->ENABLE = (PWM_ENABLE_ENABLE_Enabled << PWM_ENABLE_ENABLE_Pos);
    	NRF_PWM0->MODE = (PWM_MODE_UPDOWN_Up << PWM_MODE_UPDOWN_Pos);
    	NRF_PWM0->PRESCALER = (PWM_PRESCALER_PRESCALER_DIV_128 << PWM_PRESCALER_PRESCALER_Pos);
    	NRF_PWM0->COUNTERTOP = (32000 << PWM_COUNTERTOP_COUNTERTOP_Pos);
    	NRF_PWM0->LOOP = (1 << PWM_LOOP_CNT_Pos);
    	NRF_PWM0->DECODER = (PWM_DECODER_LOAD_Grouped << PWM_DECODER_LOAD_Pos) | (PWM_DECODER_MODE_RefreshCount << PWM_DECODER_MODE_Pos);
    	NRF_PWM0->SEQ[0].PTR = ((uint32_t)(pwm_seq0) << PWM_SEQ_PTR_PTR_Pos);
    	NRF_PWM0->SEQ[0].CNT = ((sizeof(pwm_seq0) / sizeof(uint16_t)) << PWM_SEQ_CNT_CNT_Pos);
    	NRF_PWM0->SEQ[0].REFRESH = 0;
    	NRF_PWM0->SEQ[0].ENDDELAY = 2;
    }
    

    This example simply blinks two leds with 50% duty cycle, where one led is inverted compared to the other.

    For driving a speaker your sequence would be based on the audio you are going to play. For continuous play you could create two sequences, and use interrupts to determine when to update and run the two sequences in an alternating fashion.

    Best regards

    Jørn Frøysa

Children
Related