0
votes

I'm working with a 16F1703 PIC mcu, and I want to begin a 7segment lcd cycle (0-9) loop at a touch of a button(A1), after that if I touch the button(A1) twice, I want the Pic to enter in sleep mode.

To do that, I implemented this:

#include <test_interrupt.h>
byte const DataExit[10]={0b01000100,
                               0b01011111,  
                               0b01100010,
                               0b01001010,
                               0b01011001,
                               0b11001000,
                               0b11000000,
                               0b01011110,
                               0b01000000,
                               0b01001000};
byte const bitMask[8]={1,2,4,8,16,32,64,128};

//show seven numbers
void segmentCycle(void){
   int i, j; 
   for(i=0;i<10;i++){
         for (j=0; j<8;j++){
            output_low(CLK);
            output_bit(DATA,DataExit[i] & bitMask[j]);
            output_high(CLK);  
         }

         delay_ms(7000);
         output_low(CLR);
         delay_ms(6000);
         output_high(CLR); 
   }
}

#INT_IOC
void  IOC_isr(void) 
{
  segmentCycle(); 
  sleep(); 

}

void main()
{
   port_a_pullups(0x02);
   enable_interrupts(INT_IOC_A1);
   enable_interrupts(INT_IOC_A1_H2L);
   enable_interrupts(GLOBAL);

   while(TRUE);

}

For now, if I touch the button, sometimes it starts, otherwise It don't. What do you suggest?

I'm using ccs compiler.

2
Please take the tour, read How to Ask and minimal reproducible example. Is this a development board you are working with? Which one? Are first four lines of code in main() the only chip configuration being done? You probably don't have any debounce circuit other than a mild pull-up and I certainly don't see any button debounce code.jwdonahue
Read about the debouncing and why buttons should not be implemented in the GPIO interrupts. Use the timer instead.0___________

2 Answers

5
votes

Your code lacks a proper debounce algorithm and your circuit design is probably flawed. Hooking a button to an interrupt is a waste of a valuable resource, especially if it lacks a debounce circuit. That aside for now, your ISR is going off and doing at least 13000ms of work (well "delay")! ISR's should be short and fast. When they happen, they interrupt whatever code is running at the time, and in the absence of any hard/soft debounce mechanisms, are likely to fire many times per button press (put a scope on that button). That means you're ISR routine might be entered many times before it ever exits from the first call, but even that depends on pin configurations we can only guess at because the relevent code is missing from your OP.

Normally, you'd have a main loop that does whatever work needs to happen and the ISR's simply signal state changes via flags, counters or enumerations. When the main loop detects a state change, it calls whatever function(s) handle that change. In your case, it probably needs to check the current time and the last time the button was pressed, and verify that a minimum period has elapsed (500ms is usually good enough on pin with reasonable pull-up). If not enough time has passed, it resets the flag, otherwise it does the needed work.

See page 72 of the device spec. and take note that there are multiple interrupt sources and the ISR is responsible for determining which source caused it to fire. Your code doesn't look at the interrupt flags, nor does it clear the previous interrupt before exit, so you're never going to see more than one interrupt from any particular source.

With a little bit of searching, you should be able to find free code written to work on your specific PIC chip that handles button debounce. I recommend that you find and study that code. It will make for a very good starting point for you to learn and evolve your project from.

1
votes

Looks like your hangup is that you want the PIC to be sleeping until the button is pressed. I would do something like this, using CCS C Compiler:

#include <16f1703.h>

#use delay(int=8MHz)

#use fast_io(A)

#include <stdbool.h>
#include <stdint.h>

bool g_isr_ioc_flag = 0;
uint8_t g_isr_ioc_porta = 0;

#int_ioc
void isr_ioc(void)
{
  if (!g_isr_ioc_flag)  //only trap first IOC
  {
     g_isr_ioc_flag = 1;
     g_isr_ioc_porta = input_a();
  }
}

void main(void)
{
   uint8_t debounced_a;

   set_tris_a(2);
   port_a_pullups(0x02);
   enable_interrupts(INT_IOC_A1);
   enable_interrupts(INT_IOC_A1_H2L);
   enable_interrupts(GLOBAL);

   for(;;)
   {
      sleep();

      if (g_isr_ioc_flag)
      {
         // cheap debounce.  bit in debounced_a set if pin has been low 
         // for at least 72ms.
         delay_ms(72);
         debounced_a = ~g_isr_ioc_porta & ~input_a();
         g_isr_ioc_flag = 0;
         input_a();  // clear IOC flags left by race condition

         if (bit_test(debounced_a, 1))
         {
            // TODO: user code - handle RA1 press
         }
      }
   }
}

Normally a debounce routine would have had to poll the pin to see if it went low to start debounce timing - but in your case the interrupt on change (IOC) ISR does some of that work for us.

The CCS function input_a() automatically clears the relevant IOC flag bits in IOCAF. That means calling input_a() will clear the ISR flag until the next change.

This design lets you handle other wakeup sources in the main loop. Be aware that any other peripheral ISR or the WDT will wakeup the PIC.