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

Discussion in 'Embedded Systems and Microcontrollers' started by poxkix, Dec 20, 2011.

  1. poxkix

    Thread Starter Member

    Nov 28, 2011
    42
    0
    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?
     
  2. spinnaker

    AAC Fanatic!

    Oct 29, 2009
    4,866
    988
    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.
     
  3. joeyd999

    AAC Fanatic!

    Jun 6, 2011
    2,672
    2,708
  4. Eric007

    Senior Member

    Aug 5, 2011
    1,041
    33
    Hahahah...Yes Sir!!!
     
  5. poxkix

    Thread Starter Member

    Nov 28, 2011
    42
    0
    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?
     
  6. thatoneguy

    AAC Fanatic!

    Feb 19, 2009
    6,357
    718
    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: Dec 20, 2011
  7. poxkix

    Thread Starter Member

    Nov 28, 2011
    42
    0
    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
     
  8. thatoneguy

    AAC Fanatic!

    Feb 19, 2009
    6,357
    718
    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)

    Code ( (Unknown Language)):
    1.  
    2. long oneHour=0;
    3. bit HourFlag =0;
    4.  
    5. void interrupt( void )
    6. {
    7.     if( intcon.TMR0IF ) //If interrupt was called due to timer0 Overflow
    8.     {
    9.         if( ++oneHour = 4,800,000 ) // Whatever number of interrupts makes one hour
    10.         {
    11.             [B]HourFlag=1;[/B]
    12.         }// end if
    13.        
    14.         intcon.TMR0IF = 0; //clear TMR0 overflow flag
    15.     }// end  timer interrupt
    16. } // Exit interrupt
    17.  
    18. void main(void)
    19. // Initialization
    20.     // configure Timer0    
    21.     option_reg.T0CS = 0; // use internal clock
    22.     option_reg.PSA = 0; // use prescaler form timer 0    
    23.    
    24.  
    25.     // so we get an interrupt around every 204.8us with 20MHz Clock
    26.     // set prescaller to divide by 4
    27.     //CHANGE THESE TO GET SLOWEST INTERRUPT For your clock
    28.     option_reg.PS0 = 1;
    29.     option_reg.PS1 = 0;
    30.     option_reg.PS2 = 0;
    31.            
    32.     // enable interrupts
    33.     intcon.TMR0IE = 1; //enable TMR0 overflow bit    
    34.     intcon.GIE = 1;[INDENT]while(1)
    35. {[INDENT]if (HourFlag)
    36. {
    37.   Change all the stuff I want to do each hour
    38.   [B]HourFlag=0;[/B]
    39. }// end Hour Flag
    40. [/INDENT]}// End While
    41. [/INDENT]}// End Main
    42.  
     
  9. poxkix

    Thread Starter Member

    Nov 28, 2011
    42
    0
    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: Dec 21, 2011
  10. poxkix

    Thread Starter Member

    Nov 28, 2011
    42
    0
    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:
    Code ( (Unknown Language)):
    1. char txt[7];
    2. unsigned int seconds = 0;
    3.  
    4.  
    5. void interrupt(){
    6.      if(PORTD.F0 == 1) {
    7.           PORTB.F7 = ~PORTB.F7;
    8.          
    9.           seconds++;
    10.          
    11.           if(seconds == [B]WHAT VALUE[/B]) {
    12.                if(seconds == 3600) {
    13.                     seconds=0;
    14.                     PORTB.F7 = 0x00;
    15.                }              
    16.           }
    17.  
    18.                INTCON.T0IF = 0;
    19.      }
    20. }
    21.  
    22.  
    23. void setup(){
    24.      ADCON1 = 0x0F;
    25.      TRISB = 0x00;
    26.      TRISD = 0x00;
    27.      PORTB = 0x00;
    28.      PORTD.F0 = 0x00;
    29.  
    30.      INTCON.TMR0IE   = 1; // Enables the TMR0 overflow interrupt
    31.      INTCON.GIE = 1; // Enable global interrupts
    32.      INTCON.PEIE = 1; // Enable peripheral interrupts
    33.      INTCON.T0IF = 1;
    34.      T0CON = 0x90; // binary = 0b10010000
    35. }
    36.  
    37. void main(){
    38.      setup();
    39.      
    40.      while(1) {
    41.      }
    42. }
     
  11. thatoneguy

    AAC Fanatic!

    Feb 19, 2009
    6,357
    718
    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: Dec 22, 2011
  12. joeyd999

    AAC Fanatic!

    Jun 6, 2011
    2,672
    2,708
    You'll want to check your math there, thatoneguy.
     
  13. thatoneguy

    AAC Fanatic!

    Feb 19, 2009
    6,357
    718
    You didn't read my disclaimer. :p
     
  14. poxkix

    Thread Starter Member

    Nov 28, 2011
    42
    0
    How did you do the math? In the datasheet I can't find a solution or formula in calculating the seconds that I want.
     
  15. AlexR

    Well-Known Member

    Jan 16, 2008
    735
    54
    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.
     
  16. thatoneguy

    AAC Fanatic!

    Feb 19, 2009
    6,357
    718
    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....
     
  17. AlexR

    Well-Known Member

    Jan 16, 2008
    735
    54
    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.
     
  18. poxkix

    Thread Starter Member

    Nov 28, 2011
    42
    0
    Is this correct? I just can't get it to work. The led blinks so fast:confused:
    Code ( (Unknown Language)):
    1. char txt[7];
    2. unsigned int seconds = 450;
    3. unsigned int minute = 0;
    4. unsigned int count = 0;
    5.  
    6.  
    7. void interrupt(){
    8.      if(PORTD.F0 == 1) {
    9.           PORTB.F7 = ~PORTB.F7;
    10.          
    11.           seconds--;
    12.  
    13.           if(seconds == 0) {
    14.                seconds = 450;
    15.                count++;
    16.                
    17.                if(count == 60) {
    18.                     minute++;
    19.                }
    20.           }
    21.  
    22.           if(minute == 60) { // already 1 hour
    23.                seconds = 450;
    24.                PORTB.F7 = 0x00;
    25.                INTCON.T0IF = 0;
    26.           }
    27.      }
    28. }
    29.  
    30.  
    31. void setup(){
    32.      ADCON1 = 0x0F;
    33.      TRISB = 0x00;
    34.      TRISD = 0xFF;
    35.      PORTB = 0x00;
    36.      PORTD.F0 = 0x00;
    37.      
    38.      TMR0H = 0x0BDC;
    39.      TMR0L = 0x0BDC;
    40.      
    41.      //TRISC = 0x00;
    42. //     PORTC = 0x00;
    43. //     Usart_Init(9600);
    44.  
    45.  
    46.      INTCON.TMR0IE   = 1; // Enables the TMR0 overflow interrupt
    47.      INTCON.GIE = 1; // Enable global interrupts
    48.      INTCON.PEIE = 1; // Enable peripheral interrupts
    49.      INTCON.T0IF = 1;
    50.      //T0CON = 0x90; // binary = 0b10010000
    51.      T0CON = 0x97; // binary = 0b10010111, prescaler 1:256
    52. }
    53.  
    54. void main(){
    55.      setup();
    56.      
    57.      while(1) {
    58.      }
    59. }
     
  19. AlexR

    Well-Known Member

    Jan 16, 2008
    735
    54
    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.
    Code ( (Unknown Language)):
    1.  
    2. volatile unsigned int count; //global variable count keeps track of timer0 overflows
    3.  
    4. void interrupt(){
    5.      if(INTCON.T0IF)         //if interrupt caused by Timer0
    6.      {
    7.         TMR0L = 0xDC;        // preset for Timer0 LSB register
    8.         TMR0H = 0x0B;        // preset for Timer0 MSB register
    9.         count--;            // decrement count, it was preset in t0setup()
    10.         INTCON.T0IF = 0;    // clear interrupt flag and return
    11.     }
    12. }
    13.  
    14. main(){
    15.     setup();
    16.     t0setup();
    17.  
    18.     while(1)    // loop forever
    19.     {
    20.         if(!count)    // in other word if count has reached zero
    21.         {
    22.         /* disable Timer0, and interrupts and do whatever else needs to be done once the hour is up */
    23.         }
    24.     /* and here do anything else that needs to be done or checked while the program is running*/    
    25.     }
    26. }
    27.  
    28. void setup(void){
    29. /* set up your ports here but do the Timer0 seting t0setup */
    30. }
    31.  
    32. void t0setup(void){            // this is where you set up the timer0 and enable interrupts
    33.     count = 450;            // and set the number of times it will roll over to give 1 hour
    34.     INTCON.TMR0IE   = 1;     // Enables the TMR0 overflow interrupt
    35.     INTCON.GIE = 1;         // Enable global interrupts
    36.     INTCON.PEIE = 1;         // Enable peripheral interrupts
    37.     INTCON.T0IF = 1;
    38.     T0CON = 0x97;             // binary = 0b10010111, prescaler 1:256 and activate timer    
    39.     TMR0L = 0xDC;            // preload the timer registers for an 8 second roll over
    40.     TMR0H = 0x0B;
    41. }
    42.  
     
  20. poxkix

    Thread Starter Member

    Nov 28, 2011
    42
    0
    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
     
Loading...