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

Frequency (Rate) Generation using CCP?

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







Frequency (Rate) Generation using CCP?
PostPosted: Mon Jun 02, 2003 7:34 pm     Reply with quote

I'm looking for a clean way to generate Frequencies
from about 0.1Hz to 2kHz. I read a note suggesting using the CCP rather than just programming the timer. I'm sure this has been discussed before. The manual provides me with insufficient info for this. Do I understand correctly:
value is loaded via a predefined var: CCP_1 ?
output is on RC2?
To generate a square wave, I plan to load T/2 and toggle a bit.
Is is right? is there a better way? Why doesn't this code work?
Thanks. Snippets below:

#int_CCP1
CCP1_isr() {

freqP = 1; // pulse bit
freqP = 0;

freqOut ^= 1; // toggling bit
}

void main() {

initializeStuff();
setup_ccp1(CCP_COMPARE_CLR_ON_MATCH);
enable_interrupts(GLOBAL);
enable_interrupts(INT_CCP1);

while (1) { // loop forever

if (switchPressed_flag) {
checkforuserinput();
printFreq();
CCP_1 = halfperiod;
}
}
}
___________________________
This message was ported from CCS's old forum
Original Post ID: 144514985
Kenny



Joined: 07 Sep 2003
Posts: 173
Location: Australia

View user's profile Send private message

Re: Frequency (Rate) Generation using CCP?
PostPosted: Tue Jun 03, 2003 1:02 am     Reply with quote

<font face="Courier New" size=-1>:=I'm looking for a clean way to generate Frequencies
:=from about 0.1Hz to 2kHz. I read a note suggesting using the CCP rather than just programming the timer. I'm sure this has been discussed before. The manual provides me with insufficient info for this. Do I understand correctly:
:=value is loaded via a predefined var: CCP_1 ?
:=output is on RC2?
:=To generate a square wave, I plan to load T/2 and toggle a bit.
:=Is is right? is there a better way? Why doesn't this code work?
:=Thanks. Snippets below:
:=
:=#int_CCP1
:=CCP1_isr() {
:=
:= freqP = 1; // pulse bit
:= freqP = 0;
:=
:= freqOut ^= 1; // toggling bit
:=}
:=
:=void main() {
:=
:= initializeStuff();
:= setup_ccp1(CCP_COMPARE_CLR_ON_MATCH);
:= enable_interrupts(GLOBAL);
:= enable_interrupts(INT_CCP1);
:=
:= while (1) { // loop forever
:=
:= if (switchPressed_flag) {
:= checkforuserinput();
:= printFreq();
:= CCP_1 = halfperiod;
:= }
:= }
:=}

Several problems:
Timer1 not setup, tris not set for output for port pin, pin never gets set high. Also 0.1Hz too low to work with in this approach.

Here's one method:

setup_timer_1(T1_INTERNAL | T1_DIV_BY_1);
With 4MHz crystal timer1 increments every microsecond.

setup_ccp1(CCP_COMPARE_INT);
enable_interrupts(INT_CCP1);
enable_interrupts(global);

Work with integers so 0.1Hz to 2kHz becomes 1 to 20000.
int16 freq_tenths;
int16 period;

In period setup routine, convert frequency to period:
if(freq_tenths == 0) freq_tenths = 1; // Never let go below 1
period = 20000/freq_tenths; // 20000 to 1 for 0.1Hz to 2kHz

1 needs to correspond to half the period at 2kHz ie 250uS.

CCP_1 = 250;

In CCP1 isr, increment a software counter and if it is equal to the calculated period then toggle port pin. Clear software counter for next time. Clear timer1 each interrupt.

#define SIGNAL PIN_C1 // Any valid port pin OK
short tog_fl;
int16 count;

#int_CCP1
CCP1_isr() {
if(++count == period) {
tog_fl = ~tog_fl;
if(tog_fl) output_bit(SIGNAL,1);
else output_bit(SIGNAL,0);
count = 0;
}
set_timer1(0);
}

Hope this helps.
Kenny


