| View previous topic :: View next topic | 
	
	
		| Author | Message | 
	
		| smiles 
 
 
 Joined: 20 Feb 2008
 Posts: 33
 
 
 
			    
 
 | 
			
				| 1 second with Timer1 |  
				|  Posted: Wed Mar 12, 2008 9:47 am |   |  
				| 
 |  
				| 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
 
 
			    
 
 | 
			
				| Re: 1 second with Timer1 |  
				|  Posted: Wed Mar 12, 2008 11:11 am |   |  
				| 
 |  
				|  	  | 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
 
 
 
			    
 
 | 
			
				| Re: 1 second with Timer1 |  
				|  Posted: Wed Mar 12, 2008 11:49 am |   |  
				| 
 |  
				|  	  | 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
 
 
 
			    
 
 | 
			
				|  |  
				|  Posted: Thu Mar 13, 2008 1:14 am |   |  
				| 
 |  
				| 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
  |  | 
	
		|  | 
	
		| smiles 
 
 
 Joined: 20 Feb 2008
 Posts: 33
 
 
 
			    
 
 | 
			
				|  |  
				|  Posted: Thu Mar 13, 2008 3:50 am |   |  
				| 
 |  
				| 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
 
 
			    
 
 | 
			
				|  |  
				|  Posted: Thu Mar 13, 2008 11:34 am |   |  
				| 
 |  
				| 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). 	  | smiles wrote: |  	  | Hi I try 1second from this topic link above
 embed 50Hz then result of simulation is 49,52,53... ???
 
 | 
 
 In your original program you had:
 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(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. 	  | Code: |  	  | #int_timer1 void timer1_isr()
 {
 set_timer1( get_timer1() + 0x0BDC);
 i++;
 if(i==16)
 {
 freqRes=count;
 count=0;
 }
 }
 | 
 
 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
 
 
 
			    
 
 | 
			
				|  |  
				|  Posted: Fri Mar 14, 2008 1:48 am |   |  
				| 
 |  
				| Very thanks !!! I will continue to try it  |  | 
	
		|  | 
	
		|  |