<?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 Infuriating Behaviour With Multi-channel Scan Using PPI to RTC</title><link>https://devzone.nordicsemi.com/f/nordic-q-a/105239/adc-infuriating-behaviour-with-multi-channel-scan-using-ppi-to-rtc</link><description>Been on all day at this and ready to smash something! :) I need to do three &amp;#39;steps&amp;#39; or &amp;#39;tasks&amp;#39; which are: 1. read 5 channels 2. read 3 channels, which are the same as above minus two (there&amp;#39;s a pin aux) 3. read 2 channels repeatedly filling up one of</description><dc:language>en-US</dc:language><generator>Telligent Community 13</generator><lastBuildDate>Tue, 14 Nov 2023 15:20:01 GMT</lastBuildDate><atom:link rel="self" type="application/rss+xml" href="https://devzone.nordicsemi.com/f/nordic-q-a/105239/adc-infuriating-behaviour-with-multi-channel-scan-using-ppi-to-rtc" /><item><title>RE: ADC Infuriating Behaviour With Multi-channel Scan Using PPI to RTC</title><link>https://devzone.nordicsemi.com/thread/455596?ContentTypeID=1</link><pubDate>Tue, 14 Nov 2023 15:20:01 GMT</pubDate><guid isPermaLink="false">137ad170-7792-4731-bb38-c0d22fbe4515:e4905fbf-fe6c-435f-bb7a-f3c8181a8b80</guid><dc:creator>Karl Ylvisaker</dc:creator><description>&lt;p&gt;Hello,&lt;/p&gt;
[quote user="snoopy20"]Datasheet - &amp;quot;Scan mode and oversampling cannot be combined. &amp;quot;&lt;br /&gt;Outcome - Not true, it works. It may not work for repeated scans (I haven&amp;#39;t tested) but for a single loop of multiple channels it works fine.[/quote]
&lt;p&gt;Are you here also using BURST?&lt;br /&gt;If not, then the samples will be averaged over the full scan (across channels), which will produce samples, but they will be averaged over different inputs.&lt;br /&gt;When BURST is used it will collect all the samples in one channel, average, and then move on to the next, which is why this combination works as expected.&lt;/p&gt;
[quote user="snoopy20"]The 5us, I was looking at #150 within v1 by mistake. I&amp;#39;m unlikely to have these chips in my inventory, likely v2 where it appears it was resolved.[/quote]
&lt;p&gt;Thank you for clarifying! :)&amp;nbsp;&lt;br /&gt;&lt;br /&gt;&lt;a href="https://devzone.nordicsemi.com/members/hmolesworth"&gt;hmolesworth&lt;/a&gt;&amp;nbsp;&lt;br /&gt;I concur that this&amp;nbsp;sounds like it could&amp;nbsp;be a likely scenario, but I unfortunately do not have&amp;nbsp;enough&amp;nbsp;prior knowledge of the easyDMA/AHB architecture to provide&amp;nbsp;much insight here unfortunately.&lt;br /&gt;&lt;br /&gt;Best regards,&lt;br /&gt;Karl&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>RE: ADC Infuriating Behaviour With Multi-channel Scan Using PPI to RTC</title><link>https://devzone.nordicsemi.com/thread/455203?ContentTypeID=1</link><pubDate>Sun, 12 Nov 2023 00:09:18 GMT</pubDate><guid isPermaLink="false">137ad170-7792-4731-bb38-c0d22fbe4515:6cb90c45-024d-45b3-bf52-2de6895ff4ca</guid><dc:creator>hmolesworth</dc:creator><description>&lt;p&gt;My thinking is a stalled DMA write has a write transaction which will complete but before doing so the DMA ptr hardware register is triggered to increment in the peripheral hardware (no DMA memory AHB or process APB interaction completion required for this increment, it&amp;#39;s just a hardware &amp;quot;counter&amp;quot; inside the peripheral) and writes the value at this updated memory address. This write completion triggers another hardware counter increment, so now there has been a&amp;nbsp;shift. There is no information on how this hardware pointer register (counter) works, though the likelihood is that it uses the 16MHz clock and a stalled AHB bus write may address the physical memory just before or just after the 16MHz clock edge triggers. Speculation, of course. Worst case two bytes may be written, one at the old required address and&amp;nbsp;&lt;span&gt;another (unwanted) value at the next address which in turns triggers a hardware counter address update; that&amp;#39;s even more speculative. The real issue is incorrect synchronisation between the 3 busses: cpu to peripheral via AHB2APB, peripheral DMA AHB to RAM which is shared by the higher priority cpu, and the peripheral innards which is where the DMA address memory pointer is incremented. Both cpu code and cpu data transactions can lead to a stall, so this stall is of an indeterminate period. I understand the Cortex-M7 has fixed this Cortex-M4 issue.&lt;/span&gt;&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>RE: ADC Infuriating Behaviour With Multi-channel Scan Using PPI to RTC</title><link>https://devzone.nordicsemi.com/thread/455193?ContentTypeID=1</link><pubDate>Sat, 11 Nov 2023 15:22:22 GMT</pubDate><guid isPermaLink="false">137ad170-7792-4731-bb38-c0d22fbe4515:de75c3cd-bada-4283-a251-5e1b856477e3</guid><dc:creator>snoopy20</dc:creator><description>&lt;p&gt;Cool, I&amp;#39;ll revert the code to doing calibration once per loop, thanks. I also read the temperature of the die so will likely add some skipping code at the same time.&lt;br /&gt;&lt;br /&gt;Other matters&lt;br /&gt;&lt;br /&gt;Datasheet - &amp;quot;Scan mode and oversampling cannot be combined. &amp;quot;&lt;br /&gt;Outcome - Not true, it works. It may not work for repeated scans (I haven&amp;#39;t tested) but for a single loop of multiple channels it works fine.&lt;br /&gt;&lt;br /&gt;The 5us, I was looking at #150 within v1 by mistake. I&amp;#39;m unlikely to have these chips in my inventory, likely v2 where it appears it was resolved. &lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>RE: ADC Infuriating Behaviour With Multi-channel Scan Using PPI to RTC</title><link>https://devzone.nordicsemi.com/thread/455103?ContentTypeID=1</link><pubDate>Fri, 10 Nov 2023 11:43:42 GMT</pubDate><guid isPermaLink="false">137ad170-7792-4731-bb38-c0d22fbe4515:be0ad5f3-6dc3-4131-97d0-86f435b9a956</guid><dc:creator>Karl Ylvisaker</dc:creator><description>&lt;p&gt;Hello again,&lt;br /&gt;&lt;br /&gt;A very useful discussion indeed! :)&amp;nbsp;&lt;/p&gt;
[quote user="snoopy20"]So my implementation was based on another post on this forum by a nordic engineer saying the calibration values are retained until power reset, which doesn&amp;#39;t appear to be the case according to Karl (probably because it wasn&amp;#39;t factoring in the errata code).[/quote]
&lt;p&gt;I dug deeper into this, and I stand corrected: You will fortunately&amp;nbsp;&lt;strong&gt;not&lt;/strong&gt; have to perform a new calibration each time the&amp;nbsp;&lt;a href="https://infocenter.nordicsemi.com/topic/errata_nRF52810_Rev3/ERR/nRF52810/Rev3/latest/anomaly_810_212.html"&gt;Errata 212&lt;/a&gt;&amp;nbsp;workaround is ran.&lt;br /&gt;&lt;br /&gt;Part of the workaround from the errata is to power cycle the peripheral - which means resetting all the internal registers, thus warranting a new calibration - but closer examination on my part has revealed that the previous calibration is actually carried over in the rest of the errata workaround.&lt;br /&gt;The easiest thing here is of course to abide by the Errata text and set up&amp;nbsp;&lt;em&gt;all&lt;/em&gt; registers again, but as it turns out for the specific offset calibration results this value will be carried over.&amp;nbsp;Apologies for the confusion here.&lt;/p&gt;
[quote user="hmolesworth"]Avoiding data shift can be improved by reducing the underlying cause (this affects SPIM as well). This example is for the nRF52810.[/quote][quote user="hmolesworth"]Reduce likelihood further[/quote]
&lt;p&gt;Could you clarify on what you mean when you say that you can &amp;#39;reduce likelihood&amp;#39; here?&lt;br /&gt;I agree with your analysis of the AHB priorities and the potential impact of the timing, but you should not see a buffer shift if you implement the workarounds (for each errata as they apply to your project) + &lt;a href="https://devzone.nordicsemi.com/f/nordic-q-a/20291/offset-in-saadc-samples-with-easy-dma-and-ble/79053"&gt;the PPI channel described by my colleague Jørgen in the post you referenced earlier&lt;/a&gt;, and so you should not need to take actions to &amp;#39;further reduce likelihood&amp;#39; - perhaps I am misunderstanding something here.&lt;/p&gt;
[quote user="snoopy20"]The errata says that a TASK_STOP should be used, this is not true as one of my posts above shows, however the delay it introduces may change the timing upon which easydma fails and perhaps gave the impression of such.[/quote]
&lt;p&gt;Which errata are you referring to here?&lt;br /&gt;Since we are discussing multiple ones it would be great if you could reference their number is possible.&lt;/p&gt;
[quote user="snoopy20"]Another errata says that 5us channels can&amp;#39;t be used with timer+PPI. This is untrue, I&amp;#39;m doing it and have never seen an issue.[/quote]
&lt;p&gt;Are you here referring to the&amp;nbsp;&lt;a href="https://infocenter.nordicsemi.com/topic/errata_nRF52810_Rev3/ERR/nRF52810/Rev3/latest/anomaly_810_212.html"&gt;Errata 212&lt;/a&gt;? If so, it only comes into effect when you are also switching from multiple channels to single channel input.&lt;br /&gt;If not, please specify which errata this is in relation to.&lt;/p&gt;
[quote user="snoopy20"]And in the datasheet it says burst can&amp;#39;t be combined with scan mode. This is not true, I do it just fine. But it may be the case that it can only be one sweep.[/quote]
&lt;p&gt;Could it be that you are here referring to that &amp;#39;scan + oversampling&amp;#39; should not be combined?&amp;nbsp; It is also true that you may in fact do this if you also enable BURST on all channels.&lt;br /&gt;As per the Product specification you can use scan + burst so long as burst is enabled on all channels.&lt;/p&gt;
&lt;p&gt;Best regards,&lt;br /&gt;Karl&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>RE: ADC Infuriating Behaviour With Multi-channel Scan Using PPI to RTC</title><link>https://devzone.nordicsemi.com/thread/455073?ContentTypeID=1</link><pubDate>Fri, 10 Nov 2023 09:06:30 GMT</pubDate><guid isPermaLink="false">137ad170-7792-4731-bb38-c0d22fbe4515:d4ddfa76-6486-499f-a3ff-6a251b4850b4</guid><dc:creator>snoopy20</dc:creator><description>&lt;p&gt;So my implementation was based on another post on this forum by a nordic engineer saying the calibration values are retained until power reset, which doesn&amp;#39;t appear to be the case according to Karl (probably because it wasn&amp;#39;t factoring in the errata code).&lt;br /&gt;&lt;br /&gt;This means I have to do calibration on every step, of which there are 4 in a continuous loop running at around 5Hz. I&amp;#39;m unsure if it&amp;#39;s worth the overhead.&lt;br /&gt;&lt;br /&gt;Regarding the buffer shift, the buffer always moves forwards not backwards, which I believe indicates a false signal (double or noise) not a missing one. The shift is also retained across all adc reconfiguration which indicates there&amp;#39;s state held somewhere.&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>RE: ADC Infuriating Behaviour With Multi-channel Scan Using PPI to RTC</title><link>https://devzone.nordicsemi.com/thread/454753?ContentTypeID=1</link><pubDate>Wed, 08 Nov 2023 16:26:37 GMT</pubDate><guid isPermaLink="false">137ad170-7792-4731-bb38-c0d22fbe4515:4f8b76b4-f699-4a61-ae36-cb7107a740b9</guid><dc:creator>hmolesworth</dc:creator><description>&lt;p&gt;Really useful discussion :-)&amp;nbsp; Adding to this note a couple of additional ways to improve the SAADC. Instead of calibration another way is to read the temperature and apply a correction based on that, simple to characterise to get a good curve fit and the sort of thing done frequently on (say) the 32kHz crystal for better timekeeping.&lt;/p&gt;
&lt;p&gt;Avoiding data shift can be improved by reducing the underlying cause (this affects SPIM as well). This example is for the nRF52810.&lt;/p&gt;
&lt;p&gt;&amp;quot;&lt;em&gt;EasyDMA is an AHB bus master similar to CPU and is connected to the AHB multilayer interconnect for direct access to Data RAM.&amp;nbsp;AHB multilayer enables parallel access paths between multiple masters and slaves in a system. Access is resolved using priorities.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;img style="max-height:240px;max-width:320px;" alt=" " src="https://devzone.nordicsemi.com/resized-image/__size/640x480/__key/communityserver-discussions-components-files/4/AHB_2D00_Bus_2D00_Layout.JPG" /&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Each bus master is connected to the slave devices using an interconnection matrix. The bus masters are assigned priorities. Priorities are used to resolve access when two (or more) bus masters request access to the same slave device. The following applies:&lt;/em&gt;&lt;br /&gt;&lt;em&gt;&amp;bull; If two (or more) bus masters request access to the same slave device, the master with the highest priority is granted the access first.&lt;/em&gt;&lt;br /&gt;&lt;em&gt;&amp;bull; Bus masters with lower priority are stalled until the higher priority master has completed its transaction.&lt;/em&gt;&lt;br /&gt;&lt;em&gt;&amp;bull; If the higher priority master pauses at any point during its transaction, the lower priority master in queue is temporarily granted access to the slave device until the higher priority master resumes its activity.&lt;/em&gt;&lt;br /&gt;&lt;em&gt;&amp;bull; Bus masters that have the same priority are mutually exclusive, thus cannot be used concurrently.&lt;/em&gt;&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="c_cpp"&gt;nRF52810 Priorities
Bus master name Description
CPU
SPIM0/SPIS0 Same priority and mutually exclusive
RADIO
CCM/ECB/AAR Same priority and mutually exclusive
SAADC
UARTE0
TWIM0/TWIS0 Same priority and mutually exclusive
PDM
PWM&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;CPU, RADIO and SPIM0 are likely to cause a stall on the SAADC DMA which can lead to a data shift; with a fixed streaming rate over DMA this is a problem. Reduce this likelihood by dedicating an AHB Bus Master-Slave to a single RAM segment reserved for the SAADC and low-usage cpu data.&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="c_cpp"&gt;READERBUFFER_SIZE 5
WRITERBUFFER_SIZE 6
uint8_t readerBuffer[READERBUFFER_SIZE] __at__ 0x20000000; &amp;lt;== choose dedicated RAM segment
uint8_t writerBuffer[WRITERBUFFER_SIZE] __at__ 0x20000005;
// Configuring the READER channel
MYPERIPHERAL-&amp;gt;READER.MAXCNT = READERBUFFER_SIZE;
MYPERIPHERAL-&amp;gt;READER.PTR = &amp;amp;readerBuffer;
// Configure the WRITER channel
MYPERIPHERAL-&amp;gt;WRITER.MAXCNT = WRITEERBUFFER_SIZE;
MYPERIPHERAL-&amp;gt;WRITER.PTR = &amp;amp;writerBuffer;&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;Reduce likelihood further by scheduling SAADC reads between RADIO and CPU and SPIM0 events (more tricky if streaming lots of ADC measurements).&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>RE: ADC Infuriating Behaviour With Multi-channel Scan Using PPI to RTC</title><link>https://devzone.nordicsemi.com/thread/454724?ContentTypeID=1</link><pubDate>Wed, 08 Nov 2023 14:43:40 GMT</pubDate><guid isPermaLink="false">137ad170-7792-4731-bb38-c0d22fbe4515:a3be3f56-2b3c-49f4-b850-35f64f6cf16d</guid><dc:creator>snoopy20</dc:creator><description>&lt;p&gt;There are three erratas for 810 adc at least:&lt;/p&gt;
&lt;p&gt;1. when using calibration a sample may be taken, so one should configure the channels and dma after calibration.&lt;br /&gt;2. when using calibration if a task_sample is invoked the reads will be garbage from that point forth. Really this is misconfiguration of running a timer and ppi event and not exactly errata per say.&lt;br /&gt;3. when re-configuring adc registers (config,dma) after a multi-scan (not just one pass) then easydma will eventually buffer shift.&lt;br /&gt;&lt;br /&gt;The errata says that a TASK_STOP should be used, this is not true as one of my posts above shows, however the delay it introduces may change the timing upon which easydma fails and perhaps gave the impression of such.&lt;br /&gt;&lt;br /&gt;I actually use the 810 in another project where the adc is pushed to it&amp;#39;s max computing real/apparent wattages for a generator. I don&amp;#39;t have to reset there because I don&amp;#39;t reconfigure the channels.&lt;br /&gt;&lt;br /&gt;Also...&lt;/p&gt;
&lt;p&gt;Another errata says that 5us channels can&amp;#39;t be used with timer+PPI. This is untrue, I&amp;#39;m doing it and have never seen an issue.&lt;br /&gt;&lt;br /&gt;And in the datasheet it says burst can&amp;#39;t be combined with scan mode. This is not true, I do it just fine. But it may be the case that it can only be one sweep.&lt;br /&gt;&lt;br /&gt;A.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>RE: ADC Infuriating Behaviour With Multi-channel Scan Using PPI to RTC</title><link>https://devzone.nordicsemi.com/thread/454715?ContentTypeID=1</link><pubDate>Wed, 08 Nov 2023 14:21:08 GMT</pubDate><guid isPermaLink="false">137ad170-7792-4731-bb38-c0d22fbe4515:603fbd3a-5ff2-4d3a-9890-89102c5d2243</guid><dc:creator>Karl Ylvisaker</dc:creator><description>[quote user="snoopy20"]It&amp;#39;s not an issue in this application and to be honest if it were I&amp;#39;d monitor the internal temperature of the die and skip the step if the delta isn&amp;#39;t high enough. There&amp;#39;s always a way![/quote]
&lt;p&gt;Yeah, if it had been an issue for you we&amp;#39;d have to turn some rocks to see what the possible solutions could be, but&amp;nbsp;I am glad to hear that this is not an issue! :)&amp;nbsp;&lt;/p&gt;
[quote user="snoopy20"]I do think however that some work is needed on the errata. For the 810 at least there are two around the calibration, which are the same thing, and as I&amp;#39;ve shown in this thread the errata applies at any time configuration changes between scans and not just calibration.[/quote]
&lt;p&gt;To me it looks like you are seeing the effects of &lt;a href="https://infocenter.nordicsemi.com/topic/errata_nRF52810_Rev3/ERR/nRF52810/Rev3/latest/anomaly_810_212.html"&gt;Errata 212&lt;/a&gt;&amp;nbsp;paired with the buffer-shift issue explained by my colleague Jørgen in the other thread, could you elaborate what you mean when you say &amp;#39;the errata applies at any time configuration changes between scans and not just calibration?&lt;br /&gt;&lt;br /&gt;I do agree that &lt;a href="https://infocenter.nordicsemi.com/topic/errata_nRF52810_Rev3/ERR/nRF52810/Rev3/latest/anomaly_810_237.html"&gt;Errata 237&lt;/a&gt;&amp;nbsp;and &lt;a href="https://infocenter.nordicsemi.com/topic/errata_nRF52810_Rev3/ERR/nRF52810/Rev3/latest/anomaly_810_252.html"&gt;Errata 252&lt;/a&gt;&amp;nbsp;perhaps should have been a single errata as I am not sure if their distinction is clear enough. I will check internally to see if what the background for this is.&lt;br /&gt;&lt;br /&gt;Best regards,&lt;br /&gt;Karl&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>RE: ADC Infuriating Behaviour With Multi-channel Scan Using PPI to RTC</title><link>https://devzone.nordicsemi.com/thread/454624?ContentTypeID=1</link><pubDate>Wed, 08 Nov 2023 09:59:27 GMT</pubDate><guid isPermaLink="false">137ad170-7792-4731-bb38-c0d22fbe4515:90ecf01c-d259-4a71-a723-6f4b0195652b</guid><dc:creator>snoopy20</dc:creator><description>&lt;p&gt;It&amp;#39;s not an issue in this application and to be honest if it were I&amp;#39;d monitor the internal temperature of the die and skip the step if the delta isn&amp;#39;t high enough. There&amp;#39;s always a way! &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;br /&gt;&lt;br /&gt;I do think however that some work is needed on the errata. For the 810 at least there are two around the calibration, which are the same thing, and as I&amp;#39;ve shown in this thread the errata applies at any time configuration changes between scans and not just calibration.&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>RE: ADC Infuriating Behaviour With Multi-channel Scan Using PPI to RTC</title><link>https://devzone.nordicsemi.com/thread/454613?ContentTypeID=1</link><pubDate>Wed, 08 Nov 2023 09:25:43 GMT</pubDate><guid isPermaLink="false">137ad170-7792-4731-bb38-c0d22fbe4515:3bd76c08-f9c5-49a7-8534-1ad9ed25e98e</guid><dc:creator>Karl Ylvisaker</dc:creator><description>&lt;p&gt;Hello A,&lt;br /&gt;&lt;br /&gt;I am glad to hear that it now functions as expected! :)&lt;br /&gt;I understand that it is not ideal to have to go through the calibration so often, but unfortunately it is no way around the reset of the SAADC peripheral between the configurations you are using in this case.&lt;br /&gt;&lt;br /&gt;Alternatively, if the repeated calibration is effecting the performance of other components in your system you &lt;em&gt;could&lt;/em&gt; also run some tests on the feasibility for your application &lt;em&gt;not&lt;/em&gt; to perform the offset calibration at all - as mentioned in the Product Specification the SAADC has a temperature dependent offset, but if what you are after in your application is the relative change in voltage over time, and the device is operating in a small temperature range - less than 10 degrees change in ambient temperature - it &lt;em&gt;could&lt;/em&gt; also be feasible for you to run without the calibration. At least I do not know enough about your applications requirements and constraints to rule this possibility out.&lt;br /&gt;&lt;br /&gt;Best regards,&lt;br /&gt;Karl&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>RE: ADC Infuriating Behaviour With Multi-channel Scan Using PPI to RTC</title><link>https://devzone.nordicsemi.com/thread/454497?ContentTypeID=1</link><pubDate>Tue, 07 Nov 2023 14:21:52 GMT</pubDate><guid isPermaLink="false">137ad170-7792-4731-bb38-c0d22fbe4515:5eac9ff0-ee69-4d7c-8efb-df196f446295</guid><dc:creator>snoopy20</dc:creator><description>&lt;p&gt;Hi Karl,&lt;br /&gt;&lt;br /&gt;I&amp;#39;m in production with it, it&amp;#39;s working well.&lt;br /&gt;&lt;br /&gt;I perform a number of steps in a adc-interrupt-loop with calibration being the first. Each step does a full reset of the adc using the errata code. It&amp;#39;s not the fastest or the most optimal (i.e calibration every ~100ms) but it works and is fast enough!&lt;br /&gt;&lt;br /&gt;A.&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>RE: ADC Infuriating Behaviour With Multi-channel Scan Using PPI to RTC</title><link>https://devzone.nordicsemi.com/thread/454492?ContentTypeID=1</link><pubDate>Tue, 07 Nov 2023 14:15:18 GMT</pubDate><guid isPermaLink="false">137ad170-7792-4731-bb38-c0d22fbe4515:05dfb5df-a7f3-4651-8010-f57c0b56ebd9</guid><dc:creator>Karl Ylvisaker</dc:creator><description>&lt;p&gt;Hello,&lt;/p&gt;
[quote user="snoopy20"]So after much testing yesterday it occasionally would skip, which in my case is a fatal flaw that can damage hardware. I literally exhausted everything and then finally went the whole hog and used the errata code before any adc operation to do a full reset and reconfig. This has solved it.[/quote]
&lt;p&gt;I am sorry to read the struggles you&amp;#39;ve had to get this working as expected, which no doubt was a frustrating experience!&lt;br /&gt;&lt;br /&gt;My general suggestion for your setup would be to use the SAADC driver and to have the sampling triggered through PPI with the addition described in &lt;a href="https://devzone.nordicsemi.com/f/nordic-q-a/20291/offset-in-saadc-samples-with-easy-dma-and-ble/79053"&gt;this answer by my colleague Jørgen&lt;/a&gt; referenced by hmolesworth earlier, but you would of course also need to pay heed to the erratas also mentioned by hmolesworth, which sounds close to what you are doing currently.&lt;/p&gt;
[quote user="snoopy20"]My understanding (be good if someone from Nordic can verify) is that this will also reset internal calibration values if ran.[/quote]
&lt;p&gt;Correct, adding this code will reset the entire SAADC peripheral, as mentioned in the workaround description.&amp;nbsp;The offset calibration would indeed have to be run again, which should be done before you have started sampling (or after the END event, as described in the erratas), or anytime the ambient temperature has changed more than 10 degrees to counteract changes in the measurements caused by temperature.&amp;nbsp;&lt;br /&gt;Are there anything else than the offset calibration you are referring to here, when you say &amp;#39;internal calibration values&amp;#39;?&lt;br /&gt;&lt;br /&gt;Best regards,&lt;br /&gt;Karl&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>RE: ADC Infuriating Behaviour With Multi-channel Scan Using PPI to RTC</title><link>https://devzone.nordicsemi.com/thread/453937?ContentTypeID=1</link><pubDate>Fri, 03 Nov 2023 10:53:18 GMT</pubDate><guid isPermaLink="false">137ad170-7792-4731-bb38-c0d22fbe4515:bb0ec956-8394-4c31-965e-0c6a233d0daf</guid><dc:creator>snoopy20</dc:creator><description>&lt;p&gt;Similar, a generator.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;So after much testing yesterday it occasionally would skip, which in my case is a fatal flaw that can damage hardware. I literally exhausted everything and then finally went the whole hog and used the errata code before any adc operation to do a full reset and reconfig. This has solved it.&lt;br /&gt;&lt;br /&gt;&lt;pre class="ui-code" data-mode="c_cpp"&gt;  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;/p&gt;
&lt;p&gt;My understanding (be good if someone from Nordic can verify) is that this will also reset internal calibration values if ran.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>RE: ADC Infuriating Behaviour With Multi-channel Scan Using PPI to RTC</title><link>https://devzone.nordicsemi.com/thread/453548?ContentTypeID=1</link><pubDate>Wed, 01 Nov 2023 15:03:15 GMT</pubDate><guid isPermaLink="false">137ad170-7792-4731-bb38-c0d22fbe4515:d0bde876-3a3d-41a2-ae05-0cb0a4763b3a</guid><dc:creator>hmolesworth</dc:creator><description>&lt;p&gt;Nice! Is this for off-grid solar, by any chance? I&amp;#39;ll keep an eye out for more exciting problems ..&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>RE: ADC Infuriating Behaviour With Multi-channel Scan Using PPI to RTC</title><link>https://devzone.nordicsemi.com/thread/453459?ContentTypeID=1</link><pubDate>Wed, 01 Nov 2023 10:09:47 GMT</pubDate><guid isPermaLink="false">137ad170-7792-4731-bb38-c0d22fbe4515:f45af0c0-4a14-4b07-827b-34e9b71e929a</guid><dc:creator>snoopy20</dc:creator><description>&lt;p&gt;I am simply delighted to say this is resolved! &lt;br /&gt;&lt;br /&gt;I hope the Nordic team can create an errata and use the two PPI solution above not only for calibration but for scan operations.&amp;nbsp;&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>RE: ADC Infuriating Behaviour With Multi-channel Scan Using PPI to RTC</title><link>https://devzone.nordicsemi.com/thread/453457?ContentTypeID=1</link><pubDate>Wed, 01 Nov 2023 10:02:46 GMT</pubDate><guid isPermaLink="false">137ad170-7792-4731-bb38-c0d22fbe4515:5d4974ea-c783-4fce-a16f-796c923c319c</guid><dc:creator>snoopy20</dc:creator><description>&lt;p&gt;I have used DSB() in the past, I tried a smattering after all the SAADC registers but to no avail.&lt;br /&gt;&lt;br /&gt;After debugging and waiting for a while I managed to capture the failure. The reads have shifted one in ram.&lt;br /&gt;&lt;br /&gt;I then checked all the PELP registers upon this occurrence and they&amp;#39;re all correct.&lt;br /&gt;&lt;br /&gt;Given there is some shift errata around the calibration (although I&amp;#39;ve disabled calibration) I decided to try wiring END to STOP.&lt;/p&gt;
&lt;p&gt;NRF_PPI-&amp;gt;CH[CFG_PPI_ADC_ENDAUTOSTOP].EEP = (uint32_t) &amp;amp;NRF_SAADC-&amp;gt;EVENTS_END;&lt;br /&gt; NRF_PPI-&amp;gt;CH[CFG_PPI_ADC_ENDAUTOSTOP].TEP = (uint32_t) &amp;amp;NRF_SAADC-&amp;gt;TASKS_STOP;&lt;br /&gt; NRF_PPI-&amp;gt;CH[CFG_PPI_ADC_CALIBRATEAUTOSTOP].EEP = (uint32_t) &amp;amp;NRF_SAADC-&amp;gt;EVENTS_CALIBRATEDONE;&lt;br /&gt; NRF_PPI-&amp;gt;CH[CFG_PPI_ADC_CALIBRATEAUTOSTOP].TEP = (uint32_t) &amp;amp;NRF_SAADC-&amp;gt;TASKS_STOP;&lt;br /&gt; &lt;br /&gt;I then switched the interrupt to STOP.&lt;br /&gt;&lt;br /&gt;I&amp;#39;m still running and praying but so far no further shifting!&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>RE: ADC Infuriating Behaviour With Multi-channel Scan Using PPI to RTC</title><link>https://devzone.nordicsemi.com/thread/453409?ContentTypeID=1</link><pubDate>Wed, 01 Nov 2023 03:57:24 GMT</pubDate><guid isPermaLink="false">137ad170-7792-4731-bb38-c0d22fbe4515:8e55376f-b396-4879-bb0c-fb2c4245d22c</guid><dc:creator>hmolesworth</dc:creator><description>&lt;p&gt;I tried to run the code but my test setup is using a different SDK version not recognising some of the SAADC defines, maybe I&amp;#39;ll get another look tomorrow.&lt;/p&gt;
&lt;p&gt;Regarding the PSEL taking a while, all peripheral operations take a while and code access to registers may be delayed wrt hardware event access. Two issues, one the peripherals use a 16MHz clock instead of the CPU 64MHz clock and two the AHB bus priority instigates unquantifiable delays. Here&amp;#39;s a simple example from personal experience. A single SPIM DMA transaction event should generate a single interrupt, and&amp;nbsp;&lt;em&gt;mSpiInterruptCounter&lt;/em&gt; should therefore be equal to 1 after the event. Here are 3 example interrupts, all correct; the first generates a count of 2, the second 2 examples generate a count of 1. Multiple interrupts from a single event is ridiculous, but it happens because the interrupt tail-chains and repeats before the AHB/register clear transaction completes as the CPU has both higher priority and a 4x faster clock than the peripheral. This is a known Cortex-M4 bug. Is this an issue with the SAADC here? Dunno .. the hardware event - which does not have to traverse the&amp;nbsp;stalled AHB bus - can in some cases happen before a delayed cpu transaction.&amp;nbsp;&lt;em&gt;__DSB();&lt;/em&gt; fixes this issue in some cases. Nordic engineers might step in here :-)&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="c_cpp"&gt;volatile uint32_t mSpiInterruptCounter = 0UL;

void SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0_IRQHandler(void)
{
  mSpiInterruptCounter++;
  if (NRF_SPIM0-&amp;gt;EVENTS_END) NRF_SPIM0-&amp;gt;EVENTS_END = 0;  // Subtle Bug here
}

void SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0_IRQHandler(void)
{
  if (NRF_SPIM0-&amp;gt;EVENTS_END) NRF_SPIM0-&amp;gt;EVENTS_END = 0;  // Ok
  mSpiInterruptCounter++;
}

void SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0_IRQHandler(void)
{
  mSpiInterruptCounter++;
  if (NRF_SPIM0-&amp;gt;EVENTS_END) NRF_SPIM0-&amp;gt;EVENTS_END = 0;  // Ok
  // Clear pending hardware register bus operations - Cortex-M4 issue
  __DSB();
}&lt;/pre&gt;&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>RE: ADC Infuriating Behaviour With Multi-channel Scan Using PPI to RTC</title><link>https://devzone.nordicsemi.com/thread/453397?ContentTypeID=1</link><pubDate>Wed, 01 Nov 2023 00:11:17 GMT</pubDate><guid isPermaLink="false">137ad170-7792-4731-bb38-c0d22fbe4515:b52a89b8-3daf-4087-b687-e84997a97684</guid><dc:creator>snoopy20</dc:creator><description>&lt;p&gt;Tasks Stop are supposed to be like an &amp;#39;Abort&amp;#39;, they shouldn&amp;#39;t be required once an End Event has fired, so I am led to believe.&lt;br /&gt;&lt;br /&gt;You&amp;#39;re right, it&amp;#39;s step1,2,3,4 repeat and (4) is actually running (1) atm, but was there for looping purposes.&lt;br /&gt;&lt;br /&gt;I&amp;#39;ll try and get a breakpoint on step 2 to inspect the voltages tomorrow once it&amp;#39;s gone pair shaped and see if they&amp;#39;ve shifted. It&amp;#39;s possible as most the voltage if not all the others are very low, which is what the main voltage I&amp;#39;m primarily interested switches to (voltage.storage).&lt;br /&gt;&lt;br /&gt;I haven&amp;#39;t read anything around&amp;nbsp;&lt;span&gt;PSEL taking a while to set, is that something you&amp;#39;ve seen before?&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Regards, Andrew&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>RE: ADC Infuriating Behaviour With Multi-channel Scan Using PPI to RTC</title><link>https://devzone.nordicsemi.com/thread/453395?ContentTypeID=1</link><pubDate>Tue, 31 Oct 2023 23:34:52 GMT</pubDate><guid isPermaLink="false">137ad170-7792-4731-bb38-c0d22fbe4515:45c1cd1e-540b-4d39-b504-51ce8b848fc6</guid><dc:creator>hmolesworth</dc:creator><description>&lt;p&gt;Edit: I see the sequence is repeat &lt;em&gt;step1-2-3-4-1-2&lt;/em&gt; ..If a PSEL is slow getting disabled, then the steps immediately go out of sequence so instead of &lt;em&gt;input0-1-2-3-4-5-0 ... input6-7-6-7&lt;/em&gt; it ends up maybe being&amp;nbsp;&lt;em&gt;input0-1-2-3-4-5-0 ... input0-1-0-1... &lt;/em&gt;Do the measured voltages look like that?&lt;/p&gt;
&lt;p&gt;Also there is no &lt;em&gt;TASKS_STOP&lt;/em&gt; and &lt;em&gt;EVENTS_STOPPED&lt;/em&gt; check prior to a &lt;em&gt;TASKS_START&lt;/em&gt;; I wonder if that would help ..&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>RE: ADC Infuriating Behaviour With Multi-channel Scan Using PPI to RTC</title><link>https://devzone.nordicsemi.com/thread/453394?ContentTypeID=1</link><pubDate>Tue, 31 Oct 2023 23:16:39 GMT</pubDate><guid isPermaLink="false">137ad170-7792-4731-bb38-c0d22fbe4515:943249c7-0dde-477d-82ef-e306b7279bb6</guid><dc:creator>snoopy20</dc:creator><description>&lt;p&gt;Interesting find that link. So regarding the START operations which I am indeed performing in the interrupt...&lt;br /&gt;&lt;br /&gt;1. for one scenario the SAMPLE is fired by PPI on the STARTED event, so I don&amp;#39;t believe the SAMPLE can fire before STARTED.&lt;br /&gt;&lt;br /&gt;2. for the second scenario I am calling START and then enabling the PPI channel to allow the timer&amp;nbsp;tick to come through. I actually already added code (step3) to check if STARTED event has fired before enabling the PPI channel, just to make sure no SAMPLE event comes through prior.&lt;br /&gt;&lt;br /&gt;There&amp;#39;s potentially the case where the ADC will receive a SAMPLE from the timer while easydma is operating but the END event hasn&amp;#39;t yet been fired. In the case of a unique timer it would be possible to use PPI to stop the timer on END, but in the case of my RTC I have to use an interrupt to do the dirty, which potentially is much slower. I&amp;#39;m unsure however if it makes any difference. There&amp;#39;s nothing to indicate that a timer can&amp;#39;t in fact be left running.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>RE: ADC Infuriating Behaviour With Multi-channel Scan Using PPI to RTC</title><link>https://devzone.nordicsemi.com/thread/453393?ContentTypeID=1</link><pubDate>Tue, 31 Oct 2023 23:02:25 GMT</pubDate><guid isPermaLink="false">137ad170-7792-4731-bb38-c0d22fbe4515:7a074b4c-e37a-4724-8ae3-65fbca163f4e</guid><dc:creator>snoopy20</dc:creator><description>&lt;p&gt;Hello!&lt;br /&gt;&lt;br /&gt;In my current working case I&amp;#39;ve disabled calibration. I think many of the 832 errata also apply to the 810 even if not specified. For one, I have used 10us sampling or more due to the PPI issues.&lt;br /&gt;&lt;br /&gt;Some of the extra defines are&lt;br /&gt;&lt;br /&gt;&lt;pre class="ui-code" data-mode="c_cpp"&gt;  NVIC_SetPriority(CFG_SCHEDULER_TIMER_IRQn, CFG_INTERRUPT_PRIORITY_SCHEDULER);
  NVIC_EnableIRQ(CFG_SCHEDULER_TIMER_IRQn);
  CFG_SCHEDULER_TIMER-&amp;gt;EVTENSET = RTC_EVTENSET_TICK_Set &amp;lt;&amp;lt; RTC_EVTENSET_TICK_Pos;
  CFG_SCHEDULER_TIMER-&amp;gt;PRESCALER = CFG_SCHEDULER_TIMER_PRESCALER-1;
  CFG_SCHEDULER_TIMER-&amp;gt;TASKS_START = RTC_TASKS_START_TASKS_START_Trigger &amp;lt;&amp;lt; RTC_TASKS_START_TASKS_START_Pos;

