Het lijkt de laatste tijd hier op CO wel een explosie van vragen over rgb leds, maar deze ben ik nog niet tegen gekomen.
Voor een project lijkt het me wel mooi om 6 (of misschien 8, maar iig minimaal 6) rgb leds afzonderlijk aan te sturen door een pic met hardware pwm. De code voor 1 ledjes heb ik nu werkende, maar voor 6 stuks krijg ik denk ik redelijk wat problemen met de stroom/lichtsterkte.
6 rgb's geeft in totaal 18 ledjes. Omdat ik echt van elk ledje afzondelijk de lichtsterkte wil regelen, zal één ledje in een bestcase scenario 1/18 deel van de tijd aan staan. Om toch nog even veel licht opbrengst te hebben zou ik in theorie dus 18*20=360mA door elk ledje moeten sturen. Helaas kunnen de leds maximaal als peak waarde 100 of 150mA hebben (zie hier).
Is er een gemakkelijke manier om toch 6 rgb leds afzondelijk aan te sturen zonder dat ik er zoveel stroom doorheen moet jagen en zonder verlies in lichtopbrengst, of niet? Een manier zou natuurlijk zijn om een soort van slave pic's te gebruiken die elk iets van 2 rgb ledjes aanstuurt, maar dat vindt ik toch ook wel zonde...
Alvast bedankt!
Wouter
Tsja... Voor een AVR heb ik de software PWM min of meer voor je liggen
(kan mij niet voorstellen dat dat voor een PIC spannender is om te schrijven...). Algorithme heb ik hier eens ingetikt, zou je even moeten zoeken.
RES
RES
Jep software PWM en dan de hele ports gaan schakelen.
Ongeveer zo.
f=1/T
Je maakt een bitteller (T) aan, en 18 presets zetten zodat elke RGB LED een bepaalde kleur gaat geven (via tabellen uitvogelen welke getallen de juiste kleuren geven)
1. T bitteller laden
2. 18 i/o's omhoog (start met de pulsperiode)
3. dan de bitteller 1 omlaag (dec)
Wordt een van de tellers gelijk aan de preset dan die pin omlaag zetten. (overgang naar pause van de PWM)
Daarna tot 0 laten doortellen. (de pause tijd)
De hele loop 20msec laten duren om 50Hz refresh te krijgen.
Die bitteller is 8-bit, dus 256 rondjes, die moeten T duren. Dus 256 moet 20msec duren, dus elk rondje 20msec/256 = 78usec. (ietsjes lager kiezen want het aantal instructies van de loop duurt ook nog enkele usec.)
[Bericht gewijzigd door RES op ]
Kan nog simpeler zou ik zeggen.
PIC op 20MHz.
timer0 1:1 zetten. Overflow elke 51us.
Vervolgens 18 variabelen definieren, welke een waarde van 0-255 kunnnen hebben. Dan nog een 19e registertje definieren dat de overflows gaat tellen. Noem dit bijvoorbeeld Overflows.
Alle uitgangen hoog zetten.
Interrupt op timer0 zetten.
Elke overflow increment je het register Overflows en vergelijk je de waarde in Overflows met de 18 variablen.
Wanneer "Overflows" gelijk is aan een variabele: corresponderende uitgang clearen.
Tot Overflows 0 is, dan alle uitgangen setten.
Je hebt zo een PWM regeling op een slordige 78Hz voor 18 kanalen. (tenminste als je een PIC neemt die genoeg I/O heeft)
EDIT//
Ik lees nu de edit van RES..
Geweldig! Dat ik hier nog niet aan gedacht had
. Alleen nog een beetje hopen dat het gaat lukken met de interrupts, ik gebruik ookal timer1 om een interrupt te krijgen (ongeveer elke 1ms) en interrupt on change on portb...
Aan het begin van mijn ISR zet ik de interrupts uit via INTCON, GIE. Vervolgens sla ik W en STATUS registers op. Dan kijk ik welke timer een interrupt gegenereert heeft, stop die timer en doe wat ie verder nog moet doen. Als je de andere timer overflowt, krijg ik natuurlijk geen interrupt. Dit is op te lossen door aan het begin van de ISR gewoon beide timers te stoppen, geen probleem. Maar hoe kan ik er voor zorgen dat wanneer ik een interrupt on pin change op portb niet mis? Of, als portb veranderd terwijl ik nog in mijn ISR zit, en ik ga vervolgens de ISR uit, gaat ie dan weer terug naar het begin van de ISR?
Ik hoop dat het probleem een beetje te begrijpen is, zo niet, dan zal ik even een poging doen om het wat beter te formuleren. 
iig bedankt voor deze mooie oplossing!
Het GIE-bit hoef je niet te clearen, dat gebeurt automatisch.
Bij verlaten van je ISR staat ie ook automatisch weer aan.
Je checkt in het begin van je ISR wat de interrupt source is. -> goed.
Mochten er nu twee interrupts zijn, dan zal eerst de interrupt afgehandeld worden die als eerste 'gecheckt' wordt.
De interrupt vlag van die andere interruptbron blijft hoog.
Op het moment van verlaten van de ISR, of te wel de eerste interrupt is afgehandeld, vliegt je code meteen weer de ISR in, omdat er een vlag hoogstaat. Dat is de vlag van de andere interrupt.
In het kort: je mist de interrupt niet, maar hij kan wat vertraging krijgen.
EDIT
Je moet timer0 niet willen stoppen he? Het feit dat ie een overflow genereert is voldoende om te weten.
Wouter,
je ISR kun je bijvoorbeeld zo schrijven:
;----------------------------------------------------------
;---INTERRUPT SERVICE ROUTINE -----------------------------
;----------------------------------------------------------
ISR movwf W_temp ;
swapf status,w ;SAVE CONTEXT
movwf STATUS_temp ;
;----------------------------------------------------------
SwitchToBank0 ;
;----------------------------------------------------------
; Determine which interrupt got triggered
;----------------------------------------------------------
Determine1 btfss INTCON,INTE ;
goto Determine2 ;
btfsc INTCON,INTF ;Is it the trigger?
goto PulseOUT ;Do output
;
Determine2 btfss INTCON,T0IE ;
goto Determine3 ;
btfsc INTCON,T0IF ;Is it timer0?
goto HandleOverflow ;Handle timer
;
Determine3 btfss INTCON,RBIE ;
goto Determine4 ;
btfsc INTCON,RBIF ;Is it portBchange?
goto HandleLevel ;handle levelchange
;
Determine4; btfss INTCON,PEIE ;
goto EndInterrupt ;
btfsc PIR1,RCIF ;Is it the UART?
goto HandleUART ;handle the UART
;
goto EndInterrupt ;false alarm
;----------------------------------------------------------
PulseOUT ;code
;code
;code
goto EndInterrupt
;----------------------------------------------------------
HandleOverflow ;code
;code
;code
goto EndInterrupt
;----------------------------------------------------------
HandleLevel ;code
;code
;code
goto EndInterrupt
;----------------------------------------------------------
HandleUART ;code
;code
;code
goto EndInterrupt
;----------------------------------------------------------
;--- END OF INTERRUPT -------------------------------------
;----------------------------------------------------------
EndInterrupt swapf STATUS_temp,w ;
movwf status ;RESTORE CONTEXT
swapf W_temp,F ;
swapf W_temp,w ;
;----------------------------------------------------------
retfie ;Return
;----------------------------------------------------------
[Bericht gewijzigd door DIY op ]
Op 24 november 2005 18:31:57 schreef DIY:
Het GIE-bit hoef je niet te clearen, dat gebeurt automatisch.
Denk er wel aan de interrupt source te clearen, anders vliegt ie gelijk de interrupt routine weer in.
edit Dat "false alarm" statement van DIY zorgt dus dat ie hangt bij een onbekende int. (Die had trouwens toch niet mogen optreden, dus zo erg is dat niet, maar ik laat dan meestal een ledje knipperen).
[Bericht gewijzigd door JoWi op ]
Goed punt van Jowi.
En inderdaad, dat "false alarm" slaat een beetje nergens op, maar ja, het doet ook geen kwaad zullen we maar denken.
Oke, dan denk ik dat het wel goed gaat. Maar dus ook als ik al in de interrupt zit en er komt nog een andere interrupt word er alsnog een flag gezet? De GIE bit zorgt er dus alleen voor dat, als er een interrupt komt, hij er naar toe gaat? Gelukkig, dan wordt het een heel stuk makkelijker 
Nogmaals bedankt voor de antwoorden
Wouter: Er kan inderdaad een interrupt binnen komen terwijl er een andere interrupt wordt afgehandeld. Maar omdat de GIE dus uit staat, zal er met die 'nieuwe' interrupt niks gedaan worden, tot je uit de ISR komt.
Op 24 november 2005 17:30:04 schreef DIY:
Kan nog simpeler zou ik zeggen.
PIC op 20MHz.
timer0 1:1 zetten. Overflow elke 51us.
Vervolgens 18 variabelen definieren, welke een waarde van 0-255 kunnnen hebben. Dan nog een 19e registertje definieren dat de overflows gaat tellen. Noem dit bijvoorbeeld Overflows.Alle uitgangen hoog zetten.
Interrupt op timer0 zetten.
Elke overflow increment je het register Overflows en vergelijk je de waarde in Overflows met de 18 variablen.
Wanneer "Overflows" gelijk is aan een variabele: corresponderende uitgang clearen.
Tot Overflows 0 is, dan alle uitgangen setten.
Hier is een leuke truuk voor: Als je de 'compare' doet van de variabele met de overflow-counter, dan geeft het carry-bit aan of de betreffende uitgang geset of gecleared moet zijn. Je kunt die carry simpelweg in een registertje shiften, en dan na acht compares dat register in een keer zo naar je poort gooien.
In avr-code:
ldi bits, 1 ; Dit bit komt na de 8e rol eruit.
Loop:
ld temp, X+
cp timer, temp
rol bits
bcc Loop
out PORTB, bits
Omdat die routene errug vaak wordt aangeroepen moet ie erg efficient zijn, dus je kunt er ook voor kiezen om de loop uit te rollen, dat scheelt een factortje 3 ofzo.
Er is nog een leukere truuk: Inplaats van een 'compare' kun je een 'sub' of een 'add' gebruiken (dan moet je wel timer in een temp registertje duwen). Als je even nadenkt zie je dat er dan elke ronde de puls van een led nog steeds even breed is, maar dat wordt mooi verspreid, inplaats van dat ze tegelijk aan zijn, wat ervoor zorgt dat op je led-stroomaanvoer een veel kleinere rimpel zit.
Overigens is de ULN2803 of de UDN2981 de perfecte chip om met een pic/avr een hele rij leds aan te sturen, volgens mij vindt zo'n pic het niet leuk als hij 18*20 mA moet leveren. Ze kunnen misschien wel 20mA per poort leveren, maar er is ook een totaal maximum wat de Vcc pin kan trekken (of de gnd pin kan dumpen).