PDA

View Full Version : Interruptus Frustratus



Byte_Butcher
- 8th April 2009, 04:45
I'm having a hard time again...

My interrupts seem to be taking over, and my main loop never gets executed. :(

The project I'm working on controls a few simple outputs based on some user input , some machine inputs, and the real time from a DS1302.

The user input comes from the "touch sensors" available in the 16F727. The time (minutes and seconds only) and other numeric info are displayed on a 4 digit, 7-segment LED display.

The touch sensors use Interrupts based on TMR1 and TMR0.

I had much of my program working until I added the touch sensor routine, then everything went to hell....

I've trimmed my code back to just the touch sensor and RTC code trying to get it to work. The problem is that the interrupts seem to "take over the show" and my main loop never gets executed.

This bit of test code should display the "ones of minutes" and the tens and ones of seconds on a LED display.

When a sensor button is touched, the number of the button pressed should be momentarily displayed on the 4th digit of the display.

The problem is that while the touch sensors work fine and the appropriate number is displayed when touched, the time is *not* displayed because the main loop is never executed. If I disable the interrupts so that the touch sensors don't work, then the main loop is executed and the time is displayed just fine.

Why does the interrupt routine highjack the show and keep the main loop from executing?

Here's the bit of test code that I'm working with... any suggestions would be a appreciated.
It's a little too big, so I'll split it into 2 posts....



'************************************************* ***************

Include "MODEDEFS.BAS" ' Include Shiftin/out modes
INCLUDE "DT_INTS-14.bas" ' Base Interrupt System
INCLUDE "ReEnterPBP.bas" ' Include if using PBP interrupts
DEFINE LCD_DREG PORTD ' Set LCD Data port
DEFINE LCD_DBIT 4 ' Set starting Data bit (0 or 4) if 4-bit bus
DEFINE LCD_RSREG PORTC ' Set LCD Register Select port
DEFINE LCD_RSBIT 5 ' Set LCD Register Select bit
DEFINE LCD_EREG PORTC ' Set LCD Enable port
DEFINE LCD_EBIT 6 ' Set LCD Enable bit
DEFINE LCD_BITS 4 ' Set LCD bus size (4 or 8 bits)
DEFINE ADC_BITS 8 ' Set number of bits in ADC result
DEFINE ADC_CLOCK 3 ' Set clock source for ADC (rc = 3)
DEFINE ADC_SAMPLEUS 100 ' Set ADC sampling time in microseconds
define I2C_SCLOUT 1 ' Set I2C Clock to drive high instead of needing pullup
DEFINE CCP2_REG PORTC 'Hpwm2 port
DEFINE CCP2_BIT 1 'Hpwm2 bit
DEFINE OSC 16 '16 MHz

OSCCON = %00110000 'set OSC to 16 mHz.

@ __config _CONFIG1, _DEBUG_OFF & _PLL_EN & _BORV_2_5 & _BOR_ON & _CP_OFF & _MCLRE_OFF & _PWRT_EN & _WDT_ON & _INTOSCIO
@ __config _CONFIG2, _VCAP_RA6


TRISA= %00000000 'Set 'em all to outputs
TRISB= %11111111 'all input
TRISC= %00011000 'Set portC all outputs except C.3 and C.4
TRISD= %00000000 'Set portD all outputs
TRISE= %00000000 'Set portE all outputs

ANSELA= %00000000 ' Set all pins to digital
ANSELB= %11111111 ' all analog
ANSELD= %00000000 ' Set all pins to digital
ANSELE= %00000000 ' Set all pins to digital

CPSCON0 = %10001101 'Cap sense on, high range oscillator

'---------VARIABLES-----------------------------------

'LED Driver (CAT4016) Variables-----------------------
LEDData var byte
numeral var byte
LEDCounter var byte
digit1 var byte : digit1=0
digit2 var byte : digit2=0
digit3 var byte : digit3=0
digit4 var byte : digit4=0


'DS1302 RTC Variables-----------------------
rtcyear var byte
rtcday var byte
rtcmonth var byte
rtcdate var byte
rtchr var byte
rtcmin var byte
rtcsec var byte
rtccontrol var byte
mathtemp var Byte 'For doing BCD to Decimal conversion


'Touch sensor related Variables----------------------
index var byte : index=0 'for the CSM buttons
timercount var word[6] : timercount = 0 ' raw count from TMR1, for each channel
timerave var word[6] : timerave = 0 ' long term average of timercount, for each channel
AvgCount var byte : AvgCount = 32 'number of samples to average
delay var word : delay = 0 ' to delay startup until the timers have stabilized.


'---------I/O PINS-----------------------------------
'Alias pins - DS1302---------------------------------
RST var PORTE.2 '1302 CS
IO var PORTE.1 '1302 I/O
SCLK var PORTE.0 '1302 Clock

'Alias pins - EEPROM---------------------------------
SDA var PORTC.4 'EEPROM data
SCL var PORTC.3 'EEPROM Clock

'Alias pins - LED display----------------------------
S_IN var PORTA.3 'CAT4016 data
LEDCLK var PORTA.2 'CAt4016 clock
BLANK var PORTC.1 'CAT4016 blanking line
LATCH VAR PORTA.0 'Cat4016 Latch

'Initialize variables--------------------------------
hpwm 2,100,1000 'set LED brightness
timerave = 2000 'preset the timer average to a "close" value
'-----------------------------------------------------------------
'Set initial time (this can be removed in final version)
'rtcyear = $09 'Set Year
'rtcday = $1 'Set Day
'rtcmonth = $1 'Set Month
'rtcdate = $1 'Set Date of month
'rtchr = $1 'Set Hour
'rtcmin = $1 'Set Minutes
'rtcsec = $1 'Set Seconds
'Gosub settime ' DO IT! - Set the time

'--------------------------------------------------------------------------------
'initialize the DS1302 and set the trickle charge rate
Low SCLK
low rst ' Reset RTC
high rst ' RTC Ready for transfer
Shiftout IO, SCLK, LSBFIRST, [$8e, 0] ' Enable write
low rst
high rst
Shiftout IO, SCLK, LSBFIRST, [$90, %10101011] ' Set charger on and to "2 diodes, 8Kohm"
Low RST ' Reset RTC
pause 5

' -----Grab the time out of the RTC and display it--------------------------
gosub gettime
gosub sortdigits
pause 1000

'-----Set up Interrrupts--------------------------------------------
ASM
INT_LIST macro ; IntSource, Label, Type, ResetFlag?
INT_Handler TMR1GATE_INT, _CheckCount, PBP, yes
endm
INT_CREATE ; Creates the interrupt processor
ENDASM

@ INT_ENABLE TMR1GATE_INT ; enable Timer1 gate interrupt


'----- Timer Setup------------------------------------------------
OPTION_REG = %11010110 'setup source and prescaler for TMR0
INTCON.2 = 0; // clear TMR0 interrupt flag
INTCON.5 = 1; // enable TMR0 interrupt
T1CON = %11000101 'Timer clock source=CAPOSC, prescale=1:1, dedicated OSC disabled, no external clock synchronize, timer on
T1GCON = %11100001 'Timer1 gate init/ Toggle Mode
PIR1.7 = 0 'Clear Gate Interrupt Flag


'----------JUST GET IT GOING-------------------
GOTO Main 'skip the subroutines and get on with it.

Finish below....

Byte_Butcher
- 8th April 2009, 04:46
....Continued from above.



'---[TMR1 Gate - interrupt handler]--------------------------------------------------
disable
CheckCount:
T1CON.0 = 0 'stop timer1
timercount[index] = TMR1L + TMR1H << 8 'stuff the contents of the timer register into a word
TMR1L = 0 'reset counter to 0...
TMR1H = 0 'upper 1/2 too

If delay < 1001 then 'delay user access to the buttons until the count "average" has stabilized.
delay = delay +1
endif

If timercount[index] > timerave[index] +70 then 'Help provide a speedy recovery for released buttons
timerave[index] = timercount[index]
endif

if (timercount[index] - timerave[index]) < AvgCount Then Close
timerave[index] = timerave[index] - (timerave[index]/AvgCount) 'average the count so you have a reference to compare....
timerave[index] = timerave[index] + (timercount[index]/AvgCount) '...with timercount
Goto checkscore

Close:
timerave[index] = timerave[index] - (timerave[index]/(AvgCount/16))
timerave[index] = timerave[index] + (timercount[index]/(AvgCount/16))

Checkscore: 'Determine if a button is pressed (count is less than average)
If timerave[index] - (timerave[index] /50) > timercount[index] and (delay > 1000) then
gosub ServiceHardw 'Signal that a button is pressed.
endif

If index < 5 then 'Increment to the next touch sensor input
index = index +1
else
index = 0
Endif
CPSCON1 = index

TMR0 = 0 'reset timer 0
T1CON.0 = 1 'restart timer1

@ INT_RETURN
enable


'----------SUBROUTINES--------------------------------
'Subroutine for Button 1-----------------------------
Butt1:
Digit4 = 1
gosub sortdigits
Pause 500
digit4 = 0
gosub sortdigits
Return


'Subroutine for Button 2-----------------------------
Butt2:
Digit4 = 2
gosub sortdigits
Pause 500
digit4 = 0
gosub sortdigits
Return


'Subroutine for Button 3-----------------------------
Butt3:
Digit4 = 3
gosub sortdigits
Pause 500
digit4 = 0
gosub sortdigits
Return


'Subroutine for Button 4-----------------------------
Butt4:
Digit4 = 4
gosub sortdigits
Pause 500
digit4 = 0
gosub sortdigits

Return


'Subroutine for Button 5-----------------------------
Butt5:
Digit4 = 5
gosub sortdigits
Pause 500
digit4 = 0
gosub sortdigits
Return


'Subroutine for Button 6-----------------------------
Butt6:
Digit4 = 6
gosub sortdigits
Pause 500
digit4 = 0
gosub sortdigits
Return


'Subroutine to display numbers on 7-segment display-----------
display:
shiftout S_in, LEDCLK, 1, [LEDData]
return


'Subroutine to map numbers to the correct LED segments------
getpattern:
lookup numeral,[$EE,$28,$CD,$6D,$2B,$67,$E7,$2C,$EF,$6F], LEDData 'MSBFirst / Right side up digits
return


'Subroutine to read time from RTC--------------------
gettime:
RST = 1 ' Ready for transfer
' Read all 8 RTC registers in burst mode
Shiftout IO, SCLK, LSBFIRST, [$bf]
Shiftin IO, SCLK, LSBPRE, [rtcsec, rtcmin, rtchr, rtcdate, rtcmonth, rtcday, rtcyear, rtccontrol]
RST = 0 ' Reset RTC
mathtemp =(rtcsec>>4)*10+(rtcsec & $0F) 'convert BCD seconds into Decimal seconds
digit1 = mathtemp dig 0 'digit 1 = ones of seconds
digit2 = mathtemp dig 1 'digit 2 = tens of seconds
mathtemp =(rtcmin>>4)*10+(rtcmin & $0F) 'convert BCD minutes into Decimal minutess
digit3 = mathtemp dig 0 'digit 3 = ones of minutes
' digit4 = mathtemp dig 1
gosub sortdigits
Return


'Subroutine to sort out the buttons-------------------
ServiceHardw:
branchl index, [Butt1,Butt2,Butt3,Butt4,Butt5,Butt6]
return


' Subroutine to write time to RTC------------------------
settime:
RST = 1 ' Ready for transfer
Shiftout IO, SCLK, LSBFIRST, [$8e, 0] ' Enable write
RST = 0 ' Reset RTC
RST = 1 ' Ready for transfer
' Write all 8 RTC registers in burst mode
Shiftout IO, SCLK, LSBFIRST, [$be, rtcsec, rtcmin, rtchr, rtcdate, rtcmonth, rtcday, rtcyear, 0]
rst = 0
Return


'Subroutine to lookup numbers and put them in the correct LED digit-----------
sortdigits:
numeral = digit4
gosub getpattern
swap LEDData.0, LEDdata.4 'Swap "DP" and "G" segments on upsidedown digits
gosub display
numeral = digit3
gosub getpattern
LEDData.4 = 1 'Turn on lower DP in colon
gosub display
numeral = digit2
gosub getpattern
swap LEDData.0, LEDdata.4 'Swap "DP" and "G" segments on upsidedown digits
LEDData.0 = 1 'Turn on upper DP in colon
gosub display
numeral = digit1
gosub getpattern
gosub display
High Latch
pause 1
low Latch
return


'Main loop - Gets the time and displays it----------------------------------
main:
gosub gettime
gosub sortdigits
pause 100
Goto main ' repeat until nauseated...

End



Thanks!

Steve

Acetronics2
- 8th April 2009, 07:35
Hi,

I didn't know DT interrupts were using Enable and Disable Commands ...

Alain

Coïtus interruptus frustrans est ...

Darrel Taylor
- 8th April 2009, 07:39
INTCON.5 = 1; // enable TMR0 interrupt
There's no handler for the TMR0 interrupt.

So the flag for that interrupt never gets cleared.
It then continuously enters/exits the interrupt processor, never to see the light of day again.

Added: And Alain is correct. disable/enable has no effect on DT_INTS.
<br>

Jerson
- 8th April 2009, 07:40
Steve



Checkscore: 'Determine if a button is pressed (count is less than average)
If timerave[index] - (timerave[index] /50) > timercount[index] and (delay > 1000) then
gosub ServiceHardw 'Signal that a button is pressed.
endif

Your call to gosub ServiceHardw is the culprit. It nests other subroutines. This is what may be consuming the time. I can recommend you replace the call to gosub ServiceHardw with a bit variable getting set. You then check the bit variable in your mainline code to decide if you need to serviceHardw. It will definitely yield results.



Checkscore: 'Determine if a button is pressed (count is less than average)
If timerave[index] - (timerave[index] /50) > timercount[index] and (delay > 1000) then
ServiceHw=1
endif


In your main, put this


ServiceHw var bit ' declarations section

If ServiceHw then gosub ServiceHw



Jerson

Byte_Butcher
- 8th April 2009, 17:26
Alain, Darrel, Jerson, thanks very much! You guys are great!

Sooo... if "Disable" & "Enable" don't work with DT_INT, what keeps interrupts from occurring during the interrupt handler? I assume that DT_INT must "protect" it's own handler?

Is there a way for me to disable the interrupts during certain subroutines?
When I get the rest of this put together there will be several places where I need to kill the interrupts to perform time sensitive tasks. Is that possible using DT_INT ?
Do I simply turn the interrupts on and off with the GIE bit when I want to? (INTCON.7 = 0) ?

Darrel, thanks for catching the TMR0 interrupt with no handler. I don't know why I keep doing that. :o I had to change the Timer resource from TMR2 to TMR0 and I enabled it's interrupt unnecessarily. Thanks! Turning TMR0 interrupt off helped but didn't completely cure the problem.

Jerson, please print yourself a pretty gold star and pin it to your shirt for the day!
The call to gosub "ServiceHardw" was indeed the big culprit. I assume that I just had so much stuff inside my interrupt handler that I ran out of time to do anything else before the next interrupt ?
I set a bit to tell when a button is pressed and check it in my main code as you suggested, and it works MUCH better now!

There's clearly a few bugs still to work out before I start adding the rest of my code back in, but it's looking pretty hopeful now. :)

Thanks again to all of you. What a great resource!


Steve

mister_e
- 8th April 2009, 17:38
Enable/Disable are for PBP ON INTERRUPT GOTO

For DT-INTS you have
@ INT_ENABLE IntSource
and
@ INT_DISABLE IntSource

You could disable all interrupt by clearing INTCON GIE too. Used with care this could work.

Jerson
- 8th April 2009, 18:14
Alain, Darrel, Jerson, thanks very much! You guys are great!
...snip...
Jerson, please print yourself a pretty gold star and pin it to your shirt for the day!
...snip...
Thanks again to all of you. What a great resource!
Steve
Wow, I am thrilled for that. Thank you. I'm sure many more will learn from your problem as they browse this resource. I myself have used this website to learn from DT, Steve (bow) many times before I began to return the favour. It's my hope that you will be able to soon. :thumbs up:

Byte_Butcher
- 8th April 2009, 19:13
Enable/Disable are for PBP ON INTERRUPT GOTO

For DT-INTS you have
@ INT_ENABLE IntSource
and
@ INT_DISABLE IntSource

You could disable all interrupt by clearing INTCON GIE too. Used with care this could work.

AHA! Thanks Steve!

When using @ INT_DISABLE to protect a subroutine, does it also protect nested gosubs, or do I need to disable interrupts in each nested subroutine to protect them all from interrupt?

Thanks!

mister_e
- 8th April 2009, 19:20
It protect what's in between DISABLE and ENABLE, so yes if your Gosub are in between them, they will be "protected" as well.

Byte_Butcher
- 8th April 2009, 19:54
It protect what's in between DISABLE and ENABLE, so yes if your Gosub are in between them, they will be "protected" as well.

Excellent. Thanks!




Steve

Darrel Taylor
- 8th April 2009, 22:36
Woohoo! Go TEAM Go!

And just to add more info ...
When a 16F PIC is servicing an interrupt, no other interrupts can occur until the current one is finished. The GIE bit is cleared automatically by hardware at the start of the interrupt. Global ints are then re-enabled when it executes a RETFIE (return from interrupt) at the end of the handler.

So there's no need to try to keep a handler from being interrupted, because it can't.

With ON INTERRUPT, the actual Hardware INTERRUPT was serviced long before it gets to the handler, so you need to disable/enable with those.

Byte_Butcher
- 8th April 2009, 23:39
Shucks, last week I cudnt even spel PIC, and now I are programin one! :D

Data Sheets and Application Notes are wonderful things, but sometimes there's no substitute for a good teacher(s) to help explain things, fill in the details, or to just shove me over a hurdle that I can't find my way around...

I really appreciate ALL the folks who've taken time to help... but especially Darrel for creating good things like DT_INTS and then having the patience to put up with all the silly questions we newbies will certainly have about it!
THANKS!

