<?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>SAADC Calibration Lock Up</title><link>https://devzone.nordicsemi.com/f/nordic-q-a/35239/saadc-calibration-lock-up</link><description>I am trying to implement a periodic calibration routine that is transparent to upper layers of my application. 
 
 I have the SAADC set up to continuously sample one channel (oversample X4) and I am dual buffering into two 512-byte buffers. I call calibration</description><dc:language>en-US</dc:language><generator>Telligent Community 13</generator><lastBuildDate>Fri, 15 Jun 2018 14:55:46 GMT</lastBuildDate><atom:link rel="self" type="application/rss+xml" href="https://devzone.nordicsemi.com/f/nordic-q-a/35239/saadc-calibration-lock-up" /><item><title>RE: SAADC Calibration Lock Up</title><link>https://devzone.nordicsemi.com/thread/136404?ContentTypeID=1</link><pubDate>Fri, 15 Jun 2018 14:55:46 GMT</pubDate><guid isPermaLink="false">137ad170-7792-4731-bb38-c0d22fbe4515:cc72518b-b6f7-4cb3-8e3b-a34a25e4bca7</guid><dc:creator>J&amp;#248;rgen Holmefjord</dc:creator><description>&lt;p&gt;Can you try enabling interrupts for the stopped event in abort function, just to check if this makes any difference?&lt;/p&gt;
