51 マイクロコントローラー + DS1302 は電子時計を設計します (LCD1602 は時間を表示します)

I.はじめに

電子時計は、正確な時間を表示することができる機器であり、家庭、オフィス、公共の場所などで広く使用され、人々に便利で正確な時間情報を提供しています。このプロジェクトは、51 マイクロコントローラーに基づいた電子時計を設計します。DS1302 を RTC クロック チップとして、LCD1602 を表示画面として使用し、時刻設定と目覚まし時計の設定のためにシリアル ポートを介してホスト コンピューターに接続します。

メイン制御チップとして、STC89C52 は高いパフォーマンスと安定性を備え、周辺機器の制御とデータ処理を完了できます。 DS1302 は、正確な時間カウントと日付機能を提供できる低電力リアルタイム クロック チップです。 LCD1602は、2行16列の表示領域を備えた一般的に使用されるキャラクタ型液晶ディスプレイで、時間やその他の関連情報を明確に表示できます。

シリアルポートを介してホストコンピュータに接続することで、ユーザーは個人のニーズに合わせて電子時計の時刻やアラーム時刻を簡単に設定できます。また、電子時計にはブザーも付いており、設定したアラーム時刻に応じて鳴ってユーザーに注意を促します。

画像-20230913112300737

画像-20230913112208076

電子時計には次のような機能があります。

(1) 現在の時刻と日付の表示: LCD1602 ディスプレイは、現在の時刻と日付の情報をリアルタイムで更新して表示します。

(2) 時刻設定:シリアルポートを介してホストコンピュータに接続することにより、時、分、秒を含む時刻を設定できます。

(3) 日付設定: ユーザーはホストコンピュータを介して現在の年、月、日を設定できます。

(4) 目覚まし時計の設定: ユーザーは時と分を含むアラーム時刻を設定できます。設定した時間になるとブザーが鳴り注意を促します。

(5) 時報チャイム:正時になるとブザーが短く鳴り、現在時刻を知らせます。

(6) 目覚まし時計の鳴動:アラーム時刻になると、ユーザーが停止するまでブザーが鳴り続けます。

(7) このプロジェクトは、STC89C52 マイコンの制御機能とシリアル通信機能を使用し、DS1302 クロック チップと LCD1602 表示画面を組み合わせて、シンプルで実用的な電子時計を実現します。ユーザーは独自のものを使用できます

(8) 時刻と目覚まし時計を設定する必要がありますが、これは便利で実用的であり、高い精度と安定性を備えています。

2. プロジェクト設計のアイデア

プロジェクトの設計アイデアは、ハードウェア設計とソフトウェア設計の 2 つの部分に分かれています。

2.1 ハードウェア設計のアイデア

(1) メイン制御チップの選定:メイン制御チップとして、高性能かつ安定性の高い STC89C52 を選定し、制御やデータ処理に適しています。

(2) RTC クロック チップの選択: RTC クロック チップとして、低消費電力、正確なタイミングおよび日付機能を備えた DS1302 が選択されます。

(3) 表示画面の選択: 表示画面として 2 行 16 列の文字表示領域を備えた LCD1602 を選択し、時間やその他の関連情報を明確に表示します。

(4) シリアルポート接続:時刻設定や目覚まし時計設定などのホストコンピュータとの通信を実現するシリアルポート接続回路を設計します。

(5) ブザー: 時間ごとの時刻報告と目覚まし時計の鳴動機能用にブザー モジュールを追加します。

(6) キー入力: 設定モードの切り替え、時刻調整、目覚まし時計の設定などのユーザー操作のためのキー入力モジュールを追加します。

2.2 ソフトウェア設計のアイデア

(1) 初期化設定: プログラムの開始時に、メイン制御チップのピンの構成、DS1302 クロック チップおよび LCD1602 ディスプレイの初期化を含むハードウェアの初期化を実行します。

(2) 時刻の取得と表示: DS1302 クロック チップを通じて現在の時刻と日付を取得し、LCD1602 ディスプレイに表示します。

(3) シリアル通信:シリアルポートを介してホストコンピュータと通信し、ホストコンピュータから送信される時刻設定やアラーム設定の指示を受信し、処理します。

(4) 時刻設定: ホスト コンピュータから送信された時刻設定命令に従って、DS1302 クロック チップの時刻カウンタを更新します。

(5) 日付設定: ホスト コンピュータから送信された日付設定命令に従って、DS1302 クロック チップの日付カウンタを更新します。

