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
}
}