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

Thread Starter

poxkix

Joined Nov 28, 2011
46
I'm using MikroC.

I'm caught in another problem. I'm creating a project that works like a charger. It should charge a device 1 hour.
My problem is how do I do that? I tried using Delay_ms, yes it delays but I can't do anything during that time not until the delay is done. My idea is during the "1hr. charge" the MCU can do other stuff.

Anyone have thoughts?
 

spinnaker

Joined Oct 29, 2009
7,830
Use an interrupt. Your interrupt probably won't be able to trigger after an hour but you might be able to do it every second.

Just set a counter. When the counter reaches the number of interrupts for one hour change the pin.
 

Thread Starter

poxkix

Joined Nov 28, 2011
46
Use an interrupt. Your interrupt probably won't be able to trigger after an hour but you might be able to do it every second.
Just set a counter. When the counter reaches the number of interrupts for one hour change the pin.
By using an interrupt will PORTB be used because all of the pins in my 18F4620 PORTB are used. And this should be done on 4 pins in random intervals. Is it possible?
 

thatoneguy

Joined Feb 19, 2009
6,359
Use a timer that you can put the x16 prescaler and x16 postscaler on, that will interrupt pretty slowly, you'd need to calculate it for your clock speed.

Then add them in the timer interrupt until the total number of interrupts x duration between timer interrupts = 1 hour.

If 1 hour has elapsed, set a flag, such as a global variable called oneHourTimer set to 1, in the main loop, test for it being set to 1, and do the rest of your stuff, when the timer is set to one (inside the interrupt), you then do whatever tasks you want to at the 1 hour mark, then reset oneHourTimer to zero, and continue with the main loop.

This will allow you to do a lot of things while timing hours out without having to pay attention to it. I also say "Set a flag" in the interrupt, such as oneHourTimer=1 to reduce time spent in the interrupt. Interrupts are valuable. Never put a delay into an interrupt, try not to call functions, just set flags and get out of the interrupt, then deal with the flags in the main loop, this prevents "missing" an intterupt. The uCs run at really fast speeds these days so even a long main loop is executed in well under 1 millisecond.

Sadly, most beginner's programs consist of tons of delay_ms() loops, and at the same time, they want to get the fastest clock speed they can, which leaves me very confused.
 
Last edited:

Thread Starter

poxkix

Joined Nov 28, 2011
46
Use a timer that you can put the x16 prescaler and x16 postscaler on, that will interrupt pretty slowly, you'd need to calculate it for your clock speed.
Then add them in the timer interrupt until the total number of interrupts x duration between timer interrupts = 1 hour.
If 1 hour has elapsed, set a flag, such as a global variable called oneHourTimer set to 1, in the main loop, test for it being set to 1, and do the rest of your stuff, when the timer is set to one (inside the interrupt), you then do whatever tasks you want to at the 1 hour mark, then reset oneHourTimer to zero, and continue with the main loop.
This will allow you to do a lot of things while timing hours out without having to pay attention to it. I also say "Set a flag" in the interrupt, such as oneHourTimer=1 to reduce time spent in the interrupt. Interrupts are valuable. Never put a delay into an interrupt, try not to call functions, just set flags and get out of the interrupt, then deal with the flags in the main loop, this prevents "missing" an intterupt. The uCs run at really fast speeds these days so even a long main loop is executed in well under 1 millisecond.
Sadly, most beginner's programs consist of tons of delay_ms() loops, and at the same time, they want to get the fastest clock speed they can, which leaves me very confused.
Thanks sir. I get the logic but my biggest problem is how do I start? Specially in the code. Another, can I use other pins to trigger the interrupt other than PORTB? I'm using 18F4620
 

thatoneguy

Joined Feb 19, 2009
6,359
Enable timer interrupts and use the timer for the interrupt source.

Set up the timer prescaler depending on which timer you use to get the longest, I think 1:128 is the longest prescaler, but if you go with a prescale and postscale both at 1:16, that gives you effectively 1:256Tosc (1/4 Freq) between interrupts.

Declare a global variable and "flag" (HourFlag) as I show below. That source is for BoostC, but you should be able to switch it easily for MikroC. There's not much difference.

I highlighted where the Hourflag is changed.

You could break it down to be a lot neater and have second flag set, then minute flag set, then after 60 minutes, set the hour flag, that way you wouldn't be dealing with huge numbers (>655536)

Rich (BB code):
long oneHour=0;
bit HourFlag =0;