&lt;p&gt;Add the line&amp;nbsp;&lt;code&gt;nrf_saadc_int_enable(NRF_SAADC_INT_STOPPED); &lt;/code&gt;before triggering TASK_STOP:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="c_cpp"&gt;void nrf_drv_saadc_abort(void)
{
    if (nrf_drv_saadc_is_busy())
    {
        nrf_saadc_int_enable(NRF_SAADC_INT_STOPPED);
        nrf_saadc_event_clear(NRF_SAADC_EVENT_STOPPED);
        nrf_saadc_task_trigger(NRF_SAADC_TASK_STOP);&lt;/pre&gt;&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>RE: SAADC Calibration Lock Up</title><link>https://devzone.nordicsemi.com/thread/136156?ContentTypeID=1</link><pubDate>Thu, 14 Jun 2018 12:14:05 GMT</pubDate><guid isPermaLink="false">137ad170-7792-4731-bb38-c0d22fbe4515:729df708-92f9-401a-8bee-67af41c28281</guid><dc:creator>George Beckstien</dc:creator><description>&lt;p&gt;My adc configuration is as follows:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="c_cpp"&gt;nrf_drv_saadc_config_t adc_config = {
           .resolution         = NRF_SAADC_RESOLUTION_14BIT
           .oversample         = NRF_SAADC_OVERSAMPLE_4X
           .interrupt_priority = 6,
           .low_power_mode     = 0
       };&lt;/pre&gt;&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>RE: SAADC Calibration Lock Up</title><link>https://devzone.nordicsemi.com/thread/136037?ContentTypeID=1</link><pubDate>Thu, 14 Jun 2018 06:21:49 GMT</pubDate><guid isPermaLink="false">137ad170-7792-4731-bb38-c0d22fbe4515:a3453861-f2dc-407d-9e1f-00e2ba269801</guid><dc:creator>J&amp;#248;rgen Holmefjord</dc:creator><description>&lt;p&gt;Could you check if the SAADC driver is configured in &lt;a href="http://infocenter.nordicsemi.com/topic/com.nordic.infocenter.sdk5.v14.2.0/structnrf__drv__saadc__config__t.html#ae20a95b6edce77c9196887e1b80dc406"&gt;low-power mode&lt;/a&gt;? There is a &lt;a href="https://devzone.nordicsemi.com/f/nordic-q-a/29806/nrf52832-sdk14---saadc-calibration-issues/118302#118302"&gt;known bug&lt;/a&gt; when this mode this enabled, causing timeout in abort function.&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>RE: SAADC Calibration Lock Up</title><link>https://devzone.nordicsemi.com/thread/136000?ContentTypeID=1</link><pubDate>Wed, 13 Jun 2018 17:31:30 GMT</pubDate><guid isPermaLink="false">137ad170-7792-4731-bb38-c0d22fbe4515:ce19dfda-4314-4735-a6a4-2ba89e09cf93</guid><dc:creator>George Beckstien</dc:creator><description>&lt;p&gt;It appears the nrf_drv_saadc_abort() function times out after attempting to trigger NRF_SAADC_TASK_STOP:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="c_cpp"&gt;void nrf_drv_saadc_abort(void)
{
    if (nrf_drv_saadc_is_busy())
    {
        nrf_saadc_event_clear(NRF_SAADC_EVENT_STOPPED);
        nrf_saadc_task_trigger(NRF_SAADC_TASK_STOP);

        if (m_cb.adc_state == NRF_SAADC_STATE_CALIBRATION)
        {
            m_cb.adc_state = NRF_SAADC_STATE_IDLE;
        }
        else
        {
            // Wait for ADC being stopped.
            uint32_t timeout = HW_TIMEOUT;

            while ((m_cb.adc_state != NRF_SAADC_STATE_IDLE) &amp;amp;&amp;amp; (timeout &amp;gt; 0))
            {
                --timeout;
            }
            
            // ERROR - THIS IS TIMING OUT FOR SOME REASON!
            
            ASSERT(timeout &amp;gt; 0);
        }

        m_cb.p_buffer           = 0;
        m_cb.p_secondary_buffer = 0;
        NRF_LOG_INFO(&amp;quot;Conversion aborted.&amp;quot;);
    }
}&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;EDIT:&lt;/p&gt;
&lt;p&gt;The SAADC driver IRQ handler relies on the NRF_SAADC_EVENT_STOPPED interrupt happening to complete the nrf_drv_saadc_abort() process (setting the internal state back to IDLE).&lt;/p&gt;
&lt;p&gt;However, it appears that the driver never actually enables this interrupt (I checked the SAADC INTEN register, 0x40007300 and it was 0x00000002).&lt;/p&gt;
&lt;p&gt;So the driver is relying on an interrupt that it never enabled to complete processing the abort request. This seems like a bug.&lt;/p&gt;
&lt;p&gt;I&amp;#39;m currently working with nRF SDK version 14.2 (the version mbed is on).&lt;/p&gt;
&lt;p&gt;EDIT AGAIN:&lt;/p&gt;
&lt;p&gt;Considering the event flow, it seems that my above assumption that the driver needs to enable the STOPPED_EVENT interrupt is incorrect. The interrupt should be executed when the STOPPED event also causes the SAADC END event.&lt;/p&gt;
&lt;p&gt;Any suggestions for how to properly trigger and resume from calibration with the SAADC using interrupts?&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>RE: SAADC Calibration Lock Up</title><link>https://devzone.nordicsemi.com/thread/135998?ContentTypeID=1</link><pubDate>Wed, 13 Jun 2018 17:14:11 GMT</pubDate><guid isPermaLink="false">137ad170-7792-4731-bb38-c0d22fbe4515:ce59414d-03cd-4f4f-b94c-d5f14805d998</guid><dc:creator>George Beckstien</dc:creator><description>&lt;p&gt;I checked the priority, it appears mbed instantiates Timer/Tickers with the lowest interrupt priority of 7. I changed the interrupt priority of the SAADC IRQ handler to 6 and then to 1 and still it gets stuck when calibrating.&lt;/p&gt;
&lt;p&gt;Here is my driver&amp;#39;s interrupt handler:&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="c_cpp"&gt;static void microphone_in_irq_handler(nrf_drv_saadc_evt_t const* event)
{
	// Get the microphonein_t instance
	microphonein_t* instance = (microphonein_t*) m_instance;

	/*
	 * For some reason, Nordic FW engineers called this EVT_DONE
	 * This is triggered when a sample buffer is filled
	 * While Nordic HW engineers called this EVT_END while EVT_DONE
	 * means only one conversion is done...
	 */
	if (event-&amp;gt;type == NRF_DRV_SAADC_EVT_DONE)
	{
		/*
		 * If the SAADC is aborted in the middle of a sample buffer, it will
		 * still signal this handler. So we need to check to make sure the whole
		 * buffer has been filled
		 */
		if(event-&amp;gt;data.done.size == instance-&amp;gt;buffer_len || instance-&amp;gt;calibrating)
		{
			/*
			 * Normal operation - buffer has been completely filled
			 *
			 * When a buffer is filled, we queue up that same buffer to the SAADC EasyDMA
			 * Since the buffer address is double-buffered, this will take effect
			 * after the next buffer is full
			 *
			 */
			nrf_drv_saadc_buffer_convert(event-&amp;gt;data.done.p_buffer,
					instance-&amp;gt;buffer_len);

			// Notify the application
			if(m_irq_handler)
				m_irq_handler(m_id, (uint16_t*) event-&amp;gt;data.done.p_buffer);

		}
		else
		{
			if(instance-&amp;gt;running)
			{
				if(instance-&amp;gt;calibrating)
				{
					/*
					 * Sampling was aborted due to calibration,
					 * retain samples that were already done and restart
					 * sampling after calibration where we left off.
					 *
					 * This will result in a &amp;lt;1ms gap in samples.
					 */
					instance-&amp;gt;current_buffer = (uint16_t*) event-&amp;gt;data.done.p_buffer;
					instance-&amp;gt;offset = event-&amp;gt;data.done.size;
				}
				else
				{
					/*
					 * Buffer size was shorter due to restarting
					 * a previously-aborted sampling. We must make sure
					 * to report the appropriate buffer to the application
					 * in this case
					 */

					// Queue up the full size buffer now from the beginning
					nrf_drv_saadc_buffer_convert(
							(nrf_saadc_value_t*) instance-&amp;gt;current_buffer,
							instance-&amp;gt;buffer_len);

					// Notify the application
					if(m_irq_handler)
						m_irq_handler(m_id, instance-&amp;gt;current_buffer);
				}
			}

			// In this case, sampling was aborted by calling microphonein_stop()
		}

	}
	else if(event-&amp;gt;type == NRF_DRV_SAADC_EVT_CALIBRATEDONE)
	{
		instance-&amp;gt;calibrating = 0;

		// Calibration requires ongoing sampling to be aborted
		// So we must restart from where we left off and queue up the other buffer
		nrf_drv_saadc_buffer_convert(
				(nrf_saadc_value_t*) (instance-&amp;gt;current_buffer + instance-&amp;gt;offset),
				(instance-&amp;gt;buffer_len - instance-&amp;gt;offset));

		// Get the appropriate next buffer
		nrf_saadc_value_t* next_buffer = (nrf_saadc_value_t*) instance-&amp;gt;buffer_1;
		if(instance-&amp;gt;current_buffer == instance-&amp;gt;buffer_2)
			next_buffer = (nrf_saadc_value_t*) instance-&amp;gt;buffer_2;

		nrf_drv_saadc_buffer_convert((nrf_saadc_value_t*) next_buffer,
				instance-&amp;gt;buffer_len);

		// Trigger sampling
		nrf_drv_saadc_sample();
	}
}&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;And here is the calibration routine called every 5 seconds by the mbed Ticker driver (operating at interrupt priority 7):&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="c_cpp"&gt;void microphonein_calibrate(microphonein_t* obj)
{
	/*
	 * See:
	 * https://github.com/NordicPlayground/nRF52-ADC-examples/tree/b4fbd256c886e07a515a8570a2f3d8c01cc1e913/saadc_low_power#about-this-project
	 * For more information about calibration
	 *
	 * &amp;quot;In order to trigger the offset calibration task,
	 * the SAADC driver needs to be in IDLE mode. This is
	 * achieved by calling the abort task, which will abort
	 * all ongoing conversions. A flag is set to trigger the
	 * offset calibration task in the main context, when the
	 * driver have entered IDLE state. When calibration is
	 * done, the SAADC throws a &amp;quot;calibration done&amp;quot; event.
	 * When calibration is done, both buffers need to be setup
	 * for conversion again to keep double-buffering, since they
	 * were removed by the abort task. The table below shows how
	 * long it typically takes to calibrate the SAADC for different
	 * acquisition time settings:&amp;quot;
	 *
	 * |----------|----------------|-------------------------------|
	 * | Acq time | Length of Cal 0| Start cal until SAADC ready	|
	 * |----------|----------------|-------------------------------|
	 *	|  3 us    |     102 us		 |            118 us					|
	 *	|  10 us	  | 	  279 us 	 | 			  314 us					|
	 *	|  40 us	  | 	  988 us 	 | 			  1152 us				|
	 * |----------|----------------|-------------------------------|
	 */
	obj-&amp;gt;calibrating = 1;
	nrf_drv_saadc_abort();
	
	/** Execution hangs here - the driver status never goes to IDLE */
	while(nrf_drv_saadc_calibrate_offset() != NRF_SUCCESS);
}&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;I have static structures for storing driver state information (calibrating, running, etc).&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>RE: SAADC Calibration Lock Up</title><link>https://devzone.nordicsemi.com/thread/135527?ContentTypeID=1</link><pubDate>Mon, 11 Jun 2018 09:36:17 GMT</pubDate><guid isPermaLink="false">137ad170-7792-4731-bb38-c0d22fbe4515:e5001438-973c-4671-988e-130d1311e44e</guid><dc:creator>J&amp;#248;rgen Holmefjord</dc:creator><description>&lt;p&gt;What priority is the&amp;nbsp;Ticker running at? If it runs at a higher or equal priority to the SAADC, the SAADC IRQ handler will not be able to run to pull the driver out of BUSY state.&lt;/p&gt;
&lt;p&gt;Also make sure you do not init the buffers again in the SAADC handler after a abort call. This will put the SAADC driver back in BUSY state. You can set a flag in the ticker handler when you call abort and check this flag before setting up buffers.&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item></channel></rss>