Bài tập: PC Terminal - UART - DS1307

2death

Cố Vấn CLB
Staff member
<Bài tập này của lớp C3, tháng 05/2011, tuy nhiên có nhiều trao đổi có giá trị và có phần sửa bài tập, có thể làm code mẫu để tham khảo, do đó được chuyển sang mục "Tài liệu học tập PIC16F887>

Nội dung bài tập được cho ngày 07/05/2011 như sau:

- Set thời gian cho đồng hồ số DS1307 bằng máy tính, sử dụng PC Terminal (do chưa học bài GUI).

- Như vậy, ta sẽ phải gửi một chuỗi dữ liệu từ PC xuống chip PIC16F887 thông qua UART. PIC sẽ xử lý chuỗi dữ liệu này thành ngày, tháng, năm, ... và tiến hành set giờ cho DS1307 (thông qua port I2C).

- Sau đó, giá trị thời gian hiện tại sẽ được PIC đọc về rồi hiển thị lên LCD.
- Đồng thời, gửi giá trị thời gian về máy tính, hiển thị lên PC Terminal.
 

2death

Cố Vấn CLB
Staff member
Sau đây là chương trình của bạn Nguyễn Tiến Mạnh, chương trình có nhiều ý tưởng hay.

Tuy chương trình không thực hiện phần "gửi giá trị giờ đọc về lên PC (có hiển thị lên LCD)" nhưng vẫn có những sự "tương tác" khác giữa PC và PIC, ví dụ như kiểm tra định dạng dữ liệu, ...

Lưu ý là bạn Mạnh mới chỉ mô phỏng bằng Proteus, các bạn thử chạy code trên mạch giùm để kiểm tra xem code có trục trặc gì không.


Code:
/****************************************************************
 *
 * www.payitforward.edu.vn
 *
 ****************************************************************/
/****************************************************************
 *
 * PIC Training Course
 *
 ****************************************************************/

/****************************************************************
 *
 *    Module        : DS1307.c
 *    Description    : Reading time through UART & Writing on Real-time 
 *                     clock DS1307 through I2C Communication
 *  Tool        : HI-TECH PIC
 *    Chip        : 16F887
 *     History        : 07/05/2011
 *
 *    Author        : Nguyen Tien Manh, CLB NCKH
 *    Notes        : C3-K09
 *
 *
 ****************************************************************/


 /****************************************************************
 * IMPORT
 ****************************************************************/

#include <htc.h>
__CONFIG(XT & WDTDIS & PWRTEN & MCLREN & UNPROTECT & SWBOREN & 
    IESODIS & FCMDIS & LVPDIS & DEBUGDIS); //1st config. Word

__CONFIG(BORV21); //2st config. Word

#define _XTAL_FREQ     4000000
#include "lcd.h"    // lcd definition
#include "i2c.h"    // i2c definition
#include "uart.h"   // uart definition

// DS1307 7-bit address =  0b 110 1000 = 0x68 (I2C address)
#define     SLAVE_ADD    0x68
#define        READ        1
#define     WRITE        0

 /****************************************************************
 * EXTERN
 ****************************************************************/

//extern void Exit(void);
/*none...*/

/*****************************************************************
GLOBAL VARIABLE
******************************************************************/

unsigned char sec, min, hour, day, date, month, year, tm0i = 0, c[30];

/*****************************************************************
* ROUTINES
******************************************************************/
void timer0_init()
{
    T0CS = 0; // T0 internal Clock Source
    PSA = 0; // Prescale Assignment: Timer0
    // PS Prescale Rate Select
    PS2 = 1;
    PS1 = 1;
    PS0 = 1; // Timer0 rate: 1:256
    TMR0 = 0x00 ; // T0 Module Register
    // Set up Interrupt
    GIE = 1; // Gobal Interrupt Enable
    T0IE = 1; //T0 Interrupt Enable
    T0IF = 0; // Set off T0 Interrupt Flag
}