</font>
___________________________
This message was ported from CCS's old forum
Original Post ID: 144514990
david smith
Guest







Re: Frequency (Rate) Generation using CCP?
PostPosted: Tue Jun 03, 2003 3:56 pm     Reply with quote

:=I'm looking for a clean way to generate Frequencies
:=from about 0.1Hz to 2kHz
Thanks, your example helps. I have the code implemented now.
However, times are off; seems too much for sw overhead.
Currently I step integer frequencies:
Set to 10Hz: output is 6.8Hz
Set to 100Hz: output is 69.4Hz
Set to 1000Hz: output is 836Hz

By setting CCP_1 = 99, shouldn't I get a 100us interrupt?
Verified XTL; however, interrupt occurs at 150us (6.7kHz), and occasionally takes longer 158, 161Hz as viewed on 'scope.

Why is it too long by ~50\%?
Why is the interrupt occasionally delayed by 8-12us?
(There seems to be nothing occurring in the main loop.

==Section of Code==

//*************************************************
#int_CCP1
CCP1_isr() {
if (++int_cnt == halfperiod) {
freqOut = ~freqOut;
LED2 = freqOut;
int_cnt = 0;
}

LED3 = 1;
LED3 = 0; // check interrupt speed
SET_TIMER1(0);
}


//##############################################
// main()
//##############################################
void main() {
int8U n;

initializeStuff(); // initializes TRIS and misc items
setup_ccp1(CCP_COMPARE_INT);
setup_timer_1(T1_INTERNAL | T1_DIV_BY_1);
SET_TIMER1(0);
CCP_1 = 99; // this should be 100us interrupt?

splashScreen();

freqOut = 0;
srcN = 1;
selected_freq = 1000; // 100Hz
HzperMPH = 1.0;
int_cnt = 0;

computeStuff();
printFreq(1);
enable_interrupts(GLOBAL);
enable_interrupts(INT_CCP1);

while (1) { // loop forever
switchPressed_flag = (!swFreqDN || !swFreqUP || !swSRCDN || !swSRCUP);
if (switchPressed_flag) {
checkforuserinput(); // updates freq & halfperiod
}
}
} // end of main()

//****************************************
void computeStuff() {

freq = ((float)(selected_freq)+0.005) /10;
halfperiod = (int16U)( 0.5*10000.0/(freq/HzperMPH) ); // in 100us steps
}
___________________________
This message was ported from CCS's old forum
Original Post ID: 144515020
Kenny



Joined: 07 Sep 2003
Posts: 173
Location: Australia

View user's profile Send private message

Re: Frequency (Rate) Generation using CCP?
PostPosted: Wed Jun 04, 2003 3:02 am     Reply with quote

:=:=I'm looking for a clean way to generate Frequencies
:=:=from about 0.1Hz to 2kHz
:=Thanks, your example helps. I have the code implemented now.
:=However, times are off; seems too much for sw overhead.
:=Currently I step integer frequencies:
:=Set to 10Hz: output is 6.8Hz
:=Set to 100Hz: output is 69.4Hz
:=Set to 1000Hz: output is 836Hz
:=
:=By setting CCP_1 = 99, shouldn't I get a 100us interrupt?
:=Verified XTL; however, interrupt occurs at 150us (6.7kHz), and occasionally takes longer 158, 161Hz as viewed on 'scope.
:=
:=Why is it too long by ~50\%?
:=Why is the interrupt occasionally delayed by 8-12us?
:=(There seems to be nothing occurring in the main loop.
:=
:===Section of Code==
:=
:=//*************************************************
:=#int_CCP1
:=CCP1_isr() {
:= if (++int_cnt == halfperiod) {
:= freqOut = ~freqOut;
:= LED2 = freqOut;
:= int_cnt = 0;
:= }
:=
:= LED3 = 1;
:= LED3 = 0; // check interrupt speed
:= SET_TIMER1(0);
:=}
:=
:=
:=//##############################################
:=// main()
:=//##############################################
:=void main() {
:= int8U n;
:=
:= initializeStuff(); // initializes TRIS and misc items
:= setup_ccp1(CCP_COMPARE_INT);
:= setup_timer_1(T1_INTERNAL | T1_DIV_BY_1);
:= SET_TIMER1(0);
:= CCP_1 = 99; // this should be 100us interrupt?
:=
:= splashScreen();
:=
:= freqOut = 0;
:= srcN = 1;
:= selected_freq = 1000; // 100Hz
:= HzperMPH = 1.0;
:= int_cnt = 0;
:=
:= computeStuff();
:= printFreq(1);
:= enable_interrupts(GLOBAL);
:= enable_interrupts(INT_CCP1);
:=
:= while (1) { // loop forever
:= switchPressed_flag = (!swFreqDN || !swFreqUP || !swSRCDN || !swSRCUP);
:= if (switchPressed_flag) {
:= checkforuserinput(); // updates freq & halfperiod
:= }
:= }
:=} // end of main()
:=
:=//****************************************
:=void computeStuff() {
:=
:= freq = ((float)(selected_freq)+0.005) /10;
:= halfperiod = (int16U)( 0.5*10000.0/(freq/HzperMPH) ); // in 100us steps
:=}

Don't know what the problem is with your code is so I tried the "bare bones" one I posted.
Interrupt latency and other processing time will extend the period so what I have done is reduce the value in CCP_1 to compensate. Also the reciprocal calculation to get period probably explains the larger error at higher frequency.
Here are the results.
For testing I changed the variable freq_tenths and recompiled, rather than confuse the test with problems in a setting routine.

Without correction
Set frequency Period/actual frequency
0.1Hz 1.0460sec
1Hz 1.0460sec
10Hz 104.60mS
100Hz 10.467mS
1000Hz 949.5Hz (one second measurement time, good repeatability)
2000Hz 1886Hz (ditto)

With correction
0.1Hz 10.0195sec
1Hz 1.0020sec
10Hz 100.26mS
100Hz 10.026mS
500Hz 2.01mS
1000Hz 991Hz (one second measurement, good repeatability)
2000Hz 1968Hz (ditto)

Square wave looks pretty good on an oscilloscope, can't see jitter. Period measurement shows up some jitter at the higher frequencies, as would be expected.

Only difference from code I posted is crystal 16MHz, timer1 prescaler is 4 instead of 1 for 4 MHz.
Rounding has been added.

// Program generates a square wave on port pin
// With 16MHz crystal timer1 increments every microsecond.

#include <16F876.h>
#use delay(clock=16000000)
#fuses HS,PUT,NOWDT,NOLVP
#define SIGNAL PIN_B0 // Any valid port pin OK
#define CORRECTION 11 // Correction for execution time, interrupt latency
int16 freq_tenths;
int16 period;

short tog_fl;
int16 count;

#int_CCP1
CCP1_isr() {
if(++count == period) {
tog_fl = ~tog_fl;
if(tog_fl) output_bit(SIGNAL,1);
else output_bit(SIGNAL,0);
count = 0;
}
set_timer1(0);
}


void main() {
setup_timer_1(T1_INTERNAL | T1_DIV_BY_4);
setup_ccp1(CCP_COMPARE_INT);
enable_interrupts(INT_CCP1);
enable_interrupts(global);

// 1 needs to correspond to half the period at 2kHz ie 250uS.
CCP_1 = 250 - CORRECTION;
freq_tenths = 10000; // Change value here for testing, need setup routine for real app.

if(freq_tenths == 0) freq_tenths = 1; // Never let go below 1
period = (20000 + (freq_tenths/2))/freq_tenths; // Apply rounding by adding half denom.
while(1);
___________________________
This message was ported from CCS's old forum
Original Post ID: 144515024
R.J.Hamlett
Guest







Re: Frequency (Rate) Generation using CCP?
PostPosted: Wed Jun 04, 2003 3:26 am     Reply with quote

:=:=I'm looking for a clean way to generate Frequencies
:=:=from about 0.1Hz to 2kHz
:=Thanks, your example helps. I have the code implemented now.
:=However, times are off; seems too much for sw overhead.
:=Currently I step integer frequencies:
:=Set to 10Hz: output is 6.8Hz
:=Set to 100Hz: output is 69.4Hz
:=Set to 1000Hz: output is 836Hz
:=
:=By setting CCP_1 = 99, shouldn't I get a 100us interrupt?
:=Verified XTL; however, interrupt occurs at 150us (6.7kHz), and occasionally takes longer 158, 161Hz as viewed on 'scope.
:=
:=Why is it too long by ~50\%?
:=Why is the interrupt occasionally delayed by 8-12us?
:=(There seems to be nothing occurring in the main loop.
:=
:===Section of Code==
:=
:=//*************************************************
:=#int_CCP1
:=CCP1_isr() {
:= if (++int_cnt == halfperiod) {
:= freqOut = ~freqOut;
:= LED2 = freqOut;
:= int_cnt = 0;
:= }
:=
:= LED3 = 1;
:= LED3 = 0; // check interrupt speed
:= SET_TIMER1(0);
:=}
:=
:=
:=//##############################################
:=// main()
:=//##############################################
:=void main() {
:= int8U n;
:=
:= initializeStuff(); // initializes TRIS and misc items
:= setup_ccp1(CCP_COMPARE_INT);
:= setup_timer_1(T1_INTERNAL | T1_DIV_BY_1);
:= SET_TIMER1(0);
:= CCP_1 = 99; // this should be 100us interrupt?
:=
:= splashScreen();
:=
:= freqOut = 0;
:= srcN = 1;
:= selected_freq = 1000; // 100Hz
:= HzperMPH = 1.0;
:= int_cnt = 0;
:=
:= computeStuff();
:= printFreq(1);
:= enable_interrupts(GLOBAL);
:= enable_interrupts(INT_CCP1);
:=
:= while (1) { // loop forever
:= switchPressed_flag = (!swFreqDN || !swFreqUP || !swSRCDN || !swSRCUP);
:= if (switchPressed_flag) {
:= checkforuserinput(); // updates freq & halfperiod
:= }
:= }
:=} // end of main()
:=
:=//****************************************
:=void computeStuff() {
:=
:= freq = ((float)(selected_freq)+0.005) /10;
:= halfperiod = (int16U)( 0.5*10000.0/(freq/HzperMPH) ); // in 100us steps
:=}

Might I suggests a 'variant' on the approach.
As it stands, you are interrupting very fast. Unfortunately, this introduces a problem with latency on the responses.
The sequence at present is:
Physical interrupt.
Hardware latency (shouldn't matter in this context).
Latency in the handler code (this normally takes perhaps 8-12 instructions, depending on the processor, to save the registers)
Entry to your routine.
Then there is a variable time in your routine, till the timer is reset (since you have it after the test for the 'half period').

This latter is what is probably giving the variability in your timings. Hence this can probably be fixed by moving the reset of the timer, to the _start_ of the routine.

However even here, you will have to adjust for the fact that the timer will have allready incremented by several counts. I'd expect the timer to probably have got to perhaps 112 or so at this point.

So, you either have to 'tweak' the reset value to allow for this (set_timer1(12)), or read the current timer value, and subtract 100 from it. Unfortunately, the latter also has a latency, since with the 16bit counter, it takes a couple of cycles to read the parts, a few more to do the maths, and then a couple more to write it back...

The alternative, is to change the maths, to work with a count of 256, and just interrupt on the INT_RTCC counter, and do no adjustement to the counts, which will give an accurate interrupt every 256 processor cycles. Given the low frequencies you need, this might be quite acceptable.

Best Wishes
___________________________
This message was ported from CCS's old forum
Original Post ID: 144515025
david smith
Guest







Re: Frequency (Rate) Generation using CCP?
PostPosted: Wed Jun 04, 2003 11:27 am     Reply with quote

Got things working well. Thanks to both of you
for the clarifications on interrupt adjustments, etc.
and code tests.
___________________________
This message was ported from CCS's old forum
Original Post ID: 144515032
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