PIC 12F629 Question.

Thread Starter

R!f@@

Joined Apr 2, 2009
9,918
Guess what?
I rewrote the code in MikroC and it is working as per ur HEX:D

I am gonna fiddle with this.
Is there anything I should change to improve this ?

{ED}
Slight improvement. I changed the encoder debounce from
C:
#define ENdebounce_ms  5
to
C:
#define ENdebounce_ms  0.5
And PWM now responds to both type encoders. Even at fast rotation :p
 
Last edited:

JohnInTX

Joined Jun 26, 2012
4,787
Is there anything I should change to improve this ?
What - mess with total perfection?? :)
Well done on the translate to MikroC. I guess it was OK with the 0.5 ms value. I would have thought it would complain about float to char conversion but maybe its OK at compile time. If you can sim it, set a breakpoint when the debouncer is loaded and see what it all evaluates to.

Just for reference, how big is the compiled object?
XC8 generated 193 instructions in Pro mode, 257 in Free mode.
 

Thread Starter

R!f@@

Joined Apr 2, 2009
9,918
C:
/******* PROGRAM CONFIGURATION  ***************/
// Change these as required
#define TMR0period_us  50           // how many usec per TMR0 irq
#define ENdebounce_ms  0.5            // how many ms to debounce the encoder
#define HeartBeatPeriod_ms 500        // how fast to flash the HB LED

#define PWMbaseN 100                   // how many TMR0 IRQs in one PWM cycle
#define PWMchangePerClick 1           // how much to change duty cycle each encoder click
#define InitialPWMDutyK 100            //PWM on power up (actual counts, not percent)
So I Played with these values and I can get the range of the encoder that I like from minimum brightness to maximum brightness and avoid the flickering to an accepted level.
Some parts I got some I did not yet.
Can you explain them to me
When TMROperiod is too low or too high PWM does not work. 50us is the lowest but in between mid bright to max bright there is a wee bit flicker. Can I get rid of this ? and I do not want to OFF in lowest setting. I would like a minimum level to stay. Not go off at minimum encoder rotation. There is an off function you know.


What - mess with total perfection??
Oh..OK ! I guessed this was you just doodling. :oops:

I guess it was OK with the 0.5 ms value.
Encoder is fine now.
I would have thought it would complain about float to char conversion but maybe its OK at compile time. If you can sim it, set a breakpoint when the debouncer is loaded and see what it all evaluates to.
o_Oo_Oo_O

Just for reference, how big is the compiled object?
XC8 generated 193 instructions in Pro mode, 257 in Free mode.
Used RAM (bytes): 16 (33%)
Free RAM (bytes): 32 (67%)
Used ROM (program words): 224 (22%)
Free ROM (program words): 800 (78%)

Still a nOOb to see how many instruction this generates .
 

Thread Starter

R!f@@

Joined Apr 2, 2009
9,918
Hey John thanks again.
I's 2:30 in the morning. Get to get some slepp.
I will scope the outputs (waveform) tomorrow and see if i can change the hardware to remove this flicker.
I think it may be that the 10K R at the gate to drain of mosfet is turning off the mosfet fast.
What do you think?
A wee bit flicker is at around 50-80% brightness only.

Till then, Good night.

{ED}
I just can't go to sleep with ideas on my mind. Worst way to sleep
So I returned to swap the 4Strip MOSFET to 1 strip one and viola no flicker.
Connect the 4 strip and 1 strip one together and there it was.
I guessed only 1 issue......The adapter is unregulated and a junk I got to test this.
Note to self.
Always use a good supply when doing PWM. Even for testing.
Very Happy. Will update results tomorrow.
 
Last edited:

JohnInTX

Joined Jun 26, 2012
4,787
When playing with it, I found that with the 250usec timer tik, I had to reduce the PWMbase counts to the 50 shown. That makes each duty cycle count about 2% worth. It cleared up the flicker here.
You could also shorten the timer interrupt time but I was concerned that if it got too short, there would not be enough time to complete the interrupt. Remember that at 4MHz, each instruction is 1us so you only get 250 instructions between interrupts to get the interrupt routine itself done and leave some time left over for main. In the end, it looks like both compilers are making decent code so you probably could squeeze the interrupt period down a bit. I used the left over output as a scope monitor for the interrupt routine to verify the interrupt period but you could easily make it show you the time spent in the interrupt - set the bit in GPIOimg then write to GPIO when you enter the interrupt, clear it just before leaving. Add a bit to the time shown for overhead and you'll get an idea of how much you can push it.

