PDA

View Full Version : Sleep does not really sleep...



Ioannis
- 18th June 2022, 22:24
On a 16F886 the following code initially is in sleep mode having almost 0 current through PIC.

But after first interrupt-on-change (port B), the sleep does not zero current. It stays rather high at 1,8 mA.

Something is still on and cannot see what.



#config
Line1 = _DEBUG_OFF & _LVP_OFF & _FCMEN_OFF & _IESO_OFF & _BOR_OFF
Line2 = _CP_OFF & _MCLRE_ON & _PWRTE_ON & _WDT_OFF & _INTRC_OSC_NOCLKOUT
__CONFIG _CONFIG1, Line1 & Line2
__CONFIG _CONFIG2, _WRT_OFF; & _BOR40V
#endconfig

OSCCON = %01110001 '8MHz Clock

DEFINE OSC 8

OPTION_REG.0=1 'PSA0 PRESCALER SELECT 1:1 TO 1:256
OPTION_REG.1=1 'PSA1
OPTION_REG.2=1 'PSA2
OPTION_REG.3=1 'PRESCALER TO: 1->WDT, 0->TMR0
OPTION_REG.4=0 'T0SE SOURCE EDGE 1->H TO L, 0->L TO H
OPTION_REG.5=0 'T0CS 1->FROM RA4, 0->FROM INT. CLOCK
OPTION_REG.6=0 'INT EDGE SELECT 0->H TO L, 1->L TO H
OPTION_REG.7=0 'PULL UP 1->DISABLE, 0->ENABLE

adcon0=%10111101 'ADC on, input set at Fixed Ref 0.6 Volts,adc clk @ Fosc/32
adcon1=%10000000 'right justified
ansel=$00
anselh=$00

DEFINE ADC_BITS 10 ; Set-up ADC for fast 10-bit results
DEFINE ADC_SAMPLEUS 5
DEFINE HSER_RCSTA 90h
DEFINE HSER_TXSTA 20h
DEFINE HSER_BAUD 9600
DEFINE HSER_SPBRG 12 ' 9600 Baud @ 8MHz, 0,16%
DEFINE HSER_CLROERR 1 ' Clear overflow automatically

ccp1con=0

PORTA=%00000000
PORTB=%01111000
PORTC=%11000000'%10000001

TRISC = %10000000

TRISA = %00000000 'PA0: buzzer out
'PA1:
'PA2:
'PA3:
'PA4:
'PA5:
'RA6:

TRISB = %01111000 'PB0: col3 out
'PB1: col2 out
'PB2: col1 out
'PB3: line4 in
'PB4: line3 in
'PB5: line2 in
'PB6: line1 in
'PB7: N.C.

clear

'baudctl=%00010000 'Transmit inverted data (with no MAX232/USB2Serial needed)
BAUDCTL = %00000000 'Transmit true data (with MAX232/USB2Serial needed)
WPUB = %01111000
IOCB = %01111000
INTCON = %10001000

wsave var byte $70 system
wsave1 var byte $a0 system
wsave2 var byte $120 system
wsave3 var byte $1a0 system

a var word
pressed var byte
buzzer var portc.0

char var byte
idx var byte
array var byte[5]

keyin var byte
ix var byte

'------------- INTERRUPTS SETUP ---------------------
INCLUDE "DT_INTS-14.bas"
INCLUDE "ReEnterPBP.bas"

'------------- INTERRUPTS SETUP ---------------------
ASM
INT_LIST macro; IntSource, Label, Type, ResetFlag?
INT_Handler RBC_INT, _IOC2, PBP, yes
;INT_Handler TMR1_INT, _timer, PBP, yes
endm
INT_CREATE ; Creates the interrupt processor
ENDASM

@ INT_ENABLE RBC_INT ; Enable Port B on change
;@ INT_ENABLE TMR1_INT ; Enable Timer 1 Interrupts


goto main

'------------- INTERRUPTS SETUP ---------------------

'************************************************* **********************
'*
'* Interrupt on Change
'*
'************************************************* **********************

IOC2:
adcon0=%10111101 'ADC on, input set at Fixed Ref 0.6 Volts,adc clk @ Fosc/32
adcon1=%10000000 'right justified
txsta=$20 'Enable USART
rcsta=$90
keyin=portb & %01111000 'End mismatch on port B interrupt
'hserout [bin8 keyin,13,10]'check keyin
if keyin<>120 then
adcon0.0=1:adcon0.1=1 'Start adc for bat level check
pressed=1 'Flag key press
toggle buzzer
else
pressed=0
endif
@ INT_RETURN

main:
portb=$78 'set port to 01111000
txsta=0 'Turn off all peripherals
rcsta=0 'before sleeping
adcon0.0=0 'Disable ADC
pressed=0 'reset flag pressed
intcon.0=0 'reset RBIF flag
t1con.0=0 'turn off timmer 1
buzzer=0
@ sleep
@ nop
hserout ["Alive",13,10]

while adcon0.1:wend
a.byte0=adresl:a.byte1=adresh
if a>192 then
hserout ["Bat low",dec3 a,13,10]
else
hserout ["Bat ok",dec3 a,13,10]
endif

keyscan:
while 1
for ix=0 to 2 'scan columns
portb=~dcd ix 'invert polarity
keyin=portb & %01111000
if keyin<>120 then keycheck 'no key press,scan again
next ix
wend

