Newer
Older
/****************************************************************************
* arch/arm/src/stm32/stm32_adc.c
*
* Copyright (C) 2011 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <gnutt@nuttx.org>
* Diego Sanchez <dsanchez@nx-engineering.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. 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.
* 3. Neither the name NuttX 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.
*
****************************************************************************/
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <stdio.h>
#include <sys/types.h>
#include <stdint.h>
#include <stdbool.h>
#include <semaphore.h>
#include <errno.h>
#include <debug.h>
#include <arch/board/board.h>
#include <nuttx/arch.h>
#include <nuttx/analog/adc.h>
#include "up_internal.h"
#include "up_arch.h"
#include "chip.h"
#include "stm32_internal.h"
#include "stm32_adc.h"
#ifdef CONFIG_ADC
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/* Configuration ************************************************************/
/* Up to 3 ADC interfaces are supported */
#if STM32_NADC < 3
# undef CONFIG_STM32_ADC3
#endif
#if STM32_NADC < 2
# undef CONFIG_STM32_ADC2
#endif
#if STM32_NADC < 1
# undef CONFIG_STM32_ADC1
#endif
#if defined(CONFIG_STM32_ADC1) || defined(CONFIG_STM32_ADC2) || defined(CONFIG_STM32_ADC3)
/* ADC interrupts */
#ifdef CONFIG_STM32_STM32F10XX
# define ADC_SR_ALLINTS (ADC_SR_AWD | ADC_SR_EOC | ADC_SR_JEOC)
#else
# define ADC_SR_ALLINTS (ADC_SR_AWD | ADC_SR_EOC | ADC_SR_JEOC | ADC_SR_OVR)
#endif
#ifdef CONFIG_STM32_STM32F10XX
# define ADC_CR1_ALLINTS (ADC_CR1_AWDIE | ADC_CR1_EOCIE | ADC_CR1_JEOCIE)
#else
# define ADC_CR1_ALLINTS (ADC_CR1_AWDIE | ADC_CR1_EOCIE | ADC_CR1_JEOCIE | ADC_CR1_OVRIE)
#endif
/****************************************************************************
* Private Types
****************************************************************************/
/* This structure describes the state of one ADC block */
int irq; /* Interrupt generated by this ADC block */
xcpt_t isr; /* Interrupt handler for this ADC block */
uint32_t base; /* Base address of registers unique to this ADC block */
int32_t buf[8];
uint8_t count[8];
};
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
/* ADC Register access */
static uint32_t adc_getreg(struct stm32_dev_s *priv, int offset);
static void adc_putreg(struct stm32_dev_s *priv, int offset, uint32_t value);
static void adc_interrupt(FAR struct stm32_dev_s *priv);
#if defined(CONFIG_STM32_STM32F10XX) && (defined(CONFIG_STM32_ADC1) || defined(CONFIG_STM32_ADC2))
static int adc12_interrupt(int irq, void *context)
#endif
#ifdef CONFIG_STM32_STM32F10XX && defined (CONFIG_STM32_ADC3)
static int adc3_interrupt(int irq, void *context)
#endif
#ifdef CONFIG_STM32_STM32F40XX
static int adc123_interrupt(int irq, void *context)
#endif
/* ADC Driver Methods */
static void adc_reset(FAR struct adc_dev_s *dev);
static int adc_setup(FAR struct adc_dev_s *dev);
static void adc_shutdown(FAR struct adc_dev_s *dev);
static void adc_rxint(FAR struct adc_dev_s *dev, bool enable);
static int adc_ioctl(FAR struct adc_dev_s *dev, int cmd, unsigned long arg);
/****************************************************************************
* Private Data
****************************************************************************/
static const struct adc_ops_s g_adcops =
{
.ao_reset = adc_reset,
.ao_setup = adc_setup,
.ao_shutdown = adc_shutdown,
.ao_rxint = adc_rxint,
.ao_ioctl = adc_ioctl,
};
#ifdef CONFIG_STM32_ADC1
static struct stm32_dev_s g_adcpriv1 =
{
#ifdef CONFIG_STM32_STM32F10XX
.irq = STM32_IRQ_ADC12,
.isr = adc12_interrupt,
.irq = STM32_IRQ_ADC,
.isr = adc123_interrupt,
};
static struct adc_dev_s g_adcdev1 =
{
.ad_ops = &g_adcops,
#ifdef CONFIG_STM32_ADC2
static struct stm32_dev_s g_adcpriv2 =
{
#ifdef CONFIG_STM32_STM32F10XX
.irq = STM32_IRQ_ADC12,
.isr = adc12_interrupt,
.irq = STM32_IRQ_ADC,
.isr = adc123_interrupt,
};
static struct adc_dev_s g_adcdev2 =
{
.ad_ops = &g_adcops,
.ad_priv= &g_adcpriv2,
#ifdef CONFIG_STM32_ADC3
static struct stm32_dev_s g_adcpriv3 =
{
#ifdef CONFIG_STM32_STM32F10XX
.irq = STM32_IRQ_ADC3,
.isr = adc3_interrupt,
.irq = STM32_IRQ_ADC,
.isr = adc123_interrupt,
};
static struct adc_dev_s g_adcdev3 =
{
.ad_ops = &g_adcops,
};
#endif
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* priv - A reference to the ADC block status
* offset - The offset to the register to read
*
* Returned Value:
*
****************************************************************************/
static uint32_t adc_getreg(struct stm32_dev_s *priv, int offset)
/****************************************************************************
* Name: adc_getreg
*
* Description:
* Read the value of an ADC register.
*
* Input Parameters:
* priv - A reference to the ADC block status
* offset - The offset to the register to read
*
* Returned Value:
*
****************************************************************************/
static void adc_putreg(struct stm32_dev_s *priv, int offset, uint32_t value)
{
}
/****************************************************************************
* Name: adc_reset
*
* Description:
* Reset the ADC device. Called early to initialize the hardware. This
* is called, before adc_setup() and on error conditions.
*
* Input Parameters:
*
* Returned Value:
*
****************************************************************************/
static void adc_reset(FAR struct adc_dev_s *dev)
{
FAR struct stm32_dev_s *priv = (FAR struct stm32_dev_s *)dev->ad_priv;
irqstate_t flags;
uint32_t regval;
flags = irqsave();
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
/* Initialize the ADC data structures */
/* ADC1 CR Configuration */
regval = adc_getreg(priv, STM32_ADC_CR1_OFFSET);
regval &= ~ADC_CR1_DUALMOD_MASK;
regval &= ~ADC_CR1_SCAN; /* Clear DUALMODE and SCAN bits */
adc_putreg(priv, STM32_ADC_CR1_OFFSET, regval);
/* Initialize the ADC_Mode (ADC_Mode_Independent) */
regval = adc_getreg(priv, STM32_ADC_CR1_OFFSET);
regval |= ADC_CR1_IND;
/* Initialize the ADC_CR1_SCAN member DISABLE */
regval &= ~ADC_CR1_SCAN;
adc_putreg(priv, STM32_ADC_CR1_OFFSET, regval);
/* ADC1 CR2 Configuration */
/* Clear CONT, ALIGN and EXTTRIG bits */
regval = adc_getreg(priv, STM32_ADC_CR2_OFFSET);
regval &= ~ADC_CR2_CONT;
regval &= ~ADC_CR2_ALIGN;
regval &= ~ADC_CR2_EXTSEL_MASK;
adc_putreg(priv, STM32_ADC_CR2_OFFSET, regval);
/* Set CONT, ALIGN and EXTTRIG bits */
/* Initialize the ALIGN: Data alignment Right */
regval = adc_getreg(priv, STM32_ADC_CR2_OFFSET);
regval &= ~ADC_CR2_ALIGN;
/* Initialize the External event select "Timer CC1 event" */
regval &= ~ADC_CR2_EXTSEL_MASK;
/* Initialize the ADC_ContinuousConvMode "Single conversion mode" */
regval &= ~ADC_CR2_CONT;
adc_putreg(priv, STM32_ADC_CR2_OFFSET, regval);
/* ADC1 SQR1 Configuration */
regval = adc_getreg(priv, STM32_ADC_SQR1_OFFSET);
regval &= ~ADC_SQR1_L_MASK; /* L = 0000: 1 conversion */
adc_putreg(priv, STM32_ADC_SQR_OFFSET1, regval);
irqrestore(flags);
}
/****************************************************************************
* Name: 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.
* This setup includes configuring and attaching ADC interrupts. Interrupts
* are all disabled upon return.
*
* Input Parameters:
*
* Returned Value:
*
****************************************************************************/
static int adc_setup(FAR struct adc_dev_s *dev)
{
FAR struct stm32_dev_s *priv = (FAR struct stm32_dev_s *)dev->ad_priv;
int ret;
priv->buf[i] = 0;
priv->count[i] = 0;
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
/* Enable the ADC interrupt */
up_enable_irq(priv->irq);
}
return ret;
}
/****************************************************************************
* Name: adc_shutdown
*
* Description:
* Disable the ADC. This method is called when the ADC device is closed.
* This method reverses the operation the setup method.
*
* Input Parameters:
*
* Returned Value:
*
****************************************************************************/
static void adc_shutdown(FAR struct adc_dev_s *dev)
{
FAR struct stm32_dev_s *priv = (FAR struct stm32_dev_s *)dev->ad_priv;
/* Disable ADC interrupts and detach the ADC interrupt handler */
up_disable_irq(priv->irq);
irq_detach(priv->irq);
/* Disable and reset the ADC module */
}
/****************************************************************************
* Name: adc_rxint
*
* Description:
* Call to enable or disable RX interrupts.
*
* Input Parameters:
*
* Returned Value:
*
****************************************************************************/
static void adc_rxint(FAR struct adc_dev_s *dev, bool enable)
{
FAR struct stm32_dev_s *priv = (FAR struct stm32_dev_s *)dev->ad_priv;
uint32_t adc_getreg(priv, STM32_ADC_CR1_OFFSET);
/* Enable the end-of-conversion ADC interrupt */
regval |= ADC_CR1_EOCIE;
/* Enable all ADC interrupts */
regval &= ~ADC_CR1_ALLINTS;
adc_putreg(priv, STM32_ADC_CR1_OFFSET, regval);
}
/****************************************************************************
* Name: adc_ioctl
*
* Description:
* All ioctl calls will be routed through this method.
*
* Input Parameters:
*
* Returned Value:
*
****************************************************************************/
static int adc_ioctl(FAR struct adc_dev_s *dev, int cmd, unsigned long arg)
{
return -ENOTTY;
}
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
/****************************************************************************
* Name: adc_interrupt
*
* Description:
* Common ADC interrupt handler.
*
* Input Parameters:
*
* Returned Value:
*
****************************************************************************/
static void adc_interrupt(FAR struct stm32_dev_s *priv)
{
uint32_t regval;
unsigned char ch; /* channel */
int32_t value;
regval = adc_getreg(priv, STM32_ADC_CR1_OFFSET);
regval &= ADC_CR1_AWDCH_MASK;
ch = regval;
/* Handle the ADC interrupt */
# warning "still missing logic, value computation"
adc_receive(priv, ch, value);
priv->buf[ch] = 0;
priv->count[ch] = 0;
return OK;
}
/****************************************************************************
* Name: adc12_interrupt
*
* Description:
* ADC12 interrupt handler for the STM32 F1 family.
*
* Input Parameters:
*
* Returned Value:
*
****************************************************************************/
#if defined(CONFIG_STM32_STM32F10XX) && (defined(CONFIG_STM32_ADC1) || defined(CONFIG_STM32_ADC2))
static int adc12_interrupt(int irq, void *context)
{
uint32_regval;
uint32_t pending;
/* Check for pending ADC1 interrupts */
#ifdef CONFIG_STM32_ADC1
regval = getreg32(priv, STM32_ADC1_SR);
pending = regval & ADC_SR_ALLINTS;
if (pending != 0)
{
adc_interrupt(&g_adcpriv1);
regval &= ~pending;
putreg32(regval, STM32_ADC1_SR);
}
#endif
/* Check for pending ADC2 interrupts */
#ifdef CONFIG_STM32_ADC2
regval = getreg32(priv, STM32_ADC2_SR);
pending = regval & ADC_SR_ALLINTS;
if (pending != 0)
{
adc_interrupt(&g_adcpriv2);
regval &= ~pending;
putreg32(regval, STM32_ADC2_SR);
}
#endif
return OK;
}
#endif
/****************************************************************************
* Name: adc3_interrupt
*
* Description:
* ADC1/2 interrupt handler for the STM32 F1 family.
*
* Input Parameters:
*
* Returned Value:
*
****************************************************************************/
#ifdef CONFIG_STM32_STM32F10XX && defined (CONFIG_STM32_ADC3)
static int adc3_interrupt(int irq, void *context)
{
uint32_t regval;
uint32_t pending;
/* Check for pending ADC3 interrupts */
regval = getreg32(priv, STM32_ADC3_SR);
pending = regval & ADC_SR_ALLINTS;
if (pending != 0)
{
adc_interrupt(&g_adcpriv3);
regval &= ~pending;
putreg32(regval, STM32_ADC3_SR);
}
return OK;
}
#endif
/****************************************************************************
* Name: adc123_interrupt
*
* Description:
* ADC1/2/3 interrupt handler for the STM32 F4 family.
*
* Input Parameters:
*
* Returned Value:
*
****************************************************************************/
#ifdef CONFIG_STM32_STM32F40XX
static int adc123_interrupt(int irq, void *context)
{
uint32_t regval;
uint32_t pending;
/* Check for pending ADC1 interrupts */
#ifdef CONFIG_STM32_ADC1
regval = getreg32(priv, STM32_ADC1_SR);
pending = regval & ADC_SR_ALLINTS;
if (pending != 0)
{
adc_interrupt(&g_adcpriv1);
regval &= ~pending;
putreg32(regval, STM32_ADC1_SR);
}
#endif
/* Check for pending ADC2 interrupts */
#ifdef CONFIG_STM32_ADC2
regval = getreg32(priv, STM32_ADC2_SR);
pending = regval & ADC_SR_ALLINTS;
if (pending != 0)
{
adc_interrupt(&g_adcpriv2);
regval &= ~pending;
putreg32(regval, STM32_ADC2_SR);
}
#endif
/* Check for pending ADC3 interrupts */
#ifdef CONFIG_STM32_ADC3
regval = getreg32(priv, STM32_ADC3_SR);
pending = regval & ADC_SR_ALLINTS;
if (pending != 0)
{
regval &= ~pending;
putreg32(regval, STM32_ADC3_SR);
}
#endif
return OK;
}
#endif
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: up_adcinitialize
*
* Description:
* Initialize the adc
*
* Returned Value:
* Valid can device structure reference on succcess; a NULL on failure
*
****************************************************************************/
struct adc_dev_s *up_adcinitialize(int intf)
{
# warning "Question: How do you plan to handle the ADC channels? Can we do"
# " 16 or 18 individual channels? or one group?"
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
#ifdef CONFIG_STM32_ADC1
if (intf == 1)
{
return &g_adcdev1;
}
else
#endif
#ifdef CONFIG_STM32_ADC2
if (intf == 2)
{
return &g_adcdev2;
}
else
#endif
#ifdef CONFIG_STM32_ADC3
if (intf == 3)
{
return &g_adcdev3;
}
else
#endif
{
return NULL;
}
}
#endif /* CONFIG_STM32_ADC || CONFIG_STM32_ADC2 || CONFIG_STM32_ADC3 */
#endif /* CONFIG_ADC */