PIC 12F629 Question.

Thread Starter

R!f@@

Joined Apr 2, 2009
9,918
Frequency first.
First though, I would adjust the TMR0 interrupt time to give you the sound frequency you want. Two interrupts per cycle; 250us is a period of 500us for a frequency of 2000Hz. 3800 Hz is doable with a higher interrupt rate 131usec (131Tcyc at 4MHz) or whatever you decide to use. The PWM and beep flasher will follow along
Depending on what you said I calculated the interrupts per cycle to 208us giving me 2.403KHz...WOW ! this thing is loud.. :eek: .too loud. . Sooo 200us ...still loud. Found 150us to be enough. So I will stick to it. :)

I will not go lower than this but I like to ask, should I choose to make the beep louder (over time , if found too low) I would need to increase this value. Questions is, will an increase ( not above 250us) affect any other part after finishing the code at 150us ?
 
Last edited:

JohnInTX

Joined Jun 26, 2012
4,787
Frequency first.

Depending on what you said I calculated the interrupts per cycle to 208us giving me 2.403KHz...WOW ! this thing is loud.. :eek: .too loud. . Sooo 200us ...still loud. Found 150us to be enough. So I will stick to it. :)

I will not go lower than this but I like to ask, should I choose to make the beep louder (over time , if found too low) I would need to increase this value. Questions is, will an increase ( not above 250us) affect any other part after finishing the code at 150us ?
Nope. All of the other constants are calculated off of that value. The PWM will run at a faster frequency but the duty cycle will follow it automatically. One caution - the constant calculations are not checked to see if the resulting values fit into their respective variables. For example, the prescaler that counts interrupts to drive the 5msec timers is only 8 bits. Calculating 5ms / 150us ~= 33. 5ms/250us = 20. Both values fit in 8 bits so OK. Suppose for example that you set the interrupt to 15us. An 8 bit prescaler gives a period of only 3.84ms, clearly not the 5msec that we want. I usually put something in the code like:
Code:
 #if (msec5PSset >255) // checks calculated value at compile time, raises build error iff bad.
#error "msec5PSset is too big!"
#endif
That way the compiler watches out for me. We can live without it for now.
Your 150us IRQ should be OK based on my earlier observation-based comments. We'll go from there.
 
Last edited:

Thread Starter

R!f@@

Joined Apr 2, 2009
9,918
Got it.
Ummm ! why do you write"iff" ?

The example you provided halts the build, right?

On to switch

Here is what Mike Suggested. What do you think of this
Code:
  char swnew = 0;              // switch sample var
  char swold = 0;              // switch state latch
  char flags = 0;              // switch new press flags
  #define norm 0               // flag for "short" press
  #define long 1               // flag for "long" press
  #define newpress    swnew.3 == 1 && swold.3 == 0
  #define newrelease  swnew.3 == 0 && swold.3 == 1
  while(1)                      //
  { bam();                      // do 25-msecs worth of PWM
    swold = swnew;              // update switch state latch
    swnew = ~gpio;              // sample active lo switches
    if(newpress)                // if "new press"
    { swtmr = 1000/25           // start 1-second timer
      beep = 1;                 // task a "new press" beep
    }                           //
    if(newrelease)              // if "new release"
    { if(swtmr)                 // if not 1-second timeout
      { flags.norm ^= 1;        // toggle "short" flag and
        swtmr = 0;              // clear 1 second timer
      }                         //
    }                           //
    if(swtmr)                   // if timer running
    { swtmr--;                  // decrement it
      if(swtmr == 0)            // if timed out
      { flags.long ^= 1;        // toggle "long" flag
      beeps = 3;                // task two beeps
      }                         //
    }                           //
    if(flags.long)              // if long new press
    {                           //
    }                           //
    if(flags.norm)              // if short new press
    {                           //
    }                           //
  }                             //
If this is a go I have a issue in the following part
define newpress swnew.3 == 1 && swold.3 == 0
I do not think the part in red can be used in MikroC
Will it be something like this " swnew, 3"
 

JohnInTX

