Author Topic: Button Interrupt Failing [PIC][XC8]  (Read 14427 times)

0 Members and 1 Guest are viewing this topic.

Offline blewisjr

  • Frequent Contributor
  • **
  • Posts: 301
Button Interrupt Failing [PIC][XC8]
« on: May 27, 2013, 12:35:05 am »
Hello Everyone,

This is a total stumper that I have been working on for hours.  First off I bit the bullet and moved my whole alarm clock from assembler to C.  YEY for me so much more productive actually.  :-DD

I have recently really hit a solid  |O moment.

Obviously to have an alarm clock you need buttons.  Well I have buttons and so far every single button works correctly minus this damn alarm enable button.  Essentially you press the button and it enables the capability for the alarm to go off at it's designated time.  When you start the program up and press the enable button it works sometimes not all the time.  If you use any other button before hitting this one it does not work.  If you get it to work right off the bat and hit any other buttons it stops working.  If the alarm goes off and you press it everything works but then you can't re enable the alarm after changing the time.  It is so weird I have ran through this code for the last 3 hours and am not finding the issue.  Another note is that if I change the alarm time while this is enabled the alarm never goes off.  This is so weird because until I put in this button everything was working great.
The buttons do not bounce and are de bounced using caps.

Really maybe someone here can see the obvious issue because I am getting no where at this point.  The last thing I need to do is get this feature + snooze in and the clock is done ugh!!!!!  |O |O |O

OH and sorry for the really messy code I tried to keep it neat and tidy!

OH one more thing sometimes the only way to get the alarm to work is to re program the chip and even sometimes that does not work!!!!! WTF!!!

For some reason I think something is hanging somewhere but at the same time does not effect the other interrupts so I am not sure what is going on.  The hardware debugger is not helping me at all here.

Code: [Select]
#include <xc.h>

// PIC16F1782 Configuration Fuse Settings

// CONFIG1
#pragma config FOSC = INTOSC    // Oscillator Selection (INTOSC oscillator: I/O function on CLKIN pin)
#pragma config WDTE = OFF       // Watchdog Timer Enable (WDT disabled)
#pragma config PWRTE = OFF      // Power-up Timer Enable (PWRT disabled)
#pragma config MCLRE = OFF      // MCLR Pin Function Select (MCLR/VPP pin function is digital input)
#pragma config CP = OFF         // Flash Program Memory Code Protection (Program memory code protection is disabled)
#pragma config CPD = OFF        // Data Memory Code Protection (Data memory code protection is disabled)
#pragma config BOREN = ON       // Brown-out Reset Enable (Brown-out Reset enabled)
#pragma config CLKOUTEN = OFF   // Clock Out Enable (CLKOUT function is disabled. I/O or oscillator function on the CLKOUT pin)
#pragma config IESO = OFF       // Internal/External Switchover (Internal/External Switchover mode is disabled)
#pragma config FCMEN = OFF      // Fail-Safe Clock Monitor Enable (Fail-Safe Clock Monitor is disabled)

// CONFIG2
#pragma config WRT = OFF        // Flash Memory Self-Write Protection (Write protection off)
#pragma config VCAPEN = OFF     // Voltage Regulator Capacitor Enable bit (Vcap functionality is disabled on RA6.)
#pragma config PLLEN = OFF      // PLL Enable (4x PLL disabled)
#pragma config STVREN = ON      // Stack Overflow/Underflow Reset Enable (Stack Overflow or Underflow will cause a Reset)
#pragma config BORV = LO        // Brown-out Reset Voltage Selection (Brown-out Reset Voltage (Vbor), low trip point selected.)
#pragma config LPBOR = OFF      // Low Power Brown-Out Reset Enable Bit (Low power brown-out is disabled)
#pragma config LVP = OFF        // Low-Voltage Programming Enable (High-voltage on MCLR/VPP must be used for programming)

// Global Variables

// display digit lookup table
// each segment pin needs to be turned on to make a digit
// the circuit has the A-G pins hooked up to a I/O port where A-G = 0-6 respectively
// the format for the bits is 0bGFEDCBA
volatile unsigned char digit_tbl[10] = {
    0b00111111,     // digit 0
    0b00000110,     // digit 1
    0b01011011,     // digit 2
    0b01001111,     // digit 3
    0b01100110,     // digit 4
    0b01101101,     // digit 5
    0b01111101,     // digit 6
    0b00000111,     // digit 7
    0b01111111,     // digit 8
    0b01101111      // digit 9
};

