LENDO ENTRADAS ANALÓGICAS NO NINA B112 COM SEGGER STUDIO, JAVASCRIPT,  M-BED, ARDUINO, PYTHON e o NINA W102 com ARDUINO e ESP-IDF

O objetivo deste BLOG é mostrar como ler entradas analógicas do NINA B112 e NINA W102 com diferentes linguagens de programação.

Nos BLOGS anteriores foi visto como instalar o SEGGER STUDIO, JAVASCRIPT, M-BED,  ARDUINO E PYTHON para o NINA B112 bem como ARDUINO e PLATFORMIO para NINA W102.

Foram utilizados os EVK-NINA-B1 e EVK-NINA-W1 para testes.

NINA B112

1) CÓDIGO NO SEGGER STUDIO

Dicas: no sdk_config.h, esta para 10 bits a resolução. Habilite tambe nrf_log_rtt.

#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include "nrf.h"
#include "nrf_drv_saadc.h"
#include "nrf_drv_ppi.h"
#include "nrf_drv_timer.h"
#include "boards.h"
#include "app_error.h"
#include "nrf_delay.h"
#include "app_util_platform.h"
#include "nrf_pwr_mgmt.h"

#include "nrf_log.h"
#include "nrf_log_ctrl.h"
#include "nrf_log_default_backends.h"

#define SAMPLES_IN_BUFFER 5
volatile uint8_t state = 1;

static const nrf_drv_timer_t m_timer = NRF_DRV_TIMER_INSTANCE(0);
static nrf_saadc_value_t     m_buffer_pool[2][SAMPLES_IN_BUFFER];
static nrf_ppi_channel_t     m_ppi_channel;
static uint32_t              m_adc_evt_counter;

void timer_handler(nrf_timer_event_t event_type, void * p_context)
{
}

void saadc_sampling_event_init(void)
{
    ret_code_t err_code;

    err_code = nrf_drv_ppi_init();
    APP_ERROR_CHECK(err_code);

    nrf_drv_timer_config_t timer_cfg = NRF_DRV_TIMER_DEFAULT_CONFIG;
    timer_cfg.bit_width = NRF_TIMER_BIT_WIDTH_32;
    err_code = nrf_drv_timer_init(&m_timer, &timer_cfg, timer_handler);
    APP_ERROR_CHECK(err_code);

    /* setup m_timer for compare event every 400ms */
    uint32_t ticks = nrf_drv_timer_ms_to_ticks(&m_timer, 400);
    nrf_drv_timer_extended_compare(&m_timer,
                                   NRF_TIMER_CC_CHANNEL0,
                                   ticks,
                                   NRF_TIMER_SHORT_COMPARE0_CLEAR_MASK,
                                   false);
    nrf_drv_timer_enable(&m_timer);

    uint32_t timer_compare_event_addr = nrf_drv_timer_compare_event_address_get(&m_timer,
                                                                                NRF_TIMER_CC_CHANNEL0);
    uint32_t saadc_sample_task_addr   = nrf_drv_saadc_sample_task_get();

    /* setup ppi channel so that timer compare event is triggering sample task in SAADC */
    err_code = nrf_drv_ppi_channel_alloc(&m_ppi_channel);
    APP_ERROR_CHECK(err_code);

    err_code = nrf_drv_ppi_channel_assign(m_ppi_channel,
                                          timer_compare_event_addr,
                                          saadc_sample_task_addr);
    APP_ERROR_CHECK(err_code);
}


void saadc_sampling_event_enable(void)
{
    ret_code_t err_code = nrf_drv_ppi_channel_enable(m_ppi_channel);

    APP_ERROR_CHECK(err_code);
}