Joined Jun 26, 2012
4,787
Ha. 'Iff' is an old habit. In assembler, its not uncommon to comment out big blocks of text for documentation using if 0 and endif. The assembler still parses the text between the macros so it would take the word 'if' as another conditional and be upset not to find a corresponding endif. Also over the years, I've had assemblers object to keywords even in comments and choke. By misspelling 'iff' I get the meaning without the error.

As for the button. What is it supposed to do from a user perspective?

Looking at the code snippet, I'm not sure what 'bam()' is but I probably wouldn't schedule like that. It sounds like it waits for 25ms then does the switch.. But I haven't read back through the thread to see how it all fits and I don't want to be unfair, either. If it looks like it does the job, give it a try.

FWIW, Here's how I would do it. Not necessarily better but it fits with the overall scheme. First, consider what has to be done.
1) The switch has to be sampled periodically-again, never waiting. In my world, that means scheduling the sampling with an interrupt-driven timer or some such providing a debounce interval.
2) Accumulate switch samples, open and closed, and count up when its closed, down when its open. When the switch is either for a long enough time, set/clear a flag to indicate 'open' or 'closed'. That flag is used by other routines as the current switch condition - its guaranteed to be valid at all times so any main routine that wants to read the switch never has to wait, it just checks the flag. As described above, I add a second flag (_REQUEST) that is set once each time the switch is fully debounced. Its like a flip flop - set by the debouncer, cleared when main reads it and does something, set again on the next push.
3) Use the valid flag to determine whether the switch is pressed several times, held for a long time etc. depending on effect the switch should have on the the operation.


Scheduling the sampling and counting for the debouncer is simple. We already have a section in the interrupt routine that runs every 5msec. Define a bit variable (TimeToDebounce) and set it every 5msec. OK. Scheduling done. Note the word 'schedule'. That implies that we are just noting that something has to be done, not that it has to be 'executed' right now. The flag indicates that 5ms has elapsed and its time to sample the switch and debounce it - we just put it on a list of things to do when we get around to it. That's the only thing that has to be done in the interrupt routine. The rule is never to do anything in an interrupt that can be deferred to main.

The sampling / debouncing code goes at the top of the while(1) loop in main and TimeToDebounce schedules it. If the flag is not set, skip it. If the flag is set (5ms has elapsed) clear the flag (for the next 5ms), sample the switch once and accumulate closed/open conditions. When you have enough of either do the two output flags SW_HELD and SW_REQUEST. Note that this debouncer is not in the interrupt routine because it doesn't need to be scheduled that tight. That keeps our interrupt routine lean and mean.

Once the debounce has been serviced, you can examine its two output flags at your leisure and operate the system accordingly. A simple implementation would be to just look at SW_REQUEST and toggle the LEDs on and off accordingly (clearing SW_REQUEST) each time. That would take very little code. More complex schemes (push twice to turn on for example) can be implemented at will without changing any of the switch input/debounce stuff.

Finally, consider how to control the PWM. Turn it off with a flag in the interrupt routine? Set the duty cycle to 0? How to restore the duty cycle? You'll want to inhibit the encoder when the system is 'OFF'. All of this can be done with flags but I think Mike or Joey mentioned a state machine. If its much more than push on/push off, I'd also go that way.

But first.
Decide what the switch does at the top level (ON/OFF/MELT) etc.
Get the switch hardware debounced and presented to the rest of the code with flags.
Use the flags to implement actual system operations.

Off to school for me.
Have fun.
 
Last edited:

Thread Starter

R!f@@

Joined Apr 2, 2009
9,918
Nice....lemme try.
You have missed the switch post. So in a nut shell....
normal press. toggle the GP0 to GP1 (PWM) & vice-versa.
1 sec press --> OFF to ON & vice-versa
 

Thread Starter

R!f@@

Joined Apr 2, 2009
9,918
OK ..I started with this..
First....
C:
#define msec5DEBset 5000/TMR0period_us
It's the 5 ms debounce timer set..( am I saying this right ?)
Q...? at TMR0period_us 150 what will be the time ? and how is it calculated ?

