How do I make a specific MCU pin HIGH for 1hr.

AlexR

Joined Jan 16, 2008
735
Ok I sort of see what you are doing.
While you are testing your program it might help to add a routine to toggle a led to the interrupt function. If everything is working correctly the interrupt should occur every 8 seconds.
Also when you declare the global variable count you should initialise it to some non-zero value to prevent the program going into the
"if(!count)" statement. Also for the same reason when you turn off the counter after the 1hour time-out you should reload the count variable to some non-zero value.
Once you are satisfied that the timer works you can put shift the call to t0setup(); into your button finding routines and put them in the while(1) loop.
 

Thread Starter

poxkix

Joined Nov 28, 2011
42
Ok I sort of see what you are doing.
While you are testing your program it might help to add a routine to toggle a led to the interrupt function. If everything is working correctly the interrupt should occur every 8 seconds.
Also when you declare the global variable count you should initialise it to some non-zero value to prevent the program going into the
"if(!count)" statement. Also for the same reason when you turn off the counter after the 1hour time-out you should reload the count variable to some non-zero value.
Once you are satisfied that the timer works you can put shift the call to t0setup(); into your button finding routines and put them in the while(1) loop.
The code you gave me works perfectly though the 1hr. is timed from the compiler run-time not the 1hr. I real life.

First I tried it with just 1 trigger/button. It works. I added 3 more because I need 4. It only lights up 1 led depending which button was pressed if I will press a second button it will not light up.

This is what I came up:
Sorry for being such a lowbie
Rich (BB code):
unsigned int timer_1, timer_2, timer_3, timer_4;

void interrupt(){
if(PORTD.F0 == 1) {
if(INTCON.T0IF)
     {
        PORTB.F7 = 1;
        TMR0L = 0xDC;
        TMR0H = 0x0B;

        timer_1--;
        INTCON.T0IF = 0;
    }
}

if(PORTD.F1 == 1) {
if(INTCON.T0IF)
     {
        PORTB.F6 = 1;
        TMR0L = 0xDC;
        TMR0H = 0x0B;

        timer_2--;
        INTCON.T0IF = 0;
    }
}

if(PORTD.F2 == 1) {
if(INTCON.T0IF)
     {
        PORTB.F5 = 1;
        TMR0L = 0xDC;
        TMR0H = 0x0B;

        timer_3--;
        INTCON.T0IF = 0;
    }
}

if(PORTD.F3 == 1) {
if(INTCON.T0IF)
     {
        PORTB.F4 = 1;
        TMR0L = 0xDC;
        TMR0H = 0x0B;
        timer_4--;
        INTCON.T0IF = 0;
    }
}
}

void setup();
void t0setup();

void main(){
    setup();
    t0setup();

    if(PORTD.F0 == 1) {
            while(1)
        {
        if(!timer_1)
        {
        PORTB.F7 = 0;
        INTCON.TMR0IE   = 0;
        }
        }
    }
    
        if(PORTD.F1 == 1) {
            while(1)
        {
        if(!timer_2)
        {
        PORTB.F6 = 0;
        INTCON.TMR0IE   = 0;
        }
        }
    }
    
        if(PORTD.F2 == 1) {
            while(1)
        {
        if(!timer_3)
        {
        PORTB.F5 = 0;
        INTCON.TMR0IE   = 0;
        }
        }
    }
    
        if(PORTD.F3 == 1) {
            while(1)
        {
        if(!timer_4)
        {
        PORTB.F4 = 0;
        INTCON.TMR0IE   = 0;
        }
        }
    }
}

void setup(void){
     ADCON1 = 0x0F;
     TRISB = 0x00;
     TRISD = 0xFF;
     PORTB = 0x00;    
}

void t0setup(void){
    timer_1 = 450;
    timer_2 = 450;
    timer_3 = 450;
    timer_4 = 450;
    INTCON.TMR0IE   = 1;
    INTCON.GIE = 1;
    INTCON.PEIE = 1;
    INTCON.T0IF = 1;
    T0CON = 0x97;
    TMR0L = 0xDC;
    TMR0H = 0x0B;
}
 

