blob: 85ddffc036cafffa65b908073ce4f80055021647 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#include <assert.h>
#include "os/mynewt.h"
#include <hal/hal_bsp.h>
#include <adc/adc.h>
#include <mcu/cmsis_nvic.h>
/* Nordic headers */
#include <nrfx.h>
#include <nrf_saadc.h>
#include <nrfx_saadc.h>
#include "adc_nrf52/adc_nrf52.h"
struct nrf52_saadc_stats {
uint16_t saadc_events;
uint16_t saadc_events_failed;
};
static struct nrf52_saadc_stats nrf52_saadc_stats;
static struct adc_dev *global_adc_dev;
static nrfx_saadc_config_t *global_adc_config;
static struct nrf52_adc_dev_cfg *init_adc_config;
static uint8_t nrf52_adc_chans[NRF_SAADC_CHANNEL_COUNT * sizeof(struct adc_chan_config)];
static void
nrf52_saadc_event_handler(const nrfx_saadc_evt_t *event)
{
nrfx_saadc_done_evt_t *done_ev;
int rc;
if (global_adc_dev == NULL || !global_adc_dev->ad_event_handler_func) {
++nrf52_saadc_stats.saadc_events_failed;
return;
}
++nrf52_saadc_stats.saadc_events;
switch (event->type) {
case NRFX_SAADC_EVT_DONE:
done_ev = (nrfx_saadc_done_evt_t * const) &event->data.done;
rc = global_adc_dev->ad_event_handler_func(global_adc_dev,
global_adc_dev->ad_event_handler_arg,
ADC_EVENT_RESULT, done_ev->p_buffer,
done_ev->size * sizeof(nrf_saadc_value_t));
break;
case NRFX_SAADC_EVT_CALIBRATEDONE:
rc = global_adc_dev->ad_event_handler_func(global_adc_dev,
global_adc_dev->ad_event_handler_arg,
ADC_EVENT_CALIBRATED, NULL, 0);
break;
default:
assert(0);
break;
}
if (rc != 0) {
++nrf52_saadc_stats.saadc_events_failed;
}
}
/**
* Open the NRF52 ADC device
*
* This function locks the device for access from other tasks.
*
* @param odev The OS device to open
* @param wait The time in MS to wait. If 0 specified, returns immediately
* if resource unavailable. If OS_WAIT_FOREVER specified, blocks
* until resource is available.
* @param arg Argument provided by higher layer to open, in this case
* it can be a nrfx_saadc_config_t, to override the default
* configuration.
*
* @return 0 on success, non-zero on failure.
*/
static int
nrf52_adc_open(struct os_dev *odev, uint32_t wait, void *arg)
{
struct adc_dev *dev;
int rc = 0;
int unlock = 0;
dev = (struct adc_dev *) odev;
if (os_started()) {
rc = os_mutex_pend(&dev->ad_lock, wait);
if (rc != OS_OK) {
goto err;
}
unlock = 1;
}
if (++(dev->ad_ref_cnt) == 1) {
/* Initialize the device */
rc = nrfx_saadc_init((nrfx_saadc_config_t *) arg,
nrf52_saadc_event_handler);
if (rc != NRFX_SUCCESS) {
goto err;
}
rc = 0;
global_adc_dev = dev;
global_adc_config = arg;
}
err:
if (unlock) {
os_mutex_release(&dev->ad_lock);
}
return (rc);
}
/**
* Close the NRF52 ADC device.
*
* This function unlocks the device.
*
* @param odev The device to close.
*/
static int
nrf52_adc_close(struct os_dev *odev)
{
struct adc_dev *dev;
int rc = 0;
int unlock = 0;
dev = (struct adc_dev *) odev;
if (os_started()) {
rc = os_mutex_pend(&dev->ad_lock, OS_TIMEOUT_NEVER);
if (rc != OS_OK) {
goto err;
}
unlock = 1;
}
if (--(dev->ad_ref_cnt) == 0) {
nrfx_saadc_uninit();
global_adc_dev = NULL;
global_adc_config = NULL;
}
err:
if (unlock) {
os_mutex_release(&dev->ad_lock);
}
return rc;
}
/**
* Configure an ADC channel on the Nordic ADC.
*
* @param dev The ADC device to configure
* @param cnum The channel on the ADC device to configure
* @param cfgdata An opaque pointer to channel config, expected to be
* a nrf_saadc_channel_config_t
*
* @return 0 on success, non-zero on failure.
*/
static int
nrf52_adc_configure_channel(struct adc_dev *dev, uint8_t cnum,
void *cfgdata)
{
nrf_saadc_channel_config_t *cc;
uint16_t refmv;
uint8_t res;
int rc;
cc = (nrf_saadc_channel_config_t *) cfgdata;
rc = nrfx_saadc_channel_init(cnum, cc);
if (rc != NRFX_SUCCESS) {
goto err;
}
if (global_adc_config) {
/* Set the resolution and reference voltage for this channel to
* enable conversion functions.
*/
switch (global_adc_config->resolution) {
case NRF_SAADC_RESOLUTION_8BIT:
res = 8;
break;
case NRF_SAADC_RESOLUTION_10BIT:
res = 10;
break;
case NRF_SAADC_RESOLUTION_12BIT:
res = 12;
break;
case NRF_SAADC_RESOLUTION_14BIT:
res = 14;
break;
default:
assert(0);
}
} else {
/* Default to 10-bit resolution. */
res = 10;
}
switch (cc->reference) {
case NRF_SAADC_REFERENCE_INTERNAL:
refmv = 600; /* 0.6V for NRF52 */
break;
case NRF_SAADC_REFERENCE_VDD4:
refmv = init_adc_config->nadc_refmv / 4;
break;
default:
assert(0);
}
/* Adjust reference voltage for gain. */
switch (cc->gain) {
case NRF_SAADC_GAIN1_6:
refmv *= 6;
break;
case NRF_SAADC_GAIN1_5:
refmv *= 5;
break;
case NRF_SAADC_GAIN1_4:
refmv *= 4;
break;
case NRF_SAADC_GAIN1_3:
refmv *= 3;
break;
case NRF_SAADC_GAIN1_2:
refmv *= 2;
break;
case NRF_SAADC_GAIN2:
refmv /= 2;
break;
case NRF_SAADC_GAIN4:
refmv /= 4;
break;
default:
break;
}
/* Store these values in channel definitions, for conversions to
* milivolts.
*/
dev->ad_chans[cnum].c_res = res;
dev->ad_chans[cnum].c_refmv = refmv;
dev->ad_chans[cnum].c_configured = 1;
return (0);
err:
return (rc);
}
/**
* Set buffer to read data into. Implementation of setbuffer handler.
* Sets both the primary and secondary buffers for DMA.
*/
static int
nrf52_adc_set_buffer(struct adc_dev *dev, void *buf1, void *buf2,
int buf_len)
{
int rc;
/* Convert overall buffer length, into a total number of samples which
* Nordic APIs expect.
*/
buf_len /= sizeof(nrf_saadc_value_t);
rc = nrfx_saadc_buffer_convert((nrf_saadc_value_t *) buf1, buf_len);
if (rc != NRFX_SUCCESS) {
goto err;
}
if (buf2) {
rc = nrfx_saadc_buffer_convert((nrf_saadc_value_t *) buf2,
buf_len);
if (rc != NRFX_SUCCESS) {
goto err;
}
}
return (0);
err:
return (rc);
}
static int
nrf52_adc_release_buffer(struct adc_dev *dev, void *buf, int buf_len)
{
int rc;
buf_len /= sizeof(nrf_saadc_value_t);
rc = nrfx_saadc_buffer_convert((nrf_saadc_value_t *) buf, buf_len);
if (rc != NRFX_SUCCESS) {
goto err;
}
return (0);
err:
return (rc);
}
/**
* Trigger an ADC sample.
*/
static int
nrf52_adc_sample(struct adc_dev *dev)
{
nrfx_saadc_sample();
return (0);
}
/**
* Blocking read of an ADC channel, returns result as an integer.
*/
static int
nrf52_adc_read_channel(struct adc_dev *dev, uint8_t cnum, int *result)
{
nrf_saadc_value_t adc_value;
int rc;
int unlock = 0;
if (os_started()) {
rc = os_mutex_pend(&dev->ad_lock, OS_TIMEOUT_NEVER);
if (rc != OS_OK) {
goto err;
}
unlock = 1;
}
rc = nrfx_saadc_sample_convert(cnum, &adc_value);
if (rc != NRFX_SUCCESS) {
goto err;
}
*result = (int) adc_value;
rc = 0;
err:
if (unlock) {
os_mutex_release(&dev->ad_lock);
}
return (rc);
}
static int
nrf52_adc_read_buffer(struct adc_dev *dev, void *buf, int buf_len, int off,
int *result)
{
nrf_saadc_value_t val;
int data_off;
data_off = off * sizeof(nrf_saadc_value_t);
assert(data_off < buf_len);
val = *(nrf_saadc_value_t *) ((uint8_t *) buf + data_off);
*result = val;
return (0);
}
static int
nrf52_adc_size_buffer(struct adc_dev *dev, int chans, int samples)
{
return (sizeof(nrf_saadc_value_t) * chans * samples);
}
#if MYNEWT_VAL(OS_SYSVIEW)
static void
saadc_irq_handler(void)
{
os_trace_isr_enter();
nrfx_saadc_irq_handler();
os_trace_isr_exit();
}
#endif
/**
* ADC device driver functions
*/
static const struct adc_driver_funcs nrf52_adc_funcs = {
.af_configure_channel = nrf52_adc_configure_channel,
.af_sample = nrf52_adc_sample,
.af_read_channel = nrf52_adc_read_channel,
.af_set_buffer = nrf52_adc_set_buffer,
.af_release_buffer = nrf52_adc_release_buffer,
.af_read_buffer = nrf52_adc_read_buffer,
.af_size_buffer = nrf52_adc_size_buffer,
};
/**
* Callback to initialize an adc_dev structure from the os device
* initialization callback. This sets up a nrf52_adc_device(), so
* that subsequent lookups to this device allow us to manipulate it.
*/
int
nrf52_adc_dev_init(struct os_dev *odev, void *arg)
{
struct adc_dev *dev;
dev = (struct adc_dev *) odev;
os_mutex_init(&dev->ad_lock);
dev->ad_chans = (void *) nrf52_adc_chans;
dev->ad_chan_count = NRF_SAADC_CHANNEL_COUNT;
OS_DEV_SETHANDLERS(odev, nrf52_adc_open, nrf52_adc_close);
assert(init_adc_config == NULL || init_adc_config == arg);
init_adc_config = arg;
dev->ad_funcs = &nrf52_adc_funcs;
#if MYNEWT_VAL(OS_SYSVIEW)
NVIC_SetVector(SAADC_IRQn, (uint32_t) saadc_irq_handler);
#else
NVIC_SetVector(SAADC_IRQn, (uint32_t) nrfx_saadc_irq_handler);
#endif
return (0);
}