pillum 0 Posted September 5, 2012 Share Posted September 5, 2012 Hello, I want to measure the time between the both edges of clock signal. The Purpose of the project is to build a shift light with the MSP430 and the TLC5940 LED driver. The RPM signal to the tachometer comes in a 12V HIGH/LOW signal and the higher the RPM is the higher the frequency of the signal is. Instead of using a frequency counter I measure the time the signal is on HIGH. Capturing the signal with the Open Logic Sniffer gave me a straight and continuous signal. 2 MSP430s are used. One to generate the signal and the other to measure it. MSP430 #1 P1.0 -> MSP430 #2 P2.1 This is the code for generating the clock: void main(void) { WDTCTL = WDTPW + WDTHOLD; DCOCTL = CALDCO_16MHZ; BCSCTL1 = CALBC1_16MHZ; P1DIR = BIT0; P1OUT = 0x00; TACTL = TASSEL_2 | MC_1 | ID_0; CCR0 = 8000; CCTL0 = CCIE; _enable_interrupts(); _bis_SR_register(LPM0 | GIE); } #pragma vector=TIMER0_A0_VECTOR __interrupt void Timer_A (void) { P1OUT ^= BIT0; // Toggle P1.0 } This is the code for measuring it: void main() { /* Clock Setup */ WDTCTL = WDTPW + WDTHOLD; // Stop WDT // Set Clock Frequency to 16Mhz BCSCTL1 = CALBC1_16MHZ; DCOCTL = CALDCO_16MHZ; P2SEL = BIT1; P1DIR = 0x00; TA1CTL = TASSEL_2 + MC_2; TA1CCTL1 = CM_3 | SCS | CCIS_0 | CAP | CCIE; _enable_interrupts(); for(; ; } #pragma vector=TIMER1_A1_VECTOR __interrupt void TimerA1(void) { timer = TA1CCR1; TA1CTL |= TACLR; TA1CCTL1 &= ~(COV | CCIFG); } (I even tried to set the check to positive edge and in the ISR set the check to negative edge so its only measuring the HIGH signal time.) Sometimes I get values around CCR0 (8048, 8079, ..) but mostly its trash (0, 33152, 5000, 6915, ...). Whats wrong? Thank you Quote Link to post Share on other sites
JWoodrell 285 Posted September 5, 2012 Share Posted September 5, 2012 Since I have been working in assembly recently, it makes sense lets say input signal is on pin 1.3 *interrupt vector (low to high transition)* push R10 ;save R10 to the stack mov.w #0x00, R10 ;set R10 to 0 LoopStart ;Label to jump to for loop restart inc.w R10 ;add 1 to R10 word length (max 65535 counts) bit.b #0x08, &P1In ;test P1In against bit mask 00001000 jc LoopStart ;Jump to "LoopStart" if it is still high mov.w R10, TimeOut ;Move the register to your variable pop R10 ;Restore value to R10 the loop will take inc.w - 1 cycle bit.b - 4 cycles jc - 2 cycles 7 cycles per loop using your 16MHZ setting for the CPU that works out to a resolution of just under .0004375 ms per loop 5000 RPM is .2 ms per RPM i don't know how many times the signal pulses per RPM but that is 460 loops per RPM. i hope this helps if you want to use the Timer circuitry to get finer resolution i can piddle with code for that tomorrow, but this might be easier for you in C is would be something like *interrupt vector - low to high transition* int TimeOut = 0; while (P1In & #0x08){ TimeOut++; } but Im not in front of my computer so i can't see what assembly that will compile into to predict its loop time Quote Link to post Share on other sites
pillum 0 Posted September 5, 2012 Author Share Posted September 5, 2012 That would block the cpu for other uses like updating the led driver, wouldn't it? Quote Link to post Share on other sites
oPossum 1,083 Posted September 5, 2012 Share Posted September 5, 2012 - Don't reset the capture register - defeats the purpose of using it - No need to reset interrupt flag - using an ISR does that (in most cases) - Save an array of captures to see a trend unsigned cn = 0; // Timer capture number unsigned ca[16]; // Timer capture array #pragma vector=TIMER1_A1_VECTOR __interrupt void TimerA1(void) { static unsigned tl; // Last timer capture const unsigned tc = TA1CCR1; // Current timer capture const unsigned te = tc - tl; // Elapsed time tl = tc; // Update last timer capture ca[cn++ & 15] = te; // Save captured time in array } Quote Link to post Share on other sites
pillum 0 Posted September 5, 2012 Author Share Posted September 5, 2012 This gives me 16 variables with exactly the value 59, is that right? looks weird to me (edit: after a 2nd run its 54) Also how do I handle overflows if i'm not going to reset the register everytime? Quote Link to post Share on other sites
oPossum 1,083 Posted September 5, 2012 Share Posted September 5, 2012 Oh, forget code to read TAIV. Overflow in handled properly by unsigned subtraction - it will wrap around. __interrupt void TimerA1(void) { static unsigned tl; // Last timer capture volatile unsigned n = TA1IV; // Read interrupt vector to reset interrupt flag const unsigned tc = TA1CCR1; // Current timer capture const unsigned te = tc - tl; // Elapsed time tl = tc; // Update last timer capture ca[cn++ & 15] = te; // Save captured time in array } Quote Link to post Share on other sites
pillum 0 Posted September 5, 2012 Author Share Posted September 5, 2012 reading TAIV didnt change anything but rising the value to 64 now Quote Link to post Share on other sites
JWoodrell 285 Posted September 5, 2012 Share Posted September 5, 2012 Yes the processor would be focused on the loop while the signal was high, but it would have an equal ammount of time when the signal is low to update the driver, and would know after each pulse Quote Link to post Share on other sites
pillum 0 Posted September 5, 2012 Author Share Posted September 5, 2012 @JWoodrell Is there an advantage of your version to the HW Timer version? @oPossum I get the same value for different frequencies. Quote Link to post Share on other sites
cubeberg 540 Posted September 5, 2012 Share Posted September 5, 2012 You might take a look at the code for my RC car project. I used the timer to measure the length of a pulse coming back from an ultrasonic sensor. That code is based on this post on letsmakerobots. The code is written to pick up the signal on P2.1 as well. //Timer1_A Capture //P2.0 ends up triggering this timer #pragma vector=TIMER1_A0_VECTOR __interrupt void Timer1A0(void) { if(up) //is this the rising edge? { measure_1=TA1CCR0; // take first time measure } else //is this the falling edge? { measure_2=TA1CCR0; //take second time measure measure=(measure_2-measure_1)/58; // microseconds / 58 = centimeters } up=!up; //if this was the rising edge, the next one will be a falling edge, and vice-versa TA1CTL &= ~TAIFG; //clear timer A interrupt flag, so the chip knows we handled the interrupt } Quote Link to post Share on other sites
pillum 0 Posted September 5, 2012 Author Share Posted September 5, 2012 weird, with your version I get 48 for every pulse of different length. Also I cannot use Timer1_A0 only Timer1_A1. It never gets to the ISR of it. Quote Link to post Share on other sites
cubeberg 540 Posted September 5, 2012 Share Posted September 5, 2012 Oops - I just looked again. I used 2.0. 2.1 is on Timer1_A1. You might double check your registers though. Are you using a clock source that's fast enough to read the pulse length accurately? TA1CCTL0 |= CM_3 + CCIS_0 + CAP + CCIE; TA1CTL |= TASSEL_2 + MC_2 + ID_0; Quote Link to post Share on other sites
pillum 0 Posted September 5, 2012 Author Share Posted September 5, 2012 yes the clock source should be fast enough, 16Mhz SMCLK TA1CTL = TASSEL_2 + MC_2; TA1CCTL1 = CM_3 | SCS | CCIS_0 | CAP | CCIE; Quote Link to post Share on other sites
oPossum 1,083 Posted September 5, 2012 Share Posted September 5, 2012 Here is tested code that uses a single G2553. Connect a jumper wire from P1.6 (timer A0 output) to P2.1 (timer A1 capture input) The output is 400 cycles on and then 600 cycles off. The ca[] array will have alternating values of 400 and 600. (run, halt, watch) #include void main() { WDTCTL = WDTPW | WDTHOLD; BCSCTL1 = CALBC1_16MHZ; DCOCTL = CALDCO_16MHZ; P1DIR = BIT6; P1SEL = BIT6; P2DIR = 0; P2SEL = BIT1; TA0CCR0 = 1000 - 1; TA0CCR1 = 400; TA0CTL = TASSEL_2 | MC_1; TA0CCTL1 = OUTMOD_6; TA1CTL = TASSEL_2 | MC_2; TA1CCTL1 = CM_3 | SCS | CCIS_0 | CAP | CCIE; _enable_interrupts(); for(;; } unsigned cn = 0; // Timer capture number unsigned ca[16]; // Timer capture array #pragma vector=TIMER1_A1_VECTOR __interrupt void TimerA1(void) { static unsigned tl; // Last timer capture volatile unsigned n = TA1IV; // Read interrupt vector to reset interrupt flag const unsigned tc = TA1CCR1; // Current timer capture const unsigned te = tc - tl; // Elapsed time tl = tc; // Update last timer capture ca[cn++ & 15] = te; // Save captured time in array } pillum 1 Quote Link to post Share on other sites
pillum 0 Posted September 5, 2012 Author Share Posted September 5, 2012 Thanks, your code did work somehow oPossum. Quote Link to post Share on other sites
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.