#define CFG_ADC_FRONT_PSEL SAADC_CH_PSELP_PSELP_AnalogInput0
#define CFG_ADC_REAR_PSEL SAADC_CH_PSELP_PSELP_AnalogInput1
#define CFG_ADC_USB_PSEL SAADC_CH_PSELP_PSELP_AnalogInput2
#define CFG_ADC_LIGHTSENSE_PSEL SAADC_CH_PSELP_PSELP_AnalogInput4
#define CFG_ADC_STORE_PSEL SAADC_CH_PSELP_PSELP_AnalogInput5
#define CFG_ADC_MPPT_VOLTAGE_PSEL SAADC_CH_PSELP_PSELP_AnalogInput6
#define CFG_ADC_MPPT_CURRENT_PSEL SAADC_CH_PSELP_PSELP_AnalogInput7

#define pinDisconnectInputBuffer (GPIO_PIN_CNF_INPUT_Disconnect &amp;lt;&amp;lt; GPIO_PIN_CNF_INPUT_Pos)

#define CFG_SCHEDULER_TIMER_PRESCALER 8
&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The current workings are&lt;br /&gt;&lt;br /&gt;&lt;pre class="ui-code" data-mode="c_cpp"&gt;#include &amp;lt;stdint.h&amp;gt;


#define ADC_RESOLUTION SAADC_RESOLUTION_VAL_12bit
#define ADC_SAMPLETIME_CFG_SLOW SAADC_CH_CONFIG_TACQ_40us
#define ADC_SAMPLETIME_CFG_MPPT_VOLTAGE SAADC_CH_CONFIG_TACQ_10us
#define ADC_SAMPLETIME_CFG_MPPT_CURRENT SAADC_CH_CONFIG_TACQ_10us