volatile unsigned char interrupt_cnt = 0;       // number of times interrupt is called (250 = 1 second)
volatile unsigned char seconds = 0;

// when clock is started it defaults to 12:00
volatile unsigned char hours = 12;
volatile unsigned char minutes = 0;
volatile unsigned char alarm_hours = 12;
volatile unsigned char alarm_minutes = 1;
volatile bit alarm_active = 0;
volatile bit alarm_set_enabled = 0;
volatile bit alarm_enabled = 0;

// extracts the first digit from an integer ie. the 1 in the number 12
unsigned char extract_dig1(volatile const unsigned char * value)
{
    return *value / 10;
}

// extracts the second digit from an integer ie. the 2 in the number 12
unsigned char extract_dig2(volatile const unsigned char * value)
{
    return *value % 10;
}

// updates the display digits
void update_display(volatile const unsigned char * h_val, volatile const unsigned char * m_val)
{
    if (LATB0) {
        // Display 0 was updated last update digit 1
        LATB0 = 0;                             // turn off display 0
        LATB1 = 1;                             // turn on display 1
        LATC = digit_tbl[extract_dig2(h_val)]; // load the segments with the proper value
        return;
    } else if (LATB1) {
        // Display 1 was updated last update display 2
        LATB1 = 0;                             // turn off display 1
        LATB2 = 1;                             // turn on display 2
        LATC = digit_tbl[extract_dig1(m_val)]; // load the segments with the proper value
        return;
    } else if (LATB2) {
        // Display 2 was updated last update display 3
        LATB2 = 0;                             // turn off display 2
        LATB3 = 1;                             // turn on display 3
        LATC = digit_tbl[extract_dig2(m_val)]; // load the segments with the value 0
        return;
    } else {
        if (*h_val > 9) {
            // Either display 3 was updated last or no display is enabled (first interrupt cycle only)
            LATB3 = 0;                             // turn off display 3
                                                   // on first run this is a wasted instruction but saves program space
            LATB0 = 1;                             // turn on display 0
            LATC = digit_tbl[extract_dig1(h_val)]; // load the segments with the proper value
            return;
        } else {
            // we do not need to use display 0 so update display 1 instead
            LATB3 = 0;                             // turn off display 3
            LATB1 = 1;                             // turn on display 1
            LATC = digit_tbl[extract_dig2(h_val)]; // load the segments with the proper value
            return;
        }
    }
}

void interrupt interrupt_service(void)
{
    // Check for Timer0 overflow interrupt
    if (TMR0IE && TMR0IF) {
        TMR0IF = 0;         // clear interrupt flag

        interrupt_cnt++;    // update the interrupt called count

        // Update our time if necessary
        if (interrupt_cnt == 250) {
            // One second has passed
            seconds++;
            if (seconds == 60) {
                seconds = 0;
                minutes++;
                if (minutes == 60) {
                    minutes = 0;
                    hours++;
                    if (hours == 13) {
                        hours = 1;
                    }
                }
            }

            // Toggle alarm if triggered
            if (alarm_active)
                LATA0 ^= 1;
        }

        // The overall display is a multiplexed 4 display 7 segment unit
        // displays corrispond to pins on PORTB in this circuit.
        // Display 0 is on LATB0 - Display 3 is on LATB3
        // In the circuit these displays are switched through NPN transistors

        LATC = 0;           // Clear all lit segments to prevent ghosting.

        // Update the appropriate display for this interrupt cycle

        if (alarm_set_enabled) {
            // we need to display the alarm so we can set the time
            update_display(&alarm_hours, &alarm_minutes);
            return;
        } else {
            update_display(&hours, &minutes);
            return;
        }
    } else if (IOCIE && IOCIF) {
        if (IOCAF1) {
            IOCAF1 = 0;     // clear interrupt flag
            // need to update to appropriate hours
            if (alarm_set_enabled) {
                alarm_hours++;
                if (alarm_hours == 13)
                    alarm_hours = 1;
                return;
            } else {
                hours++;
                if (hours == 13)
                    hours = 1;
                return;
            }
        } else if (IOCAF2) {
            IOCAF2 = 0;     // clear interrupt flag
            // need to update appropriate minutes
            if (alarm_set_enabled) {
                alarm_minutes++;
                if (alarm_minutes == 60)
                    alarm_minutes = 0;
                return;
            } else {
                minutes++;
                if (minutes == 60)
                    minutes = 0;
                return;
            }
        } else if (IOCAF3) {
            IOCAF3 = 0;             // clear interrupt flag
            alarm_set_enabled ^= 1; // enable alarm set mode
            return;
        } else if (IOCAF5) {
            IOCAF5 = 0;             // clear interrupt flag
            alarm_enabled ^= 1;     // toggle alarm_enabled state
            return;
        }
    }
}

