MikroC "Hello World!" LCD example


Just an example of using MikroC to make a simple "Hello World!" PIC Microcontroller LCD project. The MCU is PIC16F877A (of course, it can be PIC16F887). The LCD display word "Hellow World!" on the first line and counting number on the second line. The image above is a screen capture of a simulation. I use the Proteus 7 VSM simulator to simulate my programs.

The source code in MikroC
//Test LCDchar *text = "Hello World!";
char mytext[3];
int i;
void main() {
// pic16f887// ANSEL = 0x00; //Digital I/O for PORTA// ANSELH = 0x00; //Digital Input for PORTB
// pic16f877A
CMCON = 0x07; //Set PORTA to Digital input
TRISB = 0; // PORTB is output
TRISA = 0x07; //PORTA as the input/output 0000 0111
i=0;
Lcd_Init(&PORTB); // Initialize LCD connected to PORTB
Lcd_Cmd(Lcd_CLEAR); // Clear display
Lcd_Cmd(Lcd_CURSOR_OFF); // Turn cursor off
Lcd_Out(1, 1, text); // Print text to LCD, 2nd row
while(1){
while(i<200){
IntToStr(i,mytext);
Lcd_Out(2,1,mytext);
Delay_ms(500);
i++;
}

i=0;
}

}

Making a Digital Clock (Updated)

My fist Microcontroller project, PIC Digital Clock, is shown below.
Prototype digital clock on Breadbord

I have added time setting feature to make it to be a usable clock. The updated version is shown below:
7-Segment Digital Clock

More features will be added in the future.
There is no fast or slow time setting as in normal/simple digital clocks. In this clock, each digit of the clock display can be set one by one via 2 setting buttons.
Features
1. Bright Led 7-Segment display without Multiplexing
2. Display: Hour, Minute, Second
3. Set time via 2 buttons
4. Set time Digit by Digit
5. Use internal oscillator of PIC16F627a or PIC16F628
6. Use 32.768KHz crystal for better clock accuracy