void clock_write() // Write data to DS1307 through I2C
{
    /*/// Input data for setting manually
    sec =   0x00    ; // CH=0 (bit7, clock-counting) and seconds = 00
    min =   0x40    ; // minutes = 40
    hour =  0x19    ; // hour-mode = 24-hour (bit 6) and hours = 19
    day =   0x01    ; // day = sunday
    date =  0x08    ; // date = 08
    month = 0x05    ; // month = 05
    year =  0x11    ; // year = 2011
    ///.. */

    /// Writing..
    //Send Start condition
    i2c_start();
    //Send Slave address + WRITE command
    i2c_write((SLAVE_ADD<<1)|WRITE);
    //   ((SLAVE_ADD<<1)| WRITE) = ((0x68<<1)|0) = 0xD0
    //Send register address (register of Slave – the first register need to read data)
    i2c_write(0x00);

    //write data
    i2c_write(sec); // write to 0x00
    i2c_write(min); // write to 0x01
    i2c_write(hour); // write to 0x02
    i2c_write(day); // write to 0x03
    i2c_write(date); // write to 0x04
    i2c_write(month); // write to 0x05
    i2c_write(year); // write to 0x06

    //Send Stop condition
    i2c_stop();
    ///..
}

void clock_read() // Read data from DS1307 through I2C
{
    //Send Start condition
    i2c_start();
    //Send Slave address + WRITE command
    i2c_write((SLAVE_ADD<<1)|WRITE);
    //   ((SLAVE_ADD<<1)| WRITE) = ((0x68<<1)|0) = 0xD0
    //Send register address (register of Slave – the first register need to read data)
    i2c_write(0x00);

    //ReStart condition
    i2c_stop();
    i2c_start();

    //Send Slave address + READ command
    i2c_write((SLAVE_ADD<<1)|READ);
    // ((SLAVE_ADD<<1)| READ) = ((0x68<<1)|1) = 0xD1

    //read data
    sec = i2c_read(0); //read from 0x00 & ACK
    min = i2c_read(0); //read from 0x01 & ACK
    hour = i2c_read(0); //read from 0x02 & ACK
    day = i2c_read(0); //read from 0x03 & ACK
    date = i2c_read(0); //read from 0x04 & ACK
    month = i2c_read(0); //read from 0x05 & ACK
    year = i2c_read(1); //read from 0x06 & NACK

    //Send Stop condition
    i2c_stop();
}

void lcd_display() //Display Data on LCD
{
    lcd_gotoxy(0,0);
    switch (day)
    {
        case 2: lcd_puts("Mon"); break;
        case 3: lcd_puts("Tue"); break;
        case 4: lcd_puts("Wed"); break;
        case 5: lcd_puts("Thu"); break;
        case 6: lcd_puts("Fri"); break;
        case 7: lcd_puts("Sat"); break;
        default: lcd_puts("Sun"); break;
    }
    
    lcd_gotoxy(6,0);
    lcd_putc((date>>4) + 0x30);
    lcd_putc((date&0x0F) + 0x30);
    lcd_putc('/');
    lcd_putc((month>>4) + 0x30);
    lcd_putc((month&0x0F) + 0x30);
    lcd_puts("/20");
    lcd_putc((year>>4) + 0x30);
    lcd_putc((year&0x0F) + 0x30);

    lcd_gotoxy(0,1);
    lcd_puts("T.MANH");
    lcd_gotoxy(8,1);
    lcd_putc((hour>>4) + 0x30);
    lcd_putc((hour&0x0F) + 0x30);
    lcd_putc(':');
    lcd_putc((min>>4) + 0x30);
    lcd_putc((min&0x0F) + 0x30);
    lcd_putc(':');
    lcd_putc((sec>>4) + 0x30);
    lcd_putc((sec&0x0F) + 0x30);
}

unsigned char num(unsigned char c) // check char c
{
    if (c<'0' || c>'9')
        return 0;
    else
        return 1; // return 0 if char c is a number.
}

