Help Me With MiKroC Code

Thread Starter

kirayamato_143

Joined Jan 23, 2011
59
HI can anybody help me understand some of this code which i dont get,, i qouted the part which for me needs explanation, but im still in the process of understanding all of this its for our project,,please help me

Rich (BB code):
/*
 * Project name: Programmable Digital Timer
 * Copyright:
     (c) Rajendra Bhatt, 2010.
     MCU:             PIC16F628A
     Oscillator:      XT, 4.0 MHz
*/

// LCD module connections
 sbit LCD_RS at RA0_bit;
 sbit LCD_EN at RA1_bit;
 sbit LCD_D4 at RB4_bit;
 sbit LCD_D5 at RB5_bit;
 sbit LCD_D6 at RB6_bit;
 sbit LCD_D7 at RB7_bit;
 sbit LCD_RS_Direction at TRISA0_bit;
 sbit LCD_EN_Direction at TRISA1_bit;
 sbit LCD_D4_Direction at TRISB4_bit;
 sbit LCD_D5_Direction at TRISB5_bit;
 sbit LCD_D6_Direction at TRISB6_bit;
 sbit LCD_D7_Direction at TRISB7_bit;
// End LCD module connections

 sbit MODE at RA2_bit;
 sbit SELECT at RA3_bit;
 sbit ENTER at RA4_bit;
 sbit START at RB0_bit;
 sbit RelaySW at RB3_bit;

// Define Messages
 char MSG1[] = "Device is ";
 char MSG2[] = "OFF";
 char MSG3[] = "ON ";
 char MSG4[] = "HH:MM";

 unsigned short HHMM_Pos[] = {6, 7, 8, 9, 10};
 unsigned short ON_Time[] = {0, 0, 10, 0, 0}; // 10 + 48 is :
 unsigned short OFF_Time[] = {0, 0, 10, 0, 0};

 unsigned short Mode_Select = 0 ; // 0:ON, 1:OFF
 unsigned short i, j, k, Timer_On, Get_Input, Cur_Pos, Cur_On;
 unsigned short temp, refresh, Num, HalfSec, Blink, ChangeMin=0;
 unsigned short OFF_HH, OFF_MM, ON_HH, ON_MM;

 void Disp_First_Row(){
  Lcd_Out(1,1, MSG1);
  if (RelaySW == 0) Lcd_Out(1,11, MSG2);
  if (RelaySW == 1) Lcd_Out(1,11, MSG3);
 }

 void Disp_Char(unsigned short col, unsigned short chr){
  Lcd_Chr(2, col, chr+48);
 }

" void Disp_Time(){

   for(i=0; i<5; i++){
      if(!Mode_Select){
       Lcd_Out(2,1, MSG2);
       Disp_Char(HHMM_Pos,OFF_Time);
      }
      if(Mode_Select){
       Lcd_Out(2,1, MSG3);
       Disp_Char(HHMM_Pos,ON_Time);
 "     }
   }
 }

 void play_sound(){
  Sound_Play(2500, 500);
 }

 void debounce(){
  Delay_ms(250);
 }

 void cursor_left(){
   for(j=0; j<5; j++){
      Lcd_Cmd(_LCD_MOVE_CURSOR_LEFT);
   }
 }

 void disable_timer(){
  INTCON = 0x00;
  RelaySW = 0;
  INTCON.T0IF = 0;
  Timer_On = 0;
  Blink = 0xff;
  Mode_Select = 0;
  Disp_First_Row();
  Disp_Time();
  play_sound();
 }
 

 void interrupt() {
  Num ++;           // Interrupt causes Num to be incremented by 1
  if(Num == 9) {
   HalfSec ++;      // Increase sec
   Num = 0;
   Blink = ~Blink;
   if (HalfSec == 120){
    HalfSec = 0;
    ChangeMin = 1;
   }
  }
  TMR0 = 39;        // TMR0 returns to its initial value
  INTCON.T0IF = 0;  // Bit T0IF is cleared so that the interrupt could reoccur
}

void main() {
  CMCON = 7;           // Disable Comparators
  TRISA = 0b00111100;
  TRISB = 0b00000001;
  Sound_Init(&PORTB,2); // Initialize Buzzer o/p pin
  RelaySW = 0;
  Timer_On = 0;
  Get_Input = 0;
  Cur_Pos = 0;
  Cur_On = 0;
  refresh = 0;
  Num = 0;
  HalfSec = 0;

  Lcd_Init();                // Initialize LCD
  Lcd_Cmd(_LCD_CLEAR);       // Clear display
  Lcd_Cmd(_LCD_CURSOR_OFF);  // Cursor off
  Lcd_Out(1,1, "Copyright @");
  Lcd_Out(2,1, "Embedded-Lab.com");
  i=0;
  while(i<4){
   debounce();
   i ++;
  }
  Lcd_Cmd(_LCD_CLEAR);
  Disp_First_Row();
  Lcd_Out(2,4,"-");
  Lcd_Out(2,12, MSG4);
  Disp_Time();

  do {
   if(!MODE && !Timer_On){
    debounce();
    if(!Get_Input){
     Mode_Select = ~Mode_Select;
     Disp_Time();
    }
    if(Get_Input){
     if(!Mode_Select){
      OFF_time[Cur_Pos] = OFF_time[Cur_Pos]+1;
      temp = OFF_time[Cur_Pos];
      switch (Cur_Pos){
       case 0: if(temp > 9) OFF_time[Cur_Pos]=0;
               break;
       case 1: if(temp > 9) OFF_time[Cur_Pos]=0;
               break;
       case 3: if(temp > 5) OFF_time[Cur_Pos]=0;
               break;
       case 4: if(temp > 9) OFF_time[Cur_Pos]=0;
               break;
      }
      Disp_Char(6+Cur_Pos, OFF_time[Cur_Pos]);

     }

     if(Mode_Select){
      ON_time[Cur_Pos] ++;
      temp = ON_time[Cur_Pos];
      switch(Cur_Pos){

       case 0: if(temp > 9) ON_time[Cur_Pos]=0;
               break;
       case 1: if(temp > 9) ON_time[Cur_Pos]=0;
               break;
       case 3: if(temp > 5) ON_time[Cur_Pos]=0;
               break;
       case 4: if(temp > 9) ON_time[Cur_Pos]=0;
               break;
      }
      Disp_Char(6+Cur_Pos, ON_time[Cur_Pos]);
     }
     Lcd_Cmd(_LCD_MOVE_CURSOR_LEFT);
    }
   }   // END if(!MODE)

   if(!SELECT && !Timer_On){
     debounce();
     Get_Input = 1;

     if(Cur_On) {
       Cur_Pos ++;
       if (Cur_Pos == 2) {
         Lcd_Cmd(_LCD_MOVE_CURSOR_RIGHT);
         Cur_Pos ++;
       }
       if(Cur_Pos > 4) {
         Lcd_Cmd(_LCD_MOVE_CURSOR_RIGHT);
         Cur_Pos = 0;
         cursor_left();
       }
       else Lcd_Cmd(_LCD_MOVE_CURSOR_RIGHT);
     }

     if(!Cur_On) {
       Cur_On = 1;
       cursor_left();
       Lcd_Cmd(_LCD_UNDERLINE_ON);
     }
   }

   if(!ENTER && Get_Input){
    debounce();
    Get_Input = 0;
    Cur_On = 0;
    Cur_Pos = 0;
    Disp_Time();
    Lcd_Cmd(_LCD_CURSOR_OFF);  // Cursor off
   }

   if (!START && !Get_Input){
    debounce();
    switch(Timer_On){
    case 0: play_sound();
            Timer_On = 1;
            OPTION_REG = 0x07; // Prescaler (1:256) is assigned to the timer TMR0
            TMR0 = 39;          // Timer T0 counts from 39 to 255
            INTCON = 0xA0;     // Enable interrupt TMR0 and Global Interrupts
            INTCON.T0IF = 0;
            Mode_Select = 0;
            Blink = 0;
            Disp_Time();
            break;
    case 1: disable_timer();
            break;
    }
   }

   if(Timer_On){
    OFF_HH = OFF_Time[0]*10 + OFF_Time[1];
    OFF_MM = OFF_Time[3]*10 + OFF_Time[4];
    ON_HH = ON_Time[0]*10 + ON_Time[1];
    ON_MM = ON_Time[3]*10 + ON_Time[4];
    switch(Blink){
     case 0: Lcd_Chr(2,8,' ');
             break;
     case 255: Lcd_Chr(2,8,':');
             break;
    }

    if(!OFF_HH && !OFF_MM &&!RelaySW){
     if(ON_HH || ON_MM){
      RelaySW = 1;
      Mode_Select = 0xff;
      Disp_First_Row();
      Disp_Time();
      play_sound();
     }
     else {
      disable_timer();
     }
    }

    if(!ON_HH && !ON_MM && RelaySW){
     disable_timer();
     play_sound();
    }

    if(ChangeMin) {
     switch(Mode_Select){
      case 0: if(OFF_MM == 0 && OFF_HH>0){
               OFF_MM = 59;
               OFF_HH -- ;
              }
              else if (OFF_MM >>0) OFF_MM --;
              OFF_Time[0] = OFF_HH/10;
              OFF_Time[1] = OFF_HH%10;
              OFF_Time[3] = OFF_MM/10;
              OFF_Time[4] = OFF_MM%10;
              break;

      case 255: if(ON_MM == 0 && ON_HH>0){
               ON_MM = 59;
               ON_HH -- ;
              }
              else if(ON_MM >> 0) ON_MM --;
              ON_Time[0] = ON_HH/10;
              ON_Time[1] = ON_HH%10;
              ON_Time[3] = ON_MM/10;
              ON_Time[4] = ON_MM%10;
              break;
     }
     ChangeMin = 0;
     Disp_Time();
    }

   }


  }while(1);
 }

 