My scope showed that the PWM output was stable and jitter-free whenever I looked at it.

Glad its working for you.
 

JohnInTX

Joined Jun 26, 2012
4,787
Thinking about your questions:
I set GP4 upon entry to the interrupt service routine and cleared it at the end. This scope shot shows the result.

First, my baseline frequency is off - this is because somehow the calibration constant for the internal clock got clobbered and the clock is running slow so the measurements shown are percentage based.
Chan 1 is the PWM output. Chan 2 is GP4 - 1= its in the interrupt routine.

There are 50 IRQs for each PWM cycle. That's because PWMbaseN == 50.

There are 4 IRQs under the PWM high output and 46 under the low output. 4/50 ~= the 7.8xx %duty cycle calculated by the scope. (It gets more accurate when expanded). So that's how the PWM is generated.

Of particular note is the %duty cycle of GP4 i.e. the %of time it spends in the interrupt routine. The scope says 13.4%. In post 65 I worried about running the interrupt faster because of the slow PIC. This shot says its not a problem i.e. the interrupt is taking <15% of the total CPU time (overhead guessed at) This means that to reduce flicker, you can run the TMR0 interrupt quite a bit faster (easily double) without impacting the main code. NICE! That is in addition to the previous recommendation to shorten PWMbaseN.

All of this identifies some good approaches to reduce flicker:
Faster IRQs because of a faster PWM at the expense of processing power left over for main. The scope shot shows we have Tcyc to spare and can push it.
Shorter PWMbaseN also makes a faster PWM at the expense of granularity i.e. if you reduce PWMbaseN to 10, each encoder click is a full 10%. At 100, each click is 1%.
You'll probably find that some combination of the two approaches is in order. The values I selected worked for my setup.

You can limit the PWM duty cycle on either end in the increment/decrement routines. Just clip to the values you want.

FWIW: I rebuilt it using FREE XC8 (not as optimized) and the %overhead by the interrupt increased to 19.4%. So, not as tight but still very comfortable. See what MikroC looks like.

C:
void interrupt isr(void)
{
    GPIOimg |= ScopeOutmask; // 1= entering IRQ
    GPIO = GPIOimg;

    TMR0 += TMR0reload;      // reload TMR0 to trim period
    T0IF = 0;               // clear IRQ flag
... all the other interrupt code then at end:
  GPIOimg &= ~ScopeOutmask; // 0=done with  IRQ
  GPIO = GPIOimg;
}// IRQ service
scope_41.png

Next: use the EEPROM to save the last PWM value to restart it after power up....
Have fun!
 
Last edited:

Thread Starter

R!f@@

Joined Apr 2, 2009
9,918
Using the EEPROM to save the value is a good touch.
I noticed when at initial startup the strip flashes. I had to make the initial startup value to max for now.

I will look into ur suggestion but for now I am in trouble.
Today I checked the original 12V 2.5A SMPS that I was using before and guess what? It is worse.
The SMPS (12V 2.5A ) is worse than an unregulated 12V 1.2A linear one. Flicker is way worse.
I will find a way to stabilize the PSU and start with the coding. Need to get the setup into the workshop from home to do some scoping to find correct value caps to stabilize the PSU
 

Thread Starter

R!f@@

Joined Apr 2, 2009
9,918
I scoped the outputs. The Yellow waveforms are the 12V PSU terminal. Blue one is the PIC out

SMPS PSU

SMPS PSU.png

Linear PSU

Linear PSU.png

Bench PSU

Bench PSU.png

I tried the filtering. Adding caps upto 3 X 1000uf decoupled with 100nf. Very little change.

The SMPS one ain't designed for switching loads and linear one current capacity is a bit low.
The Bench PSU without filtering with 5 strip running without flicker loads upto 1.3A @ full brilliance.
 

Thread Starter

R!f@@

Joined Apr 2, 2009
9,918
The Bench PSU is also showing spikes

Bench PSU.png

So I filtered it and it is better.

Bench PSU with filtering.png

Just for info. The Duty cycle.

Minimum Duty.png
Maximum Duty.png

The RIGOL is pretty handy.

I need to get me a well regulated PSU now. But I think I can finish the coding with the linear one.
 