void time_receive()
{
    /* Time format:
        d dd/mm/yyyy hh:mm:ss ==> 21 character before Enter
    Ex: 1 08/05/2011 08:45:00 is Sunday, 8/5/2011, 8h45m (24 hour mode)
    */
    unsigned i, ok = 0;
    uart_puts("\r\n\r\nMoi nhap thoi gian. Dinh dang: d dd/mm/yyyy hh:mm:ss.");
    while (!ok)
    {
        uart_puts("\r\n Chuoi nhap: ");
        for (i=0; i<22; i++) // Wait & Receive 22 character
            {
                c[i] = uart_getc();
                if (c[i] == '\r') break;
                uart_putc(c[i]);
            }
        if (num(c[0])&&num(c[2])&&num(c[3])&&num(c[5])&&num(c[6])
            &&num(c[8])&&num(c[9])&&num(c[10])&&num(c[11])
            &&num(c[13])&&num(c[14])&&num(c[16])&&num(c[17])&&num(c[19])&&num(c[20])) // check for numbers
            {
                ok = 1;
                uart_puts("\r\n  Da nhap thoi gian.");
            }
        else
            {
                ok = 0;
                uart_puts("\r\n  Nhap sai dinh dang! Moi nhap lai.");
            }
    };
}

unsigned char char2bcd (unsigned char c) // convert char type to BCD type.
{
    return (c - 0x30);
}
void time_convert() // Convert input time into format of register in DS1307.
{
    sec =   (char2bcd(c[19])<<4) + char2bcd(c[20]) ;
    min =   (char2bcd(c[16])<<4) + char2bcd(c[17])    ;
    hour =  (char2bcd(c[13])<<4) + char2bcd(c[14])    ;
    day =   char2bcd(c[0])    ;
    date =  (char2bcd(c[2])<<4) + char2bcd(c[3])    ;
    month = (char2bcd(c[5])<<4) + char2bcd(c[6])    ;
    year =  (char2bcd(c[10])<<4) + char2bcd(c[11])    ;
}

void interrupt isr() // interrupt service rountine
{
    if (T0IF && T0IE) //Interrupt Flag is on
        {
        T0IF =0;
        TMR0 = 0;
        tm0i++;
        if (tm0i == 4)
        {
            // Display Time in DS1307 on LCD through interrupt of timer0.
            clock_read();
            lcd_display();
            tm0i = 0;
        }
        }
}

 /****************************************************************
 * MAIN
 ****************************************************************/

/* -- void main (void)    -----------------------------------------
 *
 * Description    : UART, PIC, I2C & DS1307
 * Notes        :

 */

void main(void)
{
    timer0_init();
    i2c_init();
    uart_init();
    lcd_init();
    __delay_ms(10);
    lcd_clear();

    lcd_puts("..starting...");
    __delay_ms(80);
    lcd_clear();

    clock_read(); // Display the old time on DS1307.
    lcd_display();

    while(1)
    {
        //Reading time through UART & Writing on DS1307 through I2C.
        time_receive();
        time_convert();
        clock_write();
        uart_puts("\r\n Da cau hinh lai DS1307.");
        __delay_ms(10);
    };

}

/****************************************************************
 * END OF DS1307.c
 ****************************************************************/
 

Manhdd

Cố Vấn CLB
Staff member
Code đã test. Chạy trên mạch thực.
Tuy nhiên, mình không biết truyền ENTER qua terminal 5.5 thế nào nên sửa lại đôi chút:
Code:
for (i=0; i<21; i++) // Wait & Receive 21 character
            {
                c[i] = uart_getc();
                if (c[i] == 'r') break;
                uart_putc(c[i]);
            }
==> Chỉ nhận 21 kí tự rồi kiểm tra, không cần ENTER. Gửi 'r' để nhập lại từ đầu :-p
 

Manhdd

Cố Vấn CLB
Staff member
Chương trình có chèn thêm phần gửi thời gian về PC:

Code:
/****************************************************************
 *
 * www.payitforward.edu.vn
 *
 ****************************************************************/
/****************************************************************
 *
 * PIC Training Course
 *
 ****************************************************************/

