SAADC Sampling rate at 1kHz with non blocking internal timer and accurate ble write notify every 20ms.

I manage to make this work with nrf52832dk.

https://github.com/zephyrproject-rtos/hal_nordic/tree/master/nrfx/samples/src/nrfx_saadc/advanced_non_blocking_internal_timer

But what I want to achieve is to capture 1 sample evey 1ms and writing the the 20 samples to notify central.


Currently I have set the ADC to oversampling with NRF_SAADC_ACQTIME_20US.


1. How do I make sure that the buffer array contains 20 of my samples , sampled at 1ms per sample where per sample is average of the samples taken in 1ms?
In other words if I understand it right 1sample with oversampling at 1ms is the average of the samples taken during that 1ms? How can I achieve that?

2. I am currently using k_msleep(20) before writing the ble characteristic in a while loop with fix ble write values as testing. The problem is sometimes the nrf sniffer shows empty buff from slave from time to time. And I can't use workqueue or zephyr timer afaik since it may cause deadlock with ble notify.

devzone.nordicsemi.com/.../bt_gatt_notify-can-block-when-running-on-zephyr-workqueue

  • Hello,

    You could also take a look at this sample to see how you may set the SAADC up with PPI to trigger sampling without CPU interaction.

    1. How do I make sure that the buffer array contains 20 of my samples , sampled at 1ms per sample where per sample is average of the samples taken in 1ms?
    In other words if I understand it right 1sample with oversampling at 1ms is the average of the samples taken during that 1ms? How can I achieve that?

    To make sure that your buffer contains exactly 20 samples you could use a ring-buffer - the downside to this is that samples might get overwritten if they are not queued for sending in time before the 21st sample arrives.

    If you have configured your SAADC SAMPLE task to trigger through PPI by the RTC's Capture/compare event every 1 ms this will make 1 sample the taken every 1 ms. If you use the NRF_SAADC_ACQTIME_20US 20 µs acquisition time then that one sample will be taken over 20 µs.
    If you would like that one sample to be averaged over multiple samples then you may use the OVERSAMPLING feature + BURST feature, which will take the configured amount of samples as fast as possible, average them, and then output the average as a single sample every 1 ms.
    Is this what you were looking for, or have I perhaps misunderstood your intentions here?

    2. I am currently using k_msleep(20) before writing the ble characteristic in a while loop with fix ble write values as testing. The problem is sometimes the nrf sniffer shows empty buff from slave from time to time. And I can't use workqueue or zephyr timer afaik since it may cause deadlock with ble notify.

    Empty PDUs will be sent when no notification or data is queued for transmission in an upcoming connection event. These are sent in order to maintain the connection.
    You could queue the samples for sending every time 20 of the samples are taken, such as in the event handling for the full SAADC buffer or similar. Could this be an opton in your case?

    Best regards,
    Karl

  • Thank you Karl. 
    You understood it right. I have set the oversample to 8x.

    [00:01:58.963,287] <inf> NRFX_EXAMPLE: 19 1548
    [00:01:58.983,001] <inf> NRFX_EXAMPLE: 19 1363
    [00:01:59.002,807] <inf> NRFX_EXAMPLE: 19 1525
    [00:01:59.022,521] <inf> NRFX_EXAMPLE: 19 1519
    [00:01:59.042,236] <inf> NRFX_EXAMPLE: 19 1370
    [00:01:59.061,950] <inf> NRFX_EXAMPLE: 19 1548
    [00:01:59.081,665] <inf> NRFX_EXAMPLE: 19 1449
    [00:01:59.101,470] <inf> NRFX_EXAMPLE: 19 1438
    [00:01:59.121,185] <inf> NRFX_EXAMPLE: 19 1550
    [00:01:59.140,899] <inf> NRFX_EXAMPLE: 19 1370
    [00:01:59.160,614] <inf> NRFX_EXAMPLE: 19 1506
    [00:01:59.180,328] <inf> NRFX_EXAMPLE: 19 1536

    As you can see in this log the time it take for 20 samples is 0.019715 seconds. So I'm off by 285uS.

    What I did is put a k_usleep(285) up to 300. But I get this error after sometime. Without the delay, the logs is spammed by this logs. 

    [00:01:27.289,489] <wrn> bt_att: Unable to allocate ATT TX meta
    [00:01:27.289,581] <wrn> bt_gatt: No buffer available to send notification
    [00:01:35.068,695] <wrn> bt_att: Unable to allocate ATT TX meta
    [00:01:35.068,725] <wrn> bt_gatt: No buffer available to send notification

    Current MTU is set at maxed.

    CONFIG_BT_BUF_ACL_RX_SIZE=251
    CONFIG_BT_BUF_ACL_TX_SIZE=251
    CONFIG_BT_L2CAP_TX_MTU=247
    CONFIG_BT_CTLR_DATA_LENGTH_MAX=251
    


    I suspect that some ble_gatt_notify writes are queued until the buffer is full? If yes but I am only sending at 20ms interval why some are still queued? How can I avoid this or is their away to clear the ATT TX buffer after I call ble_gatt_notfiy?

    Empty PDUs will be sent when no notification or data is queued for transmission in an upcoming connection event. These are sent in order to maintain the connection.
    You could queue the samples for sending every time 20 of the samples are taken, such as in the event handling for the full SAADC buffer or similar. Could this be an opton in your case?

    Which also brings me to your reply, this is what I have done now. Take 20 samples then send. It seems that if I'm off by microseconds it will send empty buff if vice versa it will be queued and buffer will be full. 

  • Hello,

    xproto said:
    As you can see in this log the time it take for 20 samples is 0.019715 seconds. So I'm off by 285uS.

    What are the numbers in the log here, is this the TIMER delta between the readings?
    Are you also using the BURST feature here?

    xproto said:
    What I did is put a k_usleep(285) up to 300. But I get this error after sometime. Without the delay, the logs is spammed by this logs. 

    As I mentioned I would instead recommend that you have the notification queued in the SAADC buffer full event, rather than doing it based on a separate kernel timer. Would this be an option for your application?

    xproto said:
    I suspect that some ble_gatt_notify writes are queued until the buffer is full? If yes but I am only sending at 20ms interval why some are still queued? How can I avoid this or is their away to clear the ATT TX buffer after I call ble_gatt_notfiy?

    It is likely that all your calls to queue the notifications for sending has filled the buffer, yes.
    The notifications will be retransmitted in the case that they are not acknowledged by the peer, which can happen if the peer never receives them such as if the devices are leaving each others range, or are operating in a noisy RF environment. This will cause the notification buffer to fill.

    Are you familiar with the nRF Sniffer tool? It would be great to see a sniffer trace of the communication between your devices to make sure that everything is being sent as expected.

    xproto said:
    Which also brings me to your reply, this is what I have done now. Take 20 samples then send. It seems that if I'm off by microseconds it will send empty buff if vice versa it will be queued and buffer will be full. 

    Could you share your .conf file from the build/zephyr/ location, so that I may see your configured buffer size?

    Best regards,
    Karl

  • What are the numbers in the log here, is this the TIMER delta between the readings?
    Are you also using the BURST feature here?

    Yes they are. The logs are printed every time the 20th sample is captured. Yes

    NRF_SAADC_BURST_ENABLED.

    As I mentioned I would instead recommend that you have the notification queued in the SAADC buffer full event, rather than doing it based on a separate kernel timer. Would this be an option for your application?

    This is what I'm doing now if I understand it correctly.

    case NRFX_SAADC_EVT_DONE:
        if(read_enabled ){
    				gpio_pin_set(pin_nrf, PIN_LED_EN, 1);	
    				k_usleep(285); // delay for completing time remaining for 20ms		
    				update_force(NULL, &force_svc.attrs[2],p_event->data.done.p_buffer, &sg_sensor);
    			}
    			else{
    				gpio_pin_set(pin_nrf, PIN_LED_EN, 0);			
    			}	
                break;


    It is likely that all your calls to queue the notifications for sending has filled the buffer, yes.
    The notifications will be retransmitted in the case that they are not acknowledged by the peer, which can happen if the peer never receives them such as if the devices are leaving each others range, or are operating in a noisy RF environment. This will cause the notification buffer to fill.

    I see, I am not aware that if not sent it will need to be re transmitted.

    Are you familiar with the nRF Sniffer tool? It would be great to see a sniffer trace of the communication between your devices to make sure that everything is being sent as expected.

    Ok I'll analyze it with the sniffer tool. 

    Could you share your .conf file from the build/zephyr/ location, so that I may see your configured buffer size?

     I can't find a conf file in that folder. Did you mean the prj.conf?

    CONFIG_BT=y
    CONFIG_BT_GATT_CLIENT=y
    CONFIG_BT_PERIPHERAL=y
    CONFIG_BT_SMP=y
    CONFIG_BT_DEVICE_NAME="FORCE"
    CONFIG_BT_DIS=y
    CONFIG_BT_DIS_PNP=n
    
    # Config logger
    CONFIG_LOG=y
    CONFIG_LOG_BACKEND_RTT=y
    CONFIG_LOG_BACKEND_UART=n
    CONFIG_LOG_PRINTK=n
    
    CONFIG_NRFX_TIMER1=y
    CONFIG_NRFX_PPI=y
    CONFIG_NRFX_SAADC=y
    
    CONFIG_BT_BUF_ACL_RX_SIZE=251
    CONFIG_BT_BUF_ACL_TX_SIZE=251
    CONFIG_BT_L2CAP_TX_MTU=247
    CONFIG_BT_CTLR_DATA_LENGTH_MAX=251
    

  • Hello,

    xproto said:

    Yes they are. The logs are printed every time the 20th sample is captured. Yes

    NRF_SAADC_BURST_ENABLED.

    Thank you for confirming this.

    xproto said:
    This is what I'm doing now if I understand it correctly.

    I am not sure what the purpose of the delay is in this code section - the DONE event will be generated once the buffer is filled, and so assuming you are using a buffer size of 20 then you will generate this event every 20 ms (+ t_conversion for the last sample), and so there should be no need for this delay. It is also generally considered bad practice to add delays inside interrupt handling, and so I would not recommend keeping this in place.

    xproto said:
    I see, I am not aware that if not sent it will need to be re transmitted.

    Ah, yes, that is a core feature of the BLE protocol, so that you can be sure that no data is ever lost in the link! :) 

    xproto said:
    Ok I'll analyze it with the sniffer tool. 

    Great! Please do not hesitate to let me know if you would like some help looking through the sniffer traces of the communication between the devices.

    xproto said:
     I can't find a conf file in that folder. Did you mean the prj.conf?

    Not quite - the prj.conf file only shows a subset of your kconfigs, while the .conf shows every kconfig option in the build.
    Perhaps you have called your build folder something else than the default 'build' name, but it should be located at 
    /build/zephyr/.conf 
    following a successful build of your application.

    Best regards,
    Karl

Related