Second.....
C:
unsigned char msec5DEBPreScaler;
unsigned char Debounce_5ms;
Did this looking at ur beep code below
C:
// Buzzer
unsigned char msec5PreScaler;
unsigned char BeepTimer_5ms;
third ... is the request flag
C:
bit Sw_Req;            //Switch Request flag
fourth I added this in the interrupt below beep int. as below

C:
    //---------------------- SERVICE DERIVED TIMERS  ---------------------------
    // Prescaler counts msec5PSset interrupts to make a 5msec period.
    // From that, as many timers can be created as desired.  Set the timer
    // to the number of IRQ periods to time.  The timer will run to 0 and stop.
    // Poll the timer, when its 0, time is out. Do something, reload and go.
    if(msec5PreScaler == 0){            // iff prescaler ran out..
        msec5PreScaler = msec5PSset;    // reset it and run timer(s)
        if(BeepTimer_5ms) BeepTimer_5ms--;
    }
    else
        msec5PreScaler--;
       
    if(msec5DEBPreScaler == 0){          // If DebounPrescaler ran out
        msec5DEBPreScaler = msec5DEBset; // reset it to debounce value
        if(Debounce_5ms) Debounce_5ms--; // if not zero, decrement
    }
    else
        msec5DEBPreScaler--;             // else if not ran out decrement
Good, correct or wrong ?
If wrong , please correct me.
If correct where do I put Switch request flag..

PS.. My head hurts
 

JohnInTX