// ADC takes 2us per channel, 4us + 10us + 10us = 24us

// 4096Hz = 244us
// 350 (fit into ram)*244 = 85.4ms
#define MPPT_SAMPLES 700

// 200ms total / 81ms
#define MPPT_LOOP 1

enum {
  ADC_CH_USB,
  ADC_CH_FRONT,
  ADC_CH_REAR,
  ADC_CH_STORE,
  ADC_CH_LIGHTSENSE,
  ADC_CH_MPPT_VOLTAGE,
  ADC_CH_MPPT_CURRENT
};

struct mpptReadProto {
  int16_t voltage;
  int16_t current;
};

static struct {
  int16_t usb;
  int16_t front;
  int16_t rear;
  int16_t storage;
  int16_t lightsense;
} voltages;

static struct {
  int16_t usb;
  int16_t front;
  int16_t rear;
} currents;

static void step4 (void);
static void step3 (void);
static void step2 (void);
static void step1 (void);

static void (*step)(void);
static uint8_t loop;
static struct mpptTrackProto track;
static struct mpptReadProto banks[MPPT_LOOP &amp;gt; 1? 2 : 1][MPPT_SAMPLES];
static uint8_t bank;

extern struct statusProto status;

static void step1 (void) {

  step = &amp;amp;step2;

  NRF_GPIO-&amp;gt;OUTCLR = 1 &amp;lt;&amp;lt; CFG_PIN_ADC_MUX;
  NRF_PPI-&amp;gt;CHENSET = 1 &amp;lt;&amp;lt; CFG_PPI_ADC_SAMPLEONSTART;

  NRF_SAADC-&amp;gt;CH[ADC_CH_USB].PSELP = CFG_ADC_USB_PSEL;
  NRF_SAADC-&amp;gt;CH[ADC_CH_FRONT].PSELP = CFG_ADC_FRONT_PSEL;
  NRF_SAADC-&amp;gt;CH[ADC_CH_REAR].PSELP = CFG_ADC_REAR_PSEL;
  NRF_SAADC-&amp;gt;CH[ADC_CH_STORE].PSELP = CFG_ADC_STORE_PSEL;
  NRF_SAADC-&amp;gt;CH[ADC_CH_LIGHTSENSE].PSELP = CFG_ADC_LIGHTSENSE_PSEL;

  NRF_SAADC-&amp;gt;RESULT.PTR = (uint32_t) &amp;amp;voltages;
  NRF_SAADC-&amp;gt;RESULT.MAXCNT = sizeof(voltages) / 2;

  //NRF_SAADC-&amp;gt;EVENTS_STARTED = 0;
  NRF_SAADC-&amp;gt;TASKS_START = SAADC_TASKS_START_TASKS_START_Trigger &amp;lt;&amp;lt; SAADC_TASKS_START_TASKS_START_Pos;
  //while (!NRF_SAADC-&amp;gt;EVENTS_STARTED) {}
  //NRF_SAADC-&amp;gt;TASKS_SAMPLE = 1;
}

