How to use ADC in continous mode on nrf5340 dk?

Hello,I am currently using nrf5340 DK.I have successfully run this samplehttps://docs.zephyrproject.org/latest/samples/drivers/adc/README.html.Now I want to use ADC continuous sampling mode to collect data, how do I configure it?Do you have any relevant examples?

looking forward to your reply.

Parents
  • Hello,

    Thank you for your extreme patience with this.

    Could you elaborate more on what sort of functionality you are after?
    For instance, if you are only looking to sample 1 channel you can use the built-in SAADC timer for continuous mode directly, but if you would like to sample multiple channels we will have to trigger the sampling externally from the SAADC.
    What sampling frequency will you be sampling with?

    The best practice for lower power consumption for a high-frequency SAADC sampling application is to use the RTC to trigger the SAADC sampling through PPI - you can see an example of how this could be done here.

    Please do not hesitate to ask if any part of this should be unclear, or if you have any follow-up questions! :) 

    Best regards,
    Karl

  • Hello,

    Thank you for your meticulous reply.

    Could you elaborate more on what sort of functionality you are after?

    I want to collect voltage through adc sampling, convert it into temperature, and transmit temperature value to app through BLE.I was able to achieve continuous sampling mode by modifying the size of extra_samplings in the adc_sequence_options structure.Is this the right thing to do?

    Here is the section of my main.c function

    /* main.c - Application main entry point */
    
    /*
     * Copyright (c) 2019 Aaron Tsui <[email protected]>
     *
     * SPDX-License-Identifier: Apache-2.0
     */
    #include <stdint.h>
    #include <zephyr/types.h>
    #include <stddef.h>
    #include <string.h>
    #include <errno.h>
    #include <zephyr/sys/printk.h>
    #include <zephyr/sys/byteorder.h>
    #include <zephyr/kernel.h>
    
    #include <zephyr/drivers/adc.h>
    #include <zephyr/bluetooth/bluetooth.h>
    #include <zephyr/bluetooth/hci.h>
    #include <zephyr/bluetooth/conn.h>
    #include <zephyr/bluetooth/uuid.h>
    #include <zephyr/bluetooth/gatt.h>
    // #include <zephyr/bluetooth/services/bas.h>
    
    #include "hts.h"
    #include "adc_sample.h"
    static const struct bt_data ad[] = {
    	BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
    	BT_DATA_BYTES(BT_DATA_UUID16_ALL,
    		      BT_UUID_16_ENCODE(BT_UUID_HTS_VAL)
    		    //   BT_UUID_16_ENCODE(BT_UUID_DIS_VAL),
    		    //   BT_UUID_16_ENCODE(BT_UUID_BAS_VAL)
                ),
    };
    
    // #define BUFFER_SIZE 5 
    int sample_complete;
    enum adc_action sample_callback(const struct device *dev,
                                    const struct adc_sequence *sequence,
                                    uint16_t sampling_index)
    {
        if (sampling_index == BUFFER_SIZE-1)
        {
            sample_complete = 1;
            printk("sample done\n");
            return ADC_ACTION_FINISH;
        }
        // printf("Sampling index: %u, Channel value: %d\n", sampling_index, sequence->channels);
        return ADC_ACTION_CONTINUE;  
    }
    
    const struct adc_sequence_options sequence_opts = {
    	.interval_us = 0,
    	.callback = sample_callback,
    	.user_data = NULL,
    	.extra_samplings = BUFFER_SIZE-1,
    };
    
    
    
    
    static void connected(struct bt_conn *conn, uint8_t err)
    {
    	if (err) {
    		printk("Connection failed (err 0x%02x)\n", err);
    	} else {
    		printk("Connected\n");
    	}
    }
    
    static void disconnected(struct bt_conn *conn, uint8_t reason)
    {
    	printk("Disconnected (reason 0x%02x)\n", reason);
    }
    
    BT_CONN_CB_DEFINE(conn_callbacks) = {
    	.connected = connected,
    	.disconnected = disconnected,
    };
    
    static void bt_ready(void)
    {
    	int err;
    
    	printk("Bluetooth initialized\n");
    
    	hts_init();
    
    	err = bt_le_adv_start(BT_LE_ADV_CONN_NAME, ad, ARRAY_SIZE(ad), NULL, 0);
    	if (err) {
    		printk("Advertising failed to start (err %d)\n", err);
    		return;
    	}
    
    	printk("Advertising successfully started\n");
    }
    
    static void auth_cancel(struct bt_conn *conn)
    {
    	char addr[BT_ADDR_LE_STR_LEN];
    
    	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
    
    	printk("Pairing cancelled: %s\n", addr);
    }
    
    static struct bt_conn_auth_cb auth_cb_display = {
    	.cancel = auth_cancel,
    };
    
    
    
    void main(void)
    {
    	int err;
        double temp;
    	err = bt_enable(NULL);
    	if (err) {
    		printk("Bluetooth init failed (err %d)\n", err);
    		return;
    	}
    
    	bt_ready();
    
    	bt_conn_auth_cb_register(&auth_cb_display);
    
    	/* Implement indicate. At the moment there is no suitable way
    	 * of starting delayed work so we do it here
    	 */
    
        uint16_t buf[BUFFER_SIZE]; 
        struct adc_sequence sequence = {
            .options = &sequence_opts,
            .buffer = &buf,
            /* buffer size in bytes, not number of samples */
            .buffer_size = sizeof(buf),
        };
    
    
        err = adc_sample(&sequence);
        if(err) {
            printk("start adc sample failed\n");
            return;
        }
    	while (1) {
    		k_sleep(K_SECONDS(2));
            if(sample_complete)
            {
    		    /* Temperature measurements simulation */
                sample_complete = 0;
                temp = adc_data_convert_to_temp(buf);
    		    hts_indicate(temp);            
            }
            k_sleep(K_SECONDS(2));
            err = adc_sample_restart(&sequence);
            if(err) {
                printk("adc sample restart failed\n");
            }
    		// /* Battery level simulation */
    		// bas_notify();
    	}
    }

    However, I had another strange problem.I use continuous sampling mode, when the buffer size is less than 5, the program works fine; However, when I adjust the buffer size to 10 or more, after the flash is successful, the serial port window will keep popping up the following message and there's no broadcast information on the app.:

    *** Booting Zephyr OS build v3.2.99-ncs2 ***
    *** Booting Zephyr OS build v3.2.99-ncs2 ***
    *** Booting Zephyr OS build v3.2.99-ncs2 ***
    *** Booting Zephyr OS build v3.2.99-ncs2 ***
    *** Booting Zephyr OS build v3.2.99-ncs2 ***
    *** Booting Zephyr OS build v3.2.99-ncs2 ***
    *** Booting Zephyr OS build v3.2.99-ncs2 ***

    At first I thought that CONFIG_MAIN_STACK_SIZE was too small, but when I reset it to a larger value, it still reported the same error.Is there any solution?

    Best regards,

    Danny

  • Hello Danny,

    Danny- said:
    I want to collect voltage through adc sampling, convert it into temperature, and transmit temperature value to app through BLE.I was able to achieve continuous sampling mode by modifying the size of extra_samplings in the adc_sequence_options structure.Is this the right thing to do?

    This depends on the frequency of the sampling. How fast do you intend for it to be sampling during regular operation?
    What you are doing now achieves the goal, but it might not be exactly what you are looking for.
    In your case, all these samples are taken back-to-back as fast as possible. This could be very useful in a scenario where you have a rapidly changing signal, but it could be unnecessary for a signal such as temperature, which changes very slowly.
    Instead, you might want to use the RTC + PPI trigger approach that I referenced in my previous reply, since this lets you set up an interval for the measurements to be taken, which considerably lowers power consumption.

    If you still would like to reduce noise in the measurement as much as possible you can use the BURST + OVERSAMPLING features of the SAADC  if you would like for each sampling to be the average of a burst of samples, taken in as short a time as possible.

    Danny- said:
    However, I had another strange problem.I use continuous sampling mode, when the buffer size is less than 5, the program works fine; However, when I adjust the buffer size to 10 or more, after the flash is successful, the serial port window will keep popping up the following message and there's no broadcast information on the app.:

    This means that your application keeps rebooting, which is likely because it is hitting an error somewhere.
    Is the only change you are doing to change the buffer size, to provoke this error?

    Best regards,
    Karl