MMcLaren

Joined Feb 14, 2010
842
Here's a fun example that might be worth studying. It uses standard parallel switch state logic with a twist. That is, it supports switches and LEDs on the same pins. The secret (with active low switches) is to never set an output high (to avoid shorts) and manage LEDs via the TRIS bits.

The rest of the program is pretty straight forward and based around a 25 msec "debounce" loop interval. With all due respect to Joey's "never-never rules", using a delay is perfectly acceptable when all program input/output events, including switch events, are in the time domain. But, using one of the timers to derive the 25 msec interval would provide more precise timing.

Food for thought...

Happy Holidays everyone...

Cheerful regards, Mike



Rich (BB code):
;
;  #pragma CLOCK_FREQ 8000000
;
;  unsigned char swnew = 0;
;  unsigned char swold = 0;
;  unsigned char flags = 0;
;
;  unsigned long sw0tmr = 144000;
;  unsigned long sw1tmr = 144000;
;  unsigned long sw2tmr = 144000;
;  unsigned long sw3tmr = 144000;
;
;  void main()                  //
;  { ansel = 0;                 //
;    trisb = 0;                 // port b all outputs
;    portb = 0;                 //
;
;    while(1)
;    { delay_ms(25);            // 25 msec debounce intervals
;  /*
;   *  swnew  __---____-----___    sample active lo switches
;   *  swold  ___---____-----__    switch state latch
;   *  delta  __-__-___-____-__    changes, press or release
;   *  newhi  __-______-_______    filter out 'release' bits
;   *  flags  ___-------_____--    toggle flag bits for main
;   *  trisb  ---_______-----__    toggle tris bits for LEDs
;   */
;      trisb |= 0b00001111;     // set RB0-RB3 pins to input
;      swnew = ~portb;          // sample active lo switches
;      swnew &= 0b00001111;     // the RB0-RB3 pins only
;      swnew ^= swold;          // changes, press or release
;      swold ^= swnew;          // update switch state latch
;      swnew &= swold;          // filter out 'release' bits
;      flags |= swnew;          // flag the 'new press' bits
;      portb &= 0b11110000;     // keep port latch bits = '0'
;      trisb ^= flags;          // light only active sw LEDs
;
;      if(flags.0)              // if SW0/LED0 (RB0) "on"
;      { if(--sw0tmr == 0)      // dec timer, if timed out
;        { flags.0 = 0;         // turn SW0/LED0 timer "off"
;          sw0tmr = 144000;     // reset 1 hr SW0/LED0 timer
;        }                      //
;      }                        //
;      if(flags.1)              // if SW1/LED1 (RB1) "on"
;      { if(--sw1tmr == 0)      // dec timer, if timed out
;        { flags.1 = 0;         // turn SW1/LED1 timer "off"
;          sw1tmr = 144000;     // reset 1 hr SW1/LED1 timer
;        }                      //
;      }                        //
;      if(flags.2)              // if SW2/LED2 (RB2) "on"
;      { if(--sw2tmr == 0)      // dec timer, if timed out
;        { flags.2 = 0;         // turn SW2/LED2 timer "off"
;          sw2tmr = 144000;     // reset 1 hr SW2/LED2 timer
;        }                      //
;      }                        //
;      if(flags.3)              // if SW3/LED3 (RB3) "on"
;      { if(--sw3tmr == 0)      // dec timer, if timed out
;        { flags.3 = 0;         // turn SW3/LED3 timer "off"
;          sw3tmr = 144000;     // reset 1 hr SW3/LED3 timer
;        }                      //
;      }                        //
;    }                          //
;  }                            // end program
;
 

Attachments

Last edited:

Thread Starter

poxkix