void interrupt( void )
{
    if( intcon.TMR0IF ) //If interrupt was called due to timer0 Overflow
    {
        if( ++oneHour = 4,800,000 ) // Whatever number of interrupts makes one hour
        {
            HourFlag=1;
        }// end if
        
        intcon.TMR0IF = 0; //clear TMR0 overflow flag
    }// end  timer interrupt
} // Exit interrupt

void main(void)
// Initialization
    // configure Timer0    
    option_reg.T0CS = 0; // use internal clock
    option_reg.PSA = 0; // use prescaler form timer 0    
    

    // so we get an interrupt around every 204.8us with 20MHz Clock
    // set prescaller to divide by 4 
    //CHANGE THESE TO GET SLOWEST INTERRUPT For your clock
    option_reg.PS0 = 1;
    option_reg.PS1 = 0;
    option_reg.PS2 = 0;
            
    // enable interrupts
    intcon.TMR0IE = 1; //enable TMR0 overflow bit    
    intcon.GIE = 1;
while(1) {
if (HourFlag) { Change all the stuff I want to do each hour HourFlag=0; }// end Hour Flag​
}// End While​
}// End Main
 

Thread Starter

poxkix

Joined Nov 28, 2011
46
Enable timer interrupts and use the timer for the interrupt source.
Set up the timer prescaler depending on which timer you use to get the longest, I think 1:128 is the longest prescaler, but if you go with a prescale and postscale both at 1:16, that gives you effectively 1:256Tosc (1/4 Freq) between interrupts.
Declare a global variable and "flag" (HourFlag) as I show below. That source is for BoostC, but you should be able to switch it easily for MikroC. There's not much difference.
I highlighted where the Hourflag is changed.
You could break it down to be a lot neater and have second flag set, then minute flag set, then after 60 minutes, set the hour flag, that way you wouldn't be dealing with huge numbers (>655536)
Rich (BB code):
long oneHour=0;
bit HourFlag =0;

void interrupt( void )
{
    if( intcon.TMR0IF ) //If interrupt was called due to timer0 Overflow
    {
        if( ++oneHour = 4,800,000 ) // Whatever number of interrupts makes one hour
        {
            HourFlag=1;
        }// end if
        
        intcon.TMR0IF = 0; //clear TMR0 overflow flag
    }// end  timer interrupt
} // Exit interrupt

void main(void)
// Initialization
    // configure Timer0    
    option_reg.T0CS = 0; // use internal clock
    option_reg.PSA = 0; // use prescaler form timer 0    
    

    // so we get an interrupt around every 204.8us with 20MHz Clock
    // set prescaller to divide by 4 
    //CHANGE THESE TO GET SLOWEST INTERRUPT For your clock
    option_reg.PS0 = 1;
    option_reg.PS1 = 0;
    option_reg.PS2 = 0;
            
    // enable interrupts
    intcon.TMR0IE = 1; //enable TMR0 overflow bit    
    intcon.GIE = 1;
while(1) {
if (HourFlag) { Change all the stuff I want to do each hour HourFlag=0; }// end Hour Flag​
}// End While​
}// End Main
Thanks sir. I will go through the code and fit it in my own code. I'll be back for the update.

Edit:
Quick question, I have coded INTCON = 0; does this mean I can't use the code above? I used INTCON = 0 because I used PORTB for the lcd connection.
 
Last edited:

Thread Starter

poxkix

Joined Nov 28, 2011
46
uhhhhh... :confused:

My problem, I just can't get how to achieve the 1 hour delay.
During my research I found that I can do it every second, if I can just compute that 1second I can trap that if variable one_hour reaches 3600seconds it set's a pin to LOW. I'm using 8Mhz oscillator.

I came with the code:
Rich (BB code):
char txt[7];
unsigned int seconds = 0;


void interrupt(){
     if(PORTD.F0 == 1) {
          PORTB.F7 = ~PORTB.F7;
          
          seconds++;
          
          if(seconds == WHAT VALUE) {
               if(seconds == 3600) {
                    seconds=0;
                    PORTB.F7 = 0x00;
               }               
          }

               INTCON.T0IF = 0;
     }
}


void setup(){
     ADCON1 = 0x0F;
     TRISB = 0x00;
     TRISD = 0x00;
     PORTB = 0x00;
     PORTD.F0 = 0x00;

     INTCON.TMR0IE   = 1; // Enables the TMR0 overflow interrupt
     INTCON.GIE = 1; // Enable global interrupts
     INTCON.PEIE = 1; // Enable peripheral interrupts
     INTCON.T0IF = 1;
     T0CON = 0x90; // binary = 0b10010000
}

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

thatoneguy

Joined Feb 19, 2009
6,359
Try blinking an LED each time you enter the interrupt.

