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

CCP with spike filter

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



Joined: 05 Sep 2013
Posts: 4

View user's profile Send private message

CCP with spike filter
PostPosted: Thu Sep 05, 2013 6:34 pm     Reply with quote

Hi. First thank you PCM Programmer for all this code Smile.
This code below is working with CCP capture only.

I need Help to change this code to implement spike filter on variable "frequency"

At the end i want to print only filtered value of frequency.
Somenthing like that:

Code:
   
           printf("%4.2f\n\r", frequency_FILTERED);



Original code:
Code:
   #include <18F4550.H>
   //#fuses HSPLL, PLL3, CPUDIV1, NOWDT, PUT, BROWNOUT, NOLVP   // 12 MHz xtal
   #fuses HSPLL, PLL5, CPUDIV1, NOWDT, PUT, BROWNOUT, NOLVP // 20 MHz xtal (PIC com XTAL de 20MHZ)
   #use delay(clock=20000000)
   #use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7, ERRORS) //(Configuracao RS232)
   
   #priority CCP1, TIMER1


   // filters.h
   //------------------------------------------
   // DEFINES
   #define MEDIAN_FILTER_WIDTH  7
   #define MEAN_FILTER_WIDTH    6

   
   #define BytePtr(var, offset)   (char *)(&(char *)var + offset)
   #byte PIR1 = 0xF9E
   #bit  TMR1IF = PIR1.0
    int8  gc_timer1_extension = 0;
   int8  gc_capture_flag = FALSE;
   int32 g32_ccp_delta;



   //------------------------------------------
   // FUNCTION PROTOTYPES

   int16 median_filter(int16 latest_element);
   void Insertion_Sort_16(int16 *data, char array_size);
   int16 mean_filter(int16 latest_element);
   //------------------------------------------------------



   #int_timer1
   void timer1_isr(void)
   {
   gc_timer1_extension++;
   }
   
   //------------------------------------------------------
   
   #int_ccp1
   void ccp1_isr(void)
   {
   char timer_ext_copy;
   int32 current_ccp;
   static int32 old_ccp = 0;
   
   gc_capture_flag = TRUE;       
   
   current_ccp = (int32)CCP_1;   
   
   // Get local copy of the timer ext.
   timer_ext_copy = gc_timer1_extension; 
   
   
   if(TMR1IF)
     {
      if(*BytePtr(current_ccp, 1) < 2)  // Was CCP captured after Timer1 wrapped?
         timer_ext_copy++;  // If so, inc the copy of the timer ext.
   
      // Since we know a timer interrupt is pending, let's just
      // handle it here and now.  That saves a little load off
      // the processor.
      gc_timer1_extension++;  // Increment the real timer extension
      TMR1IF = 0;     // Then clear the Timer1 interrupt
     }
   
   // Insert the timer extension into the proper place in the 32-bit
   // CCP value.
   // ie.,  Insert it into location "EE" as follows: 0x00EEnnnn
   // (nnnn = the CCP).
   *BytePtr(current_ccp, 2) = timer_ext_copy;
   
   g32_ccp_delta = (current_ccp > old_ccp) ? current_ccp - old_ccp : current_ccp + (0x1000000 - old_ccp);
   
   // Save the current ccp value for next time.
   old_ccp = current_ccp;
   
   }
   
   //=======================
   void main()
   {
   float frequency;
   int32 current_ccp_delta;
   
   set_timer1(0);           
   setup_timer_1(T1_INTERNAL | T1_DIV_BY_1);   
   
   setup_ccp1(CCP_CAPTURE_RE);   
   
   // Enable interrupts.
   clear_interrupt(INT_TIMER1);
   enable_interrupts(INT_TIMER1);
   
   clear_interrupt(INT_CCP1);
   enable_interrupts(INT_CCP1);
   
   enable_interrupts(GLOBAL);
   
   
   while(1)
     {
   
      if(gc_capture_flag == TRUE)
        {
         disable_interrupts(GLOBAL);
         current_ccp_delta = g32_ccp_delta;;
         enable_interrupts(GLOBAL);
   
         frequency =  (5000000L / (float)current_ccp_delta);   
           printf("%4.2f\n\r", frequency);
   
         gc_capture_flag = FALSE;
        }
     
     } 
   
   }