Joined Nov 28, 2011
42
Here's a fun example that might be worth studying. It uses standard parallel switch state logic with a twist. That is, it supports switches and LEDs on the same pins. The secret (with active low switches) is to never set an output high (to avoid shorts) and manage LEDs via the TRIS bits.
The rest of the program is pretty straight forward and based around a 25 msec "debounce" loop interval. With all due respect to Joey's "never-never rules", using a delay is perfectly acceptable when all program input/output events, including switch events, are in the time domain. But, using one of the timers to derive the 25 msec interval would provide more precise timing.
Food for thought...
Happy Holidays everyone...
Cheerful regards, Mike
Rich (BB code):
;
;  #pragma CLOCK_FREQ 8000000
;
;  unsigned char swnew = 0;
;  unsigned char swold = 0;
;  unsigned char flags = 0;
;
;  unsigned long sw0tmr = 144000;
;  unsigned long sw1tmr = 144000;
;  unsigned long sw2tmr = 144000;
;  unsigned long sw3tmr = 144000;
;
;  void main()                  //
;  { ansel = 0;                 //
;    trisb = 0;                 // port b all outputs
;    portb = 0;                 //
;
;    while(1)
;    { delay_ms(25);            // 25 msec debounce intervals
;  /*
;   *  swnew  __---____-----___    sample active lo switches
;   *  swold  ___---____-----__    switch state latch
;   *  delta  __-__-___-____-__    changes, press or release
;   *  newhi  __-______-_______    filter out 'release' bits
;   *  flags  ___-------_____--    toggle flag bits for main
;   *  trisb  ---_______-----__    toggle tris bits for LEDs
;   */
;      trisb |= 0b00001111;     // set RB0-RB3 pins to input
;      swnew = ~portb;          // sample active lo switches
;      swnew &= 0b00001111;     // the RB0-RB3 pins only
;      swnew ^= swold;          // changes, press or release
;      swold ^= swnew;          // update switch state latch
;      swnew &= swold;          // filter out 'release' bits
;      flags |= swnew;          // flag the 'new press' bits
;      portb &= 0b11110000;     // keep port latch bits = '0'
;      trisb ^= flags;          // light only active sw LEDs
;
;      if(flags.0)              // if SW0/LED0 (RB0) "on"
;      { if(--sw0tmr == 0)      // dec timer, if timed out
;        { flags.0 = 0;         // turn SW0/LED0 timer "off"
;          sw0tmr = 144000;     // reset 1 hr SW0/LED0 timer
;        }                      //
;      }                        //
;      if(flags.1)              // if SW1/LED1 (RB1) "on"
;      { if(--sw1tmr == 0)      // dec timer, if timed out
;        { flags.1 = 0;         // turn SW1/LED1 timer "off"
;          sw1tmr = 144000;     // reset 1 hr SW1/LED1 timer
;        }                      //
;      }                        //
;      if(flags.2)              // if SW2/LED2 (RB2) "on"
;      { if(--sw2tmr == 0)      // dec timer, if timed out
;        { flags.2 = 0;         // turn SW2/LED2 timer "off"
;          sw2tmr = 144000;     // reset 1 hr SW2/LED2 timer
;        }                      //
;      }                        //
;      if(flags.3)              // if SW3/LED3 (RB3) "on"
;      { if(--sw3tmr == 0)      // dec timer, if timed out
;        { flags.3 = 0;         // turn SW3/LED3 timer "off"
;          sw3tmr = 144000;     // reset 1 hr SW3/LED3 timer
;        }                      //
;      }                        //
;    }                          //
;  }                            // end program
;
Thanks for this sir. But it is still the same, only the first led will light up. The other leds will not if I press another button. Is there another way?

My code:
Rich (BB code):
unsigned int timer1 = 450, timer2 = 450, timer3 = 450, timer4 = 450;

void interrupt() {
     if(PORTB.F0 == 1) {
              if(INTCON.T0IF) {
               PORTB.F4 = 1;
               TMR0L = 0xDC;
               TMR0H = 0x0B;
               timer1--;
               INTCON.T0IF = 0;
          }
     }
     
     if(PORTB.F1 == 1) {
              if(INTCON.T0IF) {
               PORTB.F5 = 1;
               TMR0L = 0xDC;
               TMR0H = 0x0B;
               timer2--;
               INTCON.T0IF = 0;
          }
     }
}


