Hehe, well here's something that definitely is real.. my working PIC! Here we are at 2Khz PWM : And here it is again at 20Khz The pictures were taken with a 1/4s exposure and they're consistant with what i could see. I may try a few more frequencies tomorrow and see if i can get away with something lower - the lower the PWM frequency, the more cycles I'll have to do other stuff. Looking promising though, I think you'll agree. Thanks very much for all the help I've had getting to this point - I couldn't have done it without you all!!
If your having problems adjusting the PWM timebase (oposed to the interval) and your using my TMR0 method, easy way is not only the TMR0 pre-scaler, but also the value we set to TMR0 at the end of the ISR (interupt service routine).
Right now, I just wanted to test to see what the minimum useable frequency would be. If I can find out how small the maximum time the unit will be off for (low part of the PWM cycle) then I know how fast I need the base PWM frequency to be so that I don't get any flicker... that make sense?
Right, I took some time to go through TheAnimus's code for the PWM bits and I just wanted to check I'm following it right. Here's my commented version : Code: ORG 0x04 ; Load 64 (FF-C0) into the TMR0 register so that after 64 cycles, this section of code is called again MOVLW 0xC0 MOVWF TMR0 CLRF _TEMP_GPIO ; Increment our loop counter and place the result in F INCF _LOOP_COUNTER, F ; Load the loop counter into W MOVF _LOOP_COUNTER, W ; Add whatever's in _LED0 to W ADDWF _LED0, W ; If C(arry) in the status register is 0, skip the following instruction ; So if _LED0 + W > FF we skip the next instruction BTFSC STATUS, C ; set bit 0 of _TEMP_GPIO BSF _TEMP_GPIO, 0 ; If C(arry) in the status register is 1, skip the following instruction BTFSS STATUS, C ; clear bit 0 of _TEMP_GPIO BCF _TEMP_GPIO, 0 ; Same as the previous block, but for _LED1 MOVF _LOOP_COUNTER, W ADDWF _LED1, W BTFSC STATUS, C BSF _TEMP_GPIO, 1 BTFSS STATUS, C BCF _TEMP_GPIO, 1 ; Same as the previous block, but for _LED2 MOVF _LOOP_COUNTER, W ADDWF _LED2, W BTFSC STATUS, C BSF _TEMP_GPIO, 2 BTFSS STATUS, C BCF _TEMP_GPIO, 2 ; Load _TEMP_GPIO into GPIO MOVF _TEMP_GPIO, W MOVWF GPIO ; Clear the TOIF bit of INTCON BCF INTCON, T0IF RETFIE That look about right? The only part I wasn't sure about was the 'T0IF' bit.. ? Now as for the timing (I'm assuming I'm running on the internal oscillator @ 8Mhz for the following calculations)... The code about executes every 64 cycles, and we're counting up to 8 bits of luminosity. 8x64 = 512 cycles for the period of one PWM cycle. 512 - 64 = 448 cycles of 'off time' between pulses, which will give a gap between pulses of 0.000224s. If I swing my Poi at ~10m/s that's going to be a gap of a little over 2.5mm between flashes (at the lowest brightness setting). I've not tested it, but I suspect that would probably be enough to be visible... I had a look at the code and checked the number of cycles for each instruction of your loop, which gives us the following : Code: Line cycles 1 1 MOVLW 0xC0 2 1 MOVWF TMR0 3 1 CLRF _TEMP_GPIO 4 1 INCF _LOOP_COUNTER, F 5 1 MOVF _LOOP_COUNTER, W 6 1 ADDWF _LED0, W 7 2 BTFSC STATUS, C 8 skipped BSF 9 2 BTFSS STATUS, C 10 1 BCF 11 1 MOVF _LOOP_COUNTER, W 12 1 ADDWF _LED1, W 13 2 BTFSC STATUS, C 14 skipped BSF 15 2 BTFSS STATUS, C 16 1 BCF 17 1 MOVF _LOOP_COUNTER, W 18 1 ADDWF _LED2, W 19 2 BTFSC STATUS, C 20 skipped BSF 21 2 BTFSS STATUS, C 22 1 BCF 23 1 MOVF _TEMP_GPIO, W 24 1 MOVWF GPIO 25 1 BCF INTCON, T0IF 26 2 RETFIE Total 30 NOTE : only BCF or BSF is called so one is skipped That leaves ~34 cycles for my own code (how much does an interrupt use up?) right? Just wanted to double-check I've got my head around this
okay a couple of points. first sorry i missed ur post yesterday, my belovied desktop, has died, the surge protector is just laffing, and the UPS, well, my farther when he decided to use my PC didn't plug it in via that. Last time i leave something there. Now, first off a little miss-understanding with this: Code: ; Load the loop counter into W MOVF _LOOP_COUNTER, W ; Add whatever's in _LED0 to W ADDWF _LED0, W ; If C(arry) in the status register is 0, skip the following instruction ; So if _LED0 + W > FF we skip the next instruction BTFSC STATUS, C ; set bit 0 of _TEMP_GPIO BSF _TEMP_GPIO, 0 ; If C(arry) in the status register is 1, skip the following instruction BTFSS STATUS, C ; clear bit 0 of _TEMP_GPIO BCF _TEMP_GPIO, 0 MOVF _LOOP_COUNTER, W this loads W with loop counter, which we then add a value of _LED0 too, this gives so if it overflows, cuasing the carry flag to be set, we know that (LED0 + loop counter > 0xFF) BTFSC Bit Test File, Skip [if] Clear. This means the line below is skipped if its clear (ie not set), so that means you're comment is wrong (its if NOT). Because we clear _TEMP_GIPIO at the top, there is no need for the second test (its already clear!). u said ur not sure about. Code: ; Clear the TOIF bit of INTCON BCF INTCON, T0IF RETFIE In PIC interupts are exectued when the flag is set (this is checked during part of the Fetch-Execute-Decode stage) when the flag is there the ISR (Interupt Service Routine, the code at 0x04) is called. So first we need to clear the flag for the timer o, interupt. then retfie (which actually clears INTCON, GIEF or somthing, then return). Also an 8mhz PIC, will in fact only have a 2mhz instruction cycle (how long it takes to perform a instruction. I am sorry i am too tired to check ur maths. Maby someone else can. But remeber a BTFSC or similar always takes 1 instruction + the other one if true, or a NOP if its skipped. so its always at least 2 cycles (if there was a call foobar say, it would be 3 if true, 2 if false). hope that helps, any further problems please post, i should be fealing better tommorow and will be able to look at numbers without fealing faint!
Also forgot to mention. We should save the STATUS, and W registers. thats all shown in the datasheet. but the idea is that way your interupt can occur anywhere in you're code and not mess it up, processing will return to the point before, and act as normal!
Ah yes, I see what you mean about the BTFSC section. 'So if _LED0 + W > FF we skip the next instruction' should have read 'So if _LED0 + W < FF we skip the next instruction'. When you say 'save the STATUS and W registers' you mean at the beginning of the ISR? ..and then restore them before we return right? I was aware of the 2MHz instruction cycle and the timing of the BTFSC.. I just double-checked the maths and I'm fairly sure it's right. Thanks very much for the help / advice - hope you're feeling better tomorrow! I'll try adding the code into my existing bits and see if I can get it working...
yup dead right with the save and restore. also by taking out the BCF _TEMP_GPIO stanza, we save 2 precious cycles per LED! i only put that in the first one as a aid to understanding, little slimey cheats like this, aren't easy to follow but in this case provide us with prescious CYCLES!
When I looked through the code I'd wondered if it was necessary I just programmed the chip with the latest asm file, but no joy - no lights or anything. I'm not sure about my interrupt configuring - the registers on the 18F series are not the ones you specified : Code: BSF INTCON, TMR0IE ; Timer0 Interupt Enable I think I fudged that one right, but I'm not too sure about this one particularly : Code: MOVLW 0xC0 MOVWF TMR0L I couldn't find the TMR0 in the documentation, but I remember reading that TRM0 could be configured as 16 or 8 bit so I guessed it'd use the low end of the 16 bit word? Do I need to specify that it's 8 bit somewhere, and if so, any idea where? I'm sure I've just missed something obvious, so if your machine and yourself are better tomorrow, perhaps you could take a peek for me? Thanks again for all the help. I feel like I'm getting somewhere - ASM is feeling more comfortable by the day! Anyways, I'm shattered now, so time for sleep for me!
There are several registers associated with Timer0: Code: movlw b'1100yxxx' movwf T0CON Where xxx is the prescaler value: (000 = 1:2, 111 = 1:256) And y sets whether a prescaler is assigned (y=0) or not (y=1). You have now enabled Timer0, set up Timer0 as an 8-bit timer and instructed TMR0 to increment on each internal instruction clock cycle. TMR0L is the Low Byte Register which you may write your TMR0 value to if you need to modify it. Also ensure you've set up the INTCON register to enable global and the TMR0 interrupts: Code: bsf INTCON,GIE bsf INTCON,TMR0IE Sorry I missed this thread yesterday. Don't know how
Also, the TMR0 flag is named differently on the 18F series of PIC's. Put this at the start of your ISR to preserve Working and Status regs: Code: ISR_HANDLER ; FIRST PRESERVE W AND STATUS REGISTER MOVWF W_TEMP ; SAVE OFF CURRENT W REGISTER CONTENTS SWAPF STATUS,W ; MOVE STATUS REGISTER INTO W REGISTER MOVWF STATUS_TEMP ; SAVE OFF CONTENTS OF STATUS REGISTER And this at the end of your ISR: Code: INT_EXIT BCF INTCON,TMR0IF ; RESET THE TMR0 INTERRUPT FLAG SWAPF STATUS_TEMP,W ; RETRIEVE COPY OF STATUS REGISTER MOVWF STATUS ; RESTORE PRE-ISR STATUS REGISTER CONTENTS SWAPF W_TEMP,F SWAPF W_TEMP,W ; RESTORE PRE-ISR W REGISTER CONTENTS RETFIE ; RETURN FROM INTERRUPT
In the 18 series, TMR0, is in fact 16bit. mapped accross TMR0L and TMR0H so to force 8bit mode, u must set up T0CON as Stevey said. but more to the point, whats with: Code: ; Increment our loop counter by 32 (0x40) to give us 8 bits resolution MOVLW 0x40 ADDWF _LOOP_COUNTER ?! we just need to INCF _LOOP_COUNTER. increasing it by 40h, will make it NEVER equal 0xFF (needed for b'00000001' to caus an overflow). also as my code was written for a 16 series, theres no need ot bank switch (your on a 18series).
Okay, I'll try the settings you suggested - cheers guys. The odd looking code was a last minute hack I tried because I was under the impression that you ran 256*64 cycles for one PWM period (which corresponds to a gap of about 8cm if the POI's moving at 10m/s). Now since we're only using 8 bits of resolution, I thought I could speed up the whole loop by incremented 32 bits at a time.. ..Bear in mind I was pretty tired when I was trying this and I'm at work atm , so I've not had a chance to double-check my thinking / coding. Does that make sense? Have I over-simplified / missed something? [edit]Wait, I think I might understand what you're saying.. is b'00000001' the brightest setting? If so, I think I understand why it won't work - with my method, you'd get _LOOP_COUNTER going ...192, 224, 0... so adding the LED value won't work anymore![/edit]
Code: ; Increment our loop counter by 32 (0x40) to give us 8 bits resolution MOVLW 0x40 ADDWF _LOOP_COUNTER Also, how is incrementing by 32 the same as adding 40h? .32 = 20h
lol, must've been the windows calculator (or my fat fingers) doing something wrong.. 32(decimal) = 20(hex) ..
the max interval is indeed 256*64. If you want to reduce this, then the only number you can reduce is the 64. (you could take this out of the interupt, and just have it forever looping) or just whack in a 10Mhz oscillator on PLL (so it clocks at 40mhz, machine cycle 10mhz) and have it down to 1.5cm on the dimiest cycle. erm the b'0000 0001' is the dimmest one, because its only when _LOOP_COUNTER = 0xFF will that cuas an overflow, so thats once in every 256*64. second dimest is b'0000 0010', (2 in dec) so _LOOP_COUNTER >= 0xFE thats 255*64 3rd dimmest is b'0000 0100' (4 in dec) so _LOOP_COUNTER >= 0xFC thats 253*64 The problem with this method, is that the time base is still 256*64. for instnace, when u double the brightness the timebase could go to 128*64. If this is too slow, there is another way, but getting the code fast enough might be fun!
Unfortunately, I didn't have time to try your suggestions last night and I'm busy packing for a holiday next week tonight.. then holiday for a week and a course for work the week after. I'll give all this a try when I'm back from that - thanks for all the help and suggestions guys!! It's very much appreciated.
Right, back from my hols - still haven't had time to try the suggestions, but while I was away I was thinking about power supplies. Whatever happens - whether I'm driving luxeons or LEDs, using PWM hardware or software, for the amount of current I'll need to drive, the pins of the PIC won't be enough. As a result, I need some sort of FETs - any suggestions? I'm not really sure what to look for? Also, I was wondering if using a DC-DC converter (something like this) would be a good idea to drive the PIC would be a good idea? That way, even when the voltage of the batteries drops down to say 2v, the PIC will still be able to operate.. I'm sure I've heard of people using those to drive LED torches - any idea if this would work here? Again, I'm wishing I'd done more electronics at A-Level All suggestions welcome!
Any NPN transistor with a gain greater than about 15, and max current of 500mA would be fine for driving a single luxeon. N-Channel MOSFET's would work fine aswell. Your choice of transistor isn't really critical - 2N2222A (800mA, Gain 100) would do the job fine.