This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

How can I control a timer capture control register with an ADC value?



I am using timer in up-down mode to generate a PWM with dead time. I would like to control the timer period and also the pulse width with a pot which is read by the ADC. So far I got the PWM working, and the ADC is reading the POT (I also have a little TIMER1_A0 ISR to blink a LED to assure me the system is still running). I have not yet been able to link the ADC with the timer CCR register. I would appreciate any suggestions. I am trying to exclusively use the driverlib utilities for the purpose of code maintainability. Here is the code, which is split up into three files. The first one is main.c:

#include "driverlib.h"
#include "shaker.h"

#define COMPARE_VALUE 50000
volatile uint16_t frequency_input;
volatile uint16_t acceleration_input;
void setup_continual_ADC(void);
void main(void)
{
	uint16_t toggle_set = 20000;
	uint16_t toggle_reset = 4000;
	uint16_t timer_period = 24000;

	//Stop WDT
    WDT_A_hold(WDT_A_BASE);

    //Set P1.0 to output direction
    GPIO_setAsOutputPin(GPIO_PORT_P1,GPIO_PIN0);

    //P1.2 and P1.3 output
    //P1.2 and P1.3  options select
    GPIO_setAsPeripheralModuleFunctionOutputPin(
        GPIO_PORT_P1,GPIO_PIN2 + GPIO_PIN3);	//TA0.1, TA0.2

    //Start Timer A0
    Timer_A_initUpDownModeParam initUpDownParam = {0};
    initUpDownParam.clockSource = TIMER_A_CLOCKSOURCE_SMCLK;
    initUpDownParam.clockSourceDivider = TIMER_A_CLOCKSOURCE_DIVIDER_1;
    initUpDownParam.timerPeriod = timer_period;
    initUpDownParam.timerInterruptEnable_TAIE = TIMER_A_TAIE_INTERRUPT_DISABLE;
    initUpDownParam.captureCompareInterruptEnable_CCR0_CCIE =
        TIMER_A_CCIE_CCR0_INTERRUPT_DISABLE;
    initUpDownParam.timerClear = TIMER_A_DO_CLEAR;
    initUpDownParam.startTimer = false;
    Timer_A_initUpDownMode(TIMER_A0_BASE, &initUpDownParam);

    Timer_A_startCounter(TIMER_A0_BASE,TIMER_A_UPDOWN_MODE);

    //Initialze compare registers of timer A0 to generate PWM1
    Timer_A_initCompareModeParam initComp1Param = {0};
    initComp1Param.compareRegister = TIMER_A_CAPTURECOMPARE_REGISTER_1;
    initComp1Param.compareInterruptEnable =
        TIMER_A_CAPTURECOMPARE_INTERRUPT_DISABLE;
    initComp1Param.compareOutputMode = TIMER_A_OUTPUTMODE_TOGGLE_SET;
    initComp1Param.compareValue = toggle_set;
    Timer_A_initCompareMode(TIMER_A0_BASE, &initComp1Param);

    //Initialze compare registers of timer A0 to generate PWM2
    Timer_A_initCompareModeParam initComp2Param = {0};
    initComp2Param.compareRegister = TIMER_A_CAPTURECOMPARE_REGISTER_2;
    initComp2Param.compareInterruptEnable =
        TIMER_A_CAPTURECOMPARE_INTERRUPT_DISABLE;
    initComp2Param.compareOutputMode = TIMER_A_OUTPUTMODE_TOGGLE_RESET;
    initComp2Param.compareValue = toggle_reset;
    Timer_A_initCompareMode(TIMER_A0_BASE, &initComp2Param);


    //Start timerA1 in continuous mode sourced by SMCLK
    Timer_A_initContinuousModeParam initContParam = {0};
    initContParam.clockSource = TIMER_A_CLOCKSOURCE_SMCLK;
    initContParam.clockSourceDivider = TIMER_A_CLOCKSOURCE_DIVIDER_1;
    initContParam.timerInterruptEnable_TAIE = TIMER_A_TAIE_INTERRUPT_DISABLE;
    initContParam.timerClear = TIMER_A_DO_CLEAR;
    initContParam.startTimer = false;
    Timer_A_initContinuousMode(TIMER_A1_BASE, &initContParam);

    //Initiaze compare mode
    Timer_A_clearCaptureCompareInterrupt(TIMER_A1_BASE,
                                         TIMER_A_CAPTURECOMPARE_REGISTER_0);

    Timer_A_initCompareModeParam initCompParam = {0};
    initCompParam.compareRegister = TIMER_A_CAPTURECOMPARE_REGISTER_0;
    initCompParam.compareInterruptEnable =
        TIMER_A_CAPTURECOMPARE_INTERRUPT_ENABLE;
    initCompParam.compareOutputMode = TIMER_A_OUTPUTMODE_OUTBITVALUE;
    initCompParam.compareValue = COMPARE_VALUE;
    Timer_A_initCompareMode(TIMER_A1_BASE, &initCompParam);

    Timer_A_startCounter(TIMER_A1_BASE,TIMER_A_CONTINUOUS_MODE);

    ADC12_A_disableConversions(ADC12_A_BASE, ADC12_A_COMPLETECONVERSION);
    setup_continual_ADC();

    while(1)
    {
        ADC12_A_startConversion(ADC12_A_BASE,
                                ADC12_A_MEMORY_0,
                                ADC12_A_SEQOFCHANNELS);

    	timer_period = (frequency_input + 100) * 4;
    	toggle_set = (timer_period - acceleration_input * 4);
    	toggle_reset = acceleration_input * 4;
    	if (toggle_reset > (timer_period/2 - 100)){
    			toggle_reset = timer_period/2 - 100;
    			toggle_set = toggle_reset;
    	}
//        __bis_SR_register(LPM4_bits + GIE);
        __bis_SR_register(GIE);
        //For debugger
        __no_operation();
    }
}