void saadc_callback(nrf_drv_saadc_evt_t const * p_event)
{
    if (p_event->type == NRF_DRV_SAADC_EVT_DONE)
    {
        ret_code_t err_code;

        err_code = nrf_drv_saadc_buffer_convert(p_event->data.done.p_buffer, SAMPLES_IN_BUFFER);
        APP_ERROR_CHECK(err_code);

        int i;
        NRF_LOG_INFO("ADC event number: %d", (int)m_adc_evt_counter);

        for (i = 0; i < SAMPLES_IN_BUFFER; i++)
        {
            NRF_LOG_INFO("%d", p_event->data.done.p_buffer[i]);
        }
        m_adc_evt_counter++;
    }
}

void saadc_init(void)
{
    ret_code_t err_code;
    nrf_saadc_channel_config_t channel_config =
        NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN0);

    err_code = nrf_drv_saadc_init(NULL, saadc_callback);
    APP_ERROR_CHECK(err_code);

    err_code = nrf_drv_saadc_channel_init(0, &channel_config);
    APP_ERROR_CHECK(err_code);

    err_code = nrf_drv_saadc_buffer_convert(m_buffer_pool[0], SAMPLES_IN_BUFFER);
    APP_ERROR_CHECK(err_code);

    err_code = nrf_drv_saadc_buffer_convert(m_buffer_pool[1], SAMPLES_IN_BUFFER);
    APP_ERROR_CHECK(err_code);

}

/**
 * @brief Function for main application entry.
 */
int main(void)
{
    uint32_t err_code = NRF_LOG_INIT(NULL);
    APP_ERROR_CHECK(err_code);

    NRF_LOG_DEFAULT_BACKENDS_INIT();

    ret_code_t ret_code = nrf_pwr_mgmt_init();
    APP_ERROR_CHECK(ret_code);

    saadc_init();
    saadc_sampling_event_init();
    saadc_sampling_event_enable();
    NRF_LOG_INFO("SAADC HAL simple example started.");

    while (1)
    {
        nrf_pwr_mgmt_run();
        NRF_LOG_FLUSH();
    }
}

/** @} */

2) CÓDIGO NO ARDUINO

int adcin    = A0;
int adcvalue = 0;

void setup() {
  analogReference(AR_INTERNAL);
  analogReadResolution(10);
  Serial.begin(115200);
}

void loop() {
  // Get a fresh ADC value
  adcvalue = analogRead(adcin);
 // Display the results
  Serial.println(adcvalue);
  delay(1000);
}

3) CÓDIGO NO ARDUINO (AVANÇADO)

// Will Langford
// Jan 12, 2018
// Basic example showing how to use the SAADC with oversampling and interrupts
// In this code we:
//    set up the SAADC to oversample 256x in a single-ended configuration on Analog 0 (Pin 0.2)
//    enable burst mode so that we only need to trigger 1 sample to get a fully sampled (256x) result
//    enable an interrupt on the SAADC END_EVENT which triggers whenever the result buffer is full

static int16_t data_buffer[1];
volatile bool saadc_results_ready = false;

extern "C" // for some strange reason this seems necessary for the interrupt to function
{
  void SAADC_IRQHandler(void)
  {
      // Clear events
      NRF_SAADC->EVENTS_END = 0;
      saadc_results_ready = true;
  }
}

