View previous topic :: View next topic |
Author |
Message |
apakSeO
Joined: 07 Dec 2016 Posts: 60 Location: Northeast USA
|
16F1503 IOC detects L2H when none specified |
Posted: Tue Oct 10, 2017 8:09 am |
|
|
PIC16F1503 & CCS v5.070
I have succeeding in putting this PIC16 to sleep and waking it up via button presses. The problem I am having is that if the user holds the button down to put the PIC16 to sleep, instead of tapping it, upon releasing the button the PIC16 wakes up as if an interrupt was specified on low-to-high transition. I've only enabled an interrupt on high-to-low transitions as the PIC16 is using internal pullups, and 0V is considered the asserted state. There is no hardware, RC debounce on the input pins. A stripped-down demonstration code follows:
Code: |
#include <16F1503.h>
#device PIC16F1503
#use delay(INTERNAL=16MHZ, CLOCK=2MHZ)
#fuses NOWDT,NOPROTECT,NOBROWNOUT,NOPUT
#fuses NOMCLR
#fuses NOCLKOUT
#fuses NOWRT
#fuses NOLPBOR
#fuses NOSTVREN
#fuses NOLVP
// ***************** PROTOTYPES ******************//
void gotoSleep(void);
void exitSleep(void);
//******************* MAIN *******************//
void main(void)
{
set_tris_a(0b00110000); // RA4 / RA5 as inputs
port_a_pullups(0b00110000); // Use internal pullups on RA4 / RA5
enable_interrupts(GLOBAL); // Enable global int to allow handler calls
while(1)
{
if(!input(PIN_A4)){ // Quick n' dirty debounce
delay_ms(50);
if(!input(PIN_A4)){
delay_ms(125);
gotoSleep();
}
delay_ms(125);
}
if(!input(PIN_A5)){ // Quick n' dirty debounce
delay_ms(50);
if(!input(PIN_A5)){
delay_ms(125);
gotoSleep();
}
delay_ms(125);
}
// Proof-of-life 50Hz signal//
output_toggle(PIN_C0);
delay_ms(10);
}
}
void gotoSleep(void)
{
enable_interrupts(INT_IOC_A4_H2L); // Enable IOC for PIN_A4 high-to-low; WORKS
enable_interrupts(INT_IOC_A5_H2L); // Enable IOC for PIN_A5 high-to-low; WORKS
sleep();
delay_cycles(1); // nop() for resume from sleep
}
void exitSleep(void)
{
disable_interrupts(INT_IOC_A4_H2L);
disable_interrupts(INT_IOC_A5_H2L);
}
#INT_IOC
void IOC_isr(void)
{
int8 portAstate;
portAstate = input_a();
if (interrupt_active(INT_IOC_A4_H2L))
{
clear_interrupt(INT_IOC_A4_H2L);
exitSleep();
}
if (interrupt_active(INT_IOC_A5_H2L))
{
clear_interrupt(INT_IOC_A5_H2L);
exitSleep();
}
}
|
What I observe is the following: Oscilloscope probe on RC0, upon system power up I observe a 50% duty cycle 50Hz square wave as expected.
If awake, tapping RA4 or RA5 puts the PIC to sleep, as expected.
If asleep, tapping RA4 or RA5 wakes the PIC up and the RC0 signal is observed, as expected.
If awake, holding RA4 or RA5 down puts the PIC to sleep. Upon releasing the pressed button, the PIC wakes up and the signal on RC0 resumes.
Why is the PIC waking up on a low-to-high transition from RA4/RA5? I have only specified interrupts enabled on high-to-low transitions. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19552
|
|
Posted: Tue Oct 10, 2017 11:59 am |
|
|
IOC programming doesn't allow successive sets. The second overrides the first. If you want to enable two pins for IOC, the syntax is:
Code: |
enable_interrupts(INT_IOC_A4_H2L | INT_IOC_A5_H2L);
|
These are not separate interrupts, but masks for the single interrupt.
You also have to clear the IOC bit for the interrupts that have triggered. This is not done for you.
Have a look at this thread where I show how this is done for RA1:
<https://ccsinfo.com/forum/viewtopic.php?p=195505> |
|
|
apakSeO
Joined: 07 Dec 2016 Posts: 60 Location: Northeast USA
|
|
Posted: Wed Oct 18, 2017 6:07 pm |
|
|
After reading your suggestions for code changes and about a dozen threads here in relation to IOC and waking from sleep, the issue still exists. I am still seeing a wake-from-sleep occurring on a low-to-high transition, despite attempting various methods of clearing the individual IOCAF bits.
In my system, the user pushes a button which shorts RA4/RA5 to ground through a 1k resistor. This puts the system into sleep just fine, but if the user so happens to hold down the button too long ( > the debounce routine delay) the PIC16 wakes up from sleep and resumes the 50Hz proof-of-life signal. I do not understand why it is waking up from sleep, because when the user releases the buttons, RA4/5 are pulled from a low to high state, and the uC should not be waking up on such a transition.
I noticed that I did not select which edge polarity I wanted the uC to wake from, so I thought that using the function ext_int_edge(H_TO_L) would remedy this issue. Alas, it did not have any effect.
Modified code below, with my comments on various experiments I tried. Any other suggestions I may have missed?
Code: |
#include <16F1503.h>
#device PIC16F1503
#use delay(INTERNAL=16MHZ, CLOCK=2MHZ)
#fuses NOWDT,NOPROTECT,NOBROWNOUT,NOPUT
#fuses NOMCLR
#fuses NOCLKOUT
#fuses NOWRT
#fuses NOLPBOR
#fuses NOSTVREN
#fuses NOLVP
// Interrupt mask bits, need to be cleared manually from IOC
#byte IOCAF = getenv("SFR:IOCAF") // Port A IOC register
#bit IOCAF4 = IOCAF.4 // Individual pins
#bit IOCAF5 = IOCAF.5 // Individual pins
// ***************** PROTOTYPES ******************//
void gotoSleep(void);
void exitSleep(void);
//******************* MAIN *******************//
void main(void)
{
set_tris_a(0b00110000); // RA4 / RA5 as inputs
port_a_pullups(0b00110000); // Use internal pullups on RA4 / RA5
enable_interrupts(GLOBAL); // Enable global int to allow handler calls
while(1)
{
if(!input(PIN_A4)){ // Quick n' dirty debounce
delay_ms(50);
if(!input(PIN_A4)){
delay_ms(125);
gotoSleep();
}
delay_ms(125);
}
if(!input(PIN_A5)){ // Quick n' dirty debounce
delay_ms(50);
if(!input(PIN_A5)){
delay_ms(125);
gotoSleep();
}
delay_ms(125);
}
// Proof-of-life 50Hz signal//
// means we are awake //
output_toggle(PIN_C0);
delay_ms(10);
}
}//main()
void gotoSleep(void)
{
// clear_interrupt(INT_IOC_A4_H2L); // no effect here
// clear_interrupt(INT_IOC_A5_H2L); // no effect here
// IOCAF = FALSE; // no effect here
// ext_int_edge(INT_EXT_H2L); // no effect here
ext_int_edge(H_TO_L); // Try to force interrupt on high-to-low only; no effect here ???
//ext_int_edge(L_TO_H); // surprisingly, PIC16 still wakes up out of sleep with this set to L_TO_H ???
enable_interrupts(INT_IOC_A4_H2L | INT_IOC_A5_H2L); // Enable IOC for PIN_A4 & PIN_A5; maskable, must be on a single line
sleep();
delay_cycles(1); // nop() for resume from sleep
}
void exitSleep(void)
{
disable_interrupts(INT_IOC_A4_H2L | INT_IOC_A5_H2L);
//disable_interrupts(INT_IOC_A4_H2L); // doesn't matter if one line or separate
//disable_interrupts(INT_IOC_A5_H2L); //
}
#INT_IOC
void IOC_isr(void)
{
int8 portAstate;
portAstate = input_a(); // Read to clear latches
if (interrupt_active(INT_IOC_A4_H2L))
{
clear_interrupt(INT_IOC_A4_H2L); // Clears the interrupt
//IOCAF4 = 0; // no effect here
//bit_clear(IOCAF, 4); // no effect here
IOCAF = FALSE; // no effect here
exitSleep();
}
if (interrupt_active(INT_IOC_A5_H2L))
{
clear_interrupt(INT_IOC_A5_H2L); // Clears the interrupt
//IOCAF5 = 0; // no effect here
//bit_clear(IOCAF, 5); // no effect here
IOCAF = FALSE; // no effect here
exitSleep();
}
}
|
|
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Wed Oct 18, 2017 8:35 pm |
|
|
Before we go any farther, add a 100 nF (0.1 uF) ceramic capacitor
between each of the two IOC input pins and ground. Does that fix it ? |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19552
|
|
Posted: Thu Oct 19, 2017 12:08 am |
|
|
PCM_Programmer is suspecting (as I do), that in fact you are actually getting a high to low transition.
Buttons have bounce.
Almost certainly when you release the button you actually get a little sequence low-high high_low low-high etc.. Hence the interrupt triggers. I'd actually go a little smaller than this on the capacitors, even 1nF is enough to smooth bounce on most buttons. It'll prove the point though... |
|
|
temtronic
Joined: 01 Jul 2010 Posts: 9246 Location: Greensville,Ontario
|
|
Posted: Thu Oct 19, 2017 5:00 am |
|
|
I have to agree, contact bounce. I use .68 ufd caps, aluminum electrolytics. 'Somehow' I acquired a REEL of them (5000 pcs) years ago and no matter how many I use per project, the reel still seems full, lol.
One of the most important tools you need to buy is an oscilloscope. Even if it's 30 years old, 2 channel analog, 20MHz. Being able to SEE the '1's and '0's is crucial. I've not used a PC based unit maybe cause I'm 'old school' and well, brought up on CRTs.
Jay |
|
|
apakSeO
Joined: 07 Dec 2016 Posts: 60 Location: Northeast USA
|
|
Posted: Thu Oct 19, 2017 1:02 pm |
|
|
Tried using a 0.1uF cap on RA5, no luck. Moved onto a 2.2uF cap on RA5, no luck either.
Using an Agilent MSO7000 series scope, 1GHz bandwidth, acquire mode set to High-Res & Realtime, I've taken 10 screen grabs of the button being pushed and 10 screen grabs of the button being released.
The yellow trace is RA5, probed at the uC pin. The green trace below it is the "I'm awake" signal in the while(1) loop ( it looks like diamonds because of aliasing.) It doesn't seem to matter if the rising edge is clean or dirty; the PIC16 stays asleep as long as the button is held down ( RA5 low ) which is expected, but wakes up upon button release, on RA5's rising edge, which is not desired.
From this experiment and data obtained, I cannot credit the wake-from-sleep on this PIC due to contact bounce upon button release. |
|
|
gaugeguy
Joined: 05 Apr 2011 Posts: 303
|
|
Posted: Thu Oct 19, 2017 2:01 pm |
|
|
It is possible that the compiler may be incorrectly setting the interrupt for both high and low transitions. You can look at the assembler listing to see this.
It is possible that there is something in the chip that causes a wake from sleep regardless of which edge is set.
One solution to consider would be on a switch press to go to a low power but running state and wait for the switch to be released. Once the switch is released then go to sleep. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19552
|
|
Posted: Thu Oct 19, 2017 2:50 pm |
|
|
The fact you are showing aliasing in the I'm awake signal implies you are sampling at a very slow rate. The scope bandwidth may be high but unless you have it turned up to a reasonably fast sample rate you can easily miss things. After all you are missing the transitions in the I'm awake signal....
The interrupt can trigger off fast signals.
However with the capacitors present this shouldn't be happening, and the edges look to be reasonably slowed.
EXT_INT_EDGE, has nothing to do with IOC. This only affects INT_EXT.
A quick compile shows that your compiler version is correctly setting the direction control bits:
Code: |
.................... enable_interrupts(INT_IOC_A4_H2L | INT_IOC_A5_H2L); // Enable IOC for PIN_A4 & PIN_A5; maskable, must be on a single line
*
004C: BSF 0B.3 //turn on the physical IOC interrupt
004D: MOVLW 30
004E: MOVLB 07 //turn on bits 4 & 5 in the IOCAN register
004F: IORWF 12,F
0050: MOVLW CF //Turn off the same bits in IOCAP
0051: ANDWF 11,F
|
|
|
|
apakSeO
Joined: 07 Dec 2016 Posts: 60 Location: Northeast USA
|
|
Posted: Thu Oct 19, 2017 3:21 pm |
|
|
Faster time base:
|
|
|
apakSeO
Joined: 07 Dec 2016 Posts: 60 Location: Northeast USA
|
|
Posted: Thu Oct 19, 2017 3:46 pm |
|
|
*.lst file contents match in asm, line for line
|
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Thu Oct 19, 2017 3:49 pm |
|
|
I have a 16F1503. I'll test it in hardware now. Wait a bit for me to
check it with your version and the current compiler version. |
|
|
apakSeO
Joined: 07 Dec 2016 Posts: 60 Location: Northeast USA
|
|
Posted: Thu Oct 19, 2017 3:59 pm |
|
|
Also, I found an "IOC bug" which may or may not be contributing to this behavior I am seeing. At first glance, I do not think my issue is related to it because this issue seems to affect Port B ...
http://www.xargs.com/pic/portb-change-bug.html |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Thu Oct 19, 2017 5:24 pm |
|
|
I've duplicated your symptoms. I've stripped down your program and
I still get the problem. It may take me a few hours. I've got other things
to do, so maybe later this evening I'll have an answer. |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Sun Oct 22, 2017 11:58 am |
|
|
I made a short test program and it shows the same results.
If you specify that IOC is for the negative edge only, the PIC will
wake up from sleep on either edge.
Discussion of results from running my test program:
Here I start with the button in the "up" position. I'm not touching
the button initially, so it reads as a logic 1 before going to sleep.
Then I press it, and the PIC wakes up. The button pin reads as "0" which
indicates the button is pressed down. This is all expected behaviour.
Quote: | Start
Button pin = 1 before going to sleep
Woke up. Button pin = 0
|
Next, I hold down the button down and re-start the PIC from MPLAB
(v8.92). I then release the button and the PIC wakes up. This is not
expected behavior.
Quote: | Start
Button pin = 0 before going to sleep
Woke up. Button pin = 1 |
So I think the PIC hardware must be designed to wake up from sleep on
either edge, even if only one edge is specified.
This was tested with CCS vs. 5.074 on a Microchip Low Pin Count board
running at +5v. The Sparkfun RS-232 level shifter board was used to
connect to my PC. The serial results were displayed on TeraTerm.
Sparkfun level shifter: https://www.sparkfun.com/products/449
Test program:
Code: | #include <16F1503.h>
#fuses INTRC_IO, NOWDT
#use delay(clock=4M)
#use rs232(baud=9600, xmit=PIN_C4) // Software UART
#define BUTTON_PIN PIN_A4
#byte IOCAF = getenv("SFR:IOCAF")
#bit IOCAF4 = IOCAF.4
#define clear_IOCAF4() IOCAF4 = 0
#define clear_IOCA_flags() IOCAF = 0
//---------------------------
#int_ra
void ra_isr(void)
{
clear_IOCA_flags();
}
//==========================
void main()
{
port_a_pullups(0x10); // Enable pull-up on pin A4
delay_us(10); // Allow time for weak pull-ups to get to +5v
printf("Start\n\r");
clear_interrupt(INT_RA); // Clear any existing interrupt condition
clear_IOCA_flags(); // Clear any existing "change" condition
enable_interrupts(INT_RA4_H2L); // Interrupt when button is pressed
enable_interrupts(GLOBAL);
printf("Button pin = %u before going to sleep\n\r", input(BUTTON_PIN));
sleep();
delay_cycles(1);
printf("Woke up. Button pin = %u\n\r", input(BUTTON_PIN));
while(TRUE);
} |
|
|
|
|