static void step2 (void) {

  step = &amp;amp;step3;

  NRF_GPIO-&amp;gt;OUTSET = 1 &amp;lt;&amp;lt; CFG_PIN_ADC_MUX;

  NRF_SAADC-&amp;gt;RESULT.PTR = (uint32_t) &amp;amp;currents;
  NRF_SAADC-&amp;gt;RESULT.MAXCNT = sizeof(currents) / 2;
  NRF_SAADC-&amp;gt;TASKS_START = SAADC_TASKS_START_TASKS_START_Trigger &amp;lt;&amp;lt; SAADC_TASKS_START_TASKS_START_Pos;
}

static void step3 (void) {

  step = &amp;amp;step4;
  NRF_PPI-&amp;gt;CHENCLR = 1 &amp;lt;&amp;lt; CFG_PPI_ADC_SAMPLEONSTART;

  NRF_SAADC-&amp;gt;CH[ADC_CH_USB].PSELP = SAADC_CH_PSELP_PSELP_NC;
  NRF_SAADC-&amp;gt;CH[ADC_CH_FRONT].PSELP = SAADC_CH_PSELP_PSELP_NC;
  NRF_SAADC-&amp;gt;CH[ADC_CH_REAR].PSELP = SAADC_CH_PSELP_PSELP_NC;
  NRF_SAADC-&amp;gt;CH[ADC_CH_STORE].PSELP = SAADC_CH_PSELP_PSELP_NC;
  NRF_SAADC-&amp;gt;CH[ADC_CH_LIGHTSENSE].PSELP = SAADC_CH_PSELP_PSELP_NC;
  NRF_SAADC-&amp;gt;CH[ADC_CH_MPPT_VOLTAGE].PSELP = CFG_ADC_MPPT_VOLTAGE_PSEL;
  NRF_SAADC-&amp;gt;CH[ADC_CH_MPPT_CURRENT].PSELP = CFG_ADC_MPPT_CURRENT_PSEL;

  NRF_SAADC-&amp;gt;RESULT.PTR = (uint32_t) &amp;amp;banks;
  NRF_SAADC-&amp;gt;RESULT.MAXCNT = MPPT_SAMPLES * 2;

  NRF_SAADC-&amp;gt;EVENTS_STARTED = 0;
  NRF_SAADC-&amp;gt;TASKS_START = SAADC_TASKS_START_TASKS_START_Trigger &amp;lt;&amp;lt; SAADC_TASKS_START_TASKS_START_Pos;
  while (!NRF_SAADC-&amp;gt;EVENTS_STARTED) {}
  NRF_PPI-&amp;gt;CHENSET = 1 &amp;lt;&amp;lt; CFG_PPI_ADC_SAMPLEBYTIMER;
}

static void trackAssess () {

  for(struct mpptReadProto *pCur = &amp;amp;banks[bank][0]; pCur &amp;lt; &amp;amp;banks[bank][0] + sizeof(banks[0]); pCur++) {
    if (pCur-&amp;gt;voltage &amp;gt; track.voltage &amp;amp;&amp;amp; pCur-&amp;gt;current &amp;gt; track.current) {
      track.voltage = pCur-&amp;gt;voltage;
      track.current = pCur-&amp;gt;current;
    }
  }
}

static void step4 (void) {

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

  #if MPPT_LOOP &amp;gt; 1

  if (loop == MPPT_LOOP-1) {

  #endif

    NRF_SAADC-&amp;gt;CH[ADC_CH_MPPT_VOLTAGE].PSELP = SAADC_CH_PSELP_PSELP_NC;
    NRF_SAADC-&amp;gt;CH[ADC_CH_MPPT_CURRENT].PSELP = SAADC_CH_PSELP_PSELP_NC;
    trackAssess();
    MPPT_Track(&amp;amp;track);
    track.voltage = 0;
    track.current = 0;
    loop = 0;
    bank = 0;
    step1();
    return;

  #if MPPT_LOOP &amp;gt; 1

  }

  NRF_SAADC-&amp;gt;RESULT.PTR = bank? (uint32_t) &amp;amp;banks[0] : (uint32_t) &amp;amp;banks[1];
  NRF_SAADC-&amp;gt;EVENTS_STARTED = 0;
  NRF_SAADC-&amp;gt;TASKS_START = SAADC_TASKS_START_TASKS_START_Trigger &amp;lt;&amp;lt; SAADC_TASKS_START_TASKS_START_Pos;
  while (!NRF_SAADC-&amp;gt;EVENTS_STARTED) {}
  NRF_PPI-&amp;gt;CHENSET = 1 &amp;lt;&amp;lt; CFG_PPI_ADC_SAMPLEBYTIMER;
  
  trackAssess();
  bank = bank? 0:1;
  loop++;

  #endif
}

void SAADC_IRQHandler (void) {

  NRF_SAADC-&amp;gt;EVENTS_END = SAADC_EVENTS_END_EVENTS_END_NotGenerated &amp;lt;&amp;lt; SAADC_EVENTS_END_EVENTS_END_Pos;
  step();
}

uint32_t ADC_TaskCalibrate (void) {

  NRF_SAADC-&amp;gt;ENABLE = SAADC_ENABLE_ENABLE_Enabled &amp;lt;&amp;lt; SAADC_ENABLE_ENABLE_Pos;
  //NRF_SAADC-&amp;gt;TASKS_CALIBRATEOFFSET = SAADC_TASKS_CALIBRATEOFFSET_TASKS_CALIBRATEOFFSET_Trigger &amp;lt;&amp;lt; SAADC_TASKS_CALIBRATEOFFSET_TASKS_CALIBRATEOFFSET_Pos;
  return 0; //CFG_SCHEDULER_TIMER_MINIMUM;
}

uint32_t ADC_TaskStart (void) {

  NRF_SAADC-&amp;gt;EVENTS_END = SAADC_EVENTS_END_EVENTS_END_NotGenerated &amp;lt;&amp;lt; SAADC_EVENTS_END_EVENTS_END_Pos;
  NRF_SAADC-&amp;gt;INTENSET = SAADC_INTENSET_END_Enabled &amp;lt;&amp;lt; SAADC_INTENSET_END_Pos;
  step1();
  return CFG_SCHEDULER_TIMER_MINIMUM;
}

uint32_t ADC_TaskCalc (void) {

  return 0;
}