//- FILTER
   int16 median_filter(int16 latest_element)
{
static int16 input_buffer[MEDIAN_FILTER_WIDTH];
static char inbuf_index = 0;
static char num_elements = 0;
int16 sorted_data[MEDIAN_FILTER_WIDTH];
int16 median;

// Insert incoming data element into circular input buffer.
input_buffer[inbuf_index] = latest_element;
inbuf_index++;
if(inbuf_index >= MEDIAN_FILTER_WIDTH)  // If index went past buffer end
   inbuf_index = 0;       // then reset it to start of buffer

if(num_elements < MEDIAN_FILTER_WIDTH)
   num_elements++;

// THIS LINE MAY NOT BE NEEDED IF SORTED DATA IS STATIC.
memset(sorted_data, 0, MEDIAN_FILTER_WIDTH * 2);

// Copy input data buffer to the (to be) sorted data array.
memcpy(sorted_data, input_buffer, num_elements * 2);   // memcpy works on bytes

// Then sort the data.
Insertion_Sort_16(sorted_data, MEDIAN_FILTER_WIDTH);

// During the first few calls to this function, we have fewer
// elements in the sorted data array than the filter width.
// So to compensate for that, we pick the median from the number
// of elements that are available.  ie, if we have 3 elements,
// we pick the middle one of those as the median.
// Also, because the sort function sorts the data from low to high,
// we have to calculate the index from the high end of the array.
median = sorted_data[MEDIAN_FILTER_WIDTH - 1 - num_elements/2];

return(median);
}

//-------------------------------------------------------------
// This function calculates the Mean (average).

int16 mean_filter(int16 latest_element)
{
static int16 input_buffer[MEAN_FILTER_WIDTH];
static char inbuf_index = 0;
static char num_elements = 0;
int32 mean;
int32 sum;
char i;

// Insert incoming data element into circular input buffer.
input_buffer[inbuf_index] = latest_element;
inbuf_index++;
if(inbuf_index >= MEAN_FILTER_WIDTH)  // If index went past buffer end
   inbuf_index = 0;       // then reset it to start of buffer

if(num_elements < MEAN_FILTER_WIDTH)
   num_elements++;

// Calculate the mean.  This is done by summing up the
// values and dividing by the number of elements.
sum = 0;
for(i = 0; i < num_elements; i++)
    sum += input_buffer[i];

// Round-off the result by adding half the divisor to
// the numerator.
mean = (sum + (int32)(num_elements >> 1)) / num_elements;

return((int16)mean);
}

//-----------------------------------------------------
void Insertion_Sort_16(int16 *data, char array_size)
{
char i, j;
int16 index;

for(i = 1; i < array_size; i++)
   {
    index = data[i];
    j = i;

    while ((j > 0) && (data[j-1] > index))
      {
       data[j] = data[j-1];
       j = j - 1;
      }

    data[j] = index;
   }

}
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Thu Sep 05, 2013 6:55 pm     Reply with quote

Those filter routines aren't setup to work with floating point. Do you
need floating point accuracy with 2 digits to the right of the decimal ?
What is the frequency range that you are trying to measure ?

You could run the existing 16-bit filter routines on the 'current_ccp_delta'
variable. Do it before this line:
Code:

frequency =  (5000000L / (float)current_ccp_delta);   
printf("%4.2f\n\r", frequency);
 

For median_filter() and mean_filter(), you would give 'current_ccp_delta'
as the parameter and put the function return value into 'current_ccp_delta'.
Then the frequency will be calculated on a filtered version of 'current_ccp_delta'.
counterf



Joined: 05 Sep 2013
Posts: 4

View user's profile Send private message

PostPosted: Thu Sep 05, 2013 8:36 pm     Reply with quote

Hi PCM. Thank you.

I need to measure frequencies between 0.5 and 3hz. Its for Heart Rate Monitoring. So the final result is frequency*60 (Beats per minutes)
I don't need to know if the final bpm is 60.5 BPM or 90.2. It need to be integer.
Do you have any idea?