Last edited by a moderator:

THE_RB

Joined Feb 11, 2008
5,438
What's the part you don't get?

The for() loop displays 5 characters to the LCD. That could be like "HH:MM" which is 5 characters.

Depending on mode_select it displays a different message (MSG2 or MSG3). It also changes between displaying the ON time or OFF time.
 

Thread Starter

kirayamato_143

Joined Jan 23, 2011
59
if
i=1
Mode_select=1
what will this output and where will it be displayed
Disp_Char(HHMM_Pos,OFF_Time);

i dont get the meaning of this arrays
unsigned short HHMM_Pos[] = {6, 7, 8, 9, 10};
unsigned short ON_Time[] = {0, 0, 10, 0, 0}; // 10 + 48 is :
unsigned short OFF_Time[] = {0, 0, 10, 0, 0};
 

THE_RB

Joined Feb 11, 2008
5,438
The function disp_char() is shown in your code;
Rich (BB code):
void Disp_Char(unsigned short col, unsigned short chr){
  Lcd_Chr(2, col, chr+48);
 }
It accepts 2 arguments; column position and character

So,
Disp_Char(HHMM_Pos,OFF_Time);

gets the column position from HHMM_Pos[] and the character to display from OFF_Time[]
 

thatoneguy

Joined Feb 19, 2009
6,359
Looking at the inputs at the top of the code, it appears to be a settable timer with display.

The code is a good example of "why comments are good", but the variable names make it readable enough.

unsigned short HHMM_Pos[] = {6, 7, 8, 9, 10};

Means display HH:MM at roughly the center positions of a 16 character LCD, is that line you were asking about?

The other arrays are simply default values before the user presses any buttons. From the "Usage" section of the article above:


Operation of the timer
The timer gets inputs from the 4 push buttons. Their functions are described as follows:

  • ON/OFF TIME : This timer device allows you to set both on and off time. When the timer is initially powered on, the device is in off condition and both on and off times are 0. Pressing this button, you can switch between the on and off time on the display.
  • SELECT : This allows you to select between the on and off time settings as well as hour and minute digits. The selected digit is incremented by pressing the ON/OFF TIME button.
  • ENTER : When the appropriate hour and minutes are selected, pressing ENTER finalize the corresponding on or off time.
  • START/STOP is to start or stop the timer. If the timer is already on, you can stop it at anytime during its operation by pressing this button.
 

Thread Starter

kirayamato_143

Joined Jan 23, 2011
59
Looking at the inputs at the top of the code, it appears to be a settable timer with display.

The code is a good example of "why comments are good", but the variable names make it readable enough.

unsigned short HHMM_Pos[] = {6, 7, 8, 9, 10};

Means display HH:MM at roughly the center positions of a 16 character LCD, is that line you were asking about?

The other arrays are simply default values before the user presses any buttons. From the "Usage" section of the article above:
You say the other arrays are default values, does it means that it has just to be declared or else the program wont work? sorry i didnt get it why it has to be like that , you may forward me to some site if there is a similar example or please help me lighten up,,but i understand the HH:MM tnx,
 

thatoneguy

Joined Feb 19, 2009
6,359
He is setting the default time to 00:00 at the same time as declaring the variable, it saves lines, and helps make the code a bit more readable.

If you look at the display function, it goes to the position, then displays the value of the on array, which are zeroes to start until adjusted later in the program.

