<?xml version="1.0" encoding="UTF-8" ?>
<?xml-stylesheet type="text/xsl" href="https://devzone.nordicsemi.com/cfs-file/__key/system/syndication/rss.xsl" media="screen"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>ADC Sampling Synchronization using Timer and PPI</title><link>https://devzone.nordicsemi.com/f/nordic-q-a/125646/adc-sampling-synchronization-using-timer-and-ppi</link><description>I have code set up to configure Timer3 to generate a Compare event at 100 KHz, using the PPI so that triggers an ADC Sample event for the two enabled ADC channels. EasyDMA is configured to capture a run of 4K samples each alternating between 2 ADC channels</description><dc:language>en-US</dc:language><generator>Telligent Community 13</generator><lastBuildDate>Fri, 28 Nov 2025 22:00:02 GMT</lastBuildDate><atom:link rel="self" type="application/rss+xml" href="https://devzone.nordicsemi.com/f/nordic-q-a/125646/adc-sampling-synchronization-using-timer-and-ppi" /><item><title>RE: ADC Sampling Synchronization using Timer and PPI</title><link>https://devzone.nordicsemi.com/thread/555788?ContentTypeID=1</link><pubDate>Fri, 28 Nov 2025 22:00:02 GMT</pubDate><guid isPermaLink="false">137ad170-7792-4731-bb38-c0d22fbe4515:813f85d3-4844-4063-bb25-65768f7334e1</guid><dc:creator>hmolesworth</dc:creator><description>&lt;p&gt;Chipping in late so not sure if all the problems were resolved. Sometimes AHB Bus Masters collide and create unexpected results and it is worth isolating the SAADC destination buffers into separate RAM AHB Slaves which have no other memory access (such as stack or variables). The following discussion explains this in more detail:&amp;nbsp;&lt;a href="https://devzone.nordicsemi.com/f/nordic-q-a/105239/adc-infuriating-behaviour-with-multi-channel-scan-using-ppi-to-rtc/454753"&gt;adc-infuriating-behaviour&lt;/a&gt;&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>RE: ADC Sampling Synchronization using Timer and PPI</title><link>https://devzone.nordicsemi.com/thread/555311?ContentTypeID=1</link><pubDate>Tue, 25 Nov 2025 06:25:21 GMT</pubDate><guid isPermaLink="false">137ad170-7792-4731-bb38-c0d22fbe4515:a59ee8d2-d966-4677-be48-9db87ad962d0</guid><dc:creator>Kenneth</dc:creator><description>[quote user="snoopy20"]It&amp;#39;s ok if the timer tick starts before START is called, however if you have a PPI free you can wire START to call both SAMPLE and TIMER_START for perfect sync (see PPI Fork). The only reason I don&amp;#39;t is I have no PPI&amp;#39;s free (I&amp;#39;ve took nrf52810 to the max!).[/quote]
&lt;p&gt;A very good idea, I at least did not think of that.&lt;/p&gt;
&lt;p&gt;Thanks for chipping in!&lt;/p&gt;
&lt;p&gt;Kenneth&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>RE: ADC Sampling Synchronization using Timer and PPI</title><link>https://devzone.nordicsemi.com/thread/555287?ContentTypeID=1</link><pubDate>Mon, 24 Nov 2025 21:10:37 GMT</pubDate><guid isPermaLink="false">137ad170-7792-4731-bb38-c0d22fbe4515:7d87c0b1-0634-44b4-a7c8-c68c51d1f1d5</guid><dc:creator>snoopy20</dc:creator><description>&lt;p&gt;For some reason my reply didn&amp;#39;t go in-line before, I&amp;#39;ll try again.&lt;br /&gt;&lt;br /&gt;I do a lot with the ADC, it runs like tasks where the interupt is an end-of-task. I use 810 and RAM is short in supply so I loop the loop sort of speak, that is the easydma runs 256 times instead of 4,000. I did do that in the past but the values I get are multiplied and then required 64 bit maths which hogged the CPU. By doing 256 times and introducing a little jitter I lower CPU usage. It depends what else you need the CPU for and whether at the end you&amp;#39;re flip-flopping buffers to keep sampling.&lt;br /&gt;&lt;br /&gt;It&amp;#39;s ok if the timer tick starts before START is called, however if you have a PPI free you can wire START to call both SAMPLE and TIMER_START for perfect sync (see PPI Fork). The only reason I don&amp;#39;t is I have no PPI&amp;#39;s free (I&amp;#39;ve took nrf52810 to the max!).&lt;br /&gt;&lt;br /&gt;Yes RESULTSDONE to SAMPLE will cause the issue eventually even with the reset code. Sometimes it would take 30 minutes before it became an issue. As my nrf52810 controls hardware based on the reads they had to be 100% or hardware would be destroyed.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>RE: ADC Sampling Synchronization using Timer and PPI</title><link>https://devzone.nordicsemi.com/thread/555285?ContentTypeID=1</link><pubDate>Mon, 24 Nov 2025 20:57:56 GMT</pubDate><guid isPermaLink="false">137ad170-7792-4731-bb38-c0d22fbe4515:d67a14dd-835a-4452-8f67-8285527dab5e</guid><dc:creator>snoopy20</dc:creator><description>&lt;p&gt;No idea, I assume it&amp;#39;s controlling mosfets supplying power to the various aspects of the ADC hardware but who knows. Glad it worked, knew it would though, I had a personal war with the nrf52 ADC a few year back. &lt;span class="emoticon" data-url="https://devzone.nordicsemi.com/cfs-file/__key/system/emoji/1f642.svg" title="Slight smile"&gt;&amp;#x1f642;&lt;/span&gt;&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>RE: ADC Sampling Synchronization using Timer and PPI</title><link>https://devzone.nordicsemi.com/thread/555284?ContentTypeID=1</link><pubDate>Mon, 24 Nov 2025 20:56:04 GMT</pubDate><guid isPermaLink="false">137ad170-7792-4731-bb38-c0d22fbe4515:d63924ea-1400-4b83-88e1-5ecc7e1d7450</guid><dc:creator>snoopy20</dc:creator><description>&lt;p&gt;Yes, it was the only way I could get easydma not to occasionally have this issue. The full reset also needs to be done after ADC calibration as that could also trigger it (I&amp;#39;m not 100% whether the reset then wipes the calibration value though!).&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>RE: ADC Sampling Synchronization using Timer and PPI</title><link>https://devzone.nordicsemi.com/thread/555281?ContentTypeID=1</link><pubDate>Mon, 24 Nov 2025 20:16:45 GMT</pubDate><guid isPermaLink="false">137ad170-7792-4731-bb38-c0d22fbe4515:851f4119-0b87-4caa-8afc-bf16a66beb3d</guid><dc:creator>Kenneth</dc:creator><description>&lt;p&gt;Interesting, the workaround is this errata:&lt;br /&gt;&lt;a href="https://docs.nordicsemi.com/bundle/errata_nRF52840_Rev3/page/ERR/nRF52840/Rev3/latest/anomaly_840_241.html"&gt;https://docs.nordicsemi.com/bundle/errata_nRF52840_Rev3/page/ERR/nRF52840/Rev3/latest/anomaly_840_241.html&lt;/a&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;But that one should (at least according to the description) affect the power consumption only, seems it also cleanup something more in this case.&lt;/p&gt;
&lt;p&gt;Kenneth&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>RE: ADC Sampling Synchronization using Timer and PPI</title><link>https://devzone.nordicsemi.com/thread/555272?ContentTypeID=1</link><pubDate>Mon, 24 Nov 2025 17:39:39 GMT</pubDate><guid isPermaLink="false">137ad170-7792-4731-bb38-c0d22fbe4515:1a56e9df-3a34-41f8-b009-e304cba15ba7</guid><dc:creator>SteveHx</dc:creator><description>&lt;p&gt;Thank you for the above. Thank undocumented reset routine seems to do the trick. I&amp;#39;d sure like to know what those undocumented registers are, though.&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>RE: ADC Sampling Synchronization using Timer and PPI</title><link>https://devzone.nordicsemi.com/thread/555268?ContentTypeID=1</link><pubDate>Mon, 24 Nov 2025 16:12:35 GMT</pubDate><guid isPermaLink="false">137ad170-7792-4731-bb38-c0d22fbe4515:3d196d69-ea04-4d32-b2d2-bf95f99204ba</guid><dc:creator>SteveHx</dc:creator><description>&lt;p&gt;Very good, quite a lot of information here. Let me ask questions in the order of the information presented:&lt;/p&gt;
&lt;p&gt;Are those &amp;quot;magic addresses&amp;quot; in your reset() routine documented somewhere? Clearly they&amp;#39;re part of the SAADC block, but not documented in the regular user manual, which stops at 0x638.&lt;/p&gt;
&lt;p&gt;In the code block using documented addresses, you appear to be enabling the SAADC interrupt in line 7. What&amp;#39;s that for, since you and I both are using EasyDMA to actually capture the values?&lt;/p&gt;
&lt;p&gt;A ways back up this chain you&amp;#39;ll see that I explored the possibility that the timer tick was happening before the ADC is ready. In that case I would expect the random flip to happen in the middle of a capture, not just at the start. And in any case I&amp;#39;m running the SAADC precisely per the datasheet, 3&amp;micro;Sec settling, 200 KSPS (2 channels at 100KSPS each; I&amp;#39;ve also tried 8 channels at 25KSPS, with alternating channels sampling the same pins).&lt;/p&gt;
&lt;p&gt;Using RESULTSDONE to trigger SAMPLE (via the PPI, for lack of a SHORTS register on SAADC) seemed to work just fine, and didn&amp;#39;t change the random misalignment.&lt;/p&gt;
&lt;p&gt;I already know about OVERSAMPLE vs. multiple channels. Not using it.&lt;/p&gt;
&lt;p&gt;I&amp;#39;ll have a go at your reset() routine with the kinky undocumented addresses, and revisit my setup routine referencing your setup routine, and will post here with how it goes. Thank you for the detailed responses.&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>RE: ADC Sampling Synchronization using Timer and PPI</title><link>https://devzone.nordicsemi.com/thread/555264?ContentTypeID=1</link><pubDate>Mon, 24 Nov 2025 15:52:22 GMT</pubDate><guid isPermaLink="false">137ad170-7792-4731-bb38-c0d22fbe4515:7edb0cb6-ee04-4347-a96c-8234b551acbb</guid><dc:creator>snoopy20</dc:creator><description>&lt;p&gt;One more thing, oversample will work on one pass of many channels. I believe (iirc) it won&amp;#39;t work on many passes. Quite a few things can cause the random &amp;#39;flip&amp;#39;!&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>RE: ADC Sampling Synchronization using Timer and PPI</title><link>https://devzone.nordicsemi.com/thread/555261?ContentTypeID=1</link><pubDate>Mon, 24 Nov 2025 15:46:22 GMT</pubDate><guid isPermaLink="false">137ad170-7792-4731-bb38-c0d22fbe4515:8546ac15-2032-4cfe-8211-9a0f7dbf4e4e</guid><dc:creator>snoopy20</dc:creator><description>&lt;p&gt;PS) you can&amp;#39;t use&amp;nbsp;RESULTSDONE to trigger SAMPLE it won&amp;#39;t work. I can&amp;#39;t recall why but iirc the event occurs before EASYDMA may have finished so another point where the wrong buffer position can be used and reads &amp;#39;flip&amp;#39;. Only use a timer. I personally use TIMER_TICK from an RTC.&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>RE: ADC Sampling Synchronization using Timer and PPI</title><link>https://devzone.nordicsemi.com/thread/555260?ContentTypeID=1</link><pubDate>Mon, 24 Nov 2025 15:42:46 GMT</pubDate><guid isPermaLink="false">137ad170-7792-4731-bb38-c0d22fbe4515:9db29493-b421-4d2f-818b-cfb341a594b5</guid><dc:creator>snoopy20</dc:creator><description>&lt;p&gt;The randomness is a known issue and is sort of described but not fully in the errata. I basically do what you&amp;#39;re doing with the nrf52 but everytime you wish to restart a easydma run fully reset the ADC first using the following code and then set everything back up.&lt;br /&gt;&lt;br /&gt;&lt;pre class="ui-code" data-mode="c_cpp"&gt;/*
 * Performs a full reset, required due to known NRF52 errata.
 */