inline void ADC_Init (void) {

  NRF_GPIO-&amp;gt;PIN_CNF[CFG_PIN_ADC_STORE] = pinDisconnectInputBuffer;
  NRF_GPIO-&amp;gt;PIN_CNF[CFG_PIN_ADC_USB] = pinDisconnectInputBuffer;
  NRF_GPIO-&amp;gt;PIN_CNF[CFG_PIN_ADC_FRONT] = pinDisconnectInputBuffer;
  NRF_GPIO-&amp;gt;PIN_CNF[CFG_PIN_ADC_REAR] = pinDisconnectInputBuffer;
  NRF_GPIO-&amp;gt;PIN_CNF[CFG_PIN_ADC_MPPT_VOLTAGE] = pinDisconnectInputBuffer;
  NRF_GPIO-&amp;gt;PIN_CNF[CFG_PIN_ADC_MPPT_CURRENT] = pinDisconnectInputBuffer;
  NRF_GPIO-&amp;gt;PIN_CNF[CFG_PIN_ADC_LIGHTSENSE] = pinDisconnectInputBuffer;
  NRF_GPIO-&amp;gt;PIN_CNF[CFG_PIN_ADC_MUX] = pinDisconnectInputBuffer | (GPIO_PIN_CNF_DIR_Output &amp;lt;&amp;lt; GPIO_PIN_CNF_DIR_Pos);
  NRF_GPIO-&amp;gt;OUTSET = 1 &amp;lt;&amp;lt; CFG_PIN_ADC_MUX;

  NRF_SAADC-&amp;gt;RESOLUTION = SAADC_RESOLUTION_VAL_12bit &amp;lt;&amp;lt; SAADC_RESOLUTION_VAL_Pos;
  NRF_SAADC-&amp;gt;CH[ADC_CH_USB].CONFIG = SAADC_CH_CONFIG_REFSEL_Internal &amp;lt;&amp;lt; SAADC_CH_CONFIG_REFSEL_Pos | SAADC_CH_CONFIG_GAIN_Gain1 &amp;lt;&amp;lt; SAADC_CH_CONFIG_GAIN_Pos | ADC_SAMPLETIME_CFG_SLOW &amp;lt;&amp;lt; SAADC_CH_CONFIG_TACQ_Pos;
  NRF_SAADC-&amp;gt;CH[ADC_CH_FRONT].CONFIG = SAADC_CH_CONFIG_REFSEL_Internal &amp;lt;&amp;lt; SAADC_CH_CONFIG_REFSEL_Pos | SAADC_CH_CONFIG_GAIN_Gain1 &amp;lt;&amp;lt; SAADC_CH_CONFIG_GAIN_Pos | ADC_SAMPLETIME_CFG_SLOW &amp;lt;&amp;lt; SAADC_CH_CONFIG_TACQ_Pos;
  NRF_SAADC-&amp;gt;CH[ADC_CH_REAR].CONFIG = SAADC_CH_CONFIG_REFSEL_Internal &amp;lt;&amp;lt; SAADC_CH_CONFIG_REFSEL_Pos | SAADC_CH_CONFIG_GAIN_Gain1 &amp;lt;&amp;lt; SAADC_CH_CONFIG_GAIN_Pos | ADC_SAMPLETIME_CFG_SLOW &amp;lt;&amp;lt; SAADC_CH_CONFIG_TACQ_Pos;
  NRF_SAADC-&amp;gt;CH[ADC_CH_STORE].CONFIG = SAADC_CH_CONFIG_REFSEL_Internal &amp;lt;&amp;lt; SAADC_CH_CONFIG_REFSEL_Pos | SAADC_CH_CONFIG_GAIN_Gain1_2 &amp;lt;&amp;lt; SAADC_CH_CONFIG_GAIN_Pos | ADC_SAMPLETIME_CFG_SLOW &amp;lt;&amp;lt; SAADC_CH_CONFIG_TACQ_Pos;
  NRF_SAADC-&amp;gt;CH[ADC_CH_LIGHTSENSE].CONFIG = SAADC_CH_CONFIG_REFSEL_Internal &amp;lt;&amp;lt; SAADC_CH_CONFIG_REFSEL_Pos | SAADC_CH_CONFIG_GAIN_Gain2 &amp;lt;&amp;lt; SAADC_CH_CONFIG_GAIN_Pos | ADC_SAMPLETIME_CFG_SLOW &amp;lt;&amp;lt; SAADC_CH_CONFIG_TACQ_Pos;
  NRF_SAADC-&amp;gt;CH[ADC_CH_MPPT_VOLTAGE].CONFIG = SAADC_CH_CONFIG_REFSEL_Internal &amp;lt;&amp;lt; SAADC_CH_CONFIG_REFSEL_Pos | SAADC_CH_CONFIG_GAIN_Gain1_6 &amp;lt;&amp;lt; SAADC_CH_CONFIG_GAIN_Pos | ADC_SAMPLETIME_CFG_MPPT_VOLTAGE &amp;lt;&amp;lt; SAADC_CH_CONFIG_TACQ_Pos;
  NRF_SAADC-&amp;gt;CH[ADC_CH_MPPT_CURRENT].CONFIG = SAADC_CH_CONFIG_REFSEL_Internal &amp;lt;&amp;lt; SAADC_CH_CONFIG_REFSEL_Pos | SAADC_CH_CONFIG_GAIN_Gain1_6 &amp;lt;&amp;lt; SAADC_CH_CONFIG_GAIN_Pos | ADC_SAMPLETIME_CFG_MPPT_CURRENT &amp;lt;&amp;lt; SAADC_CH_CONFIG_TACQ_Pos;
  
  NRF_PPI-&amp;gt;CH[CFG_PPI_ADC_SAMPLEONSTART].EEP = (uint32_t) &amp;amp;NRF_SAADC-&amp;gt;EVENTS_STARTED;
  NRF_PPI-&amp;gt;CH[CFG_PPI_ADC_SAMPLEONSTART].TEP = (uint32_t) &amp;amp;NRF_SAADC-&amp;gt;TASKS_SAMPLE;
  NRF_PPI-&amp;gt;CH[CFG_PPI_ADC_SAMPLEBYTIMER].EEP = (uint32_t) &amp;amp;NRF_RTC1-&amp;gt;EVENTS_TICK;
  NRF_PPI-&amp;gt;CH[CFG_PPI_ADC_SAMPLEBYTIMER].TEP = (uint32_t) &amp;amp;NRF_SAADC-&amp;gt;TASKS_SAMPLE;
  
  NVIC_SetPriority(SAADC_IRQn, CFG_INTERRUPT_PRIORITY_ADC);
  NVIC_EnableIRQ(SAADC_IRQn);
}