Joined Jun 26, 2012
4,787
PS.. My head hurts
Sorry! This can be a beating, for sure. But don't worry, you are getting the idea. Instead of a long post, here's what I scratched together earlier in the day adding the switch debounce and ON/OFF control just to see if I was on the right track. I guess so as it did not take very long (not my first rodeo, pard'ner). Here it is. Of note:

The 5ms debounce scheduler can use the same prescaler as the beep timer. It's just a flag (TimeToDebounce) that signals the debounce logic in main.
The debounce logic is done in main instead of the interrupt because timing is not that critical. That is keeping with the rule to keep interrupt routines short and fast. The debounce logic generates BUTTON_HELD and BUTTON_REQUEST flags as described earlier.
The ON/OFF code is at the bottom of main. It just consumes BUTTON_REQUESTS and toggles PWMduty between 0 and the previous setting. It also beeps helpfully (and optionally).
When 'OFF' the encoder is still processed so that it can be turned and its state saved when the thing is OFF.
The fact that the whole debounce ON/OFF thing took about 30min to write and test indicates that the overall structure of the code is sound. I was able to change beep patterns and add a few other things with little rewrite of anything else. There are places where a little cleanup may be in order but mostly OK.
Not a lot of testing done. You may want to step through the debounce logic to see how (if) it works correctly.

Sorry just to dump the code on you but you've got the idea, its late and you can see how things work in actual source. Modify as desired.

Finally, note the #define USE_DEBUG_HEADER_PINOUT and following #ifdef USE_DEBUG_HEADER_PINOUT statements. I can't use MCLR/ as an input with the debug header I am using so I relocated the button to one of the PWM outputs with changes to TRIS etc. You'll notice two different IO maps, one for my pinout and one for yours. The #define is commented out for your pinout.

The .HEX is for your pinout.

G'nite.
EDIT: @R!f@@ The name of the flag that schedules the debouncer is runDebouncer, not TimeToDebounce. Late change, I guess. I did step the debouncer this AM and its working OK.
 

Attachments

Last edited:

Thread Starter

R!f@@

Joined Apr 2, 2009
9,918
Thanks....John
You do not need to rush. Take your time.
I appreciate the time you give me. To me it is priceless.
 

Thread Starter

R!f@@

Joined Apr 2, 2009
9,918
I implemented the switching in to my code.
And it is working beautifully.
I changed the time and the beeps to my needs.
The time I needed for on off was 1 sec and it is done. The system on off when the switch is pressed for 1 sec. If short no change. Sweet.
Now at power on it is off but beeps indication power is applied.
Press switch for 1 sec it beeps. Encoder works.
Press switch for 1 sec it turns off. Brightness is saved until next switch on off.

So.. can the same
C:
#define Switch_Deb_Tiks 200    // 200*5 = 1 sec for switch debounce
counter be used for a normal press GP0 to GP1 toggle and vice-versa ?

{ED}
I have changed the code to the following

C:
/****** 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 - Four Strip Output.
GP2 - Buzzer Output.
GP3 - Switch Input, Active Low, 10K pullup.
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  150      // how many usec per TMR0 IRG. This time X2 -
                                // gives 1/T = Freq for beeper tone.
#define ENdebounce_ms  1        // 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      // Encoder Phase A is on GP4
#define ENphaseBmask  0x20      // Encoder Phase B is on GP5
#define Beepermask    0x04      // Buzzer out GP2
#define PWMoutmask1   0x01      // PWM out is GP0
#define PWMoutmask2   0x02      // PWM out is GP1
#define SwitchMask    0x08      // Switch is on GP3
//#define SwitchPressed (GPIO & ~SwitchMask) // 0 means pressed
#define SwitchOpen (GPIO & SwitchMask)     // 1 means open
#define ENCODER_MASK (ENphaseAmask | ENphaseBmask) // bits with encoder inputs
//*************** CALCULATED CONSTANTS  *******************************
#define TMR0reload 256-TMR0period_us   // what to reload TMR0 to
#define ENdebTIKS ((ENdebounce_ms *1000) / TMR0period_us)
#define PWMdutyKinit ((InitialPWMpct * PWMbaseK)/100)
#define msec5PSset 5000/TMR0period_us  // 5 millisec
#define Switch_Deb_LTiks 200    // 200*5 = 1 sec for switch debounce
#define Switch_Deb_NTiks 4      // 4*5 = 20 msec for switch debounce
//********************** VARIABLES  **********************************
// Power
bit Power_On;       // On off flag
// Encoder
unsigned char curENCODER,newENCODER,lastENCODER,ENdebK,ENstate,GPIOimg;
bit NEW_ENCODER_STATE;
// PWM
unsigned char PWMbaseK,PWMdutyK,PWMduty,PWMdutySetPoint;
// Switch and Buzzer
unsigned char SwitchDebK_L;
unsigned char SwitchDebK_N;
bit runDebouncer_L;
bit runDebouncer_N;
bit Switch_Held_L;
bit Switch_Request_L;
bit Switch_Held_N;
bit Switch_Request_N;
bit Toggle;
bit ON;
unsigned char msec5PreScaler;
unsigned char BeepTimer_5ms;
bit soundBEEPER;
// Beeper pattern.  Bit mapped variable gets shifted right each BeepTIK.
// If LSbit==1 beeper sounds for one BeepTIK.If 0, beeper is off for that TIK.
// By scattering bits in BeepPattern, patterns and different beep times can be
// generated.
typedef unsigned int BeepPatternType;
BeepPatternType BeepPattern;
//unsigned char CurrentDuty;  // for duty cycle change test (Encoder beeps)
#define BeepCtrlmask ((BeepPatternType) 1)// tests LSbit in char or int or long
#define BeepTIK_ms  25                    // ms per tik - Beep duration
#define BeepTimerSet (BeepTIK_ms / 5)     //
// Beeper patterns for int..commented the ones I do not use
//#define Beep_OFF 0b0000000000000000     // no beep (copy/paste for new pattern)
#define Beep_Short1 0b0000000000000001    // 1 short beep--->Switch press Normal
#define Beep_Short2 0b000000000000101     // 2 short beeps-->Innitial On beep
#define Beep_Long1 0b0000000011111111     // 1 long beep---->ON OFF beep
//define Beep_Long2 0b0000111100001111    // 2 long beeps
//#define Beep_Alarm 0b0101010101010101   // 8 short beeps
//#define Beep_Shave_AndA_Haircut 0b1010010110101011
/*********************** 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.
    if(Toggle == 0){
    GPIOimg &= ~PWMoutmask2;  // assume it will be 0
    if(PWMbaseK){
        PWMbaseK--;
        if(PWMdutyK){
           PWMdutyK--;
           GPIOimg |= PWMoutmask2;
        }
    }
    else{
        PWMbaseK = PWMbaseN;
        PWMdutyK = PWMduty;
    }
    GPIO = GPIOimg;    // update port
  //  GPIO.GP0 = 0;
  }
    if(Toggle == 1){
    GPIOimg &= ~PWMoutmask1;  // assume it will be 0
    if(PWMbaseK){
        PWMbaseK--;
        if(PWMdutyK){
           PWMdutyK--;
           GPIOimg |= PWMoutmask1;
        }
    }
    else{
        PWMbaseK = PWMbaseN;
        PWMdutyK = PWMduty;
    }
    GPIO = GPIOimg;    // update port
//   GPIO.GP1 = 0;
  }
// GPIO = GPIOimg;    // update por
    //------------------- SERVICE BUZZER--- ---------------------------
    if(soundBEEPER)
        GPIOimg ^= Beepermask;    // 50% duty PWM at TMR0 rate
    else
        GPIOimg &= ~Beepermask;   // else, beeper output 0

    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 DERIVED TIMERS  ---------------------------
    // Prescaler counts msec5PSset interrupts to make a 5msec period.
    // From that, as many timers can be created as desired.  Set the timer
    // to the number of IRQ periods to time.  The timer will run to 0 and stop.
    // Poll the timer, when its 0, time is out. Do something, reload and go.
    if(msec5PreScaler == 0){            // iff prescaler ran out..
       msec5PreScaler = msec5PSset;     // reset it and run timer(s)
        if(BeepTimer_5ms)               // If Beem timer is > 0
           BeepTimer_5ms--;             // decrement beep timer
           runDebouncer_L = 1;          // Set Switch debouncer flag
           runDebouncer_N = 1;  
     }
    else                                   // else
       msec5PreScaler--;                   // decrement 5 msec prescaler
}// IRQ service

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

void decrement()
{
   if(Power_On){
      if (PWMduty < PWMchangePerClick)
          PWMduty = 0;
      else
          PWMduty -= PWMchangePerClick;
   }
}
//********************* BEEPER CONTROL  *************************************
// Sets pattern, clears timer to force immediate service/terminate current one
void setBeepPattern(BeepPatternType p)
{
    BeepPattern = p;
    BeepTimer_5ms = 0;
}

//************************ 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  --------------------------
    PWMduty = InitialPWMDutyK;            // starting output
    lastENCODER = (GPIO & ENCODER_MASK);  // init the encoder
    //---------------- CLEAR VARIABLES  -------------------------
    // Clearing the variable is necessary for proper operation
    NEW_ENCODER_STATE = 0;
    PWMbaseK = 0;
    ENdebK = 0;
    BeepTimer_5ms = 0;
    Toggle = 0;
// CurrentDuty = 0;
    ENstate = 0;
    curENCODER = 0;
    msec5PreScaler = 0;
    newENCODER = 0;
    soundBEEPER = 0;
    Power_On = 0;
    ON = 0;
    SwitchDebK_L = 0;
    SwitchDebK_N = 0;
    runDebouncer_L = 0;
    runDebouncer_N = 0;
    Switch_Held_L = 0;
    Switch_Request_L = 0;
    Switch_Held_N = 0;
    Switch_Request_N = 0;
    PWMdutySetPoint = 0;
  
  
    //---------------- START TIMER 0 IRQ  -----------------------
    TMR0 = 0;
    INTCON.T0IF = 0;
    INTCON.T0IE = 1;
    INTCON.GIE = 1;
    setBeepPattern(Beep_Short2);
    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
            /********* Below statements can be used if beeeping is desired
                       During Encoder movement ************************
          if (CurrentDuty != PWMduty)      // did duty cycle change?
             setBeepPattern(Beep_Short1);  // Then beep */
        }// if NEW_ENCODER_STATE

        //------------------- GENERATE BEEP PATTERN  --------------------
        // Shifts BeepPattern each time period turning beeper on/off in pattern

        if(BeepTimer_5ms == 0){               // time to do something?
            if(BeepPattern != 0){             // more to do?
             if(BeepPattern & BeepCtrlmask)   // yes, beep on/off according
                                              // to bit in pattern
               soundBEEPER = 1;               // Beep
             else
               soundBEEPER = 0;               // Else don't beep
               BeepTimer_5ms = BeepTimerSet;  // then reload timer
               BeepPattern = BeepPattern >> 1;// shift it right for
            }                                 // next pattern bit
            else
               soundBEEPER = 0;               // no bits in pattern, turn it off
        }
        //------------------- Debounce the Swtich  ---------------------
        // Debounces Swtich, generates
        // Switch_Held == 1 while switch is being processed
        // Switch_Request == 1 ONCE per press, clear it when used
        // Runs ONCE per "runDebouncer' flag. Flag gets set at intervals
        // in Int. service.
        if(runDebouncer_L){                     // If flag set
           runDebouncer_L = 0;                  // Reset it for the next int.
           if(SwitchOpen){                    // If Switch is open
              if(SwitchDebK_L == 0)             // Yes, fully debounced open?
                 Switch_Held_L = 0;             // Yes, clear Held flag
              else
                 --SwitchDebK_L;                // else, count down towards 0
           }
           else                               // Switch is pressed
           if(SwitchDebK_L < Switch_Deb_LTiks)  // still bouncing ?
                 ++SwitchDebK_L;                // Yes, count up
              else                            // count at limit...first time ?
                 if(Switch_Held_L == 0){        // Yes, 'cuz HELD is 0
                    Switch_Held_L = 1;          // so Set HELD
                    Switch_Request_L = 1;       // and set the new press flag
                 }

        }  // Long Debouncer
        //------------------- Power On Off  ------------------------
        if(Switch_Request_L == 1){              // If Switch Req is Set
           Switch_Request_L = 0;                // Clear Switch Req flag the Toggle ON OFF
           if(Power_On){                      // If Req flag ON, see about turning it Off
              Power_On = 0;                   // Clear Power On flag
              ON = 0;
              PWMdutySetPoint = PWMduty;      // Save current PWM value
              PWMduty = 0;                    // And turn PWM Off
              setBeepPattern(Beep_Long1);     // and Beep buzzer
           }
           else{
              PWMduty = PWMdutySetPoint;      // Restore PWM Value
              Power_On = 1;                   // Set Power On flag
              ON = 1;
              setBeepPattern(Beep_Long1);     // and Beep buzzer
           }
        }
        //------------------- Toggle -------------------------------
      if(ON == 1){
      if(runDebouncer_N){                     // If flag set
           runDebouncer_N = 0;                  // Reset it for the next int.
           if(SwitchOpen){                    // If Switch is open
              if(SwitchDebK_N == 0)             // Yes, fully debounced open?
                 Switch_Held_N = 0;             // Yes, clear Held flag
              else
                 --SwitchDebK_N;                // else, count down towards 0
           }
           else                               // Switch is pressed
           if(SwitchDebK_N < Switch_Deb_NTiks)  // still bouncing ?
                 ++SwitchDebK_N;                // Yes, count up
              else                            // count at limit...first time ?
                 if(Switch_Held_N == 0){        // Yes, 'cuz HELD is 0
                    Switch_Held_N = 1;          // so Set HELD
                    Switch_Request_N = 1;       // and set the new press flag
                 }
        }  // Normal Debouncer
      
      
        if(Switch_Request_N == 1){              // If Switch Req is Set
           Switch_Request_N = 0;                // Clear Switch Req flag the Toggle ON OFF
           if(Toggle){                      // If Req flag ON, see about turning it Off
              Toggle = 0;
              setBeepPattern(Beep_Short1);     // and Beep buzzer
           }
           else{
              Toggle = 1;
              setBeepPattern(Beep_Short1);     // and Beep buzzer
           }
        }
      }

    }// main while loop