//******************************************************************************
//
//This is the TIMER1_A0 interrupt vector service routine.
//
//******************************************************************************
#if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
#pragma vector=TIMER1_A0_VECTOR
__interrupt
#elif defined(__GNUC__)
__attribute__((interrupt(TIMER1_A0_VECTOR)))
#endif
void TIMER1_A0_ISR(void)
{

    uint16_t compVal = Timer_A_getCaptureCompareCount(TIMER_A1_BASE,
                                                      TIMER_A_CAPTURECOMPARE_REGISTER_0)
                       + COMPARE_VALUE;

    //Toggle P1.0
    GPIO_toggleOutputOnPin(GPIO_PORT_P1,GPIO_PIN0);
}

The second file is adc12_a_ex7_sequence_otto.c:

/*
* adc12_a_ex7_sequence_otto.c
*
* Created on: Apr 21, 2015
* Author: Otto, based on example 7
*/
#include "driverlib.h"
#include "shaker.h"
//Needs to be global in this example
//Otherwise, the compiler removes it
//because it is not used for anything.
volatile uint16_t frequency_input;
volatile uint16_t acceleration_input;
volatile uint16_t velocity_max_output;
volatile uint16_t velocity_relative_to_CIM;

void setup_continual_ADC(void)
{
//Stop Watchdog Timer
WDT_A_hold(WDT_A_BASE);

//Enable A/D channel inputs
GPIO_setAsPeripheralModuleFunctionInputPin(GPIO_PORT_P6,
GPIO_PIN0 + GPIO_PIN1 + GPIO_PIN2 +
GPIO_PIN3 + GPIO_PIN4 + GPIO_PIN5 +
GPIO_PIN6 + GPIO_PIN7
);

//Initialize the ADC12_A Module
/*
* Base address of ADC12_A Module
* Use internal ADC12_A bit as sample/hold signal to start conversion
* USE MODOSC 5MHZ Digital Oscillator as clock source
* Use default clock divider of 1
*/
ADC12_A_init(ADC12_A_BASE,
ADC12_A_SAMPLEHOLDSOURCE_SC,
ADC12_A_CLOCKSOURCE_ADC12OSC,
ADC12_A_CLOCKDIVIDER_1);

ADC12_A_enable(ADC12_A_BASE);

/*
* Base address of ADC12_A Module
* For memory buffers 0-7 sample/hold for 16 clock cycles
* For memory buffers 8-15 sample/hold for 4 clock cycles (default)
* Enable Multiple Sampling
*/
ADC12_A_setupSamplingTimer(ADC12_A_BASE,
ADC12_A_CYCLEHOLD_16_CYCLES,
ADC12_A_CYCLEHOLD_4_CYCLES,
ADC12_A_MULTIPLESAMPLESENABLE);

//Configure Memory Buffers
/*
* Base address of the ADC12_A Module
* Configure memory buffer 0
* Map input A0 to memory buffer 0
* Vref+ = AVcc
* Vref- = AVss
* Memory buffer 0 is not the end of a sequence
*/
ADC12_A_configureMemoryParam param0 = {0};
param0.memoryBufferControlIndex = ADC12_A_MEMORY_0;
param0.inputSourceSelect = ADC12_A_INPUT_A0;
param0.positiveRefVoltageSourceSelect = ADC12_A_VREFPOS_AVCC;
param0.negativeRefVoltageSourceSelect = ADC12_A_VREFNEG_AVSS;
param0.endOfSequence = ADC12_A_NOTENDOFSEQUENCE;
ADC12_A_configureMemory(ADC12_A_BASE,&param0);

/*
* Base address of the ADC12_A Module
* Configure memory buffer 1
* Map input A1 to memory buffer 1
* Vref+ = AVcc
* Vref- = AVss
* Memory buffer 1 is not the end of a sequence
*/
ADC12_A_configureMemoryParam param1 = {0};
param1.memoryBufferControlIndex = ADC12_A_MEMORY_1;
param1.inputSourceSelect = ADC12_A_INPUT_A1;
param1.positiveRefVoltageSourceSelect = ADC12_A_VREFPOS_AVCC;
param1.negativeRefVoltageSourceSelect = ADC12_A_VREFNEG_AVSS;
param1.endOfSequence = ADC12_A_NOTENDOFSEQUENCE;
ADC12_A_configureMemory(ADC12_A_BASE,&param1);

/*
* Base address of the ADC12_A Module
* Configure memory buffer 2
* Map input A2 to memory buffer 2
* Vref+ = AVcc
* Vref- = AVss
* Memory buffer 2 is not the end of a sequence
*/
ADC12_A_configureMemoryParam param2 = {0};
param2.memoryBufferControlIndex = ADC12_A_MEMORY_2;
param2.inputSourceSelect = ADC12_A_INPUT_A2;
param2.positiveRefVoltageSourceSelect = ADC12_A_VREFPOS_AVCC;
param2.negativeRefVoltageSourceSelect = ADC12_A_VREFNEG_AVSS;
param2.endOfSequence = ADC12_A_NOTENDOFSEQUENCE;
ADC12_A_configureMemory(ADC12_A_BASE,&param2);

/*
* Base address of the ADC12_A Module
* Configure memory buffer 3
* Map input A3 to memory buffer 3
* Vr+ = AVcc
* Vr- = AVss
* Memory buffer 3 IS the end of a sequence
*/
ADC12_A_configureMemoryParam param3 = {0};
param3.memoryBufferControlIndex = ADC12_A_MEMORY_3;
param3.inputSourceSelect = ADC12_A_INPUT_A3;
param3.positiveRefVoltageSourceSelect = ADC12_A_VREFPOS_AVCC;
param3.negativeRefVoltageSourceSelect = ADC12_A_VREFNEG_AVSS;
param3.endOfSequence = ADC12_A_ENDOFSEQUENCE;
ADC12_A_configureMemory(ADC12_A_BASE,&param3);

//Enable memory buffer 3 interrupt
ADC12_A_clearInterrupt(ADC12_A_BASE,
ADC12IE3);
ADC12_A_enableInterrupt(ADC12_A_BASE,
ADC12IE3);

}