/****************************************************************
 *
 *	Module		: DS1307.c
 *	Description	: Reading time through UART & Writing on Real-time clock DS1307 through I2C Communication
 *  Tool		: HI-TECH PIC
 *	Chip		: 16F887
 * 	History		: 07/05/2011
 *
 *	Author		: Nguyen Tien Manh, CLB NCKH
 *	Notes		: C3-K09
 *
 *
 ****************************************************************/


 /****************************************************************
 * IMPORT
 ****************************************************************/

#include <htc.h>
__CONFIG(XT & WDTDIS & PWRTEN & MCLREN & UNPROTECT & SWBOREN & IESODIS & FCMDIS & LVPDIS & DEBUGDIS); //1st config. Word

__CONFIG(BORV21); //2st config. Word
#define _XTAL_FREQ 	4000000
#include "lcd.h"    // lcd definition
#include "i2c.h"    // i2c definition
#include "uart.h"   // uart definition

// DS1307 7-bit address =  0b 110 1000 = 0x68 (I2C address)
#define 	SLAVE_ADD	0x68
#define	    READ		1
#define     WRITE		0

 /****************************************************************
 * EXTERN
 ****************************************************************/

//extern void Exit(void);
/*none...*/

/*****************************************************************
GLOBAL VARIABLE
******************************************************************/

unsigned char sec, min, hour, day, date, month, year, tm0i = 0, c[30];

/*****************************************************************
* ROUTINES
******************************************************************/
void timer0_init()
{
    T0CS = 0; // T0 internal Clock Source
	PSA = 0; // Prescale Assignment: Timer0
	// PS Prescale Rate Select
	PS2 = 1;
	PS1 = 1;
	PS0 = 1; // Timer0 rate: 1:256
	TMR0 = 0x00 ; // T0 Module Register
	// Set up Interrupt
	GIE = 1; // Gobal Interrupt Enable
	T0IE = 1; //T0 Interrupt Enable
	T0IF = 0; // Set off T0 Interrupt Flag
}
void clock_write() // Write data to DS1307 through I2C
{
    /*/// Input data for setting manually
    sec =   0x00    ; // CH=0 (bit7, clock-counting) and seconds = 00
    min =   0x40    ; // minutes = 40
    hour =  0x19    ; // hour-mode = 24-hour (bit 6) and hours = 19
    day =   0x01    ; // day = sunday
    date =  0x08    ; // date = 08
    month = 0x05    ; // month = 05
    year =  0x11    ; // year = 2011
    ///.. */

    /// Writing..
    //Send Start condition
    i2c_start();
    //Send Slave address + WRITE command
    i2c_write((SLAVE_ADD<<1)|WRITE);
    //   ((SLAVE_ADD<<1)| WRITE) = ((0x68<<1)|0) = 0xD0
    //Send register address (register of Slave – the first register need to read data)
    i2c_write(0x00);

    //write data
    i2c_write(sec); // write to 0x00
    i2c_write(min); // write to 0x01
    i2c_write(hour); // write to 0x02
    i2c_write(day); // write to 0x03
    i2c_write(date); // write to 0x04
    i2c_write(month); // write to 0x05
    i2c_write(year); // write to 0x06

    //Send Stop condition
    i2c_stop();
    ///..
}
void clock_read() // Read data from DS1307 through I2C
{
    //Send Start condition
    i2c_start();
    //Send Slave address + WRITE command
    i2c_write((SLAVE_ADD<<1)|WRITE);
    //   ((SLAVE_ADD<<1)| WRITE) = ((0x68<<1)|0) = 0xD0
    //Send register address (register of Slave – the first register need to read data)
    i2c_write(0x00);

    //ReStart condition
    i2c_stop();
    i2c_start();

    //Send Slave address + READ command
    i2c_write((SLAVE_ADD<<1)|READ);
    // ((SLAVE_ADD<<1)| READ) = ((0x68<<1)|1) = 0xD1

    //read data
    sec = i2c_read(0); //read from 0x00 & ACK
    min = i2c_read(0); //read from 0x01 & ACK
    hour = i2c_read(0); //read from 0x02 & ACK
    day = i2c_read(0); //read from 0x03 & ACK
    date = i2c_read(0); //read from 0x04 & ACK
    month = i2c_read(0); //read from 0x05 & ACK
    year = i2c_read(1); //read from 0x06 & NACK

    //Send Stop condition
    i2c_stop();
}
void lcd_display() //Display Data on LCD
{
    lcd_gotoxy(0,0);
    switch (day)
    {
        case 2: lcd_puts("Mon"); break;
        case 3: lcd_puts("Tue"); break;
        case 4: lcd_puts("Wed"); break;
        case 5: lcd_puts("Thu"); break;
        case 6: lcd_puts("Fri"); break;
        case 7: lcd_puts("Sat"); break;
        default: lcd_puts("Sun"); break;
    }
    lcd_gotoxy(6,0);
    lcd_putc((date>>4) + 0x30);
    lcd_putc((date&0x0F) + 0x30);
    lcd_putc('/');
    lcd_putc((month>>4) + 0x30);
    lcd_putc((month&0x0F) + 0x30);
    lcd_puts("/20");
    lcd_putc((year>>4) + 0x30);
    lcd_putc((year&0x0F) + 0x30);

    lcd_gotoxy(0,1);
    lcd_puts("T.MANH");
    lcd_gotoxy(8,1);
    lcd_putc((hour>>4) + 0x30);
    lcd_putc((hour&0x0F) + 0x30);
    lcd_putc(':');
    lcd_putc((min>>4) + 0x30);
    lcd_putc((min&0x0F) + 0x30);
    lcd_putc(':');
    lcd_putc((sec>>4) + 0x30);
    lcd_putc((sec&0x0F) + 0x30);
}
void time_send() //Send Time from DS1307 throuh UART
{
    uart_puts("\r\n DS1307: ");
    switch (day)
    {
        case 2: uart_puts("Mon "); break;
        case 3: uart_puts("Tue "); break;
        case 4: uart_puts("Wed "); break;
        case 5: uart_puts("Thu "); break;
        case 6: uart_puts("Fri "); break;
        case 7: uart_puts("Sat "); break;
        default: uart_puts("Sun "); break;
    }
    uart_putc((date>>4) + 0x30);
    uart_putc((date&0x0F) + 0x30);
    uart_putc('/');
    uart_putc((month>>4) + 0x30);
    uart_putc((month&0x0F) + 0x30);
    uart_puts("/20");
    uart_putc((year>>4) + 0x30);
    uart_putc((year&0x0F) + 0x30);

    uart_putc(' ');
    uart_putc((hour>>4) + 0x30);
    uart_putc((hour&0x0F) + 0x30);
    uart_putc(':');
    uart_putc((min>>4) + 0x30);
    uart_putc((min&0x0F) + 0x30);
    uart_putc(':');
    uart_putc((sec>>4) + 0x30);
    uart_putc((sec&0x0F) + 0x30);
}
unsigned char num(unsigned char c) // check char c
{
    if (c<'0' || c>'9')
        return 0;
    else
        return 1; // return 0 if char c is a number.
}

