Hi Balt, Welcome to the world of pics! They are great devices and a lot of fun to experiment with. One of my favorite things to do is make led and robot projects for my daughter. She really enjoys helping me make things and likes it when I make her little circuits. I do a lot of led projects using RGB leds and pwm. I've experimented and created many types of pwm code using bit banging and the pwm modules that some pics have. My favorite piece of code and the one I now use as the foundation for most of my led work was written by Scott Datalo. I've pasted a few code snippets here for you to look at. None of them are 100% complete so you will need to add code of your own. If you are interested, I would be happy to zip up a simple nightlight project I created and post it on my website or here on Piclist. Hope this helps. -Chris ; ---------------------------------------------------- ; Here is a piece of sample code from Scott. ; be sure to visit his web site for more info. The ; link is below. ; ---------------------------------------------------- list P=pic16f84 #include p16f84.inc ; ---------------------------------------------------- ;Variables - Adjust for your pic and for absolute ; or relative mode. ; ---------------------------------------------------- cblock 0x0020 ; ; The duty cycle for each PWM is stored here: ; pwm0,pwm1,pwm2,pwm3 pwm4,pwm5,pwm6,pwm7 ; ; The counters are compared to the duty cycles ; to determine when the pulse should be turned off ; pwm0_cntr,pwm1_cntr,pwm2_cntr,pwm3_cntr pwm4_cntr,pwm5_cntr,pwm6_cntr,pwm7_cntr ; ; When rhe "rising_edge" counter rolls over, all ; pwms are driven high ; rising_edge pwm_state endc #define PWM_PORT PORTB org 0 goto Start org 4 ;------------------------------------------------------------ ;pwm_multiple ; ; The purpose of this routine is to generate 8 pulse width ;modulated waveforms. The algorithm consists of 9 counters ;that change the state of the pwm bits whenever they roll over. ;One of the counters, rising_edge, will drive the pwm bits ;high when it rolls over. The other 8 counters pwm0-pwm7 will ;drive their corresponding bits low when they roll over. ; ; The technique can be understood with an example for one ; counter. (This was taken from my web page: ; http://www.dattalo.com/technical/theory/pwm.html ) ; ; In Psuedo-code: ; ;// Initialize ; ;PWM_output = 1; ;rising_edge = 0; // Rising edge counter ;falling_edge = MAX_COUNT - duty_cycle; // Falling edge counter ; ;// Loop forever: ; ;while(1) { ; ; // If the rising_edge counter rolls over, make the output high ; ; if(rising_edge++ > MAX_COUNT) { ; rising_edge = 0; ; PWM_output = 1; ; } ; ; ; // If the falling_edge counter rolls over, make the output low ; ; if(falling_edge++ > MAX_COUNT) { ; falling_edge = 0; ; PWM_output = 0; ; } ; ;} ; ; ;In this un-optimized example, we start the PWM output in the high ;state and initialize the falling edge counter with the desired ;duty cycle (or actually MAX_COUNT - duty_cycle, since the ;algorithm increments the counters). When the counters reach their ;maximum count, they're cleared back to zero and the PWM output is ;updated. Perhaps a simple picture illustrates it better: ; ; ;RE |---------|---------|---------|---------|--------- ; 01234567890123456789012345678901234567890123456789 ; ;FE ----|---------|---------|---------|---------|----- ; 67890123456789012345678901234567890123456789012345 ; ;PWM ----______----______----______----______----______ ; ; ; ;RE = Rising Edge counter ; ;FE = Falling Edge counter ; ;PWM = PWM output ; ;In this example, the counters roll over after 10 counts. In the ;beginning, the rising edge counter is cleared and the output is ;driven high. Also, the duty cycle is 4 so the falling edge ;counter is initialized to (10 - 4) = 6. When the rising edge ;counter counts from 9 to 10, it is "rolled over" back to zero and ;the output is driven high. Similarly, the falling edge counter ;drives the output low when it rolls over. ; ;Perhaps one thing to notice is that the difference (modulo 10) ;between the two counters is always equal to the duty cycle. That's ;no coincidence. In fact that's why this technique is called the ; "Phase Shifted Counters". ; ;The modulo arithmetic gets *much* simpler when MAX_COUNT = 2^N. ; ; ;RAM: ; pwm0-pwm7 - pwm counters ; rising_edge - rising edge counter ; pwm_state - current state of the pwm outputs. ;ROM ; 38 instructions ;Execution time ; 23 cycles pwm_multiple ; DECFSZ rising_edge,F ;If the rising edge counter has not rolled GOTO pwm_update ;over, then go update the PWM counters ; pwm_rising_edge: ; ; MOVF pwm0,W ;Update each counter with the latest MOVWF pwm0_cntr ;pwm value ; MOVF pwm1,W ; MOVWF pwm1_cntr ; ; MOVF pwm2,W ; MOVWF pwm2_cntr ; ; MOVF pwm3,W ; MOVWF pwm3_cntr ; ; MOVF pwm4,W ; MOVWF pwm4_cntr ; ; MOVF pwm5,W ; MOVWF pwm5_cntr ; ; MOVF pwm6,W ; MOVWF pwm6_cntr ; ; MOVF pwm7,W ; MOVWF pwm7_cntr ; ; MOVLW 0xff ;Turn on all of the PWM's. GOTO pwm_exit ; pwm_update: CLRW ;Build the bit mask for turning ;off the PWM outputs. Assume that ;all of the outputs will be turned ;off. ; DECFSZ pwm0_cntr,F ;If the first counter has not reached 0 IORLW b'00000001' ;then we don't want to turn it off. ; DECFSZ pwm1_cntr,F ;Same for the second one IORLW b'00000010' ; ; DECFSZ pwm2_cntr,F ;and so on... IORLW b'00000100' ; ; DECFSZ pwm3_cntr,F ; IORLW b'00001000' ; ; DECFSZ pwm4_cntr,F ; IORLW b'00010000' ; ; DECFSZ pwm5_cntr,F ; IORLW b'00100000' ; ; DECFSZ pwm6_cntr,F ; IORLW b'01000000' ; ; DECFSZ pwm7_cntr,F ; IORLW b'10000000' ; ; ; ; ; ANDWF pwm_state,W ; Clear all of those pwm outputs ;that have reached zero. pwm_exit: ; MOVWF PWM_PORT ;Update the outputs MOVWF pwm_state ;Save the state RETURN Start BSF STATUS,RP0 CLRF PWM_PORT BCF STATUS,RP0 MOVLW 0 MOVWF pwm0 MOVLW 0x10 MOVWF pwm1 MOVLW 0x20 MOVWF pwm2 MOVLW 0x30 MOVWF pwm3 MOVLW 0x40 MOVWF pwm4 MOVLW 0x50 MOVWF pwm5 MOVLW 0x60 MOVWF pwm6 MOVLW 0x70 MOVWF pwm7 loop CALL pwm_multiple goto loop end ; ---------------------------------------------------- ; Below are some routines from one of my nightlight ; projects. You can see how I am using Scott's code ; to PWM modulate the RGB led. I control brightness ; which lets me change colors and overall intensity. ; ; *** NOTE *** This code is not complete. I left out ; a lot of setup and other irrelivent stuff to try ; and keep it simple. ; ; ---------------------------------------------------- ;MAIN CODE ; ; The PWMLevelTable contains entries ; for bright, medium and dim led levels. ; It is used when the user changes the ; maximum brightness level of the ; nightlight. ; ; The table has 4 entries for each ; brightness level. The entries are: ; MaxDutyCycle - The new maximum duty cycle ; RedStart - Starting red duty cycle ; GreenStart - Starting green duty cycle ; BlueStart - Starting blue duty cycle ; ; USAGE: Call the table subroutine with the ; offset you want in W ; ; *** NOTE *** I did not put any error checking or ; 256 byte boundary management code in here. Make ; sure table is in first 256 bytes or write better code. ; PWMLevelTable addwf PCL,f ; Bright dt 0xf0, 0x09, 0xa5, 0xe0 ; Start offset 0x0 ; Medium dt 0x20, 0x15, 0x05, 0x1e ; Start offset 0x4 ; Dim dt 0x09, 0x05, 0x03, 0x02 ; Start offset 0x8 ; -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= ; *** Main program loop ; -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= ; PWM the RGB leds Main PWM ; check for light adjust button press btfsc LTBtnPressed goto LTAdjBtnHandler ; Check for time adjust button press btfsc TMBtnPressed goto TMAdjBtnHandler ; Check for TimeToSleep btfsc TimeToSleep goto Shutdown ; Time to change colors by adjusting duty cycles? btfsc ChangeColor goto ColorCycle ; ---------------------------------------------------- ; This is Scott's code - modified for my project ; ---------------------------------------------------- decfsz risingEdge,f ; Rising edge == 0? (time to start over) goto PWMUpdate ; NO - Update each pwm count down PWMRisingEdge movfw redPwm ; Set each color's duty cycle movwf redPwmCtr movfw greenPwm movwf greenPwmCtr movfw bluePwm movwf bluePwmCtr movlw RLED|GLED|BLED ; turn on all pwm ports goto PWMExit PWMUpdate ; build bit mask for turning off pwm's ; who's counters are zero clrw ; Clear WREG. It's the mask register. decfsz redPwmCtr,f ; Is it time to turn off this led? iorlw RLED ; NO - set mask to keep this led on. decfsz greenPwmCtr,f iorlw GLED decfsz bluePwmCtr,f iorlw BLED andwf pwmState,w ; clear all outpus that reached zero PWMExit movwf PORTC ; Update pwm outputs movwf pwmState ; Save state for next pass through goto PWM ; repeat ;----------------------------------------------------------------------- ------ ; ---------------------------------------------------- ; This routine is a cut and paste from my nightlight. ; ; It shows one way to change duty cycle and cycle ; led colors/brightness. ; ---------------------------------------------------- ; ; ; ColorCycle is responsible for incrementing the ; duty cycle for each of the colors in the RGB led. ; ; It does this by adding 0x01 or 0xFF to each ; color's duty cycle. Adding 0xFF is the same ; as subtracting 0x01. ; ; When a duty cycle reaches the maximum ; or minimum value, it's increment register ; is changed from 0x01 to 0xFF or from 0xFF to 0x01. ; This causes the duty cycle to count in the ; opposite direction. ; ; Each led cycles back and forth between its ; minimum and maximum brightness level. ; ; When the three led's are mixed, all kinds ; of cool colors come out. ; ; NOTE - ColorCycle is called by the main line ; code whenever ChangeColor is high. ChangeColor ; is set every 32 mS in the TMR0 interrupt. ; Adjust the TMR0 interrupt interval to change ; the cycle speed AND/OR add a counter here ; to only change values every X calls. ; ---------------------------------------------------- ColorCycle ; *** Automatic color cycle the pwms - test version 1 movfw redInc ; Increment/decrement duty cycles addwf redPwm,f movfw greenInc addwf greenPwm,f movfw blueInc addwf bluePwm,f ; Check each duty cycle for min value. If it's at ; the minimun, invert the incrementer. CheckRed ; min check movf redPwm,w ; Check for minimum value subwf pwmMin,w btfss STATUS,Z ; Need to invert? goto CheckRedMax ; NO, Check for max invert goto RedInvert ; YES, Invert CheckRedMax ; max check movf redPwm,w ; Check for maximum value subwf pwmMax,w btfss STATUS,Z ; Need to invert? goto CheckGreen ; NO, Check next color RedInvert movlw 0xff ; Toggle incrementer between btfsc redInc,7 ; 0xFF and 0x01. This causes it movlw 0x01 ; to increment or decrement duty movwf redInc ; cycle when added to a register. CheckGreen ; min check movf greenPwm,w subwf pwmMin,w btfss STATUS,Z goto CheckGreenMax goto GreenInvert CheckGreenMax ; max check movf greenPwm,w subwf pwmMax,w btfss STATUS,Z goto CheckBlue GreenInvert movlw 0xff btfsc greenInc,7 movlw 0x01 movwf greenInc CheckBlue ; min check movf bluePwm,w subwf pwmMin,w btfss STATUS,Z goto CheckBlueMax goto BlueInvert CheckBlueMax ; max check movf bluePwm,w subwf pwmMax,w btfss STATUS,Z goto ColorCycleDone BlueInvert movlw 0xff btfsc blueInc,7 movlw 0x01 movwf blueInc ColorCycleDone bcf ChangeColor ; Clear the color change flag. goto PWM ;----------------------------------------------------------------------- ------ -- http://www.piclist.com PIC/SX FAQ & list archive View/change your membership options at http://mailman.mit.edu/mailman/listinfo/piclist