void main(void)
{
   /*
    * OSCCON Register
    * bit 0 = Software PLL Enable
    * bit 6:3 = Internal oscillator frequency
    * bit 2 = Unimplemented
    * bit 1:0 = system clock select
    */
    OSCCON = 0b01101000;                // 4 MHZ oscillator frequency clk selected via FOSC fuse
    ANSELA = 0;                         // ensure PORTA analog inputs are disabled
    ANSELB = 0;                         // ensure PORTB analog inputs are disabled
    TRISA = 0b00111110;                 // set RA0 as output RA1 - RA5 as input
    TRISB = 0;                          // set PORTB data direction to output
    TRISC = 0;                          // set PORTC data direction to output
    PORTA = 0;
    LATB = 0;
    LATC = 0;
    TMR0 = 0;                           // clear the Timer0 counter and prescaler

    // Enable interrupt on change falling edge for PIN RA1 - RA5
    IOCAN = 0b00111110;

    /*
     * INTCON
     * bit 7 = Global Interrupt Enable
     * bit 6 = Periferal Interrupt Enable
     * bit 5 = Timer0 Overflow Interrupt Enable
     * bit 4 = INT External Interrupt Enable
     * bit 3 = Interrupt-On-Change Interrupt Enable
     * bit 2 = Timer0 Overflow Interrupt Flag
     * bit 1 = INT External Interrupt Flag
     * bit 0 = Interrupt-On-Change Interrupt Flag
     */
    INTCON = 0b10101000;        // enable global interrupts, enable Timer0 interrupt and pin change interrupts

    /*
     * OPTION_REG Register
     * bit 7 = Weak Pull-Up Enable
     * bit 6 = Interrupt Edge Select
     * bit 5 = Timer0 Clock Source Select
     * bit 4 = Timer0 Source Edge Select
     * bit 3 = Prescale Assignment
     * bit 2:0 = Prescale Rate
     */
    OPTION_REG = 0b11010011;            // bit 7:6 is not used by Timer0
                                        // set timer to use internal instruction clock (FOSC/4)
                                        // bit 4 is not used and prescale Timer0 by 1:16
                                        // this will give us a overflow interrupt of aprox. 4ms
    while (1)
    {
        if (alarm_enabled) {
            LATB4 = 1;
            if (hours == alarm_hours && minutes == alarm_minutes)
                alarm_active = 1;
            else
                alarm_active = 0;
        }
        else {
            LATB4 = 0;
            alarm_active = 0;
        }
    }
}
 

Offline croberts

  • Regular Contributor
  • *
  • Posts: 94
  • Country: us
Re: Button Interrupt Failing [PIC][XC8]
« Reply #1 on: May 27, 2013, 01:57:32 am »
I'm not sure how C handles it but in assembler it is essential to save context when entering the interrupt routine and then recovering it before returning to the main loop. I don't see this in your code but maybe C somehow does it for you.
« Last Edit: May 27, 2013, 03:07:06 am by croberts »
 

Offline David_AVD

  • Super Contributor
  • ***
  • Posts: 2607
  • Country: au
Re: Button Interrupt Failing [PIC][XC8]
« Reply #2 on: May 27, 2013, 02:02:23 am »
That code doe seem to be doing a lot in the ISR.  I tend to do the minimum amount in the ISR and use flags set there to perform tasks in the main loop.
 

Offline blewisjr

  • Frequent Contributor
  • **
  • Posts: 301
Re: Button Interrupt Failing [PIC][XC8]
« Reply #3 on: May 27, 2013, 02:13:43 am »
That code doe seem to be doing a lot in the ISR.  I tend to do the minimum amount in the ISR and use flags set there to perform tasks in the main loop.

Actually there is really not too much going on in the ISR at any one given time.  Just basic multiplexed display updates, the time keeping for the clock and some flag updates.  There is only one ISR vector on PIC16 chips so you can't break it out into separate vectors like you can on the AVR.  Hence the reason for the ISR flag checks to see which set of interrupt code needs to get executed.  Truthfully this is one of the largest downfalls of the PIC.

As for state saving I am not sure I would come to think that the compiler generates the code for it but there really is not much state to save as there is only one accumulator so I do not think it is that.  Everything was working fine until I had to add the IOCAF5 button check which lead to nesting the alarm code in the main loop under the alarm_enabled flag check.
 