(6) 目覚まし時計の設定:ホストコンピュータから送信される目覚まし時計設定指示に従ってアラーム時刻を設定し、主制御チップの内部メモリに保存します。

(7) 時時刻: DS1302 クロック チップの時カウンタを検出することにより、時の値が変化すると、ブザーがトリガーされて短い音が鳴ります。

(8) 目覚まし時計の鳴動: 現在時刻と保存されたアラーム時刻を比較することにより、アラーム時刻に達するとブザーがトリガーされ、ユーザーが停止するか設定時間が終了するまでブザーが鳴り続けます。

3. プロジェクトのハードウェア配線

(1) STC89C52 および DS1302:

STC89C52 の P2.0 ポートは DS1302 の SCLK (クロック) ピンに接続され、クロック信号を提供します。

STC89C52 の P2.1 ポートは、データ送信のために DS1302 の IO (データ) ピンに接続されています。

STC89C52 の P2.2 ポートは、DS1302 のリセットに使用される DS1302 の RST (リセット) ピンに接続されています。

(2) STC89C52 および LCD1602:

STC89C52 の P0 ポートは LCD1602 の D0 ~ D7 ピンに接続され、文字データと制御信号を送信します。

STC89C52 の P2.3 ポートは LCD1602 の RS (レジスタ選択) ピンに接続されており、データまたはコマンド レジスタの選択に使用されます。

STC89C52 の P2.4 ポートは、LCD1602 の RW (読み取りおよび書き込み選択) ピンに接続されており、読み取りまたは書き込み動作を選択するために使用されます。

STC89C52のP2.5ポートをLCD1602のE(イネーブル)ピンに接続して送信を開始します。

(3) STC89C52 とブザーモジュール:

STC89C52 の P3.7 ポートは、ブザー モジュールの信号ピンに接続されており、ブザーをトリガーするために使用されます。

(4) シリアル通信インターフェース。 STC89C52 マイクロコントローラーのシリアル ポート ピンは次のとおりです。

UART 受信ライン (RXD): 外部デバイスに接続される送信ライン。

STC89C52 の P3.0 ポート (RXD) は、シリアル ポート データの受信に使用されます。

UART 送信ライン (TXD): 外部デバイスに接続される受信ライン。

STC89C52 の P3.1 ポート (TXD) は、シリアル ポート データの送信に使用されます。

4. プロジェクトコード

4.1 DS1302 クロックの読み取りと設定

次のコードは、STC89C52 の DS1302 クロック情報の読み取りとシリアル ポートへの出力、および目覚まし時計の設定、時刻の読み取り、およびシリアル ポートへの出力の機能を実装します。このうち、UART 通信はホスト コンピュータとの対話に使用され、ホスト コンピュータから送信された時刻文字列を受信し、それに応じて目覚まし時計と時刻を設定できます。

#include <reg52.h>
#include <stdio.h>

#define uchar unsigned char
#define uint unsigned int

// 定义DS1302时钟寄存器地址
#define DS1302_SEC_REG 0x80
#define DS1302_MIN_REG 0x82
#define DS1302_HR_REG 0x84
#define DS1302_DAY_REG 0x86
#define DS1302_MONTH_REG 0x88
#define DS1302_YEAR_REG 0x8C

// 定义DS1302控制寄存器命令
#define DS1302_CMD_WRITE 0x80
#define DS1302_CMD_READ 0x81

// 定义串口波特率为9600
#define BAUDRATE 9600
#define FOSC 11059200L
#define TIMER_INTERVAL (65536 - FOSC / 12 / BAUDRATE)

// 声明全局变量
uchar time_buffer[20]; // 存放时间字符串
uchar alarm_buffer[20]; // 存放闹钟时间字符串
uint i;
bit flag; // 标记是否接收到上位机的时间字符串

// 初始化UART模块
void InitUart() {
    
    
    TMOD &= 0x0F;
    TMOD |= 0x20;
    TH1 = TIMER_INTERVAL / 256;
    TL1 = TIMER_INTERVAL % 256;
    PCON |= 0x80;
    SCON = 0x50;
    ES = 1;
    TR1 = 1;
    EA = 1;
}

// 将单个字节发送到串口
void SendData(uchar dat) {
    
    
    SBUF = dat;
    while (!TI);
    TI = 0;
}

// 将字符串发送到串口
void SendString(uchar *s) {
    
    
    while (*s != '\0') {
    
    
        SendData(*s++);
    }
}