Thread Starter

R!f@@

Joined Apr 2, 2009
9,918
I changed the Diagram to the attached pdf.

Found an old IBM laptop PSU in junk rated at 12V 3A and it works perfectly.
Infact I changed the TMR0period and PWMbaseN values to original john's Hex and there was no flicker. It was the supply that gave the flickering even before.
Been fiddling with it trying to add the Switch and buzzer according to MMcLaren example.
Really Nice touch the buzzer was. I changed the inputs and outputs pin config and the heartbeat is in the trash (Sorry John) :p.
Added the buzzer code and I get a nice two beep at startup. beep ! beep !.
Brightness variation is flawless. One full turn from Min to Max and power on I have minimum bright (right now). Good part is lowest PWM is now not off but minimum at power up, so that part is A-OK.
Here is the code as of now
C:
        Project : PC Table Top Led strip light. Dimming Controlled by a
                  Quadrature encoder with shaft switch. Switch controls
                  On and Off with a 1 second press and normal press to
                  Toggle the lights. Two Outputs control a Single strip and
                  a 4 strip Mosfet driver.
        PIC     : 12F629.
        Language: MikroC Pro.
================================================================================
****** PIC 12F629 CONFIGURATION ******
Set at Project setting in MikroC ~ Config = 0x3194
Internal Osc at 4Mhz. IO on GP4 and GP5
WDT Disabled
PWRT Disabled
MCLR Disabled
Brown Out Disabled
Code Proection Disabled
Data Protection Disabled
--------------------------------------------------------------------------------
****** PIC IO Details ******
GP0 - Single Strip Output.
GP1 - 4 Strip Output.
GP2 - Buzzer Output.
GP3 - Switch Input.
GP4 - Encoder Input, Active Low, 10K pullup.
GP5 - Encoder Input, Active Low, 10K pullup.

// IO is shadowed i.e. set/clear bits in GPIOimg, NOT on GPIO directly.
// The interrupt will write the port.
// This avoids having to turn off the IRQ when main does IO
*/
//------------------------------------------------------------------------------
/******* PROGRAM CONFIGURATION  ***************/
#define TMR0period_us  250      // how many usec per TMR0 irq
#define ENdebounce_ms  0.5      // how many ms to debounce the encoder
#define PWMbaseN 50             // how many TMR0 IRQs in one PWM cycle
#define PWMchangePerClick 1     // how much to change duty cycle each encoder click
#define InitialPWMDutyK 0       // PWM on power up (actual counts, not percent)
                                // 0 for Min. and 100 for Max.
#define ENphaseAmask  0x10      // Phase A is on GP4
#define ENphaseBmask  0x20      // Phase B is on GP5
#define PWMoutmask   0x03       // PWM out is GP0 & GP1
#define ENCODER_MASK (ENphaseAmask | ENphaseBmask) // bits with encoder inputs
//*************** CALCULATED CONSTANTS  *******************************
#define TMR0reload 256-TMR0period_us+3   // what to reload TMR0 to
#define ENdebTIKS ((ENdebounce_ms *1000) / TMR0period_us)
#define PWMdutyKinit ((InitialPWMpct * PWMbaseK)/100)
//********************* IO DEFINITIONS  *******************************
sbit Buzzer at GP2_bit;
sbit Sw at GP3_bit;
//********************** VARIABLES  **********************************
// Encoder
unsigned char curENCODER,newENCODER,lastENCODER,ENdebK,ENstate,GPIOimg;
bit NEW_ENCODER_STATE;
// Switch and Buzzer
static unsigned char duration = 32000/250;
unsigned char beep,swtmr;
bit Normal,Long;
// PWM
unsigned char PWMbaseK,PWMdutyK,PWMduty;

/*********************** INTERRUPT SERVICE  **************************
Interrupts when TMR0 rolls over. Reloads it to trim period then
processes items below.
Timer 1 is unused.
Note that IO is done here.  No direct outputs to GPIO in main is allowed
unless interrupts are disabled - not super good when running a software PWM*/

