• Wed 2020.03.04

    This is a first attempt at attempting to grasp the concepts behind PPI using nRF52. I am at a point where peppering the NRF52LL.js module with console.log() statements is/will be the next step in revealing the PPI mysteries.

    PPI - the "Programmable Peripheral Interconnect". This allows you to 'wire' peripherals together internally.
    nRF52 Low Level Interface Library


    Example code heading: 'Use the RTC to measure how long a button has been held down for'

    http://www.espruino.com/NRF52LL


    Scenario: Onboard button is held down (at L10) for approx one second. Output shows the counter (L11) value. A clear command is issued, but the counter continues to output what appears to be an overflow (L12-L14) value, despite the button never being pressed a subsequent time.

     2v03 (c) 2018 G.Williams
    Espruino is Open Source. Our work is supported
    only by sales of official boards and donations:
    http://espruino.com/Donate
    >
    8763371
    0
    0
    0
    0
    37785
    163827
    163831
    163827
    >
    


    I added a few console.log() statements to the example code block, that show the counter increments around twelve times for each console.log() statement that is executed, even after the counter is cleared.

     2v03 (c) 2018 G.Williams
    Espruino is Open Source. Our work is supported
    only by sales of official boards and donations:
    http://espruino.com/Donate
    >
    >
    5739316
    0
    0
    0
     
    0
    0
    0
    0
     
    0
    0
    0
    0
     
    98636
    7
    19
    31
     
    163832
    7
    19
    31
     
    163822
    9
    20
    32
     
    163823
    7
    19
    31
     
    >ci()
    =undefined
    >
    


    Q1: What is the counter frequency? (presumeably 32Khz)

    Q2: Clarification needed:

    The datasheet indicates two external crystals, but the schematic shows none. I presume a software generated clock, 32Mhz?/64Mhz? but unsure what the frequency actually is, that is used to determine the clocking here. Several references to 32Khz, but that is 1000 times slower than xtal.

    Spec

    p.550 shows two 32Mhz xtals

    p.103 19.2 LFCLK clock controller
    32Mhz

    L10 comment: // no prescaler, 32 kHz



    From module:
    cc3stop : true, // if cc[0] matches, stop the timer

    Q:3 L14 indicates on an up that rtc.tStop should occur. After the clear, shouldn't the clock stop and therefore no additional counting?

    163822
    Q4: What is this value? (total counter edges?)

    From the L10 comment above 32K is 32 000 but 163 822 is five times that value


    I'm choosing not to upgrade to 2v04 just yet, as PPI example existed prior to 2V03

    >process.env
    ={
      VERSION: "2v03",
      GIT_COMMIT: "e77d74f6",
      BOARD: "MDBT42Q",
    

    Will continue to read/filter through spec.




    Modified example to reveal counter changes and ci() function to halt output

    //http://www.espruino.com/NRF52LL
    //Use the RTC to measure how long a button has been held down for
    var ll = require("NRF52LL");
    // Source of events - the button
    // Note: this depends on the polarity of the physical button (this assumes that 0=pressed)
    var btnu = ll.gpiote(0, {type:"event",pin:BTN,lo2hi:1,hi2lo:0});­
    var btnd = ll.gpiote(1, {type:"event",pin:BTN,lo2hi:0,hi2lo:1});­
    // A place to recieve Tasks - the RTC
    var rtc = ll.rtc(2);
    poke32(rtc.prescaler, 0); // no prescaler, 32 kHz
    poke32(rtc.tStop, 1); // ensure RTC is stopped
    // Set up and enable PPI to start and stop the RTC
    ll.ppiEnable(0, btnd.eIn, rtc.tStart);
    ll.ppiEnable(1, btnu.eIn, rtc.tStop);
    // Every so often, check the RTC and report the result
    setInterval(function() {
      print(peek32(rtc.counter));
      poke32(rtc.tClear, 1);
      print(peek32(rtc.counter));
      print(peek32(rtc.counter));
      print(peek32(rtc.counter)); 
      print("    ");
    }, 5000);
    function ci() { clearInterval(); }
    


    Reference Links

    NRF52LL Module source

    http://www.espruino.com/modules/NRF52LL.­js

    nRf52 spec

    https://infocenter.nordicsemi.com/pdf/nR­F52832_PS_v1.1.pdf

    MDBT42Q breakout board schematic

    https://github.com/espruino/EspruinoBoar­d/blob/master/MDBT42/pdf/mdbt42q_breakou­t_sch.pdf


  • Q1: What is the counter frequency? (presumeably 32Khz)

    Yes, 32kHz

    Q2: The datasheet indicates two external crystals, but the schematic shows none.

    The MDBT42 module has a crystal in it. Just a high speed (8MHz?) one though, no low speed (32kHz) one - that is just an on-chip RC oscillator calibrated by the high speed one.

    Q:3 L14 indicates on an up that rtc.tStop should occur. After the clear, shouldn't the clock stop and therefore no additional counting?

    It should, yes. However it appears not to be working for some reason.

    Calling poke32(rtc.tStop,1) does stop the RTC though.

    Q4: 163822 What is this value? (total counter edges?)

    Yes. Clock runs at 32kHz, you're checking every 5 seconds. It's basically 32768*5

  • Thr 2020.03.05

    Thank you @Gordon for taking the time to respond promptly.

    re: 'Q:3 L14 indicates on an up that rtc.tStop should occur'

    'It should, yes. However it appears not to be working for some reason'

    From your detailed explanation, I believe I now have enough to push on. I have a bunch of time this weekend to dive into the examples, and with a few other trial attempts, may be able to get to the bottom of that example.


    re: 'Calling poke32(rtc.tStop,1)'

    On the drive into work this morning, had the exact thought, (before your post #2 arrived) but hadn't the time last evening to test. In any event, I should have the ammo to get the task I am after, done.


    re: 'It's basically 32768*5'

    Geez, do I feel like a Dolt! I even had that detail (in words) nicely spelled out beneath question #4! Amazing how I was so immersed in, and convinced that, the issue was with how the crystals (or lack of) controlled the timing at an underlying level, causing me to ponder why 163 822 wasn't smaller than 32 000, but not stepping back and looking from the 10,000ft level to view the interval duration and the run time effect with time.

    Thanks for the insight and now afford you the time back to Bangleland.
    or maybe it was a nice break taking a bit of a sidestep . . .

  • Great - if you find out why the example isn't working I'd love to be able to get the docs changed.

    As a starting point you could attempt to do it by using a rising edge on a second pin to stop the RTC (since we know that rising edge on BTN1 works to start it).

  • Sun 2020.03.08

    Ran into another snag.

    While I'm getting the hang of wiring (via code) the PPI tasks and events, un-wiring them has me a bit stumped. While flags stop and clear seem to work, when using say ll.ppiDisable(0);, while it does seem to change the functionality, it also locks the pin in the state it was at, as the command is executed. The pin mode seems to stay the same, but it then isn't possible to change modes or set the pin state.

    Not a super critical solve now issue, as powering off, does allow the nRF52 to reset, it would make for a good tutorial, should I be able to determine the proper un-wiring sequence. I presume it is in the reverse order of setup, so I'll continue to play until frustration sets in. ;-)

    Back to the datsheet as I'm not sure if the issue is with the event and state or the interconnect, or a combination.

    EDIT:  Sun 2020.03.08  

    Quick patch fix for 'Button held down example' is to reverse the 'btnu' and 'btnd' var definitions, but I'm not convinced just yet that, . . . that is all there is to it, as, . . . . post #6

  • Sun 2020.03.08

    Six hours later and another peculiarity.

    Adding a setWatch(); after setup of a wired PPI group never fires.

    Entering clearWatch() will also inadvertently kill the PPI setup.



    Am attempting to simulate an IRQ using the following instructions:

    Heading: Interrupts
    'Espruino doesn't allow you to react to interrupts from these peripherals directly, however you can change the state of an external pin (see the examples above) and can then short that pin to another pin that you can use as an input with setWatch.'

    http://www.espruino.com/NRF52LL


    Setup:
    MDBT42Q breakout board with USART edge pins at 12 O'clock

    • D22 Lower Left corner is set as an 'output' and it feeds a base resisitor of an 2n2222 to drive an LED from it's collector
    • D20 is an 'input' and is driven by pin D22 with a simple jumper

    • Watch visually the LED or hang a scope on either pin D20/D22



    Execution:
    After around a two second wait, a near 100Hz square wave appears on pin D20/D22

    The setWatch function never executes.

    Entering cw() or clearWatch() will also inadvertently kill the PPI setup.


    Some Helper functions and comments removed for brevity

    var ll = require("NRF52LL");
    
    // Set up pin as an output -> wire to external resistor 2n2222 base AND to neighboring D20
    // Hi out at base turns on 2n2222 collector LED
    digitalWrite(D22,0);
    digitalRead(D20);
    
    
    // create a 'toggle' task for the LED
    var tog = ll.gpiote(0, {type:"task",pin:D22,lo2hi:1,hi2lo:1,ini­tialState:0});
    
    // set up the rtc
    var rtc = ll.rtc(2);
    
    //save slower ~4x ea sec
    //poke32(rtc.prescaler, 4095); // 32kHz / 4095 = 8 Hz
    
    poke32(rtc.prescaler, 320); // 32kHz / 320 = ~100 Hz  roughy 10msec
    
    //Start
    function st() {
      rtc.enableEvent("eTick");
      poke32(rtc.tStart,1); // start RTC
      // use a PPI to trigger the toggle event
      ll.ppiEnable(0, rtc.eTick, tog.tOut);
    }
    
    
    
    var countD20 = 0;
    var watchID = {};
    
    // Pulse count the in data on D22
    function sw() {
    
      watchID = setWatch(function(e) {
    
    
    //    console.log( "L86  inside sw  e.state: " + e.state);
    
    //    debugger;
    
        countD20++;
    
    // Display count
    //    dc();
    
    
    //},D20,{edge:"rising", data:D20, repeat: true});
    //},D20,{edge:"rising", repeat: true});
    //},D20,{edge:"both", repeat: true});
    },D20,{edge:"falling", repeat: true});
    
    //kills signal at D22 
    //},D20,true);
    //from
    //options - If a boolean or integer, it determines whether to call this once (false = default) or every time a change occurs (true). 
    
    }
    
    
    
    // Display Counter
    function dc() {
      console.log( "count: " + countD20 );
    }
    
    
    
    function cw() {
      clearWatch();
    }
    
    
    
    
    // Wire PPI, init setWatch, start RTC
    
    setTimeout( function() {
      
      console.log("Hello World!");
      
      sw();
      console.log("init setWatch");
    
      st();
      console.log("start RTC");
      
    }, 1500);
    
    
    more complete comments and helper functions in attached code file
    

    1 Attachment

  • I think the issue may be that while the PPI is disabled, the pin number is still set in the GPIOTE, which might stop the rest of the hardware from being able to access it. You could try:

    var tog = ll.gpiote(0, {type:"task",pin:some_other_pin,lo2hi:1,­hi2lo:1,initialState:0});
    

    and see if that helps.

  • Tue 2020.03.10

    'and see if that helps'

    Well, I have given it the good ol' college try, but am now out of ideas, and faced with a new anomaly.

    I thought I saw the same possible 'Stepped On' pin issue, from your explanation in post #7, and changed that to a separate adjacent pin, but the setWatch never triggers with that change either.



    I noticed the following over the weekend, but dismissed it, that is until this evening. I powered down the setup, and shut off the PC. Tonight, I powered up the setup and the PC.

    New anomaly.

    For a sanity check, I uploaded the code file I submitted in post #6 but this time, the output square wave was crunched to 1Vp-p. Odd, as this all worked when I posted. I made several attempts with other code I had, and the output now swings to the rails, 0-3v3. I chose to retest, and now have a repeatable scenario. The first upload seems to always crunch the output, and the second upload seems to always allow full range of output swing. No ideas, but the PPI setup isn't allowing the pin output. I'll continue to try settings pin modes and playing with the start interval etc.



    A more perplexing issue is what I stated in post #6. I didn't want to start a new related thread so as not to draw attention. Entering cw() or clearWatch() will also inadvertently kill the PPI setup.

    Reviewing ' If no parameter is supplied, all watches will be removed'

    http://www.espruino.com/Reference#l__glo­bal_clearWatch

    I tried with both a no argument and also passing the setWatch ID in cwi() but both exhibit the same output killing result on Pin D22. As clearWatch is meant to do just that, then why is PPI getting clobbered? No idea here either.



    I'll continue to test, and this weekend, I'll try these examples with STM32 to see if more satisfactory resulsts can be obtained.


    1 Attachment

  • I'm afraid I'm a bit too busy getting things shipped to help out much with this for the next 2 weeks.

    If the output is getting cropped to ~1V peak to peak then chances are you're trying to write to a pin that is already set to an output though

    Also internally setWatch uses GPIOTE in order to work, so if you're setWatching on the same pin then that could be a problem.

    You could also try changing the gpiote number from 0 to something else. It's possible it's conflicting with the ones used for setWatch??

  • Sun 2020.03.15

    'If the output is getting cropped to ~1V peak to peak then chances are you're trying to write to a pin that is already set to an output though'

    The truly bizarre! Things that changed: Last week, I powered off the circuit and scope, Google Chrome locked up Windows, so I had to warm reboot. Yesterday I powered on the scope and applied power to the circuit and played with an external setWatch file for a sanity check. I loaded the file from post #6 and to my amazement it ran this time as expected. Floored!! Was it the newly introduced setWatch as it was indicated in post #9 that setWatch uses gpiote? Was it that last week after using ppi.disable that when re-uploading code PPI doesn't get set correctly? So I tinkered with the code a bit, both from the console and the editor side, ending up hosing something. So I uploaded the original file again, but this time no luck, even after re-applying power to the circuit. So, possibly a timing thing setting up PPI, perhaps?
    incidentally, using the Native IDE 0.70.6 - so Chrome and reboot shouldn't have had an effect


    Discovered another anomaly, but might be more of correct proceedural thing.

    Steps: Uploaded code which starts the PPI rtc. Played with rtc.prescaler and re-uploaded. Even with WebIDE Setting 'Reset before Send' set, the new prescaler value doesn't take effect. That said, a hard poke32(rtc.tStop,1) stop of the counter, then re-uploading does work.

    That sort of makes sense as we are only to poke a value, and the counter doesn't need to peek at it a second time as it is already doing it's work. But, in the process of re-uploading code, the counter is restarted, so it is reset(1) that may be the culprit.

    EDIT:
    ' Writing to the PRESCALER register when the RTC is started has no effect'
    p.241   https://infocenter.nordicsemi.com/pdf/nR­F52832_PS_v1.1.pdf

    Note that the register value does change though, and is read on subsequent uploads. It just doesn't change the RTC after it has been started.

    So, should a reset(1) also control the stopping and resetting of what ever PPI peripherals (intuitive as reset means reset - implying all parts of chip) are running, or is this a procedural thing where the user must follow a strict path in reverse order of setup, before they upload code, meaning reset(1) only applies to Espruino in RAM?

    EDIT:    three hours later
    Attempting to make changes to PPI using the L-hand console, it is possible to place the peripherals in a state that is not recoverable or (yet to be discovered maybe) re-configurable when attempting to add a setWatch, in my case on an unrelated PPI external (D15) pin. Yanking the power is the only way to recover.

  • Sun 2020.03.15

    Eight hours later . . . .

    Before I started messing with the channel within enable, ll.ppiEnable( 0, rtc.eTick, tog.tOut ); the setWatch using PPI on channel 0 worked one and only one time!! I've attempted to reproduce, and the only thing left off the list is to reboot the PC. Although an unlikely solution, will try over the next few days.



    Found this note:
    ** Real time counter
    You should only set on ch2 as 0 and 1 are used by Espruino/Bluetooth
    This function intentionally doesn't set state itself to allow you
    to still query RTC0/1 without modifying them.

    http://www.espruino.com/modules/NRF52LL.­js



    So did a complete code re-write, carefully inserting working snippets and using channel 3 for the enable and thought I was making progress. Now I've found a repeatable/reproduceable condition using the PPI counter.

    The prescaler calculation needs a review. Any value greater than 2047 (0x07FF) yielding a frequency slower than 16Hz or 62.5msec always outputs a 5Hz or 200msec output wave.

    // Toggle LED
    function tled() {
    
      // Is needed to setup PPI for some reason  
      poke32( rtc.tStop, 1 );
    
    
    // These work - but is positive pulse width and not freq - so is one half of freq
    
    //  poke32( rtc.prescaler, 64 ); // 32kHz / 64 = 500 Hz - 2000 usec
    //actual 4msec = 250Hz
    
    //  poke32( rtc.prescaler, 162 ); // 32kHz / 162 = 200 Hz - 5000 usec
    //actual 9msec = 111Hz
    
    //  poke32( rtc.prescaler, 326 ); // 32kHz / 326 = 100 Hz - 10000 usec
    //actual 18msec = 55Hz
    
    //  poke32( rtc.prescaler, 2047 ); // 32kHz / 2047 = 16 Hz - 62.5 msec
    //actual 120msec = 8.3Hz
    
    
    
    
      
    
    //These are all the same - and in error
    //  poke32( rtc.prescaler, 4095 ); // 32kHz / 4095 = 8 Hz - 125 msec
    //actual 200msec = 5Hz
    
    //  poke32( rtc.prescaler, 8191 ); // 32kHz / 8191 = 4 Hz - 250 msec
    //  poke32( rtc.prescaler, 16383 ); // 32kHz / 16383 = 2 Hz - 500 msec
    //  poke32( rtc.prescaler, 32767 ); // 32kHz / 32767 = 1 Hz - 1000 msec
    
    
    
    // No prescaler test
      poke32( rtc.prescaler, 0 ); // no prescaler, 32 kHz
      //actual 60usec = 16,667Hz
    
    // Just realized that the counter clock is putting out very narrow 
    // edges <1usec which is rtc.eTick
    // Our scope is monitoring pin D22 with the alternating state of 
    // the toggle register, so each edge
    // changes state, thereby giving us a frequency that is half at 
    // the output pin.
    
    
      rtc.enableEvent( "eTick" );
    
      poke32( rtc.tStart, 1 ); // start RTC
    
    // Use a PPI to trigger the toggle event
    //ll.ppiEnable(0, rtc.eTick, tog.tOut);
    
    // http://www.espruino.com/modules/NRF52LL.­js
    // ** Real time counter
    //  You should only set on ch2 as 0 and 1 are used by Espruino/Bluetooth
    //  This function intentionally doesn't set state itself to allow you
    //  to still query RTC0/1 without modifying them.
    
    
      ll.ppiEnable( 3, rtc.eTick, tog.tOut );
    }
    



    While this will need to be corrected, at the moment I still don't have a means to reflash the MDBT42Q, although I expect to pick up a tablet once a suitable one can be sourced. At least a week out as of today.


    Firing up the STM32 WiFi to compare techniques . . . .

    https://www.espruino.com/STM32+Periphera­ls

  • Tue 2020.03.17

    Spent yesterday toying with Low Level Access on STM32 using a Pico. All went well, but not all the functionality appears to be available as with the nRF52LL

    So, this morning, after heeding this note:

    ** Real time counter
    You should only set on ch2 as 0 and 1 are used by Espruino/Bluetooth

    I modifed the toggle channel to 4 and the enable channel to 3. Now the setWatch can be added without killing the underlying PPI counter. But, the setWatch never detects a pulse that clearly swings both rails.

    That is, until during code cleanup to post to the forum, I failed to remove a commented out line of code within the setInterval initialization that caused a separate function to execute unintentionally.
    It contains a local scope copy of the toggle command, (and now I see the global scope one also) but isn't really needed. Why it allows PPI and setWatch to play together is still a bit of a mystery, but at least I have a stable, repeatable, reloadable after power up chunk to play with.

    There is also another anomaly, that when specifying 'af_output' using pinMode, that after PPI signal is sent to the external LED, that the mode changes from 'af_output' to just 'output' with no intentional change by me. (maybe this is why this code block now appears to be stable? - but don't we need the special 'af_' prefix as we are using PPI?)

    While I continue to play/learn how all the bits work under the hood, the following still needs review:

    • The flipped buttons in post #5
    • What channel setWatch is using that may conflict with PPI channels
    • The prescaler code that limits the counter frequency post #11
    • Inadvetent pinMode change 'af_output' to just 'output'
    • Review reset(1) to stop underlying PPI post #10
    • Requirement to force a hard counter stop when making a setup change

    When playing with this code file, be aware it takes around five seconds to upload and initialize before commands may be entered.


    1 Attachment

  • Fri 2020.03.20

    Found this note:

    'Note: On nRF52 chips (used in Puck.js, Pixl.js, MDBT42Q) setWatch disables the GPIO output on that pin. In order to be able to write to the pin again you need to disable the watch with clearWatch.'

    http://www.espruino.com/Reference#l__glo­bal_setWatch

    This explains why setWatch was killing the PPI task, as PPI was writing to that pin, a positive pulse output.

    Also adds a bit of insight to the post #9 statement:

    'so if you're setWatching on the same pin then that could be a problem.'


    EDIT:   Sat 2020.03.21
    To which now the AhhHa light goes on as to why the watch needs to be on an input, as in this explanation:
    Heading: Interrupts
    'Espruino doesn't allow you to react to interrupts from these peripherals directly, however you can change the state of an external pin (see the examples above) and can then short that pin to another pin that you can use as an input with setWatch.'

    http://www.espruino.com/NRF52LL

  • Post a reply
    • Bold
    • Italics
    • Link
    • Image
    • List
    • Quote
    • code
    • Preview
About

Tutorial example output anomaly using low level access PPI on MDBT42Q breakout board

Posted by Avatar for Robin @Robin

Actions