Works but for two bugs
1. Initially applying power, the first ON produces a short beep instead of long one ( code is set to keep the lights off at initial main PSU On).If the power is not removed the on off beeps normally until the next power cycle. --- Not much of an issue but like to know why
2. Toggling works at low light level. Any brightness higher than ~30% toggling is not working most of the time. The light which is supposed to be OFF remains fully on while the other one can be varied. And sometimes the other way round. Low than30% PWM ( around that value) toggling is flawless. What gives ?
 
Last edited:

JohnInTX

Joined Jun 26, 2012
4,787
What are you trying to do with the 2 PWM outputs and the toggle flag?? I assumed that they tracked each other.
Also, why the two debouncers for the same button?
Not sure what the goals / design parameters are here


A long beep after power up may indicate incomplete initialization but I haven't run it up. Let's understand the toggling first.
 

Thread Starter

R!f@@

Joined Apr 2, 2009
9,918
Just messing with the code to see if my approach works. It doesn't for sure. Not 100%

Just dunno why it works perfectly at low brightness

The two debouncer works. One for 1 sec ( ON OFF). short one to toggle.

Forget the beep. It's the toggling I am trying to fix.
 
Last edited:

Thread Starter

R!f@@

Joined Apr 2, 2009
9,918
When GP0 is PWMing GP1 is off.
When GP1 is PWMing GP0 is off

