打印
[应用方案]

51单片机万年历的C语言代码

[复制链接]
1402|9
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
biechedan|  楼主 | 2023-11-26 20:47 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
#include <reg51.h>
#include <intrins.h>

sbit LCD_RS = P2^0; // LCD_RS引脚
sbit LCD_RW = P2^1; // LCD_RW引脚
sbit LCD_E = P2^2; // LCD_E引脚
sbit SDA = P1^1; // I2C数据线
sbit SCL = P1^0; // I2C时钟线
sbit KEY_UP = P3^2; // 上键
sbit KEY_DOWN = P3^3; // 下键
sbit KEY_SET = P3^4; // 设置键

typedef unsigned char uint8_t; // 无符号8位整型数据类型
typedef unsigned int uint16_t; // 无符号16位整型数据类型

void Delay(uint16_t i); // 延时函数
void WriteCmd(uint8_t cmd); // 写指令到LCD函数
void WriteData(uint8_t dat); // 写数据到LCD函数
void LCD_Init(); // LCD初始化函数
void LCD_ShowString(uint8_t x, uint8_t y, uint8_t* str); // LCD显示字符串函数
void LCD_ShowTime(); // LCD显示时间函数
void LCD_ShowDate(); // LCD显示日期函数
void PCF8563_Write(uint8_t addr, uint8_t dat); // 写PCF8563芯片寄存器函数
uint8_t PCF8563_Read(uint8_t addr); // 读PCF8563芯片寄存器函数
void ReadTime(); // 读取当前时间函数
void SetTime(); // 设置时间函数
uint8_t Key_Scan(); // 按键扫描函数
void AdjustTime(); // 调整时间函数

uint8_t sec, min, hour, day, week, month, year; // 时间变量定义

void main()
{
    LCD_Init(); // LCD初始化
    while(1){
        ReadTime(); // 读取当前时间
        LCD_ShowTime(); // 显示时间
        LCD_ShowDate(); // 显示日期
        Delay(1000); // 延时1s
    }
}

void Delay(uint16_t i) // 延时函数
{
    while(i--);
}

void WriteCmd(uint8_t cmd) // 写指令到LCD函数
{
    LCD_RS = 0; // 设置LCD_RS引脚为0,表示传输的是指令
    LCD_RW = 0; // 设置LCD_RW引脚为0,表示写操作
    P0 = cmd; // 设置P0口输出值为指令
    LCD_E = 1; // 设置LCD_E引脚为1,表示开始传输数据
    _nop_(); // 等待一段时间
    _nop_();
    LCD_E = 0; // 设置LCD_E引脚为0,表示数据传输结束
}

void WriteData(uint8_t dat) // 写数据到LCD函数
{
    LCD_RS = 1; // 设置LCD_RS引脚为1,表示传输的是数据
    LCD_RW = 0; // 设置LCD_RW引脚为0,表示写操作
    P0 = dat; // 设置P0口输出值为数据
    LCD_E = 1; // 设置LCD_E引脚为1,表示开始传输数据
    _nop_(); // 等待一段时间
    _nop_();
    LCD_E = 0; // 设置LCD_E引脚为0,表示数据传输结束
}

void LCD_Init() // LCD初始化函数
{
    Delay(1000); // 延时等待LCD初始化完成
    WriteCmd(0x38); // 初始化LCD显示模式
    WriteCmd(0x0c); // 开启LCD显示,并设置光标不可见
    WriteCmd(0x06); // 光标右移,字符不动
    WriteCmd(0x01); // 清屏
}

void LCD_ShowString(uint8_t x, uint8_t y, uint8_t* str) // LCD显示字符串函数
{
    uint8_t i;
    if(y == 0) WriteCmd(0x80 + x); // 如果y为0,则设置光标在第一行的第x个位置
    else WriteCmd(0xc0 + x); // 如果y为1,则设置光标在第二行的第x个位置
    for(i=0; str!='\0'; i++){ // 逐个字符显示到LCD上
        WriteData(str);
    }
}

void LCD_ShowTime() // LCD显示时间函数
{
    LCD_ShowString(0, 0, "Time: "); // 在第一行第一个字符显示"Time: "
    WriteData(hour/10+'0'); // 显示小时十位数
    WriteData(hour%10+'0'); // 显示小时个位数
    WriteData(':'); // 显示冒号
    WriteData(min/10+'0'); // 显示分钟十位数
    WriteData(min%10+'0'); // 显示分钟个位数
    WriteData(':'); // 显示冒号
    WriteData(sec/10+'0'); // 显示秒钟十位数
    WriteData(sec%10+'0'); // 显示秒钟个位数
}

void LCD_ShowDate() // LCD显示日期函数
{
    uint8_t buf[16];
    sprintf(buf, "%04d-%02d-%02d", year+2000, month, day); // 将年、月、日格式化为字符串
    LCD_ShowString(0, 1, buf); // 在第二行显示日期
}

void PCF8563_Write(uint8_t addr, uint8_t dat) // 写PCF8563芯片寄存器函数
{
    Start_I2C(); // 启动I2C总线
    Write_I2C(0xa2); // 发送写地址
    Write_I2C(addr); // 发送寄存器地址
    Write_I2C(dat); // 发送数据
    Stop_I2C(); // 停止I2C总线
}

