CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to CCS Technical Support

1 second with Timer1

 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
smiles



Joined: 20 Feb 2008
Posts: 33

View user's profile Send private message

1 second with Timer1
PostPosted: Wed Mar 12, 2008 9:47 am     Reply with quote

I intend to measure frequency using method: counting the number of low-to-high pulses in 1 second.
Use this code
Code:

   setup_timer_1(T1_INTERNAL|T1_DIV_BY_1);       // Start timer 1
//   set_timer1(0x0BDB);     //this sets timer1 register to 0
   setup_ccp1(CCP_CAPTURE_RE);    // Configure CCP1 to capture rise
   enable_interrupts(INT_CCP1); // Setup interrupt on falling edge
   enable_interrupts(INT_TIMER1);
   enable_interrupts(GLOBAL);

My crystal is 4MHz so each internal instruction takes 1us, I use T1_DIV_BY_1 then interrupt #int_timer1 happens after 65.536us
I need 1s=1000000us so I need 16 times to do this
1000000=16*(65536-3036)
caused 3036 equivalent to 0x0BDC
so I did this code for interrupt
Code:

#int_timer1
void timer1_isr()
   {
      set_timer1(0x0BDC);
      i++;
      if(i==16)
      {
      freqRes=count;
      count=0;
      }
   }
#int_ccp1
void CCP1_isr()
   {
   count++;
   }

Now test with simulate tool, I embed 50Hz then got 53Hz (100Hz then 106Hz, 150 Hz then 160Hz)
Could you show me where I am wrong ?
Thanks !!!
Neutone



Joined: 08 Sep 2003
Posts: 839
Location: Houston

View user's profile Send private message

Re: 1 second with Timer1
PostPosted: Wed Mar 12, 2008 11:11 am     Reply with quote

smiles wrote:
I intend to measure frequency using method: counting the number of low-to-high pulses in 1 second.

Could you show me where I am wrong ?
Thanks !!!

Read this thread describing how to measure time with timer1 accurately.
http://www.ccsinfo.com/forum/viewtopic.php?t=26177

You can probably configure timer2 to interrupt an exact number of times per second without loading the timer value. Use timer1 to count falling edges.
RLScott



Joined: 10 Jul 2007
Posts: 465

View user's profile Send private message

Re: 1 second with Timer1
PostPosted: Wed Mar 12, 2008 11:49 am     Reply with quote

smiles wrote:

Code:

#int_timer1
void timer1_isr()
   {
      set_timer1(0x0BDC);
      i++;
      if(i==16)
      {
      freqRes=count;
      count=0;
      }
   }


The problem is you did not allow for processing overhead. When you get a Timer 1 interrupt, the Timer 1 has just then wrapped around to 0000. By the time you get to your set_timer1(0x0BDC), it is probably already 12 microseconds later, and maybe more depending on what other interrupts are enabled and whether or not you ever disable interrupts, even for a short time. In general, it is a bad idea to count on precise interrupt overhead, because it can change so easily. It would be better to let Timer 1 run free. After 16 overflows, the time elapsed will be 1.048576 seconds. So whatever count you get, divide it my 1.048576 to get the frequency.

Robert Scott
Real-Time Specialties
smiles



Joined: 20 Feb 2008
Posts: 33

View user's profile Send private message

PostPosted: Thu Mar 13, 2008 1:14 am     Reply with quote

Thanks Scott, the result is better
Code:


   int i=0;
   int32 count=0,freqRes;
   char freqChar[10];
   float freqNum;
   char words[]="THE VALUE OF FREQUENCY: ";
#int_ccp1
void isr()
   {
      count++;
   }
//**********************
#int_timer1
void isr_timer()
   {
      i++;
      if(i==16)
      {
      setup_timer_1(T1_DISABLED);
      freqNum=(float)count/1.048576;
      }
   }
void main()
{
   // TODO: USER CODE!!
//   int16 adc_value;
//   float volts;
//   int16 voltsRes;
//   char voltsChar[12];
   InitPic();
   InitLcd();
   Speech(words,0x00);
   delay_ms(1000);
   do
   {
   if(freqNum!=0)
   {
   WrCmd2Lcd(lcd_clr);
   delay_ms(100);
   freqRes=ceil(freqNum);
   itoa(freqRes,10,freqChar);
   Speech(freqChar,0x00);
   delay_ms(1000);
   freqNum=0;
   i=0;
   count=0;
   setup_timer_1(T1_INTERNAL|T1_DIV_BY_1);       // Start timer 1
   set_timer1(0);     //this sets timer1 register to 0
   }
   }
   while(1);
}
void InitPic()
{
   setup_adc_ports(NO_ANALOGS);
   setup_adc(ADC_OFF);
   setup_psp(PSP_DISABLED);
   setup_spi(FALSE);
   output_b(0x00);
   output_d(0x00);
   output_c(0x00);
   setup_adc_ports(AN0);
   setup_adc(ADC_CLOCK_DIV_8);
   set_adc_channel(0);
   setup_timer_1(T1_INTERNAL|T1_DIV_BY_1);       // Start timer 1
   set_timer1(0);     //this sets timer1 register to 0
   setup_ccp1(CCP_CAPTURE_RE);    // Configure CCP1 to capture rise
   enable_interrupts(INT_TIMER1);
   enable_interrupts(INT_CCP1); // Setup interrupt on falling edge
   enable_interrupts(GLOBAL);
}