Anyway I'm a very newbie with C coding and CCS.
I just know how to change codes.
Embarassed

Am I doing right?

Code:
  void main()
   {
   float frequency;
   int16 bpmfrequency;
   int32 current_ccp_delta;
   
   set_timer1(0);           
   setup_timer_1(T1_INTERNAL | T1_DIV_BY_1);   
   
   setup_ccp1(CCP_CAPTURE_RE);   
   
   // Enable interrupts.
   clear_interrupt(INT_TIMER1);
   enable_interrupts(INT_TIMER1);
   
   clear_interrupt(INT_CCP1);
   enable_interrupts(INT_CCP1);
   
   enable_interrupts(GLOBAL);
   
   
   while(1)
     {
   
      if(gc_capture_flag == TRUE)
        {
         disable_interrupts(GLOBAL);
         current_ccp_delta = g32_ccp_delta;;
         enable_interrupts(GLOBAL);
   

 // HERE FILTER ROUTINES
          median_filter(current_ccp_delta);
          mean_filter(current_ccp_delta);


         frequency = (5000000L / (float)current_ccp_delta);   
         bpmfrequency = 60* frequency;     

 //PRINT WITH FILTERED CCP       
         printf("%lu \n\r", bpmfrequency);
         
         gc_capture_flag = FALSE;
        }
     
     } 
   
   } 
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Thu Sep 05, 2013 10:24 pm     Reply with quote

Quote:
// HERE FILTER ROUTINES
median_filter(current_ccp_delta);
mean_filter(current_ccp_delta);

You aren't using the return value from the function. It won't work without it.

I said to do this. You are doing the first part:
Quote:

For median_filter() and mean_filter(), you would give 'current_ccp_delta'
as the parameter
and put the function return value into 'current_ccp_delta'.


But you are not doing this part:
Quote:
and put the function return value into 'current_ccp_delta'


Check out this link. He is doing exactly the same mistake as you are
doing and the answer tells how to fix it.
http://stackoverflow.com/questions/5801574/functions-and-returning-values-in-c
counterf



Joined: 05 Sep 2013
Posts: 4

View user's profile Send private message

PostPosted: Fri Sep 06, 2013 10:47 am     Reply with quote

PCM programmer wrote:
Quote:
// HERE FILTER ROUTINES
median_filter(current_ccp_delta);
mean_filter(current_ccp_delta);

You aren't using the return value from the function. It won't work without it.

I said to do this. You are doing the first part:
Quote:

For median_filter() and mean_filter(), you would give 'current_ccp_delta'
as the parameter
and put the function return value into 'current_ccp_delta'.


But you are not doing this part:
Quote:
and put the function return value into 'current_ccp_delta'


Check out this link. He is doing exactly the same mistake as you are
doing and the answer tells how to fix it.
http://stackoverflow.com/questions/5801574/functions-and-returning-values-in-c


I changed this:

Code:

// HERE FILTER ROUTINES
median_filter(current_ccp_delta);
mean_filter(current_ccp_delta);



to this:

Code:

// HERE FILTER ROUTINES
current_ccp_delta= median_filter(current_ccp_delta);
current_ccp_delta = mean_filter(current_ccp_delta);


Median Function First and Then Mean Function.
The frequency will be calculated with filtered current_ccp_delta.

This is my final main code:

Code:


 void main()
   {
   float frequency;
   int16 bpmfrequency;
   int32 current_ccp_delta;
   
   set_timer1(0);           
   setup_timer_1(T1_INTERNAL | T1_DIV_BY_1);   
   
   setup_ccp1(CCP_CAPTURE_RE);   
   
   // Enable interrupts.
   clear_interrupt(INT_TIMER1);
   enable_interrupts(INT_TIMER1);
   
   clear_interrupt(INT_CCP1);
   enable_interrupts(INT_CCP1);
   
   enable_interrupts(GLOBAL);
   
   
   while(1)
     {
   
      if(gc_capture_flag == TRUE)
        {
         disable_interrupts(GLOBAL);
         current_ccp_delta = g32_ccp_delta;;
         enable_interrupts(GLOBAL);
   

 // HERE FILTER ROUTINES
          current_ccp_delta= median_filter(current_ccp_delta);
          current_ccp_delta = mean_filter(current_ccp_delta);

         frequency = (5000000L / (float)current_ccp_delta);   
         bpmfrequency = 60* frequency;     

 //PRINT WITH FILTERED CCP       
         printf("%lu \n\r", bpmfrequency);
         
         gc_capture_flag = FALSE;
        }
     
     } 
   
   }