Have you compiled the program and gotten it to work in the simulator? You can change the numbers and see how it changes the displayed values.
 

Thread Starter

kirayamato_143

Joined Jan 23, 2011
59
He is setting the default time to 00:00 at the same time as declaring the variable, it saves lines, and helps make the code a bit more readable.

If you look at the display function, it goes to the position, then displays the value of the on array, which are zeroes to start until adjusted later in the program.

Have you compiled the program and gotten it to work in the simulator? You can change the numbers and see how it changes the displayed values.
thanks sir your advice really works now i'm stuck in understanding this code.. please help
void interrupt() {
Num ++; // Interrupt causes Num to be incremented by 1
if(Num == 9) {
HalfSec ++; // Increase sec
Num = 0;
Blink = ~Blink;
if (HalfSec == 120){
HalfSec = 0;
ChangeMin = 1;
}
}
TMR0 = 39; // TMR0 returns to its initial value
INTCON.T0IF = 0; // Bit T0IF is cleared so that the interrupt could reoccur
}
 

thatoneguy

Joined Feb 19, 2009
6,359
thanks sir your advice really works now i'm stuck in understanding this code.. please help
void interrupt() {
Num ++; // Interrupt causes Num to be incremented by 1
if(Num == 9) {
HalfSec ++; // Increase sec
Num = 0;
Blink = ~Blink;
if (HalfSec == 120){
HalfSec = 0;
ChangeMin = 1;
}
}
TMR0 = 39; // TMR0 returns to its initial value
INTCON.T0IF = 0; // Bit T0IF is cleared so that the interrupt could reoccur
}
He set Timer 0 up to interrupt often, so to get accurate time, he loads a "pre-count" of 39 in (interrupt occurs when timer counts to 255), The prescaler for Timer0 was set to divide the clock down to interrupt roughly every half second, and the preloading of 39 in the Timer0 counter fine tunes that to be very close to 1/2 second.

If the interrupt has been called (from Timer 0 overlow) 9 times, 1/2 second has elapsed, so he increments it, resets the Num counter to zero, and toggles the state of the Blink output, so that something blinks on for 1 second, off for 1 second, probably the ":" in HH:MM
He then tests if Num (half seconds) = 120, if so, that means 60 full seconds have elapsed. If so he sets the variable ChangeMin to 1 to alert the main loop test of the ChangeMin variable that the minute needs to be incremented, and zeros the counters used to determine minutes.
Finally, he re-enables Timer Interrupts, they are automatically disabled when the interrupt occurs by setting the flag, to re-enable the Timer 0 interrupt, the flag bit for timer 0 needs to be cleared.

Setting flags like ChangeMin to 1 in is very common for interrupt routines. Unless the project is extremely straightforward, you never want to use a delay or long function inside the interrupt code, unless you really know exactly what you are doing.

Since timer0 interrupts are disabled when the interrupt routine is entered, and they are the only interrupt enabled from initialization code is Timer0, a few interrupts would be missed while processing a few millisecond delay, for example.

Interrupts are the best tool to ensure things happen when you want them to, and to alert the code of just about anything (UART data received, button pressed, CCP even occured, etc). Interrupts eliminate running in an endless loop with a bunch of delays between polling the conditions you are looking for.

The downside to interrupts on the 16F series is there is only one interrupt routine, so if you are using more than one interrupt source, say 2 timers and the UART, you need to test which event triggered the interrupt and process it QUICKLY. This typically means setting a flag that is checked in the main loop, which still runs extremely fast, plenty of speed in the main loop to fully service/debounce button presses, UART, etc. A good example is the ChangeMin flag in the interrupt routine above.

The general idea is that the interrupt is serviced re-enabled as fast as possible due to the multiple sources of interrupts. This is the situation where tight code in the interrupt routine really matters. For the example above, some dawdling could be forgiven, but at the risk of losing a few half seconds.

The PIC18 series allows for prioritized interrupts (high and low priority), but that only means that a high priority interrupt can interrupt the low priority interrupt. This is useful if time critical code must be done inside the high priority interrupt routine, say to respond to Ethernet or USB, and the low priority interrupt is changing PWM duty cycle or "not as critical but important" task. Though most hobbyist PIC18 programs use only the single interrupt for simplicity, and a bit of compatibility to port the code to a PIC16 device.

Let me know if all of that makes sense to you. It appears I got a little verbose, as I am unaware of your level of knowledge.
 
Last edited:
Top