void interrupt()
{
    TMR0 += TMR0reload;     // Reload TMR0 to trim period
    INTCON.T0IF = 0;        // Clear IRQ flag

    //------------------ SERVICE PWM  --------------------------------
    // PWMbaseK counts PWMbaseN interrupts to make the PWM period
    // The PWM output will be high for the fraction of this period
    // determined by PWMdutyK.
    GPIOimg &= ~PWMoutmask;     // assume it will be 0
    if(PWMbaseK){
        PWMbaseK--;
        if(PWMdutyK){
           PWMdutyK--;
           GPIOimg |= PWMoutmask;
        }
    }
    else{
        PWMbaseK = PWMbaseN;
        PWMdutyK = PWMduty;
        GPIOimg |= PWMoutmask;
        }
    GPIO = GPIOimg;         // update port

    //------------------- SERVICE ENCODER  ---------------------------
    // Reads switch and compares current value to last valid one.
    // If its different, it attempts to debounce it by counting
    // each time it enters the interrupt.  When the switch is stable
    // for ENdebTIKS, the previous and current value are combined into
    // one 4bit value which indicates which way the switch turned.
    // See the encoder processor in main for more details.
    // Note that the switch inputs can be on any GPIO input.  This sorts that
    // out.

    curENCODER = (GPIO & ENCODER_MASK);
    if(ENdebK == 0){        // 0 means not debouncing any changes
        if(curENCODER != lastENCODER){  // but it has just changed so..
            ENdebK = ENdebTIKS;
            newENCODER = curENCODER;    // save value to compare
        }
        else;   // else its not changed, do nothing
    } // was not debouncing, debK >0 iff it just started a new one
    else{  // it is currently debouncing
        if(curENCODER != newENCODER){  // changed during debounce!
            ENdebK = ENdebTIKS;         // reset debouncer w/new value
            newENCODER = curENCODER;
        }
        else{   // still same value
            if(--ENdebK == 0){ // dec and test debounce counter iff Z..
                lastENCODER=newENCODER; // update 'last' value for next click
                ENstate = (ENstate << 2);       // shift is to was
                ENstate &= 0b00001100;  // clear out old and unused bits
                                        // then combine new inputs to two bits
                if(newENCODER & ENphaseAmask) ENstate |= 0x01;
                if(newENCODER & ENphaseBmask) ENstate |= 0x02;
                NEW_ENCODER_STATE = 1;
            } // debounce complete-new ENstate
        }// encoder value was same during debounce
    }// its currently debouncing
    //------------------- SERVICE BUZZER  ---------------------------
    if(beep){
     Buzzer ^= beep;
      if(--duration == 0){
       duration = 32000/250;
       beep --;
      }
    }
}// IRQ service

//********************* INCREMENT AND DECREMENT  *****************************
// Called by the encoder switch state decoder.  Increments/Decrements PWMduty
void increment()
{
    if ((PWMduty + PWMchangePerClick) >= PWMbaseN)
        PWMduty = PWMbaseN;
    else
        PWMduty += PWMchangePerClick;
}

void decrement(void)
{
   if (PWMduty < PWMchangePerClick)
           PWMduty = 0;
   else
       PWMduty -= PWMchangePerClick;
}