Offline David_AVD

  • Super Contributor
  • ***
  • Posts: 2607
  • Country: au
Re: Button Interrupt Failing [PIC][XC8]
« Reply #4 on: May 27, 2013, 02:23:47 am »
Do you have a schematic ?
 

Offline Paul Price

  • Super Contributor
  • ***
  • Posts: 1419
Re: Button Interrupt Failing [PIC][XC8]
« Reply #5 on: May 27, 2013, 02:40:03 am »
How do you know the button does not bounce? Have you hardware circuit to prevent bounce? If, not scope it.

Try looking at the changes. It looks like you are toggling the alarm enable every second.
All changes by me have PPC in the comment line somewhere

I would change the volatile declarations to static for the few vars  you have, esp. the alarm_active

Explain what your code's intention is here:
   if (interrupt_cnt == 250) {
            // One second has
            passed


            interrupt_cnt=0;  //must reset the counter or else you get extra 5 counts and you will wake up late  /b]


            seconds++;
            if (seconds == 60) {
                seconds = 0;
                minutes++;
                if (minutes == 60) {
                    minutes = 0;
                    hours++;
                    if (hours == 13) {
                        hours = 1;
                    }
                }
            }

            // Toggle alarm if triggered
                                                                         //PPC  What  does "LATA0"  mean, refer to, action of?? Please explain
                                                                        //PPC is "LATA0" a not a variable...it is not declared.
                                                                        //Must be some register or flag. Please explain.

         if (alarm_active)
                          LATA0 ^= 1; //PPC
     
         
   
« Last Edit: May 27, 2013, 03:10:59 pm by Paul Price »
 

Offline Rufus

  • Super Contributor
  • ***
  • Posts: 2094
Re: Button Interrupt Failing [PIC][XC8]
« Reply #6 on: May 27, 2013, 02:51:44 am »
You have PORTA bit 4 set as input with interrupt on change enabled and nothing in the interrupt handler for it.

One change on that pin will have the processor permanently taking interrupts although it looks like it should fumble through and sort of work regardless.

Not your main problem but:

you should also be setting interrupt_cnt back to 1 when it reaches 250.

you should move the alarm active check from the main loop to the interrupt handler.

You have more things than you need declared volatile. If you move the alarm active check nothing needs to be volatile.

 

Offline blewisjr

  • Frequent Contributor
  • **
  • Posts: 301
Re: Button Interrupt Failing [PIC][XC8]
« Reply #7 on: May 27, 2013, 02:55:00 am »
Thanks for the efforts I just found the issue.  I was getting a false positive on an interrupt flag somewhere.  Found this by lots of trial and error.  I realized the actual alarm check code under certain conditions was never reached which meant I was stuck in the ISR until another interrupt aka a button press kicked it out if and only if it cleared out the false positive.

Here is the modified code.  I started by instead of clearing just one IOCAF bit I changed it to clear the whole register.  Then I added an else to do the same incase of a false positive.

Works perfectly now.  While I was testing I did notice a switch bounce when pressing the buttons quickly so I might need to drop the value of the caps or the resistor so they charge faster.

Ah thanks Rufus did not realize I forgot to reset the interrupt counter that would explain the small deviation I see on my scope every so often.

Code: [Select]
} else if (IOCIE && IOCIF) {
        if (IOCAF1) {
            IOCAF = 0;     // clear interrupt flag
            // need to update to appropriate hours
            if (alarm_set_enabled) {
                alarm_hours++;
                if (alarm_hours == 13)
                    alarm_hours = 1;
                return;
            } else {
                hours++;
                if (hours == 13)
                    hours = 1;
                return;
            }
        } else if (IOCAF2) {
            IOCAF = 0;     // clear interrupt flag
            // need to update appropriate minutes
            if (alarm_set_enabled) {
                alarm_minutes++;
                if (alarm_minutes == 60)
                    alarm_minutes = 0;
                return;
            } else {
                minutes++;
                if (minutes == 60)
                    minutes = 0;
                return;
            }
        } else if (IOCAF3) {
            IOCAF = 0;             // clear interrupt flag
            alarm_set_enabled ^= 1; // enable alarm set mode
            return;
        } else if (IOCAF5) {
            IOCAF = 0;              // clear interrupt flag
            alarm_enabled ^= 1;     // toggle alarm_enabled state
            return;
        } else {
            IOCAF = 0;
            return;
        }
 

Offline Rufus

  • Super Contributor
  • ***
  • Posts: 2094
Re: Button Interrupt Failing [PIC][XC8]
« Reply #8 on: May 27, 2013, 03:12:27 am »
You should take note of the rest of what I said. Your code has one of the most horrid kind of bugs. In this program it will only cause a glitch which will be quickly corrected in others it could be disastrous.
 

Offline blewisjr

  • Frequent Contributor
  • **
  • Posts: 301
Re: Button Interrupt Failing [PIC][XC8]
« Reply #9 on: May 27, 2013, 03:42:08 am »
You should take note of the rest of what I said. Your code has one of the most horrid kind of bugs. In this program it will only cause a glitch which will be quickly corrected in others it could be disastrous.

Yes I know PORTA4 is set as input there is actually a button with a pull-up resistor on it right now.  That is for the snooze button did not get around to implementing it yet as I wanted to fix the other darn bugs first.  But it is being held high and set to interrupt on falling edge so in this case it should not be causing any bugs but yes I am implementing that next.

I will also move the alarm check code to the ISR.  One thing I must be misunderstanding is I thought variables used in the ISR had to be volatile or they would get optimized out.
 

Offline poorchava

  • Super Contributor
  • ***
  • Posts: 1576
  • Country: pl
  • Troll Cave Electronics!
Re: Button Interrupt Failing [PIC][XC8]
« Reply #10 on: May 27, 2013, 06:19:05 am »
I would look at the disassembly and check if there is really an interrupt and context save / restore. Had some bad experience with XC8 in this regard.
I love the smell of FR4 in the morning!
 

Offline blewisjr

  • Frequent Contributor
  • **
  • Posts: 301
Re: Button Interrupt Failing [PIC][XC8]
« Reply #11 on: May 27, 2013, 09:31:18 am »
I would look at the disassembly and check if there is really an interrupt and context save / restore. Had some bad experience with XC8 in this regard.

In all honesty it has been nothing but headaches with XC8.  The code bloat is insane.  I did a comparison of the free compile vs the Pro Trial and you are quite literally looking at close to a 25% difference with my particular firmware.  What makes it worse is if you do look at the disassembly it is all intentional artificial code bloat. With random 3 - 4 goto's tossed everywhere which is 2 - 3 branches too many.  Never saw such horror.

As for the context save keep in mind the enhanced mid-range has a automatic context save built in the hardware for interrupts where it automatically saves WREG, STATUS, BSR, FSR,  and PCLATH, as well as the PC address.
 

Offline Rufus

  • Super Contributor
  • ***
  • Posts: 2094
Re: Button Interrupt Failing [PIC][XC8]
« Reply #12 on: May 27, 2013, 11:54:09 am »
I will also move the alarm check code to the ISR.

That is what will fix the horrid bug. I'm trying to let you discover what the bug is.
 

Offline poorchava

  • Super Contributor
  • ***
  • Posts: 1576
  • Country: pl
  • Troll Cave Electronics!
Re: Button Interrupt Failing [PIC][XC8]
« Reply #13 on: May 27, 2013, 12:01:19 pm »
I would look at the disassembly and check if there is really an interrupt and context save / restore. Had some bad experience with XC8 in this regard.

In all honesty it has been nothing but headaches with XC8.  The code bloat is insane.  I did a comparison of the free compile vs the Pro Trial and you are quite literally looking at close to a 25% difference with my particular firmware.  What makes it worse is if you do look at the disassembly it is all intentional artificial code bloat. With random 3 - 4 goto's tossed everywhere which is 2 - 3 branches too many.  Never saw such horror.

As for the context save keep in mind the enhanced mid-range has a automatic context save built in the hardware for interrupts where it automatically saves WREG, STATUS, BSR, FSR,  and PCLATH, as well as the PC address.

The last remark is not exactly right - compiler is so dumb, that in some cases it will do 'emergency context save' which means it will put ALL the core registers and probably something else too on the stack with software, no hardware involved. This causes context switch to be over 100 cycles in some cases. Another case of Microchip trying to show you that you need to buy the paid version instead of using the free one, I guess. Best way I found to avoid it was to not call any function withing ISR and specify explicitly which registers should be saved on ISR entry (check our the XC8 manual for specifics). I'm sure there still my thread somewhere in depths of this forum.
I love the smell of FR4 in the morning!
 

Offline blewisjr

  • Frequent Contributor
  • **
  • Posts: 301
Re: Button Interrupt Failing [PIC][XC8]
« Reply #14 on: May 28, 2013, 02:15:20 am »
Yes I much prefer AVR when it comes to C code.  Even their assembler is a little bit better due to multiple GPR's.  I like the actual PIC chips themselves much better just because of the shear feature set you get off of comparable devices.  I guess you can't win everywhere.  Originally the clock was being done on an AVR but I had to switch to my PIC chips I had laying around because on the ATtiny2313 I ran out of pins I could use.  So I had to switch to a 28 Pin PIC as I don't have any AVR's over 20 Pins.  There might be a way to make the 20 pin chip work but that will be left for a second project or maybe a alarm clock rework project.  I am sure there is a better way to do this without using as many pins.  Either way I am still excited that my first project is almost complete.  Just need to do the schematic and the PCB/Project case.  Feels really good to complete something like this.
 

Offline poorchava

  • Super Contributor
  • ***
  • Posts: 1576
  • Country: pl
  • Troll Cave Electronics!
Re: Button Interrupt Failing [PIC][XC8]
« Reply #15 on: May 28, 2013, 07:13:15 am »
XC8 bullshit was the final factor which made me go for ARM-Cortex (together with poor performance and silicon bugs). Learning curve is kinda steep at the beginning, but when you have all the basic stuff figured out it only gets easier and easier.

 
I love the smell of FR4 in the morning!
 

Offline blewisjr

  • Frequent Contributor
  • **
  • Posts: 301
Re: Button Interrupt Failing [PIC][XC8]
« Reply #16 on: May 28, 2013, 10:19:02 am »
Oh yes the silicon bugs the joy.  It is amazing how those things get through the process.  Some of them are so bad they even say don't do this or use this feature there is no work around.  Seems kind of pointless.  I understand they have lots of older rev chips out there but you think they would have extensive testing to avoid that kind of stuff before they go to mass production.  Oh well.

I do like your motto there.  PIC has poor C support from the hobby point of view.  AVR often gimps features on a lot of the chips making it hard to get a perfectly affordable one for the hobby project.  The next best thing is ARM.  The main thing that gimps ARM is the package formats.  Would be nice to see some ARM DIPs.

As for learning curve they all have one.  Some with the chip and some with the development tools.  I think Atmel has the dev tools down well at least on windows.  PIC has so many chip variations it makes it easy to pick one for the project so I would have to say PIC has the chip side down in the 8 bit world.  ARM is just a powerhouse on every front but are over kill for tons of projects.  But I don't want to start a chip war.  I might have to go back and rewrite the alarm clock in ASM and see the factor of 3 better code I get out of it compared to XC8 and I am not even particularly good at assembler lol.
 

Offline MacAttak

  • Supporter
  • ****
  • Posts: 682
  • Country: us
Re: Button Interrupt Failing [PIC][XC8]
« Reply #17 on: May 30, 2013, 12:59:11 am »
I would never assume that manually written assembly would outperform straightforward high-level code (like C) by 3x unless you have a lot of time under your belt in assembly targeted to the specific platform.
 

Offline C

  • Super Contributor
  • ***
  • Posts: 1345
  • Country: us
Re: Button Interrupt Failing [PIC][XC8]
« Reply #18 on: May 30, 2013, 02:32:53 am »
This is just me,
but if I was doing this, I might think
8 segments = 8 pins
6 digits = 6 pins
need some additional leds
If I use one more digit pin I could get 8 leds
need switches
Use digit pins to mux digit counts worth of switches in to one pin
need more than one switch pressed at a time, so need a diode in-line with each switch   
so
8 segments = 8 pins
8 leds  + 6 digits = 7 pins
7 switches = 1 pin
just need 16 pins

In display mux if i used a array containing segment data to update the display, I could display any thing possible and not have to change what section to display something else on the display. I could change the array data in the slow code section. Would make it easy to switch from time display to alarm time display to alarm time2 and to _____. I could even flash the display from slow code.

but as I said this is just me
C



 

Offline blewisjr

  • Frequent Contributor
  • **
  • Posts: 301
Re: Button Interrupt Failing [PIC][XC8]
« Reply #19 on: May 30, 2013, 10:55:21 am »
I would never assume that manually written assembly would outperform straightforward high-level code (like C) by 3x unless you have a lot of time under your belt in assembly targeted to the specific platform.

In the case of XC8 Free Edition a 12yr old can out optimize the compiler's asm generation.  The general idea is with the XC8 free it looks like they run through the parsing and translation to ASM like any compiler would but then from there they go write to assembling it.  They never even do a mediocre single pass optimization.  The end result is tons of extraneous code that should not have to be there it is just there because of the way a compiler parses the code and maps it out to asm.  The optimization is what cleans it up but again XC8 free does not even give you a basic single pass optimization.  So in return your code size is 20 - 30% larger and can become unstable in timing because there are so many extra branches, goto's etc....

You don't have to be a assembler programmer expert to look at the disassembly and go WTF is this compiler doing.  Microchip really needs to give the compiler a single level optimization.  Not asking for everything including the kitchen sink just a restricted set of optimization rules that will clean up some of the mess compilers make when compiling.

----------------

As for my pin count it is a 4 digit 7 segment multiplexed common cath display.  I avoid the  decimal and just run the colon full time off a resistor to V+ and cathode to gnd.

7 segments = 7 pins
4 digits = 4 pins
1 LED = 1 pin (alarm on indicator)
Buzzer = 1 pin
5 tactile push spst = 5 pins (each one has a different function)

Total pins = 18. 

(Keep in mind I am a total newb to designing circuits)

My ATtiny only has 18 io if you turn off the reset pin (lose the ability to program chip without bootloader).  So in reality you only have maybe 17 I/O pins or so to work with if you use only overflow interrupts and no external crystals.  It would work but you lose a pin when you need to do the timer compare to get the timing down. So 16 pins total if using the more accurate hardware timer compare.

Now if I ran off a 8 bit timer overflow like I do on the PIC but with a 1:8 prescale in stead of the 1:16 the pic gives me I would need one extra variable to manage the time due to needing the tick twice for the 4ms display update rate because at 1:8 the isr would go off every 2ms at a 1 uS instruction cycle time.  Or you can still use the 16 bit timer and do the compare in software.  This would still mean dropping 1 button or the led indicator out of the design.

So the general idea would be to just convert the code to assembler so I can still use the 28 pin PIC and remove the variations from the timing due to the horribly optimized code.  I am not saying the variations are large enough to break the project but they are still there and I would rather them not be because it is causing slight hiccups here and there.  For instance there is a slight pause before the alarm goes off.  Not all the time but occasionally.  I have a hunch the little pauses here and there are coming from all the damn extraneous branching the compiler left in the code.  Also before people say it NO the ISR is not overlapping or running too long but it does have a runtime variance depending on which interrupt it is servicing.  (Downfall of only having a single ISR vector).  Worse case is I still have the same issues if I port it over to assembler.  In which case it is more or less the fault of the PIC ISR architecture causing me to have to do so many comparisons.  Other then the occasional hiccup like I mentioned the clock works great.
« Last Edit: May 30, 2013, 11:01:04 am by blewisjr »
 

Offline croberts

  • Regular Contributor
  • *
  • Posts: 94
  • Country: us
Re: Button Interrupt Failing [PIC][XC8]
« Reply #20 on: May 30, 2013, 11:57:13 am »
This is a topic I did some time ago for a real time clock/floating point display using a 20 pin PIC16F690 that may be of some interest. This design does not have an alarm function but may be of some interest to you.

https://www.eevblog.com/forum/microcontrollers/networked-rtc-fp-display/

I discovered a problem with the hardware blanking in floating point mode since that post that I fixed by adding a resistor and diode to the circuit. I have attached the revised schematic.
 

Offline C

  • Super Contributor
  • ***
  • Posts: 1345
  • Country: us
Re: Button Interrupt Failing [PIC][XC8]
« Reply #21 on: May 30, 2013, 05:54:18 pm »
croberts
Nice design, Nice and simple.
Pin 3 on the mpu, A very small change to dual purpose the board, I like it.
=====

Now using croberts circuit as a reference, If you have software space left, what changes could be made in the hardware to add more capabilities?

One of my clocks has two buttons, one for forward in time, one for back in time, Holding the button down makes the time change faster.
The Two buttons makes setting the clock nice and quick.
Some other ideas also need more switches, and switches can bounce.
So can you see some simple hardware changes to add more switches to croberts's circuit?

What would happen if instead of connecting a switch across pins 1 & 2 of JP1 or JP2 and you connected it to pin 1 and the collector of one of the transistors T1 -T5?
You could read the switch state just before you turned off the transistor it was connected to.
There are four more transistors, so you could have 5 switches where you did have one.There is a problem with this if you press 2 or more switches at the same time as it would mess up the display, BUT there is a simple fix also of just putting a diode in-line with each switch. And with that diode in-line with switch you could have a Alarm-On switch with out problems.

So the question becomes,
Do you want more switches and if so, do you have a little space for software & is a switch and diode to much cost.

Software switch DE-bounce.
Now you could get cheaper switches that could bounce to cut costs if you had a little more software space. Some of the little mpu's are really lacking in ram space, so it could be a good idea to keep that use low when adding de-bounce. So if you had a byte sized integer, That was positive for switch pressed and negative for not, that would be nice  When you read the switch status you could increase/decrease this integer until you reached a display scan count that would cover switch bounce time. So if this integer is 1 to display scan count, the switch is pressed and if 0 to -display scan count it is not pressed.

So applying this to your clock
Quote
4 digits = 4 pins
5 tactile push spst = 5 pins (each one has a different function)

becomes
4 digits = 4 pins
4 tactile push spst = 1 pins (each one has a different function) connected via a diode to digits pins.
For a savings of 3 pins at the cost of a little more software and 4 diodes.

EDIT:
croberts
In addition to switches, You could use this idea to add a zero cross ac input and/0r a one pulse per second input to your clock. Just gate the signal with the proper digit signal and diode in logic output.
 
C
« Last Edit: May 30, 2013, 06:26:02 pm by C »
 

Offline croberts

  • Regular Contributor
  • *
  • Posts: 94
  • Country: us
Re: Button Interrupt Failing [PIC][XC8]
« Reply #22 on: May 30, 2013, 06:30:16 pm »
Thanks C
Great comments and ideas all!

Dual purposing pins with hardware design is a great way to get more function out of fewer pins. I really like software debouncing for switches. For this design I've only used 1/4 of the available programming space and 1/5 of the available RAM and this includes software that supports the half duplex network so no problem there. I use R17 to set the time, works very smoothly (got the idea from the way time is set in my car radio with the volume control). When the circuit is used as a floating point display a 3 pin connector can be installed in place of R17 for stand alone display of an analog signal or the board can display values given it over the network.
 

Offline C

  • Super Contributor
  • ***
  • Posts: 1345
  • Country: us
Re: Button Interrupt Failing [PIC][XC8]
« Reply #23 on: May 30, 2013, 08:57:25 pm »
croberts:

Row/Column scan of switches is a very common idea. The old radio shack TRS-80 used 8 address lines to drive the columns and read the rows with a data read. So a simple read from memory could check the switches. Note that in this case a read with all address lines set properly you could check if any switch was pressed. So 8 x 8 matrix of switches = 64 switches. Others did it with an 8 bit latch and a 8 bit input. You should note that most switch matrix do not have the diodes in-line with the switches so you could get a false report of a switch being closed when it is not. With the speed of a human and fast scanning, software can most times correct this. Common way to help prevent this is to put switches normally pressed with other keys on a separate row or column. Most likely your PC keyboard is a matrix.

Sharing the row or column lines between display and input is also common. 

With a loss of a little flexibility your 5 digit lines could goto a 5 to 32 decoder for 32 digits or you could save 2 pins and use a 3 to 8 like the 74xx138 to drive your digits.
And you could do the same with the switches with a 1 of 8 data selector.
so your 8 segments + 5 for digits pins could become 8 segments + 8 digits + 8 switches for a saving of 1 pins and a cost of two chips.
And as long as the digit scan rate is fast enough slow speed data could replace a switch.

Now a hint: is this one of the pic's that can count an input faster then it's clock like the ones used in a pic based frequency counter?

Have fun
C
 

Offline blewisjr

  • Frequent Contributor
  • **
  • Posts: 301
Re: Button Interrupt Failing [PIC][XC8]
« Reply #24 on: May 31, 2013, 11:29:22 am »
On my particular circuit I am debouncing the pins with hardware.  Each button is pulled up with a 4.7k ohm resistor.  Then there is a 10nF capacitor that gets charged.  When you press the button it shorts draining the cap and reading the input off that gets rid of the bounce as you get a nice quick smooth drop.  Works quite well.

The dual purposing sound cool but I am not quite sure how you can use a output pin which changes the digits in the multiplexed display to read the button state unless you are rapidly changing it between input and output as you update each digit?

Keep in mind I am new to all this only been messing around with the stuff for a few months so I think learning how to multi purpose pins like this would be a great learning experience so if you can direct me in impart on me some information about doing this it would be much appreciated.  I would love to be able to iterate on the design of the alarm to use a smaller chip and avoid having to port over to assembler because of a bad free compiler.
« Last Edit: May 31, 2013, 11:56:25 am by blewisjr »
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf