# PIC 12F629 Question.

#### R!f@@

Joined Apr 2, 2009
9,812
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

#### Attachments

• 19 KB Views: 26

#### Lestraveled

Joined May 19, 2014
1,946
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.

#### JohnInTX

Joined Jun 26, 2012
4,671
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.

C:
/*
* File:   10Fplay.c
* Author: John
*
* Created on April 23, 2013, 10:14 PM
*/

#include <xc.h>

// CONFIG
#define _XTAL_FREQ 4000000
#pragma config WDTE = OFF       // Watchdog Timer (WDT disabled)
#pragma config CP = OFF         // Code Protect (Code protection off)
#pragma config MCLRE = OFF      // Master Clear Enable (GP3/MCLR pin fuction is digital I/O, MCLR internally tied to VDD)
/*
*
*/

#define PWMbaseK 255

// For now
#define GRNpct 50
#define REDpct 50
#define BLUEpct 100

unsigned char PWMbase;
unsigned char RedDUTY,BlueDUTY,GrnDUTY;
//unsigned char redK,grnK,blueK;

//---------------------- OUTPUT PWMs  ----------------------------
void outPWM(void)
{

if(RedDUTY){                // iff still some RED, dec and output 1
RedDUTY--;
}

if(BlueDUTY){
BlueDUTY--;
}

if(GrnDUTY){
GrnDUTY--;
}

}

void main(void) {
OPTION = 0b11011111;  //TOCKI is internal so GP2 is output
TRISGPIO = 0;

CMCON0 = 0;

PWMbase = PWMbaseK;     // init

do{
if(PWMbase){
PWMbase--;
outPWM();
}
else{
PWMbase = PWMbaseK;
RedDUTY = (REDpct * PWMbaseK)/100;
GrnDUTY = (GRNpct * PWMbaseK)/100;
BlueDUTY = (BLUEpct * PWMbaseK)/100;
}

} while(1);

}

Last edited:

#### GopherT

Joined Nov 23, 2012
8,012
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@@

Joined Apr 2, 2009
9,812
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.

#### R!f@@

Joined Apr 2, 2009
9,812
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 ?

#### spinnaker

Joined Oct 29, 2009
7,835
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:

#### spinnaker

Joined Oct 29, 2009
7,835
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.

#### R!f@@

Joined Apr 2, 2009
9,812
Aah ! A counter approach
But wouldn't that counter be affected by Sw bounce ?

#### spinnaker

Joined Oct 29, 2009
7,835
Aah ! A counter approach
But wouldn't that counter be affected by Sw bounce ?
The timer is for the PWM. See my post on how to do the button push.

#### MMcLaren

Joined Feb 14, 2010
856
Are you using C (XC8) or Assembly language?

#### JohnInTX

Joined Jun 26, 2012
4,671
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.

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

Good luck.

#### R!f@@

Joined Apr 2, 2009
9,812
The timer is for the PWM. See my post on how to do the button push.
Which one

Are you using C (XC8) or Assembly language?
MikroC pro

@JohnInTX
I think I get what you are saying.
{ED}
Deleted the comments as I found out using xtal limits the I/O pins.
So I will start as suggested using int 4Mhz.
Will Post as I get something to brag about.

Last edited:

#### R!f@@

Joined Apr 2, 2009
9,812
Sorry Am having trouble with one of my HDD.
Running a back up on that drive and system is like a Pentium II
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.

#### Attachments

• 252.8 KB Views: 7

#### spinnaker

Joined Oct 29, 2009
7,835
Sorry Am having trouble with one of my HDD.
Running a back up on that drive and system is like a Pentium II
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.

You should predefine the value of Whiteduty.

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

#### spinnaker

Joined Oct 29, 2009
7,835
And I think OutMask will always equal zero.

#### JohnInTX

Joined Jun 26, 2012
4,671
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
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:
C:
 if(WhiteDuty){
WhiteDuty--;
GPIO.0 = 1;  // LED ON whenever there are counts left in the duty register
}
else
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:

#### R!f@@

Joined Apr 2, 2009
9,812
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
Still more to go.
I will post the rest of the code after swapping a new drive