Please tell me if I need to change something else.
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Fri Sep 06, 2013 12:47 pm     Reply with quote

Quote:
#fuses HSPLL, PLL5, CPUDIV1, NOWDT, PUT, BROWNOUT, NOLVP // 20 MHz xtal (PIC com XTAL de 20MHZ)
#use delay(clock=20000000)

If you want to run at 20 MHz, use the HS fuse, not HSPLL.


Quote:
Please tell me if I need to change something else.

I don't want to approve every line of your project. You must learn
about PICs and you must approve of each line.
counterf



Joined: 05 Sep 2013
Posts: 4

View user's profile Send private message

PostPosted: Fri Sep 06, 2013 12:54 pm     Reply with quote

PCM programmer wrote:
Quote:
#fuses HSPLL, PLL5, CPUDIV1, NOWDT, PUT, BROWNOUT, NOLVP // 20 MHz xtal (PIC com XTAL de 20MHZ)
#use delay(clock=20000000)

If you want to run at 20 MHz, use the HS fuse, not HSPLL.


Quote:
Please tell me if I need to change something else.

I don't want to approve every line of your project. You must learn
about PICs and you must approve of each line.


Ok. Anyway Thank you for everything.
Smile
matthewsachs



Joined: 19 Jun 2016
Posts: 22

View user's profile Send private message

PostPosted: Sun Jun 19, 2016 3:05 pm     Reply with quote

I'm having a lot of problems getting this well-proven PCM's code example to work on a PIC18F2321 running at 5Vdc clocked from an external 40MHz powered oscillator. I am using CCS v5.059
I am trying to measure the frequency of a 0-5V square wave applied at PIN 13 (CCP1). The frequency ranges from 10Hz to 1KHz. I'm sending the result out to the SIOW over RS485.

The input signal is generated by an AD654 voltage to frequency converter and monitored with a scope.

My code:
Code:

#include <18F2321.h>
#device adc=10
#fuses HS,NOLVP,NOWDT,NOCPD,NOWRTD,NOMCLR,STVREN,PUT,BROWNOUT,BORV45
#use delay(clock=40000000)

#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7, ERRORS)
     
#priority CCP1, TIMER1

#define RS485_DIR      PIN_C5
     
#define BytePtr(var, offset)   (char *)(&(char *)var + offset)
#byte PIR1 = 0xF9E
#bit  TMR1IF = PIR1.0
     
int8  gc_timer1_extension = 0;
int8  gc_capture_flag = FALSE;
int32 g32_ccp_delta;
       
#int_timer1
void timer1_isr(void)
{
    gc_timer1_extension++;
}
     
#int_ccp1
void ccp1_isr(void)
{
    char timer_ext_copy;
    int32 current_ccp;
    static int32 old_ccp = 0;
     
    gc_capture_flag = TRUE;       
     
    current_ccp = (int32)CCP_1;   
     
    // Get local copy of the timer ext.
    timer_ext_copy = gc_timer1_extension; 
     
     
    if(TMR1IF)
      {
       if(*BytePtr(current_ccp, 1) < 2)  // Was CCP captured after Timer1 wrapped?
          timer_ext_copy++;  // If so, inc the copy of the timer ext.
     
       // Since we know a timer interrupt is pending, let's just
       // handle it here and now.  That saves a little load off
       // the processor.
       gc_timer1_extension++;  // Increment the real timer extension
       TMR1IF = 0;     // Then clear the Timer1 interrupt
      }
     
    // Insert the timer extension into the proper place in the 32-bit
    // CCP value.
    // ie.,  Insert it into location "EE" as follows: 0x00EEnnnn
    // (nnnn = the CCP).
    *BytePtr(current_ccp, 2) = timer_ext_copy;
     
    g32_ccp_delta = (current_ccp > old_ccp) ? current_ccp - old_ccp : current_ccp + (0x1000000 - old_ccp);
     
    // Save the current ccp value for next time.
    old_ccp = current_ccp;
}

