PIC 50Hz square

Status
Not open for further replies.

barkerben

New Member
Hi,

I'm building a power inverter that needs a high frequency clock at about 25Khz and a low frequency one at 50Hz. I have access to various crystal speeds, and as the choice of high frequency is not vital I can see how by choosing prescaler values I can use TMR0 to give me a suitable square wave to control my bridge. However, the 50Hz output is causing more problems - it needs to be exact, but crystal speed divided by 4 divided by 256 divided by prescaler doesnt equal 50 for any crystal/prescaler combination! I realise I could use a simple op-amp and cap/resistor network to get a 50Hz square wave, but I'm hoping to be able eventually to get the PIC to give me a nice quasi sine wave (square wave with deadtime). However, If I can't get the frequency right this is a non-starter!As is probably evident, I am very innexperienced with the PIC, so any advice would be very welcome.
 
I don't think you really need to get exactly 50hz. An output within 1% would be good enough. A crystal controlled PIC will give a closer frequency match than any op-amp based RC oscillator. Nevertheless, it is possible to get exactly 50Hz. Using the low cost PIC16F627:

First, initialize timer 2 control register T2CON, to 1:1 prescale and 1:2 postscale+TMR2ON = b'00001100'.

Second, setup PIC to generate a 25Khz PWM signal. Assuming, you use a 4Mhz PIC16F627, initialize the period register PR2 with the value D'39'. The period is therefore equal to (39+1)*4*250nsec=40usec. Configure the CCP1 module for PWM operation. Follow the steps outlined in section 10.3.3 of the PIC16F627 datasheet.

Third, enable timer2 interrupts by setting TMR2IE bit in PIE1 as well as GIE and PEIE in INTCON. Interrupts will be generated every 80usec because of the 1:2 postscale. In the interrupt service routine (ISR), toggle any desired output pin every 125 interrupts to generate the 50Hz signal. The PWM duty cycle can also be modulated during the ISR.

The interrupt service routine is limited to 80 instructions when using a 4Mhz crystal but this may be sufficient. Or you can use a faster crystal up to 20Mhz. A faster PIC will also give a finer resolution PWM signal.
 
was just thinking of something.

Say I want a 100hz signal purely based on tmr0. Say I scale tmr0 this way: 4Mhz/4/256 = 3906hz. So if I count tmr0 a hundred times. that would be 100hz? or 3906/100 = 39 tmr0 pulses.

Edit: 1/3906 = 256µs - this makes sense as one instruction = 1µs and prescaler is 256 and it takes this long for a timer overflow.

So, 1/100 = 0.01s (period) of 100hz, and 0.01/256x10^-6 = 39.06.

Sweetness.....

So what I've been doing so far is taking 105(pulses) too = 105hz >> WRONG


1/(105*256x10^-6) = 39hz, :lol: :lol:

This solves my problem
 
Thanks - one more question

Thanks for that. One quick question though:

Interrupts are generated every 80 uS, and the interrupt routine can be used to send whatever pins wanted to high or low as needed. While the interrupt routine is being processed, is the counter still counting ready for the next interrupt, or does the interrupt routine stop this. If the counting stops when the interrupt is being processed, then presumably the length of the interrupt code will affect the frequency...? Sorry to be so slow, your help is much appreciated.

Cheers,

Ben
 
Re: Thanks - one more question


I've not played with timers very much, but as far as I'm aware the timer keeps going, but it's interrupt enable flag is turned OFF - part of your interrupt routine should turn the flag back on. Usually, the interrupt routine will also reload the required start value back into the timer - although if the timer is simply free running, you don't need to do this.

Here's the line from a timer2 routine which resets the flag:
Code:
        BCF PIR1,TMR2IF 	; Clear flag and continue.
 
Re: Thanks - one more question


The timer2 counts up continuously and automatically resets to zero once timer0 is equal to the PR2 value regardless whether interrupts are enabled or not. It proceeds on counting while servicing interrupts. The only way to stop timer2 is to clear TMR2ON bit.

TMR2IE is not disabled (cleared) by the interrupts. GIE is cleared during interrupts but is restored automatically with the RETFIE instruction. You only have to clear TMR2IF in the ISR as Nigel has suggested.

This is a more accurate way of generating periodic interrupts rather than using timer0 and reloading timer0 in the ISR. Writing to timer0 to set the delay clears the prescale registers and this results in small errors in timing. This old technique applies to the PIC16F84 because it's the only timer available.
 
Unfortunately I only have access to a PIC16F84A, Maplins seem to be out of any others for the moment. I am fiddling with a piece of code, but have a question. I want to, if possible, generate a number of signals:

a high frequency square wave (exact f not important, but 20-30Khz) and its inverse (although could invert outside the pic)

2 50Hz square waves with duty cycles and phase shifts such that subtracting the two gives a quasi sine wave (a 50Hz square wave with deadtime)

Am I right in thinking that I can't, for instance, set pin 6 high, but have to set the entire port to a value? This seems to complicate things, as to change just one pin I have to take a copy of current outputs, modify the desired bit, then set the new outputs...
 

You can! Just do...

BSF PORTB,6

You may need to modify that for whatever pin 6 is on your PIC.
 
davepusey said:
You can! Just do...

BSF PORTB,6

You may need to modify that for whatever pin 6 is on your PIC.

Exactly, and to generate it's inverse (on pin 5) just do this:

Code:
BSF   PORTB, 6
BCF   PORTB, 5
.
.
;and later on, for the other change.
.
.
BCF   PORTB, 6
BSF   PORTB, 5

As easy as that.
 
And again ...

Sorry about this - thanks for being so helpful.

I've been trying to calculate the values to put into the PWM duty cycle registers, and getting in a bit of a mess. I am looking for 50% duty cycle,
and have to fill the whole of CCPR1L and bits 4 and 5 of CCP1CON.

Duty_cycle = registervalue * Tosc * TMR2_prescale

registervalue= Duty_cycle/(Tosc * TMR2_prescale)

registervalue=(1/2*(1/25,000))/(4,000,000 * 39)

=1.28 * 10^-13

Somethings gone wrong somewhere ...!
 
Final posting..... (promise!)

Hello again. I've put together a piece of code that I hope will make my PIC output two square waves, at 50Hz and at 25Khz - thanks to Nigel for advice on that!

The only thing I can't work out as yet is what to initialise the duty cycle registers to. I won't have a chance to try out the code until Monday, but if any kind hearted person wants to have a look at my handiwork, it is up at:

**broken link removed**

This is my first PIC program, so I am slightly worried it might be completely wrong, and any critiscism would be very welcome!

Cheers,

Ben
 
I think I've found out how to initialise the duty cycle reg.

If:

PWM duty cycle = (register value) * Tosc * (TMR2 prescale value)

At 25Khz, duty cycle = T/2 = (25000*2)^-1

Tosc = 1/(4,000,000)

TMR2 prescale = 39

thus register value = 200

200=b'11001000'

So bits 4 and 5 of CCP1CON = 0 and the rest of the sequence fills CCPR1L
 
I had a quick glance at your code and I would like to recommend a few optimizations:

Code:
isr	
	decfsz	interrupt_count, F	
	goto	end_isr
;
	movlw	d'125'	
	movwf	interrupt_count	
;
	movlw   slow_toggle         ;slow_toggle is defined under 'constants' 
	xorwf   PORT_B,W	      ; get the inverse of the slow toggle pins and save in W reg
	andwf	PORTB,F         ; First turn OFF the pins with "0" bits
	nop		     ; insert as many dead instructions as necessary
	iorwf	PORTB,F         ; Then turn ON the pins with "1" bits       

end_isr	bcf	PIR1,TMR2IF   	;end isr, resetting interrupts
	retfie
 
Thanks

Thanks

I couldn't use the nop methid to get my phaseshift as I would need to many to fi in my interrupt (about 3000). However, by using another variable initialised at startup to a positive value, but decremented just like my interrupt counter, I can arrange for them to reach 0 at different times, and so produce my out of phase signals.

The modified code is up at:

**broken link removed**

Thanks for all your help - hopefully when I get this code to a PIC it will
work... in the end!


Cheers,

Ben
 
Oh dear...

I ran the code through a simulator, and for some reason interrupts never seem to occur. I cant work out what is going wrong... (hair pulling time)!
 
Almost there ...

Ok, I've got the interrupts to run, but the pwm doesn't seem to be
working, and I am having trouble with my 50Hz output. Does anybody have any thoughts?

**broken link removed**

Cheers,

Ben
 
You don't HAVE to go straight off the timer value!

Here you go- you put in a 20MHz crystal. Thus you have a 5MHz instruction cycle. Assuming you've got 2 interrupts to make the high & low phases within a 50Hz cycle, you need 50k clks per phase.

Go with TMR0 configured as an 8-bit, and give it a 16x prescalar so you have interrupts every 4096 cycles.

Use a 16 bit register in your code, initialize to 0 only at startup.
TMR0 ISR:
reg16 += 4096;
if(reg16>50000){
take your output action.
reg16-=50000;
}

The average frequency will be 50Hz, but the individual transitions will vary from where they're supposed to be by as much as 8% of a phase. That does create some RFI outside of a tight band. If you want better, you can use a smaller prescalar and do more interrupts.
 
Thanks

Thanks - I think I've got it working now. I ran it through the MPlab simulator, and the PWM output seemed to work. The 50Hz output from the
ISR didn't work, but apparently MPLab can't simulate interupts correctly?

Running the interrupt code on its own seemed to work (although of course not at the correct frequency). The PIC I ordered should arrive tommorow, so I'll get a chance to try it in anger.

Thanks everyone for all the help and ideas,

Ben
 
Status
Not open for further replies.
Cookies are required to use this site. You must accept them to continue using the site. Learn more…