I've been attempting to create a frequency measurement device with the MSP430. I'm attempting to use Timer A in capture mode, capturing two consecutive rising edges and subtracting their "captured" count values to determine the period of an incoming square wave. This code seems to get correct periods for signals above 200hz, but when attempting to measure below 200hz, the count value is not stable and seems to fluctuate wildly.
I've posted my code below. I'm also outputting the value of the count to an LCD screen I have an have included the LCD code as well.
Any help in the right direction would be greatly appreciated.
//Libraries To Include #include<msp430G2553.h> #include <stdio.h> #include <stdint.h> /* * EE449 Lux-Meter * Andrew Vocaire-Tramposch & Sukjinder Singh * Wilson - Section 04 * November 5th 2013 */ #define LCM_DIR P2DIR // Port 2 OUTPUT DIRECTION #define LCM_OUT P2OUT // Port 2 OUTPUT BITS // MSP LCM #define LCM_PIN_EN BIT0 // P2.0 6 #define LCM_PIN_RW BIT1 // P2.1 5 #define LCM_PIN_RS BIT2 // P2.2 4 #define LCM_PIN_D4 BIT4 // P2.4 11 #define LCM_PIN_D5 BIT5 // P2.5 12 #define LCM_PIN_D6 BIT6 // P2.6 13 #define LCM_PIN_D7 BIT7 // P2.7 14 #define LCM_PIN_MASK ((LCM_PIN_RS | LCM_PIN_RW | LCM_PIN_EN | LCM_PIN_D7 | LCM_PIN_D6 | LCM_PIN_D5 | LCM_PIN_D4)) #define TRUE 1 #define FALSE 0 //Variables for more readable code /*volatile static unsigned int i; volatile static unsigned long freq; volatile static unsigned long FootCandles; volatile static unsigned long counter; volatile static unsigned long prevccr; volatile static unsigned long ccrvalue; */ unsigned int buffer[100]; unsigned int i=0; unsigned int Current_Edge_Time; unsigned int Previous_Edge_Time; void set_clk_1Mhz(void) { //1Mhz if (CALBC1_1MHZ==0xFF) // If calibration constant erased { while(1); // do not load, trap CPU!! } DCOCTL = 0; // Select lowest DCOx and MODx settings BCSCTL1 = CALBC1_1MHZ; // Set range DCOCTL = CALDCO_1MHZ; } void set_clk_8Mhz(void) { //8Mhz if (CALBC1_8MHZ==0xFF) // If calibration constant erased { while(1); // do not load, trap CPU!! } DCOCTL = 0; // Select lowest DCOx and MODx settings BCSCTL1 = CALBC1_8MHZ; // Set range DCOCTL = CALDCO_8MHZ; } void set_clk_16Mhz(void) { //16Mhz if (CALBC1_16MHZ==0xFF) // If calibration constant erased { while(1); // do not load, trap CPU!! } DCOCTL = 0; // Select lowest DCOx and MODx settings BCSCTL1 = CALBC1_16MHZ; // Set range DCOCTL = CALDCO_16MHZ; // Set DCO step + modulation*/ } //Function to read in data to the LCM void PulseLcm() { // // pull EN bit low // LCM_OUT &= ~LCM_PIN_EN; __delay_cycles(20000); // // pull EN bit high // LCM_OUT |= LCM_PIN_EN; __delay_cycles(20000); // // pull EN bit low again // LCM_OUT &= (~LCM_PIN_EN); __delay_cycles(20000); } //Function to send a byte to the LCM through 4-bit mode void SendByte(char ByteToSend, int IsData) { // // clear out all pins // LCM_OUT &= ~(LCM_PIN_MASK); // // set High Nibble (HN) - // usefulness of the identity mapping // apparent here. We can set the // DB7 - DB4 just by setting P1.7 - P1.4 // using a simple assignment // LCM_OUT |= (ByteToSend & 0xF0); if (IsData == TRUE) { LCM_OUT |= LCM_PIN_RS; } else { LCM_OUT &= ~LCM_PIN_RS; } // // we've set up the input voltages to the LCM. // Now tell it to read them. // PulseLcm(); // // set Low Nibble (LN) - // usefulness of the identity mapping // apparent here. We can set the // DB7 - DB4 just by setting P1.7 - P1.4 // using a simple assignment // LCM_OUT &= ~(LCM_PIN_MASK); LCM_OUT |= ((ByteToSend & 0x0F) << 4); if (IsData == TRUE) { LCM_OUT |= LCM_PIN_RS; } else { LCM_OUT &= ~LCM_PIN_RS; } // // we've set up the input voltages to the LCM. // Now tell it to read them. // PulseLcm(); } //The initialization function for the LCM void InitializeLCM(void) { //Power on //Initialize direction of Port1 pins, and clear the output on Port1 LCM_DIR = LCM_PIN_MASK; LCM_OUT = ~LCM_PIN_MASK; //delay 30ms __delay_cycles(400000); //Function Set LCM_OUT = 0x20; PulseLcm(); LCM_OUT = 0x20; PulseLcm(); LCM_OUT = 0xC0; PulseLcm(); //Delay > 39us __delay_cycles(10000); //Display Set LCM_OUT = 0x00; PulseLcm(); LCM_OUT = 0xF0; PulseLcm(); //Delay > 39us //Display Clear LCM_OUT = 0x00; PulseLcm(); LCM_OUT = 0x10; PulseLcm(); //Delay 1.53ms __delay_cycles(30000); //Entry Mode Set LCM_OUT = 0x00; PulseLcm(); LCM_OUT = 0x60; PulseLcm(); //Initialization Complete } void PrintStr(char *Text) { char *c; c = Text; while ((c != 0) && (*c != 0)) { SendByte(*c, TRUE); c++; } } void UpdateLCM(void) { char buf1[9]; //buffers for sprintf string creation function SendByte(0x80, FALSE); sprintf(buf1, "FQ: %d Hz", buffer[i-1]); PrintStr(buf1); } void main(void) { WDTCTL = WDTPW | WDTHOLD; // Stop watchdog timer set_clk_1Mhz(); // set SMCLK to 1Mhz P2SEL = 0x00; // Clear the P2SEL pins 6 and 7 to make these pins GPIO pins. P2SEL2 = 0x00; InitializeLCM(); // Initalize the LCM for outputing // initialize long counter //counter = 0; // initialize GPIO (Optimalized) P1DIR = BIT0 + BIT6; // Set P1.0 out,1.1 input dir P1OUT &= ~BIT0 + ~BIT6; // LEDs off P1DIR &= ~BIT2; //Port 1.2/TACCR1 - Input P1SEL |= BIT2; // Select Timer Capture option // Initialize Timer_A0 // TACTL register // BIT 9-8 - 00 - Source: SMCLK (TASSEL_2) // BIT 7-6 - 00 - Input divider: /1 // BIT 5-4 - 10 - Mode: Continuous (MC_2) // BIT 1 - 1 - Enable TAIV interrupt (TAIE) TACTL = TASSEL_2 + TACLR; // SMCLK // Initialize compare block 0 // CAP = 1 - Use capture mode // SCS = 0 - Asynchronous capture // CM1 - Capture on rising edge // CCIE - External input TACCTL1 = CM_1 + CCIS_0 + SCS + CAP + CCIE; Current_Edge_Time = 0x00; Previous_Edge_Time = 0x00; i=0; TACTL |= MC_2; _enable_interrupts(); // Enable interrupts while(1) { UpdateLCM(); } } //Timer_A1 TACCR0 Interrupt Vector Handler Routine #pragma vector = TIMER0_A1_VECTOR __interrupt void TA1_ISR(void) { switch (__even_in_range(TAIV, 10)) // Efficient switch-implementation { case 0: break; case 2: // CCR1 capture interrupt P1OUT ^= BIT0; // P1.0 toggle every rising input edge Current_Edge_Time = TACCR1; if(Current_Edge_Time < Previous_Edge_Time) { buffer[i++] = ((0xFFFF - Previous_Edge_Time) + Current_Edge_Time); } else { buffer[i++] = Current_Edge_Time - Previous_Edge_Time; } Previous_Edge_Time = Current_Edge_Time; if (i>100) { i=0; __no_operation(); // PLACE BREAKPOINT HERE } break; case 4: break; // CCR2 interrupt case 6: break; // Reserved case 8: break; // Reserved case 10:P1OUT ^= BIT6; break; // Timer Overflow } }