void time_receive()
{
    /* Time format:
        d dd/mm/yyyy hh:mm:ss ==> 21 character before Enter
    Ex: 1 08/05/2011 08:45:00 is Sunday, 8/5/2011, 8h45m (24 hour mode)
    */
    unsigned i, ok = 0;
    uart_puts("\r\n\r\nMoi nhap thoi gian. Dinh dang: d dd/mm/yyyy hh:mm:ss.");
    uart_puts("\r\n  Nhap 'r' de nhap lai tu dau");
    while (!ok)
    {
        for (i=0; i<21; i++) // Wait & Receive 22 character
            {
                c[i] = uart_getc();
                if (c[i] == 'r') break;
            }
        if (num(c[0])&&num(c[2])&&num(c[3])&&num(c[5])&&num(c[6])
            &&num(c[8])&&num(c[9])&&num(c[10])&&num(c[11])
            &&num(c[13])&&num(c[14])&&num(c[16])&&num(c[17])&&num(c[19])&&num(c[20])) // check for numbers
            {
                ok = 1;
                uart_puts("\r\n  Da nhap thoi gian.");
            }
        else
            {
                ok = 0;
                uart_puts("\r\n  Nhap sai dinh dang! Moi nhap lai. Dinh dang: d dd/mm/yyyy hh:mm:ss.");
            }
    };
}