It's been a good day. I learned a lot.


Steve

rsocor01
- 13th April 2009, 06:27
Hi all,
I've been working in a project similar to byte_butcher's but using PBP only (I don't know any MPASM). My touch buttons work nicely but when I try to use GOSUB inside the ISR routine somehow PBP doesn't like it. I have my sub-routines outside the Disable-Enable of the ISR routine but it doesn't work.

Am I missing something here?? Darrel said it is posible to call sub-routines when the interrupts are disable.

Thanks in advance for any help.

Robert

Darrel Taylor
- 13th April 2009, 07:45
... My touch buttons work nicely but when I try to use GOSUB inside the ISR routine somehow PBP doesn't like it. I have my sub-routines outside the Disable-Enable of the ISR routine but it doesn't work.
If you are putting DISABLE/ENABLE around your interrupt handler ...
can I assume you are using ON INTERRUPT?

With ON INTERRUPT, the handler and any subroutines called by the handler MUST be inside DISABLE/ENABLE directives.

But if at all possible, GOSUB's should be avoided entirely in any interrupt handler.
<br>

rsocor01
- 13th April 2009, 22:51
Thank you Darrel. That solves part of my problems in this project I'm working on.

Robert

rsocor01
- 17th April 2009, 20:36
Attached is some of my code (without all the other stuff that nobody cares about) of the Capacitive Sensing Module (CSM) that I'm working on. My project is similar to Byte_Butcher's but I'm only using PBP. There is still some work to be done like having some arrays would be nice, but so far the touch pads work fine.



'************************************************* ***************
'* DEFINING VARIABLES AND SETTING INITIAL VALUES *
'************************************************* ***************

DEFINE OSC 4 'Define Oscillator to 4MHz

I VAR WORD
TRIP VAR BYTE
STABILIZE_CSM VAR BYTE 'VARIABLE USED TO LET MCU STABILIZE CSM
BUTTON VAR BIT
START_PROGRAM VAR BIT 'ONLY USED AT START OF PROGRAM (=0 NO BUTTONS TOUCHED YET)
RAW_0 VAR WORD
RAW_1 VAR WORD
AVERAGE_0 VAR WORD
AVERAGE_1 VAR WORD
LED_0 VAR PORTB.6
LED_1 VAR PORTB.7

TEST_VAR VAR BYTE

STABILIZE_CSM = 2 'LET CSM RUN 2 TIMES BEFORE TAKING READINGS
TRIP = 50 'TRIP USED AS (1/50)*(AVERAGE)

LED_0 = 0 'LEFT BUTTON LED
LED_1 = 0 'RIGHT BUTTON LED

RAW_0 = 0
RAW_1 = 0
AVERAGE_0 = 0
AVERAGE_1 = 0

BUTTON = 0 'START WITH BUTTON 0 (LEFT)

TEST_VAR = 0

'************************************************* ***************
'* CAPACITIVE SENSING MODULE INITIALIZATION *
'************************************************* ***************
TRISA = %00000000 'Initialize ports as outputs
ANSELA = %00000000 'Initialize as digital ports
TRISB = %00010001 'Initialize PortB.0 and PortB.4 as inputs.
ANSELB = %00010001 'Initialize CPS4 and CPS0 as analog inputs.
TRISD = %00000000 'INITIALIZE ALL PORTS AS OUTPUTS
ANSELD = %00000000 'INITIALIZE ALL PORTS AS DIGITAL PORTS
TRISC = %10000000 'INITIALIZE RC7 AS INPUT (SERIAL RX)

CPSCON0 = %10001111 'CAPACITIVE SENSING MODULE IS ON. OSCILLATOR IS IN HIGH RANGE.
CPSCON1 = %00000000 'BITS 3-0, 0000= RB0/CPS0 IS THE CAPACITIVE INPUT CHANNEL.


'************************************************* ***************
'* TMR0 TIME BASE SETUP *
'* ENABLE TMR0 TIMER INTERRUPTS *
'************************************************* ***************
OPTION_REG = %11100011 'Bit 2-0, 011= prescaler rate 1:16 (TMRO INTERRUPTS EVERY 4.096 mSec)
INTCON = %01100000 'enable TMR0 interrupts. enables all unmasked peripheral interrupts
T1CON = %11000101 'Timer1 initialization
T1GCON = %11100001 'Timer1 gate init /Toggle Mode/TMR0 time base
PIR1.7 = 0 'Clear Gate Interrupt Flag. Timer1 gate is active (TMR1GIF BIT)
PIE1.7 = 1 'Disable the Timer1 Gate Acquisition complete interrupt (TMR1GIE BIT)

TMR0 = 0 'TMR0 overflows every (256-TMRO)*(Prescaler rate)(4*(1/4)uS)=4096uSec.

ON INTERRUPT GOTO ISR
INTCON = %10100000 'Enable Interrupts


'<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
'<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
'< MAIN PROGRAM >
'<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
'<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

START:
'0= PROGRAM HASN'T STARTED YET. 1= PROGRAM STARTED ALREADY (TOUCH BUTTON DETECTED).
START_PROGRAM = 0

WHILE START_PROGRAM = 0
PAUSE 1
WEND

MAIN:
FOR I = 1 TO 200
PAUSE 1
NEXT I
GOTO MAIN


'<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
'<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
'< INTERRUPT ROUTINE ISR >
'<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
'<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
DISABLE 'Disable further interrupts
ISR:
T1CON.0 = 0 'Stop/Clears Timer1 before reading TMR1

IF BUTTON = 0 THEN RAW_0.BYTE0 = TMR1L : RAW_0.BYTE1 = TMR1H
IF BUTTON = 1 THEN RAW_1.BYTE0 = TMR1L : RAW_1.BYTE1 = TMR1H

IF STABILIZE_CSM <> 0 THEN
STABILIZE_CSM = STABILIZE_CSM - 1 'RUN CSM 2 TIMES BEFORE TAKING A READING
IF BUTTON = 0 THEN AVERAGE_0 = 0
IF BUTTON = 1 THEN AVERAGE_1 = 0
ENDIF

'STABILIZING AVERAGE IF RAW OVERSHOOTS DURING STARTUP.
IF BUTTON = 0 THEN
IF AVERAGE_0 > (RAW_0 + 600) THEN AVERAGE_0 = 0
ENDIF
IF BUTTON = 1 THEN
IF AVERAGE_1 > (RAW_1 + 600) THEN AVERAGE_1 = 0
ENDIF

'SIMPLE DETECTION. IF RAW < AVERAGE*(1-2.0%)
IF BUTTON = 0 THEN
IF RAW_0 < (AVERAGE_0 - (AVERAGE_0/TRIP)) THEN
LED_0 = 1
PAUSE 500
LED_0 = 0

AVERAGE_0 = 0 'AVOIDS SECOND FALSE READINGS
STABILIZE_CSM = 2 'HELPS STABILIZE CSM TO AVOID FALSE READINGS
START_PROGRAM = 1 'ONLY USED AT START OF PROGRAM (=0 NO BUTTONS TOUCHED YET)
BUTTON = 1 'SWAP BUTTON BIT
CPSCON1 = %00000100 'CHANGE BUTTON CAPACITIVE INPUT CHANNEL
ELSE
AVERAGE_0 = RAW_0
CPSCON1 = %00000000
ENDIF
ELSE
IF RAW_1 < (AVERAGE_1 - (AVERAGE_1/TRIP)) THEN
LED_1 = 1
PAUSE 500
LED_1 = 0

AVERAGE_1 = 0 'AVOIDS SECOND FALSE READINGS
STABILIZE_CSM = 2 'HELPS STABILIZE CSM TO AVOID FALSE READINGS
START_PROGRAM = 1 'ONLY USED AT START OF PROGRAM (=0 NO BUTTONS TOUCHED YET)
BUTTON = 0 'SWAP BUTTON BIT
CPSCON1 = %00000000 'CHANGE BUTTON CAPACITIVE INPUT CHANNEL
ELSE
AVERAGE_1 = RAW_1
CPSCON1 = %00000100
ENDIF
ENDIF


TMR1L = 0 'Reset Timer1
TMR1H = 0
T1CON.0 = 1 'Restart/Enable Timer1 (TMR1ON BIT)
TMR0 = 0 'RESET TMR0 TO 0
INTCON.2 = 0 'Re-enable TMR0 interrupts. Bit 2, 0= clears overflow, 1= overflow ocurred

RESUME 'Return to main program
ENABLE 'Enable interrupts


END ' End of program

One of the differences that I can see between my program and Byte_Butchers is OPTION_REG.5. I have it set to one and Byte_Butcher's have it cleared. OPTION_REG.5 set to one means that the TMR0 clock source is the CPSOSC signal and if cleared means the clock source is the internal instruction cycle clock. It works either way because I have tried it. However, AN1171 "CSM using PIC16F72x" and the datasheet suggests to clear this bit, so I guess that would be the right thing to do.

Another difference between the two programs is CPSCON0.1. If set to 1 means the oscillator is sourcing current out of the pin and if cleared means the opposite. Again, It works either way but I don't know what would be the right way.

By experimenting with the program and displaying the different variables in the PC monitor through a serial connection I could come up with the above detection algorithm, which I can say it's pretty accurate (so far).

Finally, I want to say that testing this kind of CSM circuits on a breadboard (like I did) and using two adjacent pins on the MCU for CSM detection (like I also did) is not a very good idea. If you do that there will be some crosstalk interference between the two touch pads and touching one button will activate the other one. Apparently a breadboard is not good for this kind of circuits.

Robert