//************************ MAIN  *********************************************
void main() {
    INTCON = 0;         // Clear all IRQs.
    PIE1 = 0;           // Clear all Interrupts.
    OPTION_REG = 0x88;  // No Pullups, TMR0 runs at Tcyc.
    CMCON = 0x7;        // Comparators off.
    //---------------- INIT HARDWARE IO  ----------------------
    GPIOimg = 0x38;     // this must agree with the next line
    GPIO = 0x38;        // Init the I/O f/fs first.. then TRIS
    TRISIO = 0x38;      // GP0~2 Outputs, GP3~5 Inputs
    //---------------- INIT VARIABLES  --------------------------
    ENdebK = 0;
    NEW_ENCODER_STATE = 0;
    PWMduty = InitialPWMDutyK;       // starting output
    lastENCODER = (GPIO & ENCODER_MASK);  // init the encoder
    //---------------- START TIMER 0 IRQ  -----------------------
    TMR0 = 0;
    INTCON.T0IF = 0;
    INTCON.T0IE = 1;
    INTCON.GIE = 1;

    while(1){
        //--------------------- PROCESS ENCODER  ---------------------
        // Iff NEW state has been posted, process it.
        // ENstate is formatted as 0000llcc where
        //  ll is the last value of the switch
        //  nn is the current value.
        //  The switch runs a gray code (only one bit changes at a time). By
        //  combining the last and current bit inputs, a unique number
        //  0000 to 1111 is created.  This number indicates what happened when
        //  the switch was turned.  For example:
        //  Last value  Current value   State   What happened
        //-------------------------------------------
        //  00          01              1       Rotated CW one click
        //  00          10              2       Rotated CCW one click
        //  and
        //  10          00              8       Rotated CW one click
        //  10          11              11      Rotated CCW one click
        //  also
        //  00          11              3       Illegal, both bits changed,
        //                                      ignored.
        // A total of 4 states indicate CW and 4 indicate CCW.  The other 8
        // possibilities are the 4 possible both bit changes and 4
        // possible no bit changes.  These are ignored.
        // A valid CW or CCW calls increment/decrement which adjusts the PWM.

        if(NEW_ENCODER_STATE){
            NEW_ENCODER_STATE = 0;
            switch (ENstate){
                case 0b00000001:
                case 0b00000111:
                case 0b00001000:
                case 0b00001110:
                    increment();
                    break;

                case 0b00000010:
                case 0b00000100:
                case 0b00001011:
                case 0b00001101:
                    decrement();
                    break;
                // ignore others: bad reading
            }// switch
        }// if NEW_ENCODER_STATE

        if(Sw == 0){
         swtmr = 1000/25;
         beep = 1;
        }
        if(Sw ==0){
          if(swtmr){
          Normal ^= 1;
          swtmr = 0;
          }
        }
        if(swtmr){
        swtmr--;
          if(swtmr == 0){
          Long ^= 1;
          beep = 3;
          }
        }
     
    }// main while loop
}
Funny Part is the below code does not respond the Encoder in put ports but still I can adjust PWM. Really confusing

C:
        if(NEW_ENCODER_STATE){
            NEW_ENCODER_STATE = 0;
            switch (ENstate){
                case 0b00000001:
                case 0b00000111:
                case 0b00001000:
                case 0b00001110:
                    increment();
                    break;

                case 0b00000010:
                case 0b00000100:
                case 0b00001011:
                case 0b00001101:
                    decrement();
                    break;
                // ignore others: bad reading
            }// switch
        }// if NEW_ENCODER_STATE
I think the above code needs to match the Encoder input ports to work but I have no idea why it is working as of now.

Bugs...
1.. At Power on the Strip flashes before going to the startup value, which is minimum brightness but as John said I am looking forward to saving the value at power down.
2. Switch press does not produce beeps. Rather...Beeeeeeeeeeep. ! Meaning as long as switch is pressed I get Beeeeeeeeeeeeeep!.

one more thing
C:
    if(beep){
     Buzzer ^= beep;
      if(--duration == 0){
       duration = 32000/250;
       beep --;
      }
    }