Setting Time:
1. The clock shows 12:34:56 when power the clock on. The first digit of hour (it's number 1 in this case) will be blinking to notify that the time is not correct and need to be set.
2. Press SET button to count up the digit.
3. Press MODE button when the digit is the correct time. The next digit will be blinking.
4. Repeat step 2 and 3 for setting minute and second.
5. Make sure that pressing MODE button for setting the second digit of second at the correct time.


Shecmatic of the Digital Clock
Schematic of a Digital Clock using PIC16F627a or PIC16F628 and Led 7-Segment with showing second

Example of a PCB design of the Digital Clock
PCB of a Digital Clock using PIC16F627a or PIC16F628 and Led 7-Segment with showing second

The firmware ( source code in C ) written in MikroC
//6 digit  clock //Using timer1 16bit counter interrupt // PIC16F627A or PIC16F628// Internal Clock 4MHz // PUNKKY@gmail.com #define MODE PORTB.F4 #define SET PORTB.F5 #define Sec_port_l PORTA.F6 #define Sec_port_h PORTA.F4 #define Min_port_l PORTA.F3 #define Min_port_h PORTA.F2 #define Hr_port_l PORTA.F1 #define Hr_port_h PORTA.F0 #define Blink PORTA.F7 #define HTMR1 0x80 #define LTMR1 0x00 typedef unsigned short uns8;
uns8 i;
uns8 hr_h;
uns8 hr_l;
uns8 min_h;
uns8 min_l;
uns8 sec_h;
uns8 sec_l;
uns8 tick;
uns8 myTimer;
uns8 setting_time;
void setup ();
void set_time ();
void show_time ();
void display (uns8 digit);
void blink_digit (uns8 digit);
void check_bt ();

//void check_bt(); //chech button void interrupt ()
{
        PIR1.TMR1IF = 0;
        // clears TMR1IF         TMR1H = HTMR1;
        tick = 1;
        Blink = 1;
        sec_l ++;
        if(sec_l>9){
            sec_l = 0;
            sec_h++;
        }
        if(sec_h>5){
            sec_h=0;
            min_l++;
        }
        if(min_l>9){
            min_l = 0;
            min_h++;
        }
        if(min_h>5){
            min_h = 0;
            hr_l++;
        }
        if(hr_l>9){
            hr_l = 0;
            hr_h++;
        }
        if(hr_h >2){
            hr_h = 0;
        }
        if(hr_h >=2 && hr_l>3){
           hr_h = 0;
           hr_l = 0;
        }
}
void main ()
{
        setup ();
        
        //Set time         hr_h = 1;
        hr_l = 2;
        min_h = 3;
        min_l = 4;
        sec_h = 5;
        sec_l = 6;
        show_time ();
        setting_time = 1;
        set_time();
        while (1)
        {
                //blink_digit();                 if (tick)
                {
                        tick = 0;
                        show_time ();
                        Delay_ms (300);
                        Blink = 0;
                }
                check_bt ();
        }
}
void setup ()
{
        tick = 0;
//Digital output on PORTA         CMCON = 0x07;
        //Input buttons + external clock         TRISB = 0xB0;

        PORTB = 0x00;
        TRISA = 0x00;
        PORTA = 0x00;
        //Internal Clock 4MHz         PCON.OSCF = 1;
        // Prescaler 1:1   external clock         T1CON = 0x0F;

        PIE1.TMR1IE = 0;  // disable interupt to stop the clock
        INTCON = 0xC0;
        // Set GIE, PEIE         TMR1L = LTMR1;
        TMR1H = HTMR1;
        // TMR1 starts at 0x0BDC = 3036 to make TMR1 counts to 62500 and         // overclows in every 0.1 sec         // Math: 1/500000*8*62500 = 0.1         // 1/5000000 : time for 20MHz crystal (internal clock will be 20/4 = 5MHz)         // 8: prescaler         // 62500: TMR1 counts to 62500         // Counting number of overflows to 10 will get 1 sec.
}

void show_time ()
{
        display (1);
        display (2);
        display (3);
        display (4);
        display (5);
        display (6);
}
void display (uns8 digit)
{
        switch (digit)
        {
                case 1 :
                PORTB = hr_h;
                Hr_port_h = 1;
                Hr_port_h = 0;
                break;
                case 2 :
                PORTB = hr_l;
                Hr_port_l = 1;
                Hr_port_l = 0;
                break;
                case 3 :
                PORTB = min_h;
                Min_port_h = 1;
                Min_port_h = 0;
                break;
                case 4 :
                PORTB = min_l;
                Min_port_l = 1;
                Min_port_l = 0;
                break;
                case 5 :
                PORTB = sec_h;
                Sec_port_h = 1;
                Sec_port_h = 0;
                break;
                case 6 :
                PORTB = sec_l;
                Sec_port_l = 1;
                Sec_port_l = 0;
                break;
        }
}
void blink_digit (uns8 digit)
{
        switch (digit)
        {
                case 1 :
                PORTB = 0xFF;
                Hr_port_h = 1;
                Hr_port_h = 0;
                Delay_ms (100);
                display (1);
                Delay_ms (100);
                break;
                case 2 :
                PORTB = 0xFF;
                Hr_port_l = 1;
                Hr_port_l = 0;
                Delay_ms (100);
                display (2);
                Delay_ms (100);
                break;
                case 3 :
                PORTB = 0xFF;
                Min_port_h = 1;
                Min_port_h = 0;
                Delay_ms (100);
                display (3);
                Delay_ms (100);
                break;
                case 4 :
                PORTB = 0xFF;
                Min_port_l = 1;
                Min_port_l = 0;
                Delay_ms (100);
                display (4);
                Delay_ms (100);
                break;
                case 5 :
                PORTB = 0xFF;
                Sec_port_h = 1;
                Sec_port_h = 0;
                Delay_ms (100);
                display (5);
                Delay_ms (100);
                break;
                case 6 :
                PORTB = 0xFF;
                Sec_port_l = 1;
                Sec_port_l = 0;
                Delay_ms (100);
                display (6);
                Delay_ms (100);
                break;
        }
}
void set_time ()
{

        i = 1;
        while (setting_time)
        {
                blink_digit (i);
                while (SET == 0)
                {
                        Delay_ms (5);
                        switch (i)
                        {
                                case 1 :
                                hr_h ++;
                                if (hr_h > 2)
                                {
                                        hr_h = 0;
                                }
                                break;
                                case 2 :
                                hr_l ++;
                                if (hr_l > 9)
                                {
                                        hr_l = 0;
                                }
                                if (hr_h >= 2 && hr_l > 3)
                                {
                                        hr_l = 0;
                                }
                                break;
                                case 3 :
                                min_h ++;
                                if (min_h > 5)
                                {
                                        min_h = 0;
                                }
                                break;
                                case 4 :
                                min_l ++;
                                if (min_l > 9)
                                {
                                        min_l = 0;
                                }
                                break;
                                case 5 :
                                sec_h ++;
                                if (sec_h > 5)
                                {
                                        sec_h = 0;
                                }
                                break;
                                case 6 :
                                sec_l ++;
                                if (sec_l > 9)
                                {
                                        sec_l = 0;
                                }
                                break;
                        }
                        while (SET == 0)
                        {
                                Delay_ms (5);
                        }
                }
                while (MODE == 0)
                {
                        Delay_ms (5);
                        i ++;
                        if (i > 6)
                        {
        sec_l--;
        TMR1H = 0x80;
        TMR1L = 0x00;
        PIE1.TMR1IE = 1;
        setting_time = 0;
                                break;
                        }
                        while (MODE == 0)
                        {
                                Delay_ms (5);
                        }
                }
        }
}
void check_bt ()
{
        myTimer = 0;
        if (setting_time == 0)
        {
                while (MODE == 0)
                {
                        Delay_ms (5);
                        myTimer ++;
                        if (myTimer > 200)
                        {
                                setting_time = 1;
                                myTimer = 0;
                                break;
                        }
                }
        }
        while (MODE == 0)
        {
                PIE1.TMR1IE = 0;
                //Stop clock                 Delay_ms (5);
                blink_digit (1);
        }
        set_time ();
}

I don't have the picture of the prototype as I haven't made it yet. But I have tested the circuit and firmware with proteus already.

6 Digits 7-Segment LED Multiplexing using a Shift Register

Multiplexing technique can reduce number of needed I/O pins of the MCU as I have explained in 'LED 7-Segment Multiplexing' and '6 Digits LED 7-Segment Multiplexing'. In those posts, I used 13 I/O pins for driving 6 digits LED 7-Segment. However, the PIC16F627A and PIC16F628 have only 15 usable I/O pins that include 2 pins for external 32.768KHz oscillator. So, there is no pin left for time setting buttons. I can change to the PIC that has more I/O pins, but I don't think it's a good solution. From my searches, I can overcome this I/O pins shortage problem by using shift register to expand the MCU I/O pins.

The concept is very similar to led dot matrix driving technique. Each digit is multiplexed via a shift register 74HC595 which is required 3 pins of the MCU. Each segment of the 7-segment display is driven by PORTA of the PIC16F628. As a result, the required pins for driving 6-Digit 7-Segment display are just 3+7 = 10 pins!. With this configuration, there are 3 I/O pins that are free for time setting buttons and driving blinking second LEDs.

I use TMR2 module for scanning digits. TMR2 is an 8-bit timer which overflows every 256 (0xFF) counts. It's known that the refresh rate above 50Hz would be enough for human's eyes to see the display without recognizing the flickering. If I set TMR2 with 1:8 Prescaler (T2CON = 0x3C), the multiplexing frequency will be 81.3Hz (4MHz/4/256/8/6 = 81.3Hz) which is enough for flicker free display.

PORTA is used to drive each segment of the 7-segment displays. However, I have to skip the RA5 as it's a MCLR pin and it can be only input pin. So, my 7-segment digit mask is different then the normal 7-segment digit mask.

my PORTA 7-segment digit mask : {0x5F, 0x06, 0x9b, 0x8f, 0xC6, 0xCd,0xDD, 0x07,
0xDf, 0xCf}
Normal 7-segment digit mask : {0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07, 0x7f, 0x6f} for number 0-9 respectively.


Below is the example schematic of 999999-second counter using the PIC16F627A or PIC16F628 and a shift register. I will not implement a clock with this configuration as I need more free MCU pins for driving Alarm buzzer and other things.

pic16F627a PIC16F628 74HC595 LED 7-segment multiplex

The source code for 999999-second counter in MikroC is listed below

//PIC16F627A or PIC16F628
//4MHz Internal OSC
//MUX by using Shift Register 595
// Critical parameters:
// Delay, Postscaler, Prescaler
// Low delay + High Prescaler
// 03/11/2008
// punkky@gmail.com
#define SH_CP PORTB.F0
#define DS PORTB.F1
#define ST_CP PORTB.F2
// 7-Segment code is skipping RA5, so the code is not as normal 7-segment coding
unsigned short number [10] = {
    0x5F0x060x9b0x8f0xC60xCd, 0xDD0x07,
    0xDf, 0xCf
};
unsigned short digit [6] = {
    000000
};
unsigned short i;
unsigned short n;
unsigned short counter;
unsigned short tick;
unsigned short shift_register;
unsigned short x1;
unsigned short x2;
unsigned short x3;
unsigned short x4;
unsigned short x5;
unsigned short x6;
void interrupt ()
{
    if (PIR1.TMR2IF)
    {
        PIR1.TMR2IF = 0;
        if (counter == 5)
        {
            //Shift data
            DS = 0;
            //Store data
            SH_CP = 1;
            SH_CP = 0;
            Delay_us (250);
            counter = 0;
        }else
        {
            //Shift data
            DS = 1;
            //Store
            SH_CP = 1;
            SH_CP = 0;
            Delay_us (250);
            counter ++;
        }
        ST_CP = 1;
        ST_CP = 0;
        PORTA = 0x00;
        PORTA = number [digit [counter]];
    }
    if (PIR1.TMR1IF)
    {
        TMR1H = 0x80;
        PIR1.TMR1IF = 0;
        tick = 1;
        x6 ++;
        if (x6 > 9)
        {
            x6 = 0;
            x5 ++;
            if (x5 > 9)
            {
                x5 = 0;
                x4 ++;
                if (x4 > 9)
                {
                    x4 = 0;
                    x3 ++;
                    if (x3 > 9)
                    {
                        x3 = 0;
                        x2 ++;
                        if (x2 > 9)
                        {
                            x2 = 0;
                            x1 ++;
                            if (x1 > 9)
                            {
                                x1 = 0;
                            }
                        }
                    }
                }
            }
        }
    }
}
void main ()
{
    //Digital I/O for PORTA
    CMCON = 0x07;
    TRISA = 0x00;
    PORTA = 0x00;
    TRISB = 0x00;
    PORTB = 0x00;
    //Internal Clock 4MHz
    PCON.OSCF = 1;
    counter = 0;
    // Set GIE, PEIE
    INTCON = 0xC0;
    //1:8 post scaler
    T2CON = 0x3C;
    // enable interupt
    PIE1.TMR2IE = 1;
    T1CON = 0x0F;
    //Initial value TMR1: 0x8000
    TMR1H = 0x80;
    TMR1L = 0x00;
    // enable interupt
    PIE1.TMR1IE = 1;
    //Data
    DS = 0;
    //Store
    SH_CP = 0;
    ST_CP = 0;
    x1 = 0;
    x2 = 0;
    x3 = 0 ;
    x4 = 0;
    x5 = 0;
    x6 = 0;
    while (1)
    {
        if (tick)
        {
            tick = 0;
            digit [0] = x1;
            digit [1] = x2;
            digit [2] = x3;
            digit [3] = x4;
            digit [4] = x5;
            digit [5] = x6;
        }
    }
}

A Simple Clock using DS1307 + PIC16F877A

Even I have posted about "DS1307 + PIC16F877A", I didn't have chance to make a real prototype of the clock. I have done only on the simulation software. Today, I have received a comment about that post. ah_bear followed my code and schematic on that post but the clock didn't work. This is because the code on that post is for reading time from DS1307 so there must be some values in the DS1307 before you can read. The solution is simple. Just place setting time codes before reading codes.

This time, I have made a real prototype to confirm that it's working. There is no setting buttons. If you want to make a real usable clock you have to implement the button interfaces (I may make one and post it here). The photo of my working prototype is featured below. Please check out my flikr at http://flickr.com/photos/punkky/ for more photos.
A simple clock using DS1307 and PIC16F877A
The schematic of the clock is very simple. Please note that the schematic does not show power supply to the PIC16F877A and the DS1307, you have to connect them by youself. If you are new to PIC/LCD interface please see MikroC "Hello World!" LCD example .
Schematic of a simple clock using DS1307 and PIC16F877A

The source code:
//Sample code for
//DS1307 RTC Interfacing with PIC16F877A
//Coded by punkky@gmail.com
//Compiler: mikroC 8.0.0
//http://picnote.blogspot.com
//05/01/2009
//Use with your own risk

unsigned short read_ds1307(unsigned short address );
void write_ds1307(unsigned short address,unsigned short w_data);
unsigned short sec;
unsigned short minute;
unsigned short hour;
unsigned short day;
unsigned short date;
unsigned short month;
unsigned short year;
unsigned short data;
char time[9];
char ddate[11];

unsigned char BCD2UpperCh(unsigned char bcd);
unsigned char BCD2LowerCh(unsigned char bcd);

void main(){

I2C_Init(100000); //DS1307 I2C is running at 100KHz
PORTB = 0;
TRISB = 0// Configure PORTB as output
TRISC = 0xFF;
Lcd_Init(&PORTB); // Initialize LCD connected to PORTB
Lcd_Cmd(Lcd_CLEAR); // Clear display
Lcd_Cmd(Lcd_CURSOR_OFF); // Turn cursor off
Lcd_Out(11"TIME:");
Lcd_Out(21"DATE:");

//Set Time
write_ds1307(0,0x80); //Reset second to 0 sec. and stop Oscillator
write_ds1307(1,0x10); //write min 27
write_ds1307(2,0x01); //write hour 14
write_ds1307(3,0x02); //write day of week 2:Monday
write_ds1307(4,0x05); // write date 17
write_ds1307(5,0x01); // write month 6 June
write_ds1307(6,0x09); // write year 8 --> 2008
write_ds1307(7,0x10); //SQWE output at 1 Hz
write_ds1307(0,0x00); //Reset second to 0 sec. and start Oscillator

while(1)
{
sec=read_ds1307(0); // read second
minute=read_ds1307(1); // read minute
hour=read_ds1307(2); // read hour
day=read_ds1307(3); // read day
date=read_ds1307(4); // read date
month=read_ds1307(5); // read month
year=read_ds1307(6); // read year

time[0] = BCD2UpperCh(hour);
time[1] = BCD2LowerCh(hour);
time[2] = ':';
time[3] = BCD2UpperCh(minute);
time[4] = BCD2LowerCh(minute);
time[5] = ':';
time[6] = BCD2UpperCh(sec);
time[7] = BCD2LowerCh(sec);
time[8] = '\0';

ddate[0] = BCD2UpperCh(date);
ddate[1] = BCD2LowerCh(date);
ddate[2] ='/';
ddate[3] = BCD2UpperCh(month);
ddate[4] = BCD2LowerCh(month);
ddate[5] ='/';
ddate[6] = '2';
ddate[7] = '0';
ddate[8] = BCD2UpperCh(year);
ddate[9] = BCD2LowerCh(year);
ddate[10] = '\0';

Lcd_Out(1,6,time);
Lcd_Out(2,6,ddate);
Delay_ms(50);
}
}

unsigned short read_ds1307(unsigned short address)
{
I2C_Start();
I2C_Wr(0xd0); //address 0x68 followed by direction bit (0 for write, 1 for read) 0x68 followed by 0 --> 0xD0
I2C_Wr(address);
I2C_Repeated_Start();
I2C_Wr(0xd1); //0x68 followed by 1 --> 0xD1
data=I2C_Rd(0);
I2C_Stop();
return(data);
}

unsigned char BCD2UpperCh(unsigned char bcd)
{
return ((bcd >> 4) + '0');
}

unsigned char BCD2LowerCh(unsigned char bcd)
{
return ((bcd & 0x0F) + '0');
}
void write_ds1307(unsigned short address,unsigned short w_data)
{
I2C_Start(); // issue I2C start signal
//address 0x68 followed by direction bit (0 for write, 1 for read) 0x68 followed by 0 --> 0xD0
I2C_Wr(0xD0); // send byte via I2C (device address + W)
I2C_Wr(address); // send byte (address of DS1307 location)
I2C_Wr(w_data); // send data (data to be written)
I2C_Stop(); // issue I2C stop signal
}

Twitter Delicious Facebook Digg Stumbleupon Favorites More

 
Design by Engr.Arif Naseem | Bloggerized by Naseem - | Affiliate Network Reviews