/****************************************************************************** 版权所有: 文件名称: rtc_ds1338.c 文件版本: 01.01 创建作者: sunxi 创建日期: 2008-07-09 功能说明: DS1338 实时时钟驱动程序。 其它说明: DS1338中的年从2000年开始。 修改记录: */ /*------------------------------- 头文件 -------------------------------------- */ #include "bspconfig.h" #include "i2c.h" #include "my_rtc.h" #include "watchdog.h" // #include "rt_ints.h" /*------------------------------- 宏定义 -------------------------------------- */ #if CFG_BSP_DEBUG #define _DEBUG_RTC #endif #define RTC_I2C_ID 0X64 #define RTC_ADDR_EXT 0X0D #define RTC_ADDR_FLAG 0X0E #define RTC_ADDR_CONTROL 0X0F #define RTC_FLAG_VLF 0X02 // rtc rx8025内部结构定义 typedef struct { u8 second; // 0~59 u8 minute; // 0~59 u8 hour; // 0~23 u8 week; // 每个星期用一个bit表示 u8 date; // 1~31 u8 month; // 1~12 *与tm结构范围不同 u8 year; // 0~99 *与tm结构范围不同 } rtc_t; typedef struct { u8 ext_reg; u8 flg_reg; u8 ctl_reg; // } rtc_ctrl_t; // 此结构仅供参考,不可使用 typedef struct { rtc_t rtc; u8 ram; // 可以任意读写 u8 min_alm; // u8 hour_alm; u8 day_alm; u8 tim_cnt0; u8 tim_cnt1; rtc_ctrl_t rtc_ctrl; } rtc_all_t; /*------------------------------ 全局变量 ------------------------------------- */ int g_rtc_write_5ms; // 离实际写入时间还差多少个5ms rtc_t g_rtc_write_data; // 需要写入RTC芯片的时间 /*------------------------------ 函数声明 ------------------------------------- */ int _rtc_read(uint32_t offset, unsigned char *buffer, uint32_t length); int _rtc_write(uint32_t offset, unsigned char *buffer, uint32_t length); /*------------------------------ 外部函数 ------------------------------------- */ /****************************************************************************** 函数名称: rtc_init 函数版本: 01.01 创建作者: sunxi 创建日期: 2008-08-06 函数说明: rtc初始化。 参数说明: 无 返回值: 成功返回0. 修改记录: */ int rtc_init(void) { return 0; } /****************************************************************************** 函数名称: rtc_time_read 函数版本: 01.01 创建作者: sunxi 创建日期: 2008-08-06 函数说明: 从时钟芯片中读出时间。 参数说明: p_tm(out): 读出的时间存放在这个指针所指的结构中。 返回值: 0: 成功返回。 1: 成功返回,但是由于时钟芯片振荡器曾经停止过,所以时间可能不准。 <0:读取失败。 修改记录: */ int rtc_time_read(struct rtc_time_t *p_tm) { int ret; rtc_t rtc; u8 flag = 0; // 检查参数 if (p_tm == 0) { return -1; } memset(&rtc, 0x00, sizeof(rtc_t)); // 读出数据 ret = _rtc_read(0, (unsigned char *)&rtc, sizeof(rtc)); if (ret != 0) { return -2; } ret = _rtc_read(RTC_ADDR_FLAG, &flag, 1); if (ret != 0) { return -3; } // 转换格式 // 清除不需要的标志 rtc.second &= ~0x80; rtc.hour &= ~0xc0; // BCD转化为二进制 p_tm->ms = (unsigned short)((rtc.second >> 4) * 10 + (rtc.second & 0xf)); // 秒 p_tm->min = (unsigned char)((rtc.minute >> 4) * 10 + (rtc.minute & 0xf)); // 分 p_tm->hour = (unsigned char)((rtc.hour >> 4) * 10 + (rtc.hour & 0xf)); // 时 p_tm->day = (unsigned char)((rtc.date >> 4) * 10 + (rtc.date & 0xf)); // 日 p_tm->month = (unsigned char)((rtc.month >> 4) * 10 + (rtc.month & 0xf)); // 月 p_tm->year = (unsigned char)((rtc.year >> 4) * 10 + (rtc.year & 0xf)); // 年 // s->ms p_tm->ms *= 1000; // 如果时钟振荡器曾经停止过,时间有可能不准,返回1。 if (flag & RTC_FLAG_VLF) { return 1; } return 0; } /****************************************************************************** 函数名称: rtc_time_write 函数版本: 01.01 创建作者: sunxi 创建日期: 2008-08-06 函数说明: 将时间写入时钟芯片。 参数说明: p_tm(in): 要写入的时间存放在这个指针所指的结构中。 返回值: 0: 成功返回。 <0:写入失败。 修改记录: */ extern void sys_time_change(struct rtc_time_t *p_ct); int rtc_time_write(struct rtc_time_t *p_tm) { // uint32_t flags; int seccond, ms5; rtc_t rtc; // rt_printf("rtc_time_write.\r\n"); // 检查参数 if (p_tm == 0) { return -11; } if (p_tm->ms > 59999) { return -12; } if (p_tm->min > 59) { return -13; } if (p_tm->hour > 23) { return -14; } if (p_tm->day < 1 || p_tm->day > 31) { return -15; } if (p_tm->month < 1 || p_tm->month > 12) { return -16; } if (p_tm->year > 99) { return -17; } // ms->s,使用下一秒作为要写入的秒值 seccond = p_tm->ms / 1000 + 1; ms5 = (seccond * 1000 - p_tm->ms) / 5; if (ms5 < 1) { ms5 = 1; } if (seccond >= 60) { seccond -= 60; sys_time_change(p_tm); } // 二进制转换为BCD rtc.second = (unsigned char)(((seccond / 10) << 4) | (seccond % 10)); rtc.minute = (unsigned char)(((p_tm->min / 10) << 4) | (p_tm->min % 10)); rtc.hour = (unsigned char)(((p_tm->hour / 10) << 4) | (p_tm->hour % 10)); rtc.date = (unsigned char)(((p_tm->day / 10) << 4) | (p_tm->day % 10)); rtc.month = (unsigned char)(((p_tm->month / 10) << 4) | (p_tm->month % 10)); rtc.year = (unsigned char)(((p_tm->year / 10) << 4) | (p_tm->year % 10)); // 星期不使用直接赋值1 rtc.week = 1; // 写入数据 rt_irq_save(flags); g_rtc_write_data = rtc; g_rtc_write_5ms = ms5; rt_irq_restore(flags); return 0; } void rtc_time_write_5ms(void) { int ret = 0; rtc_ctrl_t rtc_ctrl; if (g_rtc_write_5ms == 0) { return; } if (g_rtc_write_5ms == 1) { // 写入时间数据 // ret = _rtc_write(0,(unsigned char *)&g_rtc_write_data,sizeof(g_rtc_write_data)); //jack.liu 20200921 读写RTC失能 if (ret) { rt_printf("rtc_write_5ms0:ret=%d)\r\n", ret); } // 写入控制数据 rtc_ctrl.ext_reg = 0x00; rtc_ctrl.flg_reg = 0x00; rtc_ctrl.ctl_reg = 0x40; // 默认2.0s温度补偿 // ret = _rtc_write(RTC_ADDR_EXT,(unsigned char *)&rtc_ctrl,sizeof(rtc_ctrl)); //jack.liu 20200921 读写RTC失能 if (ret) { rt_printf("rtc_write_5ms1:ret=%d)\r\n", ret); } } g_rtc_write_5ms--; } /****************************************************************************** 函数名称: rtc_nvram_read 函数版本: 01.01 创建作者: sunxi 创建日期: 2008-08-07 函数说明: 从时钟芯片的NVRAM中读出数据。 参数说明: offset(in):需要读的数据在NVRAM中的偏移量,和length之和必须小于CFG_RTC_NVRAM_SIZE. buffer(out):读出数据的buffer。 length(in): 需要读出数据的长度,和offset之和必须小于CFG_RTC_NVRAM_SIZE。 返回值: 成功返回0. 修改记录: */ int rtc_nvram_read(uint32_t offset, unsigned char *buffer, uint32_t length) { // 检查参数 if (buffer == 0 || (offset + length) > CFG_RTC_NVRAM_SIZE) { return -1; } if (length == 0) { return 0; } return _rtc_read(offset + 7, buffer, length); } /****************************************************************************** 函数名称: rtc_nvram_write 函数版本: 01.01 创建作者: sunxi 创建日期: 2008-08-07 函数说明: 将数据写入时钟芯片的NVRAM中。 参数说明: offset(in):需要写入的数据在NVRAM中的偏移量,和length之和必须小于CFG_RTC_NVRAM_SIZE. buffer(in):写入数据的buffer。 length(in): 需要写入数据的长度,和offset之和必须小于CFG_RTC_NVRAM_SIZE。 返回值: 成功返回0. 修改记录: */ int rtc_nvram_write(uint32_t offset, unsigned char *buffer, uint32_t length) { // 检查参数 if (buffer == 0 || (offset + length) > CFG_RTC_NVRAM_SIZE) { return -1; } if (length == 0) { return 0; } return _rtc_write(offset + 7, buffer, length); } /*------------------------------ 内部函数 ------------------------------------- */ /****************************************************************************** 函数名称: _rtc_read 函数版本: 01.01 创建作者: sunxi 创建日期: 2008-08-07 函数说明: 从时钟芯片中读出数据。 参数说明: offset(in):需要读的数据在时钟芯片的偏移量. buffer(out):读出数据的buffer。 length(in): 需要读出数据的长度。 返回值: 成功返回0. 修改记录: */ int _rtc_read(uint32_t offset, unsigned char *buffer, uint32_t length) { #if 0 unsigned char c; int h_i2c,ret; h_i2c = i2c_open(I2C_ID_RTC); if(h_i2c < 0) { return -1; } ret = -2; //等待总线释放 I2C_WAIT_BUS_IDLE(); //发送模式 MCF_I2CR |= MCF_I2C_I2CR_MTX; //发送START信号 MCF_I2CR |= MCF_I2C_I2CR_MSTA; //发送控制字节 MCF_I2DR = RTC_I2C_ID; I2C_WAIT_COMPLETION(); I2C_CHECK_ACK(); //发送地址 MCF_I2DR = (unsigned char)(offset); I2C_WAIT_COMPLETION(); I2C_CHECK_ACK(); //发送重开始信号,方向改为读 MCF_I2CR |= MCF_I2C_I2CR_RSTA; MCF_I2DR = RTC_I2C_ID | 0x01; I2C_WAIT_COMPLETION(); I2C_CHECK_ACK(); //接收模式 MCF_I2CR &= ~MCF_I2C_I2CR_MTX; //接收数据,最后一个字节不发ACK. if(length == 1) { MCF_I2CR |= MCF_I2C_I2CR_TXAK; //不发ACK c = MCF_I2DR; //空读,启动下一个字节的读 I2C_WAIT_COMPLETION(); *buffer++ = MCF_I2DR; I2C_WAIT_COMPLETION(); } else { //循环接收length个字节。 MCF_I2CR &= ~MCF_I2C_I2CR_TXAK; //发送ACK c = MCF_I2DR; //空读,启动下一个字节的读 I2C_WAIT_COMPLETION(); while(length--) { if(length == 1) { MCF_I2CR |= MCF_I2C_I2CR_TXAK; //不发ACK } *buffer++ = MCF_I2DR; //读数据,并启动下一个字节的读。 I2C_WAIT_COMPLETION(); } } ret = 0; LABEL_END: //发送STOP信号 MCF_I2CR &= ~MCF_I2C_I2CR_MSTA; i2c_close(h_i2c); return ret; #endif return 0; } /****************************************************************************** 函数名称: _rtc_write 函数版本: 01.01 创建作者: sunxi 创建日期: 2008-08-07 函数说明: 将数据写入时钟芯片中。 参数说明: offset(in):需要写入的数据在时钟芯片中的偏移量. buffer(in):写入数据的buffer。 length(in): 需要写入数据的长度。 返回值: 成功返回0. 修改记录: */ int _rtc_write(uint32_t offset, unsigned char *buffer, uint32_t length) { #if 0 int h_i2c,ret; h_i2c = i2c_open(I2C_ID_RTC); if(h_i2c < 0) { return -1; } ret = -2; //等待总线释放 I2C_WAIT_BUS_IDLE(); //发送模式 // MCF_I2CR |= MCF_I2C_I2CR_MTX; //发送START信号 // MCF_I2CR |= MCF_I2C_I2CR_MSTA; //发送控制字节 MCF_I2DR = RTC_I2C_ID; I2C_WAIT_COMPLETION(); I2C_CHECK_ACK(); //发送地址 MCF_I2DR = (unsigned char)(offset); I2C_WAIT_COMPLETION(); I2C_CHECK_ACK(); //发送数据 while(length--) { MCF_I2DR = *buffer++; I2C_WAIT_COMPLETION(); I2C_CHECK_ACK(); } ret = 0; LABEL_END: //发送STOP信号 // MCF_I2CR &= ~MCF_I2C_I2CR_MSTA; i2c_close(h_i2c); return ret; #endif return 0; } /*------------------------------ 测试函数 ------------------------------------- */ #ifdef _DEBUG_RTC #include "ustimer.h" #include "rt.h" #define RTC_TEST_SIZE 1 int rtc_test(void) { int ret; uint32_t us0, us; uint32_t i, offset, length; unsigned char buffer[CFG_RTC_BUFFER_SIZE]; struct rtc_time_t tm0; rt_printf("rtc_test start...\r\n"); offset = 0; while (offset < CFG_RTC_NVRAM_SIZE) { // 写 length = (CFG_RTC_NVRAM_SIZE - offset) > RTC_TEST_SIZE ? RTC_TEST_SIZE : (CFG_RTC_NVRAM_SIZE - offset); for (i = 0; i < length; i++) { buffer[i] = (unsigned char)(offset + i + 0x37); } us0 = ustimer_get_origin(); ret = rtc_nvram_write(offset, (unsigned char *)buffer, length); us = ustimer_get_duration(us0); // rt_printf("rtc_nvram_write:(offset=%d,ret=%d,us=%d)\r\n",offset,ret,us); if (ret != 0) { break; } offset += length; } offset = 0; while (offset < CFG_RTC_NVRAM_SIZE) { // 读 length = (CFG_RTC_NVRAM_SIZE - offset) > RTC_TEST_SIZE ? RTC_TEST_SIZE : (CFG_RTC_NVRAM_SIZE - offset); memset(buffer, 0, RTC_TEST_SIZE); us0 = ustimer_get_origin(); ret = rtc_nvram_read(offset, (unsigned char *)buffer, length); us = ustimer_get_duration(us0); // rt_printf("rtc_nvram_read:(offset=%d,ret=%d,us=%d)\r\n",offset,ret,us); if (ret != 0) { break; } // 比较 for (i = 0; i < length; i++) { if (buffer[i] != (unsigned char)(offset + i + 0x37)) { break; } } if (i != length) { rt_printf("compare error(offset=%lu,i=%lu)!\r\n", offset, i); break; } offset += length; } if (offset == CFG_RTC_NVRAM_SIZE) { rt_printf("rtc_nvram ok!\r\n\n"); } else { rt_printf("rtc_nvram fail!\r\n\n"); } // 开始测试时间 ret = rtc_time_read(&tm0); if (ret == 1) { rt_printf("rtc was stoped!\r\n"); } else if (ret == 0) { rt_printf("rtc is running!\r\n"); } else { rt_printf("rtc_time_read test fail!"); } tm0.ms = 8000; tm0.min = 28; tm0.hour = 14; tm0.day = 19; tm0.month = 10; tm0.year = 2010 - 2000; #if 0 ret = rtc_time_write(&tm0); if(ret == 0) { rt_printf("rtc_time_write ok!\r\n\n"); } else { rt_printf("rtc_time_write fail(ret=%d)!\r\n\n",ret); } #endif us0 = ustimer_get_origin(); while (1) { ret = rtc_time_read(&tm0); if (ret == 0 || ret == 1) { rt_printf("date(ret=%d): %04d-%02d-%02d time: %02d:%02d:%05d!\r\n", ret, tm0.year + 2000, tm0.month, tm0.day, tm0.hour, tm0.min, tm0.ms); } else { rt_printf("rtc_time_read fail(ret=%d)!\r\n\n", ret); } watchdog_feed_mainloop(); // 精确间隔1S循环。 ustimer_delay_origin(us0, USTIMER_SEC); us0 += USTIMER_SEC; } return 0; } #endif