View previous topic :: View next topic |
Author |
Message |
asmboy
Joined: 20 Nov 2007 Posts: 2128 Location: albany ny
|
- SLEEP on the 16F616 just one PIN ISR [solved] |
Posted: Mon Feb 08, 2016 3:30 pm |
|
|
I am trying to put out somebody else's fire with
a 16F616-
and won't have a "working" hardware sample for another day or so.
but i do have the schemo and source code is CCS . (YEAH!)
PIC runs on INT 8 MHZ OSC - and #FUSE INTRC ( no IO funct)
the question involves sleep mode.
the fuses are set to include NOMCLR and the MCLR pin (A3) is uncommitted.
#USE FASTIO() is set for all ports and pin_A3 is TRIS'd as an INPUT but floated - no connection but to customers own ICSD jack.
BUT --
the data sheet says the NOT/MCLR pin (A3) must be pulled high in order to
enter sleep ......?
Question #1
is the NOMCLR fuse for sure the same as pulling the pin high externally?
---------------------
next:
pin_a4 is available - but set now as a debug output -
however mine for the taking to use as i see fit ..
i can easily trace jump it to a pulled-high push button already in the design.
It is trivial for me to make A4 an input -
Then after the trace jump - a4 as an input will be normally HIGH
and i can ideally use it to detect a wake up call when it goes LOW-
one of the OK-2-sleep logic requirements to ENTER sleep is that A4 is detected as HIGH
and YES i plan to add a delay function after we wake up to allow the finger on the button to release it or STAY locked low for the duration of wakefulness.
THEN there is a ( useless looking to me ) G*D Watchdog running with resets salted through out the code .... grrrrr
so when entering sleep i THINK i need to
1* set a detect point in MAIN to go to sleep under defined conditions
an when conditions are met: (including A4 currently HIGH)
2* disable watchdog timer
3* enable INT_RA4
4* enter sleep
INSIDE the INT on CHANGE A port ISR - handler
* disable further INTS ( no other INTS are used in the design so far)
* dumi read port_a to clear the int state
* restart the watchdog
* return --> to the instruction after the SLEEP in main - right ?
what i am having a devil of a time with is
getting the enable interrupt function options set correct to do the darned job
when i turn it on before executing SLEEP.
i'm unsure of the correct constants needed in setting up the
#INT_RA4 handler to get the high to low thing to work right
-----------
Q#2 ??
Can anybody knowledgeable give me a clue about optimal syntax
to correctly enable the int handler?
Last edited by asmboy on Mon Feb 08, 2016 8:16 pm; edited 1 time in total |
|
|
temtronic
Joined: 01 Jul 2010 Posts: 9229 Location: Greensville,Ontario
|
|
Posted: Mon Feb 08, 2016 3:54 pm |
|
|
re: NOMCLR
In 'regular ' use.
If the pin is tied high, then any low will reset the PIC.
NOMCLR effectively disables the 'hardware reset' function attached to the pin, allowing it to be used for I/O(some restrictions apply, gotta read the datasheets!). Without that functionality you either have to kill power to reset or run a watchdog.
As for watchdogs. they can be 'fun' depending on what the PIC has to do as you need to figure out 'worst case' timings to calculate how long the PIC can be in LALA land before it resets.
Good news is you will have the entire SOURCE code ! Hopefully you can figure out the guys 'programming style' which has to be easier than 30 year old Moto ASM code !!
Jay |
|
|
asmboy
Joined: 20 Nov 2007 Posts: 2128 Location: albany ny
|
|
Posted: Mon Feb 08, 2016 4:33 pm |
|
|
PART OF THE BRAINBURN IS THIS --
i get an invalid processor directive on the line
#INT_RA4
using 5.042
Code: |
void sleepysleep(void){
//disable_wdt
restart_wdt();
enable_interrupts(GLOBAL);
enable_interrupts(INT_RA4);
EXT_INT_EDGE(H_TO_L);
delay_cycles(8);
sleep();
delay_cycles(8);
disable_interrupts(GLOBAL);
delay_cycles(8);
restart_wdt();
}
#INT_RA4
void wakeywake(void){
byte dumi;
disable_interrupts(GLOBAL);
disable_interrupts(INT_RA4);
dumi=input_a();
}
|
|
|
|
jeremiah
Joined: 20 Jul 2010 Posts: 1349
|
|
Posted: Mon Feb 08, 2016 4:38 pm |
|
|
I don't think there are separate interrupts for each RA pin. It is probably one single interrupt for all of them (and you enable them individually to trigger that interrupt). See if #INT_RA works?
EDIT: I am not sure the High to Low setting can be used on IOC pins. Normally it is just for external interrupt pins denoted by INT, INT1, INT2, etc. Pin A2 looks like the INT pin. You may have to do the logic in the interrupt to verify high to low. |
|
|
stinky
Joined: 05 Mar 2012 Posts: 99 Location: Central Illinois
|
|
Posted: Mon Feb 08, 2016 4:52 pm |
|
|
I agree with Jeremiah.
Code: | enable_interrupts(INT_RA4);
enable_interrupts(INT_RA);
enable_interrupts(GLOBAL);
#INT_RA
void foo(void) {
input_a();
} |
Re: hi --> lo. I think it uses the last read it has made and looks for the opposite. Might not be interpreting that correctly.
edit: There ARE separate interrupt flags. So it is possible to wake the cpu without an interrupt handler |
|
|
asmboy
Joined: 20 Nov 2007 Posts: 2128 Location: albany ny
|
|
Posted: Mon Feb 08, 2016 5:04 pm |
|
|
and though this now compiles -it is still a bit unclear in the .LST file
if it is really the ode i want
Code: |
void sleepysleep(void){
restart_wdt();
enable_interrupts(GLOBAL);
enable_interrupts(INT_RA); //should this really come before
EXT_INT_EDGE(INT_RA4,H_TO_L); // << THIS ?
delay_cycles(8);
sleep();
delay_cycles(8);
disable_interrupts(GLOBAL);
delay_cycles(8);
restart_wdt();
}
#INT_RA
void wakeywake(void){
byte dumi;
disable_interrupts(GLOBAL);
disable_interrupts(INT_RA);
dumi=input_a();
}
|
|
|
|
asmboy
Joined: 20 Nov 2007 Posts: 2128 Location: albany ny
|
|
Posted: Mon Feb 08, 2016 5:26 pm |
|
|
so then brute force on IOCA but see what .LST conveys ???
Code: |
/ using IOCA directly at 0x96 for pin RA4
#BIT useRA4=0x96.4
void sleepysleep(void){
restart_wdt();
enable_interrupts(GLOBAL);
enable_interrupts(INT_RA); //should this really come before
USERA4=1; // brute force approach
// EXT_INT_EDGE(INT_RA4,H_TO_L); // first arg only takes int8
EXT_INT_EDGE(0,H_TO_L); // << THIS ?
delay_cycles(8);
sleep();
delay_cycles(8);
disable_interrupts(GLOBAL);
delay_cycles(8);
restart_wdt();
}
|
BUT .LST is puzzling as to USERA4
Code: |
.................... // using IOCA directly at 0x96 for pin RA4
.................... #BIT useRA4=0x96.4
....................
.................... void sleepysleep(void){
....................
.................... restart_wdt();
*
0192: CLRWDT
.................... enable_interrupts(GLOBAL);
0193: MOVLW C0
0194: IORWF 0B,F
.................... enable_interrupts(INT_RA); //should this really come before
0195: BSF 0B.3 // PORTA IE enabled
0196: MOVLW FF // load work reg
0197: BSF 03.5 //select bank one effectively adding 0x80 to address
0198: IORWF 16,F // turn on ALL bits in port a INT enable <<<<<
.................... USERA4=1; // brute force approach
0199: BSF 16.4 // turned on again
.................... // EXT_INT_EDGE(INT_RA4,H_TO_L); // first arg only takes int8
.................... EXT_INT_EDGE(0,H_TO_L); // << THIS ?
019A: BCF 01.6
.................... delay_cycles(8);
019B: MOVLW 02
019C: BCF 03.5
019D: MOVWF 20
019E: DECFSZ 20,F
019F: GOTO 19E
01A0: NOP
.................... sleep();
|
i'm at the point of using #ASM to set ONLY the RA4 edge INT
Last edited by asmboy on Mon Feb 08, 2016 6:15 pm; edited 1 time in total |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Mon Feb 08, 2016 6:13 pm |
|
|
The bank select bit is set, prior to executing that line. This makes the
register access be to Bank 1. So it will be accessing register 0x96.
Quote: | 0197: BSF 03.5
0198: IORWF 16,F
.................... USERA4=1; // brute force approach
0199: BSF 16.4 // <--- WTF ?????????????????????????????????????? |
As shown in bold below, INT_RA will enable all the PortA interrupts.
It's moving 0xFF to the IOCA register:
Quote: | .................... enable_interrupts(INT_RA); //should this really come before
0195: BSF 0B.3 // PORTA IE enabled
0196: MOVLW FF // load work reg
0197: BSF 03.5 //select bank one effectively adding 0x80 to address
0198: IORWF 16,F // turn on ALL bits in port a INT enable <<<<<
.................... USERA4=1; // brute force approach
0199: BSF 16.4 // turned on again |
But you don't want that. You could just write 0x10 (which is bit 4)
to IOCA with one line of code. Create a #byte statement for the IOCA
register to declare the address.
Also, ext_int_edge() is for the external interrupt only. It doesn't work
with interrupt-on-change interrupts. And, the 16F616 doesn't have the
ability to set the edge for interrupt-on-change. It will interrupt on either
edge. |
|
|
asmboy
Joined: 20 Nov 2007 Posts: 2128 Location: albany ny
|
|
Posted: Mon Feb 08, 2016 6:52 pm |
|
|
so i came up with this
Code: |
void sleepysleep(void){
restart_wdt();
enable_interrupts(GLOBAL);
enable_interrupts(INT_RA); //should this really come before
#ASM
MOVLW 0x10
ANDWF 0x96,W
#endasm
EXT_INT_EDGE(0,H_TO_L); // << THIS ?
delay_cycles(8);
sleep();
delay_cycles(8);
disable_interrupts(GLOBAL);
delay_cycles(8);
restart_wdt();
}
|
which produces THIS:
Code: |
.................... enable_interrupts(INT_RA); //should this really come before
0195: BSF 0B.3
0196: MOVLW FF
0197: BSF 03.5
0198: IORWF 16,F
.................... #ASM
.................... MOVLW 0x10
0199: MOVLW 10
.................... ANDWF 0x96,W
019A: ANDWF 16,W
.................... #endasm
.................... EXT_INT_EDGE(0,H_TO_L); // << THIS ?
019B: BCF 01.6
.................... delay_cycles(8);
019C: MOVLW 02
019D: BCF 03.5
019E: MOVWF 20
019F: DECFSZ 20,F
01A0: GOTO 19F
01A1: NOP
.................... sleep();
|
selecting only ONE port A pin for int handling was more fun than i thought and i wont know for sure till the hardware piece shows up.
thanks PCM !! |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Mon Feb 08, 2016 7:34 pm |
|
|
Enabling global interrupts should be the last thing done in the interrupt
setup code. A couple more things should be done before you do that:
Code: |
temp = input(PIN_A4); // Clear interrupt-on-change condition
clear_interrupt(INT_RA); // Clear the interrupt flag
enable_interrupts(GLOBAL);
|
The line below can be removed from the program. It's irrelevant to
interrupt-on-change interrupts:
Quote: | EXT_INT_EDGE(0,H_TO_L); |
|
|
|
asmboy
Joined: 20 Nov 2007 Posts: 2128 Location: albany ny
|
|
Posted: Mon Feb 08, 2016 8:08 pm |
|
|
I am in your debt as are we all.
Thanks PCM |
|
|
asmboy
Joined: 20 Nov 2007 Posts: 2128 Location: albany ny
|
|
Posted: Mon Feb 08, 2016 8:14 pm |
|
|
now all together
Code: |
#INT_RA
void wakeywake(void){
byte dumi;
disable_interrupts(GLOBAL);
disable_interrupts(INT_RA);
dumi=input_a();
}
#BYTE IOCA=0x96
void sleepysleep(void){
byte temp;
restart_wdt();
temp = input(PIN_A4); // Clear interrupt-on-change condition
clear_interrupt(INT_RA); // Clear the interrupt flag
enable_interrupts(INT_RA); //should this really come before
IOCA=0x10; // set ONLY RA4 for action
enable_interrupts(GLOBAL); // NOW let it fire
delay_cycles(8); // pause
sleep(); // snore
delay_cycles(8); // wakey here
// disable_interrupts(GLOBAL); // now done inside the ISR
// disable_interrupts(INT_RA);
// delay_cycles(8);
restart_wdt();
} |
|
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19519
|
|
Posted: Tue Feb 09, 2016 2:09 am |
|
|
As a couple of little comments on this:
First the INT_RAx stuff is very poorly explained. As has already been found, there is only one actual interrupt, INT_RA. So why does INT_RA4 exist?. The answer is in the IOC setting.
If you enable INT_RA4, then the compiler automatically does the IOC setting for you. However you can't 'use' RA4 as the actual name for the interrupt routine.
This is unfortunately not documented, but was found be experiment. So:
Code: |
enable_interrupts(INT_RA4);
//codes as:
0016: BSF 0B.3 //enable the interrupt
0017: BSF 03.5
0018: BSF 16.4 //set bit 4 in IOCA
|
Then there is a little 'oops' in the use of delay_cycles(8)
The point about having a delay after the sleep, is that the instruction after this operation is 'pre-fectched', and should be a NOP.
Now 'delay_cycles(1);', codes as a NOP. So delay_cycles(8) must be better?. Answer. No.
Code: |
.................... delay_cycles(8);
0019: MOVLW 02
001A: BCF 03.5
001B: MOVWF 20
001C: DECFSZ 20,F
001D: GOTO 01C
|
While:
Code: |
.................... delay_cycles(1);
0020: NOP
|
So the way to code what is wanted here is:
Code: |
void sleepysleep(void)
{
byte temp;
restart_wdt();
temp = input(PIN_A4); // Clear interrupt-on-change condition
clear_interrupt(INT_RA); // Clear the interrupt flag
enable_interrupts(INT_RA4); //This does the IOCA setting for you
enable_interrupts(GLOBAL); // NOW let it fire
delay_cycles(8); // pause
sleep(); // snore
delay_cycles(1); // Just one cycle here
restart_wdt();
}
|
Have fun. |
|
|
asmboy
Joined: 20 Nov 2007 Posts: 2128 Location: albany ny
|
|
Posted: Tue Feb 09, 2016 8:05 am |
|
|
i am always grateful to find how to do it better. and delighted that there are others willing to share. Thanks to you too Mr. T |
|
|
|