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

TIMER + PWM + SAADC + PPI

I'd like to control a DC motor using PWM (PWM0, pretty straight forward). To determine motor speed I disable the PWM every 5ms (TIMER1, pretty straight forward) and measure Back-EMF via SAADC. To synchronise this I'd like to use PPI

In order to implement a stable solution I'd like to clarify a few things with you:

1. Start SAADC

err = nrf_drv_ppi_channel_assign (ppiCh, (uint32_t)&NRF_TIMER1->EVENTS_COMPARE[1], (uint32_t)&NRF_SAADC->TASKS_START);

NRF_PPI->FORK[ppiCh].TEP = (uint32_t)&NRF_SAADC->TASKS_SAMPLE;

Do I need the FORK instruction? What is the difference between START and SAMPLE? I find the manual is not very clear on this.

2. SAADC multi-channel Every 5ms (i.e. when PWM disabled) I'd like to sample 2 channels. Here is my code

nrf_saadc_channel_config_t  adcChannel0 = NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE (AIN0);
adcChannel0.gain      = NRF_SAADC_GAIN1_4;
adcChannel0.reference = NRF_SAADC_REFERENCE_VDD4;
err = nrf_drv_saadc_channel_init (0, &adcChannel0);

nrf_saadc_channel_config_t  adcChannel1 = NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE (AIN1);
adcChannel1.gain      = NRF_SAADC_GAIN1_4;
adcChannel1.reference = NRF_SAADC_REFERENCE_VDD4;
err = nrf_drv_saadc_channel_init (1, &adcChannel1);

In case that one channel is configured (AIN0 only) my code works as expected In case that two channels are configured (AIN0/1, as per example above) the 5ms time slots are lost. Why? We're working with Rev1 samples. Can we use Scan Mode? There are numerous references about it being buggy...

3. SAADC results

I believe I understand the concept behind this:

NRF_SAADC->RESULT.PTR    = (uint32_t)mAdcResult;
NRF_SAADC->RESULT.MAXCNT = 1;

When running my code I doubt that values in mAdcResult are correct. They are updated but the value doesn't change even though the actual analog voltage does.

4. Other stuff Here is some more you might need to know:

nrf_drv_saadc_init (NULL, onAdcEvent);

static void  onAdcEvent (nrf_drv_saadc_evt_t const *event)
{
    if (event->type == NRF_DRV_SAADC_EVT_DONE)
    {
        NRF_SAADC->TASKS_STOP = 1;  <-- Do I need this ?
        NRF_SAADC->EVENTS_END = 0;  <-- Do I need this ?

        nrf_gpio_pin_toggle (PIN_DEBUG);
    }
}

SAADC resolution: set to 10bit SAADC oversamling: set to disabled

Thanky for your help

Parents
  • Hi

    Difference between START and SAMPLE: Figure 4 in the SAADC specification shows this a bit. You need to call start to prepare the RAM buffer and you call START once for every buffer setup. However, you call SAMPLE for every sample.

    Do you not want to use the nrf_drv_saadc driver? The SAADC example in the SDK uses the driver, which seems to make the setup much easier. If you do not want to use the driver, I think it is good to look at the code in nrf_drv_saadc.c file to see how the driver sets up things.

    Sync PWM and SAADC events: From your description I imagine the procedure could be as follows:

    1. Setup timer with 5ms periodic interrupts
    2. On every interrupt, disable PWM, then sample once with SAADC. Enable oversampling for better noise immunity
    3. Change PWM duty cycle based on SAADC measurement
    4. Enable PWM

    With the SAADC driver, you enable oversampling in the nrf_drv_config.h file, which is the driver configuration file, example provided here. Also taking a single sample is a matter of calling nrf_drv_saadc_sample_convert driver function. The nrf_drv_saadc_sample_convert call is blocking so it will return the SAADC value and you do not need to implement any nrf_drv_saadc callback function.

    The SAADC scan mode is not operational for nRF52832 Engineering A, i.e. the nRF52-DK preview kit, but should be good for the nRF52-DK which holds nRF52 Engineering B chip and later. Do you think there are references for the scan mode to not work for Engineering B?

    If you use the driver, you do not need to worry about details, the driver would do that for you.

    The nrf_drv_saadc driver implements the SAADC interrupt hanlder internally and does all the necessary stuff. Look at the SAADC example in the SDK to realize what you need to implement in the nrf_drv_saadc callback.

  • Thanks for the information. My first response would be that you are using an SDK that is compatible with Engineering A only, but you are using Engineering C. SDK 11.0.0 should be the correct one to use with Engineering C, which should implement the correct workarounds for listed anomalies.

    Anyhow, from the Errata document for engineering C I see no obvious reason for your unexpected behavior in the list of anomalies.

    My recommendation for now: Try our code with SDK 11.0.0 to see if you can reproduce the issue. If you can reproduce it, I would try to reproduce it on my side.

    P.S. Is this behavior consistent if you change the timing of events, e.g. make events far apart from each other?

Reply
  • Thanks for the information. My first response would be that you are using an SDK that is compatible with Engineering A only, but you are using Engineering C. SDK 11.0.0 should be the correct one to use with Engineering C, which should implement the correct workarounds for listed anomalies.

    Anyhow, from the Errata document for engineering C I see no obvious reason for your unexpected behavior in the list of anomalies.

    My recommendation for now: Try our code with SDK 11.0.0 to see if you can reproduce the issue. If you can reproduce it, I would try to reproduce it on my side.

    P.S. Is this behavior consistent if you change the timing of events, e.g. make events far apart from each other?

Children
No Data
Related