void adc_setup() {
  NVIC_EnableIRQ(SAADC_IRQn);
  NVIC_ClearPendingIRQ(SAADC_IRQn);
  //configure SAADC resolution
  NRF_SAADC->RESOLUTION = SAADC_RESOLUTION_VAL_10bit;

  // enable oversampling
  NRF_SAADC->OVERSAMPLE = (SAADC_OVERSAMPLE_OVERSAMPLE_Over256x << SAADC_OVERSAMPLE_OVERSAMPLE_Pos) & SAADC_OVERSAMPLE_OVERSAMPLE_Msk ;

  //enable SAADC
  NRF_SAADC->ENABLE = (SAADC_ENABLE_ENABLE_Enabled << SAADC_ENABLE_ENABLE_Pos);

  //set result pointer
  NRF_SAADC->RESULT.PTR = (uint32_t)(&data_buffer);
  NRF_SAADC->RESULT.MAXCNT = 1; // number of samples

  for (int i = 0; i < 8; i++) {
    NRF_SAADC->CH[i].PSELN = SAADC_CH_PSELP_PSELP_NC;
    NRF_SAADC->CH[i].PSELP = SAADC_CH_PSELP_PSELP_NC;
  }

  //set channel 0 resistor network, gain, reference, sample time, and mode
  NRF_SAADC->CH[0].CONFIG =   ((SAADC_CH_CONFIG_RESP_Bypass     << SAADC_CH_CONFIG_RESP_Pos)   & SAADC_CH_CONFIG_RESP_Msk)
                            | ((SAADC_CH_CONFIG_RESP_Bypass     << SAADC_CH_CONFIG_RESN_Pos)   & SAADC_CH_CONFIG_RESN_Msk)
                            | ((SAADC_CH_CONFIG_GAIN_Gain1_5    << SAADC_CH_CONFIG_GAIN_Pos)   & SAADC_CH_CONFIG_GAIN_Msk)
                            | ((SAADC_CH_CONFIG_REFSEL_Internal << SAADC_CH_CONFIG_REFSEL_Pos) & SAADC_CH_CONFIG_REFSEL_Msk)
                            | ((SAADC_CH_CONFIG_TACQ_3us        << SAADC_CH_CONFIG_TACQ_Pos)   & SAADC_CH_CONFIG_TACQ_Msk)
                            | ((SAADC_CH_CONFIG_BURST_Enabled   << SAADC_CH_CONFIG_BURST_Pos)  & SAADC_CH_CONFIG_BURST_Msk)
                            | ((SAADC_CH_CONFIG_MODE_SE         << SAADC_CH_CONFIG_MODE_Pos)   & SAADC_CH_CONFIG_MODE_Msk);
                         
  //configure Channel 0 to use A0 as positive
  NRF_SAADC->CH[0].PSELP = SAADC_CH_PSELP_PSELP_AnalogInput0;
  NRF_SAADC->CH[0].PSELN = SAADC_CH_PSELP_PSELP_AnalogInput0;


  // Enable SAADC END interrupt to do maintainance and printing of values.
  NRF_SAADC->INTENSET = SAADC_INTENSET_END_Enabled << SAADC_INTENSET_END_Pos;
  NVIC_EnableIRQ(SAADC_IRQn);
}

void adc_start()
{
    // Enable SAADC. This should be done after the SAADC is configure due to errata 74 SAADC: Started events fires prematurely
    NRF_SAADC->ENABLE = (SAADC_ENABLE_ENABLE_Enabled << SAADC_ENABLE_ENABLE_Pos);
 
    //start task
    NRF_SAADC->TASKS_START = 0x01UL;
    while (!NRF_SAADC->EVENTS_STARTED);
    NRF_SAADC->EVENTS_STARTED = 0x00UL;
    NRF_SAADC->TASKS_SAMPLE = 0x01UL;
}

void setup() {

  Serial.begin(9600);
  Serial.println("started...");

  adc_setup();
  adc_start();

  Serial.println("adc started...");
}

void loop() {
  if (saadc_results_ready) {
    Serial.println(data_buffer[0]);
 
    // restart acquistion
    NRF_SAADC->TASKS_START = 0x01UL;
    while (!NRF_SAADC->EVENTS_STARTED); NRF_SAADC->EVENTS_STARTED = 0x00UL;
    NRF_SAADC->TASKS_SAMPLE = 0x01UL;
    saadc_results_ready = false;
  }
  delay(1000);
}

4) CÓDIGO NO M-BED (testado com Platformio)

#include "mbed.h"
#include "stdio.h"

static int16_t data_buffer[1];
volatile bool saadc_results_ready = false;

AnalogIn ain(A0);