void main()
{
    float frequency;
    int32 current_ccp_delta;
     
    set_timer1(0);           
    setup_timer_1(T1_INTERNAL | T1_DIV_BY_1);   
     
    setup_ccp1(CCP_CAPTURE_RE);   
     
     output_high(RS485_DIR);
     
    // Enable interrupts.
    clear_interrupt(INT_TIMER1);
    enable_interrupts(INT_TIMER1);
     
    clear_interrupt(INT_CCP1);
    enable_interrupts(INT_CCP1);
     
    enable_interrupts(GLOBAL);
     
     
    while(1)
      {
     
       if(gc_capture_flag == TRUE)
         {
             disable_interrupts(GLOBAL);
             current_ccp_delta = g32_ccp_delta;;
             enable_interrupts(GLOBAL);
       
             frequency =  (10000000L / (float)current_ccp_delta);   
               printf("%4.2f\n\r", frequency);
       
             gc_capture_flag = FALSE;
         }
       
      } 
}


At some lower input frequencies the measurement result is totally wrong. It's correct in the upper portion of the frequency range, but in all cases there is a mysterious 0.59Hz reading that appears randomly in the readings:

~750Hz In
Code:

751.08
750.58
0.59
0.59
0.59
0.59
0.59
0.59
0.59
0.59
0.59
751.03
750.91
750.30
0.59
0.59
0.59
0.59
0.59
0.59
0.59
0.59
0.59
750.80
751.65
751.08
0.59
0.59
0.59
0.59
0.59
0.59
0.59
0.59
0.59
751.03
750.30
750.41
0.59
0.59
0.59
0.59
0.59
0.59
0.59
0.59
751.14
751.08
750.86
0.59
0.59


~48Hz In
Code:

856.75
905.63
903.01
896.53
887.23
0.59
765.93
882.84
869.33
902.77
0.59
890.94
850.91
869.11
870.77
908.59
0.59
885.11
850.41
946.43
857.11
917.09
0.59
924.64
869.41
986.77
727.90
0.59


~120Hz In
Code:

856.75
905.63
903.01
896.53
887.23
0.59
765.93
882.84
869.33
902.77
0.59
890.94
850.91
869.11
870.77
908.59
0.59
885.11
850.41
946.43
857.11
917.09
0.59
924.64
869.41
986.77
727.90
0.59
868.80
838.36
970.59
820.68
869.79
0.59
912.65
873.51
701.50
955.38
868.58
0.59
895.49
947.50
848.96
1009.79
836.82
0.59
691.56
899.19
881.21
826.44
0.59
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Tue Jun 21, 2016 9:59 am     Reply with quote

I found the problem. You need to delete the line near the top that
has the #define statement for BytePtr. Then put this line in to replace it:
Code:
#define BytePtr(var, offset)   (char *)((char *)&var + offset)

The line above is the correct way to do it.

I don't know where you got that code, but when I started working on your
problem I initially didn't see the defect. After a while, I decided to go to
a post where I know for a fact that it was working code. I pasted that
code in and it worked. After that, I printed out the program that failed
and the program that worked. A quick visual comparison indicated the
problem was in that #define BytePtr line. Just substitute the line above
and your program should work.

That code is fairly old and was probably done back in vs. 3.xxx.
The current compiler has a lot of newer constructs that would make the
code more clean. But the older code still works.
matthewsachs



Joined: 19 Jun 2016
Posts: 22

View user's profile Send private message

PostPosted: Tue Jun 21, 2016 4:07 pm     Reply with quote

It works !!!

with

Code:

#define BytePtr(var, offset)   (char *)((char *)&var + offset)


Although I am not sure why the old line

Code:

#define BytePtr(var, offset)   (char *)(&(char *)var + offset)


worked perfectly well on a PIC18F6722 project, whose code I copied over to start this PIC18F2321 project.

Perhaps I'd better go back and fix that PIC18F6722 code with the correct line and re-test everything.

Cannot thank you enough PCM.

Matt
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