Hi Neutone, I will try it Smile
smiles



Joined: 20 Feb 2008
Posts: 33

View user's profile Send private message

PostPosted: Thu Mar 13, 2008 3:50 am     Reply with quote

Quote:
Read this thread describing how to measure time with timer1 accurately.
http://www.ccsinfo.com/forum/viewtopic.php?t=26177

Hi I try 1second from this topic link above
embed 50Hz then result of simulation is 49,52,53... ???
Code:

   //RTC VARIABLES
   #define XTAL_FREQUENCY  4000000
   #define TIMER1_FREQUENCY (XTAL_FREQUENCY / 4)      // 1 clock tick = 1 instr. cycle = crystal frequency / 4
   int32 Ticker;
   int8 seconds=0;

   // GUIDED WORDS AND VARIABLES
   int32 freqCount=0,freqStore;
   char freqChar[10];
   char words[]="THE VALUE OF FREQUENCY: ";
//************************
void InitCCP1(void)
{
   setup_ccp1(CCP_CAPTURE_RE);    // Configure CCP1 to capture rise
   enable_interrupts(INT_CCP1); // Setup interrupt on falling edge
}
//************************
#int_TIMER1
void TIMER1_isr()
{
  Ticker -= 65536;                        // Decrement ticker by clocks per interrupt
  if ( Ticker < 65536 )                   // If second has expired
  {
     Ticker += TIMER1_FREQUENCY;          //   Increment ticker by clocks per second
     seconds++;                           //   Increment number of seconds
  }
}
//*************************
#int_CCP1
void CCP1_isr()
   {
      freqCount++;
   }
//*************************
void main()
{
   // TODO: USER CODE!!
  int8 prevSeconds;
  InitLcd();
  InitRTC();
  InitCCP1();
  Speech(words,0x00);
  delay_ms(1000);
  enable_interrupts(GLOBAL);
  while(1)
  {
    if (seconds != prevSeconds)
    {
       prevSeconds = seconds;
       freqStore=freqCount;   //I think there are problem with my freqCount value, should I let it increase freely like Timer1
       freqCount=0;
       itoa(freqStore,10,freqChar);
       WrCmd2Lcd(lcd_clr);
       delay_ms(100);
       Speech(freqChar,0x00);
    }
  }
}
ckielstra



Joined: 18 Mar 2004
Posts: 3680
Location: The Netherlands

View user's profile Send private message

PostPosted: Thu Mar 13, 2008 11:34 am     Reply with quote

smiles wrote:
Quote:
Read this thread describing how to measure time with timer1 accurately.
http://www.ccsinfo.com/forum/viewtopic.php?t=26177

Hi I try 1second from this topic link above
embed 50Hz then result of simulation is 49,52,53... ???
The algorithm from the linked thread is very accurate in the long term but it is not very accurate for timing a single second interval (in your implementation at 4MHz about 2% deviation).

In your original program you had:
Code:
#int_timer1
void timer1_isr()
   {
      set_timer1(0x0BDC);
      i++;
      if(i==16)
      {
      freqRes=count;
      count=0;
      }
   }
As Robert Scott already pointed setting the timer this way is not very accurate because you never know how much time has passed when your function is entered. Instead of the floating point calculation there is another method with much less overhead:
Code:
#int_timer1
void timer1_isr()
   {
      set_timer1( get_timer1() + 0x0BDC);
      i++;
      if(i==16)
      {
      freqRes=count;
      count=0;
      }
   }
A small inaccuracy in this code is introduced by the fact that Timer1 will continue counting in between the instructions for reading the timer and setting it again. This can be improved by slowing down timer1 (use DIVIDE_BY_16 instead of dividing by 1) or by adjusting the offset value.

A more elegant solution was suggested by Neutone; use Timer2 instead of Timer1. The beauty of Timer2 is that it has a programmable hardware compare register so it won't reset at the overflow from 0xFF to 0x00 but at any value you specify. This means you don't have to load the offset value again on every interrupt, resulting in a higher accuracy with less code.
Code:
// Assuming 4MHz crystal.
// 4Mhz = 1 million instruction ticks
// In order to detect an exact 1 second interval we set the prescaler to 16,
// the comparator to 250 and postscaler to 10. This way we get an interrupt
// exact every 0.04 sec. Counting 25 of these is 1 second. (25 * 16 * 250 * 10 = 1 million)
// Note: other value combinations are possible to achieve the same result, just choose a combination you like.
setup_timer_2( T2_DIV_BY_16, 250, 10);
.
.
.

#int_timer2
void timer2_isr()
{
   i++;
   if (i==25)
   {
      freqRes=count;
      count=0;
   }
}
smiles



Joined: 20 Feb 2008
Posts: 33

View user's profile Send private message

PostPosted: Fri Mar 14, 2008 1:48 am     Reply with quote

Very thanks !!! I will continue to try it Very Happy
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group