void ADC_Shutdown (void) {

  NRF_SAADC-&amp;gt;ENABLE = SAADC_ENABLE_ENABLE_Disabled &amp;lt;&amp;lt; SAADC_ENABLE_ENABLE_Pos;
}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Effectively I&amp;#39;m using two methods both PPI, one wires STARTED to SAMPLE, where I want to use scan for n amount of channels once, and then an alternative second PPI wires the RTC tick to SAMPLE, where I scan 2 channels multiple times.&lt;br /&gt;&lt;br /&gt;Due to RAM limitations I was using multiple banks, so easydma would fill one and processing would occur while it began filling the other one. Currently given the prescaler is so high I don&amp;#39;t need the loop and am running just the one bank, 750 x 2 channels.&lt;br /&gt;&lt;br /&gt;I&amp;#39;m using various interrupts, no code occurs within the main loop it&amp;#39;s just an idle there. For some reason the ADC must run with priority 3 or greater when the softdevice is running. This suggested the softdevice was overwhelming the cpu to service higher interrupts, but then the scheduler and other higher interrupts I have are being serviced ok. It&amp;#39;s odd!&lt;br /&gt;&lt;br /&gt;&lt;pre class="ui-code" data-mode="c_cpp"&gt;enum {
  CFG_INTERRUPT_PRIORITY_ADC = 3,
  CFG_INTERRUPT_PRIORITY_GPIOTE = 3, // must come before scheduler
  CFG_INTERRUPT_PRIORITY_EGU = 5,
  CFG_INTERRUPT_PRIORITY_SCHEDULER = 4
};
&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;It shouldn&amp;#39;t make any difference I believe with the CPU jumping out of the ADC interrupt and back-in since none of the code that I can see needs to be atomic.&amp;nbsp;&lt;br /&gt;&lt;br /&gt;At this point I have **** all idea why it randomly crashes. Also when I say crash I mean the ADC peripheral; the ADC values randomly transform to low garbage and only a power reset clears it.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>RE: ADC Infuriating Behaviour With Multi-channel Scan Using PPI to RTC</title><link>https://devzone.nordicsemi.com/thread/453390?ContentTypeID=1</link><pubDate>Tue, 31 Oct 2023 22:29:23 GMT</pubDate><guid isPermaLink="false">137ad170-7792-4731-bb38-c0d22fbe4515:b39eba4e-5db0-45be-84fb-68272e96bb29</guid><dc:creator>hmolesworth</dc:creator><description>&lt;p&gt;Such fun .. some years ago I had a similar issue and made these notes; if you post the sequence of calls to these functions (and more of the #defines as it&amp;#39;s tedious creating them) I could do some independant testing, as I don&amp;#39;t see where you are checking things like cal finished before starting a scan, and there is an SAADC errata where cal breaks things unless issued prior to a START or following an END. nRF52810 errata 237 and errata 252. Also note errata 212.&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="c_cpp"&gt;// Problem when the SAADC is configured in continuous mode using PPI to trigger the SAMPLE task at a regular interval,
// while the END event and START task is handled by CPU interrupt. When the SAMPLE task is triggered, each channel is
// sampled and written to RAM with DMA as fast as possible. When the buffer have been filled, the DMA transfer will be
// delayed until the START task have been triggered. You are triggering the START task in the interrupt handler after
// receiving the END event. If you receive the END event when IRQ is disabled or an interrupt with higher priority is
// executing, the triggering of the START task can get delayed until after the SAMPLE task have been triggered using
// PPI. Triggering of the SAMPLE task will generate a DMA transfer request, but this request will not be acknowledged
// until the START task have been triggered. The scan cycle of the SAADC will however expect the DMA transfer to finish,
// and will sample next channel. When the START task is triggered, the pending DMA transfer will be executed, but the
// transferred sample will correcpond to the latest sampled channel. Samples from previous channels will have been lost.
//
// There are two possible solutions to this problem:
//
// Use PPI to trigger START task on an END event. This will avoid the delayed triggering og the START task due to a
// queued interrupt generated by the END event, but in the case of high sample frequency and long delays, it can cause
// your buffer to be overwritten before you are able to process the buffer. In case of using this solution, it is
// neccessary to use double buffering and large enough buffers to avoid data loss.
// Trigger sampling from CPU interrupt. If the SAMPLE task is triggered from an interrupt with lower priority than the
// SAADC IRQ handler, the START task will always be triggered between an END event and a new SAMPLE task. This solution
// will make the sampling vary a bit in time, as higher priority tasks can cause the triggering of SAMPLE task to be delayed.&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;I think the following post might have some useful info:&amp;nbsp;&lt;a href="https://devzone.nordicsemi.com/f/nordic-q-a/20291/offset-in-saadc-samples-with-easy-dma-and-ble/79053"&gt;offset-in-saadc-samples-with-easy-dma-and-ble&lt;/a&gt;&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>RE: ADC Infuriating Behaviour With Multi-channel Scan Using PPI to RTC</title><link>https://devzone.nordicsemi.com/thread/453387?ContentTypeID=1</link><pubDate>Tue, 31 Oct 2023 22:05:22 GMT</pubDate><guid isPermaLink="false">137ad170-7792-4731-bb38-c0d22fbe4515:99f90b1b-afbc-4e81-b1e7-e59a50305e96</guid><dc:creator>snoopy20</dc:creator><description>&lt;p&gt;Nope it&amp;#39;s crashed again, just took a lot longer. F**** sake!&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>RE: ADC Infuriating Behaviour With Multi-channel Scan Using PPI to RTC</title><link>https://devzone.nordicsemi.com/thread/453386?ContentTypeID=1</link><pubDate>Tue, 31 Oct 2023 22:04:08 GMT</pubDate><guid isPermaLink="false">137ad170-7792-4731-bb38-c0d22fbe4515:165f8f01-31c9-4bb9-b0c7-07e3ecd44a23</guid><dc:creator>snoopy20</dc:creator><description>&lt;p&gt;I appear to have stability. Max RTC prescaler is 8, or 4096Hz.&lt;br /&gt;&lt;br /&gt;To resolve the instability disable calibration completely. It appears to be linked to the channel so reconfiguring them as I do will result in bad data eventually.&amp;nbsp;&lt;br /&gt;&lt;br /&gt;So far the ADC hasn&amp;#39;t crashed so fingers crossed its sorted.&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 Infuriating Behaviour With Multi-channel Scan Using PPI to RTC</title><link>https://devzone.nordicsemi.com/thread/453383?ContentTypeID=1</link><pubDate>Tue, 31 Oct 2023 21:27:08 GMT</pubDate><guid isPermaLink="false">137ad170-7792-4731-bb38-c0d22fbe4515:6d6191e2-b4c3-4c12-8931-3edfe7a3a95c</guid><dc:creator>snoopy20</dc:creator><description>&lt;p&gt;Right, so I&amp;#39;ve determined that it will mostly work with an RTC prescale of 9, but mostly the ADC will eventually fail and start giving crap out. At a prescale of 8 it will mostly not work and at 7 it&amp;#39;s crap out all the time.&lt;br /&gt;&lt;br /&gt;A prescale of 9 equals 3631Hz, which is 274us.&amp;nbsp;&lt;br /&gt;&lt;br /&gt;Something is interfering with the operation and it worsens as load increases (i.e run softdevice).&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="ui-code" data-mode="c_cpp"&gt;
#define ADC_RESOLUTION SAADC_RESOLUTION_VAL_12bit
#define ADC_SAMPLETIME_CFG_SLOW SAADC_CH_CONFIG_TACQ_20us
#define ADC_SAMPLETIME_CFG_MPPT_VOLTAGE SAADC_CH_CONFIG_TACQ_20us
#define ADC_SAMPLETIME_CFG_MPPT_CURRENT SAADC_CH_CONFIG_TACQ_10us

// ADC takes 2us per channel, 4us + 40us + 40us = 84us

// 3641Hz = 274us
// 667 (fit into ram)*274 = 182ms
#define MPPT_SAMPLES 350

// 200ms total / 81ms
#define MPPT_LOOP 3

enum {
  ADC_CH_USB,
  ADC_CH_FRONT,
  ADC_CH_REAR,
  ADC_CH_STORE,
  ADC_CH_LIGHTSENSE,
  ADC_CH_MPPT_VOLTAGE,
  ADC_CH_MPPT_CURRENT
};

struct mpptReadProto {
  int16_t voltage;
  int16_t current;
};

volatile static struct {
  int16_t usb;
  int16_t front;
  int16_t rear;
  int16_t storage;
  int16_t lightsense;
} voltages;
volatile static struct {
  int16_t usb;
  int16_t front;
  int16_t rear;
} currents;
static struct mpptReadProto banks[2][MPPT_SAMPLES];

static void step4 (void);
static void step3 (void);
static void step2 (void);
static void step1 (void);

static void (*step)(void);
static uint8_t loop;
static struct mpptTrackProto track;
static uint8_t bank;

static void step1 (void) {

  step = &amp;amp;step2;

  NRF_GPIO-&amp;gt;OUTCLR = 1 &amp;lt;&amp;lt; CFG_PIN_ADC_MUX;
  NRF_PPI-&amp;gt;CHENSET = 1 &amp;lt;&amp;lt; CFG_PPI_ADC_SAMPLEONSTART;

  NRF_SAADC-&amp;gt;CH[ADC_CH_USB].PSELP = CFG_ADC_USB_PSEL;
  NRF_SAADC-&amp;gt;CH[ADC_CH_FRONT].PSELP = CFG_ADC_FRONT_PSEL;
  NRF_SAADC-&amp;gt;CH[ADC_CH_REAR].PSELP = CFG_ADC_REAR_PSEL;
  NRF_SAADC-&amp;gt;CH[ADC_CH_STORE].PSELP = CFG_ADC_STORE_PSEL;
  NRF_SAADC-&amp;gt;CH[ADC_CH_LIGHTSENSE].PSELP = CFG_ADC_LIGHTSENSE_PSEL;

  NRF_SAADC-&amp;gt;RESULT.PTR = (uint32_t) &amp;amp;voltages;
  NRF_SAADC-&amp;gt;RESULT.MAXCNT = sizeof(voltages) / 2;

  NRF_SAADC-&amp;gt;TASKS_START = SAADC_TASKS_START_TASKS_START_Trigger &amp;lt;&amp;lt; SAADC_TASKS_START_TASKS_START_Pos;
}

static void step2 (void) {

  step = &amp;amp;step3;

  NRF_GPIO-&amp;gt;OUTSET = 1 &amp;lt;&amp;lt; CFG_PIN_ADC_MUX;

  NRF_SAADC-&amp;gt;RESULT.PTR = (uint32_t) &amp;amp;currents;
  NRF_SAADC-&amp;gt;RESULT.MAXCNT = sizeof(currents) / 2;
  NRF_SAADC-&amp;gt;TASKS_START = SAADC_TASKS_START_TASKS_START_Trigger &amp;lt;&amp;lt; SAADC_TASKS_START_TASKS_START_Pos;
}

static void step3 (void) {

  step = &amp;amp;step4;
  NRF_PPI-&amp;gt;CHENCLR = 1 &amp;lt;&amp;lt; CFG_PPI_ADC_SAMPLEONSTART;

  NRF_SAADC-&amp;gt;CH[ADC_CH_USB].PSELP = SAADC_CH_PSELP_PSELP_NC;
  NRF_SAADC-&amp;gt;CH[ADC_CH_FRONT].PSELP = SAADC_CH_PSELP_PSELP_NC;
  NRF_SAADC-&amp;gt;CH[ADC_CH_REAR].PSELP = SAADC_CH_PSELP_PSELP_NC;
  NRF_SAADC-&amp;gt;CH[ADC_CH_STORE].PSELP = SAADC_CH_PSELP_PSELP_NC;
  NRF_SAADC-&amp;gt;CH[ADC_CH_LIGHTSENSE].PSELP = SAADC_CH_PSELP_PSELP_NC;
  NRF_SAADC-&amp;gt;CH[ADC_CH_MPPT_VOLTAGE].PSELP = CFG_ADC_MPPT_VOLTAGE_PSEL;
  NRF_SAADC-&amp;gt;CH[ADC_CH_MPPT_CURRENT].PSELP = CFG_ADC_MPPT_CURRENT_PSEL;

  NRF_SAADC-&amp;gt;RESULT.PTR = (uint32_t) &amp;amp;banks;
  NRF_SAADC-&amp;gt;RESULT.MAXCNT = MPPT_SAMPLES * 2;

  NRF_SAADC-&amp;gt;TASKS_START = SAADC_TASKS_START_TASKS_START_Trigger &amp;lt;&amp;lt; SAADC_TASKS_START_TASKS_START_Pos;
  NRF_PPI-&amp;gt;CHENSET = 1 &amp;lt;&amp;lt; CFG_PPI_ADC_SAMPLEBYTIMER;
}

static void trackAssess (uint8_t bankId) {

  for(struct mpptReadProto *pCur = &amp;amp;banks[bankId][0]; pCur &amp;lt; &amp;amp;banks[bankId][0] + sizeof(banks[0]); pCur++) {
    if (pCur-&amp;gt;voltage &amp;gt; track.voltage &amp;amp;&amp;amp; pCur-&amp;gt;current &amp;gt; track.current) {
      track.voltage = pCur-&amp;gt;voltage;
      track.current = pCur-&amp;gt;current;
    }
  }
}