That is what a normal press does. It toggles the PWM between the two outputs.
Like I said it beeps properly and at any duty cycle that is less that around 30% toggling works flawlessly but when brightness is high the output that is supposed to be off is lit at full brightness while the output that is on works 100% .
 

JohnInTX

Joined Jun 26, 2012
4,787
Hmmm..
In increment() it forcesPWMduty to zero if its not PowerOn but not in decrement(). That could cause confusion.

Question: for the button, a long push turns both on and off and a short push toggles between the two PWM outputs (at the same duty cycle). Is that correct?

Observations:
If you want to switch the PWM outputs separately, you don't have to do separate PWM code for each. One better way might be to sample 'Toggle' when it has to turn an output ON and just set the mask for the appropriate bit:
Code:
...  whenPWM out needs to be 1
if (Toggle)
   GPIOimg |= PWMmask1;
else
   GPIOimg |= PWMmask2;

  GPIO = GPIOimg;
I would probably prefer a more general approach that did not require the IRQ PWM to make assumptions about the toggle logic.
Revert to setting both PWM bits as before to eliminate the duplicate PWM code.
Add another mask (PWMcontrolmask) that is ANDed with GPIOimg after the PWM and just before writing to GPIO.
In main, set/clear corresponding bits in PWMcontrolmask according to the system mode:
OFF: both bits = 0
PWM1: set only the bit under PWMoutmask1
PWM2: set only the bit under PWMoutmask2
BOTH: set both bits.
Toggle then just flips 2 bits in PWMcontrolmask. Simple.