// 初始化DS1302时钟芯片
void InitDS1302() {
    
    
    uchar i;

    // 使能DS1302写保护功能
    DS1302_CE = 0;
    DS1302_SCL = 0;
    DS1302_CE = 1;
    Write_DS1302(DS1302_CMD_WRITE | 0x8e, 0x80);

    // 关闭时钟允许,准备写入数据
    Write_DS1302(DS1302_CMD_WRITE | 0x90, 0x00);

    // 写入年月日时分秒周
    Write_DS1302(DS1302_SEC_REG, 0x00);
    Write_DS1302(DS1302_MIN_REG, 0x30);
    Write_DS1302(DS1302_HR_REG, 0x11);
    Write_DS1302(DS1302_DAY_REG, 0x08);
    Write_DS1302(DS1302_MONTH_REG, 0x09);
    Write_DS1302(DS1302_YEAR_REG, 0x21);
    Write_DS1302(0x8e, 0x00);

    // 初始化闹钟时间
    for (i = 0; i < 20; i++) {
    
    
        alarm_buffer[i] = 0;
    }
}

// 向DS1302写入数据
void Write_DS1302(uchar addr, uchar dat) {
    
    
    uchar i;

    DS1302_CE = 0;
    DS1302_SCL = 0;

    // 发送起始信号
    DS1302_CE = 1;
    DS1302_SCL = 1;
    DS1302_CE = 0;

    // 发送命令字节地址
    DS1302_WriteByte(addr);
    // 发送数据字节
    DS1302_WriteByte(dat);

    // 停止信号
    DS1302_SCL = 0;
    DS1302_CE = 1;

    // 延时至少1us
    for (i = 0; i < 10; i++);
}

// 向DS1302读取数据
uchar Read_DS1302(uchar addr) {
    
    
    uchar dat;
    uchar i;

    DS1302_CE = 0;
    DS1302_SCL = 0;

    // 发送起始信号
    DS1302_CE = 1;
    DS1302_SCL = 1;
    DS1302_CE = 0;

    // 发送命令字节地址
    DS1302_WriteByte(addr | 0x01);
    // 读取数据字节
    dat = DS1302_ReadByte();

    // 停止信号
    DS1302_SCL = 0;
    DS1302_CE = 1;

    // 延时至少1us
    for (i = 0; i < 10; i++);
    return dat;
}

// 读取DS1302时间并打印到串口
void ReadTime() {
    
    
    uchar sec, min, hour, day, month, year;
    sprintf(time_buffer, "Time: ");
    sec = Read_DS1302(DS1302_SEC_REG);
    min = Read_DS1302(DS1302_MIN_REG);
    hour = Read_DS1302(DS1302_HR_REG);
    day = Read_DS1302(DS1302_DAY_REG);
    month = Read_DS1302(DS1302_MONTH_REG);
    year = Read_DS1302(DS1302_YEAR_REG);
    sprintf(time_buffer + 6, "%02d:%02d:%02d %02d/%02d/%02d\r\n", hour, min, sec, day, month, year);
    SendString(time_buffer);
}

// 向DS1302写入闹钟时间
void SetAlarm(uchar *str) {
    
    
    uint i = 0;

    // 将字符串转换为数字
    while (str[i] != '\0') {
    
    
        alarm_buffer[i] = str[i] - '0';
        i++;
        if (i > 19) // 防止溢出
            break;
    }

    // 写入闹钟时间
    Write_DS1302(DS1302_CMD_WRITE | 0x81, alarm_buffer[10] << 4 | alarm_buffer[11]);
    Write_DS1302(DS1302_CMD_WRITE | 0x83, alarm_buffer[8] << 4 | alarm_buffer[9]);
    Write_DS1302(DS1302_CMD_WRITE | 0x85, alarm_buffer[6] << 4 | alarm_buffer[7]);
}

// 从串口接收数据中解析出时间信息
void ParseTime() {
    
    
    uchar i, j;
    uchar temp;
    for (i = 0; i < 20; i++) {
    
    
        time_buffer[i] = 0;
    }

    // 接收字符串格式为:hh:mm:ss dd/mm/yy
    for (i = 0; i < 8; i++) {
    
    
        temp = 0;
        for (j = 0; j < 2; j++) {
    
    
            temp *= 10;
            temp += (SBUF - '0');
            while (!RI); // 等待接收完成
            RI = 0;
        }
        time_buffer[i] = temp;
        if (i == 2 || i == 4) {
    
    
            while (SBUF != ' '); // 跳过空格字符
            while (!RI); // 等待接收完成
            RI = 0;
        }
    }
    flag = 1; // 标记已经接收到字符串
}

