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

nrf52832 PWM with EasyDMA problem

I'm trying to communicate with a single NeoPixel (WS2812B) using the PWM module.

I've configured a sequence array with the compare values necessary to turn on the red and the blue leds.

I would like to transmit this data without having to poll EVENTS_SEQEND[0] before moving on. This doesn't seem to be possible even if I use the shortcut EVENTS_SEQEND[0] and STOP task. The communication doesn't seem to work.

However, if I insert the following polling before the while(); statement everything works ok. Any ideas?

while(!NRF_PWM1->EVENTS_SEQEND[0])

{

  yield();

}

NRF_PWM1->EVENTS_SEQEND[0] = 0;

Here's the code:

#define PIXPIN 7  //data pin for neopixel
            
void main()                                         
{
  NRF_GPIO->DIRSET = (1 << PIXPIN);  //set ref pin as output
  NRF_GPIO->OUTCLR = (1 << PIXPIN);  //set ref pin low
  NRF_PWM1->MODE = 0;  //up counter
  NRF_PWM1->PRESCALER = 0;  //16MHz ticks
  NRF_PWM1->COUNTERTOP = 20;  //1.25 µsec pwm frequency
  NRF_PWM1->LOOP = 0;  //looping disabled
  NRF_PWM1->DECODER = 0;  //common, refresh count
  NRF_PWM1->SEQ[0].REFRESH  = 0;
  NRF_PWM1->SEQ[0].ENDDELAY = 0;
  NRF_PWM1->PSEL.OUT[0] = PIXPIN;
  NRF_PWM1->ENABLE = 1;
  NRF_PWM1->SHORTS = 0b00001;  //Shortcut between SEQEND[0] event and STOP task

  uint16_t neoPixelWavFrm[64] = {32774, 32774, 32774, 32774, 32774, 32774, 32781, 32774,
					32774, 32774, 32774, 32774, 32774, 32774, 32781, 32774,
					32774, 32774, 32774, 32774, 32774, 32774, 32781, 32774,
					32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768,
					32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768,
					32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768,
					32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768,
					32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768};
  NRF_PWM1->SEQ[0].PTR  = (uint32_t)(neoPixelWavFrm);
  NRF_PWM1->SEQ[0].CNT = 64;  //one uint16_t for each of the 24 bits per pixel plus 40 extra
  NRF_PWM1->SEQ[0].REFRESH  = 0;
  NRF_PWM1->SEQ[0].ENDDELAY = 0;
  NRF_PWM1->EVENTS_SEQEND[0] = 0;
  NRF_PWM1->TASKS_SEQSTART[0] = 1;
  
  while(1);
}

  • Hi Marc

    I would strongly suggest using the nrfx_pwm driver provided with the SDK, rather than using the module directly. 

    One of the unofficial examples on our Github account actually has a WS2812B implementation already, using the nrfx_pwm driver:
    https://github.com/NordicPlayground/nrf52-ble-multi-link-multi-role/tree/master/common/drv_ws2812

    I would suggest having a look at it, and see if you can't just reuse this code. 

    Best regards
    Torbjørn

  • Thanks Torbjorn,

    I appreciate that you want to promote the use of your drivers, but I like to use the modules directly, as you say, so that I can understand them better.

    Since my original post I think I understand the issue much better. The main problem is the large amount of RAM required for communication with these chips. Each chip needs 24 (8 bits for each color) 16 bit integers for the wave pattern. 100 of these, for instance, would require 4800 bytes of RAM. Therefore it is imperative to free this space up immediately after the transmission is complete. That's why looping on the SEQEND event is important. The following code works for me.

    #define PIXPIN 7 //data pin for neopixel
    
    void main()
    
    {
    
      NRF_GPIO->DIRSET = (1 << PIXPIN); //set ref pin as output
    
      NRF_GPIO->OUTCLR = (1 << PIXPIN); //set ref pin low
    
      NRF_PWM1->MODE = 0; //up counter
    
      NRF_PWM1->PRESCALER = 0; //16MHz ticks
    
      NRF_PWM1->COUNTERTOP = 20; //1.25 µsec pwm period
    
      NRF_PWM1->LOOP = 0; //looping disabled
    
      NRF_PWM1->DECODER = 0; //common, refresh count
    
      NRF_PWM1->SEQ[0].REFRESH = 0;
    
      NRF_PWM1->SEQ[0].ENDDELAY = 0;
    
      NRF_PWM1->PSEL.OUT[0] = PIXPIN;
    
      NRF_PWM1->ENABLE = 1;
    
      uint16_t neoPixelWavFrm[24] = {6 | 0x8000, 6 | 0x8000, 6 | 0x8000, 6 | 0x8000, 6 | 0x8000, 6 | 0x8000, 13 | 0x8000, 6 | 0x8000,
    
    6 | 0x8000, 6 | 0x8000, 6 | 0x8000, 6 | 0x8000, 6 | 0x8000, 6 | 0x8000, 13 | 0x8000, 6 | 0x8000,
    
    6 | 0x8000, 6 | 0x8000, 6 | 0x8000, 6 | 0x8000, 6 | 0x8000, 6 | 0x8000, 13 | 0x8000, 6 | 0x8000,};
    
      NRF_PWM1->SEQ[0].PTR = (uint32_t)(neoPixelWavFrm);
    
      NRF_PWM1->SEQ[0].CNT = 24; //one uint16_t for each of the 24 bits per pixel
    
      NRF_PWM1->SEQ[0].REFRESH = 0;
    
      NRF_PWM1->SEQ[0].ENDDELAY = 0;
    
      NRF_PWM1->EVENTS_SEQEND[0] = 0;
    
      NRF_PWM1->TASKS_SEQSTART[0] = 1;
    
      while(1) //non-blocking while loop
    
      {
    
        if(NRF_PWM1->EVENTS_SEQEND[0]) break;
    
        __asm__("nop\n\t");
    
      }
      NRF_PWM1->TASKS_STOP = 1;  //drives PIXPIN low as determined by NRF_GPIO->OUTCLR = (1 << PIXPIN) to latch data for 50µs
      while(1);
    }

    I've successfully tried it with 60 LEDs and it should work with any number. In my implementation I put the first half of the code in an initialization function and the second half in a transmission call that first constructs the waveform from an array of LED colors that is passed to it. At the conclusion of the transmission of the sequence as marked by SEQEND a STOP task is sent. Aside from keeping the module from sending the sequence again, it also drives the data pin low which is necessary for latching.  In my experience the required 50µs latching time is always accomplished between transmission calls.

    By the way, there’s an error in the definition of the RAM defined register on page 498 of nRF52832_PS_v1.4. Bit 15 should be 0 for FallingEdge and 1 for RisingEdge. This would make it consistent with Fig 142 on page 496.

  • Hi Marc

    It is good to hear you got the code working. 

    In general I would suggest configuring the EVENTS_SEQEND event to generate an interrupt, but maybe this is your next step already ;)

    Thanks for the heads up regarding the product specification. I will report this to the tech writers. 

    Best regards
    Torbjørn

  • Good idea about using an interrupt to trigger the STOP task instead of the while loop.

  • Hi Marc

    Should make for a more efficient implementation. 

    Best of luck Slight smile

    Regards
    Torbjørn

Related