void setup(void);


void main() {
     setup();

     if(1) {
          while(PORTB.F0 != 0) {
               if(!timer1) {
                    PORTB.F4 = 0;
                    INTCON.TMR0IE = 0;
               }
          }
     }
     
     if(PORTB.F1 == 1) {
          while(1) {
               if(!timer2) {
                    PORTB.F5 = 0;
                    INTCON.TMR0IE = 0;
               }
          }
     }
}

//methods
void setup(void){
     ADCON1 = 0x0F;
     TRISB = 0b00001111;
     PORTB = 0x00;
     INTCON.TMR0IE = 1;
     INTCON.GIE = 1;
     INTCON.PEIE = 1;
     INTCON.T0IF = 1;
     T0CON = 0x97;
     TMR0L = 0xDC;
     TMR0H = 0x0B;
}
 

Thread Starter

poxkix

Joined Nov 28, 2011
42
Ok I sort of see what you are doing.
While you are testing your program it might help to add a routine to toggle a led to the interrupt function. If everything is working correctly the interrupt should occur every 8 seconds.
Also when you declare the global variable count you should initialise it to some non-zero value to prevent the program going into the
"if(!count)" statement. Also for the same reason when you turn off the counter after the 1hour time-out you should reload the count variable to some non-zero value.
Once you are satisfied that the timer works you can put shift the call to t0setup(); into your button finding routines and put them in the while(1) loop.
Sir, won't you mind if I ask on some key points or explanation about these codes:

What are these registers and why are they initiated with these values
TMR0L = 0xDC;
TMR0H = 0x0B;

Especially the prescaler, what is it's purpose?

Is there a formula in getting the 1hr. delay? I think a time will come that I too will need to explain these stuff but I just don't know how it actually work.:(

By the way, the code you gave works well though I'm still having problems adding up more buttons.
 

thatoneguy

Joined Feb 19, 2009
6,349
You have one of the advanced PICs, you need to disable some stuff.
(disable comparators)
Rich (BB code):
PORTB ; Initialize PORTB by
; clearing output
; data latches
CLRF LATB ; Alternate method
; to clear output
; data latches
MOVLW 0Fh ; Set RB<4:0> as
MOVWF ADCON1 ; digital I/O pins
; (required if config bit
; PBADEN is set)
MOVLW 0CFh ; Value used to
; initialize data
; direction
MOVWF TRISB ; Set RB<3:0> as inputs
; RB<5:4> as outputs
; RB<7:6> as inputs
 

Thread Starter

poxkix

Joined Nov 28, 2011
42
You have one of the advanced PICs, you need to disable some stuff.
(disable comparators)
Rich (BB code):
PORTB ; Initialize PORTB by
; clearing output
; data latches
CLRF LATB ; Alternate method
; to clear output
; data latches
MOVLW 0Fh ; Set RB<4:0> as
MOVWF ADCON1 ; digital I/O pins
; (required if config bit
; PBADEN is set)
MOVLW 0CFh ; Value used to
; initialize data
; direction
MOVWF TRISB ; Set RB<3:0> as inputs
; RB<5:4> as outputs
; RB<7:6> as inputs
Yes, I have done that already.
I added
ADCON1 = 0x0F;
INTCON = 0x00;

Disabled PBADEN and debug.
Nothing works.
 

thatoneguy

Joined Feb 19, 2009
6,349
Can you temporarily move the buttons to port A to see if it works there (change code accordingly), it may be a different peripheral interfering.

With PIC Mid-Range Devices, ALL analog peripherals are enabled at power on. I'm not sure why they decided to do it that way, but it's been confusing people since about the 16F877 was released and became comon and CMCON=0x07 had to be set.

Now there's even more peripherals packed onto these chips (which is awesome), but you need to look through each peripheral and see what pins it uses, and if it blocks changing the TRIS by default or something.

You stated you set up the code for 4 pins and 4 LEDs, are 3 of the 4 working, or just 1 of them working? Which one(s) aren't working, and what port.pin is the working one connected to?

I found this:
The interrupt-on-change feature is recommended for
wake-up on key depression operation and operations
where PORTB is only used for the interrupt-on-change
feature. Polling of PORTB is not recommended while
using the interrupt-on-change feature.

RB3 can be configured by the configuration
Unsure why polling isn't recommended when interrupts are enabled, but not sure if it applies to your case or not.
 
Last edited:

Thread Starter

poxkix

Joined Nov 28, 2011
42
Can you temporarily move the buttons to port A to see if it works there (change code accordingly), it may be a different peripheral interfering.
With PIC Mid-Range Devices, ALL analog peripherals are enabled at power on. I'm not sure why they decided to do it that way, but it's been confusing people since about the 16F877 was released and became comon and CMCON=0x07 had to be set.
Now there's even more peripherals packed onto these chips (which is awesome), but you need to look through each peripheral and see what pins it uses, and if it blocks changing the TRIS by default or something.
You stated you set up the code for 4 pins and 4 LEDs, are 3 of the 4 working, or just 1 of them working? Which one(s) aren't working, and what port.pin is the working one connected to?
I found this:
Unsure why polling isn't recommended when interrupts are enabled, but not sure if it applies to your case or not.
Transferred and still the same. Can interrupt be used more than once?

Changed the ports, still the same.

It is like this, 4 buttons right? If I press button 1 it starts to blink led 1 with 1 hour counting. Now if I press any other button to start counting another 1 hour nothing happens. It is like only once at a time
 

thatoneguy

Joined Feb 19, 2009
6,349
If you press button 2 first, does it start timing?

If so, reset, test button 3

if so, reset, test button 4

report back with results.
 

AlexR

Joined Jan 16, 2008
735
Sir, won't you mind if I ask on some key points or explanation about these codes:

What are these registers and why are they initiated with these values
TMR0L = 0xDC;
TMR0H = 0x0B;

Especially the prescaler, what is it's purpose?

Is there a formula in getting the 1hr. delay? I think a time will come that I too will need to explain these stuff but I just don't know how it actually work.:(

By the way, the code you gave works well though I'm still having problems adding up more buttons.
Right, I see that a quick lesson in timers is called for.

The way that the timer works is that a pulse on the timer input causes the timer to increment its timer register. When the register rolls over from its maximum count to zero the relevant timer flag is set and if interrups are enabled then an interrupt also occurs.

The timer input can either be an external source or the PIC's system clock. If the system clock is used then frequency that is fed to the timer is Fosc/4 that is 1/4 of the actual crystal frequency.

As we are using the system clock and our oscillator is running at 8MHz so the timer input frequency is 2MHz, or in other words we are sending the timer one pulse every 0.5uSec.

A useful feature of Timer0 in the PIC18 chips is that it supports a pre-scaler. Pre-scaler is just a fancy name for a divider that goes between the timer input and the circuit that increments the timer register.

With the pre-scaler set to 256 the timer will increment its register every 128uSec (0.5uSec X 256)

Timer0 in the PIC18 series of chips can be configured as either an 8 bit timer or a 16 bit timer.

Since we need a long delay we are using it as a 16 bit timer and left to its own devices a 16 bit timer running at 128uSec will roll over every 8.388608 seconds (2^16 = 65536, 65536 X 128uSec = 8.388608 seconds).

We however would like the timer to take an even number of seconds to roll over so we per-load the timer register with a value that will cause it to roll over in even number of seconds. If we choose to make the timer roll over and hence interrupt every 8 seconds then the timer will have to count 62,500 times for each roll over.
Count = DelayTime/CounterPeriod = 8Sec/128uSec = 62500.

Free running the timer would have a count 65536 (2^16), we need its count to be 62500 so we pre-load the timer register with 3060 (65536 - 62500 = 3036). We do this when we initially set up the timer and we do this each time the timer overflows (timer rolls over from a maximum count of 0xffff to 0x0000) and causes an interrupt. That is what the instructions TMR0L = 0xDC; and TMR0H = 0x0B; do. TMR0L is the low byte of the Timer0 register and TMR0H is the high byte and you are pre-loading the timer register with 3036 (0x0BDC).
 