Reply
  • Hello Danny,

    Danny- said:
    I want to collect voltage through adc sampling, convert it into temperature, and transmit temperature value to app through BLE.I was able to achieve continuous sampling mode by modifying the size of extra_samplings in the adc_sequence_options structure.Is this the right thing to do?

    This depends on the frequency of the sampling. How fast do you intend for it to be sampling during regular operation?
    What you are doing now achieves the goal, but it might not be exactly what you are looking for.
    In your case, all these samples are taken back-to-back as fast as possible. This could be very useful in a scenario where you have a rapidly changing signal, but it could be unnecessary for a signal such as temperature, which changes very slowly.
    Instead, you might want to use the RTC + PPI trigger approach that I referenced in my previous reply, since this lets you set up an interval for the measurements to be taken, which considerably lowers power consumption.

    If you still would like to reduce noise in the measurement as much as possible you can use the BURST + OVERSAMPLING features of the SAADC  if you would like for each sampling to be the average of a burst of samples, taken in as short a time as possible.

    Danny- said:
    However, I had another strange problem.I use continuous sampling mode, when the buffer size is less than 5, the program works fine; However, when I adjust the buffer size to 10 or more, after the flash is successful, the serial port window will keep popping up the following message and there's no broadcast information on the app.:

    This means that your application keeps rebooting, which is likely because it is hitting an error somewhere.
    Is the only change you are doing to change the buffer size, to provoke this error?

    Best regards,
    Karl