int main() {

//configure SAADC resolution
  NRF_SAADC->RESOLUTION = SAADC_RESOLUTION_VAL_10bit;

// enable oversampling
  NRF_SAADC->OVERSAMPLE = (SAADC_OVERSAMPLE_OVERSAMPLE_Over256x << SAADC_OVERSAMPLE_OVERSAMPLE_Pos) & SAADC_OVERSAMPLE_OVERSAMPLE_Msk;

//enable SAADC
  NRF_SAADC->ENABLE = (SAADC_ENABLE_ENABLE_Enabled << SAADC_ENABLE_ENABLE_Pos);

//set result pointer
  NRF_SAADC->RESULT.PTR = (uint32_t)(&data_buffer);
  NRF_SAADC->RESULT.MAXCNT = 1; // number of samples

 for (int i = 0; i < 8; i++) {
    NRF_SAADC->CH[i].PSELN = SAADC_CH_PSELP_PSELP_NC;
    NRF_SAADC->CH[i].PSELP = SAADC_CH_PSELP_PSELP_NC;
  }

  //set channel 0 resistor network, gain, reference, sample time, and mode
  NRF_SAADC->CH[0].CONFIG =   ((SAADC_CH_CONFIG_RESP_Bypass     << SAADC_CH_CONFIG_RESP_Pos)   & SAADC_CH_CONFIG_RESP_Msk)
                            | ((SAADC_CH_CONFIG_RESP_Bypass     << SAADC_CH_CONFIG_RESN_Pos)   & SAADC_CH_CONFIG_RESN_Msk)
                            | ((SAADC_CH_CONFIG_GAIN_Gain1_5    << SAADC_CH_CONFIG_GAIN_Pos)   & SAADC_CH_CONFIG_GAIN_Msk)
                            | ((SAADC_CH_CONFIG_REFSEL_Internal << SAADC_CH_CONFIG_REFSEL_Pos) & SAADC_CH_CONFIG_REFSEL_Msk)
                            | ((SAADC_CH_CONFIG_TACQ_3us        << SAADC_CH_CONFIG_TACQ_Pos)   & SAADC_CH_CONFIG_TACQ_Msk)
                            | ((SAADC_CH_CONFIG_BURST_Enabled   << SAADC_CH_CONFIG_BURST_Pos)  & SAADC_CH_CONFIG_BURST_Msk)
                            | ((SAADC_CH_CONFIG_MODE_SE         << SAADC_CH_CONFIG_MODE_Pos)   & SAADC_CH_CONFIG_MODE_Msk);
                         
//configure Channel 0 to use A0 as positive
  NRF_SAADC->CH[0].PSELP = SAADC_CH_PSELP_PSELP_AnalogInput0;
  NRF_SAADC->CH[0].PSELN = SAADC_CH_PSELP_PSELP_AnalogInput0;

// Enable SAADC. This should be done after the SAADC is configure due to errata 74 SAADC: Started events fires prematurely
    NRF_SAADC->ENABLE = (SAADC_ENABLE_ENABLE_Enabled << SAADC_ENABLE_ENABLE_Pos);

    //start task
    NRF_SAADC->TASKS_START = 0x01UL;
    while (!NRF_SAADC->EVENTS_STARTED);
    NRF_SAADC->EVENTS_STARTED = 0x00UL;
    NRF_SAADC->TASKS_SAMPLE = 0x01UL;
 
  while(1)
  {
    printf("%d\r\n",data_buffer[0]);
    fflush(stdout);
    // restart acquistion
    NRF_SAADC->TASKS_START = 0x01UL;
    while (!NRF_SAADC->EVENTS_STARTED);
    NRF_SAADC->EVENTS_STARTED = 0x00UL;
    NRF_SAADC->TASKS_SAMPLE = 0x01UL;
    wait(0.5);
  }
}

5) CÓDIGO NO JAVASCRIPT

var ll = require("NRF52LL");
var  on = false;

var saadc = ll.saadc({
  resolution : 10,
  channels : [ { // channel 0
    pin:D29,
    gain:1/4,
    tacq:40,
    refvdd:false,
  } ]
});

setInterval(function() {
  on = !on;
  LED1.write(on);
  print(saadc.sample()[0]);
}, 250);