Thread Starter

poxkix

Joined Nov 28, 2011
42
If you press button 2 first, does it start timing?
If so, reset, test button 3
if so, reset, test button 4
report back with results.
Yes, it starts timing. Tried it with the other buttons it works. I'm still stuck here. I'm working on another solution right now I hope this work.

More help and solutions would be greatly appreciated.
 

MrChips

Joined Oct 2, 2009
19,280
You know the old fish story?

Give a man fish and he feeds for a day. Teach a man how to fish and he feeds for a lifetime.
 

Thread Starter

poxkix

Joined Nov 28, 2011
42
Please post the latest revision of your source code again, I'm not sure what all has changed since the OP.
I can now light 2 leds. With much set backs. If I hold down button1, led1 starts lighting. If I hold down button2 while led1 is still turned on, led2 will light up after a few seconds of delay. And it seems it goes on an endless loop.
Here is my revision:
Rich (BB code):
unsigned int timer_1, timer_2;
unsigned short flag_1;

void interrupt() {
if(INTCON.T0IF) {
     if(PORTC.F0 == 1) {
          PORTC.F4 = 1;
          TMR0L = 0xDC;
          TMR0H = 0x0B;
          timer_1--;
          INTCON.T0IF = 0;

          if(timer_1 == 0) {
               PORTC.F4 = 0;
          }
     }
     
     if(PORTC.F1 == 1) {
          PORTC.F5 = 1;
          TMR0L = 0xDC;
          TMR0H = 0x0B;
          timer_2--;
          INTCON.T0IF = 0;

          if(timer_2 == 0) {
               PORTC.F5 = 0;
          }
     }
}
}


void setup(void);
void t0setup(void);


void main() {
     setup();
     t0setup();
     
     while(1) {
     }
}

//methods
void setup(){
     ADCON1 = 0x0F;
     INTCON = 0x00;
     TRISC = 0b00001111;
     PORTC = 0x00;
}

void t0setup(){
    //timer_1 = 450;
    timer_1 = 3;
    timer_2 = 3;
    INTCON.TMR0IE = 1;
    INTCON.GIE = 1;
    INTCON.PEIE = 1;
    INTCON.T0IF = 1;
    T0CON = 0x97;
    TMR0L = 0xDC;
    TMR0H = 0x0B;
}
 

Attachments

thatoneguy

Joined Feb 19, 2009
6,349
You are doing too much work in the interrupt routine, IMO.

Only Timer0 is creating an interrupt

So when you press a button, you need to hold it down until that interrupt is entered, at which point the state of the switch is checked.

You need to have 4 variables to keep track of time, 1 for each button/LED combination.

You then add the duration of the timer0 overflows as counts to your (active) variables, comparing each to see if an hour has passed.

The main loop code should check for button press and debounce it, turn on the LED (which is also a flag meaning timer is active).

In interrupt routine, simply update the timers

Back in the main routine, continually check buttons to see if one was pressed, if it was already active, ignore (or reset to 0, depending on behavior), or start using another counter variable and light up that led.

Dirty Pseudocode (i'm tired)

interrupt()
if (timer interrupt)
if (LED1) delay1 ++;
if (LED2) delay2 ++;
if (LED3) delay3++;
if (LED4) delay4 ++;
reset interrupts

main()
check for button 1
debounce(button1) // ONLY IF BOUNCE IS AN ISSUE, OR PRESSING BUTTON AGAIN EXTENDS TIMER
if button LED1=1; //LED1 =1 will turn on LED, AND tell interrupt to start counting for this timer.
check button 2
debounce(button2)
if button2 LED2=1;
...

if delay1 == 1hr
{turn off LED}

That's sloppy pseudo code, and you'll need to do the math for timer counts.

That's roughly how I would do it, though I'd make button a pointer array to buttons, and counter unsigned array as well.

How big will counter need to be to time an hour? What can you do to reduce that number so the code runs faster?
 
Top