1092 lines
30 KiB
C
1092 lines
30 KiB
C
/****************************************************************************
|
|
* arch/arm/src/tiva/tiva_adclow.c
|
|
*
|
|
* Copyright (C) 2016-2017 Gregory Nutt. All rights reserved.
|
|
* Copyright (C) 2015 TRD2 Inc. All rights reserved.
|
|
* Author: Calvin Maguranis <calvin.maguranis@trd2inc.com>
|
|
* Gregory Nutt <gnutt@nuttx.org>
|
|
*
|
|
* References:
|
|
*
|
|
* TM4C123GH6PM Series Data Sheet
|
|
* TI Tivaware driverlib ADC sample code.
|
|
*
|
|
* The Tivaware sample code has a BSD compatible license that requires this
|
|
* copyright notice:
|
|
*
|
|
* Copyright (c) 2005-2014 Texas Instruments Incorporated. All rights reserved.
|
|
* Software License Agreement
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
*
|
|
* Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
*
|
|
* Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the
|
|
* distribution.
|
|
*
|
|
* Neither the name of Texas Instruments Incorporated nor the names of
|
|
* its contributors may be used to endorse or promote products derived
|
|
* from this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*
|
|
* This is part of revision 2.1.0.12573 of the Tiva Peripheral Driver Library.
|
|
****************************************************************************/
|
|
|
|
/* Keep in mind that for every step there should be another entry in the
|
|
* CONFIG_ADC_FIFOSIZE value.
|
|
* e.g. if there are 12 steps in use; CONFIG_ADC_FIFOSIZE = 12+1 = 13
|
|
* if there are 3 steps in use; CONFIG_ADC_FIFOSIZE = 3+1 = 4
|
|
*/
|
|
|
|
/****************************************************************************
|
|
* Included Files
|
|
****************************************************************************/
|
|
|
|
#include <nuttx/config.h>
|
|
|
|
#include <sys/types.h>
|
|
#include <stdio.h>
|
|
#include <stdint.h>
|
|
#include <stdbool.h>
|
|
#include <semaphore.h>
|
|
#include <errno.h>
|
|
#include <assert.h>
|
|
#include <debug.h>
|
|
|
|
#include <nuttx/arch.h>
|
|
#include <nuttx/wqueue.h>
|
|
#include <nuttx/analog/adc.h>
|
|
#include <nuttx/analog/ioctl.h>
|
|
|
|
#include <arch/board/board.h>
|
|
|
|
#include "up_arch.h"
|
|
#include "up_internal.h"
|
|
#include "tiva_gpio.h"
|
|
#include "tiva_adc.h"
|
|
#include "chip/tiva_adc.h"
|
|
#include "chip/tiva_pinmap.h"
|
|
#include "chip/tiva_syscontrol.h"
|
|
|
|
#ifdef CONFIG_TIVA_ADC
|
|
|
|
/****************************************************************************
|
|
* Pre-processor Definitions
|
|
****************************************************************************/
|
|
|
|
/* Configuration ************************************************************/
|
|
|
|
#ifndef CONFIG_TIVA_ADC_CLOCK
|
|
# define CONFIG_TIVA_ADC_CLOCK TIVA_ADC_CLOCK_MIN
|
|
#endif
|
|
|
|
#ifdef CONFIG_TIVA_ADC_VREF
|
|
# ifndef CONFIG_ARCH_CHIP_TM4C129
|
|
# error Voltage reference selection only supported in TM4C129 parts
|
|
# endif
|
|
#endif
|
|
|
|
#ifdef CONFIG_TIVA_ADC_ALT_CLK
|
|
# warning CONFIG_TIVA_ADC_ALT_CLK unsupported.
|
|
#endif
|
|
|
|
#ifndef CONFIG_SCHED_WORKQUEUE
|
|
# error Work queue support is required (CONFIG_SCHED_WORKQUEUE)
|
|
#endif
|
|
#ifndef CONFIG_SCHED_HPWORK
|
|
# error High priority worker threads is required (CONFIG_SCHED_HPWORK)
|
|
#endif
|
|
|
|
/* PWM trigger support definitions ******************************************/
|
|
|
|
/* Decodes the PWM generator and module from trigger and converts
|
|
* to the TSSEL_PS register
|
|
*/
|
|
|
|
#define ADC_TRIG_PWM_CFG(t) \
|
|
(1 << (ADC_TSSEL_PS_SHIFT(ADC_TRIG_gen(t))))
|
|
|
|
/* ADC support definitions **************************************************/
|
|
|
|
#define SSE_PROC_TRIG(n) (1 << (n))
|
|
#define SEM_PROCESS_PRIVATE 0
|
|
#define SEM_PROCESS_SHARED 1
|
|
|
|
/* DEBUG ********************************************************************/
|
|
|
|
#ifdef CONFIG_DEBUG_ANALOG
|
|
#endif
|
|
|
|
/****************************************************************************
|
|
* Public Functions
|
|
* **************************************************************************/
|
|
|
|
/* Upper level ADC driver ***************************************************/
|
|
|
|
static int tiva_adc_bind(FAR struct adc_dev_s *dev,
|
|
FAR const struct adc_callback_s *callback);
|
|
static void tiva_adc_reset(struct adc_dev_s *dev);
|
|
static int tiva_adc_setup(struct adc_dev_s *dev);
|
|
static void tiva_adc_shutdown(struct adc_dev_s *dev);
|
|
static void tiva_adc_rxint(struct adc_dev_s *dev, bool enable);
|
|
static int tiva_adc_ioctl(struct adc_dev_s *dev, int cmd, unsigned long arg);
|
|
|
|
/****************************************************************************
|
|
* Public Data
|
|
****************************************************************************/
|
|
|
|
/* ADC lower half device operations */
|
|
|
|
static const struct adc_ops_s g_adcops =
|
|
{
|
|
.ao_bind = tiva_adc_bind,
|
|
.ao_reset = tiva_adc_reset,
|
|
.ao_setup = tiva_adc_setup,
|
|
.ao_shutdown = tiva_adc_shutdown,
|
|
.ao_rxint = tiva_adc_rxint,
|
|
.ao_ioctl = tiva_adc_ioctl,
|
|
};
|
|
|
|
/****************************************************************************
|
|
* Private Types
|
|
****************************************************************************/
|
|
|
|
struct tiva_adc_s
|
|
{
|
|
struct adc_dev_s *dev;
|
|
const struct adc_callback_s *cb;
|
|
bool cfg; /* Configuration state */
|
|
bool ena; /* Operation state */
|
|
uint8_t devno; /* ADC device number */
|
|
};
|
|
|
|
struct tiva_adc_sse_s
|
|
{
|
|
sem_t exclsem; /* Mutual exclusion semaphore */
|
|
struct work_s work; /* Supports the interrupt handling "bottom half" */
|
|
bool cfg; /* Configuration state */
|
|
bool ena; /* Sample sequencer operation state */
|
|
uint8_t adc; /* Parent peripheral */
|
|
uint8_t num; /* SSE number */
|
|
};
|
|
|
|
/****************************************************************************
|
|
* Private Function Prototypes
|
|
****************************************************************************/
|
|
|
|
static void tiva_adc_interrupt(struct tiva_adc_sse_s *sse);
|
|
#ifdef CONFIG_DEBUG_ANALOG
|
|
static void tiva_adc_runtimeobj_ptrs(void);
|
|
static void tiva_adc_runtimeobj_vals(void);
|
|
static void tiva_adc_dump_dev(void);
|
|
#endif
|
|
|
|
/****************************************************************************
|
|
* Private Data
|
|
****************************************************************************/
|
|
|
|
/* Run-time ADC objects */
|
|
|
|
#ifdef CONFIG_TIVA_ADC0
|
|
static struct adc_dev_s dev0;
|
|
static struct tiva_adc_s adc0;
|
|
|
|
static struct tiva_adc_sse_s sse00;
|
|
static struct tiva_adc_sse_s sse01;
|
|
static struct tiva_adc_sse_s sse02;
|
|
static struct tiva_adc_sse_s sse03;
|
|
#endif
|
|
|
|
#ifdef CONFIG_TIVA_ADC1
|
|
static struct adc_dev_s dev1;
|
|
static struct tiva_adc_s adc1;
|
|
|
|
static struct tiva_adc_sse_s sse10;
|
|
static struct tiva_adc_sse_s sse11;
|
|
static struct tiva_adc_sse_s sse12;
|
|
static struct tiva_adc_sse_s sse13;
|
|
#endif
|
|
|
|
/* Offer run-time ADC objects in array form to help reduce the reliance on
|
|
* hard-coded, non-generic function calls.
|
|
*/
|
|
|
|
static struct adc_dev_s *g_devs[] =
|
|
{
|
|
#ifdef CONFIG_TIVA_ADC0
|
|
&dev0,
|
|
#else
|
|
NULL,
|
|
#endif
|
|
#ifdef CONFIG_TIVA_ADC1
|
|
&dev1
|
|
#else
|
|
NULL
|
|
#endif
|
|
};
|
|
|
|
static struct tiva_adc_s *g_adcs[] =
|
|
{
|
|
#ifdef CONFIG_TIVA_ADC0
|
|
&adc0,
|
|
#else
|
|
NULL,
|
|
#endif
|
|
#ifdef CONFIG_TIVA_ADC1
|
|
&adc1
|
|
#else
|
|
NULL
|
|
#endif
|
|
};
|
|
|
|
static struct tiva_adc_sse_s *g_sses[] =
|
|
{
|
|
#ifdef CONFIG_TIVA_ADC0
|
|
&sse00, &sse01, &sse02, &sse03,
|
|
#else
|
|
NULL, NULL, NULL, NULL,
|
|
#endif
|
|
#ifdef CONFIG_TIVA_ADC1
|
|
&sse10, &sse11, &sse12, &sse13
|
|
#else
|
|
NULL, NULL, NULL, NULL
|
|
#endif
|
|
};
|
|
|
|
/****************************************************************************
|
|
* Private Functions
|
|
****************************************************************************/
|
|
|
|
/* Interrupt handlers are inevitably hard-coded and specific */
|
|
|
|
#ifdef CONFIG_TIVA_ADC0
|
|
static int tiva_adc0_sse0_interrupt(int irq, void *context)
|
|
{
|
|
tiva_adc_interrupt(&sse00);
|
|
return OK;
|
|
}
|
|
|
|
static int tiva_adc0_sse1_interrupt(int irq, void *context)
|
|
{
|
|
tiva_adc_interrupt(&sse01);
|
|
return OK;
|
|
}
|
|
|
|
static int tiva_adc0_sse2_interrupt(int irq, void *context)
|
|
{
|
|
tiva_adc_interrupt(&sse02);
|
|
return OK;
|
|
}
|
|
|
|
static int tiva_adc0_sse3_interrupt(int irq, void *context)
|
|
{
|
|
tiva_adc_interrupt(&sse03);
|
|
return OK;
|
|
}
|
|
#endif
|
|
|
|
#ifdef CONFIG_TIVA_ADC1
|
|
static int tiva_adc1_sse0_interrupt(int irq, void *context)
|
|
{
|
|
tiva_adc_interrupt(&sse10);
|
|
return OK;
|
|
}
|
|
|
|
static int tiva_adc1_sse1_interrupt(int irq, void *context)
|
|
{
|
|
tiva_adc_interrupt(&sse11);
|
|
return OK;
|
|
}
|
|
|
|
static int tiva_adc1_sse2_interrupt(int irq, void *context)
|
|
{
|
|
tiva_adc_interrupt(&sse12);
|
|
return OK;
|
|
}
|
|
|
|
static int tiva_adc1_sse3_interrupt(int irq, void *context)
|
|
{
|
|
tiva_adc_interrupt(&sse13);
|
|
return OK;
|
|
}
|
|
#endif
|
|
|
|
static void tiva_adc_irqinitialize(struct tiva_adc_cfg_s *cfg)
|
|
{
|
|
ainfo("initialize irqs for ADC%d...\n", cfg->adc);
|
|
|
|
#ifdef CONFIG_TIVA_ADC0
|
|
if (cfg->adc == 0)
|
|
{
|
|
if (cfg->sse[0] && (cfg->ssecfg[0].trigger > 0))
|
|
{
|
|
tiva_adc_irq_attach(0, 0, tiva_adc0_sse0_interrupt);
|
|
}
|
|
|
|
if (cfg->sse[1] && (cfg->ssecfg[1].trigger > 0))
|
|
{
|
|
tiva_adc_irq_attach(0, 1, tiva_adc0_sse1_interrupt);
|
|
}
|
|
|
|
if (cfg->sse[2] && (cfg->ssecfg[2].trigger > 0))
|
|
{
|
|
tiva_adc_irq_attach(0, 2, tiva_adc0_sse2_interrupt);
|
|
}
|
|
|
|
if (cfg->sse[3] && (cfg->ssecfg[3].trigger > 0))
|
|
{
|
|
tiva_adc_irq_attach(0, 3, tiva_adc0_sse3_interrupt);
|
|
}
|
|
}
|
|
#endif
|
|
#ifdef CONFIG_TIVA_ADC1
|
|
if (cfg->adc == 1)
|
|
{
|
|
if (cfg->sse[0] && (cfg->ssecfg[0].trigger > 0))
|
|
{
|
|
tiva_adc_irq_attach(1, 0, tiva_adc1_sse0_interrupt);
|
|
}
|
|
|
|
if (cfg->sse[1] && (cfg->ssecfg[1].trigger > 0))
|
|
{
|
|
tiva_adc_irq_attach(1, 1, tiva_adc1_sse1_interrupt);
|
|
}
|
|
|
|
if (cfg->sse[2] && (cfg->ssecfg[2].trigger > 0))
|
|
{
|
|
tiva_adc_irq_attach(1, 2, tiva_adc1_sse2_interrupt);
|
|
}
|
|
|
|
if (cfg->sse[3] && (cfg->ssecfg[3].trigger > 0))
|
|
{
|
|
tiva_adc_irq_attach(1, 3, tiva_adc1_sse3_interrupt);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: tiva_adc_bind
|
|
*
|
|
* Description:
|
|
* Bind the upper-half driver callbacks to the lower-half implementation. This
|
|
* must be called early in order to receive ADC event notifications.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int tiva_adc_bind(FAR struct adc_dev_s *dev,
|
|
FAR const struct adc_callback_s *callback)
|
|
{
|
|
struct tiva_adc_s *priv = (struct tiva_adc_s *)dev->ad_priv;
|
|
|
|
DEBUGASSERT(priv != NULL);
|
|
priv->cb = callback;
|
|
return OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: tiva_adc_reset
|
|
*
|
|
* Description:
|
|
* Reset the ADC device. Called early to initialize the hardware. This is
|
|
* called before tiva_adc_setup() and on error conditions.
|
|
*
|
|
* Resetting disables interrupts and the enabled SSEs for the ADC.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static void tiva_adc_reset(struct adc_dev_s *dev)
|
|
{
|
|
ainfo("Resetting...\n");
|
|
|
|
struct tiva_adc_s *priv = (struct tiva_adc_s *)dev->ad_priv;
|
|
struct tiva_adc_sse_s *sse;
|
|
uint8_t s;
|
|
|
|
tiva_adc_rxint(dev, false);
|
|
|
|
for (s = 0; s < 4; ++s)
|
|
{
|
|
sse = g_sses[SSE_IDX(priv->devno, s)];
|
|
|
|
if (sse->ena == true)
|
|
{
|
|
tiva_adc_sse_enable(priv->devno, s, false);
|
|
sse->ena = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: tiva_adc_setup
|
|
*
|
|
* Description:
|
|
* Configure the ADC. This method is called the first time that the ADC
|
|
* device is opened. This will occur when the port is first opened.
|
|
* Interrupts are all disabled upon return.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int tiva_adc_setup(struct adc_dev_s *dev)
|
|
{
|
|
ainfo("Setup\n");
|
|
|
|
struct tiva_adc_s *priv = (struct tiva_adc_s *)dev->ad_priv;
|
|
struct tiva_adc_sse_s *sse;
|
|
uint8_t s = 0;
|
|
|
|
priv->ena = true;
|
|
|
|
for (s = 0; s < 4; ++s)
|
|
{
|
|
sse = g_sses[SSE_IDX(priv->devno, s)];
|
|
if (sse->cfg == true)
|
|
{
|
|
tiva_adc_sse_enable(priv->devno, s, true);
|
|
sse->ena = true;
|
|
}
|
|
}
|
|
|
|
tiva_adc_rxint(dev, false);
|
|
return OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: tiva_adc_shutdown
|
|
*
|
|
* Description:
|
|
* Disable the ADC. This method is called when the ADC device is closed.
|
|
* This method reverses the operation the setup method.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static void tiva_adc_shutdown(struct adc_dev_s *dev)
|
|
{
|
|
struct tiva_adc_s *priv = (struct tiva_adc_s *)dev->ad_priv;
|
|
ainfo("Shutdown\n");
|
|
|
|
DEBUGASSERT(priv->ena);
|
|
|
|
/* Resetting the ADC peripheral disables interrupts and all SSEs */
|
|
|
|
tiva_adc_reset(dev);
|
|
|
|
/* Currently all of the setup operations are undone in reset() */
|
|
|
|
#if 0
|
|
struct tiva_adc_sse_s *sse;
|
|
uint8_t s = 0;
|
|
|
|
for (s = 0; s < 4; ++s)
|
|
{
|
|
}
|
|
#endif
|
|
|
|
priv->ena = false;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: tiva_adc_rxint
|
|
*
|
|
* Description:
|
|
* Call to enable or disable RX interrupts
|
|
*
|
|
* Input Parameters:
|
|
* enable - the enable state of interrupts for this device
|
|
*
|
|
****************************************************************************/
|
|
|
|
static void tiva_adc_rxint(struct adc_dev_s *dev, bool enable)
|
|
{
|
|
ainfo("RXINT=%d\n", enable);
|
|
|
|
struct tiva_adc_s *priv = (struct tiva_adc_s *)dev->ad_priv;
|
|
struct tiva_adc_sse_s *sse;
|
|
uint32_t trigger;
|
|
uint8_t s = 0;
|
|
|
|
DEBUGASSERT(priv->ena);
|
|
|
|
for (s = 0; s < 4; ++s)
|
|
{
|
|
trigger = tiva_adc_get_trigger(priv->devno, s);
|
|
sse = g_sses[SSE_IDX(priv->devno, s)];
|
|
if ((sse->ena == true)
|
|
&& (trigger > 0))
|
|
{
|
|
tiva_adc_sse_int_enable(priv->devno, s, enable);
|
|
}
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: tiva_adc_ioctl
|
|
*
|
|
* Description:
|
|
* All ioctl calls will be routed through this method.
|
|
*
|
|
* Input Parameters:
|
|
* cmd - ADC ioctl command
|
|
* arg - argument for the ioctl command
|
|
*
|
|
* Returned Value:
|
|
* Non negative value on success; negative value on failure.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int tiva_adc_ioctl(struct adc_dev_s *dev, int cmd, unsigned long arg)
|
|
{
|
|
int ret = OK;
|
|
|
|
ainfo("cmd=%d arg=%ld\n", cmd, arg);
|
|
|
|
switch (cmd)
|
|
{
|
|
/* Software trigger */
|
|
|
|
case ANIOC_TRIGGER:
|
|
{
|
|
struct tiva_adc_s *priv = (struct tiva_adc_s *)dev->ad_priv;
|
|
uint8_t i = 0;
|
|
uint8_t fifo_count = 0;
|
|
uint8_t sse = (uint8_t) arg;
|
|
int32_t buf[8];
|
|
|
|
/* Get exclusive access to the driver data structure */
|
|
|
|
tiva_adc_lock(priv, sse);
|
|
|
|
/* Start conversion and wait for end of conversion */
|
|
|
|
tiva_adc_proc_trig(priv->devno, (uint8_t)SSE_PROC_TRIG(sse));
|
|
while (!tiva_adc_sse_int_status(priv->devno, sse))
|
|
{
|
|
usleep(100);
|
|
}
|
|
|
|
tiva_adc_sse_clear_int(priv->devno, sse);
|
|
|
|
/* Pass sampled data to upper ADC driver */
|
|
|
|
fifo_count = tiva_adc_sse_data(priv->devno, sse, buf);
|
|
|
|
/* Verify that the upper-half driver has bound its callback functions */
|
|
|
|
if (priv->cb != NULL)
|
|
{
|
|
DEBUGASSERT(priv->cb->au_receive != NULL);
|
|
|
|
for (i = 0; i < fifo_count; ++i)
|
|
{
|
|
/* Perform the data received callback */
|
|
|
|
priv->cb->au_receive(dev,
|
|
tiva_adc_get_ain(priv->devno, sse, i),
|
|
buf[i]);
|
|
}
|
|
}
|
|
|
|
/* Release our lock on the ADC structure */
|
|
|
|
tiva_adc_unlock(priv, sse);
|
|
}
|
|
break;
|
|
|
|
/* PWM triggering */
|
|
|
|
#warning Missing Logic
|
|
|
|
/* TODO: Needs to be tested */
|
|
|
|
#ifdef CONFIG_EXPERIMENTAL
|
|
case TIVA_ADC_PWM_TRIG_IOCTL:
|
|
{
|
|
uint8_t sse = (uint8_t)(arg & 0x2);
|
|
uint8_t regval = tiva_adc_get_trigger(adc, sse);
|
|
|
|
/* Verify input SSE trigger is a PWM trigger */
|
|
|
|
if ((regval & TIVA_ADC_TRIG_PWM0) ||
|
|
(regval & TIVA_ADC_TRIG_PWM1) ||
|
|
(regval & TIVA_ADC_TRIG_PWM2) ||
|
|
(regval & TIVA_ADC_TRIG_PWM3))
|
|
{
|
|
tiva_adc_sse_pwm_trig(adc, sse, (uint32_t)(arg&0xFFFFFFFC));
|
|
}
|
|
}
|
|
break;
|
|
#endif
|
|
|
|
#warning Missing Logic
|
|
|
|
/* Unsupported or invalid command */
|
|
|
|
default:
|
|
ret = -ENOTTY;
|
|
break;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: tiva_adc_read
|
|
*
|
|
* Description:
|
|
* This function executes on the worker thread. It is scheduled by
|
|
* tiva_adc_interrupt whenever any enabled event occurs. All interrupts
|
|
* are disabled when this function runs. tiva_adc_read will
|
|
* re-enable interrupts when it completes processing all pending events.
|
|
*
|
|
* Input Parameters
|
|
* arg - The ADC SSE data structure cast to (void *)
|
|
*
|
|
* Returned Value:
|
|
* None
|
|
*
|
|
****************************************************************************/
|
|
|
|
static void tiva_adc_read(void *arg)
|
|
{
|
|
struct tiva_adc_sse_s *sse = (struct tiva_adc_sse_s *)arg;
|
|
struct adc_dev_s *dev = 0;
|
|
int irq = tiva_adc_getirq(sse->adc, sse->num);
|
|
uint8_t i = 0;
|
|
uint8_t fifo_count = 0;
|
|
int32_t buf[8];
|
|
|
|
/* Get exclusive access to the driver data structure */
|
|
|
|
tiva_adc_lock(g_adcs[sse->adc], sse->num);
|
|
|
|
/* Get sampled data */
|
|
|
|
fifo_count = tiva_adc_sse_data(sse->adc, sse->num, buf);
|
|
|
|
/* Determine which adc_dev_s we need */
|
|
|
|
dev = g_devs[sse->adc];
|
|
if (dev == NULL)
|
|
{
|
|
/* This is a serious error: indicates invalid pointer indirection
|
|
* and should cause a full system stop.
|
|
*/
|
|
|
|
aerr("ERROR: Invalid ADC device number given %d\n", sse->adc);
|
|
PANIC();
|
|
return;
|
|
}
|
|
|
|
/* Verify that the upper-half driver has bound its callback functions */
|
|
|
|
if (priv->cb != NULL)
|
|
{
|
|
DEBUGASSERT(priv->cb->au_receive != NULL);
|
|
|
|
for (i = 0; i < fifo_count; ++i)
|
|
{
|
|
/* Perform the data received callback */
|
|
|
|
priv->cb->au_receive(dev,
|
|
tiva_adc_get_ain(sse->adc, sse->num, i),
|
|
buf[i]);
|
|
ainfo("AIN%d = 0x%04x\n",
|
|
tiva_adc_get_ain(sse->adc, sse->num, i), buf[i]);
|
|
}
|
|
}
|
|
|
|
/* Exit, re-enabling ADC interrupts */
|
|
|
|
up_enable_irq(irq);
|
|
|
|
/* Release our lock on the ADC structure */
|
|
|
|
tiva_adc_unlock(g_adcs[sse->adc], sse->num);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: tiva_adc_interrupt
|
|
*
|
|
* Description:
|
|
* This function is called by every interrupt handler and handles the
|
|
* actual worker dispatching.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static void tiva_adc_interrupt(struct tiva_adc_sse_s *sse)
|
|
{
|
|
int ret;
|
|
int irq = tiva_adc_getirq(sse->adc, sse->num);
|
|
|
|
DEBUGASSERT(sse->ena == true);
|
|
|
|
/* disable further interrupts. Interrupts will be re-enabled
|
|
* after the worker thread executes.
|
|
*/
|
|
|
|
up_disable_irq(irq);
|
|
|
|
/* Clear interrupt status */
|
|
|
|
tiva_adc_sse_clear_int(sse->adc, sse->num);
|
|
|
|
/* Transfer processing to the worker thread. Since interrupts are
|
|
* disabled while the work is pending, no special action should be
|
|
* required to protected the work queue.
|
|
*/
|
|
|
|
DEBUGASSERT(sse->work.worker == NULL);
|
|
ret = work_queue(HPWORK, &sse->work, tiva_adc_read, sse, 0);
|
|
if (ret != 0)
|
|
{
|
|
aerr("ERROR: Failed to queue work: %d ADC.SSE: %d.%d\n",
|
|
ret, sse->adc, sse->num);
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: tiva_adc_struct_init
|
|
*
|
|
* Description:
|
|
* Initialize public and private adc structures their member SSE's.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static struct tiva_adc_s *tiva_adc_struct_init(struct tiva_adc_cfg_s *cfg)
|
|
{
|
|
struct tiva_adc_s *adc = g_adcs[cfg->adc];
|
|
struct tiva_adc_sse_s *sse = 0;
|
|
uint8_t s = 0;
|
|
|
|
/* Do not re-initialize the run-time structures, there is a chance another
|
|
* process is also using this ADC.
|
|
*/
|
|
|
|
if (adc->cfg == true)
|
|
{
|
|
goto tiva_adc_struct_init_ok;
|
|
}
|
|
else
|
|
{
|
|
if (adc != NULL)
|
|
{
|
|
adc->ena = false;
|
|
adc->devno = cfg->adc;
|
|
|
|
for (s = 0; s < 4; s++)
|
|
{
|
|
|
|
/* Only configure selected SSEs */
|
|
|
|
if (cfg->sse[s])
|
|
{
|
|
sse = g_sses[SSE_IDX(cfg->adc, s)];
|
|
|
|
if (sse != NULL)
|
|
{
|
|
sse->adc = cfg->adc;
|
|
sse->num = s;
|
|
sem_init(&sse->exclsem, SEM_PROCESS_PRIVATE, 1);
|
|
sse->ena = false;
|
|
sse->cfg = true;
|
|
}
|
|
else
|
|
{
|
|
goto tiva_adc_struct_init_error;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Initialize the public ADC device data structure */
|
|
|
|
adc->dev = g_devs[cfg->adc];
|
|
if (adc->dev != NULL)
|
|
{
|
|
adc->dev->ad_ops = &g_adcops;
|
|
adc->dev->ad_priv = adc;
|
|
}
|
|
else
|
|
{
|
|
goto tiva_adc_struct_init_error;
|
|
}
|
|
goto tiva_adc_struct_init_ok;
|
|
}
|
|
else
|
|
{
|
|
goto tiva_adc_struct_init_error;
|
|
}
|
|
}
|
|
|
|
tiva_adc_struct_init_error:
|
|
ainfo("Invalid ADC device number: expected=%d actual=%d\n",
|
|
0, cfg->adc);
|
|
ainfo("ADC%d (CONFIG_TIVA_ADC%d) must be enabled in Kconfig first!",
|
|
cfg->adc, cfg->adc);
|
|
return NULL;
|
|
|
|
tiva_adc_struct_init_ok:
|
|
adc->cfg = true;
|
|
return adc;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Public Functions
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Name: tiva_adc_initialize
|
|
*
|
|
* Description:
|
|
* Configuration and bind the ADC to the ADC lower half instance and
|
|
* register the ADC driver at 'devpath'.
|
|
*
|
|
* Input Parameters:
|
|
* devpath - The full path to the ADC device. This should be of the
|
|
* form /dev/adc0
|
|
* cfg - ADC configuration structure, configures the whole ADC.
|
|
* clock - clock speed for all ADC's. This is only set once for the first
|
|
* call to tiva_adc_initialize, otherwise the values are ignored.
|
|
* sample_rate - maximum sample rate of any ADC. This is only set once
|
|
* for the first call to tiva_adc_initialize, otherwise the values are
|
|
* ignored.
|
|
*
|
|
****************************************************************************/
|
|
|
|
int tiva_adc_initialize(const char *devpath, struct tiva_adc_cfg_s *cfg,
|
|
uint32_t clock, uint8_t sample_rate)
|
|
{
|
|
struct tiva_adc_s *adc;
|
|
int ret = 0;
|
|
|
|
ainfo("initializing...\n");
|
|
|
|
/* Initialize the private ADC device data structure */
|
|
|
|
adc = tiva_adc_struct_init(cfg);
|
|
if (adc == NULL)
|
|
{
|
|
aerr("ERROR: Invalid ADC device number: expected=%d actual=%d\n",
|
|
0, cfg->adc);
|
|
return -ENODEV;
|
|
}
|
|
|
|
/* Turn on peripheral */
|
|
|
|
if (tiva_adc_enable(adc->devno, true) < 0)
|
|
{
|
|
aerr("ERROR: failure to power ADC peripheral (devno=%d)\n",
|
|
cfg->adc);
|
|
return ret;
|
|
}
|
|
|
|
/* Perform actual initialization */
|
|
|
|
tiva_adc_one_time_init(clock, sample_rate);
|
|
tiva_adc_configure(cfg);
|
|
tiva_adc_irqinitialize(cfg);
|
|
|
|
/* Now we are initialized */
|
|
|
|
adc->ena = true;
|
|
adc->cb = NULL;
|
|
|
|
#ifdef CONFIG_DEBUG_ANALOG
|
|
tiva_adc_runtimeobj_vals();
|
|
#endif
|
|
|
|
/* Ensure our lower half is valid */
|
|
|
|
if (adc->dev == NULL)
|
|
{
|
|
aerr("ERROR: Failed to get interface %s\n", devpath);
|
|
return -ENODEV;
|
|
}
|
|
|
|
ainfo("adc_dev_s=0x%08x\n", adc->dev);
|
|
|
|
/* Register the ADC driver */
|
|
|
|
ainfo("Register the ADC driver at %s\n", devpath);
|
|
|
|
ret = adc_register(devpath, adc->dev);
|
|
if (ret < 0)
|
|
{
|
|
aerr("ERROR: Failed to register %s to character driver: %d\n",
|
|
devpath, ret);
|
|
return ret;
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: tiva_adc_lock
|
|
*
|
|
* Description:
|
|
* Get exclusive access to the ADC interface
|
|
*
|
|
****************************************************************************/
|
|
|
|
void tiva_adc_lock(FAR struct tiva_adc_s *priv, int sse)
|
|
{
|
|
ainfo("Locking...\n");
|
|
|
|
struct tiva_adc_sse_s *s = g_sses[SSE_IDX(priv->devno, sse)];
|
|
int ret;
|
|
#ifdef CONFIG_DEBUG_ANALOG
|
|
uint16_t loop_count = 0;
|
|
#endif
|
|
|
|
do
|
|
{
|
|
ret = sem_wait(&s->exclsem);
|
|
|
|
/* This should only fail if the wait was canceled by an signal (and the
|
|
* worker thread will receive a lot of signals).
|
|
*/
|
|
|
|
DEBUGASSERT(ret == OK || errno == EINTR);
|
|
|
|
#ifdef CONFIG_DEBUG_ANALOG
|
|
if (loop_count % 1000)
|
|
{
|
|
ainfo("loop=%d\n");
|
|
}
|
|
++loop_count;
|
|
#endif
|
|
}
|
|
while (ret < 0);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: tiva_adc_unlock
|
|
*
|
|
* Description:
|
|
* Relinquish the lock on the ADC interface
|
|
*
|
|
****************************************************************************/
|
|
|
|
void tiva_adc_unlock(FAR struct tiva_adc_s *priv, int sse)
|
|
{
|
|
ainfo("Unlocking\n");
|
|
struct tiva_adc_sse_s *s = g_sses[SSE_IDX(priv->devno, sse)];
|
|
sem_post(&s->exclsem);
|
|
}
|
|
|
|
/* DEBUG ********************************************************************/
|
|
|
|
#ifdef CONFIG_DEBUG_ANALOG
|
|
|
|
/****************************************************************************
|
|
* Name: tiva_adc_runtimeobj_ptrs
|
|
*
|
|
* Description:
|
|
* Dumps the address of all run-time objects for verification.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static void tiva_adc_runtimeobj_ptrs(void)
|
|
{
|
|
# ifdef CONFIG_TIVA_ADC0
|
|
ainfo("ADC0 [struct] [global value] [array value]\n");
|
|
ainfo(" adc_dev_s dev0=0x%08x g_devs[0]=0x%08x\n",
|
|
&dev0, g_devs[0]);
|
|
ainfo(" tiva_adc_s adc0=0x%08x g_adcs[0]=0x%08x\n",
|
|
&adc0, g_adcs[0]);
|
|
ainfo(" tiva_adc_sse_s sse0=0x%08x g_sses[0,0]=0x%08x\n",
|
|
&sse00, g_sses[SSE_IDX(0, 0)]);
|
|
ainfo(" tiva_adc_sse_s sse1=0x%08x g_sses[0,1]=0x%08x\n",
|
|
&sse01, g_sses[SSE_IDX(0, 1)]);
|
|
ainfo(" tiva_adc_sse_s sse2=0x%08x g_sses[0,2]=0x%08x\n",
|
|
&sse02, g_sses[SSE_IDX(0, 2)]);
|
|
ainfo(" tiva_adc_sse_s sse3=0x%08x g_sses[0,3]=0x%08x\n",
|
|
&sse03, g_sses[SSE_IDX(0, 3)]);
|
|
# endif
|
|
# ifdef CONFIG_TIVA_ADC1
|
|
ainfo("ADC1 [struct] [global value] [array value]\n");
|
|
ainfo(" adc_dev_s dev1=0x%08x g_devs[1]=0x%08x\n",
|
|
&dev1, g_devs[1]);
|
|
ainfo(" tiva_adc_s adc1=0x%08x g_adcs[1]=0x%08x\n",
|
|
&adc1, g_adcs[1]);
|
|
ainfo(" tiva_adc_sse_s sse0=0x%08x g_sses[1,0]=0x%08x\n",
|
|
&sse10, g_sses[SSE_IDX(1, 0)]);
|
|
ainfo(" tiva_adc_sse_s sse1=0x%08x g_sses[1,1]=0x%08x\n",
|
|
&sse11, g_sses[SSE_IDX(1, 1)]);
|
|
ainfo(" tiva_adc_sse_s sse2=0x%08x g_sses[1,2]=0x%08x\n",
|
|
&sse12, g_sses[SSE_IDX(1, 2)]);
|
|
ainfo(" tiva_adc_sse_s sse3=0x%08x g_sses[1,3]=0x%08x\n",
|
|
&sse13, g_sses[SSE_IDX(1, 3)]);
|
|
# endif
|
|
}
|
|
|
|
static void tiva_adc_runtimeobj_vals(void)
|
|
{
|
|
struct tiva_adc_sse_s *sse;
|
|
uint8_t s;
|
|
# ifdef CONFIG_TIVA_ADC0
|
|
ainfo("ADC0 [0x%08x] cfg=%d ena=%d devno=%d\n",
|
|
&adc0, adc0.cfg, adc0.ena, adc0.devno);
|
|
|
|
for (s = 0; s < 4; ++s)
|
|
{
|
|
sse = g_sses[SSE_IDX(0, s)];
|
|
ainfo("SSE%d [0x%08x] adc=%d cfg=%d ena=%d num=%d\n",
|
|
s, sse, sse->adc, sse->cfg, sse->ena, sse->num);
|
|
}
|
|
# endif
|
|
# ifdef CONFIG_TIVA_ADC1
|
|
ainfo("ADC1 [0x%08x] cfg=%d ena=%d devno=%d\n",
|
|
&adc1, adc1.cfg, adc1.ena, adc1.devno);
|
|
|
|
for (s = 0; s < 4; ++s)
|
|
{
|
|
sse = g_sses[SSE_IDX(1, s)];
|
|
ainfo("SSE%d [0x%08x] adc=%d cfg=%d ena=%d num=%d\n",
|
|
s, sse, sse->adc, sse->cfg, sse->ena, sse->num);
|
|
}
|
|
# endif
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: tiva_adc_unlock
|
|
*
|
|
* Description:
|
|
* umps the device level objects for verification.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static void tiva_adc_dump_dev(void)
|
|
{
|
|
# ifdef CONFIG_TIVA_ADC0
|
|
ainfo("adc_ops_s g_adcops=0x%08x adc0.dev->ad_ops=0x%08x\n",
|
|
&g_adcops, adc0.dev->ad_ops);
|
|
ainfo("tiva_adc_s adc0=0x%08x adc0.dev->ad_priv=0x%08x\n",
|
|
&adc0, adc0.dev->ad_priv);
|
|
# endif
|
|
# ifdef CONFIG_TIVA_ADC1
|
|
ainfo("adc_ops_s g_adcops=0x%08x adc1.dev->ad_ops=0x%08x\n",
|
|
&g_adcops, adc1.dev->ad_ops);
|
|
ainfo("tiva_adc_s adc1=0x%08x adc1.dev->ad_priv=0x%08x\n",
|
|
&adc1, adc1.dev->ad_priv);
|
|
# endif
|
|
}
|
|
#endif
|
|
|
|
#endif /* CONFIG_TIVA_ADC */
|