the above code compiles but below (MMcLaren's post #53)does not
C:
    if(beep){
     Buzzer ^= beep.0;
      if(--duration == 0){
       duration = 32000/250;
       beep --;
      }
    }
The beep.0 is halting the build.
Did not get this part via my empty skull

PS...I added the part MMcLaren's post # 51
 

Attachments

Last edited:

JohnInTX

Joined Jun 26, 2012
4,787
Read the comment on line 27:
// IO is shadowed i.e. set/clear bits in GPIOimg, NOT on GPIO directly.
Your beep code accesses GPIO directly so GPIOimg is out of date. The next interrupt will re-write the original value in GPIOimg. You can't mix shadowed and direct IO on the same port. The good news is that the beeper output is also in the interrupt routine so set/clear bit 2 in GPIOimg using masking like the PWM does then write GPIOimg to GPIO. That's how it must be done.
To toggle the buzzer bit do it like TOGGLE_HEARTBEAT used to do in the original code.
C:
#define Buzzermask 0b00000100  // 1 masks GP2
GPIOimg ^= Buzzermask;  //toggle buzzer output
GPIO = GPIOimg;
Try that.

You may wonder why the extra effort to shadow the port - trust me, its necessary.
 

MMcLaren

Joined Feb 14, 2010
861
the above code compiles but below (MMcLaren's post #53)does not
Code:
    if(beep){
     Buzzer ^= beep.0;
      if(--duration == 0){
       duration = 32000/250;
       beep --;
      }
    }
Sorry! Accessing 'beep' bit 0 using 'beep.0' is a feature of the BoostC compiler I use. You'd probably need to use something like this in XC8 or Mikro Pro C;
Code:
    if(beep){
     Buzzer ^= (beep & 1);
      if(--duration == 0){
       duration = 32000/250;
       beep --;
      }
    }
Let me look at how the "shadow" operation works...
 
Last edited:

MMcLaren

Joined Feb 14, 2010
861
No @MMcLaren , since the output is shadowed, he cannot access Buzzer directly but has to go through GPIOimg. Otherwise, pretty cool addition.
How about refreshing 'GPIOimg' from 'GPIO' in the small PWM section of the ISR where you're only changing the GP0 and GP1 LED pins?
Code:
    GPIOimg = GPIO & ~PWMoutmask;     // assume it will be 0
    if(PWMbaseK){
        _delay(11);
        PWMbaseK--;
        if(PWMdutyK){
           PWMdutyK--;
           GPIOimg |= PWMoutmask;
        }
    }
    else{
        PWMbaseK = PWMbaseN;
        PWMdutyK = PWMduty;
        //GPIOimg |= PWMoutmask;
        }
    GPIO = GPIOimg;         // update port
I wonder if a shadow is necessary? Perhaps we could try it without the shadow register?
 

JohnInTX

Joined Jun 26, 2012
4,787
How about refreshing 'GPIOimg' from 'GPIO' in the small PWM section of the ISR where you're only changing the GP0 and GP1 LED pins?
GPIOimg = GPIO & ~PWMoutmask; // assume it will be 0
No. That still is read-modify-write and can fail for all the reasons that bsf/bcf et al can fail when using GPIO (or any PORTx instead of LATx in other PICs) - that reason is that the 'current value' of any of the port output bits read can be corrupted by noise, loading, and many other things. I shadowed the GPIO for that reason. GPIOimg is the stable record of what should be on the port pins and that's why I shadow. What's on the pins themselves is not-necessarily a stable image and run this way, there is never a need to read GPIO output bits to modify them. Never.

You can still do the buzzer. Just flip the bit in GPIOimg then write GPIOimg as a byte to GPIO. Never fails. In fact, as you've shown, if you are doing several things you can flip bits in GPIO as you go along the interrupt routine and just write GPIOimg to GPIO once at the end. It may cause some PWM jitter so I didn't do it that way.

I wonder if a shadow is necessary? Perhaps we could try it without the shadow register?
Yes and I wouldn't - in that order.

Look, I'm not trying to be a poot about this but I've fought the shadow / r-m-w battles for a long time. I've discovered and discussed way deeper r-m-w issues than simple bsf/bcf PORTx with the guys who designed the silicon. I know what I'm talking about and try to spread the word so that next Tuesday we don't get another one of those 'I turned on an LED and the relay dropped out' questions. All of the things you are trying to do are completely consistent with the shadowed port if you do it I like I did in the PWM and heartbeat LED.

But these days, my lance is heavy, my horse tired, Sancho quit in disgust and the windmills are still there. So is the r-m-w problem. If you want to run baseline/midrange without shadowing, have at it.

Good luck.

FWIW,some of this 629 code came from a project I helped a member with a couple of years ago. He was flashing LEDs w/MOSFETs to drive them and we wound up having to shadow his ports too. Who woulda' thunk it?
 
Last edited:

Thread Starter

R!f@@

Joined Apr 2, 2009
9,918
Will try the suggestions.

Can you address this issue John. Details in post#70

C:
        if(NEW_ENCODER_STATE){
            NEW_ENCODER_STATE = 0;
            switch (ENstate){
                case 0b00000001:
                case 0b00000111:
                case 0b00001000:
                case 0b00001110:
                    increment();
                    break;
                case 0b00000010:
                case 0b00000100:
                case 0b00001011:
                case 0b00001101:
                    decrement();
                    break;
                // ignore others: bad reading
            }// switch
Am I mistaken here?
 

JohnInTX

Joined Jun 26, 2012
4,787
I think the above code needs to match the Encoder input ports to work but I have no idea why it is working as of now.
It doesn't have to match the input bits. In the encoder service, the two arbitrary input bits are treated independently and only at the end are they combined to build ENstate. I did this on purpose so that if you wanted to relocate the inputs, you didn't have to redo the state table. If you have to change an input location, all you have to do is change its mask definition e.g. ENphaseAmask, and rebuild. No other changes necessary. In the code, these 4 lines combine the arbitrary input assignments and generate ENstate in a consistent manner, regardless of where the encoder is connected:
C:
                ENstate = (ENstate << 2);       // shift is to was
                ENstate &= 0b00001100;  // clear out old and unused bits
                                        // then combine new inputs to two bits
                if(newENCODER & ENphaseAmask) ENstate |= 0x01;
                if(newENCODER & ENphaseBmask) ENstate |= 0x02;
The flash on power up may be due to the fact that the output ports float until the PIC gets done initializing. A good practice would be to add pull-downs to the outputs to keep the drivers off until the PIC wakes up. As a test, you should be able to power up with NO PIC installed and have the hardware hold everything in a benign condition. If you added the beeper code that read GPIO to modify the outputs, that could cause problems in operation.

As noted in 'Known Issues' at the top, ENstate is not being initialized. The effect would be a possible wrong way click from the encoder code the first time its moved. We'll fix that shortly.

What is the beep supposed to do? It sounds like the beeper driver is supposed to generate a square wave to make the beeper and that is working i.e. it makes noise?
 
Last edited:

Thread Starter

R!f@@

Joined Apr 2, 2009
9,918
Thanks, I got the encoder part.
The flash over was there in the beginning. I did not bring tht up before. It was there in the Hex file you gave too. So it was not the beeper issue. There are 10k pull downs in the FET ckt. I will further use the EasyPic pull downs tonight and see . When Easy PIC is off there are no flashes. As I turn on the EasyPic the flash comes and settles down. It could be that the FET is always powered separately. So I guess I will find out after I complete the whole circuit. This way at power on everything gets power simultaneously. I will check that part.

The beeper is a cool addition . It beeps when switch is pressed. Something like tht.
 

MMcLaren

Joined Feb 14, 2010
861
If I understand correctly, would this modified beep routine be compatible with the shadow mechanism? The routine doesn't write to the GPIO port but relies instead on the PWM routine updating the port from the modified 'GPIOimg' shadow register.
Code:
/*                                                                 *
 *  Mike McLaren's non-blocking "beep task" runs at 250-usec       *
 *  intervals and produces one or more 32-msec 2000-Hz beeps       *
 *  spaced 32-msecs apart.  Set 'beep' to a value of 'N+N-1'       *
 *  for the number of beeps desired (with a 128 beep limit).       *
 *                                                                 *
 *  beeptask() macro;  #define beeptask(n)  beep = (n+n-1)         *
 *                                                                 */
    static unsigned char duration = 32000/250;

    if(beep)                    // if beep task running
    { if(beep & 1)              // if beep bit 0 == '1'
        GPIOimg ^= 0b00000100;  // toggle skpr (GP2) bit
      if(--duration == 0)       // if duration timeout
      { duration = 32000/250;   // reset 32-ms duration
        beep--;                 // and decrement 'beep'
      }                         //
    }                           //
This code is not isochronous so it shouldn't be placed in front of the PWM driver where it could add jitter to the PWM output. So far I have not been able to eliminate the conditional code to make it isochronous. Any suggestions appreciated. By comparison, here's a tiny isochronous assembly language 'beep' routine (XC8 code);
Code:
/*                                                                 *
 *  Mike McLaren's non-blocking "beep task" runs at 250-usec       *
 *  intervals and produces one or more 32-msec 2000-Hz beeps       *
 *  spaced 32-msecs apart.  Set 'beep' to a value of 'N+N-1'       *
 *  for the number of beeps desired (with a 128 beep limit).       *
 *                                                                 *
 *  beeptask() macro;  #define beeptask(n)  beep = (n+n-1)         *
 *                                                                 */
    asm("movlw   0b00000100   ");   // mask for 'spkr' on GP2
    asm("btfsc   _beep,0      ");   // beep b0 == 1? no, skip,
    asm("xorwf   _GPIOimg,F   ");   // else, toggle 'spkr' bit
    asm("movf    _beep,F      ");   // beep task running?
    asm("skipz                ");   // no, skip, else
    asm("incf    _duration,F  ");   // bump 'duration'
    asm("btfsc   _duration,7  ");   // 32-ms timout? no, skip,
    asm("decf    _beep,F      ");   // else, decrement 'beep'
    asm("bcf     _duration,7  ");   // reset 32-ms timer
 
Last edited:
Top