6) CÓDIGO NO PYTHON (default 10 bits, internal REF. Analogin sempre gera valor de 16 bits)

import time

import board
from analogio import AnalogIn

analogin = AnalogIn(board.A0)

while True:
    print(analogin.value)
    time.sleep(1.0)

NINA B112

1) CÓDIGO NO ARDUINO

int val;

const uint8_t ANALOG_PIN = A0;

void setup() {
  Serial.begin(19200);
}

void loop() {
  val = analogRead(ANALOG_PIN);    //12 BITS
  Serial.println(val);           
  delay(1000);
}

2) CÓDIGO NO ESP-IDF

#include <stdio.h>
#include <stdlib.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"
#include "driver/adc.h"
#include "esp_adc_cal.h"

#define DEFAULT_VREF    1100        //Use adc2_vref_to_gpio() to obtain a better estimate
#define NO_OF_SAMPLES   64          //Multisampling

static esp_adc_cal_characteristics_t *adc_chars;
static const adc_channel_t channel = ADC_CHANNEL_6;     //GPIO34 if ADC1, GPIO14 if ADC2
static const adc_atten_t atten = ADC_ATTEN_DB_0;
static const adc_unit_t unit = ADC_UNIT_1;

static void check_efuse()
{
    //Check TP is burned into eFuse
    if (esp_adc_cal_check_efuse(ESP_ADC_CAL_VAL_EFUSE_TP) == ESP_OK) {
        printf("eFuse Two Point: Supported\n");
    } else {
        printf("eFuse Two Point: NOT supported\n");
    }

    //Check Vref is burned into eFuse
    if (esp_adc_cal_check_efuse(ESP_ADC_CAL_VAL_EFUSE_VREF) == ESP_OK) {
        printf("eFuse Vref: Supported\n");
    } else {
        printf("eFuse Vref: NOT supported\n");
    }
}

static void print_char_val_type(esp_adc_cal_value_t val_type)
{
    if (val_type == ESP_ADC_CAL_VAL_EFUSE_TP) {
        printf("Characterized using Two Point Value\n");
    } else if (val_type == ESP_ADC_CAL_VAL_EFUSE_VREF) {
        printf("Characterized using eFuse Vref\n");
    } else {
        printf("Characterized using Default Vref\n");
    }
}

void app_main()
{
    //Check if Two Point or Vref are burned into eFuse
    check_efuse();

    adc1_config_width(ADC_WIDTH_BIT_12);
    adc1_config_channel_atten(channel, atten);
 
    //Characterize ADC
    adc_chars = calloc(1, sizeof(esp_adc_cal_characteristics_t));
    esp_adc_cal_value_t val_type = esp_adc_cal_characterize(unit, atten, ADC_WIDTH_BIT_12, DEFAULT_VREF, adc_chars);
    print_char_val_type(val_type);

    //Continuously sample ADC1
    while (1) {
        uint32_t adc_reading = 0;
        //Multisampling
        for (int i = 0; i < NO_OF_SAMPLES; i++)
                adc_reading += adc1_get_raw((adc1_channel_t)channel);
        adc_reading /= NO_OF_SAMPLES;
        printf("%d\n", adc_reading);
        vTaskDelay(pdMS_TO_TICKS(1000));
    }
}


Questoes: suporte@smartcore.com.br

Referências

https://learn.adafruit.com/adafruit-circuit-playground-express/circuitpython-analog-in
https://u-blox-ninab112-circuitpython.blogspot.com/2018/10/u-blox-nina-b112-javascript-o-objetivo.html
https://www.segger.com/
http://www.espruino.com/NRF52LL
https://os.mbed.com/handbook/AnalogIn
https://learn.adafruit.com/bluefruit-nrf52-feather-learning-guide/nrf52-adc
https://docs.espressif.com/projects/esp-idf/en/latest/api-reference/peripherals/adc.html#overview
https://mjrobot.org/2017/09/26/iot-feito-facil-brincando-com-o-esp32-no-arduino-ide/