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

A capacitance meter

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



Joined: 28 May 2017
Posts: 94

View user's profile Send private message

A capacitance meter
PostPosted: Sun Jun 15, 2025 10:28 am     Reply with quote

Hey, I am doing a capacitor counter with a PIC18F2620 and CCS 5.120 compiler.
I have a 555 as an oscillator, with two 1k resitors, 22uF measuring capacitor from pin 2 and 6 to ground.
It gives 21Hz pulses from pin 3 to PIC18's C2.
I am trying to measure the pulse with a CCP1 using rising and falling signals.
All interrupts Timer1 and CCP1 goes well, but the result is too small.
Fall 208 Rise: 191
Pulse width: 17 us
It should give 1/21Hz or 47.6ms/2 = 23.8ms
What am I doing wrong?
Here is the code:
Code:

#include <18F2620.h>
#FUSES NOWDT, INTRC_IO, NOPROTECT, NOBROWNOUT, NOPUT, NOCPD
#FUSES NODEBUG, NOLVP, NOWRT, NOFCMEN, NOMCLR, BORV21, NOIESO
#use delay(internal=8000000) // 8 MHz
#use rs232(baud=115200, parity=N, xmit=PIN_C6, rcv=PIN_C7, bits=8, invert)

#byte CCP1CON = 0xFBD
#byte PIR1 = 0xF9E
#byte CCPR1L = 0xFBE
#byte CCPR1H = 0xFBF

volatile unsigned long timer = 0, rise = 0, fall = 0, pulse_width = 0;
volatile int edge_state = 0, new_pulse = 0;
unsigned int16 overflow_count ;
//measure rising-falling time
#int_ccp1
void ccp1_isr() {
   unsigned long time = ((unsigned long)timer * 65536UL) + ((CCPR1H << 8) | CCPR1L); // look time CCPR1H <<8 + CPPR1L
   if (edge_state == 0) {
      rise = time;
      CCP1CON = 0x05; //Change to falling edge
      edge_state = 1;
   } else {
      fall = time;
      if (fall > rise)// if fall time is longer than rise
         pulse_width = fall - rise; // count puse width
      new_pulse = 1;
      CCP1CON = 0x04; //back to riding rdge
      edge_state = 0;
      timer=0;
   }
   PIR1 &= ~0x01; // zero  CCP1IF
}
#int_timer1
void timer1_isr() {
   timer++; // increase 32-bit timer value by over 65536
   overflow_count++;
}
void main() {
   set_tris_c(0b00000110); // PIN_C2 (CCP1) ja PIN_C1 (CCP2) input

   CCP1CON = 0x04; // CCP1 rising edge
 

   setup_timer_1(T1_INTERNAL | T1_DIV_BY_8); // Timer1, 1 µs per tick
   set_timer1(0);
 

   enable_interrupts(INT_CCP1);

   enable_interrupts(INT_TIMER1);
   enable_interrupts(GLOBAL);

   delay_ms(10);
   
   while(TRUE) {
   
 
      if (overflow_count >= 10) {
         printf("Timer1 overflow: %lu\r", timer);
         overflow_count = 0;
      }
 
      if (new_pulse) { 
      printf("Fall %lu Rise: %lu\r",fall, rise);
      pulse_width==fall-Rise;
         printf("Pulse width: %lu us\r", pulse_width); // nyt oikea yksikkö!
         new_pulse = 0;
      }
   
   }
}

Ttelmah



Joined: 11 Mar 2010
Posts: 19857

View user's profile Send private message

PostPosted: Sun Jun 15, 2025 2:40 pm     Reply with quote

First thing. Don't use terms like long.
Use the actual size you want. The problem is that 'long' means an int16 on
the PIC. On chips like the PC, it means an int32. Its is always safer to be
explicit and say what size you actually want.
The big problem comes here, because an unsigned long, is just a 16bit value
so can't hold the result of your maths with the timer overflow.... :(
Then you don't need to fiddle around performing the rotation to combine the
two parts of the CCP. The compiler already has CCP_1 defined for you as a
16vit word, that reads both parts in one operation. Look in the header file for the chip.
Then just use a union to combine the values. Have your overflow word
in the top 16bits of a union, and load the CCP_1 value into the low 16bits,
and you can then read the 32bit value, with no overhead involved in combining
these.
Code:

unsigned int32 rise=0; fall=0;
union {
    unsigned int16 words[2];
    unsigned int32 whole;
} timerval = 0;