Put a photodiode face to face with the LED, connected with your mutimeter set to Hz, see what the frequency is (if you don't have a scope).

Otherwise, do the math in the datasheet to find out what your interrupt rate will be.

Fosc is clock/4, or the instruction clock, so if you are running at 8Mhz, with NO prescaler, timer0 would update every 0.5uS. If you set the prescaler to 16, the Timer0 would be incremented every 8uS, So the Interrupt on "Rollover" from 254 to 0 would happen every 32.875 MILLIseconds.

Try turning an LED on and off to get an idea of how your calculations are working. These were back of napkin and may be off by an order of magnitude or two.:)
 
Last edited:

Thread Starter

poxkix

Joined Nov 28, 2011
46
Try blinking an LED each time you enter the interrupt.
Put a photodiode face to face with the LED, connected with your mutimeter set to Hz, see what the frequency is (if you don't have a scope).
Otherwise, do the math in the datasheet to find out what your interrupt rate will be.
Fosc is clock/4, or the instruction clock, so if you are running at 8Mhz, with NO prescaler, timer0 would update every 0.5uS. If you set the prescaler to 16, the Timer0 would be incremented every 125mS, So the Interrupt on "Rollover" from 254 to 0 would happen every 32.875 seconds.
Try turning an LED on and off to get an idea of how your calculations are working. These were back of napkin and may be off by an order of magnitude or two.:)
How did you do the math? In the datasheet I can't find a solution or formula in calculating the seconds that I want.
 

AlexR

Joined Jan 16, 2008
732
...........................................
...................................................................................
Fosc is clock/4, or the instruction clock, so if you are running at 8Mhz, with NO prescaler, timer0 would update every 0.5uS. If you set the prescaler to 16, the Timer0 would be incremented every 125mS, So the Interrupt on "Rollover" from 254 to 0 would happen every 32.875 seconds.
................................................................................
....................
That is totally wrong!
8MHz clock = 500nSec instruction time (so far so good)
prescaler set to 16 = Timer0 increments every 8uSec!
so that if T0 is an 8 bit counter the maximum roll-over will be 32.77mSec!

All is not lost however as T0 in the PIC18 chips can be configured as either an 8 bit or a 16 bit timer/counter.
In 16 bit mode if you set the pre-scaler to 1:32 and pre-load the registers TMR0H, TMR0L with decimal 3060 (TMR0H = 0x0B and TMR0L = 0xDC), you will get an interrupt once every second.
 

thatoneguy

Joined Feb 19, 2009
6,359
Yeah, I know the math was wrong there at the end. I admitted it in the post.

Just keeping you all on your toes is all.

Stupid slide rules and remembering decimal points....
 

AlexR

Joined Jan 16, 2008
732
On further thought you could make Timer0 interrupt every 8 seconds that way you would only need 450 interrupts to time for an hour. Just set the pre-scaler to 1:256 and pre-load TRM0H, TMR0L with 3036 (0x0BDC) as before.

Also it makes more sense and produces more efficient code if you set your delay counter ("minute" variable in your case) to the maximum value (450 in this case) and use the interrupt routine to decrement the counter rather than incrementing the counter and comparing to some target value. Checking a register for zero is always quicker and more efficient than comparing it to a predetermined value.
That way all that the interrupt routine has to do is reload the TMR0L and TMR0H registers decrement the minute counter and clear the interrupt flag. All of the checking of the "minute" variable can be done in the main() function loop routine.
 

Thread Starter

poxkix

Joined Nov 28, 2011
46
On further thought you could make Timer0 interrupt every 8 seconds that way you would only need 450 interrupts to time for an hour. Just set the pre-scaler to 1:256 and pre-load TRM0H, TMR0L with 3036 (0x0BDC) as before.
Also it makes more sense and produces more efficient code if you set your delay counter ("minute" variable in your case) to the maximum value (450 in this case) and use the interrupt routine to decrement the counter rather than incrementing the counter and comparing to some target value. Checking a register for zero is always quicker and more efficient than comparing it to a predetermined value.
That way all that the interrupt routine has to do is reload the TMR0L and TMR0H registers decrement the minute counter and clear the interrupt flag. All of the checking of the "minute" variable can be done in the main() function loop routine.
Is this correct? I just can't get it to work. The led blinks so fast:confused:
Rich (BB code):
char txt[7];
unsigned int seconds = 450;
unsigned int minute = 0;
unsigned int count = 0;