Since you no longer turn it off by setting PWMduty to 0, you don't have to remember and restore it for on/off. You can dispense with the PWMdutysetpoint stuff.
In increment()/decrement(), you still should look at ON/OFF (to allow the encoder to keep up to date) but just ignore changes to PWMduty when its off.

I guess what you have is OK for the switch logic - I'd probably do it simpler. For starters, the debouncer is just that, it debounces the switch, any other timing should be done separately. I never considered replicating the debouncing with different times.. If it works, OK but I might think about something like this - independent of the debouncer like I did the original on/off code:

Code:
//********************** PROCESS DEBOUNCED SWITCH  ***********************
// This is done in the main loop

if (..REQUEST) // button is debounced - (switch will be HELD too)
    clear REQUEST
    load a new derived 5ms timer with the time corresponding to a long push
    set flag: TIMING SWITCH

//************************* SWITCH PUSHED - TIME LONG OR SHORT  ***********
// This is done in the main loop
if TIMING SWITCH
   if HELD && timer >0 // continue
   else
       if !HELD set SHORT_REQUEST // button cleared before timer
       else set LONG_REQUEST
    clear flag: TIMING SWITCH

//************************* PROCESS TIMED SWITCH REQUESTS  *******************
// This is done in the main loop
    if(LONG_REQUEST)
        toggle ON/OFF
   if(SHORT_REQUEST)
       toggle PWMcontrol mask bits to change PWM outputs.
 

