Setting up the ADC

The aim of this recipe is to configure the ADC in single-conversion mode and then convert the voltage set by the thumbwheel into a 12-bit digital value. We'll configure the ADC to generate an interrupt at the end of each conversion and write an interrupt handler to read the ADC and initiate a new conversion. The only task for our main function to perform is to output the ADC value to the LEDs, but as there are only 8 LEDs we can only display the most-significant 8-bits of the ADC value. We'll call this recipe adcISR_c5v0.

How to do it…

To set up the ADC follow the steps outlined:

  1. Open a new folder named adcISR_c5v0 and create a new project named adcISR.uvprojx.
  2. Select LED (API) from RTE Board Support but do not select A/D converter (we will write our own code for this). Set the CMSIS and Device software components as for previous projects. Be sure to select resolve so that the correct runtime environment (RTE) is included.
  3. Create an adcISR.c file (the main function) and enter the source code that is shown next. Remember to include the boilerplate code (hidden by the editor folds):
    /*--------------------------------------------------
     * Recipe:  adcISR_c5v0
     * Name:    adcISR.c
     * Purpose: A/D Conversion Demo for MCBSTM32F400
     *          using IRQ
     *--------------------------------------------------
     * Modification History
     * 16.04.14 created
     * 22.12.15 updated uVision5.17 + DFP2.6.0
     *
     * Dr Mark Fisher, UEA, Norwich
     *--------------------------------------------------*/
    
    #include ""stm32f4xx_hal.h""
    #include ""Board_LED.h""
    #include ""Custom_ADC.h""
    
    #define wait_delay HAL_Delay
    
    /* Globals */
    uint32_t adcValue;
    
    #ifdef __RTX
    ______________________________________________________
    /* Function Prototypes */
    void SystemClock_Config(void);
    
    /**
      * System Clock Configuration
      */
    void SystemClock_Config(void) {
    ______________________________________________________
  4. Include code to handle the interrupt generated by the ADC:
    void ADC_IRQHandler (void) {
        
      ADC3->SR &= ~2;       /* Clear EOC interrupt flag */
      adcValue = (ADC3->DR);     /* Get converted value */
      ADC3->CR2 |= (1 << 30);  /* Start next conversion */
                
    }
  5. Include a main () function:
    int main (void) {
      
      HAL_Init ( );
      SystemClock_Config ( );
    
    
      LED_Initialize ();          /* LED Initialization */
      ADC_Initialize_and_Set_IRQ ();/* ADC Special Init */
    
      while (1) {              /* output 8-bit adcValue */
        LED_SetOut (adcValue >> 4);   /* to LEDs        */
        wait_delay ( 100 );                     /* wait */
      }
    }
  6. Create a Custom_ADC.c file and enter code to set up the ADC:
    #include ""stm32f4xx_hal.h"" /* STM32F4xx Definitions */
    #include ""Custom_ADC.h""
    
    /*--------------------------------------------------
     * ADC_Initialize_and_Set_IRQ: Initialize Analog to 
     *            Digital Converter and Enable IRQ
     *--------------------------------------------------*/
    void ADC_Initialize_and_Set_IRQ (void) {
       /* Setup potentiometer pin PF9 (ADC3_7) and ADC3 */
    
      RCC->APB2ENR |= (1UL <<  10);     /* En. ADC3 clk */
      RCC->AHB1ENR |= (1UL <<   5);    /* En. GPIOF clk */
      GPIOF->MODER |= (3UL << 2*9);/* PF9 is Analog mde */
    
      ADC3->SQR1   =   0;
      ADC3->SQR2   =   0;
      ADC3->SQR3   =  (7UL <<  0);   /* SQ1 = channel 7 */
      ADC3->SMPR1  =   0;            /* Channel 7 smple */
      ADC3->SMPR2  =   (7UL <<  18); /* time = 480 cyc. */
      ADC3->CR1    =  (1UL <<  8);      /* Scan mode on */
      ADC3->CR2   &= ~2;           /* single conv. mode */
        
      ADC3->CR1   |=  ( 1UL <<  5);      /* En. EOC IRQ */
      ADC3->CR2   |=  ( 1UL <<  0);       /* ADC enable */    
      NVIC_EnableIRQ( ADC_IRQn );            /* En. IRQ */    
      ADC3->CR2 |= (1 << 30);   /* Start 1st conversion */
    }
  7. Add the adcISR.c and Custom_ADC.c files to the project.
  8. Declare a function prototype for ADC_Initialize_and_Set_IRQ () in the Custom_ADC.h file.
  9. Build, download, and run the program.

How it works…

The STM32F407xx features 3 × 12-bit successive approximation ADCs, each sharing up to 16 external channels and performing conversions in single-shot or scan mode. A simplified schematic showing the architecture of each converter is presented next (please note that a more detailed diagram is included in STM's RM0090 Reference manual at http://www.st.com).

How it works…

Simplified STM32F4xxxx microcontroller ADC schematic

The 16 multiplexed input channels are organized in two groups comprising regular and injected channels. A subset of GPIO port pins can be connected to the ADC multiplexer by configuring the pin as a high-impedance analog input. The pin/input channel mapping is device-dependent. Details for the STM32F407IG device used by the ARM MCBSTM32F400 evaluation board can be found in the STM32F405xx and STM32F407xx Datasheet (http://www.st.com), and a simplified form is given in the following table. The ADC can be configured to carry out a sequence of up to 16 conversions on each group, each triggered separately by either an external-or-timed start signal.

ADC1 Input Channel

GPIO Port

ADC2 Input Channel

GPIO Port

ADC3 Input Channel

GPIO Port

IN_0

PA0

IN_0

PA0

IN_0

PA0

IN_1

PA1

IN_1

PA1

IN_1

PA1

IN_2

PA2

IN_2

PA2

IN_2

PA2

IN_3

PA3

IN_3

PA3

IN_3

PA3

IN_4

PA4

IN_4

PA4

IN_4

PF6

IN_5

PA5

IN_5

PA5

IN_5

PF7

IN_6

PA6

IN_6

PA6

IN_6

PF8

IN_7

PA7

IN_7

PA7

IN_7

PF9

IN_8

PB0

IN_8

PB0

IN_8

PF10

IN_9

PB1

IN_9

PB1

IN_9

PF3

IN_10

PC0

IN_10

PC0

IN_10

PC0

IN_11

PC1

IN_11

PC1

IN_11

PC1

IN_12

PC2

IN_12

PC2

IN_12

PC2

IN_13

PC3

IN_13

PC3

IN_13

PC3

IN_14

PC4

IN_14

PC4

IN_14

PF4

IN_15

PC5

IN_15

PC5

IN_15

PF5

The GPIO ports used by the ADC must be configured as analog inputs by writing appropriate values to MODERy[1:0] bits of the Mode Register that is shown as follows:

31

30

29

28

27

26

25

24

MODER15[1:0]

MODER14[1:0]

MODER13[1:0]

MODER12[1:0]

rw

rw

rw

rw

rw

rw

rw

rw

23

22

21

20

19

18

17

16

MODER11[1:0]

MODER10[1:0]

MODER09[1:0]

MODER08[1:0]

rw

rw

rw

rw

rw

rw

rw

rw

15

14

13

12

11

10

09

08

MODER07[1:0]

MODER06[1:0]

MODER05[1:0]

MODER04[1:0]

rw

rw

rw

rw

rw

rw

rw

rw

07

06

05

04

03

02

01

00

MODER03[1:0]

MODE02[1:0]

MODER01[1:0]

MODER00[1:0]

rw

rw

rw

rw

rw

rw

rw

rw

The Mode Register bits are defined as follows:

MODERy[1:0]

I/O Mode

00 :

Input

01 :

General Purpose output

10 :

Alternate Function

11 :

Analog Input

The ADC is configured by an initialization function named ADC_Initialize_and_Set_IRQ() that has been written specially for this recipe. The following description should be read with reference to STM''s RM0090 Reference manual (http://www.st.com).

The thumbwheel labelled ADC1 on the evaluation board provides a variable voltage input connected to GPIO port F pin 9 (ADC3 channel 7). To sample this voltage, we first configure GPIOF pin 9 as an analog input by writing to the port mode register (GPIOF_MODER). Statements in ADC_Initialize_and_Set_IRQ() are explained as follows:

  1. The bit map for the port mode register shown in the MODER register bit table indicates that we must write logic-1 to bit 18 and 19. ARM writes the code like this to emphasize that we're configuring port F bit-9 (PF9):
    GPIOF->MODER |= (3UL << 2*9);
  2. We also need to select the clock for ADC3 and GPIOF:
    RCC->APB2ENR |= (1UL <<  10);
    RCC->AHB1ENR |= (1UL <<   5);
  3. Our aim is to set up a single conversion in the regular sequence. The first conversion is identified by the bits 4:0 of ADC regular sequence register 3 (ADC_SQR3). As PF9 maps to ADC3 channel 7, we write 7 to this register and 0 to ADC_SQR1 and ADC_SQR2:
    ADC3->SQR1   =   0;
    ADC3->SQR2   =   0;
    ADC3->SQR3   =  (7UL <<  0);
  4. The Sample/Hold time can be set (for each channel) by writing to the two ADC Sample Time Registers (SMPR1 and SMPR2). In this case, as the input voltage is derived from a potentiometer, the sample frequency can be quite low, and so, a long Sample/Hold time of 480 cycles can be set:
    ADC3->SMPR1  =   0; 
    ADC3->SMPR2  =   (7UL <<  18); 
  5. We carry out a single conversion on each group of channels identified by the regular sequence register, so we enable scan mode by writing to bit-8 of Control Register 1:
    ADC3->CR1  =  (1UL <<  8);
  6. To set up single conversion mode, enable an end of conversion interrupt (EOCIE), and enable the ADC (ADON), we write the following code:
    ADC3->CR2   &=  ~2;
    ADC3->CR1   |=  ( 1UL <<  5);
    ADC3->CR2   |=  ( 1UL <<  0);
  7. Finally, we must configure the Nested Vectored Interrupt Controller (NVIC) to respond to interrupts from the ADC and initiate the first conversion by writing SWSTART (bit-30), as follows:
    NVIC_EnableIRQ( ADC_IRQn );
    ADC3->CR2 |= (1 << 30);

The ADC_IRQHandler () interrupt handler needs to clear the interrupt, read the ADC data, and start another conversion cycle. The super-loop in the main function calls the LED_SetOut () function to display the most significant 8-bits of the ADC output on the LEDs.

There's more…

In continuous conversion mode, the ADC starts a new conversion as soon as the previous one has been completed. In practice, the new conversion starts after a delay of 15 cycles to allow the ADC to stabilize. Only the regular group of channels can be converted in continuous mode, as follows:

  1. We can enable continuous mode by changing the last line of the function, ADC_Initialize (), to the following:
    ADC3->CR2   |=  2;
  2. As our interrupt handler no longer needs to trigger a new conversion, we only need the following two statements:
    ADC3->SR &= ~2;
    adcValue = (ADC3->DR);
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.223.196.211