I want to read around 6 analog pins, not necessarily simultaneously. I could afford some delay in between them. i am using nrf52832 dev kit with sdk 15. How do i modify the SAADC example to achieve the same because the example only reads 1 channel. i did found this thread https://devzone.nordicsemi.com/f/nordic-q-a/5561/adc-from-various-pins/19481#19481 which looks similar but i was unable to open the GitHub link provided in the thread. Can you please direct me to the relevant examples.
It should be fine to measure 6 ADC channels at once.
Attached is a slightly modified project from the SDK15.0.0\examples\peripheral\saadc example. Only the main.c file is changed. It will measure the 6 pins AIN0 - AIN5, and print the values in the UART log.
Remember that AIN0 is not the same as A0 printed on the PCB. The AINx pins are:
AIN0 = P0.02AIN1 = P0.03AIN2 = P0.04AIN3 = P0.05AIN4 = P0.28AIN5 = P0.29
Hello Edvin, that was a great help. I was wondering what would i have to do to take the readings from the other 2 analog pins? it does not have to be at same time. could be after a delay, but how do i take those readings as well?
The two other pins? AIN6 and AIN7?
Just change the channel_X_config in saadc_init() to use NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(pin_nr) with the correct pin_nr. You can find the list of the AIN pins in the list on this page.
AIN6 = P0.30 and AIN7 = P0.31.
You can either add them to the list in saadc_init, like the other are added:
nrf_saadc_channel_config_t channel_N_config = NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN#);
nrf_drv_saadc_channel_init(channel_nr, &channel config);
Note that you should match your #define SAMPLES_IN_BUFFER with the number of channels that you use. If you have 6 channels, and SAMPLES_IN_BUFFER is 5, then you will get the values from channel 0, 1, 2, 3, 4 in the first event, and 5, 0, 1, 2, 3 in the second, 4, 5, 0, 1, 2 in the third, and so on.
void saadc_callback(nrf_drv_saadc_evt_t const * p_event)
if (p_event->type == NRF_DRV_SAADC_EVT_DONE)
// set buffers
err_code = nrf_drv_saadc_buffer_convert(p_event->data.done.p_buffer, SAADC_SAMPLES_IN_BUFFER);
// print samples on hardware UART and parse data for BLE transmission
printf("ADC event number: %d\r\n",(int)m_adc_evt_counter);
for (int i = 0; i < SAADC_SAMPLES_IN_BUFFER; i++)
adc_value = p_event->data.done.p_buffer[i];
value[i*2] = adc_value;
value[(i*2)+1] = adc_value >> 8;
// Send data over BLE via NUS service. Makes sure not to send more than 20 bytes.
if((SAADC_SAMPLES_IN_BUFFER*2) <= 20)
bytes_to_send = (SAADC_SAMPLES_IN_BUFFER*2);
bytes_to_send = 20;
err_code = ble_nus_string_send(&m_nus, value, bytes_to_send);
if (err_code != NRF_ERROR_INVALID_STATE)
Hello Edvin, thank you for your explanation. Regarding this attached saadc callback function. When is this function called, which interrupt calls this function? and also how do i convert the raw data into voltage. just take the data, then multiply by 5V and divide by 1023 ? or does it function differently? Thanks and Regards!
In the SAADC example you can see two functions which sets up and enables the saadc callback:
saadc_sampling_event_init() and saadc_sampling_event_enable().
What they do is that they set up a timer and a PPI, and connect the two. This means that the timer interrupt is not called in the application, but when the timeout occurs, the chip will automatically call nrfx_saadc_sample().
When this function is called N times, where N is the size of your SAADC buffer, that is N = SAMPLES_IN_BUFFER, you will get the callback function.
I don't know whether you are familiar with the PPI, but it is a module which, when enabled, can perform tasks without involving the CPU. In this case, it works a bit like an advanced timer.
I really recommend that you use this implementation to trigger the samples. It will not work to call nrfx_saadc_sample() N times in a row in the application, because the sample has to finish before calling nrfx_saadc_sample again.
Regarding the conversion from the raw SAADC value to voltage, there are a few things to take into consideration:
Your resolution (by default set to 10 bits, your gain, usually set to 1/6.
Please see this scheeme:
From the link:
RESULT = [V(P) – V(N) ] * GAIN/REFERENCE * 2(RESOLUTION - m)Which means that V(P) - V(N) = RESULT/ (GAIN/REFERENCE * 2(RESOLUTION - m))The result is linear, so you can also do some measurements to known voltages (using a voltmeter), and map the result to voltage using this.Best regards,Edvin
V(p) : voltage at input p
V(n): voltage at input n, I do not exactly understand this. What are N and P? reference voltage and input voltage?
Thanks and Regards,
This is the positive and negative inputs. I don't know exactly where you found this, but it is possible to use a different reference voltage than GND. The default setup is to use pin_n as ground (NRF_SAADC_INPUT_DISABLE).
If you want to measure the voltage between two pins, where none of them are GND, you can use pin_n = another pin, and the result will be the difference between the pins.
I was referring this post in the datasheet. In my case I only want to measure the voltage from the interfaced sensor on the analog pins. So using this formulae: my Vp would be the voltage on the pin from the sensors and Vn would be 0 if I use reference voltage as 0? does it sound right?
Yes. That is correct. If you set Vn = NRF_SAADC_INPUT_DISABLE (the default setting) it will measure the voltage difference between GND (0) and Vp.
How can i convert my adc values into float?
I do not see any result when i try to cast these values into float.
Don't see any result, or do you see some results, but not the ones you are looking for? (Does the application stop, or does it print blank values?)
Please see this post.
As described, you must use NRF_LOG_FLOAT_MARKER instead of %f when using NRF_LOG to print float values.