// 主函数
void main() {
    
    
    InitUart();
    InitDS1302();
    flag = 0;
    while (1) {
    
    
        if (flag) {
    
     // 接收到时间字符串,设置闹钟和时间
            SetAlarm(time_buffer);
            Write_DS1302(DS1302_CMD_WRITE | 0x80, time_buffer[6] << 4 | time_buffer[7]);
            Write_DS1302(DS1302_CMD_WRITE | 0x82, time_buffer[3] << 4 | time_buffer[4]);
            Write_DS1302(DS1302_CMD_WRITE | 0x84, time_buffer[0] << 4 | time_buffer[1]);
            flag = 0;
        }
        ReadTime(); // 读取当前时间并发送到串口
    }
}

// UART接收中断函数
void UartIsr() interrupt 4 {
    
    
    if (RI) {
    
     // 接收到数据
        ParseTime(); // 解析时间字符串
    }
    RI = 0;
}

4.2 LCD1602 表示時計

STC89C52 に基づいて LCD1602 を制御して時刻文字列を表示する実装コード。

#include <reg52.h>
#include <stdio.h>

// 定义Data和Command寄存器选择端口
sbit LCD_RS = P2^0;  // RS引脚(寄存器选择)
sbit LCD_RW = P2^1;  // RW引脚(读写选择)
sbit LCD_EN = P2^2;  // EN引脚(使能)

// 定义数据总线端口
#define LCD_DATA P0    

void DelayMs(unsigned int ms) {
    
    
    unsigned int i, j;
    for (i = 0; i < ms; i++)
        for (j = 0; j < 120; j++);
}

void WriteCommand(unsigned char cmd) {
    
    
    LCD_RS = 0;  // 选择指令寄存器
    LCD_RW = 0;  // 写模式
    LCD_EN = 0;  // 低电平使能
    LCD_DATA = cmd;  // 发送指令
    DelayMs(1);  // 延时等待指令写入
    LCD_EN = 1;  // 高电平使能
    DelayMs(1);  // 持续一段时间
    LCD_EN = 0;  // 结束使能
}

void WriteData(unsigned char dat) {
    
    
    LCD_RS = 1;  // 选择数据寄存器
    LCD_RW = 0;  // 写模式
    LCD_EN = 0;  // 低电平使能
    LCD_DATA = dat;  // 发送数据
    DelayMs(1);  // 延时等待数据写入
    LCD_EN = 1;  // 高电平使能
    DelayMs(1);  // 持续一段时间
    LCD_EN = 0;  // 结束使能
}

void LCDInit() {
    
    
    WriteCommand(0x38);  // 设置显示模式为2行、5x8点阵字符
    WriteCommand(0x0C);  // 显示器开,光标关闭
    WriteCommand(0x06);  // 光标右移,整屏不移动
    WriteCommand(0x01);  // 清除显示并设置光标回到初始位置
}

void LCDDisplayTime(char* time) {
    
    
    int i;
    WriteCommand(0x80);  // 设置光标位置为第一行的起始位置

    for (i = 0; i < 16; i++) {
    
    
        WriteData(time[i]);  // 在第一行显示时间字符串
    }

    WriteCommand(0xC0);  // 设置光标位置为第二行的起始位置

    for (i = 0; i < 16; i++) {
    
    
        WriteData(time[16 + i]);  // 在第二行显示时间字符串
    }
}

void main() {
    
    
    char time_buffer[32] = "Current Time: 00:00:00";  // 时间字符串
    unsigned char sec = 0, min = 0, hour = 0;  // 当前时间变量

    LCDInit();  // 初始化LCD显示器

    while (1) {
    
    
        // 更新时间变量
        sec++;
        if (sec >= 60) {
    
    
            sec = 0;
            min++;
            if (min >= 60) {
    
    
                min = 0;
                hour++;
                if (hour >= 24) {
    
    
                    hour = 0;
                }
            }
        }

        // 格式化时间字符串
        sprintf(time_buffer + 14, "%02d:%02d:%02d", hour, min, sec);

        // 显示时间字符串
        LCDDisplayTime(time_buffer);

        DelayMs(1000);  // 延时1秒
    }
}

コードは、LCD_RSLCD_RWLCD_EN を使用して、LCD1602 の RS、RW、EN ピンをそれぞれ表します。 。データ バスは LCD_DATA を通じて定義され、P0 ポートに接続されます。最初に LCD ディスプレイを初期化し、無限ループで時間変数を更新して時間文字列をフォーマットし、最後に LCD に時間文字列を表示します。

おすすめ

転載: blog.csdn.net/xiaolong1126626497/article/details/135010781
おすすめ