|
|
View previous topic :: View next topic |
Author |
Message |
John_Lintern
Joined: 30 Sep 2004 Posts: 14
|
How to output 2 squarewaves - which one can be phase shifted |
Posted: Tue Jan 11, 2005 10:32 am |
|
|
Please please please help me !!!
I am getting very frustrated trying to do something that seems so simple !!
I want to have two outputs, each giving a squarewave.
One output will be a reference squarewave.
The other output will be a squarewave of the same frequency, but can be phase shifted from 0 to 360 degrees.
Sounds simple enough... but it's taken me all day and Ive got no where !!
But the way I have tried to do it doesn't work !!
Does anyone have any idea's how to do this ?
For the end result I need two 50Hz squarewaves, one of which can be phase shifted from 0 to 360 degrees.
Or can anyone help with the program I have tried to write ?
I am using a PIC16C74B.
My Program
--------------
The idea was to supply a clock frequency (from a signal generator) which could be phase shifted.
So my program uses timer1 as a counter.
Then I use the compare interrupts CCP1 (for the reference squarewave) and CCP2 (for the phase shifted squarewave) to toggle an output.
I set the compare value of CCP1 to interrupt every 128 counts, which toggles an output for the reference squarewave.
I set the compare value of CCP2 to interrupt every 128 counts + a delay, which toggles an output for the phase shifted squarewave.
This means a full cycle (an equal high and low output) would be 256 counts.
Therefore, a clock input to timer1 would have to be 256 times faster than the required frequency of the squarewave (i.e. a 256kHz clock in gives a 1kHz squarewave output).
Then I use the 8-bit ADC to read an input from a pot - this pot controls the delay for the phase shift.
I tried adding the result of the ADC to CCP2 which works fine UNTIL the ADC reaches a value of 128.
At this point the frequency of the phase shifted squarewave doubles and sometimes shifts phase by 180 degrees (i.e. inverted).
I have tried all sorts of things to get it to work but can't.
See code below...
Code: |
#include "C:\Saved Work\Microchip\PhaseShifter\PhaseShifter.h"
unsigned int16 DELAY=0;
#int_CCP1
CCP1_isr()
{
disable_interrupts(INT_CCP1);
if(bit_test(*6,4))
{
output_low (PIN_B4);
}
else
{
output_high (PIN_B4);
}
CCP_1 = CCP_1+128;
enable_interrupts(INT_CCP1);
}
#int_CCP2
CCP2_isr()
{
disable_interrupts(INT_CCP2);
if(bit_test(*6,5)==1)
{
output_low (PIN_B5);
}
else
{
output_high (PIN_B5);
}
CCP_2 = CCP_1+DELAY;
enable_interrupts(INT_CCP2);
}
void main()
{
setup_adc_ports(ALL_ANALOG);
setup_adc(ADC_CLOCK_INTERNAL);
setup_psp(PSP_DISABLED);
setup_spi(FALSE);
set_adc_channel(0);
set_tris_a (0xFF); // set PORTA as intputs.
set_tris_b (0x00); // set PORTB as outputs.
set_tris_c (0x01); // set PORTC bit 0 as clock input for tmr1, all other bits as outputs.
output_low (PIN_B4);
output_low (PIN_B5);
setup_counters(RTCC_INTERNAL,RTCC_DIV_1);
setup_timer_1(T1_EXTERNAL|T1_DIV_BY_1);
setup_timer_2(T2_DISABLED,0,1);
setup_ccp1(CCP_COMPARE_INT);
setup_ccp2(CCP_COMPARE_INT);
enable_interrupts(INT_CCP1);
enable_interrupts(INT_CCP2);
CCP_1 = 128;
CCP_2 = 128;
set_timer1(0);
enable_interrupts(GLOBAL);
do
{
delay_ms(100);
DELAY=read_adc();
}
while(TRUE);
}
|
|
|
|
John P
Joined: 17 Sep 2003 Posts: 331
|
|
Posted: Tue Jan 11, 2005 11:10 am |
|
|
I'm not familiar with that exact part, and you didn't say what your clock frequency is, nor what resolution you need, but if it were a 16F877 at 20MHz, and if there weren't much else going on in the main() function, I'd deal with it by setting up a regular interrupt at 18000 Hz. The interrupt would increment a counter from 0 to 359 and then reset it.
The interrupt would set one output high at count = 0 and then low again at count = 180. It would also check the desired phase shift (0 < shift < 360) and set the other output high at (count == shift) and low at (count == shift+180), or if (shift >= 180), set it low at (count == shift-180). I hope I got the math right there! Anyway, you should get the idea.
Pretty straightforward, but 18KHz is a fast interrupt rate and might cause problems. Could you accept only allowing phase shifts at even-numbered degree settings, so the interrupt could be at 9KHz? If time is tight, you can also consider writing your own interrupt service routine and not push/pop as much stuff as CCS does, but you have to know what you're doing to make that work. |
|
|
bluetooth
Joined: 08 Jan 2005 Posts: 74
|
|
Posted: Tue Jan 11, 2005 1:29 pm |
|
|
This is an untested idea, and I don't know your clock speed, but it should work:
1. Init Timer 1 for internal clock @ 1 microsecond (this is a 16 bit timer)
2. Init Timer 2 for 10 millisecond interrupts (you can get close @ 4 MHz Fosc)
3. Enable Timer 2 interrupts
4. In the Timer 2 interrupt routine:
a. Toggle main output (gives 50 Hz output)
b. Load Timer 1 with (65536 - (phase delay in microseconds))
c. Enable Timer 1 interrupts
5. In the Timer 1 interrupt routine:
a. Set the phase-shifted-output to the same state as the main output
b. Turn off Timer 1 interrupts (not really necessary because it will get reloaded before it can interrupt again)
This should not use much processor time, might have some jitter, and might not be suitable depending on other requirements not stated.
Good luck... |
|
|
John_Lintern
Joined: 30 Sep 2004 Posts: 14
|
|
Posted: Wed Jan 12, 2005 3:36 am |
|
|
Thanks for your posts bluetooth and John P.
I have now got it working using the idea of John P.
I am using a PIC16C74B running at 20MHz (although the clock speed could have been changed to anything).
Phase shifting two squarewave outputs is all I need the PIC to do (I know its a bit of an overkill using this type of PIC!) - there is nothing else going on in the main program.
In case you are interested I have posted the code below.
The only slight glitch (which I can live with) is that when the pot is changed to adjust the phase shift, sometimes a pulse will be missed.
Any ideas why and how to resolve ??
Code: |
#include <16C74.h>
#device adc=8
#use delay(clock=20000000)
#fuses NOWDT,HS, PUT, NOPROTECT
unsigned int16 DELAY=0;
unsigned int16 DegreeCount=0;
unsigned int16 Delay_RE=0;
unsigned int16 Delay_FE=180;
#int_CCP1
CCP1_isr()
{
disable_interrupts(INT_CCP1);
if (DegreeCount==0)
{
output_high (PIN_B0);
}
if (DegreeCount==180)
{
output_low (PIN_B0);
}
if (DegreeCount==Delay_RE)
{
output_high (PIN_B1);
}
if (DegreeCount==Delay_FE)
{
output_low (PIN_B1);
}
if (DegreeCount==360)
{
DegreeCount=0;
}
else
{
DegreeCount++;
}
enable_interrupts(INT_CCP1);
}
void main()
{
setup_adc_ports(ALL_ANALOG);
setup_adc(ADC_CLOCK_INTERNAL);
setup_psp(PSP_DISABLED);
setup_spi(FALSE);
set_adc_channel(0);
set_tris_a (0xFF); // set PORTA as intputs.
set_tris_b (0x00); // set PORTB as outputs.
output_low (PIN_B0);
output_low (PIN_B1);
setup_counters(RTCC_INTERNAL,RTCC_DIV_1);
setup_timer_1(T1_INTERNAL|T1_DIV_BY_1);
setup_timer_2(T2_DISABLED,0,1);
setup_ccp1(CCP_COMPARE_RESET_TIMER);
enable_interrupts(INT_CCP1);
CCP_1 = 277;
set_timer1(0);
enable_interrupts(GLOBAL);
do
{
delay_ms(100);
DELAY=read_adc();
DELAY=DELAY*1.4117647;
Delay_RE=DELAY;
if (DELAY<180)
{
Delay_FE=DELAY+180;
}
else
{
Delay_FE=180-(360-DELAY);
}
}
while(TRUE);
}
|
|
|
|
John P
Joined: 17 Sep 2003 Posts: 331
|
|
Posted: Thu Jan 13, 2005 8:16 am |
|
|
Glad to have helped.
But there are a couple of items in your code that I'm dubious about.
You probably want to use fast_io for the port pins.
It looks as though your loop will actually count to 361, not 360, because you use both 0 and 360 as values for DegreeCount. Think of the values of the variable which can exist when the interrupt ends. You could solve this with a more elegant handling of the variable (and unlike a lot of single-line C constructions, this one is legible). Also, you don't need to disable and re-enable the interrupt.
Code: |
if (++DegreeCount >= 360) // Note defensive use of >= not ==,
// just in case variable gets over 360
DegreeCount = 0; // Reset 50 times per second
|
There's also this:
DELAY=DELAY*1.4117647;
DELAY is an INT16, and I don't know what the effect of multiplying it with a float will be, but it may not be what you really want. 7 digits of precision implies that you're looking for an accurate result!
delay_ms() won't work very well if there are frequent interrupts, and with a fast clock available, it's not needed. You could use the DegreeCount increment for this:
Code: |
bool tick; // This is a global variable
if (++DegreeCount >= 360) // This is in the interrupt
{
DegreeCount = 0; // 50 per second
if (++fiftieths >= 5)
{
fiftieths = 0; // 10 per second
tick = 1;
}
}
|
and then in main():
Code: |
do
{
if (!tick)
continue;
tick = 0;
...................more stuff
} while(TRUE);
|
|
|
|
|
|
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
|