uint8_t PCF8563_Read(uint8_t addr) // 读PCF8563芯片寄存器函数
{
    uint8_t dat;
    Start_I2C(); // 启动I2C总线
    Write_I2C(0xa2); // 发送写地址
    Write_I2C(addr); // 发送寄存器地址
    Start_I2C(); // 启动I2C总线
    Write_I2C(0xa3); // 发送读地址
    dat = Read_I2C(); // 读取数据
    NoAck_I2C(); // 发送非应答信号
    Stop_I2C(); // 停止I2C总线
    return dat; // 返回读到的数据
}

void ReadTime() // 读取当前时间函数
{
    sec = bcd2hex(PCF8563_Read(0x02) & 0x7f); // 读取秒,并将转换后的十进制数赋值给sec变量
    min = bcd2hex(PCF8563_Read(0x03) & 0x7f); // 读取分,并将转换后的十进制数赋值给min变量
    hour = bcd2hex(PCF8563_Read(0x04) & 0x3f); // 读取小时,并将转换后的十进制数赋值给hour变量
    day = bcd2hex(PCF8563_Read(0x05) & 0x3f); // 读取日,并将转换后的十进制数赋值给day变量
    week = bcd2hex(PCF8563_Read(0x06) & 0x07); // 读取周,并将转换后的十进制数赋值给week变量
    month = bcd2hex(PCF8563_Read(0x07) & 0x1f); // 读取月,并将转换后的十进制数赋值给month变量
    year = bcd2hex(PCF8563_Read(0x08)); // 读取年,并将转换后的十进制数赋值给year变量
}

void SetTime() // 设置时间函数
{
    PCF8563_Write(0x02, hex2bcd(sec)); // 写入秒
    PCF8563_Write(0x03, hex2bcd(min)); // 写入分
    PCF8563_Write(0x04, hex2bcd(hour)); // 写入小时
    PCF8563_Write(0x05, hex2bcd(day)); // 写入日
    PCF8563_Write(0x06, hex2bcd(week)); // 写入周
    PCF8563_Write(0x07, hex2bcd(month)); // 写入月
    PCF8563_Write(0x08, hex2bcd(year)); // 写入年
}

uint8_t Key_Scan() // 按键扫描函数
{
    uint8_t key = 0xff; // 初始化按键值为无效值
    if(KEY_UP == 0){ // 如果检测到上键按下
        Delay(10); // 延时一段时间
        if(KEY_UP == 0) key = KEY_UP; // 如果还是检测到上键按下,则将按键值设置为KEY_UP
    }
    if(KEY_DOWN == 0){ // 如果检测到下键按下
        Delay(10); // 延时一段时间
        if(KEY_DOWN == 0) key = KEY_DOWN; // 如果还是检测到下键按下,则将按键值设置为KEY_DOWN
    }
    if(KEY_SET == 0){ // 如果检测到设置键

使用特权

评论回复
沙发
pmp| | 2024-1-6 16:45 | 只看该作者
51单片机的时钟精度有限,通常依赖于外部时钟源。确保外部时钟源(如DS1302)的精度和稳定性,以维护日历时间的准确性。

使用特权

评论回复
板凳
pentruman| | 2024-1-6 19:11 | 只看该作者
使用定时器来实现时间的自动更新              

使用特权

评论回复
地板
ulystronglll| | 2024-1-8 13:42 | 只看该作者
在低功耗应用中,可以考虑使用电源管理技术来降低功耗。

使用特权

评论回复
5
mikewalpole| | 2024-1-8 14:46 | 只看该作者
定义一个足够大的数组来存储所有可能的日期信息

使用特权

评论回复
6
yeates333| | 2024-1-8 15:23 | 只看该作者
为了实时更新万年历的时间,可以设置一个定时器中断服务例程。在这个例程中,可以更新万年历的显示,并计算下一个中断到来之前的时间。

使用特权

评论回复
7
phoenixwhite| | 2024-1-10 13:14 | 只看该作者
由于51单片机的资源有限(如内存和处理能力),代码应该尽可能高效。避免使用复杂的算法和不必要的循环,尽量使用简洁的代码实现功能。

使用特权

评论回复
8
tabmone| | 2024-1-10 20:05 | 只看该作者
根据需要存储的数据量和精度,选择合适的数据类型。例如,如果需要存储日期信息,可以使用unsigned char类型的变量来存储年、月、日等。

使用特权

评论回复
9
lzmm| | 2024-1-10 22:53 | 只看该作者
在液晶显示器(LCD)或其他显示设备上显示时间时,需要将其格式化为年、月、日、时、分、秒等格式。可以使用C语言的库函数(如sprintf())来实现格式化。

使用特权

评论回复
10
mollylawrence| | 2024-1-11 11:43 | 只看该作者
单片机资源有限,要合理安排内存空间来存放日期和时间数据以及可能用到的查找表或算法变量。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

294

主题

7976

帖子

12

粉丝