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.
This is the first time I have programmed a microcontroller. My current program uses SMCLK and Timer_A interrupt. The ADC example below is based on a program I found on this forum, and it was originally written to use ACLK. I wish to execute using SMCLK so that I can incorporate ADC into my existing program.
I set ADC10SSEL_3 in the ADC10CTL1 register, and set TASSEL_2 in the TACTL register. But the ADC10 ISR does not execute. What's missing?
Thanks
#include <msp430.h> int main(void) { WDTCTL = WDTPW + WDTHOLD; // Stop WDT ADC10CTL1 = INCH_3 + ADC10SSEL_3 + SHS_1; // P1.3, SMCLK, TA1 trigger sample start ADC10AE0 = 0x08; // P1.3 ADC10 option select P2DIR |= 0x08; // Set P1.0 to output direction TACCTL0 = CCIE; // Enable interrupt TACCR0 = 32-1; // PWM Period TACCTL1 = OUTMOD_3; // TACCR1 set/reset TACCR1 = 2; // TACCR1 PWM Duty Cycle TACTL = TASSEL_2 + MC_1; // SMCLK, up mode __bis_SR_register(GIE); // enable interrupts } // ADC10 interrupt service routine #pragma vector=ADC10_VECTOR __interrupt void ADC10_ISR(void) { ADC10CTL0 &= ~ENC; // ADC10 disabled ADC10CTL0 = 0; // ADC10, Vref disabled completely if (ADC10MEM > 600) P2OUT &= ~0x08; else P2OUT |= 0x08; } // Timer A0 interrupt service routine #pragma vector=TIMER0_A0_VECTOR __interrupt void Timer_A(void) { ADC10CTL0 = SREF_1 + ADC10SHT_2 + REFON + ADC10ON + ADC10IE; ADC10CTL0 |= (ENC); // ADC10 enable set }
Russell,
from the first sight I notice that your program simply ends after enabling the interrupts in your main. You should add a
... __bis_SR_register( GIE ); // enable interrupts while( 1 ) { } }
to it so the program can run in an infinite loop, interrupted by your interrupts.
Dennis
He does it in the Timer ISR:
// Timer A0 interrupt service routine #pragma vector=TIMER0_A0_VECTOR __interrupt void Timer_A(void) { ADC10CTL0 = SREF_1 + ADC10SHT_2 + REFON + ADC10ON + ADC10IE; ADC10CTL0 |= (ENC); // ADC10 enable set }
Anyway - I don't really get what you want to do. You want the timer to trigger an ADC sample, but you use the timer's CCR0 interrupt to configure the ADC. So maybe you could explain what you intend to do.
Dennis
I want to trigger an ADC sample from the timer interrupt with SMCLK. The original program used ACLK, i.e., (TACTL = TASSEL_1 + MC_1). I changed the input channel and threshold level and was able to verify correct operation, but I am unable to change the clock source to SMCLK and get the ADC sampling to execute. Ultimately, I want to use this knowledge to incorporate an ADC into my existing program that uses SMCLK. Perhaps there's a better way to do this?
You are using SMCLK for the timer and the ADC. Maybe it is because you have such small timer values:
TACCR0 = 32-1; // PWM Period TACCR1 = 2; // TACCR1 PWM Duty Cycle
You are running the timer, the ADC and the CPU with the same clock and stepping into and out of the ISR is (if I remember correctly) 11 MCLK cycles. So there is not much left until the next interrupt occurs. The ADC needs 13 clock cycles. This could be a reason - the ACLK normally runs at much slower frequencies. You now have about 1MHz. Try using some dividers for the clock to see if it then works.
Dennis
I found a way to read the ADC value in the Timer A interrupt. The pertinent code snippets are below.
#define P1_3_VOLTAGE_LVL BIT3 #define P2_3_LED BIT3 void main(void) { // Enable peripheral module on P1.3 P1SEL |= P1_3_VOLTAGE_LVL; // Configure the ADC to use Vref/Vss, sample & hold 8 x ADC10CLKs, // use internal 2.5V Vref, and enable ADC ADC10CTL0 = SREF_1 | ADC10SHT_2 | REF2_5V | REFON | ADC10ON; // Configure the ADC to use Channel 3 (P1.3), Sample & Hold Source ADC, SMCLK ADC10CTL1 = INCH_3 | SHS_0 | ADC10SSEL_3;
// Set output direction on LED P2DIR |= P2_3_LED; // Timer A Control = Timer A Source Select 2 (SMCLK) and Mode Control 1 (up to CCR0) TACTL = TASSEL_2 | MC_1; //use CPU clk as source, don't divide, Count to TACCR0 repeatedly // Timer A Capture/Compare 0 = 20ms TACCR0 = 20000; // Timer A Capture/Compare Interrupt Enable TACCTL0 = CCIE; // Set Timer A Counter Interrupt Enable TACTL |= TAIE; // Globally enable interrupts _BIS_SR(GIE);
// Only need System Clock Generator 1 _bis_SR_register(SCG0+OSCOFF+CPUOFF); } #pragma vector=TIMER0_A0_VECTOR __interrupt void Timer_A (void) { // Turn the ADC on, enable converter, start conversion, and wait for it to complete ADC10CTL0 |= (ADC10ON | ENC | ADC10SC); while(ADC10CTL1 & ADC10BUSY); // Disable the ADC ADC10CTL0 &= (~ADC10ON & ~ENC & ~ADC10SC); // Read the sampled value voltage = ADC10MEM;
if(voltage > 700)
P2OUT &= ~P2_3_LED;
else
P2OUT |= P2_3_LED;
// Clear the interrupt vector TA0IV = TA0IV_NONE; }
Russel,
good to hear that it works now, but the way you are doing it isn't a good one in general. At least if your program will grow and therefore has to do more tasks. Never use a while()-loop inside an interrupt - try to avoid it in all cases...during the initialization before the main program starts this might be OK. So I would recommend to configure the ADC at startup and then only set the ADC10SC bit in the timer-interrupt. No need to set and reset ENC since you are not changing the configuration of the ADC. And no need to disable the ADC everytime, unless you want to save as much power as possible. But doing so isn't a problem, of course. Then use the ADC's interrupt and wait for it to fire and fetch the ADC result in it. The small portion of code to set/reset the output pin can be done in the interrupt, too.
And here is a critical thing in your code:
// Set Timer A Counter Interrupt Enable TACTL |= TAIE;
The TAIE interrupt is located in
#pragma vector = TIMER0_A1_VECTOR
so you do not have an interrupt vector for it. This can cause your program to crash.
What I'm also wondering about is that you do not set any ADC10AE bit for your input - you should not get any correct value since P1.3 isn't configured to be an analog input. Furthermore this statement
// Enable peripheral module on P1.3 P1SEL |= P1_3_VOLTAGE_LVL;
is no valid configuration of P1.3 - when you set P1SEL in combination with P1DIR then you can select the ADC10CLK output function. Or setting P1SEL in combination with P1SEL2 and P1DIR will select the CAOUT function. But only setting P1SEL is nothing:
For using P1.3 as an analog input, the PSEL bits are don't care.
Dennis
Dennis, these are good suggestions; thanks for the general review. Particularly I was not aware of TAIE located in the A1 vector (oops!) But I cannot explain why not setting ADC10AE did not cause me any grief. I tested the sampled ADC value using 2.5V reference and a 1.8V input (1.8V = 736 ADC clicks.) The ADC was usually within +/-3 values from expected. Nonetheless I took your advice to set ADC10AE appropriately.
Thanks
**Attention** This is a public forum