#if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
#pragma vector=ADC12_VECTOR
__interrupt
#elif defined(__GNUC__)
__attribute__((interrupt(ADC12_VECTOR)))
#endif
void ADC12ISR(void)
{
switch(__even_in_range(ADC12IV,34))
{
case 0: break; //Vector 0: No interrupt
case 2: break; //Vector 2: ADC overflow
case 4: break; //Vector 4: ADC timing overflow
case 6: break; //Vector 6: ADC12IFG0
case 8: break; //Vector 8: ADC12IFG1
case 10: break; //Vector 10: ADC12IFG2
case 12: //Vector 12: ADC12IFG3
//Move results, IFG is cleared
velocity_max_output =
ADC12_A_getResults(ADC12_A_BASE,
ADC12_A_MEMORY_0);
//Move results, IFG is cleared
velocity_relative_to_CIM =
ADC12_A_getResults(ADC12_A_BASE,
ADC12_A_MEMORY_1);
//Move results, IFG is cleared
frequency_input =
ADC12_A_getResults(ADC12_A_BASE,
ADC12_A_MEMORY_2);
//Move results, IFG is cleared
acceleration_input =
ADC12_A_getResults(ADC12_A_BASE,
ADC12_A_MEMORY_3);

//Exit active CPU, SET BREAKPOINT HERE
// __bic_SR_register_on_exit(LPM4_bits);
case 14: break; //Vector 14: ADC12IFG4
case 16: break; //Vector 16: ADC12IFG5
case 18: break; //Vector 18: ADC12IFG6
case 20: break; //Vector 20: ADC12IFG7
case 22: break; //Vector 22: ADC12IFG8
case 24: break; //Vector 24: ADC12IFG9
case 26: break; //Vector 26: ADC12IFG10
case 28: break; //Vector 28: ADC12IFG11
case 30: break; //Vector 30: ADC12IFG12
case 32: break; //Vector 32: ADC12IFG13
case 34: break; //Vector 34: ADC12IFG14
default: break;
}
}

 And finally there is the third file which is shaker.h:

