PIC 12F629 Question.

Discussion in 'Embedded Systems and Microcontrollers' started by R!f@@, Oct 28, 2015.

  1. R!f@@

    Thread Starter AAC Fanatic!

    Apr 2, 2009
    I needed a light at my computer table to light the keyboard. A led strip does the Job. But since I started using EasyPic I needed more light, So I got me more strips and the diagram I made to light them. Works so far.
    The PIC uses two buttons. One to switch on and off and other to Toggle the lights. When I need keyboard only I use the Dim ( 1 strip ) and when I work, I use the Bright ( 4 strip ).
    I made the diagram to be upgradable fro dimming when I get to it. I believe now I like to have that & I prefer to use an encoder. The board is wired in such way that all I need it to plug in the encoder (via connector )and viola.

    I did not check if the coding would be possible for me but I guessed I can try.
    The current code is working since it was quite simple ( not to me ). I had recently added the comments to the source code. If any one like to see, just ask.

    My Q..
    Look at the diagram for reference.
    Will the 12F629 be able to soft PWM the Led from minimum bright to max using a rotary encoder?
    And use one switch to ON OFF & Toggle the leds?
    One second long press to On Off and normal press to Toggle.
    In Toggle either Dim or Bright is Active. Both Output should be able to control the Strip Brightness

    I tried to figure out a way to use one switch to to control both functions but the Darn delay is coming into play.

    If this is possible I can try to continue if not would I need a 12F675 or something similar. But I rather learn to do a soft PWM
  2. Lestraveled

    Well-Known Member

    May 19, 2014
    I took a quick look at the 629 data sheet. It looks like it would do fine for your project. It won't take much to implement a software PWM to drive LEDs. You could make it easier on yourself and get a PIC that has a PWM built in. Make sure which every PIC you use, is compatible with your programmer.
    R!f@@ likes this.
  3. JohnInTX


    Jun 26, 2012
    You can do it.
    Use the interrupt driven timer to refresh the software PWM.
    Hook one phase of the encoder to the external interrupt. When you get the interrupt, examine the other phaze to see if its high or low (turned CW or CCW).
    You also could use an RC network with a GPIO port. Discharge the cap with out(0) then make it an input and see how long it takes to charge to the port threshold. Its sloppy but workable.

    Here's a little PWM test I did awhile back that put fixed duty cycles onto an RGB LED. Its for a 10F200, no interrupt, not much timer. I just wanted to see what the PWM section would look like. It worked but I never implemented the control stuff. I think I may have used it on a subsequent project with more control. If you are interested in the approach, I'll take a look.

    Compiler is XC8-Pro
    Have fun.

    EDIT: If you have occasion to look at other 8pin PICs, consider the 12F1840 - its a little jewel and with a PICkit and the AC244063 debugger (not the 244043 which is a simple debug header) you get a serious development environment.

    And: you should never use a _delay() in your code. There are better ways to do delays that don't foul up the rest of your code.

    Code (C):
    1. /*
    2. * File:   10Fplay.c
    3. * Author: John
    4. *
    5. * Created on April 23, 2013, 10:14 PM
    6. */
    8. #include <xc.h>
    11. // CONFIG
    12. #define _XTAL_FREQ 4000000
    13. #pragma config WDTE = OFF       // Watchdog Timer (WDT disabled)
    14. #pragma config CP = OFF         // Code Protect (Code protection off)
    15. #pragma config MCLRE = OFF      // Master Clear Enable (GP3/MCLR pin fuction is digital I/O, MCLR internally tied to VDD)
    16. /*
    17. *
    18. */
    19. #define GRNmask  0x01
    20. #define REDmask  0x02
    21. #define BLUEmask 0x04
    22. #define OFFmask (~0x07)
    24. #define PWMbaseK 255
    26. // For now
    27. #define GRNpct 50
    28. #define REDpct 50
    29. #define BLUEpct 100
    32. unsigned char PWMbase;
    33. unsigned char RedDUTY,BlueDUTY,GrnDUTY;
    34. unsigned char OutMask;
    35. //unsigned char redK,grnK,blueK;
    37. //---------------------- OUTPUT PWMs  ----------------------------
    38. void outPWM(void)
    39. {
    40.         OutMask &= OFFmask;         // all off under mask
    42.         if(RedDUTY){                // iff still some RED, dec and output 1
    43.             RedDUTY--;
    44.             OutMask |= REDmask;
    45.         }
    47.         if(BlueDUTY){
    48.             BlueDUTY--;
    49.             OutMask |= BLUEmask;
    50.         }
    52.         if(GrnDUTY){
    53.             GrnDUTY--;
    54.             OutMask |= GRNmask;
    55.         }
    57.         GPIO = OutMask;
    58. }
    60. void main(void) {
    61.     OPTION = 0b11011111;  //TOCKI is internal so GP2 is output
    62.     TRISGPIO = 0;
    64.     CMCON0 = 0;
    66.     GPIO = OutMask = 0;
    67.     PWMbase = PWMbaseK;     // init
    69.     do{
    70.         if(PWMbase){
    71.             PWMbase--;
    72.             outPWM();
    73.         }
    74.         else{
    75.             PWMbase = PWMbaseK;
    76.             RedDUTY = (REDpct * PWMbaseK)/100;
    77.             GrnDUTY = (GRNpct * PWMbaseK)/100;
    78.             BlueDUTY = (BLUEpct * PWMbaseK)/100;
    79.         }
    81.     } while(1);
    83. }
    Last edited: Oct 28, 2015
    R!f@@ likes this.
  4. GopherT

    AAC Fanatic!

    Nov 23, 2012
    You could use a 256 tick delay. Then a dutyCycle variable that is checked after each tick. Once the dutyCycle is reached, the LED is turned off.

    Use two push buttons with debounce to increase/decrease duty cycle variable until the lamp is as bright as you want. The 256 ticks should be completed within 0.0003 seconds (min) to 0.015 seconds (max). That is 60 to 300 Hz.

    Good luck.
    R!f@@ likes this.
  5. R!f@@

    Thread Starter AAC Fanatic!

    Apr 2, 2009
    OK then. Thanks all.
    I am keen on doing this with 12F629 as I have plenty of those lying around. For easy jobs I like to use them up.
    I will start modifying the code and post if I get to a bump.
  6. R!f@@

    Thread Starter AAC Fanatic!

    Apr 2, 2009
    I was thinking a lot and I could not get my head around this.
    Two timers can be used to Soft PWM, One to urn on the out and the other to turn off. Right or wrong? I am thinking Right.
    Problem is how can I control On off and toggle using just one switch without using delays. A delay will stop the PWM, won't it?
    Is it possible to use 1 second press to turn On and Off the Led and normal press to toggle the two outputs without using delay.
    I have made the output toggling part without delays. i.e by using a Flag bit and checking the sw state from on to off.

    The 1 second On off is confusing me. Do I need to use two switches as before ?
  7. spinnaker

    AAC Fanatic!

    Oct 29, 2009
    For 50% duty cycle only one timer is needed. The timer interrupt sets a flag. The flag determines if the output should be high or low. Set to that value and then reverse the flag

    Other duty cycles could be used but a little more trick in that you would need to set your registers with different values on each timer interrupt.
    Last edited: Oct 31, 2015
  8. spinnaker

    AAC Fanatic!

    Oct 29, 2009
    You can do your button push with polling. It would be a bit tricky but you could read the button's pin, if it is high (or low depending on what you want) increment a counter, loop back and start all overagain. When the counter reaches some value that equates to a second then run your function.
  9. R!f@@

    Thread Starter AAC Fanatic!

    Apr 2, 2009
    Aah ! A counter approach
    But wouldn't that counter be affected by Sw bounce ?
  10. spinnaker

    AAC Fanatic!

    Oct 29, 2009
    The timer is for the PWM. See my post on how to do the button push.
  11. MMcLaren

    Distinguished Member

    Feb 14, 2010
    Are you using C (XC8) or Assembly language?
  12. JohnInTX


    Jun 26, 2012
    You can do both with one timer/interrupt scheme.
    Forget about the switch for now.
    Start by free running TMR0 and enable its interrupt. At 4MHz internal clock it will give you an overflow interrupt every 256uS. Use that as the PWM base (256uS period, 1-of-256 resolution). You can change it later.
    Strip down the code I posted to one channel and incorporate everything inside the do-while loop into the interrupt routine. You can inline the call to outPWM too. Set the remaining PWMduty to 50 (%).
    Fire it up, you should get the LED running at 50% duty cycle. Play around with the fixed duty if you want.

    You will probably find that the PWM base (PWMbaseK) with the 256us tik is too long 65ms and the LED will flicker. Reduce it by reducing the constant PWMbaseK. Reducing to 100 gives a base of 25.6ms. Note that the TMR0 interrupt period is still 256us, you don't have as much resolution (you changed from 256 to 100 counts) but its OK for a visible LED. The alternative would be to reload TMR0 at the interrupt (as opposed to free running it) to make the tiks happen faster but at only 4MHz you don't want to interrupt too often.

    To complete the PWM:
    Make the DUTY register a variable. Its value can run from 0 - PWMdutyK for 0-100%. Now the main code can just jam a value there and the PWM will run at that value with no further intervention.

    For the switch(es):
    In the interrupt handler, after updating the PWM output, debounce the switch(es) by incrementing a byte counter (one for each switch) when the switch is closed and clearing it when its open. When the switch counts up to the debounce value (the number of 256us tiks), set a flag call SW_HELD. When it opens clear the flag. I like to go one step further and set another flag (SW_REQUEST) that get sets ONCE when the counter reaches its limit. SW_REQUEST is a latched bit is cleared when the main routine sees and processes it.

    All main has to do now is
    1) look for SW_REQUEST(s) and increment/decrement the PWM duty register as required. It clears SW_REQUEST after processing each push.
    2) compute the new value of PWMduty (from 0-PWMbaseK) and jam it onto the PWMduty register.

    Note since all of the time-involved stuff is done on the fly (by maintaing the state of each separate item for each interrupt tik), there is no _delay() required, ever. Main can take its sweet time about getting around to things which is what you want - relieve main of the problem of doing time-intensive stuff without screwing up time-sensitive stuff. In this case, main just samples the switch requests and bumps the PWM duty cycle accordingly. It can take as long as it needs to do it.

    There are ways to slick this up. If you elect to proceed along this path, we can visit them then.

    Good luck.
    R!f@@ likes this.
  13. R!f@@

    Thread Starter AAC Fanatic!

    Apr 2, 2009
    Which one

    MikroC pro

    I think I get what you are saying.
    Deleted the comments as I found out using xtal limits the I/O pins.:oops:
    So I will start as suggested using int 4Mhz.
    Will Post as I get something to brag about.:D
    Last edited: Nov 1, 2015
  14. R!f@@

    Thread Starter AAC Fanatic!

    Apr 2, 2009
    Sorry Am having trouble with one of my HDD. :(
    Running a back up on that drive and system is like a Pentium II :mad:
    System is rather slow now. Problem is it is My Data drive so everything is on it. accessing them takes a while
    But I did start off with the attached pdf.
    Am I on the right track.
    • V2.pdf
      File size:
      252.8 KB
  15. spinnaker

    AAC Fanatic!

    Oct 29, 2009
  16. spinnaker

    AAC Fanatic!

    Oct 29, 2009

    You should predefine the value of Whiteduty.
    You should predefine Outmask

    Is this your full code? Because I do no see a while loop in main().
  17. spinnaker

    AAC Fanatic!

    Oct 29, 2009
    And I think OutMask will always equal zero.
  18. JohnInTX


    Jun 26, 2012
    You have the LED connected to GPIO,0 (the mask bit is 01h) so with only one LED, OffMask should be ~0x01. The way the IO works is OutMask (probably should be called OutImage..) is the value that GPIO will have after PWM updates. It is written as a single byte, all output bits at once (even non-PWM ones) so all IO operations (including PWM) work by modifying OutMask then writing it as a byte to GPIO. That avoids r-m-w issues as well as updating all PWM bits (in my code at least) at once. But the OFFmask must be corrected otherwise you will modify other, non PWM bits when you write the port.

    The reason for
    OutMask &= OFFmask;
    is to clear the 3 bits assigned to my 3 channel PWM. Each of the following tests examines the xxxDuty register and if its still > 0, sets the appropriate bit for each PWM output. When the 3 tests are done, the PWM bits of OutMask have been updated (without changing any others in OutMask) and the whole thing is written to GPIO.

    Since you only are using one bit you could do something like:
    Code (C):
    1.  if(WhiteDuty){
    2.   WhiteDuty--;
    3.   GPIO.0 = 1;  // LED ON whenever there are counts left in the duty register
    4. }
    5.   else
    6.    GPIO.0 = 0; // LED OFF the rest of the time
    I don't like this way as much because I don't like modifying individual bits on a PORT (non LATx) output.

    There's more to do but so far so good.
    TS said his system crashed midway through..
    Last edited: Nov 1, 2015
    R!f@@ likes this.
  19. R!f@@

    Thread Starter AAC Fanatic!

    Apr 2, 2009
    Me at workshop now.
    I am doing the coding at home before sleep.
    System is still running the back up off the data drive. It took 6 hours to take 11GB:mad:
    Still more to go.
    I will post the rest of the code after swapping a new drive
  20. R!f@@

    Thread Starter AAC Fanatic!

    Apr 2, 2009
    Come again.