MMcLaren

Joined Feb 14, 2010
861
If you don't clear both PWM output bits in the "GPIOimg" variable, there's a chance you'll leave one of the output bits on when you change the "Toggle" variable. When that happens the old PWM channel will run at full brightness.
Code:
if(Toggle == 0){
    GPIOimg &= ~(PWMoutmask2|PWMoutmask1);   // clear both PWM output bits
    if(PWMbaseK){
Instead of using two separate PWM routines, why not use an OutputMask variable to select the active output in a single PWM routine and simply toggle the OutputMask variable in your switch code. That is, initialize the OutputMask variable to either the PWMoutmask1 or PWMoutmask2 constant and then toggle OutputMask between those two constants in your 'short' new press routine;
Code:
/*
 *  alternate simple PWM routine
 */
    GPIOimg &= ~(PWMoutmask2|PWMoutmask1);  // clear both PWM output bits
    if(PWMbaseK < PWMdutyK)                 // if PWM step < duty cycle
      GPIOimg |= OutputMask;                // set the active PWM output bit
    if(++PWMbaseK == PWMbaseN)              // if end-of-period
      PWMbaseK = 0;                         // reset PWM step counter
    GPIO = GPIOimg;                         // update PWM outputs
Code:
    if(Switch_Request_N == 1){                        // If 'short new press' flag
        Switch_Request_N = 0;                         // Clear the flag,
        OutputMask ^= (PWMoutmask2|PWMoutmask1);      // toggle output select bits,
        setBeepPattern(Beep_Short1);                  // and Beep buzzer
    }
Good luck on your project.

Cheerful regards, Mike
 
Last edited:

Thread Starter

R!f@@

Joined Apr 2, 2009
9,918
Thank you both for the suggestions. I will try them both.
Last night I manage to fix the PWM issue but it messed up the beep code :eek:, and I am back to square one.
I will try both suggestions tonight.
@MMcLaren
You showed a remote project you did in an earlier post.
I was wondering if you have simple approach to a ceiling light I have. It has a remote and warm and white leds. The remote works but the light does not respond. I can switch lights manually. I believe it's the 8 pin chip that is not responding. Remote has brightness adjust.
by the way the chip no. is sanded off. Friggin Chinese cloners....:mad:
 

Thread Starter

R!f@@

Joined Apr 2, 2009
9,918
If you don't clear both PWM output bits in the "GPIOimg" variable, there's a chance you'll leave one of the output bits on when you change the "Toggle" variable. When that happens the old PWM channel will run at full brightness.
Code:
if(Toggle == 0){
    GPIOimg &= ~(PWMoutmask2|PWMoutmask1);   // clear both PWM output bits
    if(PWMbaseK){
Wow ! This fixed everything.
I thought I would try this approach to check and it is switching no matter what the brightness is. Clumsy me
Thank you very much to both of you. Especially John. You are always here when I needed
And thank you Mike for the advices and suggestions.

So the code is working but I will try both the suggestions in a fresh code. need to understand what the heck I am doing wrong. :oops:

PS. I admit I took that approach cause I understood it easily and it made sense to my current problem . The rest of the John's and Mike's suggestions will take a bit long to get via this empty skull of mine.
Luv u guys.
Take care.
 
Last edited:

Thread Starter

R!f@@

Joined Apr 2, 2009
9,918
I got the Encoder I ordered. Wired it and it works.
One problem.
It was a detent one. The one that clicks and stops.
The detent one I had had no issue in brightness variation.
But this one varies 3 levels per detent.
If I start with "OFF", I start to rotate very slowly. One click, brightness goes from off, 1,2,3 and stops another click brightens goes from 3,4,5,6.
I like to fix this issue. Would a delay fix it or do I need to eat my brains out.
 
Top