#int_ccp1
void ccp1_isr(void) {
   timerval.words[0]=CCP_1; //load the low 16bits from the CCP
   if (edge_state == 0) {
      rise = timerval.whole;;
      CCP1CON = 0x05; //Change to falling edge
      edge_state = 1;
   } else {
      fall = timerval.whole;
      pulse_width = fall - rise; // count puse width
      new_pulse = 1;
      CCP1CON = 0x04; //back to riding rdge
      edge_state = 0;
   }
   //PIR1 &= ~0x01; // zero  CCP1IF //Not needed the compiler clears this
   //Unless you specify 'NOCLEAR'
}
#int_timer1
void timer1_isr() {
   timerval.word[1]++; //Increment the upper 16vbits of the timer
   overflow_count++;
}


Then the other thing is that the timer does not count off FOSC. It counts
off FOSC/4. So you need to use T1_DIV_BY_2, not 8. Look at figure 12-1
in the data sheet 'Timer 1 block diagram'. FOSC/4 internal clock......

pulse_width needs to be an int32.

You don't need to check for the fall value being greater. The maths will
wrap correctly if there has been an overflow. Similarly you don't need to
zero the upper part of the counter. Just let it run as a 32bit timer value
and let the arithmetic handle overflows.
pekka1234



Joined: 28 May 2017
Posts: 94

View user's profile Send private message

PostPosted: Mon Jun 16, 2025 3:31 am     Reply with quote

Thanks Ttelmah.

I rechecked this code and found that it is an asymmetric signal.
I modified the code so that it measures the entire cycle, and now it returns the correct value.

Here is the modified code ;
Code:

 int8 edge_state = 0, new_pulse = 0;
 unsigned int16 pulse_width = 0;
unsigned int32 rise=0, fall=0;
union {
    unsigned int16 words[2];
    unsigned int32 whole;
} timerval = 0;
int8 second=0;
#int_ccp1
void ccp1_isr(void) {
   timerval.words[0]=CCP_1; //load the low 16bits from the CCP
   if (edge_state == 0 &&!second ) {
      rise = timerval.whole;;
    //  CCP1CON = 0x05; //Change to falling edge
      edge_state = 1;
   } else {
      fall = timerval.whole;
      pulse_width = fall - rise; // count puse width
      new_pulse = 1;
    //  CCP1CON = 0x04; //back to riding rdge
      edge_state = 0;
      second =0;
   }
 
}
#int_timer1
void timer1_isr() {
   timerval.whole++; //Increment the upper 16vbits of the timer every 65536 us 
}
void main() {
   set_tris_c(0b00000110); // PIN_C2 (CCP1) ja PIN_C1 (CCP2) inpu
   CCP1CON = 0x04; // CCP1 rising edge
   setup_timer_1(T1_INTERNAL | T1_DIV_BY_2); // Timer1, 1 µs per tick
   set_timer1(0);
   enable_interrupts(INT_CCP1);
   enable_interrupts(INT_TIMER1);
   enable_interrupts(GLOBAL);

    timerval.whole=0;
   while(TRUE) {
      if (new_pulse) { 
      printf("Fall %lu Rise: %lu Whole %lu\r",fall, rise,timerval.whole);
      timerval.whole=0;
      pulse_width=fall-Rise;
      printf("Pulse width: %lu us\r", pulse_width); // 
      new_pulse = 0;
       
      }
   }
}
 


Now it gives 21Hz
Here is a serial result:

Fall 46087 Rise: 65252 Whole 46088
Pulse width: 46371 us

10000000/ 46371 = 21,566Hz, which is the right value of my 22uF capacitor

Now I must also measure 3300uF capacitors.
Ttelmah



Joined: 11 Mar 2010
Posts: 19857

View user's profile Send private message

PostPosted: Mon Jun 16, 2025 6:44 am     Reply with quote

OK. makes sense. The 555 is always like that.

Glad it works. but a couple of minor things:
If you are now doing the calculation in the main code, get rid of doing
this in the interrupt. Saves time in the interrupt which is good.
As you say, now for some different values. Smile

Worth saying that you will find you can get much better linearity if you
use a constant current source to charge the capacitor.
pekka1234



Joined: 28 May 2017
Posts: 94

View user's profile Send private message

PostPosted: Mon Jun 16, 2025 12:56 pm     Reply with quote

Thank you for your help.

How do I get a 555 or constant current source to get a faster pulse?

Now, when I tried 1k resistors 555, it gives a 10 times smaller value.

Then I changed 555 resistors to 10k.
It now takes about 10 seconds to make a measure.
It measures now to C=240.07 uF (should be 220 uF about).
Well, it will be good, but how do I get 3300 uF working?

It will take about 3 minutes to make a one measurement.
It will give a good value C=3303.49 uF (should be 3300 uF)
Maybe I will place the display (LCD) "Wait.."
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