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

ADC / PPI: SDK v11.0.0 works but v12.2.0 does not

I was building my own custom application that connects a nrf_drv_timer to an ADC start trigger using PPI on an nRF51822 and I could not get it to work. I asked a previous question about it here. This is a new question and a continuation of that exploration.

A strategy for figuring out how ADC / PPI should work

This is a continuation from that question. I decided that the best way to debug this issue would be to start with some working ADC scanning code that somebody has written previously for the nRF51822 and then modify it to work in the scenario that I need it to work in.

Proving that SDK 11.0.0 works (starting point)

The starting place that I picked was this repository: NordicSemiconductor/nrf51-ADC-examples

By updating the Makefile to point to the correct location for the SDK on my OSX machine I was able to get this compiling on my computer and working on my nRF51822. Importantly, it uses SDK 11.0.0. The differences that I made to the default repository are here and they are minimal. Using UART logging into minicom on my OSX machine I see this in the logs:

UART Start - with ADC !
  adc event counter: 1
ADC value channel 0: 173
ADC value channel 1: 187
ADC value channel 2: 174
ADC value channel 0: 173
ADC value channel 1: 182
ADC value channel 2: 174
  adc event counter: 2
ADC value channel 0: 173
ADC value channel 1: 181
ADC value channel 2: 174
ADC value channel 0: 173
ADC value channel 1: 181
ADC value channel 2: 174
  adc event counter: 3
ADC value channel 0: 173
ADC value channel 1: 180
ADC value channel 2: 174
ADC value channel 0: 173
ADC value channel 1: 180
ADC value channel 2: 174
<...snip...repeats endlessly...>

This is great. I am able to get a working version of ADC being read on my nRF51822 and being sent over the Bluetooth UART service to my android phone running the NRF toolkit. Awesome! It can work! I was pretty happy when this worked.

Getting the same code working on SDK 12.2.0

Now that I had a working example to start from I decided to try and migrate this code to v12.2.0 of the SDK. This version of the SDK has better internal logging for the modules and it has better utility functions avaliable. I would much prefer to be developing on v12.2.0 instead of v11.0.0.

To do that required a little bit of work but I managed to make the changes and get the code to compile.

You can view my modifications here.

However, the logs for this change look like this:

main.c:INFO:Connected...
main.c:INFO:Logging started.
PPI:INFO:Function: nrf_drv_ppi_init, error code: NRF_SUCCESS.
TIMER:INFO:Function: nrf_drv_timer_init, error code: NRF_SUCCESS.
TIMER:INFO:Timer id: 0, capture value set: 1536, channel: 0.
TIMER:INFO:Timer id: 0, capture value set: 1536, channel: 0.
TIMER:INFO:Enabled instance: 0.
PPI:INFO:Allocated channel: 0.
PPI:INFO:Function: nrf_drv_ppi_channel_alloc, error code: NRF_SUCCESS.
PPI:INFO:Assigned channel: 0, event end point: 4000a140, task end point: 40007000.
PPI:INFO:Function: nrf_drv_ppi_channel_assign, error code: NRF_SUCCESS.
ADC:INFO:Function: nrf_drv_adc_init, error code: NRF_SUCCESS.
ADC:INFO:Enabled.
ADC:INFO:Enabled.
ADC:INFO:Enabled.
ADC:INFO:Number of samples requested to convert: 6.
ADC:INFO:Function: nrf_drv_adc_buffer_convert, error code: NRF_SUCCESS.
PPI:INFO:Function: nrf_drv_ppi_channel_enable, error code: NRF_SUCCESS.
main.c:INFO:Connected...
<...nothing else is logged here except for connect / disconnect messages...no ADC logs messages...>

As you can see logging is started, the timer is instantiated, PPI is initialised: but no ADC readings are ever taken and logged. It is as though I have missed a step in the configuration of the PPI and caused those events to never be triggered.

This is where I need your help. I'm stuck at this point and can't get the ADC working or triggering. What am missing that is preventing this from working?

Concluding words and a question for you

At this point in time I have been pouring over the documentation and source code examples and am struggling to work out what I am doing wrong. My current thoughts are:

  • Maybe if I hook up GDB to this chip and keep scanning I'll be able to work out why the PPI is not triggering
  • Maybe v12.2.0 simply does not work with Timer 0 => PPI => ADC readings (unlikely, I'm sure that Nordic tests this stuff before releasing)
  • Maybe I should just add logging all over the SDK until I can figure out what is wrong.
  • Maybe I have configured something wrong in the "sdk_config.h" file

If anybody could help me work out what I am doing wrong that is preventing my 12.2.0 code from working then that would be much much appreciated.

  • One more thing. How do I work out what is the fastest possible ADC sampling rate for my application? Is it trial and error based on how much else is going on? Should I always try and buffer N samples before processing them in order to have less interrupts?

    I guess the docs are great in that they properly explain what each individual method does but they are lacking what I would call "Narrative" documentation; plain text documentation that explains how to bring all of the methods for a library or SOC together and explain their limits.

    Anyway, that is just a docs feature request. I'm going to use this to trial and error for my own device and reverse engineer the answers. Cheers!

  • On the nRF51 series, I'm not aware of any better method for determining the sample rate limitation than trial and error. When there is a lot of other things going on in your application, you will be have to limit the amount of interrupts to not break other time critical operations, like radio activity. With the nRF52 series, the ADC is updated to a SAADC with EasyDMA support. Using PPI, you can trigger the conversion task without intervention by the CPU. This will allow you to achieve the full sample rate, even with other operations going on in your application (given that your sample buffer is large enough to handle all the samples).

  • Interesting, so it is the number of interrupts that will be the limiting factor for the device. So the more that I batch the ADC readings the less interrupts that I will have and the less likely that my application is to run into CPU starvation issues? Excellent, that sounds good to me.

    Now I just have to figure out what a reasonable batch size will be for my application. :)

  • Yes, when using scan mode and TIMER/PPI to trigger the ADC_START task, you will only get a callback when the tash has been triggered BUFFER_SIZE/#CHANNELS times, as you experienced in your comment above.

Related