Children
  • Hello,

    Thank you very much for your patient and meticulous reply!

    This depends on the frequency of the sampling. How fast do you intend for it to be sampling during regular operation?

    Sorry I didn't think about the sampling rate, I just set the default sampling time like ADC_ACQ_TIME_DEFAULT in my overlay file.Is there any influence between them.

    Instead, you might want to use the RTC + PPI trigger approach that I referenced in my previous reply, since this lets you set up an interval for the measurements to be taken, which considerably lowers power consumption

    I really did not consider the power consumption problem, I will try to switch to the practice as you recommend.

    Is the only change you are doing to change the buffer size, to provoke this error?

    Yes,I only changed the CONFIG_MAIN_STACK_SIZE.

    Best regards,

    Danny

  • Hello Danny,

    Danny- said:
    Thank you very much for your patient and meticulous reply!

    No problem at all, I am happy to help! :) 

    Danny- said:
    Sorry I didn't think about the sampling rate, I just set the default sampling time like ADC_ACQ_TIME_DEFAULT in my overlay file.Is there any influence between them.

    The acquisition time is just the time whether the capacitor will be collecting charge for the sampling. You will still need to configure the sampling frequency by configuring how often each sampling will start.
    In the previous case you had the samples performed back-to-back, in which case the sampling frequency would be equal to acquisition time + conversion time (2µs), but when you space the measurements out more then the sampling time will depend on whatever event you are using to trigger the samples (such as a TIMER or RTC CC event).

    Danny- said:
    I really did not consider the power consumption problem, I will try to switch to the practice as you recommend.

    Great! Let me know if you should encounter any issues or questions when you give this a try.

    Danny- said:
    Yes,I only changed the CONFIG_MAIN_STACK_SIZE.

    Thank you for confirming - let's revisit this issue if you ever go back to the previous approach, since I do not think it will be relevant for the PPI + RTC approach that you are going for now.

    Best regards,
    Karl

Related