void interrupt(){
     if(PORTD.F0 == 1) {
          PORTB.F7 = ~PORTB.F7;
          
          seconds--;

          if(seconds == 0) {
               seconds = 450;
               count++;
               
               if(count == 60) {
                    minute++;
               }
          }

          if(minute == 60) { // already 1 hour
               seconds = 450;
               PORTB.F7 = 0x00;
               INTCON.T0IF = 0;
          }
     }
}


void setup(){
     ADCON1 = 0x0F;
     TRISB = 0x00;
     TRISD = 0xFF;
     PORTB = 0x00;
     PORTD.F0 = 0x00;
     
     TMR0H = 0x0BDC;
     TMR0L = 0x0BDC;
     
     //TRISC = 0x00;
//     PORTC = 0x00;
//     Usart_Init(9600);


     INTCON.TMR0IE   = 1; // Enables the TMR0 overflow interrupt
     INTCON.GIE = 1; // Enable global interrupts
     INTCON.PEIE = 1; // Enable peripheral interrupts
     INTCON.T0IF = 1;
     //T0CON = 0x90; // binary = 0b10010000
     T0CON = 0x97; // binary = 0b10010111, prescaler 1:256
}

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

AlexR

Joined Jan 16, 2008
732
I'm not sure what you are doing but what I had in mind is more or less shown in the code/pseudo-code below. Hopefully it will be fairly self explanatory.
Rich (BB code):
volatile unsigned int count; //global variable count keeps track of timer0 overflows

void interrupt(){
     if(INTCON.T0IF)         //if interrupt caused by Timer0
     {
        TMR0L = 0xDC;        // preset for Timer0 LSB register
        TMR0H = 0x0B;        // preset for Timer0 MSB register
        count--;            // decrement count, it was preset in t0setup()
        INTCON.T0IF = 0;    // clear interrupt flag and return 
    }
}

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

    while(1)    // loop forever
    {
        if(!count)    // in other word if count has reached zero
        {
        /* disable Timer0, and interrupts and do whatever else needs to be done once the hour is up */
        }
    /* and here do anything else that needs to be done or checked while the program is running*/    
    }
}

void setup(void){
/* set up your ports here but do the Timer0 seting t0setup */
}

void t0setup(void){            // this is where you set up the timer0 and enable interrupts
    count = 450;            // and set the number of times it will roll over to give 1 hour
    INTCON.TMR0IE   = 1;     // Enables the TMR0 overflow interrupt
    INTCON.GIE = 1;         // Enable global interrupts
    INTCON.PEIE = 1;         // Enable peripheral interrupts
    INTCON.T0IF = 1;
    T0CON = 0x97;             // binary = 0b10010111, prescaler 1:256 and activate timer    
    TMR0L = 0xDC;            // preload the timer registers for an 8 second roll over
    TMR0H = 0x0B;
}
 

Thread Starter

poxkix

Joined Nov 28, 2011
46
I'm not sure what you are doing but what I had in mind is more or less shown in the code/pseudo-code below. Hopefully it will be fairly self explanatory.
Rich (BB code):
volatile unsigned int count; //global variable count keeps track of timer0 overflows

void interrupt(){
     if(INTCON.T0IF)         //if interrupt caused by Timer0
     {
        TMR0L = 0xDC;        // preset for Timer0 LSB register
        TMR0H = 0x0B;        // preset for Timer0 MSB register
        count--;            // decrement count, it was preset in t0setup()
        INTCON.T0IF = 0;    // clear interrupt flag and return 
    }
}

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

    while(1)    // loop forever
    {
        if(!count)    // in other word if count has reached zero
        {
        /* disable Timer0, and interrupts and do whatever else needs to be done once the hour is up */
        }
    /* and here do anything else that needs to be done or checked while the program is running*/    
    }
}

void setup(void){
/* set up your ports here but do the Timer0 seting t0setup */
}

void t0setup(void){            // this is where you set up the timer0 and enable interrupts
    count = 450;            // and set the number of times it will roll over to give 1 hour
    INTCON.TMR0IE   = 1;     // Enables the TMR0 overflow interrupt
    INTCON.GIE = 1;         // Enable global interrupts
    INTCON.PEIE = 1;         // Enable peripheral interrupts
    INTCON.T0IF = 1;
    T0CON = 0x97;             // binary = 0b10010111, prescaler 1:256 and activate timer    
    TMR0L = 0xDC;            // preload the timer registers for an 8 second roll over
    TMR0H = 0x0B;
}
Okay. I'll try this one.
What I'm trying to do is that the interrupt will only start counting if any of the 4 assigned buttons are pressed, it turns a specific pin to HIGH. After an hour the pin will go LOW
 
Top