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.