extern volatile uint16_t frequency_input;
extern volatile uint16_t acceleration_input;
extern uint16_t toggle_set;
extern uint16_t toggle_reset;
extern uint16_t timer_period;

  • Otto,

    without having a look at your code now...if you can read the ADC and get the value from it - then you can of course use these values as frequency and duty cycle for your PWM. Use the timer in OUTMOD_7 (reset/set) for a hardware PWM and use CCR0 for the frequency and CCR1/2/x for the duty cycle. If you get a new value from the ADC, then simply assign this to the frequency CCR0 = ADC_VALUE or to the duty cycle CCR1 = ADC_VALUE. Of course your resolution is limited to the ADC's resolution of 12 bit. And CCR1 should never be greater than CCR0.

    Dennis

  • Dennis, What I am trying to accomplish is mimicking, for instance, a benchtop signal generator, where one can turn a knob and change the frequency or the duty cycle. My attempt to write the ADC value into the relevant variable in the while(1) block did not work. That is why I posted this question.
  • I realized what I was overlooking. First of all, driverlib is great for self-documenting code, but to do anything fancy, the roads aren't paved, and driverlib won't let you go there. Setting the registers directly in the while(1) block did the trick. I still have some fine-tuning to do, but at least I got my ADC hooked into the timer now. Here is the new while(1) block:

        while(1)
        {
            ADC12_A_startConversion(ADC12_A_BASE,
                                    ADC12_A_MEMORY_0,
                                    ADC12_A_SEQOFCHANNELS);
    
            TA0CCR0 = (frequency_input + 100) * 4;
        	toggle_set = (timer_period - acceleration_input * 4);
        	toggle_reset = acceleration_input * 4;
        	if (toggle_reset > (timer_period/2 - 100)){
        			toggle_reset = timer_period/2 - 100;
        			toggle_set = toggle_reset;
        	}
        	TA0CCR1 = toggle_set;
        	TA0CCR2 = toggle_reset;
    
    //        __bis_SR_register(LPM4_bits + GIE);
            __bis_SR_register(GIE);
            //For debugger
            __no_operation();
        }

**Attention** This is a public forum