I thought I had this resolved here before but ran into a rare error.
Here is the basic operation:
1. I am using Timer A to capture a square wave input on P1.1. Timer A is in cont mode and the signal input range I am needing is 0Hz to 3KHz.
2. I keep track of the edges triggering the Timer A0 int. My input signal source I am not gauranted a 50/50 duty cycle so I capture on the rising edges. 2 rising edges get me one full period.
3. I capture the first edge, grab the timer TACCR0 and put into "startcount", I grab the next edge TACCR0 and put into "stop count" This always works.
4. Since I am running Timer A continuously and my clock is high (for precision) then in order to extend the range of the freq counter I keep track of the Timer A overflows.
5. With the Timer A going continuously I do a test in the TIMER A0 INT and see if "startcount" is less than "stopcount" or if it greater than.
6. If startcount is greater than stopcount then I substract 1 overflow from the total. This usually works.
If startcount is less than stopcount then no correction to the overflows total. This usually works.
7. The overflows are increased in the Timer A1 INT.
8. In my main loop then my final freq is the total of overflows accumlated plus the "startcount - stopcount" amount.
9. I set a breakpoint in the main loop after I calculate my freq to check accuracy. On rare occasions the overflow is 1 too high if startcount < stopcount. On rare occasions the overflow is 1 too low if startcount > stopcount. WHen I use the breakpoint then I keep the input freq. fixed. In this example I keep at 500 Hz.
10. Frequency is then output on the COM port and fed to a PC terminal. (this works). The freq error is so rare I have yet to see it on the COM output, however I only output once every second.
I have other features in the pgm but not related to the freq calculate problem, for reference I highlighted the critcal and relavent code bold red.
MAIN CODE
//******************************************************************************
// MSP430x26x - USCI_A0, 115200 UART Echo ISR,
//
// Description: Echo a received character, RX ISR used.
// USCI_A0 RX interrupt triggers TX Echo.
// Baud rate divider with 1MHz = 1MHz/115200 = ~8.7
// Using External Clock = 10MHz
// Start and Stop timers, enable/disable Port 1 int, and Set Modes Variables based on numbers from COMM RX ISR
// Generate Pulses on P4.0 output based on Input P1.1 activity
//
// MSP430F261x/241x
// -----------------
// /|\| XIN|------------->
// | | |10 MHz ext clock
// --|RST XOUT|-<------------
// | |
// | P1.1 | Timer_A3.CCIOA Input. Int on rising edge
// | P3.4/UCA0TXD|------------>
// | | 115200 - 8N1
// | P3.5/UCA0RXD|<------------
// | |
// | P4.0 | Output. Default low
//******************************************************************************
/*
* ======== Standard MSP430 includes ========
*/
#include <msp430.h>
#include <stdio.h>
#include <string.h>
/*
* ======== Grace related declaration ========
*/
extern void CSL_init(void);
int mode; // based on rcv character, used to set which variable to output on 1Hz "heartbeat"
int rxdata; // rcv char over comm
extern volatile unsigned int period; //
extern volatile unsigned int period_freeze; //
double overflow_time;//
double clockrate;// 10 MHz clock
extern double freq; // FRAW frequency!!!!!
extern volatile double period_total;//
extern unsigned long leading_edges;
extern volatile unsigned int ta_overflows;
extern volatile unsigned int ta_overflow_end;
extern volatile unsigned int ta_overflows_start;//
extern volatile unsigned int ta_overflows_stop;//
extern volatile unsigned int ta_overflow_total;// Used for total ticks tally so that < 153 Hz can be measured
extern volatile int heartbeat; //Software counter variable based on Timer A overflow ... Sets 1 Hz txdata out rate
extern volatile unsigned int startcount;// First leading edge
extern volatile unsigned int stopcount; // 2nd leading
double period_total_freeze;
extern volatile int count_finished;
extern volatile unsigned int fault;
unsigned int periodB; //
float DegC;//
char freq_strg[40];
unsigned char n_avg;
double freq_avg;
double tempdbl_freq;
double tempdb2_freq;
const double taovr = 0.0065535;
/* This works but must know length
void sendstring(char * str, int len)
{
int i = 0;
for(i = 0; i < len; i++)
{
while (!(IFG2 & UCA0TXIFG)); // USART0 TX buffer ready?
UCA0TXBUF = str[i];
mode=9;
}
}
*/
void sendstring(char * str)// Used to send TX data out as string when 1Hz has elapsed from Timer A "heartbeat" ticks captured in software counter
{
int i = 0;
for(i = 0; i < strlen(str); i++)
{
while (!(IFG2 & UCA0TXIFG)); // USART0 TX buffer ready?
UCA0TXBUF = str[i];
}
}
/*
* ======== main ========
*/
main(void)
{
WDTCTL = WDTPW + WDTHOLD; // Stop WDT
CSL_init(); // Activate Grace-generated configuration
P4DIR |= 0x01; // Set P1.0 to output direction
P4SEL |= 0x01;
TBCCR0 = 1000-1;
TBCTL = TBCLGRP_0 + CNTL_0 + TBSSEL_2 + ID_0 + MC_1; // SMCLK, upmode
TBCCTL0 = CM_0 + CCIS_0 + SCS + CLLD_1 + OUTMOD_4 + OUT + TBCLR;
_BIS_SR(GIE); // Allow interrupts
overflow_time= 0.0065535; // full timer A elapsed time after overflow 65535 x 0.0000001 (10 MHz clock)
// Initialize all the variables
mode = 1;
rxdata = 1;
leading_edges = 0;
ta_overflows=0;
startcount=0;
stopcount=0;
count_finished = 0;
ta_overflows_start = 0;
ta_overflows_stop = 0;
period = 0;
freq = 0;
clockrate = 0.0000001; // 10 MHz clock
ta_overflow_total = 0; // initializes timer A overflow counts
n_avg = 1;
TBCCR0 = 1000-1;
while (1) {
// Scan COM for mode change
if (rxdata == 1) {
// If rxdata = 1 then mode = 1
mode = 1;
}
if (rxdata == 2) {
// If rxdata = 2 then mode = 2
mode = 2;
}
if (rxdata == 3) {
// If rxdata = 3 then mode = 3
mode = 3;
}
if (rxdata == 4) {
// If rxdata = 4 then mode = 4
mode = 4;
}
if (rxdata == 5) {
// If rxdata = 5 then mode = 5
mode = 5;
}
if (rxdata == 6) {
// If rxdata = 6 then mode = 6
mode = 6;
}
if (rxdata == 7) {
// If rxdata = 7 then mode = 7
mode = 7;
}
if (rxdata == 8) {
// If rxdata = 8 then mode = 8
mode = 8;
}
if (rxdata == 9) {
// If rxdata = 9 then mode = 9
mode = 9;
}
if (heartbeat == 152){
heartbeat = 0;
//sprintf(freq_strg, "FRAW Frequency = %04.4f Hz\r\n",freq);
sprintf(freq_strg, "FRAW Frequency = %04.4f Hz\r\n",freq_avg);
sendstring(freq_strg); //string output
}
if (count_finished == 1){
period_total= (taovr * ta_overflow_end) + (period_freeze * 0.0000001);
freq= 1/period_total; // mgp
if ((freq > 500.5) || (freq < 499.5)){
MY BREAKPOINT IS HERE mode = 9; // Stop if freq out of limits. the error is always "ta_overflow_end" error and always from the Timer A INT
}
// If single period average, use current input as average
if (n_avg == 1)
freq_avg = freq;
else // Else, using rolling average formula
{
tempdbl_freq = n_avg-1; // f_avg = ((n_avg-1)*f_avg+fin)/n_avg
tempdb2_freq = tempdbl_freq * freq; // Simplified to reduce temps
tempdbl_freq = tempdb2_freq + freq;
freq_avg = tempdbl_freq/n_avg;
//mode = 9; // I use this only for debug purposes setting break point
}
if (freq_avg >= 153){
TBCTL = TBCLGRP_0 + CNTL_0 + TBSSEL_2 + ID_1 + MC_1;
periodB = (1/freq_avg)/(.0000004);
TBCCR0 = periodB;
//mode = 9; // MGP I use this only for debug purposes setting break point here
}
period = 0;
period_total = 0;
count_finished = 0;
}
// mode = 9; // MGP I use this only for debug purposes setting break point here
}
}
void USCI0RX_ISR(void)
{
while (!(IFG2&UCA0TXIFG)); // USCI_A0 TX buffer ready?
rxdata = UCA0RXBUF;
UCA0TXBUF = UCA0RXBUF; // TX -> RXed character
}
// Timer B0 interrupt service routine
#pragma vector=TIMERB0_VECTOR
__interrupt void Timer_B (void)
{
}
TIMER A INIT CODE (from Grace)
void Timer_A3_graceInit(void)
{
/* USER CODE START (section: Timer_A3_graceInit_prologue) */
/* User initialization code */
/* USER CODE END (section: Timer_A3_graceInit_prologue) */
/*
* TACCTL0, Capture/Compare Control Register 0
*
* CM_1 -- Rising Edge
* CCIS_0 -- CCIxA
* SCS -- Sychronous Capture
* ~SCCI -- Latched capture signal (read)
* CAP -- Capture mode
* OUTMOD_0 -- PWM output mode: 0 - OUT bit value
*
* Note: ~SCCI indicates that SCCI has value zero
*/
TACCTL0 = CM_1 + CCIS_0 + SCS + SCCI + CAP + OUTMOD_0 + CCIE;
/*
* TACTL, Timer_A3 Control Register
*
* TASSEL_2 -- SMCLK
* ID_0 -- Divider - /1
* MC_2 -- Continuous Mode
*/
TACTL = TASSEL_2 + ID_0 + MC_2 + TAIE;
/* USER CODE START (section: Timer_A3_graceInit_epilogue) */
/* User code */
/* USER CODE END (section: Timer_A3_graceInit_epilogue) */
TIMER A INT CODE (in Grace generated file)
/*
* ======== Timer_A3 Interrupt Service Routine ========
*/
#pragma vector=TIMERA0_VECTOR
__interrupt void TIMERA0_ISR_HOOK(void)
{
/* USER CODE START (section: TIMERA0_ISR_HOOK) */
count_finished = 0;
leading_edges ++;
fault = 0;
switch (leading_edges)
{
case 1:
ta_overflows_start = ta_overflows;
startcount = TACCR0;
fault = 1;
break;
case 2:
ta_overflows_stop = ta_overflows;
stopcount = TACCR0;
period = stopcount - startcount; //MGP
period_freeze = period;
ta_overflow_total = ta_overflows_stop - ta_overflows_start;
fault = 2;
if(startcount > stopcount){
ta_overflow_end = ta_overflow_total - 1;// Sometime this fails - may be 1 too high!
leading_edges = 0;
count_finished = 1;
fault = 3;
}
if(startcount < stopcount){
ta_overflow_end = ta_overflow_total + 0; // Sometime this fails - may be 1 too low!!
leading_edges = 0;
count_finished = 1;
fault = 4;
}
break;
}
}
//* USER CODE END (section: TIMERA0_ISR_HOOK) */
/*
* ======== Timer_A3 Interrupt Service Routine ========
*/
#pragma vector=TIMERA1_VECTOR
__interrupt void TIMERA1_ISR_HOOK(void)
{
/* USER CODE START (section: TIMERA1_ISR_HOOK) */
/* replace this comment with your code */
if (count_finished == 0) ta_overflows ++;
heartbeat ++;
TACTL &= ~TAIFG;
/* USER CODE END (section: TIMERA1_ISR_HOOK) */
}
/*
* ======== Preserved user code snippets ========
*/
#if 0
/* USER CODE START (section: TIMERB0_ISR_HOOK) */
// TBCTL &= ~TBIFG;
//P4OUT ^= 0x01; // Toggle P4.0 using exclusive-OR - MGP added
/* USER CODE END (section: TIMERB0_ISR_HOOK) */
#endif
}