static void step4 (void) {

  if (loop == MPPT_LOOP-1) {
    loop = 0;
    bank = 0;

    NRF_SAADC-&amp;gt;CH[ADC_CH_MPPT_VOLTAGE].PSELP = SAADC_CH_PSELP_PSELP_NC;
    NRF_SAADC-&amp;gt;CH[ADC_CH_MPPT_CURRENT].PSELP = SAADC_CH_PSELP_PSELP_NC;
    NRF_PPI-&amp;gt;CHENCLR = 1 &amp;lt;&amp;lt; CFG_PPI_ADC_SAMPLEBYTIMER;
    
    trackAssess(bank);
    MPPT_Track(&amp;amp;track);
    track.voltage = 0;
    track.current = 0;
    step1();
    return;
  }

  NRF_SAADC-&amp;gt;RESULT.PTR = bank? (uint32_t) &amp;amp;banks[0] : (uint32_t) &amp;amp;banks[1];
  NRF_SAADC-&amp;gt;TASKS_START = SAADC_TASKS_START_TASKS_START_Trigger &amp;lt;&amp;lt; SAADC_TASKS_START_TASKS_START_Pos;
  trackAssess(bank);
  bank = bank? 0:1;
  loop++;
}

void SAADC_IRQHandler (void) {

  NRF_SAADC-&amp;gt;EVENTS_END = SAADC_EVENTS_END_EVENTS_END_NotGenerated &amp;lt;&amp;lt; SAADC_EVENTS_END_EVENTS_END_Pos;
  step();
}

uint32_t ADC_TaskCalibrate (void) {

  NRF_SAADC-&amp;gt;ENABLE = SAADC_ENABLE_ENABLE_Enabled &amp;lt;&amp;lt; SAADC_ENABLE_ENABLE_Pos;
  NRF_SAADC-&amp;gt;TASKS_CALIBRATEOFFSET = SAADC_TASKS_CALIBRATEOFFSET_TASKS_CALIBRATEOFFSET_Trigger &amp;lt;&amp;lt; SAADC_TASKS_CALIBRATEOFFSET_TASKS_CALIBRATEOFFSET_Pos;
  return CFG_SCHEDULER_TIMER_MINIMUM;
}

uint32_t ADC_TaskStart (void) {

  NRF_SAADC-&amp;gt;EVENTS_END = SAADC_EVENTS_END_EVENTS_END_NotGenerated &amp;lt;&amp;lt; SAADC_EVENTS_END_EVENTS_END_Pos;
  NRF_SAADC-&amp;gt;INTENSET = SAADC_INTENSET_END_Enabled &amp;lt;&amp;lt; SAADC_INTENSET_END_Pos;

  step1();
  return CFG_SCHEDULER_TIMER_MINIMUM;
}

inline void ADC_Init (void) {

  NRF_GPIO-&amp;gt;PIN_CNF[CFG_PIN_ADC_STORE] = pinDisconnectInputBuffer;
  NRF_GPIO-&amp;gt;PIN_CNF[CFG_PIN_ADC_USB] = pinDisconnectInputBuffer;
  NRF_GPIO-&amp;gt;PIN_CNF[CFG_PIN_ADC_FRONT] = pinDisconnectInputBuffer;
  NRF_GPIO-&amp;gt;PIN_CNF[CFG_PIN_ADC_REAR] = pinDisconnectInputBuffer;
  NRF_GPIO-&amp;gt;PIN_CNF[CFG_PIN_ADC_MPPT_VOLTAGE] = pinDisconnectInputBuffer;
  NRF_GPIO-&amp;gt;PIN_CNF[CFG_PIN_ADC_MPPT_CURRENT] = pinDisconnectInputBuffer;
  NRF_GPIO-&amp;gt;PIN_CNF[CFG_PIN_ADC_LIGHTSENSE] = pinDisconnectInputBuffer;
  NRF_GPIO-&amp;gt;PIN_CNF[CFG_PIN_ADC_MUX] = pinDisconnectInputBuffer | (GPIO_PIN_CNF_DIR_Output &amp;lt;&amp;lt; GPIO_PIN_CNF_DIR_Pos);
  NRF_GPIO-&amp;gt;OUTSET = 1 &amp;lt;&amp;lt; CFG_PIN_ADC_MUX;

  NRF_SAADC-&amp;gt;RESOLUTION = SAADC_RESOLUTION_VAL_12bit &amp;lt;&amp;lt; SAADC_RESOLUTION_VAL_Pos;
  NRF_SAADC-&amp;gt;CH[ADC_CH_USB].CONFIG = SAADC_CH_CONFIG_REFSEL_Internal &amp;lt;&amp;lt; SAADC_CH_CONFIG_REFSEL_Pos | SAADC_CH_CONFIG_GAIN_Gain1 &amp;lt;&amp;lt; SAADC_CH_CONFIG_GAIN_Pos | ADC_SAMPLETIME_CFG_SLOW &amp;lt;&amp;lt; SAADC_CH_CONFIG_TACQ_Pos;
  NRF_SAADC-&amp;gt;CH[ADC_CH_FRONT].CONFIG = SAADC_CH_CONFIG_REFSEL_Internal &amp;lt;&amp;lt; SAADC_CH_CONFIG_REFSEL_Pos | SAADC_CH_CONFIG_GAIN_Gain1 &amp;lt;&amp;lt; SAADC_CH_CONFIG_GAIN_Pos | ADC_SAMPLETIME_CFG_SLOW &amp;lt;&amp;lt; SAADC_CH_CONFIG_TACQ_Pos;
  NRF_SAADC-&amp;gt;CH[ADC_CH_REAR].CONFIG = SAADC_CH_CONFIG_REFSEL_Internal &amp;lt;&amp;lt; SAADC_CH_CONFIG_REFSEL_Pos | SAADC_CH_CONFIG_GAIN_Gain1 &amp;lt;&amp;lt; SAADC_CH_CONFIG_GAIN_Pos | ADC_SAMPLETIME_CFG_SLOW &amp;lt;&amp;lt; SAADC_CH_CONFIG_TACQ_Pos;
  NRF_SAADC-&amp;gt;CH[ADC_CH_STORE].CONFIG = SAADC_CH_CONFIG_REFSEL_Internal &amp;lt;&amp;lt; SAADC_CH_CONFIG_REFSEL_Pos | SAADC_CH_CONFIG_GAIN_Gain1_2 &amp;lt;&amp;lt; SAADC_CH_CONFIG_GAIN_Pos | ADC_SAMPLETIME_CFG_SLOW &amp;lt;&amp;lt; SAADC_CH_CONFIG_TACQ_Pos;
  NRF_SAADC-&amp;gt;CH[ADC_CH_LIGHTSENSE].CONFIG = SAADC_CH_CONFIG_REFSEL_Internal &amp;lt;&amp;lt; SAADC_CH_CONFIG_REFSEL_Pos | SAADC_CH_CONFIG_GAIN_Gain2 &amp;lt;&amp;lt; SAADC_CH_CONFIG_GAIN_Pos | ADC_SAMPLETIME_CFG_SLOW &amp;lt;&amp;lt; SAADC_CH_CONFIG_TACQ_Pos;
  NRF_SAADC-&amp;gt;CH[ADC_CH_MPPT_VOLTAGE].CONFIG = SAADC_CH_CONFIG_REFSEL_Internal &amp;lt;&amp;lt; SAADC_CH_CONFIG_REFSEL_Pos | SAADC_CH_CONFIG_GAIN_Gain1_6 &amp;lt;&amp;lt; SAADC_CH_CONFIG_GAIN_Pos | ADC_SAMPLETIME_CFG_MPPT_VOLTAGE &amp;lt;&amp;lt; SAADC_CH_CONFIG_TACQ_Pos;
  NRF_SAADC-&amp;gt;CH[ADC_CH_MPPT_CURRENT].CONFIG = SAADC_CH_CONFIG_REFSEL_Internal &amp;lt;&amp;lt; SAADC_CH_CONFIG_REFSEL_Pos | SAADC_CH_CONFIG_GAIN_Gain1_6 &amp;lt;&amp;lt; SAADC_CH_CONFIG_GAIN_Pos | ADC_SAMPLETIME_CFG_MPPT_CURRENT &amp;lt;&amp;lt; SAADC_CH_CONFIG_TACQ_Pos;
  
  NRF_PPI-&amp;gt;CH[CFG_PPI_ADC_SAMPLEONSTART].EEP = (uint32_t) &amp;amp;NRF_SAADC-&amp;gt;EVENTS_STARTED;
  NRF_PPI-&amp;gt;CH[CFG_PPI_ADC_SAMPLEONSTART].TEP = (uint32_t) &amp;amp;NRF_SAADC-&amp;gt;TASKS_SAMPLE;
  NRF_PPI-&amp;gt;CH[CFG_PPI_ADC_SAMPLEBYTIMER].EEP = (uint32_t) &amp;amp;NRF_RTC1-&amp;gt;EVENTS_TICK;
  NRF_PPI-&amp;gt;CH[CFG_PPI_ADC_SAMPLEBYTIMER].TEP = (uint32_t) &amp;amp;NRF_SAADC-&amp;gt;TASKS_SAMPLE;
  
  NVIC_SetPriority(SAADC_IRQn, CFG_INTERRUPT_PRIORITY_ADC);
  NVIC_EnableIRQ(SAADC_IRQn);
}

void ADC_Shutdown (void) {

  NRF_SAADC-&amp;gt;ENABLE = SAADC_ENABLE_ENABLE_Disabled &amp;lt;&amp;lt; SAADC_ENABLE_ENABLE_Pos;
}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;Here&amp;#39;s an example of when it switches to crap out. High reads are valid.&lt;br /&gt;&lt;br /&gt;&amp;lt;info&amp;gt; app: Store 625&lt;br /&gt;&amp;lt;info&amp;gt; app: Store 633&lt;br /&gt;&amp;lt;info&amp;gt; app: Store 626&lt;br /&gt;&amp;lt;info&amp;gt; app: Store 628&lt;br /&gt;&amp;lt;info&amp;gt; app: Store 627&lt;br /&gt;&amp;lt;info&amp;gt; app: Store 632&lt;br /&gt;&amp;lt;info&amp;gt; app: Store 632&lt;br /&gt;&amp;lt;info&amp;gt; app: Store 5&lt;br /&gt;&amp;lt;info&amp;gt; app: Store 7&lt;br /&gt;&amp;lt;info&amp;gt; app: Store 11&lt;br /&gt;&amp;lt;info&amp;gt; app: Store 12&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item></channel></rss>