|
|
View previous topic :: View next topic |
Author |
Message |
counterf
Joined: 05 Sep 2013 Posts: 4
|
CCP with spike filter |
Posted: Thu Sep 05, 2013 6:34 pm |
|
|
Hi. First thank you PCM Programmer for all this code .
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
|
|
Posted: Thu Sep 05, 2013 6:55 pm |
|
|
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
|
|
Posted: Thu Sep 05, 2013 8:36 pm |
|
|
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.
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
|
|
Posted: Thu Sep 05, 2013 10:24 pm |
|
|
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
|
|
Posted: Fri Sep 06, 2013 10:47 am |
|
|
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
|
|
Posted: Fri Sep 06, 2013 12:47 pm |
|
|
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
|
|
Posted: Fri Sep 06, 2013 12:54 pm |
|
|
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.
|
|
|
matthewsachs
Joined: 19 Jun 2016 Posts: 22
|
|
Posted: Sun Jun 19, 2016 3:05 pm |
|
|
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
|
|
Posted: Tue Jun 21, 2016 9:59 am |
|
|
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
|
|
Posted: Tue Jun 21, 2016 4:07 pm |
|
|
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 |
|
|
|
|
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
|