|
|
View previous topic :: View next topic |
Author |
Message |
david smith Guest
|
Frequency (Rate) Generation using CCP? |
Posted: Mon Jun 02, 2003 7:34 pm |
|
|
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
|
Re: Frequency (Rate) Generation using CCP? |
Posted: Tue Jun 03, 2003 1:02 am |
|
|
<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? |
Posted: Tue Jun 03, 2003 3:56 pm |
|
|
:=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
|
Re: Frequency (Rate) Generation using CCP? |
Posted: Wed Jun 04, 2003 3:02 am |
|
|
:=:=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? |
Posted: Wed Jun 04, 2003 3:26 am |
|
|
:=:=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? |
Posted: Wed Jun 04, 2003 11:27 am |
|
|
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 |
|
|
|
|
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
|