Results 1 to 23 of 23

Thread: Checking for button push on3 buttons using interrupts on PIC16F72

  1. #1
    Member
    Join Date
    Mar 2013
    Location
    Sheboygan, Wisconsin, USA
    Posts
    57

    Checking for button push on3 buttons using interrupts on PIC16F72

    Processor: PIC16F72
    Objective: Scroll prompts to LCD Module in main program while being able to respond to button push to one of three buttons. Button push then directs program flow to various program functions.

    General hardware layout LCD display pins on Ports A & C. Three buttons on Port B. Using pullups and check for low condition on pin when pushed.

    I see that several forum posts recommend using a timer overflow (interrupt) to check for Port B pin states. I sort of get this as long as the overflow is faster than the scrolling going on in the main progtram code. I could save the pin state to a variable in the interrupt, resume and then the main program could test that variable and branch appropriately. (Please comment if my understading of this process is in error.)

    So I'm studying the 16F72 datasheet and looking at the various SFR's which are involved in interruptsfor this specific part. I percieve that in "INTCON" I would disenable the "PEIE", "INTE", "RBIE", "INTF", & "RBIF" bits because I am not using this system to check for pin changes on Port B . Can you confirm this assumption? I am also quite confused about how to deal with the SFR's "PIE1 & "PIR1" or whether to deal with them at all.

    It looks like this part has a lot of SFR's dealing with interrupts and I may not have to worry about them given the fairly rudementary nature of my task.

    Alternately, I would consider using "ON INTERRUPT" on this problem but am not sure how to implement three buttons since the MEL example "ONINT.PBP" does not address this.

    As always I learn well from example code if you could recommend any.

    Regards,
    Allan

  2. #2
    Member
    Join Date
    Mar 2013
    Location
    Sheboygan, Wisconsin, USA
    Posts
    57

    More on this project

    I have decided to experiment with "ON INTERRUPT" as a means of working with 3 buttons. I have downloaded the MEL "Onint.pbp" code and successfully ported it over to the 16F72 where I get the expected results. I have then modified the interrupt handler to attempt to save the state of PortB at the time when a button is pushed. This state saving variable is then tested in the main line of code with an IF/THEN. The objective is that I want to find out which of 3 buttons was pushed and branch in the main line of code to other logic.

    I test the saved state variable and intend to blink the led twice if the saved state indicates a "0" in bit PortB.0
    Unfortunately I do not get the desired result in that the IF/THEN test does not blink the led twice. I wonder if the "PortBstate" variable is not being written to correctly while the interrupt handler is being executed. Could this be a result of the way "ON INTERRUPT" operates? I may have to hook up an LCD display to display the "PortBstate" variable in the main line of code.

    In the end I may have to abandon "ON INTERRUPT" as a method and try to write an assembly interrupt handler to copy the PortB state to a variable.

    Any thoughts or suggestions?

    I have attached the full code:

    MyOnInt.pbp

  3. #3
    Allen, attached is a program and a subroutine I have used for about 10 years or so and it works flawlessly. Please enjoy and I hope this will be a starting point on your journey.
    Attached Files Attached Files
    Dave Purola,
    N8NTA
    EN82fn

  4. #4
    Member
    Join Date
    Mar 2013
    Location
    Sheboygan, Wisconsin, USA
    Posts
    57
    Quote Originally Posted by DavidP View Post
    Allen, attached is a program and a subroutine I have used for about 10 years or so and it works flawlessly. Please enjoy and I hope this will be a starting point on your journey.
    Dave thanks for the file. I have taken a cursory look at it and my first impression is that it is quite intimidating even when one takes out the copious processor specific setup. I am not yet familiar with Darrel's DT_INTS and would have to make a study of that as well. The included macro adds another element which will require further study. I also find the commenting to be a bit cryptic for my understanding. In any case it sounds like your code is quite robust.

    I have purchased the MEL Trainer Kit based on the PIC16F1937 within the last year and Chapter 9 demonstrates button use with LEDs which seems to be very straight forward and I am in the process of applying that program to my part no. With a bit more effort and comparing the datasheets, I look forward to some success. The only problem with that example is that it only employs a single button.

  5. #5
    Actually Allen, the code as written supports 4 buttons and can support as many as you have port pins available. I don't understand the comment about the documentation? What don't you understand so as I may be able to help?
    Dave Purola,
    N8NTA
    EN82fn

  6. #6
    Senior Member
    Join Date
    Sep 2011
    Location
    australia
    Posts
    247
    allan
    i'm a little late to this party but i'm wondering what your attempted code is trying to achieve.
    the interrupt you have selected to use [INT interrupt] can only react to one stimulus , in your case
    a falling edge on pin portb.0 .i see two issues, its never going to react to the other switches and due to the inherent
    latency of "oninterrupt" the read value of portb is not going to be much use .
    a better solution would be RBC int {portb change} but that only works for portb pins 4-7.
    that way you can compare portb current state with previous state to determine key states.

    if you find daves solution too complex rest assure that "oninterrupt" can do what you want .i offer you this example
    that still only reads 1 button but demonstrates a way to aleviate some of the latency
    [I used a 20mhz xtal and moved the led to suit my setup]
    Code:
        ' Name        : ONINT.pbp
        ' Compiler    : PICBASIC PRO Compiler 2.6
        '
        ' Oscillator  : external
        ' Description : PICBASIC PRO program to demonstrate use of On Interrupt
        ' Interrupts in BASIC.  Interrupt on PORTB.0 (INT) flashes PilotLED .
        
        ' button press event in variable for testing in Main line code.
    	'	
    	'	16F72- CPU Hardware Layout
    	'	---------------------------	
    
    	'	RB0- Button1 wpu to Vss    causes int on low edge
    
    	'	RB6- Not Used (ICSPCLK)
    	'	RB7- Not Used (ICSPDAT)
    	'
    	'	RC1- Used for Pilot led
    	
     	'
    
    #CONFIG
    cfg = _HS_OSC
    cfg&= _WDT_ON
    cfg&= _PWRTE_ON
    cfg&= _CP_OFF
    cfg&= _BOREN_ON
      __CONFIG cfg
    
    #ENDCONFIG
    	'
    	'	This chip powers up with PortA pins as analog input
    
    	'
    	'	Apply values to predefined constants
    	'	----------------------------------------------
    	Define OSC 20  
    	'
    	'	Define Program Constants
    	'	----------------------------------------------
    	'   Constant con value	
    	'
    	'	Define RAM Assignments and Variables
    	Bstate var bit
    	Dly_Cnt var word
    	'	----------------------------------------------
    	'	Define variables and aliases
    	'	----------------------------------------------
    	PilotLED Var PORTC.1
    	
    	'
    	'	Start - Initialize Processor
    	'	==============================================
    	;	Set the tri-state registers using bits
    
    	TRISB=%11111111  ' Buttons on portb bits 0,1 & 2
    	TRISC=%11111101  ' PilotLED    as o/p
    	;	Initialize the ports using bits
    	
    	PORTC=%00000000
    	'
        OPTION_REG.7 = 0    'Enable PortB pullup for TrisB input-only pins
        '
        On Interrupt Goto myint ' Define interrupt handler
        INTCON = $90            ' Enable INT  interrupt	
    	'
    	'	Body of main line code
    	'
    	         
        gosub blink  ' test and Turn LED off
    	
    	
    mainloop:
       
       If Bstate Then  ' If button was pushed (PortB.0 went low) then blink LED twice
            gosub flash
            Bstate=0    ;CLEAR THE FLAG
       Endif
       Goto mainloop           ' Do it forever
    
      
     end  
    
    flash:
           PilotLED =1
           Dly_Cnt=333
           while Dly_Cnt
           Pause 1
           Dly_Cnt=Dly_Cnt-1
           wend
           PilotLED = 0
           Dly_Cnt=333
           while Dly_Cnt
           Pause 1
           Dly_Cnt=Dly_Cnt-1
           wend
    blink:        
           PilotLED =1
           Dly_Cnt=333
           while Dly_Cnt
           Pause 1
           Dly_Cnt=Dly_Cnt-1
           wend 
           PilotLED  =0
           Dly_Cnt=333
           while Dly_Cnt
           Pause 1
           Dly_Cnt=Dly_Cnt-1
           wend
    return
    
      
       Disable                  ' No interrupts past this point
    myint:         ' Interrupt handler
       Bstate = 1     '  (PortB.0 became low  )  SET A FLAG TO TELL MAIN LOOP THE BUTTON WAS PRESSED
       Pause 10            ' debounce a little bit
       INTCON.1 = 0            ' Clear INT interrupt flag
       Resume                  ' Return to main program
       Enable  	
       '
       End

  7. #7
    Senior Member
    Join Date
    Sep 2011
    Location
    australia
    Posts
    247
    This ver uses rbc [portb change] for keys on b.4 and b.5 (pickit uses 6 and 7 so that's all I can manage with my dev board)

    leds on c.1/c.2

    Code:
       ' Name        : ONINT.pbp
        ' Compiler    : PICBASIC PRO Compiler 2.6
        ' Assembler   : PM or MPASM
        ' Oscillator  : external
        ' Description : PICBASIC PRO program to demonstrate use of On Interrupt
        ' Interrupts in BASIC.  Interrupt on PORTB.0 (INT) flashes PilotLED .
        
        ' button press event in variable for testing in Main line code.
    	'	
    	'	16F72- CPU Hardware Layout
    	'	---------------------------	
    
    	'	RB4- Button1 wpu to Vss    causes int on CHANGE
            '       RB5- Button2 wpu to Vss    causes int on CHANGE
    
    	'	RB6- Not Used (ICSPCLK)
    	'	RB7- Not Used (ICSPDAT)
    	'
    	'	RC1- Used for B4 led1
    	'	RC2- Used for B5 led2
     	'
    
    #CONFIG
    cfg = _HS_OSC
    cfg&= _WDT_ON
    cfg&= _PWRTE_ON
    cfg&= _CP_OFF
    cfg&= _BOREN_ON
      __CONFIG cfg
    
    #ENDCONFIG
    	'
    	'	This chip powers up with PortA pins as analog input
    
    	'
    	'	Apply values to predefined constants
    	'	----------------------------------------------
    	Define OSC 20  
    	'
    	'	Define Program Constants
    	'	----------------------------------------------
    	'   Constant con value	
    	'
    	'	Define RAM Assignments and Variables
    	Bstate      var BYTE
    	Dly_Cnt     var word
    	LAST_Bstate VAR BYTE
    	LEDS        VAR BYTE
    	'	----------------------------------------------
    	'	Define variables and aliases
    	'	----------------------------------------------
    	LED1 Var PORTC.1
    	LED2 Var PORTC.2
    	
    	'
    	'	Start - Initialize Processor
    	'	==============================================
    	;	Set the tri-state registers using bits
    
    	TRISB=%11111111  ' Buttons on portb bits 0,1 & 2
    	TRISC=%11111001  ' PilotLED    as o/p
    	;	Initialize the ports using bits
    	
    	PORTC=%00000000
    	'
        OPTION_REG.7 = 0    'Enable PortB pullup for TrisB input-only pins
        '
        On Interrupt Goto myint ' Define interrupt handler
        INTCON = $88            ' Enable RBC interrupt	
        Bstate = PORTB
        Bstate = 0
    	'
    	'	Body of main line code
    	'
    	LEDS=6
                 
        gosub blink  ' test and Turn LEDS off
    	
    	
    mainloop:
       
       If Bstate Then  ' If A button was pushed (PortB CHANGE) then blink MATCHING LED twice
            LEDS=Bstate
            gosub flash
            Bstate=0    ;CLEAR THE FLAG
       Endif
       Goto mainloop           ' Do it forever
    
      
     end  
    
    flash:
           PORTC = LEDS
           Dly_Cnt=333
           while Dly_Cnt
           Pause 1
           Dly_Cnt=Dly_Cnt-1
           wend
           PORTC  = 0
           Dly_Cnt=333
           while Dly_Cnt
           Pause 1
           Dly_Cnt=Dly_Cnt-1
           wend
    blink:        
           PORTC  = LEDS
           Dly_Cnt=333
           while Dly_Cnt
           Pause 1
           Dly_Cnt=Dly_Cnt-1
           wend 
           PORTC   =0
           Dly_Cnt=333
           while Dly_Cnt
           Pause 1
           Dly_Cnt=Dly_Cnt-1
           wend
    return
    
      
       Disable                  ' No interrupts past this point
    myint:         ' Interrupt handler
       Bstate = ~((PORTB>>3)&6)    '  (PortB CHANGED  )  SET A FLAG TO TELL MAIN LOOP WHICH BUTTON/S WAS PRESSED
       Pause 10            ' debounce a little bit
       INTCON.0 = 0            ' Clear INT interrupt flag
       Resume                  ' Return to main program
       Enable  	
       '
       End

  8. #8
    Member
    Join Date
    Mar 2013
    Location
    Sheboygan, Wisconsin, USA
    Posts
    57
    Quote Originally Posted by DavidP View Post
    Actually Allen, the code as written supports 4 buttons and can support as many as you have port pins available. I don't understand the comment about the documentation? What don't you understand so as I may be able to help?
    Dave,

    I will make a more in depth study of the code today. Indeed code which supports 3 buttons and reports the state of the 3 all at once to the main program is what I am trying to achieve. I suspected the problem using "ON INTERRUPT" is the latency inherent n the command does not allow PortB's state to be properly captured before the button is released and the pin returns high again. Richard's replay later in the thread seems to be saying pretty much that.

    Thanks for your help. If I can clarify my misunderstanding later I will attempt to do so.

    A.

  9. #9
    Member
    Join Date
    Mar 2013
    Location
    Sheboygan, Wisconsin, USA
    Posts
    57
    Richard,

    Thanks for the code. I will look at it and see what I can learn.

    My initial concept here which you my debunk if you choose, is to do something within a timed interrupt perhaps overflowing at 20ms intervals while a display is busy scrolling instructions to an LCD in the main program line. In theory the interrupt does nothing more than check portB and saves a "snapshot" of the lowest three bits to a byte variable which the main program line can read and use to branch to the next line of logic based on a button press.

    I had suspected the reason "ON INTERRUPT" did not update my variable to save the switch pattern was due to latency in that before it could copy the pin states the button opened circuit and the pin went high again due to the PortB pullup. I am interested to see if there is something in your examples that will make this process work for 3 buttons on PortB.

    Otherwise I am considering abandoning "ON INTERRUPT" altogether and using assembly code in the interrupt handler to copy the PortB state at the time of the interrupt to a variable something like:

    .
    .
    movf PORTB,0
    movwf _PortBsav
    .
    .

    The idea is to then use the timed interrupt to "poll" the switches in the background and make use of the result at certain intervals in the main program line. For example after scrolling an instruction line checking the PortB save variable to see if a button was pressed.

    Do I have a workable scheme here? This certainly can't be the first time this is to be done. All sorts of devices display a scrolling message and wait for a buttons to be pressed pressed.

  10. #10
    Well Allen, That is exactly what the function of the code I sent you is. The interrupts occur every 10 milliseconds and read the state of the buttons connected. The button status subroutine then checks for the buttons to be debounced before updating the current state. With 5 data shifts the debounce time is 50 milliseconds at a 10 millisecond interrupt rate.
    Dave Purola,
    N8NTA
    EN82fn

  11. #11
    Member
    Join Date
    Mar 2013
    Location
    Sheboygan, Wisconsin, USA
    Posts
    57
    Quote Originally Posted by richard View Post
    This ver uses rbc [portb change] for keys on b.4 and b.5 (pickit uses 6 and 7 so that's all I can manage with my dev board)

    leds on c.1/c.2

    Code:
       ' Name        : ONINT.pbp
        ' Compiler    : PICBASIC PRO Compiler 2.6
        ' Assembler   : PM or MPASM
        ' Oscillator  : external
        ' Description : PICBASIC PRO program to demonstrate use of On Interrupt
        ' Interrupts in BASIC.  Interrupt on PORTB.0 (INT) flashes PilotLED .
        
        ' button press event in variable for testing in Main line code.
    	'	
    	'	16F72- CPU Hardware Layout
    	'	---------------------------	
    
    	'	RB4- Button1 wpu to Vss    causes int on CHANGE
            '       RB5- Button2 wpu to Vss    causes int on CHANGE
    
    	'	RB6- Not Used (ICSPCLK)
    	'	RB7- Not Used (ICSPDAT)
    	'
    	'	RC1- Used for B4 led1
    	'	RC2- Used for B5 led2
     	'
    
    #CONFIG
    cfg = _HS_OSC
    cfg&= _WDT_ON
    cfg&= _PWRTE_ON
    cfg&= _CP_OFF
    cfg&= _BOREN_ON
      __CONFIG cfg
    
    #ENDCONFIG
    	'
    	'	This chip powers up with PortA pins as analog input
    
    	'
    	'	Apply values to predefined constants
    	'	----------------------------------------------
    	Define OSC 20  
    	'
    	'	Define Program Constants
    	'	----------------------------------------------
    	'   Constant con value	
    	'
    	'	Define RAM Assignments and Variables
    	Bstate      var BYTE
    	Dly_Cnt     var word
    	LAST_Bstate VAR BYTE
    	LEDS        VAR BYTE
    	'	----------------------------------------------
    	'	Define variables and aliases
    	'	----------------------------------------------
    	LED1 Var PORTC.1
    	LED2 Var PORTC.2
    	
    	'
    	'	Start - Initialize Processor
    	'	==============================================
    	;	Set the tri-state registers using bits
    
    	TRISB=%11111111  ' Buttons on portb bits 0,1 & 2
    	TRISC=%11111001  ' PilotLED    as o/p
    	;	Initialize the ports using bits
    	
    	PORTC=%00000000
    	'
        OPTION_REG.7 = 0    'Enable PortB pullup for TrisB input-only pins
        '
        On Interrupt Goto myint ' Define interrupt handler
        INTCON = $88            ' Enable RBC interrupt	
        Bstate = PORTB
        Bstate = 0
    	'
    	'	Body of main line code
    	'
    	LEDS=6
                 
        gosub blink  ' test and Turn LEDS off
    	
    	
    mainloop:
       
       If Bstate Then  ' If A button was pushed (PortB CHANGE) then blink MATCHING LED twice
            LEDS=Bstate
            gosub flash
            Bstate=0    ;CLEAR THE FLAG
       Endif
       Goto mainloop           ' Do it forever
    
      
     end  
    
    flash:
           PORTC = LEDS
           Dly_Cnt=333
           while Dly_Cnt
           Pause 1
           Dly_Cnt=Dly_Cnt-1
           wend
           PORTC  = 0
           Dly_Cnt=333
           while Dly_Cnt
           Pause 1
           Dly_Cnt=Dly_Cnt-1
           wend
    blink:        
           PORTC  = LEDS
           Dly_Cnt=333
           while Dly_Cnt
           Pause 1
           Dly_Cnt=Dly_Cnt-1
           wend 
           PORTC   =0
           Dly_Cnt=333
           while Dly_Cnt
           Pause 1
           Dly_Cnt=Dly_Cnt-1
           wend
    return
    
      
       Disable                  ' No interrupts past this point
    myint:         ' Interrupt handler
       Bstate = ~((PORTB>>3)&6)    '  (PortB CHANGED  )  SET A FLAG TO TELL MAIN LOOP WHICH BUTTON/S WAS PRESSED
       Pause 10            ' debounce a little bit
       INTCON.0 = 0            ' Clear INT interrupt flag
       Resume                  ' Return to main program
       Enable  	
       '
       End
    Richard,

    I have studied your second example and am in the process of porting it over to the 16F72 which is my part no. of choice right now. I find the code quite understandable but have a few things to confirm with you if you would favor me with a reply.

    In order of appearance in the code listing:

    I am using a 4 MHz crystal and you are woking with 20 MHz. I don't see this as a problem because the "ON INTERRUPT" method used here does not introduce any timing concerns as long as PBP knows the oscillator speed. Is that correct?

    The variable definition: "LAST_Bstate var BYTE" I see this defined but not used. Is it in fact NOT used?

    TRISB=%11111111
    TRISC=%11111001: I'm just curious- Do you feel it best practice to make all unused pins Inputs, Why?

    INTCON = $88: Does this line just enable global & peripheral interrupts on your processor? Mine is INTCON = $90.
    I am not familiar with the acronym "RBC." Could you briefly define that?

    Bstate = PORTB
    Bstate = 0: I assume these two lines are just giving Bstate an initial value, Is that correct?

    The last Inquiry is regarding the line in interrupt handler:
    Bstate = ((PORTB>>3)&6)
    Can you confirm my bitwise manipulations?

    My theoretical testing of Button4 = 11101111
    11101111 PortB at low pin B4
    11101 >>3
    110 (6)
    100 &6
    11111011 " ~ "
    11111011 Led C1 On, Led C2 Off

    My theoretical testing of Button5 = 11011111
    11011111 PortB at low pin B5
    11011 >>3
    110 (6)
    10 &6
    11111101 " ~ "
    11111101 Led C2 On, Led C1 Off

  12. #12
    Senior Member
    Join Date
    Sep 2011
    Location
    australia
    Posts
    247
    I am using a 4 MHz crystal and you are woking with 20 MHz. I don't see this as a problem because the "ON INTERRUPT" method used here does not introduce any timing concerns as long as PBP knows the oscillator speed. Is that correct?
    the faster the better , 4mhz should still be ok



    The variable definition: "LAST_Bstate var BYTE" I see this defined but not used. Is it in fact NOT used?
    yes I did forget to eliminate it


    TRISB=%11111111
    TRISC=%11111001: I'm just curious- Do you feel it best practice to make all unused pins Inputs,
    its my preference to do that it minimises risk and power usage




    Why INTCON = $88: Does this line just enable global & peripheral interrupts on your processor? Mine is INTCON = $90
    .
    read the data sheet
    bit7 gie , bit3 rbie intcon = $88

    I am not familiar with the acronym "RBC." Could you briefly define that?
    rbc == rb change == rb port change




    Bstate = PORTB
    Bstate = 0: I assume these two lines are just giving Bstate an initial value, Is that correct?
    yes but to set the initial portb state for rbc int a read of portb is required





    The last Inquiry is regarding the line in interrupt handler:
    Bstate = ((PORTB>>3)&6)
    Can you confirm my bitwise manipulations?
    using 11101111 PortB at low pin B4
    11101111 >>3 = 00011101
    (6)=110
    00011101 &110 = 00000100
    ~00000100= 11111011
    portc = 11111011 ie Led C1 On, Led C2 Off

  13. #13
    Member
    Join Date
    Mar 2013
    Location
    Sheboygan, Wisconsin, USA
    Posts
    57
    Thanks Richard,

    I learned an important thing here:

    Apparently INTCON $88 is the correct setup for this functionality. I now see where the RB Port Change Interrupt enters into the picture in bit3 of INTCON. I erroneously had set my INTCON to $90.

    The code compiles. Tomorrow I'll breadboard the hardware and give this a whirl.

  14. #14
    Member
    Join Date
    Mar 2013
    Location
    Sheboygan, Wisconsin, USA
    Posts
    57
    Quote Originally Posted by DavidP View Post
    Actually Allen, the code as written supports 4 buttons and can support as many as you have port pins available. I don't understand the comment about the documentation? What don't you understand so as I may be able to help?
    Dave,

    Now that I have had several good tests of DT_INTS-14 I feel I can start to dive into your example code.

    After studying MicroChip's application note "AN566" I get the impression that it is not a simple matter of copying the state of the Port handling say 3 switches (at the time a button is pushed) and using that "snapshot" in the main program to determine the program branching. Apparently "pulse width" issues are important. It is interesting that the "small pulse width" and "large pulse width" are not described in the docuument in terms of real time milli/micro seconds. As near as I can understand, the pulse width referred to means that a pulse relates to the number of times an interrupt completes while the subject button is pressed down. Please correct me on this if you will.

    My question to you is this. In your design is the code you provide addressing this concern? If so an explanation (list of steps?) of how you do this would be useful. Initially I thought that introducing 5 button states was making things over-complicated but now I am beginning to think that it may be a necessity. I just don't follow your design method from reading your running comments.

    For instance I don't follow the process that is behind this comment: "THE BUTTUP VALUE IS EQUAL THE DECIMAL VALUE OF INTERRUPTS REQUIRED FOR THE VALUE TO ACCUMULATE. HERE IT IS 31 WHICH IS EQUAL TO 50 MILLISECONDS or 5 LEFT SHIFTS. 1 BIT SHIFT PER INTERRUPT @ 10 MILLISECONDS."

    What is the significance of BUTTUP accumulating up to 31 in 50 mS? What is the significance of the 5 left shifts? What purpose does that serve, de-bouncing? (My switches are already hardware wired with an R/C de-bounce.)

    In my effort to make this code relevant to my chip, I need to sort through all your Chip init settings. I am puzzled by the all 3 references to PR2,PR4 & PR6. Are all three of these necessary to make this code run? Are all these settings required to be set for the 16F1825 because the power up settings conflict with the correct operation of the code? I see from the datasheet that there is a lot of pin multi-plexing going on with this part.

    I see you have defined several de-bounce variables. Could you explain how the code achieves de-bouncing?

    I believe this code determines which button was pushed:
    '****************** POLL PUSH BUTTON INPUTS FOR DEBOUNCE **********************
    BUTTBYTE(0) = (BUTTBYTE(0) << 1) + BTN0
    BUTTBYTE(1) = (BUTTBYTE(1) << 1) + BTN1
    BUTTBYTE(2) = (BUTTBYTE(2) << 1) + BTN2
    BUTTBYTE(3) = (BUTTBYTE(3) << 1) + BTN3

    Why is it done this way? Is it not possible just to copy the port state directly to another variable all 8 bits at once?
    I had a concept of being able to do the same thing with something similar to:
    asm
    movf PORTB,0
    movwf _Bsave
    endasm
    Where _Bsave was initially declared as a byte variable. Any reason that you know of why this doesn't work?

    The subroutine BTNSTAT is a bit of a mystery, that is what it all seems to be checking. Can't quite follow what all the JUNK and the History stuff is all about.

    So the Main loop just checks all the buttons only for pressed state so why is all the code necessary in BTNSTAT sorting through states other than PRESSED?

    I hope I am not imposing on you too much to address these questions but if this method is the only way to identify switch presses then I will just have to soldier through it.

  15. #15
    Well Allen, to answer your first question, The interrupt is going on in the background and being accessed every 10 milliseconds. Here I read the state of the button inputs connected to the processor. Here in this instance of the code value 31 is equal to 00011111 or 1 shifted five times to the left ie. "BUTTBYTE(0) = (BUTTBYTE(0) << 1) + BTN0" The state of BTN0 is the active bit being read from the processor pin and if it is high for 5 interrupt interations then it is assumed to be high for 50 milliseconds. Most switches and push buttons have switch bounce anywhere from 20 to as much as 50 milliseconds with out some sort of filtering. This method for interrogation works quite well. If there is a break in the connection of 1 count or shift then the binary value would look something like 00011110 which is not equal to 31 so there it is assumed to not be debounced. There has to be a constant connection of 50 milliseconds or more to be considered connected. The states defined for the button's are :

    BUTTUP CON 31 'BUTTON RELEASED VALUE (5 BITS)
    PRESSED CON 0 'BUTTON PRESSED VALUE (5 BITS)
    RELEASED CON 1 'BUTTON RELEASED VALUE
    BUTTDOWN CON 2 'BUTTON STILL PRESSED VALUE
    BETWEEN CON 3 'BUTTON IN BETWEEN STATES (TRANSITION)

    in this instance of the constant's declaration the buttons are pulled up to +5 volts by resistors in there static state. When pressed the button is connected to ground. if the button is released it will take 50 milliseconds to return to a value of 31. It will also take 50 milliseconds to reach a value of 0 if pressed or connected to ground. The states of "PRESSED", and "RELEASED" are one shot functions. The states of "BUTTUP", "BUTTDOWN", and "BETWEEN" are static states.
    The routine can be used with buttons connected to ground or connected to +5 volts upon circuit connection.
    The only change to the define variables code would be:

    BUTTUP CON 31 'BUTTON RELEASED VALUE (5 BITS)
    PRESSED CON 0 'BUTTON PRESSED VALUE (5 BITS)
    for a button connected to ground when pressed or:

    BUTTUP CON 0 'BUTTON RELEASED VALUE (5 BITS)
    PRESSED CON 31 'BUTTON PRESSED VALUE (5 BITS)
    for a button connected to +5 volts upon circuit connection.

    Also in the actual BTNSTAT routine one would comment out one of the 2 lines of code such as:
    ' JUNK = BUTTBYTE(BTNINDEX) & PRESSED 'AND ONLY WITH TESTED BITS (USE FOR PRESSED = HIGH)
    JUNK = BUTTBYTE(BTNINDEX) & BUTTUP 'AND ONLY WITH TESTED BITS (USE FOR PRESSED = LOW)

    As far as the use of the call GOSUB BTNSTAT is concerned the history of the states for the buttons is revealed the first pass thru the routine. for instance, if a button is pressed for 50 milliseconds the first time the routine is called the state will be "PRESSED" which is a one shot function. If the routine is called again before the button is released the active state would then be "BUTTDOWN". If the routine is called when the button has been released for 50 milliseconds the state would be "RELEASED" which is a one shot function. There after if the routine is called and the button has not been pressed the active state would be "BUTTUP".

    To answer your question about why I don't read an entire port, The way I have it configured you can have buttons on any pins available. Also the shifting of the data port pin values would have to be done by bit manipulation after the port was read which would add more time to the interrupt routine. It is however possible to do it that way if one decides to.

    I hope this long dissertation makes some sense. As far as all of the register settings go, I just do this as I have done for all these years and NOT assumed the states of them after power on.

    Dave Purola,
    Last edited by DavidP; 5 Days Ago at 05:19 AM.
    Dave Purola,
    N8NTA
    EN82fn

  16. #16
    Member
    Join Date
    Mar 2013
    Location
    Sheboygan, Wisconsin, USA
    Posts
    57
    Dave,

    Thanks for the detailed explanation. I had only a vague notion of what was going on but this fills in the blanks.
    It does necessitate that I now check my hardware de-bounced switch board with my scope to assure that the R/C circuit to ground is producing a pulse between 20ms and 50 ms.
    Alternatively I may have to go with simple buttons.

    This explanation was very useful.

    Allan

  17. #17
    Senior Member
    Join Date
    Sep 2011
    Location
    australia
    Posts
    247
    allan
    dave's code has one glaring error and a nasty trap for novices

    Code:
    '----------- FRONT PANEL PUSH BUTTON CONSTANTS ---------------------------------
    
    BTN0		VAR	PORTC.0	'1-PUSH PUTTON #0
    BTN1		VAR	PORTC.1	'1-PUSH PUTTON #1
    BTN2	    VAR	PORTC.2	'1-PUSH PUTTON #2
    BTN3	   	VAR	PORTC.3	'1-PUSH PUTTON #3
    
    
    NBTNS		CON	3		'NUMBER OF BUTTONS - 1
    
    
    
    '********************** PUSH BUTTON DEBOUNCE VARIABLES ************************
    BUTTBYTE	VAR	BYTE(NBTNS)	'BUTTON COUNTER ARRAY
    BTNSTATE	VAR	BYTE(NBTNS)	'BUTTON STATE
    this will never be ok
    
    
    BTNCLR		VAR	BYTE	'BUTTON ONE SHOT FLAG
    BTNINDEX	VAR	BYTE	'BUTTON ARRAY INDEX
    it must be

    Code:
    BUTTBYTE	VAR	BYTE(NBTNS+1)	'BUTTON COUNTER ARRAY
    BTNSTATE	VAR	BYTE(NBTNS+1)	'BUTTON STATE
    otherwise the array boundaries will be exceeded

    secondly

    he uses this define
    DEFINE NO_CLRWDT 1

    if you leave that in without disabling the wdt in your config the mpu will reset every 45 seconds or so
    it tripped me up for a while.

    when fixed it works very nicely providing that the main loop is non blocking

  18. #18
    Yes, you are right Richard but to my defense this code snippet was lifted from a program I did many years ago. The only thing I have changed in the last 6 years is the first line in the subroutine:
    BTNINDEX = (NBTNS - 1)

    As far as the watchdog statement goes those configuration statements were left out of the example snippet do to the exact processor nature.
    Dave Purola,
    N8NTA
    EN82fn

  19. #19
    Member
    Join Date
    Mar 2013
    Location
    Sheboygan, Wisconsin, USA
    Posts
    57
    Thanks gentlemen for your useful remarks.

    I expect my challenges are only about to begin when I attempt to implement this code for my 16F72, or my 16F628 two parts with which I have a good working familiarity.

    I do have two different control box projects in mind in which my ultimate result is scrolling instructive prompts while accepting button input from up to 3 buttons.

    I have wondered (sometimes it is necessary to turn a problem around) if I should really make the prompt scrolling a task handled by a timer overflow and treat the button presses in the main program line with an If-Else-Then construct which always works on multiple buttons. That might prove a bit too much for this novice!

    A.

  20. #20
    Member
    Join Date
    Mar 2013
    Location
    Sheboygan, Wisconsin, USA
    Posts
    57
    I have finally translated Dave's code over to the PIC16F72 to the best of my knowledge. I have reduced the buttons to three and made the outputs all LED's Buttons are pulled up and code is edited to look for LOW when button is pressed. I have tested the code with both hardware de-bounced and NOT hardware de-bounced buttons.

    The results were nil. No response from the led's when buttons are pressed. The code is attached and if anyone cares to cast an eye and point out a problem I would be much obliged. Otherwise I am in for some hit or miss trials of my own. I have tried to comment on my computation of the timer value, etc.

    Here is the .pbp file:
    MyButInt.pbp

  21. #21
    Senior Member
    Join Date
    Sep 2011
    Location
    australia
    Posts
    247
    you have no wsave vars , its best to place them in your code and not have to modify the includes
    your "system initialise" code was orphaned by the goto mainloop statement an will never run
    I have fixed dave's mistakes too

    this works for me on a 16f72

    Code:
    	'	MyButInt.pbp
    	'	Allan Becker
    	'	3 22 17
    	'
    	'   Attempt to make DavePs button interrupt prog work.  Modified for
    	'   3 buttons and all digital outputs.  Button minimum press duration
        '   is 50ms. 5 shifts at 10ms per each tick of timer.
    	'	
    	'	16F72- CPU Hardware Layout
    	'	---------------------------	
    	'	RA0- LED0 
    	'	RA1- LED1 
    	'	RA2- LED2 
    	'	RA3- Not Used 
    	'	RA4- Not Used
    	'	RA5- Not Used 
    	'
    	'	RB0- Not Used 
    	'	RB1- Not Used
    	'	RB2- Not Used
    	'	RB3- Not Used
    	'	RB4- Not Used
    	'	RB5- Not Used
    	'	RB6- Not Used (ICSPCLK)
    	'	RB7- Not Used (ICSPDAT)
    	'
    	'	RC0- BTN0
    	'	RC1- BTN1  
    	'	RC2- BTN2  
    	'	RC3- Not Used  
    	'	RC4- Not Used
    	'	RC5- Not Used
    	'	RC6- Not Used
    	'	RC7- Not Used
     	'
    	'	Use the following two lines of assembly code to set configuration fuses.
    	'	Be sure that EpicWin settings enable UPDATE CONFIGURATION so they are
    	'	read from this file each time.
    	'
    
    #CONFIG
        __CONFIG  _HS_OSC & _WDT_OFF & _PWRTE_ON & _CP_OFF
    #ENDCONFIG
    
    DEFINE OSC 8
    DEFINE NO_CLRWDT 1        ;save a bit of code space
       
        INCLUDE "DT_INTS-14.bas"      	' Interrupt Control routines	
        INCLUDE "ReEnterPBP.bas"     ' Interrupt Control routines	    
        '
        ' ********************************************************************
        '				Declare Port Variables
        ' ********************************************************************
        LED0		VAR	PORTA.0	'0-DIGITAL OUTPUT
        LED1	    VAR	PORTA.1	'0-DIGITAL OUTPUT
        LED2		VAR	PORTA.2	'0-DIGITAL OUTPUT
    
        BTN0		VAR	PORTC.0	'1-PUSH PUTTON #0
        BTN1		VAR	PORTC.1	'1-PUSH PUTTON #1
        BTN2	    VAR	PORTC.2	'1-PUSH PUTTON #2
        '
        ' ********************************************************************
        '				Define Constants
        ' ********************************************************************
        ' following is based on instruction clock, 4MHz crystal, 1:8 prescaler,
        ' i.e.: 8uS per count * 1250 counts = .010 secs
        TIMESEG     CON 64286   '65536 - 1250 = 64286
        '
        '----------- FRONT PANEL PUSH BUTTON CONSTANTS -----------------------
        NBTNS		CON	3		'NUMBER OF BUTTONS  
        BUTTUP		CON	31		'BUTTON RELEASED VALUE (5 BITS)
        PRESSED		CON	0		'BUTTON PRESSED VALUE (5 BITS)
        RELEASED	CON	1		'BUTTON RELEASED VALUE
        BUTTDOWN	CON	2		'BUTTON STILL PRESSED VALUE
        BETWEEN		CON	3		'BUTTON IN BETWEEN STATES (TRANSITION)
        '
        ' ********************************************************************
        '		       Declare Data Variables
        ' ********************************************************************
        TIMESEGM    VAR WORD BANK0		'SCRATCH VARIABLE USED FOR INTERRUPT ROUTINE
    
        BITSWORD	VAR	WORD BANK0	'BITS FOR STORAGE
        COWS_HOME	VAR BITSWORD.0	'LOOP FLAG
    
        JUNK	    VAR	BYTE	'SCRATCH VARIABLE
    
        '********************** PUSH BUTTON DEBOUNCE VARIABLES ************************
        BUTTBYTE	VAR	BYTE(NBTNS)	'BUTTON COUNTER ARRAY
        BTNSTATE	VAR	BYTE(NBTNS)	'BUTTON STATE
        BTNCLR		VAR	BYTE	'BUTTON ONE SHOT FLAG
        BTNINDEX	VAR	BYTE	'BUTTON ARRAY INDEX
    	' 
    	
    
    ;-- Place a copy of these variables in your Main program -------------------
    ;--   The compiler will tell you which lines to un-comment                --
    ;--   Do Not un-comment these lines                                       --
    ;---------------------------------------------------------------------------
    wsave   VAR BYTE    $20     SYSTEM      ' location for W if in bank0
    ;wsave   VAR BYTE    $70     SYSTEM      ' alternate save location for W 
                                             ' if using $70, comment wsave1-3
    
    ' --- IF any of these three lines cause an error ?? ------------------------
    '       Comment them out to fix the problem ----
    ' -- Which variables are needed, depends on the Chip you are using -- 
    wsave1  VAR BYTE    $A0     SYSTEM      ' location for W if in bank1
    ;wsave2  VAR BYTE    $120    SYSTEM      ' location for W if in bank2
    ;wsave3  VAR BYTE    $1A0    SYSTEM      ' location for W if in bank3
    ' --------------------------------------------------------------------------	'
    '*********************************************************************
    asm
    INT_LIST  macro    ; IntSource,       Label,  Type, ResetFlag?
    		INT_Handler    TMR1_INT,	_TIMR1,		PBP,  yes
    	endm
    	INT_CREATE               ; Creates the High Priority interrupt processor
    ENDASM
    
    
    
    GOTO	 MAINLOOP
    
    '*********************************************************************
    TIMR1:		'INTERRUPT SERVICE ROUTINE FOR TIMER 1
    '*********************************************************************
       	T1CON.0 = 0      'TURN OFF TIMER1
        TIMESEGM.LOWBYTE = TMR1L   'READ CURRENT TIMER VALUE
        TIMESEGM.HIGHBYTE = TMR1H
        TIMESEGM = TIMESEGM + TIMESEG 'RELOAD & ADJUST FOR LATENCY
        TMR1H = TIMESEGM.HIGHBYTE	'WRITE NEW TIMER VALUE
        TMR1L = TIMESEGM.LOWBYTE
        T1CON.0 = 1      'TURN ON TIMER1
    
    '****************** POLL PUSH BUTTON INPUTS FOR DEBOUNCE **********************
    	BUTTBYTE(0) = (BUTTBYTE(0) << 1) + BTN0
    	BUTTBYTE(1) = (BUTTBYTE(1) << 1) + BTN1
    	BUTTBYTE(2) = (BUTTBYTE(2) << 1) + BTN2
    
    @	INT_RETURN
    
    ' ********************************************************************
    '				SUBROUTINES
    ' ********************************************************************
    
    '*********************************************************************
    BTNSTAT:		'UPDATE FOR ALL BUTTON'S PRESS or RELEASE
    '*********************************************************************
    	BTNINDEX = NBTNS	'CYCLE THRU ALL BUTTONS AVAILABLE
    	WHILE BTNINDEX	'SET UP INDEX POINTER
    	     BTNINDEX = BTNINDEX - 1	'DECREMENT THRU ALL BUTTONS
    		'JUNK = BUTTBYTE(BTNINDEX) & PRESSED	'AND ONLY WITH TESTED BITS (USE FOR PRESSED = HIGH)
    		JUNK = BUTTBYTE(BTNINDEX) & BUTTUP	'AND ONLY WITH TESTED BITS (USE FOR PRESSED = LOW)
    		IF JUNK = BUTTUP THEN	'CHECK CURRENT STATE FOR RELEASED
    			BTNSTATE(BTNINDEX) = RELEASED		'SET HISTORY STATE
    			BTNCLR.0(BTNINDEX) = 0	'CLEAR ONE SHOT FLAG
    		ELSE
    			IF JUNK = PRESSED THEN	'CHECK CURRENT STATE FOR PRESSED
    				IF BTNCLR.0(BTNINDEX) = 0 THEN	'IF ONE SHOT FLAG IS CLEAR
    					BTNCLR.0(BTNINDEX) = 1		'SET IT
    					BTNSTATE(BTNINDEX) = PRESSED	'SET HISTORY STATE
    				ELSE	'SET CURRENT STATE FOR STILL BEING PRESSED
    					BTNSTATE(BTNINDEX) = BUTTDOWN	'SET HISTORY STATE
    				ENDIF    		
    			ELSE		'SET CURRENT STATE FOR IN BETWEEN STATES
    				BTNSTATE(BTNINDEX) = BETWEEN	'SET HISTORY STATE
    			ENDIF
    		ENDIF
    		
    	WEND
    	RETURN
    	
    ' ********************************************************************
    MAINLOOP:'				PROGRAM LOOP
    ' ********************************************************************
    	
    	
    	' ********************************************************************
        '				SYSTEM INITIALIZATION
        ' ********************************************************************
    	TRISA=%11111000
    	'TRISB=%00000000
    	TRISC=%11111111
    	 ADCON1=6
    	;	Initialize the ports using bits
    	PORTA=7
        pause 500
        PORTA=0
        COWS_HOME=0	
        TIMESEGM = TIMESEG      'SET TMRREG FOR 10mS INTERRUPT
        TMR1H = TIMESEGM.HIGHBYTE
        TMR1L = TIMESEGM.LOWBYTE
     @	INT_ENABLE  TMR1_INT    ; Enable Timer 1 Interrupts
        T1CON = $21      'TURN ON TIMER1  with prescale value 1:8  
       	 	               
     
    
    
    
    
    
    	REPEAT
    
    		GOSUB BTNSTAT		'UPDATE FOR ALL BUTTON'S PRESS or RELEASE ONCE PER LOOP
    
    		IF BTNSTATE(0) = PRESSED THEN	'FIRST PRESS OF BUTTON #0
    			LED0 = ~LED0	'TOGGLE LED0 PORT PIN
    		ENDIF
    		IF BTNSTATE(1) = PRESSED THEN	'FIRST PRESS OF BUTTON #1
    			LED1 = ~LED1	'TOGGLE LED1 PORT PIN
    		ENDIF
    		IF BTNSTATE(2) = PRESSED THEN	'FIRST PRESS OF BUTTON #2
    			LED2 =  ~LED2	'TOGGLE LED2 PORT PIN
    		ENDIF
    				
    	UNTIL COWS_HOME
    ' ********************************************************************
    	END				'SHOULD NEVER GET HERE.........
    	STOP

  22. #22
    Member
    Join Date
    Mar 2013
    Location
    Sheboygan, Wisconsin, USA
    Posts
    57
    Richard,

    I replaced my 4MHz crystal with an 8MHz oscillator because I noticed you had defined "OSC" at 8 because I assume this is how your bench test is set up. I ran the code and it worked as designed. So many thanks for the corrections to my code. I also set up my hardware with the 4MHz crystal and the code still ran well.

    So I am left with one question. My code calculated the value of "TIMESEG" based on a 4 MHz clock speed and I got the final constant from that. Your code retained the same calculation but used it with an 8 MHz clock speed and the code still worked. What effect did using the 8MHz "OSC" setting have on that calculation? My perception is that it would have changed the interrupt timout to something other than 10ms and the button minimum press time to something other than 50ms?

    This code will no doubt find its way into more of my future designs since the utility of it is essential to a good user interface when combined with scrolling prompts.
    Many thanks to you and Dave for a successful result.

    Allan

  23. #23
    Senior Member
    Join Date
    Sep 2011
    Location
    australia
    Posts
    247
    Quote Originally Posted by abecker View Post
    .

    So I am left with one question. My code calculated the value of "TIMESEG" based on a 4 MHz clock speed and I got the final constant from that. Your code retained the same calculation but used it with an 8 MHz clock speed and the code still worked. What effect did using the 8MHz "OSC" setting have on that calculation? My perception is that it would have changed the interrupt timout to something other than 10ms and the button minimum press time to something other than 50ms?
    an interrupt rate of anywhere between 5 and 60mS seems to give an acceptable response time.
    but in reality it's a trade off between response time for key press detection and lost cpu cycles in the main loop. the "faster" the interrupt the more cpu time lost. this method can also severely impact time critical commands like serin/serout owin/owout if not done correctly, a rbc style interrupt key detection is generally much more efficient. its also possible to poll the keys in the main loop in the same manner without using interrupts at all (the scan rate is not super critical for buttons) in a good design

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •