PDA

View Full Version : Timers perhaps?



Doormatt
- 6th June 2007, 03:50
Here's one that's got me scratching my head so far.

I'm interfacing to an LED PWM chip that (as part of the display routine), needs a constant pulsetrain fed to one pin, and on a second pin, one pulse for every 4096 pulses of the first pin.

Now, the first part is easy, as I just enable clkout, and feed that to the chip. However, the second part is throwing me. I'll be the first to admit that the whole timer concept kinda throws me, but I have a feeling that's where the solution to this lies. I'd really like to avoid interrupts if at all possible, as it adds to the overhead, but if that's how this cat must be skinned, then so be it.

Here's my test code...it's cobbled together from sources, so don't nail me to the wall (please) if it really doesn't make sense.



@ device pic16F648A, intrc_osc_clkout, mclr_off, protect_off, lvp_off, wdt_off
DEFINE OSC 4
TRISB = 0 ' Outputs on
CMCON = 7 ' Turn all comparators to fully digital (needed??)
OPTION_REG = %10000011 '$84 assign prescaler to TMR0, set prescale xxx0PPP
TMR0 = 2 'add 2 to timer if you're anal about it starting exactly on the 1st cycle
INTCON = %10100000 '$A0 enable TMRO interrupt
pause 20


on interrupt goto clkreset
INTCON = %10100000 '$A0 enable TMRO interrupt

start:
goto start

clkreset:
PORTB.7=1
PORTB.7=0
TMR0 = 2 'correct timer for missed cycles?
INTCON = %00100000 '$20 Reset interrupt set T0IE, clear T0IF
resume


And so I'm (hopefully) clear, here's what the code would look like if I didn't need it to run in the background constantly.



start:
for I=0 to 4095
PORTB.7=1
PORTB.7=0
NEXT i
PORTB.6=1
PORTB.6=0
goto start

-Matt

skimask
- 6th June 2007, 05:22
An idea that might be even easier for you...

'setup code...
counter var word

main:
counter = counter + 1
portb.7 = counter.0 'portb.7 toggles at the rate of the LSB
portb.6 = counter.12 'portb.6 toggles at the rate of the LSB/4096
pauseus xxxx 'add your pause to get the rate down
goto main

The loop will always execute at the same speed.

Doormatt
- 6th June 2007, 05:33
Very interesting!

But sadly enough, I can't use it. I need the PIC to generate these two clock pulses independently of the rest of my program. The second chunk of code I had was just an example of what I need to accomplish.

I'm still reading, and from what I garner, I need to set the prescaler on one of the timers, and trigger it from the internal clock...or something to that effect. I'm honestly quite confused at this point.

Thanks for the bit trick there though! VERY cool!

skimask
- 6th June 2007, 05:44
Very interesting! But sadly enough, I can't use it. I need the PIC to generate these two clock pulses independently of the rest of my program. The second chunk of code I had was just an example of what I need to accomplish. I'm still reading, and from what I garner, I need to set the prescaler on one of the timers, and trigger it from the internal clock...or something to that effect. I'm honestly quite confused at this point. Thanks for the bit trick there though! VERY cool!

How fast does that bit on portb.7 have to toggle? What freq do you want?

Doormatt
- 6th June 2007, 05:50
Let's abstract for a sec.

I need two pins, call them A and B.

If Port A is pulsing at 4096Hz, then Port B needs to pulse at 1Hz. (In the real world, Port A needs to be a minimum of 245Khz)

But, I need to do this in the background.

Ideally, port A would be driven by the clock out, and port B would pulse once for every 4096 pulses coming out of the external clock.

Does that make more sense (sorry if I'm being confusing!)

-Matt

skimask
- 6th June 2007, 05:54
Let's abstract for a sec.
I need two pins, call them A and B.
If Port A is pulsing at 4096Hz, then Port B needs to pulse at 1Hz.
But, I need to do this in the background.
Ideally, port A would be driven by the clock out, and port B would pulse once for every 4096 pulses coming out of the external clock.
Does that make more sense (sorry if I'm being confusing!)
-Matt

Ok, so you need the fast pin at 4096Hz and the slow pin to pulse at 1Hz? Yes?
Is that what you actually need?
It is definitely a doable project, interrupt driven pulses and all...just need to know your exact numbers.

Doormatt
- 6th June 2007, 05:59
Yay!

It's doable!

I need One pin at a minimum of 245Khz, and then the other pin at a 1:4096 ratio.

As long as that one pin is over 245Khz, I'm happy.

(Detail warning)
Basically, the chip I'm controlling has 16 PWM outputs, each with 4096 brightness levels. You serially clock in your data, and then feed it the aforementioned pulse train. That pulse train makes it step through the brightness levels, and then the single pulse resets the display chip. I need a minimum of 247Khz, as I have to update the display 60 times a second, and with 4096 levels, thats 60*4096Hz = 245Khz.

Thanks SO much for helping me with this!

skimask
- 6th June 2007, 06:12
I need One pin at a minimum of 245Khz, and then the other pin at a 1:4096 ratio.

Woof!
You initially said 4096hz, that's doable...
245khz is a bit much...still doable, probably not at 4mhz though...

Quicky explanation: Take your 4mhz clock rate, divided by 4 because of the instruction rate, gives you 1mhz. You need 250khz, that gives you 4 instructions per tick. That's just enough for the jump to the interrupt and the resume (at 2 cycles per instruction), not including any PBP overhead. If you were able to run your PIC at 40Mhz, that would give you 40 instructions per tick to play with, actually 36 after the jump/resume (again, not including any PBP overhead). Not a lot to work with...since you'd have to reload TMR0 with a reload value to get it to kick the overflow interrupt at the 250Khz rate. So, you can't really use a PIC to keep track of an input pin reliably and to output a pulse unless that's ALL it's doing, and you have really tight code. Otherwise, you might miss a pulse here and there...

EDIT: Disregard the paragraph that previously talked about the 74LS92...

Take that same idea above and feed 3 cascaded 4 bit counters and use the high bit of the high counter as your 1/4096 pulse generator. (for some reason I was thinking of the above paragraph in terms of divide-by-12 vs. divide-by-12-bits.

Actually, I think this is the first time I've suggested using logic chips instead of a PIC, instead of the other way around.

Doormatt
- 6th June 2007, 06:22
Riiiight! I forgot that the PIC is "running" at 1Mhz, not 4.

After reading over the datasheet a few more times, I think I've wrapped my head around the timers.

This code now seems to work (with the inclusion of a enable and disable), and should be outputting the right ratios (With the full output coming off of Clkout, and the rest coming out of B.7).



@ device pic16F648A, intrc_osc_clkout, mclr_off, protect_off, lvp_off, wdt_off
DEFINE OSC 4
TRISB = 0 ' Outputs on
CMCON = 7 ' Turn all comparators to fully digital (needed??)
OPTION_REG = %10000011 '$84 assign prescaler to TMR0, set prescale to 1:16
TMR0 = 2 'add 2 to timer if you're anal about it starting exactly on the 1st cycle
INTCON = %10100000 '$A0 enable TMRO interrupt

on interrupt goto clkreset

start:
goto start

clkreset:
disable
PORTB.7=1
PORTB.7=0
TMR0 = 2 'correct timer for missed cycles?
INTCON = %00100000 '$20 Reset interrupt set T0IE, clear T0IF
enable
resume


I don't have my scope handy (left it at work), so I can't measure the output, so I'm trying to figure out what frequency they're actually firing at. I know someone who's gotten this to work on the PIC I'm using (16F648A) with the internal clock, so I know this isn't a hopeless case...maybe there's something wrong with my math somewhere...math was always my weak point.

Thanks again for your ongoing patience and help!

skimask
- 6th June 2007, 06:43
With the 1:16 prescale, you should have an output pulse at 244Hz (4mhz / 4 / 16 / 256 ), if my math is correct. And your portb.7 high pulse will only be 1us, maybe 2us. Disable the prescale, and you should get 3906.25Hz on the fast pin and if you rewrite the code to include the slow pin, you should get a bit slower than 1hz on the slow pin (just for testing of course). The way to speed this up is to preload tmr0 with a higher number to get the overflow interrupt to kick in faster. Problem with this is that once you get up to a certain point, you won't have enough left over cycles between interrupts to do anything useful, except jump and resume into and out of the interrupt routine. And I'm talking about using the Instant Interrupts method, not the PBP On Interrupt method. With the PBP On Interrupt method, you use even more cycles due to overhead when using PBP.

And the tmr0=2 to correct for missed cycles? That won't correct for missed cycles, all that will do is reset tmr0 back to '2'. That 'missed cycle correction' is only used when you are preloading tmr0. So, say you want to preload tmr0 with 100, you'd actually load it with 102 since tmr0 will stop for a couple of cycles while it's being reloaded.

If you're looking to update at 60hz to provide a visually pleasing effect, so you won't see a flicker, I really wouldn't worry about that until you get below about 30hz. When I'm messing with LEDs, I (or anybody I show my projects to or whatever it is I'm working on) can't really see a flicker down to about 25hz, and only then if I look at the LEDs at certain angles.

But if you're hellbent on getting this to work (and I think it can work), you've got to get a fast PIC (40Mhz would be optimal, but the '648 is limited to around 20Mhz, might be fast enough, I'm just thinking an 18F series @ 40Mhz would give you the extra muscle to get the job done better and leave you more room to play with), make your code tight, and one the thing that'll help you out a bunch is to download and make use of those 'Instant Interrupts'. PBP's On Interrupt could be used to get the code working and debugged, then you could switch over to the other interrupt method, but I think you'd be better off with the Instant Interrupt's from the beginning.

Doormatt
- 6th June 2007, 06:52
Forgive me if I'm wrong, but I think you might be missing something (or more likely I haven't explained it properly).

I'll let you know how I think this works, and you can tell me where I'm missing the boat.

With this code, Pin A.6/Clkout is putting out a 1Mhz stream.
With the selected prescale, the interrupt will fire on every 4096 ticks of the main clock.
When the interrupt fires, it sets port B.7 high, then low.

Effectively, this gives me a 1Mhz signal on A.6, and a 244Hz signal on B.7.

I'm not trying to generate the "fast" signal, as that's done by Clkout. I'm just trying to generate the "slow" signal.

Do I have this right?

My apologies for the poor explanations.

skimask
- 6th June 2007, 13:32
With this code, Pin A.6/Clkout is putting out a 1Mhz stream. With the selected prescale, the interrupt will fire on every 4096 ticks of the main clock. When the interrupt fires, it sets port B.7 high, then low. Effectively, this gives me a 1Mhz signal on A.6, and a 244Hz signal on B.7. I'm not trying to generate the "fast" signal, as that's done by Clkout. I'm just trying to generate the "slow" signal.

Aha! You see, the problem is, you didn't tell me that you were going to use ClkOut for one of your pulse sources!
Now this makes sense...
Yes, this should work exactly as you describe.

Doormatt
- 6th June 2007, 16:02
*Grin*

Sorry! It was one of those cases of too much information trying to come out at once.

Thank you again for working through this with me. It was greatly appreciated.