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

comp or lpcomp for 2 analog inputs with different v-- (ref)

i am working with nrf52832

i need to check 2 analog inputs and set my own v- (ref) 

i can have value read every few seconds so no fast readings needs to be done 

if i understand correctly i better use lpcomp if no fast readings is needed?

also,  is it possible, and if there are some code examples, to be reading 2 analog inputs with 2 different v-- (references) (one for each input pin) ?

best regards

Ziv

Parents
  • Hello, 

    if i understand correctly i better use lpcomp if no fast readings is needed?

    Yes, Lpcomp is good for low-power readings of pin state changes. Have you seen the documentation for the LPCOMP peripheral, and the lpcomp example in the SDK?

    also,  is it possible, and if there are some code examples, to be reading 2 analog inputs with 2 different v-- (references) (one for each input pin) ?

    We have no examples demonstrating this directly, but you may modify the lpcomp example to use the external references on pins AIN0 and AIN1.
    Since the LPCOMP is only made to monitor one pin at the time, you will have to switch between the two pins and their references, to check the levels.

    Have you considered using the SAADC with differential ended input instead of the LPCOMP?
    Depending on your application, this might be both easier to implement and more precise.

    Best regards,
    Karl

  • thanks Karl

    since i can comp (or lpcomp) only one input pin each time and to switch between them i have to go through the handler and CPU anyway the COMP option in my case doesn't really helps

    i have started working on configuring and reading the 3 inputs pin with saadc driver, it is also not so easy, for example it is not possible to do OVERSAMPLING with multiple pins (aka SCAN MODE) (i did made a small change to the NRFX_SAADC.C file to achieve just that) but actually it is still not so clear to me how to manage the 3 inputs if i want to read each one of the in a different timing and is less CPU intervention as possible (i saw the saadc example with the timer and ppi but i am not sure how to manage that with 3 inputs, i have tried to look for a way to change the configured pin with in the saadc event handler but it fail) 

    if the saadc is kind of a new topic it is possible to close this post (i am not sure how to do that)

    best regards

    Ziv

  • thanks

    i will check , i have been looking through the saadc driver and HAL API, i didn't find the way to configur some of the pins to HIGH or LOW values to use the event type:

    NRF_SAADC_EVENT_CH1_LIMITH 

    NRF_SAADC_EVENT_CH1_LIMITL

    if i am to use one saadc periferial with 3 channels (for 3 input pins), i wish to use 2 of them as high/low interrupts for the analog input and one that reads the values.. can you help with giving me direction or a close example?

    also, if i use the 'app_timer' , does it changes the way i work with the ppi in the "least CPU intensive wat" asspect ?

    best regards

    Ziv

  • also i wonder what if i have 3 instances of timer that activates the saadc sample and at one point both timer call the saadc sample (read values ->oversample = time consuming) task, wouldn't that cause a problem ?

  • Hello,

    You will need to make sure that the sampling criterias in the SAADC documentation is satisfied, to ensure proper function of the SAADC. Mainly, this would be the continuous mode criteria:

     fSAMPLE < 1/[tACQ + tconv]

    and the scan mode criteria: 

    Total time < Sum(CH[x].tACQ+tCONV), x=0..enabled channels

    As for the event that multiple triggers for the SAMPLE task happens at the same time, or in close succession I do not know what the output will be, since this is undefined behavior. My guess is that one of the triggers is ignored, which will cause you to loose a sample if you are going to only extract 1 of the samples each trigger.

    ziv123 said:
    also, if i use the 'app_timer' , does it changes the way i work with the ppi in the "least CPU intensive wat" asspect ?

    I am not sure I understand what you are asking? The app timer module sets up the timer peripheral, which does not require CPU interaction after it is started. However, if it is operating in single_shot_mode, then you will need to have the CPU restart the timer every so often, for example.

    ziv123 said:
    if i am to use one saadc periferial with 3 channels (for 3 input pins), i wish to use 2 of them as high/low interrupts for the analog input and one that reads the values.. can you help with giving me direction or a close example?

    Please see the "Limits event monitoring" section of the SAADC documentation.
    As it is described there, you must set each channels limits, which will then trigger the events you mention when they are exceeded. You could use the set_limits function for this. 
    Be advised that when having limits enabled and using scan mode, the limit event will trigger every time the channel is sampled and the limits are exceeded.

    Best regards,
    Karl

  • well .. not sure i know where to go now... 

    1. if i init sampeling (  nrf_drv_saadc_sample() ) via timer1 or flage in timer 1's handler, and uninit the saadc ( nrf_drv_saadc_uninit() ) in the saadc_callback function, i can risk calling for sampeling twice if i understood your answer correctly, which is something i don't want to do if it can cause problems

    so there is no known safe solution to do the analog readings for each channel in a different period of time (for each channel, to be clear),  if i understand corrctly again

    2. where this beeing my init:

    "

    ret_code_t err_code;

    // set adc port for battery and level sensors
       nrf_drv_saadc_config_t saadc_config;
       nrf_saadc_channel_config_t channel_config0;

    //Configure SAADC
       saadc_config.resolution = NRF_SAADC_RESOLUTION_10BIT; 
       saadc_config.oversample = NRF_SAADC_OVERSAMPLE_4X; 
       saadc_config.interrupt_priority = APP_IRQ_PRIORITY_LOW; //Set SAADC interrupt to low priority.
       saadc_config.low_power_mode = NRFX_SAADC_CONFIG_LP_MODE;

    //Configure SAADC channel
       channel_config0.reference = NRF_SAADC_REFERENCE_INTERNAL; 
       channel_config0.gain = NRF_SAADC_GAIN1_6; 
       channel_config0.acq_time = NRF_SAADC_ACQTIME_10US; 
       channel_config0.mode = NRF_SAADC_MODE_SINGLE_ENDED; 
       channel_config0.pin_n = NRF_SAADC_INPUT_DISABLED; 
       channel_config0.resistor_p = NRF_SAADC_RESISTOR_DISABLED; 
       channel_config0.resistor_n = NRF_SAADC_RESISTOR_DISABLED;
       channel_config0.burst = NRF_SAADC_BURST_ENABLED;

    //Initialize SAADC
       err_code = nrf_drv_saadc_init(&saadc_config, saadc_callback);
       APP_ERROR_CHECK(err_code);

       channel_config0.pin_p = IRRIGATION_LOW_LEVEL_ANALOG_SENS_PIN_IN;
       err_code = nrf_drv_saadc_channel_init(0, &channel_config0);
       APP_ERROR_CHECK(err_code);
       channel_config0.pin_p = EVACUATION_LOW_LEVEL_ANALOG_SENS_PIN_IN;
       err_code = nrf_drv_saadc_channel_init(1, &channel_config0);
       APP_ERROR_CHECK(err_code);

       channel_config0.pin_p = BATTERY_MILLI_WATT_PIN_IN;
       err_code = nrf_drv_saadc_channel_init(2, &channel_config0);
       APP_ERROR_CHECK(err_code);

    // seting limits for the analog values read of the tanks:
       nrf_drv_saadc_limits_set(1, IRRI_TANK_LOW_V_LIMIT, IRRI_TANK_HIGH_V_LIMIT);
       nrf_drv_saadc_limits_set(2, EVAC_TANK_LOW_V_LIMIT, EVAC_TANK_HIGH_V_LIMIT);


       err_code = nrf_drv_saadc_buffer_convert(adc_read_buffer[0], NUMBER_OF_ADC_SAMPLES);
       APP_ERROR_CHECK(err_code);

       err_code = nrf_drv_saadc_buffer_convert(adc_read_buffer[1], NUMBER_OF_ADC_SAMPLES);
       APP_ERROR_CHECK(err_code);

    "

    and this beeing my saadc callback (and for the moment all reading as i understand are in the same time :

    "

    static void saadc_callback(nrf_drv_saadc_evt_t const * p_event)
    {
      if (p_event->type == NRF_DRV_SAADC_EVT_DONE)
      {
           // here i need to read the battery value likewise
           battery_val = (uint16_t)(((uint32_t)(p_event->data.done.p_buffer[0])*3600)/1024);
      }
      if (p_event->type == NRFX_SAADC_EVT_LIMIT)
      {
          // i need to know somehow wich pin triggered the event and if it was high limit or low limit ??? how can           i do that ??
      }
    }

    "

    i need to know somehow wich pin triggered the event and if it was high limit or low limit ??? how can i do that ?? 

    3. you wrote: "Be advised that when having limits enabled and using scan mode, the limit event will trigger every time the channel is sampled and the limits are exceeded."

    does that means that when limits enable then the saadc works all the time, or just when triggered via some timer for example? 

    will each limits event will raise the NRFX_SAADC_EVT_LIMIT event type event followed by NRF_DRV_SAADC_EVT_DONE event type ?

    also, if i want to be reading all 3 channels at the same time like that, the first channel will generate only the NRF_DRV_SAADC_EVT_DONE  event, so can i miss an event of type NRFX_SAADC_EVT_LIMIT  ? 

    sorry for so many questions but i just don't get the right way to do 1 analog value read for one pin and 2 analog limits or compares for 2 other pins .. maybe it will be better to use LPCOMP afterall, and find out how to switch the pins in each read which means CPU intervention but i don't know what other option there is

    4. can using this function

    and this 

    or this

    help to configure an event for each channel ?

    best regards

    Ziv

  • Hello Ziv,

    Do I understand you correctly that you are intending to use the one SAADC channel to read the battery voltage of the device, and the two others to compare the pin levels to a reference?
    If this is the case, you should see the Shared Resources documentation, and you might also want to have a look at this ticket(NB its for the nRF51 series).

    ziv123 said:
    1. if i init sampeling (  nrf_drv_saadc_sample() ) via timer1 or flage in timer 1's handler, and uninit the saadc ( nrf_drv_saadc_uninit() ) in the saadc_callback function, i can risk calling for sampeling twice if i understood your answer correctly, which is something i don't want to do if it can cause problems

    What I said in my last comment was that double sampling(having two PPI events trigger the SAMPLE TASK at the same time) is undefined behavior - meaning it is not specified what happens if this occurs. You could test it out and see, my guess is that one of the trigger events will be ignored(only 1 sample will occur).

    ziv123 said:
    2. where this beeing my init:

    Please use the "Insert->Code" option, to drastically increase readability.
    At first glance you code looks alright to me, but I notice that you are configuring your channels are single ended inputs - instead of double ended? From earlier I think you said you wanted to use double ended(with an external reference?).
    I also wonder whether you intend for all the 3 sampling channels to have the same configurations?
    I also note that your saadc callback seems very sparse.
    The limit event will contain both channel that triggered the event, and the type of limit that was exceeded, as can be seen here in the limit event documentation. 
    For an example on how to set up and use the battery level service(if you are intending to do so), you can see it demonstrated in the ble_peripheral proximity example.

    ziv123 said:
    i need to know somehow wich pin triggered the event and if it was high limit or low limit ??? how can i do that ?? 

     As stated in my initial comment, LPCOMP is intended for use on one pin - which means you will know which pin that triggered the event every time it happens. The challenge with using LPCOMP for multiple pins is the constant reconfiguration. Please see this ticket for further detail.

    ziv123 said:
    does that means that when limits enable then the saadc works all the time, or just when triggered via some timer for example? 

     The sampling will still only occur when the SAMPLE TASK is triggered. The limit events will trigger based on the value of the acquired sample.

    ziv123 said:
    will each limits event will raise the NRFX_SAADC_EVT_LIMIT event type event followed by NRF_DRV_SAADC_EVT_DONE event type ?

    The DONE event will occur once the sampling completes. The LIMIT events may trigger whenever a new sample is gathered, that exceeds the limits. So the order would be DONE then LIMIT. You will get an event for each channel, if there are multiple LIMIT events.

    ziv123 said:
    sorry for so many questions

     No problem at all! I am happy to help.

    ziv123 said:
    help to configure an event for each channel ?

    I would not recommend mixing HAL and driver function calls in your application, since this may unintentionally put the driver into an invalid state. What happens when you build and run the code you included above?

    Best regards,
    Karl

Reply
  • Hello Ziv,

    Do I understand you correctly that you are intending to use the one SAADC channel to read the battery voltage of the device, and the two others to compare the pin levels to a reference?
    If this is the case, you should see the Shared Resources documentation, and you might also want to have a look at this ticket(NB its for the nRF51 series).

    ziv123 said:
    1. if i init sampeling (  nrf_drv_saadc_sample() ) via timer1 or flage in timer 1's handler, and uninit the saadc ( nrf_drv_saadc_uninit() ) in the saadc_callback function, i can risk calling for sampeling twice if i understood your answer correctly, which is something i don't want to do if it can cause problems

    What I said in my last comment was that double sampling(having two PPI events trigger the SAMPLE TASK at the same time) is undefined behavior - meaning it is not specified what happens if this occurs. You could test it out and see, my guess is that one of the trigger events will be ignored(only 1 sample will occur).

    ziv123 said:
    2. where this beeing my init:

    Please use the "Insert->Code" option, to drastically increase readability.
    At first glance you code looks alright to me, but I notice that you are configuring your channels are single ended inputs - instead of double ended? From earlier I think you said you wanted to use double ended(with an external reference?).
    I also wonder whether you intend for all the 3 sampling channels to have the same configurations?
    I also note that your saadc callback seems very sparse.
    The limit event will contain both channel that triggered the event, and the type of limit that was exceeded, as can be seen here in the limit event documentation. 
    For an example on how to set up and use the battery level service(if you are intending to do so), you can see it demonstrated in the ble_peripheral proximity example.

    ziv123 said:
    i need to know somehow wich pin triggered the event and if it was high limit or low limit ??? how can i do that ?? 

     As stated in my initial comment, LPCOMP is intended for use on one pin - which means you will know which pin that triggered the event every time it happens. The challenge with using LPCOMP for multiple pins is the constant reconfiguration. Please see this ticket for further detail.

    ziv123 said:
    does that means that when limits enable then the saadc works all the time, or just when triggered via some timer for example? 

     The sampling will still only occur when the SAMPLE TASK is triggered. The limit events will trigger based on the value of the acquired sample.

    ziv123 said:
    will each limits event will raise the NRFX_SAADC_EVT_LIMIT event type event followed by NRF_DRV_SAADC_EVT_DONE event type ?

    The DONE event will occur once the sampling completes. The LIMIT events may trigger whenever a new sample is gathered, that exceeds the limits. So the order would be DONE then LIMIT. You will get an event for each channel, if there are multiple LIMIT events.

    ziv123 said:
    sorry for so many questions

     No problem at all! I am happy to help.

    ziv123 said:
    help to configure an event for each channel ?

    I would not recommend mixing HAL and driver function calls in your application, since this may unintentionally put the driver into an invalid state. What happens when you build and run the code you included above?

    Best regards,
    Karl

Children
  • Do I understand you correctly that you are intending to use the one SAADC channel to read the battery voltage of the device, and the two others to compare the pin levels to a reference?

    to be clear: 

    i want to use ont pin to read analog values of battery every few seconds. 

    on 2 other pins i have 2 optical sensors that gives analog inputs and i need to do something when each of them goes down from a ref v and up again.

    What I said in my last comment was that double sampling(having two PPI events trigger the SAMPLE TASK at the same time) is undefined behavior - meaning it is not specified what happens if this occurs. You could test it out and see, my guess is that one of the trigger events will be ignored(only 1 sample will occur).

    i can not take the risk of some event allwys or sometimes ignored, so this is not a vlide solution for me.

    you are configuring your channels are single ended inputs - instead of double ended? From earlier I think you said you wanted to use double ended(with an external reference?).

    i am planing to use some internal referance (some value i will hard code) not external.

     

    The DONE event will occur once the sampling completes. The LIMIT events may trigger whenever a new sample is gathered, that exceeds the limits. So the order would be DONE then LIMIT. You will get an event for each channel, if there are multiple LIMIT events.

    strange .. i read somewherer in this forom that the DONE event is triggered after data has been writen to RAM via easyDMA and the LIMIT event is triggered once the sempeled value exceeds a value in a register and this comparison happens before data is passed to the RAM hanse the LIMIT event will be triggered first.. is this wrong ?

    also, now when i use BURST and i read 3 channels is it a new event for each channel because it seems to be one call of the event handler for all 3 ? 

    another question is - is it possible to disable a certain limit event in the handler?  .. because after, for example, i got high limit event i don't want to get it again every read but i will want to enable it again once i get low limit event ?

    I would not recommend mixing HAL and driver function calls in your application, since this may unintentionally put the driver into an invalid state. What happens when you build and run the code you included above?

    not sure i allways know which functions are HAL and which drv 

    anyway and maybe it is related.. can i use the app_timer in my app and also the '

    nrf_drv_timer_t' timer (all exampels i saw that use ppi use the ' nrf_drv_timer_t ' but on other modules in my program i use the app_timer - which i guess picks the channel itself and i don't know which channel and can it be interfered with the ' NRF_DRV_TIMER_INSTANCE(channel); ' ?
    or is there any example on how to use ppi with app_timer ? 
    p.s. if i understand correctly from what you wrote and what i have read in the forom it is not a good idea to use the LPCOMP with 2 changing channels, sounless i missunderstood i am working on saadc only now (3 channels as i mentioned iand i guess i have no choice but to read them all at the same time ) 
    best regards
    Ziv
  • well i am trying to set a timer for saadc triggered via pi, i used app_timer in another module in my program but app_timer has no API to get saadc samle event address so ..

    i read the RTC0 is used by the softdevice and RTC1 is used by app_timer so i can use RTC2 for the 

    ' static const nrf_drv_timer_t m_timer = NRF_DRV_TIMER_INSTANCE(2); '

    but i get a compilation error - 

    'NRFX_TIMER2_INST_IDX' undeclared here (not in a function); did you mean 'NRFX_TIMER_INSTANCE'?

    /** @brief Macro for creating a timer driver instance. */
    #define NRFX_TIMER_INSTANCE(id)                                   \
    {                                                                 \
        .p_reg            = NRFX_CONCAT_2(NRF_TIMER, id),             \
        .instance_id      = NRFX_CONCAT_3(NRFX_TIMER, id, _INST_IDX), \
        .cc_channel_count = NRF_TIMER_CC_CHANNEL_COUNT(id),           \
    }
    
    #ifndef __NRFX_DOXYGEN__
    enum {
    #if NRFX_CHECK(NRFX_TIMER0_ENABLED)
        NRFX_TIMER0_INST_IDX,
    #endif
    #if NRFX_CHECK(NRFX_TIMER1_ENABLED)
        NRFX_TIMER1_INST_IDX,
    #endif
    #if NRFX_CHECK(NRFX_TIMER2_ENABLED)
        NRFX_TIMER2_INST_IDX,
    #endif
    #if NRFX_CHECK(NRFX_TIMER3_ENABLED)
        NRFX_TIMER3_INST_IDX,
    #endif
    #if NRFX_CHECK(NRFX_TIMER4_ENABLED)
        NRFX_TIMER4_INST_IDX,
    #endif
        NRFX_TIMER_ENABLED_COUNT
    };
    #endif

    i wonder why ???

    .. i have tried to edit the sdk_config file enableing 

    // <e> NRFX_TIMER_ENABLED - nrfx_timer - TIMER periperal driver
    //==========================================================
    #ifndef NRFX_TIMER_ENABLED
    #define NRFX_TIMER_ENABLED 1
    #endif
    // <q> NRFX_TIMER0_ENABLED  - Enable TIMER0 instance
     
    
    #ifndef NRFX_TIMER0_ENABLED
    #define NRFX_TIMER0_ENABLED 0
    #endif
    
    // <q> NRFX_TIMER1_ENABLED  - Enable TIMER1 instance
     
    
    #ifndef NRFX_TIMER1_ENABLED
    #define NRFX_TIMER1_ENABLED 0
    #endif
    
    // <q> NRFX_TIMER2_ENABLED  - Enable TIMER2 instance
    
    
    #ifndef NRFX_TIMER2_ENABLED
    #define NRFX_TIMER2_ENABLED 1
    #endif
    

    Best regards

    Ziv

  • hi Karl 

    one more question: 

    nrf_drv_saadc_limits_set(CHANNEL_IRRI_LEVEL, 1000, 1000);

    can i use the pin set like this .. where the limit high and limit low are of the same value and expect that when measured v goes below 1000mv then i will get a ' NRF_SAADC_LIMIT_LOW ' event

    and when measured v goes above 1000mv then i will get the ' NRF_SAADC_LIMIT_HIGH ' event ? 

    also i have noticed (not sure yet, i have to use some debug counters maybe) that i only get v limit event and no done event though i am waiting to read values in it from the other pin 

    best regards

    Ziv

  • i read somewherer in this forom that the DONE event is triggered after data has been writen to RAM via easyDMA and the LIMIT event is triggered once the sempeled value exceeds a value in a register and this comparison happens before data is passed to the RAM hanse the LIMIT event will be triggered first.. is this wrong ?

    to correct my comment -> the END (not DONE) event is triggered after data is writen to RAM .

    so i was wrong in my statement

    however, it brings a question to mind .. how many events are generated in each sample and if there are also channels set with limits what comes first and does some events gets lost or not triggered at all ???

  • Hello Ziv,

    ziv123 said:
    i can not take the risk of some event allwys or sometimes ignored, so this is not a vlide solution for me.

    That is totally understandable. As it is undefined behavior I am unable to confirm this particular behavior for you, and so you must either test it yourself or change the approach.

    ziv123 said:
    also, now when i use BURST and i read 3 channels is it a new event for each channel because it seems to be one call of the event handler for all 3 ? 

     I am not sure I understand what you mean here. As I have mentioned earlier, the SAADC only has 1 SAMPLE TASK, and when it is triggered it will sample all enabled channels. In your case, that means that it will sample all three channels each time the SAMPLE TASK is triggered. The event handler for the entire SAADC is the same one, as set up during the SAADC init.
    If this is not what you were asking about, please elaborate.

    ziv123 said:
    another question is - is it possible to disable a certain limit event in the handler?  .. because after, for example, i got high limit event i don't want to get it again every read but i will want to enable it again once i get low limit event ?

    I suppose you could just keep a variable for if the channel exceeded the high limit last time, and return from the callback immediately if a new LIMIT HIGH event is generated. If not, you would have to disable the channel to avoid generating the events all together, but then you would not know when it is back below the high limit again.
    As long as the channel is enabled, it will generate the event every time a sample comes in that exceeds its limits.

    ziv123 said:
    not sure i allways know which functions are HAL and which drv

    The HAL functions are prefaced by nrf_saadc, the legacy driver functions are prefaced by nrf_drv_saadc and the newest driver functions are prefaced by nrfx_saadc. You could see them all in the SAADC API Reference.
    Furthermore, there is nothing wrong with using those functions - many only use the HAL functions directly - or even program bare-metal - but I would recommend sticking to only one of the options, to avoid unexpected behavior. 

    ziv123 said:
    or is there any example on how to use ppi with app_timer ? 

    Please see this ticket.

    ziv123 said:
    p.s. if i understand correctly from what you wrote and what i have read in the forom it is not a good idea to use the LPCOMP with 2 changing channels, sounless i missunderstood i am working on saadc only now (3 channels as i mentioned iand i guess i have no choice but to read them all at the same time ) 

    I would have to agree, as it will require a lot of CPU interference, especially at high intervals. The LPCOMP peripheral is most commonly used to keep an eye on a reset button - or other very slow input.
    I absolutely think using the SAADC is right for your application, the more I heard about it.

    ziv123 said:
    however, it brings a question to mind .. how many events are generated in each sample and if there are also channels set with limits what comes first and does some events gets lost or not triggered at all ???

    My mistake, I seem to have misspoken. Since the SAADC events are all generated in the hardware, multiple events may be set when entering the callback handler. In that case, every set event will be handled in the same IRQ(if the callback is implemented as if(event), if(event) etc.). However, you will need to make sure that you are not staying in the IRQ for too long, so that a new event is generated before the old one is handled - this will lead to the new event going unnoticed, since the event is already set.

    ziv123 said:

    i wonder why ???

    .. i have tried to edit the sdk_config file enableing 

    Please make sure that the issue in this ticket is not the cause.

    ziv123 said:

    can i use the pin set like this .. where the limit high and limit low are of the same value and expect that when measured v goes below 1000mv then i will get a ' NRF_SAADC_LIMIT_LOW ' event

    and when measured v goes above 1000mv then i will get the ' NRF_SAADC_LIMIT_HIGH ' event ?

    I would think you can use it like this, but it will result in a limit event being generated every sample, either high or low. If this is not clear, please have another look at the SAADC LIMIT documentation.

    ziv123 said:
    i have noticed (not sure yet, i have to use some debug counters maybe) that i only get v limit event and no done event though i am waiting to read values in it from the other pin 

    Please elaborate what you mean here.

    Looking forward to solving this issue together, Ziv!

    Best regards,
    Karl

Related