unsigned char char2bcd (unsigned char c) // convert char type to BCD type.
{
    return (c - 0x30);
}
void time_convert() // Convert input time into format of register in DS1307.
{
    sec =   (char2bcd(c[19])<<4) + char2bcd(c[20]) ;
    min =   (char2bcd(c[16])<<4) + char2bcd(c[17])    ;
    hour =  (char2bcd(c[13])<<4) + char2bcd(c[14])    ;
    day =   char2bcd(c[0])    ;
    date =  (char2bcd(c[2])<<4) + char2bcd(c[3])    ;
    month = (char2bcd(c[5])<<4) + char2bcd(c[6])    ;
    year =  (char2bcd(c[10])<<4) + char2bcd(c[11])    ;
}

void interrupt isr() // interrupt service rountine
{
	if (T0IF && T0IE) //Interrupt Flag is on
		{
		T0IF =0;
		TMR0 = 0;
		tm0i++;
            if (tm0i == 6)
            {
                // Display Time in DS1307 on LCD through interrupt of timer0.
                clock_read();
                lcd_display();
                time_send();
                tm0i = 0;
            }
		}
}

 /****************************************************************
 * MAIN
 ****************************************************************/

/* -- void main (void)	-----------------------------------------
 *
 * Description	: UART, PIC, I2C & DS1307
 * Notes		:

 */

void main(void)
{
	timer0_init();
	i2c_init();
	uart_init();
	lcd_init();
	__delay_ms(10);
	lcd_clear();

	lcd_puts("..starting...");
	__delay_ms(80);
	lcd_clear();

	clock_read(); // Display the old time on DS1307.
    lcd_display();

	while(1)
	{
	    //Reading time through UART & Writing on DS1307 through I2C.
	    time_receive();
	    time_convert();
	    clock_write();
	    uart_puts("\r\n Da cau hinh lai DS1307.");
	    __delay_ms(10);
	};

}

/****************************************************************
 * END OF DS1307.c
 ****************************************************************/
 
Mạnh làm phần GUI chưa? Mình viết code, giao tiếp được với terminal thì ổn còn qua chương trình mình viết bằng GUI thì k được? Nếu làm phần GUI rồi thì Mạnh share luôn nha.
THANKS!!!

---ANHXTUAN---
---40903079---
 

Manhdd

Cố Vấn CLB
Staff member
Đây nè bạn:
Code:
http://www.box.net/shared/byjy0kcmzu5r0fjcg0k1
Còn vài vấn đề, nhưng chung quy thì ổn rồi :d
 

xangvo

Trứng gà
Khi mình biên dịch chương trình"Chương trình có chèn thêm phần gửi thời gian về PC" của bạn manhdd ở trên thì có lỗi như sau:

HI-TECH C Compiler for PIC10/12/16 MCUs (PRO Mode) V9.80
Copyright (C) 2010 Microchip Technology Inc.
Warning [1090] C:\Documents and Settings\Administrator\Desktop\MpLab Projects\Copy (2) of Lab7 - UART\lcd.c; 6. variable "_i" is not used
Error [499] ; 0. undefined symbol:
_lcd_clear(UART.obj)

********** Build failed! **********


Bạn nào biết giúp mình với!
Thanks!
 

tungbk

Cố Vấn CLB
Staff member
Dự đoán: cái này do trong thư viện lcd.h và lcd.c không có hàm lcd_clear()(mở lên xem thử có k nghe); chuyển lệnh này thành lcd_put_byte(0,DCLEAR);lcd_gotoxy(0,0);
 

xangvo

Trứng gà
Cảm ơn Tùng,mình chạy được rồi
nguyên nhân:
trong lcd.h thiếu "void lcd_clear();"
trong lcd.c thiếu hàm
void lcd_clear()
{
lcd_put_byte(0,1); // display off
while(lcd_busy());
//lcd_put_byte(0,1);
//lcd_gotoxy(0,0);
}
 
Top