keycheck:
' 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, *, #
' 117,59,61,62,91,93,94,107,109,110,115,118
keyin=portb & 127
lookdown keyin,[117,59,61,62,91,93,94,107,109,110,115,118],char
if char>11 then keyscan 'wrong key,noise etc
char=char+48
hserout [char,13,10]
array[idx]=char
idx=idx+1
if idx=5 then
hserout [str array\5,13,10]
idx=0
endif
pause 200
goto main

End


Thanks,
Ioannis

Ioannis
- 18th June 2022, 22:29
Additional info:

The ADC uses internal Vref of 0.6 volts to check the battery power supply. Before sleep the ADC is switched off.

Also UART is switched off, along with Tmr1 and some ports setting.

The only one that stays as input, is the Portb.7 the Rx pin but even if it is grounded the current stays at 1.8mA.

WDT is disabled.

BOR is disabled.

/MCLR is set as internal and as the data sheet states, there is an internal pull up.

Ioannis

Ioannis
- 18th June 2022, 23:04
The interrupt on change is created by a matrix keyboard connected on portB (0-6).

I noticed that a short keypress allows PIC go to sleep, while a longer press keeps it semi awake.

Then I added this while-wend check just before pressed=0 in the main routine and now works OK:



main:
portb=$78 'set port to 01111000
txsta=0 'Turn off all peripherals
rcsta=0 'before sleeping
adcon0.0=0 'Disable ADC
while pressed:pause 10:wend
pressed=0 'reset flag pressed
intcon.0=0 'reset RBIF flag
t1con.0=0 'turn off timmer 1
buzzer=0
@ sleep
@ nop



Though not elegant nor explains why this happens, it kinda fixed the problem.

Ioannis

Ioannis
- 19th June 2022, 21:47
Any idea how can the keyboard matrix scan routine check for key release, before jumps to the keycheck subroutine please?



keyscan:
while 1
for ix=0 to 2 'scan columns
portb=~dcd ix 'invert polarity
keyin=portb & %01111000
if keyin<>120 then keycheck 'if key is pressed then check it
next ix
wend


Ioannis

richard
- 20th June 2022, 09:04
from the data sheet, my best guess . i see no attempt to read portb before sleep


For enabled interrupt-on-change pins, the present valueis compared with the old value latched on the last read
of PORTB to determine which bits have changed or
mismatched the old value. The ‘mismatch’ outputs of
the last read are OR’d together to set the PORTB
Change Interrupt flag bit (RBIF) in the INTCON register.
This interrupt can wake the device from Sleep. The user,
in the Interrupt Service Routine, clears the interrupt by:
a) Any read or write of PORTB. This will end the
mismatch condition.
b) Clear the flag bit RBIF.
A mismatch condition will continue to set flag bit RBIF.
Reading or writing PORTB will end the mismatch
condition and allow flag bit RBIF to be cleared. The latch
holding the last read value is not affected by a MCLR nor
Brown-out Reset. After these Resets, the RBIF flag will
continue to be set if a mismatch is present.

Ioannis
- 21st June 2022, 15:29
Mismatch is taken care in the ISR (5th command in the IOC2 routine) and the then interrupt flag bit is reset.

My problem is that it does interrupt continuously as the user keeps pressing the button.

But since it is a matrix scanned keyboard, I want to check for key release and then take action, not on key press.

Ioannis

Jerson
- 21st June 2022, 16:32
Couple of things that caught my attention

1 - in keyscan, this lne should ensure the rows are high
portb=~dcd ix 'invert polarity
So, I guess, you should add
portb=~(dcd ix) + $78 'invert polarity

2. I infer, the keypress interrupt is just to wake the processor out of sleep. So, the interrupt routine needs to do nothing other than just return when the interrupt occurs. After that, the processor will be awake and the scan, beep, transmit routine can work like normal in the foreground. No need of any flags. If no key is found, the processor can go back to sleep.

This line need not re-scan till it finds a key. If no valid key is found, the processor can just go back to sleep.
if char>11 then keyscan 'wrong key,noise etc

Ioannis
- 21st June 2022, 21:09
Hi Jerson.

Thanks for the good points. Yes I will change the ISR to what you suggested. But in any case as it is now, it works very good. With your suggestion will be more efficient and fast in regards of ISR view.

The PortB has Pull-Ups enabled so the line is correct as it is in the program.

My only problem is how to check for key release instead of key press.

Ioannis

Jerson
- 22nd June 2022, 03:29
Not too difficult to check for key release to act on.

Right now you are checking a keypress when the portb value is different from $78

Key release is simply getting the transition from (Not $78) to $78

What caused it to be Not $78 is the key that got released.

For easy implementation in case of short keypresses, a tight blocking loop doing the above test is fine. In case you anticipate the user will have a button down for a long while, then a different approach using a software timer may give better results.

Just a short pseudo code as I am not well oiled in PBP these days



while 1
key = ~portb & $78
if (key <> $78) then
savedkey = key
else
if savedkey <> $78 then
' saved key is the one that was released
' act on saved key
endif
endif
wend


If you wish to have a keyboard that has buttons that give pressed, hold, repeat and release states, I suggest you look at some arduino code that shows such an implementation using a finite state machine