static void reset (void) {

  NRF_PPI-&amp;gt;CHENCLR = 1 &amp;lt;&amp;lt; CFG_PPI_ADC_SAMPLEMETHOD;

  volatile uint32_t temp1;
  volatile uint32_t temp2;
  volatile uint32_t temp3;
  temp1 = *(volatile uint32_t *)0x40007640ul;
  temp2 = *(volatile uint32_t *)0x40007644ul;
  temp3 = *(volatile uint32_t *)0x40007648ul;
  *(volatile uint32_t *)0x40007FFCul = 0ul; 
  *(volatile uint32_t *)0x40007FFCul; 
  *(volatile uint32_t *)0x40007FFCul = 1ul;
  *(volatile uint32_t *)0x40007640ul = temp1;
  *(volatile uint32_t *)0x40007644ul = temp2;
  *(volatile uint32_t *)0x40007648ul = temp3;
}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;And for reference here&amp;#39;s the code which sets up my timed run...&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="ui-code" data-mode="c_cpp"&gt;  NRF_SAADC-&amp;gt;CH[0].PSELP = CFG_ADC_MPPT_VOLTAGE_PSEL;
  NRF_SAADC-&amp;gt;CH[1].PSELP = CFG_ADC_MPPT_CURRENT_PSEL;
  NRF_SAADC-&amp;gt;CH[0].CONFIG = SAADC_CH_CONFIG_GAIN_Gain1_6 &amp;lt;&amp;lt; SAADC_CH_CONFIG_GAIN_Pos | ADC_SAMPLETIME_CFG_MPPTVOLTAGE &amp;lt;&amp;lt; SAADC_CH_CONFIG_TACQ_Pos;
  NRF_SAADC-&amp;gt;CH[1].CONFIG = mpptCurrentAttenuation &amp;lt;&amp;lt; SAADC_CH_CONFIG_GAIN_Pos | ADC_SAMPLETIME_CFG_CURRENT &amp;lt;&amp;lt; SAADC_CH_CONFIG_TACQ_Pos;
  NRF_SAADC-&amp;gt;RESULT.MAXCNT = ADC_MPPT_SAMPLES * 2;
  NRF_SAADC-&amp;gt;RESULT.PTR = (uint32_t) &amp;amp;buffer;
  NRF_SAADC-&amp;gt;INTENSET = SAADC_INTENSET_END_Enabled &amp;lt;&amp;lt; SAADC_INTENSET_END_Pos;
  NRF_SAADC-&amp;gt;RESOLUTION = ADC_RESOLUTION &amp;lt;&amp;lt; SAADC_RESOLUTION_VAL_Pos;
  NRF_SAADC-&amp;gt;ENABLE = SAADC_ENABLE_ENABLE_Enabled &amp;lt;&amp;lt; SAADC_ENABLE_ENABLE_Pos;
  NRF_SAADC-&amp;gt;TASKS_START = SAADC_TASKS_START_TASKS_START_Msk;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Ensure your timer source isn&amp;#39;t ticking faster than the ADC required per run. For 2 channels with 20us and 5us TACQ...&lt;br /&gt;&lt;br /&gt;// ADC takes 2us per channel, 4us + 20us + 5us = 29us&lt;br /&gt;// 32768Hz = 30us&lt;br /&gt;// 8192Hz = 122us&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>RE: ADC Sampling Synchronization using Timer and PPI</title><link>https://devzone.nordicsemi.com/thread/555167?ContentTypeID=1</link><pubDate>Mon, 24 Nov 2025 09:49:44 GMT</pubDate><guid isPermaLink="false">137ad170-7792-4731-bb38-c0d22fbe4515:f8cc28b7-0c20-4ec7-8d7b-5fcc2e26c9d2</guid><dc:creator>Kenneth</dc:creator><description>&lt;p&gt;Hi,&lt;/p&gt;
&lt;p&gt;Sorry, but not sure I have anything more to suggest here. I guess an option might be to consider the nRF54L-series instead, since we support a bare metal option there:&lt;br /&gt;&lt;a href="https://www.nordicsemi.com/Products/Development-software/nRF-Connect-SDK/Bare-Metal-option-for-nRF54L-Series?lang=en#infotabs"&gt;https://www.nordicsemi.com/Products/Development-software/nRF-Connect-SDK/Bare-Metal-option-for-nRF54L-Series?lang=en#infotabs&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Kenneth&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>RE: ADC Sampling Synchronization using Timer and PPI</title><link>https://devzone.nordicsemi.com/thread/555114?ContentTypeID=1</link><pubDate>Sun, 23 Nov 2025 02:32:52 GMT</pubDate><guid isPermaLink="false">137ad170-7792-4731-bb38-c0d22fbe4515:4b6f2f49-3137-4405-8467-a547869f1afc</guid><dc:creator>SteveHx</dc:creator><description>&lt;p&gt;Standing by for any guidance you can offer......&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>RE: ADC Sampling Synchronization using Timer and PPI</title><link>https://devzone.nordicsemi.com/thread/554852?ContentTypeID=1</link><pubDate>Wed, 19 Nov 2025 23:25:41 GMT</pubDate><guid isPermaLink="false">137ad170-7792-4731-bb38-c0d22fbe4515:087f2d85-f60c-45c7-844a-164f9c3b2e49</guid><dc:creator>SteveHx</dc:creator><description>&lt;p&gt;Update: Using RESULTSDONE to trigger SAMPLE, it samples ever so slightly faster than 100 KSPS. And the problem behavior doesn&amp;#39;t change. At random, about one buffer in a few tens of buffers is misaligned by one int16.&lt;/p&gt;
&lt;p&gt;I tried another thought: Slow the timer by a factor of 4, and set up all 8 channels so they alternate between the two real sets of pins. That is, Ch 0, 2, 4, and 6 all see the same set of pins (the original Ch 0), and Ch 1, 3, 5, and 7 sample the original Ch 1. It appears I understand the SAADC because this gives exactly the same result - except that now the misaligned buffers appear to happen more frequently, maybe one in half a dozen. Any ideas as to why that would be, and what it points to as the underlying cause?&lt;/p&gt;
&lt;p&gt;Can you point me to a simple example of just the code that sets up the SAADC to capture a buffer of N results (4096 in my case) on two channels that alternate, at the max 200 KSPS (100 KSPS per channel) rate? I don&amp;#39;t need a whole application, just want to see the approved sequence to set things up.&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>RE: ADC Sampling Synchronization using Timer and PPI</title><link>https://devzone.nordicsemi.com/thread/554851?ContentTypeID=1</link><pubDate>Wed, 19 Nov 2025 23:05:27 GMT</pubDate><guid isPermaLink="false">137ad170-7792-4731-bb38-c0d22fbe4515:7752d513-e7a3-4e52-8111-8118a6539833</guid><dc:creator>SteveHx</dc:creator><description>&lt;p&gt;That sounds reasonable, so much so that I explored it some time ago (and again just now). By changing the sample rate to 50 KSPS I guarantee that the ADC is ready when the timer calls for the next sample. Same result. And anyway, with your explanation, the mismatch would usually happen in the middle of a buffer. I never see that - it&amp;#39;s always a full buffer off by one int16, if it&amp;#39;s off at all (one in a few tens of buffers).&lt;/p&gt;
&lt;p&gt;I don&amp;#39;t see any SHORTS register for the SAADC, so I&amp;#39;ll next try using the PPI to connect the EVENTS_RESULTDONE to TASKS_SAMPLE. In theory that will run the ADC at the fastest possible rate, which should be 100 KSPS with two channels enabled. Will be interesting to compare and see how accurate that is, and also whether it solves the problem.&lt;/p&gt;
&lt;p&gt;I&amp;#39;m not hopeful, though. Right now, after the first two passes, most of the ADC setup is skipped on each future pass. Only the EasyDMA registers are set up on each pass after the 2nd, along with the timer and PPI.&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>RE: ADC Sampling Synchronization using Timer and PPI</title><link>https://devzone.nordicsemi.com/thread/554845?ContentTypeID=1</link><pubDate>Wed, 19 Nov 2025 21:40:08 GMT</pubDate><guid isPermaLink="false">137ad170-7792-4731-bb38-c0d22fbe4515:c0161713-33a0-404d-a892-c8372424782d</guid><dc:creator>Kenneth</dc:creator><description>&lt;p&gt;My best suggestion is that the timer is like triggering continuous timer compare events asynchronous to the state and configuration of the SAADC at any given time, so likely the CPU is updating buffer pointer, triggering start task or something related at a &amp;quot;bad&amp;quot; time compared to the timer triggering. Do you see the same if you simply disable the timer compare ppi channel to the saadc sample task when you do this &amp;quot;job&amp;quot;?&lt;/p&gt;
&lt;p&gt;Kenneth&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>RE: ADC Sampling Synchronization using Timer and PPI</title><link>https://devzone.nordicsemi.com/thread/554768?ContentTypeID=1</link><pubDate>Wed, 19 Nov 2025 11:03:43 GMT</pubDate><guid isPermaLink="false">137ad170-7792-4731-bb38-c0d22fbe4515:edf9602d-73ff-4f71-add6-b856592b4405</guid><dc:creator>SteveHx</dc:creator><description>&lt;p&gt;Almost the same, certainly the same random (perhaps &amp;lt; 10% of the time) swapping of channels. A very clear effect is that with 5uSec acquisition time + 2uSec conversion time, x 2 channels = 14uSec, it&amp;#39;s not ready at the 10uSec point when the timer tries to trigger the next sample, so it waits for the next tick and gets a 50 KSPS sampling rate instead of the advertised 100 KSPS.&lt;/p&gt;
&lt;p&gt;I also tried going out to 10uSec acquisition time, and got the expected 33.3KSPS sampling rate, no visible change in the rate of channel swapping.&lt;/p&gt;
&lt;p&gt;Then I went back to the original 3uSec acquisition time, but changed the timer to trigger at 90KSPS. Yep, the sampling slows down - and the channel swapping stays at the same rate.&lt;/p&gt;
&lt;p&gt;Getting desperate here...hmmm, maybe it&amp;#39;s some noisy node in this particular processor. Swapped another processor (different board) and the result was.....exactly the same.&lt;/p&gt;
&lt;p&gt;Clearly my code is doing something that causes a race condition, doesn&amp;#39;t quite meet setup or hold times, something like that. But what??&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>RE: ADC Sampling Synchronization using Timer and PPI</title><link>https://devzone.nordicsemi.com/thread/554744?ContentTypeID=1</link><pubDate>Wed, 19 Nov 2025 08:41:12 GMT</pubDate><guid isPermaLink="false">137ad170-7792-4731-bb38-c0d22fbe4515:ac9085de-e37b-44f8-9552-4b6501099e10</guid><dc:creator>Kenneth</dc:creator><description>&lt;p&gt;Strange indeed.&lt;/p&gt;
&lt;p&gt;If you try 5us acquisition time instead, do you see the same then?&lt;/p&gt;
&lt;p&gt;Kenneth&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>RE: ADC Sampling Synchronization using Timer and PPI</title><link>https://devzone.nordicsemi.com/thread/554728?ContentTypeID=1</link><pubDate>Wed, 19 Nov 2025 00:40:17 GMT</pubDate><guid isPermaLink="false">137ad170-7792-4731-bb38-c0d22fbe4515:a6b04fc1-ef4a-4ce0-b768-59718b8311a2</guid><dc:creator>SteveHx</dc:creator><description>&lt;p&gt;I now have it more stable, still not quite there. I see from ten to several tens of good captures, then one capture with the channels swapped (or equivalently, misaligned in memory by 2 bytes). I have tried moving a lot of code around, and in particular made sure that nothing can get triggered while I&amp;#39;m setting things up. I also set up most of the SAADC registers only on the first two passes, or if something is changed by a user command (the user can change the gain settings etc.) I&amp;#39;ve now trimmed down the code to the basics, without a lot of the redundant stops and starts that I tried to no effect. Here&amp;#39;s the code I&amp;#39;m currently running - results are close but not quite good enough.&lt;/p&gt;
&lt;p&gt;&lt;span style="font-family:courier new, courier;"&gt;void StartAdcFor2Ch (PingPongT Which, RangeT Range1, RangeT Range2)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new, courier;"&gt;{&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new, courier;"&gt;#define AdcPpiChan 12&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new, courier;"&gt;#define AdcTimer NRF_TIMER4&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new, courier;"&gt;// Make sure we don&amp;#39;t get any premature triggers&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new, courier;"&gt;NRF_SAADC -&amp;gt; TASKS_STOP = 1;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new, courier;"&gt;for (int i = 10000000; i; --i) if (NRF_SAADC -&amp;gt; EVENTS_STOPPED) break;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new, courier;"&gt;NRF_PPI -&amp;gt; CHENCLR = 1 &amp;lt;&amp;lt; AdcPpiChan;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new, courier;"&gt;AdcTimer -&amp;gt; TASKS_STOP = 1;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new, courier;"&gt;AdcTimer -&amp;gt; TASKS_CLEAR = 1;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new, courier;"&gt;AdcTimer -&amp;gt; EVENTS_COMPARE [0] = 0;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new, courier;"&gt;// Set up EasyDMA&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new, courier;"&gt;NRF_SAADC -&amp;gt; ENABLE = 1;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new, courier;"&gt;NRF_SAADC -&amp;gt; RESULT.MAXCNT = sizeof Wave [Which] / sizeof (uint16_t);&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new, courier;"&gt;NRF_SAADC -&amp;gt; RESULT.PTR = (uint32_t) &amp;amp;(Wave [Which]);&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new, courier;"&gt;if (AlreadyConfigured &amp;lt; 2)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new, courier;"&gt;&amp;nbsp; &amp;nbsp;{&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new, courier;"&gt;&amp;nbsp; &amp;nbsp;// Build the parts of WaveInfo that only we know about&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new, courier;"&gt;&amp;nbsp; &amp;nbsp;WaveInfo.uSecPerPoint = 10;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new, courier;"&gt;&amp;nbsp; &amp;nbsp;WaveInfo.uVPerCount [0] = 1000;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new, courier;"&gt;&amp;nbsp; &amp;nbsp;WaveInfo.uVPerCount [1] = 1000;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new, courier;"&gt;&amp;nbsp; &amp;nbsp;// Set ADC inputs back to reset defaults&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new, courier;"&gt;&amp;nbsp; &amp;nbsp;NRF_SAADC -&amp;gt; ENABLE = 0;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new, courier;"&gt;&amp;nbsp; &amp;nbsp;NRF_GPIO -&amp;gt; DIRCLR = 0b10100000000000000000000000010100; // All ADC pins to inputs&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new, courier;"&gt;&amp;nbsp; &amp;nbsp;//Note that P0.04 must be jumpered to P0.09 via a C28 pad&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new, courier;"&gt;&amp;nbsp; &amp;nbsp;for (int i = 0; i &amp;lt; 8; ++i)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new, courier;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; {&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new, courier;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; NRF_SAADC -&amp;gt; CH [i].PSELP = adcNoConnect;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new, courier;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; NRF_SAADC -&amp;gt; CH [i].PSELN = adcNoConnect;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new, courier;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; }&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new, courier;"&gt;&amp;nbsp; &amp;nbsp;NRF_SAADC -&amp;gt; ENABLE = 1;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new, courier;"&gt;&amp;nbsp; &amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new, courier;"&gt;&amp;nbsp; &amp;nbsp;// Set up 2 channels to capture samples at 100 KSPS&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new, courier;"&gt;&amp;nbsp; &amp;nbsp;NRF_SAADC -&amp;gt; ENABLE = 0;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new, courier;"&gt;&amp;nbsp; &amp;nbsp;NRF_SAADC -&amp;gt; CH [1].PSELP = adcCh1P;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new, courier;"&gt;&amp;nbsp; &amp;nbsp;NRF_SAADC -&amp;gt; CH [1].PSELN = adcCh1M;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new, courier;"&gt;&amp;nbsp; &amp;nbsp;NRF_SAADC -&amp;gt; CH [1].CONFIG = (Range1 &amp;lt;&amp;lt; 8) | (1 &amp;lt;&amp;lt; 20); // nonburst mode, diffierential, 3 uSec, internal 600 mV ref, specified gain, no pullups&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new, courier;"&gt;&amp;nbsp; &amp;nbsp;NRF_SAADC -&amp;gt; CH [5].PSELP = adcCh2P;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new, courier;"&gt;&amp;nbsp; &amp;nbsp;NRF_SAADC -&amp;gt; CH [5].PSELN = adcCh2M;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new, courier;"&gt;&amp;nbsp; &amp;nbsp;NRF_SAADC -&amp;gt; CH [5].CONFIG = (Range2 &amp;lt;&amp;lt; 8) | (1 &amp;lt;&amp;lt; 20); // nonburst mode, diffierential, 3 uSec, internal 600 mV ref, specified gain, no pullups&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new, courier;"&gt;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new, courier;"&gt;&amp;nbsp; &amp;nbsp;// Set up SAADC for 12 bits, 2 channels, rate determined by AdcTimer&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new, courier;"&gt;&amp;nbsp; &amp;nbsp;NRF_SAADC -&amp;gt; OVERSAMPLE = 0;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new, courier;"&gt;&amp;nbsp; &amp;nbsp;NRF_SAADC -&amp;gt; RESOLUTION = 2; // 12 bits&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new, courier;"&gt;&amp;nbsp; &amp;nbsp;NRF_SAADC -&amp;gt; SAMPLERATE = 0; // Cannot use internal timer for multiple channels&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new, courier;"&gt;&amp;nbsp; &amp;nbsp;NRF_SAADC -&amp;gt; EVENTS_DONE = 0;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new, courier;"&gt;&amp;nbsp; &amp;nbsp;NRF_SAADC -&amp;gt; EVENTS_STARTED = 0;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new, courier;"&gt;&amp;nbsp; &amp;nbsp;NRF_SAADC -&amp;gt; EVENTS_END = 0;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new, courier;"&gt;&amp;nbsp; &amp;nbsp;NRF_SAADC -&amp;gt; EVENTS_STOPPED = 0;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new, courier;"&gt;&amp;nbsp; &amp;nbsp;NRF_SAADC -&amp;gt; EVENTS_RESULTDONE = 0;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new, courier;"&gt;&amp;nbsp; &amp;nbsp;NRF_SAADC -&amp;gt; ENABLE = 1;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new, courier;"&gt;&amp;nbsp; &amp;nbsp;// Set up AdcTimer to trigger sampling (multiple channel sampling cannot use internal timer)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new, courier;"&gt;&amp;nbsp; &amp;nbsp;AdcTimer -&amp;gt; SHORTS = 1; // auto restart on CC[0]&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new, courier;"&gt;&amp;nbsp; &amp;nbsp;AdcTimer -&amp;gt; TASKS_CLEAR;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new, courier;"&gt;&amp;nbsp; &amp;nbsp;AdcTimer -&amp;gt; MODE = 0; // Timer&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new, courier;"&gt;&amp;nbsp; &amp;nbsp;AdcTimer -&amp;gt; BITMODE = 3; // 32 bit timer&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new, courier;"&gt;&amp;nbsp; &amp;nbsp;AdcTimer -&amp;gt; PRESCALER = 4; // /16 prescale&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new, courier;"&gt;&amp;nbsp; &amp;nbsp;AdcTimer -&amp;gt; CC [0] = 16000000 / 100000 / (1 &amp;lt;&amp;lt; AdcTimer -&amp;gt; PRESCALER);&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new, courier;"&gt;&amp;nbsp; &amp;nbsp;++AlreadyConfigured;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new, courier;"&gt;&amp;nbsp; &amp;nbsp;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new, courier;"&gt;AdcTimer -&amp;gt; EVENTS_COMPARE [0] = 0;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new, courier;"&gt;NRF_SAADC -&amp;gt; TASKS_START = 1;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new, courier;"&gt;for (int i = 10000000; i; --i) if (NRF_SAADC -&amp;gt; EVENTS_STARTED) break;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new, courier;"&gt;// Connect timer output to ADC sampler via PPI&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new, courier;"&gt;NRF_PPI -&amp;gt; CH [AdcPpiChan].EEP = (uint32_t) &amp;amp;(AdcTimer -&amp;gt; EVENTS_COMPARE [0]);&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new, courier;"&gt;NRF_PPI -&amp;gt; CH [AdcPpiChan].TEP = (uint32_t) &amp;amp;(NRF_SAADC -&amp;gt; TASKS_SAMPLE);&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new, courier;"&gt;NRF_PPI -&amp;gt; CHENSET = 1 &amp;lt;&amp;lt; AdcPpiChan;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new, courier;"&gt;// Ready to run, turn it loose!&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new, courier;"&gt;AdcTimer -&amp;gt; TASKS_CLEAR;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new, courier;"&gt;AdcTimer -&amp;gt; TASKS_START = 1;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new, courier;"&gt;}&lt;/span&gt;&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>RE: ADC Sampling Synchronization using Timer and PPI</title><link>https://devzone.nordicsemi.com/thread/554723?ContentTypeID=1</link><pubDate>Tue, 18 Nov 2025 23:00:37 GMT</pubDate><guid isPermaLink="false">137ad170-7792-4731-bb38-c0d22fbe4515:a467a889-7fc2-4675-acd1-6d29c5bb9547</guid><dc:creator>SteveHx</dc:creator><description>&lt;p&gt;I&amp;#39;m not sure I understand what you&amp;#39;re suggesting. I looked at the text around that figure and I did see that the preferred way is to set MAXCNT first and then PTR. At first that looked like a solution, because I got lucky and the first half-dozen or so captures worked correctly. But then it went back to randomly swapping the two channels.&lt;/p&gt;
&lt;p&gt;I don&amp;#39;t see where I would put the PPI link you suggest. My code fully sets up one buffer, waits for that capture to complete, then sets up the other buffer (Wave [0 or 1]) and starts that running while transmitting the previous capture. I have confirmed that the corruption is happening in the ADC capture and not in the transmission, by inserting flag values in the buffer after the capture and before transmitting.&lt;/p&gt;
&lt;p&gt;I have now confirmed that the RESULT.AMOUNT register shows 8192 values (4K samples, two channels) every time, whether the channels are swapped or not.&lt;/p&gt;
&lt;p&gt;I&amp;#39;m nearly certain there&amp;#39;s some kind of race condition that I&amp;#39;m setting up, that the documentation doesn&amp;#39;t specify, but I can&amp;#39;t figure out where it would be. I configure all the ADC, Timer, and PPI registers with all of them stopped, then enable and start the ADC, then the PPi, and finally the timer. Is that not the correct sequence?&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>RE: ADC Sampling Synchronization using Timer and PPI</title><link>https://devzone.nordicsemi.com/thread/554720?ContentTypeID=1</link><pubDate>Tue, 18 Nov 2025 21:01:06 GMT</pubDate><guid isPermaLink="false">137ad170-7792-4731-bb38-c0d22fbe4515:05017b59-c036-48de-ba57-4c71212a8c55</guid><dc:creator>Kenneth</dc:creator><description>&lt;p&gt;Do you also have a PPI channel between SAADC END-&amp;gt;START event to ensure it will start with next buffer when the first is full? Looking at figure 4 it may indicate this is needed.&lt;/p&gt;
&lt;p&gt;Kenneth&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>RE: ADC Sampling Synchronization using Timer and PPI</title><link>https://devzone.nordicsemi.com/thread/554718?ContentTypeID=1</link><pubDate>Tue, 18 Nov 2025 20:26:02 GMT</pubDate><guid isPermaLink="false">137ad170-7792-4731-bb38-c0d22fbe4515:281ef118-5b23-4127-a3a3-a78263055571</guid><dc:creator>SteveHx</dc:creator><description>&lt;p&gt;I agree that it *should*. I&amp;#39;ve re-read that section with close attention to detail, at least a hundred times (I&amp;#39;m not exaggerating!) over the past couple of weeks, finding no help. Is there a simple example code somewhere that shows the correct sequence for setting up the registers?&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>RE: ADC Sampling Synchronization using Timer and PPI</title><link>https://devzone.nordicsemi.com/thread/554657?ContentTypeID=1</link><pubDate>Tue, 18 Nov 2025 09:36:01 GMT</pubDate><guid isPermaLink="false">137ad170-7792-4731-bb38-c0d22fbe4515:e0e253c6-b3d2-4da0-92b0-33ea4b92679c</guid><dc:creator>Kenneth</dc:creator><description>&lt;p&gt;Hello again,&lt;/p&gt;
&lt;p&gt;I don&amp;#39;t remember all the small details at hand, but the peripheral chapter covering SAADC should cover what is needed to know (e.g. including double buffering of buffer and figures showing tasks and events):&amp;nbsp;&lt;br /&gt;&lt;a href="https://docs.nordicsemi.com/bundle/ps_nrf52840/page/saadc.html"&gt;https://docs.nordicsemi.com/bundle/ps_nrf52840/page/saadc.html&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Kenneth&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>RE: ADC Sampling Synchronization using Timer and PPI</title><link>https://devzone.nordicsemi.com/thread/554605?ContentTypeID=1</link><pubDate>Mon, 17 Nov 2025 23:28:30 GMT</pubDate><guid isPermaLink="false">137ad170-7792-4731-bb38-c0d22fbe4515:58d6af92-8461-4c76-bffa-8b18538afd6f</guid><dc:creator>SteveHx</dc:creator><description>&lt;p&gt;Another finding that may or may not point the way: While the SAADC is running, I watch for NRF_SAADC -&amp;gt; EVENTS_END to determine when the buffer is ready for use. If I do NRF_SAADC -&amp;gt; TASKS_STOP = 1; at that time, the buffer is truncated. ??? Doesn&amp;#39;t EVENTS_END indicate that the buffer is complete?&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>RE: ADC Sampling Synchronization using Timer and PPI</title><link>https://devzone.nordicsemi.com/thread/554600?ContentTypeID=1</link><pubDate>Mon, 17 Nov 2025 20:46:18 GMT</pubDate><guid isPermaLink="false">137ad170-7792-4731-bb38-c0d22fbe4515:5d73b134-9782-4b65-9ae6-17daae795a1e</guid><dc:creator>SteveHx</dc:creator><description>&lt;p&gt;I have tried a number of things, including moving the sequence around, enuring sections are clearly stopped during programming, etc. It appears that RESULT.AMOUNT is only updated during a runtime (TASKS_START) and the value can only be read after a TASKS_STOP, so I have added routine ReadResAmt() to handle the housekeeping. I find that most of the readings are 0, occasionally the first reading [0] is 1, but that does not coincide with the captures that are misaligned. Before I tried doing a TASKS_STOP and TASKS_START I was reliably seeing 8192 for all reads, which would be correct for the previous complete capture (4096 sets of two channels).&lt;/p&gt;
&lt;p&gt;I find that reducing the sample rate to 10 KSPS cuts down the occurrence of the misaligned data to about 1 in 50 times. Setting it to 25 KSPS gives misaligned data around one in a couple dozen, and 100 KSPS gives misaligned data about 1 in 4 times. This seems to point to some kind of race condition, but I can&amp;#39;t find it.&lt;/p&gt;
&lt;p&gt;I also tried moving more of the clock divider into the Timer prescaler, which had no measurable effect.&lt;/p&gt;
&lt;p&gt;My code is below. I&amp;#39;m at my wit&amp;#39;s end as to why this doesn&amp;#39;t reliably start EasyDMA capture into the designated buffer, without an extra 16-bit value causing a misalignment.&lt;/p&gt;
&lt;p&gt;uint32_t ReadResAmt (void)&lt;br /&gt;{&lt;br /&gt;uint32_t Result;&lt;br /&gt;NRF_SAADC -&amp;gt; ENABLE = 1;&lt;br /&gt;NRF_SAADC -&amp;gt; TASKS_START = 1;&lt;br /&gt;for (int i = 1000000; i; --i) if (NRF_SAADC -&amp;gt; EVENTS_STARTED) break;&lt;br /&gt;NRF_SAADC -&amp;gt; TASKS_STOP = 1;&lt;br /&gt;for (int i = 1000000; i; --i) if (NRF_SAADC -&amp;gt; EVENTS_STOPPED) break;&lt;br /&gt;Result = NRF_SAADC -&amp;gt; RESULT.AMOUNT;&lt;br /&gt;NRF_SAADC -&amp;gt; ENABLE = 0;&lt;br /&gt;return Result;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;void StartAdcFor2Ch (PingPongT Which, RangeT Range1, RangeT Range2)&lt;br /&gt;{&lt;br /&gt;#define AdcPpiChan 11&lt;br /&gt;#define AdcTimer NRF_TIMER3&lt;br /&gt;&lt;br /&gt;// Make sure we don&amp;#39;t get any premature triggers&lt;br /&gt;NRF_SAADC -&amp;gt; TASKS_STOP = 1;&lt;br /&gt;NRF_PPI -&amp;gt; CHENCLR = 1 &amp;lt;&amp;lt; AdcPpiChan;&lt;br /&gt;AdcTimer -&amp;gt; TASKS_STOP = 1;&lt;br /&gt;AdcTimer -&amp;gt; TASKS_CLEAR = 1;&lt;br /&gt;AdcTimer -&amp;gt; EVENTS_COMPARE [0] = 0;&lt;br /&gt;&lt;br /&gt;// Build the parts of WaveInfo that only we know about&lt;br /&gt;WaveInfo.uSecPerPoint = 10;&lt;br /&gt;WaveInfo.uVPerCount [0] = 1000;&lt;br /&gt;WaveInfo.uVPerCount [1] = 1000;&lt;br /&gt;&lt;br /&gt;// Set ADC inputs back to reset defaults&lt;br /&gt;NRF_SAADC -&amp;gt; TASKS_STOP = 1;&lt;br /&gt;for (int i = 1000000; i; --i) if (NRF_SAADC -&amp;gt; EVENTS_STOPPED) break;&lt;br /&gt;NRF_SAADC -&amp;gt; ENABLE = 0;&lt;br /&gt;WaveInfo.ResAmt [0] = ReadResAmt ();&lt;br /&gt;NRF_GPIO -&amp;gt; DIRCLR = 0b10100000000000000000000000010100; // All ADC pins to inputs&lt;br /&gt;//Note that P0.04 must be jumpered to P0.09 via a C28 pad&lt;br /&gt;for (int i = 0; i &amp;lt; 8; ++i)&lt;br /&gt;&amp;nbsp; &amp;nbsp;{&lt;br /&gt;&amp;nbsp; &amp;nbsp;NRF_SAADC -&amp;gt; CH [i].PSELP = adcNoConnect;&lt;br /&gt;&amp;nbsp; &amp;nbsp;NRF_SAADC -&amp;gt; CH [i].PSELN = adcNoConnect;&lt;br /&gt;&amp;nbsp; &amp;nbsp;}&lt;br /&gt;&lt;br /&gt;// Set up 2 channels to capture samples at 100 KSPS&lt;br /&gt;NRF_SAADC -&amp;gt; CH [0].PSELP = adcCh1P;&lt;br /&gt;NRF_SAADC -&amp;gt; CH [0].PSELN = adcCh1M;&lt;br /&gt;NRF_SAADC -&amp;gt; CH [0].CONFIG = (Range1 &amp;lt;&amp;lt; 8) | (1 &amp;lt;&amp;lt; 20); // nonburst mode, diffierential, 3 uSec, internal 600 mV ref, specified gain, no pullups&lt;br /&gt;NRF_SAADC -&amp;gt; CH [1].PSELP = adcCh2P;&lt;br /&gt;NRF_SAADC -&amp;gt; CH [1].PSELN = adcCh2M;&lt;br /&gt;NRF_SAADC -&amp;gt; CH [1].CONFIG = (Range2 &amp;lt;&amp;lt; 8) | (1 &amp;lt;&amp;lt; 20); // nonburst mode, diffierential, 3 uSec, internal 600 mV ref, specified gain, no pullups&lt;br /&gt;&lt;br /&gt;// Set up SAADC for 12 bits, 2 channels, rate determined by AdcTimer&lt;br /&gt;NRF_SAADC -&amp;gt; OVERSAMPLE = 0;&lt;br /&gt;NRF_SAADC -&amp;gt; RESOLUTION = 2; // 12 bits&lt;br /&gt;NRF_SAADC -&amp;gt; SAMPLERATE = 0; // Cannot use internal timer for multiple channels&lt;br /&gt;NRF_SAADC -&amp;gt; EVENTS_DONE = 0;&lt;br /&gt;NRF_SAADC -&amp;gt; EVENTS_STARTED = 0;&lt;br /&gt;NRF_SAADC -&amp;gt; EVENTS_END = 0;&lt;br /&gt;NRF_SAADC -&amp;gt; EVENTS_STOPPED = 0;&lt;br /&gt;NRF_SAADC -&amp;gt; EVENTS_RESULTDONE = 0;&lt;br /&gt;WaveInfo .ResAmt [1] = ReadResAmt ();&lt;br /&gt;&lt;br /&gt;// Set up AdcTimer to trigger sampling (multiple channel sampling cannot use internal timer)&lt;br /&gt;AdcTimer -&amp;gt; SHORTS = 1; // auto restart on CC[0]&lt;br /&gt;AdcTimer -&amp;gt; TASKS_CLEAR;&lt;br /&gt;AdcTimer -&amp;gt; MODE = 0; // Timer&lt;br /&gt;AdcTimer -&amp;gt; BITMODE = 3; // 32 bit timer&lt;br /&gt;AdcTimer -&amp;gt; PRESCALER = 4; // /16 prescale&lt;br /&gt;AdcTimer -&amp;gt; CC [0] = 16000000 / 100000 / (1 &amp;lt;&amp;lt; AdcTimer -&amp;gt; PRESCALER);&lt;br /&gt;WaveInfo .ResAmt [2] = ReadResAmt ();&lt;br /&gt;&lt;br /&gt;// Connect timer output to ADC sampler via PPI&lt;br /&gt;NRF_PPI -&amp;gt; CH [AdcPpiChan].EEP = (uint32_t) &amp;amp;(AdcTimer -&amp;gt; EVENTS_COMPARE [0]);&lt;br /&gt;NRF_PPI -&amp;gt; CH [AdcPpiChan].TEP = (uint32_t) &amp;amp;(NRF_SAADC -&amp;gt; TASKS_SAMPLE);&lt;br /&gt;NRF_PPI -&amp;gt; CHENSET = 1 &amp;lt;&amp;lt; AdcPpiChan;&lt;br /&gt;WaveInfo .ResAmt [3] = ReadResAmt ();&lt;br /&gt;&lt;br /&gt;// Set up EasyDMA, twice to ensure double-buffering isn&amp;#39;t a problem&lt;br /&gt;NRF_SAADC -&amp;gt; RESULT.PTR = (uint32_t) &amp;amp;(Wave [Which]);&lt;br /&gt;NRF_SAADC -&amp;gt; RESULT.MAXCNT =&amp;nbsp; &amp;nbsp; sizeof Wave [Which] / sizeof (uint16_t);&lt;br /&gt;NRF_SAADC -&amp;gt; ENABLE = 1;&lt;br /&gt;NRF_SAADC -&amp;gt; TASKS_START = 1;&lt;br /&gt;for (int i = 1000000; i; --i) if (NRF_SAADC -&amp;gt; EVENTS_STARTED) break;&lt;br /&gt;NRF_SAADC -&amp;gt; TASKS_STOP = 1;&lt;br /&gt;for (int i = 1000000; i; --i) if (NRF_SAADC -&amp;gt; EVENTS_STOPPED) break;&lt;br /&gt;&lt;br /&gt;NRF_SAADC -&amp;gt; RESULT.PTR = (uint32_t) &amp;amp;(Wave [Which]);&lt;br /&gt;NRF_SAADC -&amp;gt; RESULT.MAXCNT =&amp;nbsp; &amp;nbsp; sizeof Wave [Which] / sizeof (uint16_t);&lt;br /&gt;&lt;br /&gt;// Ready to run, turn it loose!&lt;br /&gt;NRF_SAADC -&amp;gt; ENABLE = 1;&lt;br /&gt;NRF_SAADC -&amp;gt; TASKS_START = 1;&lt;br /&gt;for (int i = 1000000; i; --i) if (NRF_SAADC -&amp;gt; EVENTS_STARTED) break;&lt;br /&gt;AdcTimer -&amp;gt; TASKS_CLEAR;&lt;br /&gt;AdcTimer -&amp;gt; TASKS_START = 1;&lt;br /&gt;}&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item></channel></rss>