Using timers to trigger conversions

As sampling frequency plays such a critical role in determining the quality of the digital representation of the analog signal input, and to avoid aliasing artifacts, it is preferable to use a timer to trigger the conversion rather than to enable continuous conversions as we did in the previous recipe. This recipe, adcTimerISR_c5v0, illustrates this technique. The aim of this recipe is to configure TIM2 _CH2 in output compare mode so that it toggles every 100 ms and then use this timing signal to trigger the ADC.

How to do it…

  1. First create a new project called adcTimerISR.uvprojx and use the RTE manager to configure it as we did for the folder adcISR_c5v0 for the Setting up the ADC recipe.
  2. Copy timer.c and Custom_ADC.c from the previous recipes and add these to the project. Copy adcISR.c and rename it adcTimerISR.c. Add this to the project.
  3. Add #include timer.h to adcTimerISR.c and call TIM2_Initialize() in main(). Check whether the project successfully builds.
  4. Modify the TIM2_Initialize() function so that it no longer produces an update interrupt flag (UIF) by deleting the following statements:
        TIM2->DIER = (1UL << 0);
        NVIC_EnableIRQ(TIM2_IRQn);
  5. Configure TIM2_CH2 to toggle channel 2 capture/compare output by writing to the appropriate fields of Capture/Compare Mode Register 1 (CCMR1) and Capture/Compare Enable Register (CCER):
        TIM2->CCMR1 |= ( 3UL << 12 );
        TIM2->CCER |= ( 1UL << 4 );

    Tip

    There is no need to write to the Capture/Compare Register. If we leave it set to zero (that is, Reset), then the Capture/Compare output will toggle each time TIM2_CNT is zero (that is, every 100 ms):

    /***************************************************
     * TIM2_Initialize ( )
     ***************************************************
     * Initializes TIM2
     * Capture Compare 2 Interrupt Flag (CC2IF)  
     * generates interrupts every 100ms (0.1s)
     * SystemCoreClock = 168 MHz - set by SystemInit ( )
     * Refer to Figure 134 of STM Reference Manual
     * TIMxCLK = SystemCoreClock/2
     * Hence ticks = 0.1 * 168,000,000 / 2 = 8,400,000
     * Prescaler = 8400-1; ARR = 1000-1;
     * Note: Capture Compare Register is left in Reset
     ***************************************************/
    void TIM2_Initialize (void) {
      const uint16_t PSC_val = 8400;
      const uint16_t ARR_val = 1000;
        
      /* En. clk for TIM2 */
      RCC->APB1ENR |= RCC_APB1ENR_TIM2EN; 
    
      TIM2->PSC = PSC_val - 1;        /* set prescaler */
      TIM2->ARR = ARR_val - 1;      /* set auto-reload */
      TIM2->CR1 = ( 1UL << 0 );          /* set Ctr. En. (CEN) */
    TIM2->CCMR1 |= ( 3UL << 12 );   /* OC1REF toggles 
                                                       when TIMx_CNT=TIMx_CCR1*/
      TIM2->CCER |= ( 1UL << 4 );      /* CC2E set */
    }
  6. Modify the adc_Initialize_and_Set_IRQ ( ) function to trigger conversions on both the rising and falling edge of TIM2_CH2 by writing to Control Register 2:
        ADC3->CR2   |=  ( 3UL << 28);
        ADC3->CR2   |=  ( 3UL << 24); 
  7. Remember to run the ADC in single conversion mode:
    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 = Analog mode */
    
      ADC3->SQR1   =   0;
      ADC3->SQR2   =   0;
      ADC3->SQR3   =  (7UL <<  0);     /* SQ1 = chan. 7 */
      ADC3->SMPR1  =   0;             /* Chan. 7 sample */
      ADC3->SMPR2  =   (7UL <<  18); /* time = 480 cyc. */
      ADC3->CR1    =  (1UL <<  8);      /* Scan mode on */
        
      ADC3->CR1   |=  ( 1UL <<  5);      /* En. EOC IRQ */
      ADC3->CR2   |=  ( 3UL << 28); /* Trig on both edg */
      ADC3->CR2   |=  ( 3UL << 24);      /* of TIM2_CC2 */
      ADC3->CR2   |=  ( 1UL <<  0);       /* ADC enable */        
      NVIC_EnableIRQ( ADC_IRQn );         /* Enable IRQ */    
      ADC3->CR2 |= (1 << 30);   /* Start 1st conversion */
    }
  8. Build, download, and run the program. You will notice that, when we execute this program, the output appears much more stable than it did using a continuous mode. This is just a consequence of performing fewer conversions, but it does serve to emphasize the need to avoid oversampling unless there is good reason.

How it works…

In addition to the update event interrupt, each timer also allows interrupts to be generated by up to four capture compare channels (TIMx_CH1-TIMx_CH4). Each Capture/Compare channel comprises a Capture/Compare register, an input stage for capture (with digital filter, multiplexing, and prescaler), and an output stage (with comparator and output control). Each can be configured as the input capture, PWM input, forced output, output compare, PWM, or one-pulse modes. The output compare mode can be used to provide timing signals that can be used to start A-D conversions.

One of 16 possible start conversion triggers can be selected for the regular group of channels by writing to the ADC control register 2 (ADC_CR2) bit field, EXTSEL[3:0]. The following table shows how the trigger sources are encoded

Note

CH1-CH4 and TRGO refer to timer channels. For further information, refer to STM's RM0090 Reference manual (http://www.st.com), Chapters 17 and 18.

EXTSEL[3:0]

Start Trigger

EXTSEL[3:0]

Start Trigger

0000

TIM1_CH1

1000

TIM3_TRGO

0001

TIM1_CH2

1001

TIM4_CH4

0010

TIM1_CH3

1010

TIM5_CH1

0011

TIM2_CH2

1011

TIM5_CH2

0100

TIM2_CH3

1100

TIM5_CH3

0101

TIM2_CH4

1101

TIM8_CH1

0110

TIM2_TRGO

1110

TIM8_TRGO

0111

TIM3_CH1

1111

EXT11

The polarity of the trigger is determined by EXTEN, as shown in the following table:

EXTEN

Trigger Polarity

00

Trigger detection disabled

01

Trigger detection on the rising edge

10

Trigger detection on the falling edge

11

Trigger detection on both the rising and falling edges

There's more…

If we wish to confirm that the ADC is sampled every 100 ms, then simply add a global tick variable and increment this in the IRQ handler. Change the code within the super-loop to blink the LEDs every 10 ticks.

..................Content has been hidden....................

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