/*
* calendar.c
*
* Created: 2018/3/30 9:08:12
* Author: blust
*/
#include "calendar.h"
PCF8563_Time_Buff real_time_buff;
PCF8563_Time_Buff pcf8563_time_buff;
/*
main函数初始化时调用如下函数即可,其余函数在该使用时调用即可。
iic_pin_Init();
pcf8563_init();
pcf8563_clear_alarm_flag();
*/
//引脚初始化程序需按照对应单片机和硬件引脚连接做响应调整
void iic_pin_Init(void)
{
struct port_config pin_conf;
port_get_config_defaults(&pin_conf);
pin_conf.direction = PORT_PIN_DIR_OUTPUT;
port_pin_set_config(SCL_PIN, &pin_conf);
port_pin_set_output_level(SCL_PIN, true); // 时钟信号SCL配置为输出口
pin_conf.input_pull =PORT_PIN_PULL_UP;
pin_conf.direction = PORT_PIN_DIR_OUTPUT_WTH_READBACK;
port_pin_set_config(SDA_PIN, &pin_conf);
port_pin_set_output_level(SDA_PIN, true); // 数据信号SDA配置为双向口
}
/****************************************************************************
FUNCTION : iic_start
DESCRIPTION : IIC通讯起始位(专用函数)
INPUT : None
OUTPUT : None
UPDATE :
AUTHOR : blust
DATE : 2018/03/30
*****************************************************************************/
void iic_start(void)
{ //时钟保持高,数据线从高到低一次跳变,I2C通信开始
SDA_PIN_High();
SCL_PIN_High();
delay_us(5); // 延时5us
SDA_PIN_Low();
delay_us(5);
SCL_PIN_Low();
}
/****************************************************************************
FUNCTION : iic_stop
DESCRIPTION : IIC通讯停止位(专用函数)
INPUT : None
OUTPUT : None
UPDATE :
AUTHOR : blust
DATE : 2018/03/30
*****************************************************************************/
void iic_stop(void)
{
SDA_PIN_Low(); //时钟保持高,数据线从低到高一次跳变,I2C通信停止
SCL_PIN_High();
delay_us(5);
SDA_PIN_High();
delay_us(5);
SCL_PIN_Low();
}
/****************************************************************************
FUNCTION : slave_ACK
DESCRIPTION : 从机发送应答位子程序(专用函数)
INPUT : None
OUTPUT : None
UPDATE :
AUTHOR : blust
DATE : 2018/03/30
*****************************************************************************/
void slave_ACK(void)
{
SDA_PIN_Low();
SCL_PIN_High();
delay_us(5);
SCL_PIN_Low();
SDA_PIN_High();
}
/****************************************************************************
FUNCTION : slave_NOACK
DESCRIPTION : 从机发送非应答位子程序,迫使数据传输过程结束(专用函数)
INPUT : None
OUTPUT : None
UPDATE :
AUTHOR : blust
DATE : 2018/03/30
*****************************************************************************/
void slave_NOACK(void)
{
SDA_PIN_High();
SCL_PIN_High();
delay_us(5);
SDA_PIN_Low();
SCL_PIN_Low();
}
/****************************************************************************
FUNCTION : check_ACK
DESCRIPTION : 主机应答位检查子程序,迫使数据传输过程结束(专用函数)
INPUT : None
OUTPUT : 非应答标志位
UPDATE :
AUTHOR : blust
DATE : 2018/03/30
*****************************************************************************/
bool check_ACK(void)
{
bool err = false;
uint8_t i = 100;
//SDA_PIN_High(); // 将SDA设置成输入,必须先向端口写1
SCL_PIN_High();
err = false;
while(i--)
{
if(SDA_Read()==0) // 若SDA=0表明有应答
{
break;
}
}
if(SDA_Read() == 1) // 若SDA=1表明非应答,置位非应答标志
err = true;
SCL_PIN_Low();
return err;
}
/****************************************************************************
FUNCTION : iic_send_byte
DESCRIPTION : 发送一个字节(专用函数)
INPUT : para
OUTPUT :
UPDATE :
AUTHOR : blust
DATE : 2018/03/30
*****************************************************************************/
void iic_send_byte(uint8_t para)
{
uint8_t n=8; // 向SDA上发送一位数据字节,共八位
while(n--)
{
if((para&0x80) == 0x80) // 若要发送的数据最高位为1则发送位1
{
SDA_PIN_High(); // 传送位1
SCL_PIN_High();
delay_us(5);
SCL_PIN_Low();
delay_us(1);
}
else
{
SDA_PIN_Low(); // 否则传送位0
SCL_PIN_High();
delay_us(5);
SCL_PIN_Low();
delay_us(1);
}
para = para << 1; // 数据左移一位
}
}
/****************************************************************************
FUNCTION : iic_receive_byte
DESCRIPTION : 接收一字节子程序(专用函数)
INPUT : None
OUTPUT : 接收到的数据
UPDATE :
AUTHOR : blust
DATE : 2018/03/30
*****************************************************************************/
uint8_t iic_receive_byte(void)
{
uint8_t i; // 从SDA线上读取一上数据字节,共八位
uint8_t rdata = 0;
//SDA_PIN_High();
for(i=0;i<8;i++)
{
delay_us(5);
SCL_PIN_High();
rdata = rdata<<1; // 左移一位,或_crol_(temp,1)
delay_us(5);
if(SDA_Read() == 1)
rdata |= 0x01; // 若接收到的位为1,则数据的最后一位置1
else
rdata &= 0xfe; // 否则数据的最后一位置0
SCL_PIN_Low();
}
return rdata;
}
/****************************************************************************
FUNCTION : write_CFGbyte
DESCRIPTION : 写PCF8563寄存器配置(专用函数)
INPUT : CFG_add寄存器地址,CFG_data要写入寄存器的数值
OUTPUT : 成功返回0 失败返回1
UPDATE :
AUTHOR : blust
DATE : 2018/03/30
*****************************************************************************/
uint8_t write_CFGbyte(uint8_t CFG_add, uint8_t CFG_data)
{
iic_start(); // 启动I2C
iic_send_byte(PCF8563_WRITE_ADDR); // 发送器件写地址
if(check_ACK() == 1) // 检查应答位
{
iic_stop();
return 1; // 若非应答表明器件错误或已坏,置错误标志位SystemError
}
iic_send_byte(CFG_add); // 发送寄存器地址
if(check_ACK() == 1) // 检查应答位
{
iic_stop();
return 1; // 若非应答表明器件错误或已坏,置错误标志位SystemError
}
iic_send_byte(CFG_data); // 发送寄存器数据
if(check_ACK() == 1) // 检查应答位
{
iic_stop();
return 1; // 若非应答表明器件错误或已坏,置错误标志位SystemError
}
iic_stop(); // 全部发完则停止
return 0;
}
/****************************************************************************
FUNCTION : receive_CFGbyte
DESCRIPTION : 读取某个寄存器数据(专用函数)
INPUT : CFG_add寄存器地址,* CFG_data读取到的寄存器值存放位置
OUTPUT : 成功返回0 失败返回1
UPDATE :
AUTHOR : blust
DATE : 2018/03/30
*****************************************************************************/
uint8_t receive_CFGbyte(uint8_t CFG_add, uint8_t * CFG_data)
{
uint8_t receive_da;
iic_start();
iic_send_byte(PCF8563_WRITE_ADDR); //器件写地址
if(check_ACK() == 1)
{
iic_stop();
return 1;
}
iic_send_byte(CFG_add); //寄存器地址
if(check_ACK() == 1)
{
iic_stop();
return 1;
}
iic_start();
iic_send_byte(PCF8563_READ_ADDR); //器件读地址
if(check_ACK() == 1)
{
iic_stop();
return 1;
}
receive_da=iic_receive_byte();
slave_NOACK(); // 收到最后一个字节后发送一个非应答位
iic_stop();
*CFG_data = receive_da;
return 0;
}
/****************************************************************************
FUNCTION : receive_CFGNbyte
DESCRIPTION : 读取n个寄存器数据(专用函数)
INPUT : CFG_add寄存器地址地址,n连续读数位,* buff存储区地址
OUTPUT : 成功返回0 失败返回1
UPDATE :
AUTHOR : blust
DATE : 2018/03/30
*****************************************************************************/
uint8_t receive_CFGNbyte(uint8_t CFG_add, uint8_t n, uint8_t * buff)
{
uint8_t i=0;
iic_start();
iic_send_byte(PCF8563_WRITE_ADDR); //器件写地址
if(check_ACK() == 1)
{
iic_stop();
return 1;
}
//check_ACK();
iic_send_byte(CFG_add); //寄存器地址
if(check_ACK() == 1)
{
iic_stop();
return 1;
}
//check_ACK();
iic_start();
iic_send_byte(PCF8563_READ_ADDR); //器件读地址
if(check_ACK() == 1)
{
iic_stop();
return 1;
}
//check_ACK();
for(i=0;i<n;i++)
{
buff[i]=iic_receive_byte();
if(i < n-1)
slave_ACK(); // 收到一个字节后发送一个应答位
}
slave_NOACK(); // 收到最后一个字节后发送一个非应答位
iic_stop();
return 0;
}
/****************************************************************************
FUNCTION : reset_pcf8563_time
DESCRIPTION : 复位时钟时间值,用以初次配置时钟
INPUT : None
OUTPUT : None
UPDATE :
AUTHOR : blust
DATE : 2018/03/30
*****************************************************************************/
void reset_pcf8563_time(void)
{
pcf8563_time_buff.val.seconds = 0x00;
pcf8563_time_buff.val.minutes = 0x00;
pcf8563_time_buff.val.hours = 0x00;
pcf8563_time_buff.val.days = 0x30;
pcf8563_time_buff.val.weeks = 0x05;
pcf8563_time_buff.val.months = 0x03;
pcf8563_time_buff.val.years = 0x18;
}
/****************************************************************************
FUNCTION : pcf8563_read_time
DESCRIPTION : 读出时间信息
INPUT : None
OUTPUT : None
UPDATE :
AUTHOR : blust
DATE : 2018/03/30
*****************************************************************************/
void pcf8563_read_time(void)
{
receive_CFGNbyte(PCF8563_TIME_ADD,7,pcf8563_time_buff.data);
pcf8563_time_buff.val.seconds &= 0x7F;
pcf8563_time_buff.val.minutes &= 0x7F;
pcf8563_time_buff.val.hours &= 0x3F;
pcf8563_time_buff.val.days &= 0x3F;
pcf8563_time_buff.val.weeks &= 0x07;
pcf8563_time_buff.val.months &= 0x1F;
pcf8563_time_buff.val.years &= 0xFF;
}
/****************************************************************************
FUNCTION : pcf8563_set_time
DESCRIPTION : 写时间修改值
INPUT : None
OUTPUT : None
UPDATE :
AUTHOR : blust
DATE : 2018/03/30
*****************************************************************************/
void pcf8563_set_time(void)
{
uint8_t i;
for(i=0;i<7;i++)
{
write_CFGbyte(PCF8563_TIME_ADD+i,pcf8563_time_buff.data[i]);
}
}
/****************************************************************************
FUNCTION : pcf8563_alarm_set
DESCRIPTION : 设置报警时间
INPUT : min_alarm报警分,hour_alarm报警时,day_alarm报警日,week_alarm报警周
OUTPUT : None
UPDATE :
AUTHOR : blust
DATE : 2018/03/30
*****************************************************************************/
void pcf8563_alarm_set(uint8_t min_alarm, uint8_t hour_alarm, uint8_t day_alarm, uint8_t week_alarm)
{
write_CFGbyte(PCF8563_MIN_ALARM_ADD,min_alarm); // 分钟报警配置 每小时报警一次
write_CFGbyte(PCF8563_HOUR_ALARM_ADD,hour_alarm); // 小时报警配置 每天报警一次
write_CFGbyte(PCF8563_DAY_ALARM_ADD,day_alarm); // 日报警配置 每月报警一次
write_CFGbyte(PCF8563_WEEK_ALARM_ADD,week_alarm); // 星期报警配置 每周报警一次
}
/****************************************************************************
FUNCTION : pcf8563_clear_alarm_flag
DESCRIPTION : 清除报警时间
INPUT : None
OUTPUT : None
UPDATE :
AUTHOR : blust
DATE : 2018/04/02
*****************************************************************************/
void pcf8563_clear_alarm_flag(void)
{
write_CFGbyte(PCF8563_CTRL2_ADD,0x12); // 清除报警标志位
}
/****************************************************************************
FUNCTION : pcf8563_init
DESCRIPTION : PCF8563初始化
INPUT : None
OUTPUT : None
UPDATE :
AUTHOR : blust
DATE : 2018/03/30
*****************************************************************************/
void pcf8563_init(void)
{
uint8_t temp;
if(receive_CFGbyte(PCF8563_TIME_ADD, &temp)==0)
{
if((temp&0x80) == 0x80) /*检查是否第一次启动,是则初始化时间*/
{
reset_pcf8563_time();
pcf8563_set_time();
write_CFGbyte(PCF8563_CTRL1_ADD,0x00); // 启动PCF8563
write_CFGbyte(PCF8563_CTRL2_ADD,0x12); // 报警允许中断输出
write_CFGbyte(PCF8563_CLKOUT_ADD,0x00); // CLKOUT输出禁止
write_CFGbyte(PCF8563_TIMER_CTRL_ADD,0x00); // 定时器禁止
write_CFGbyte(PCF8563_MIN_ALARM_ADD,0x80); // 分钟报警配置 每小时报警一次
write_CFGbyte(PCF8563_HOUR_ALARM_ADD,0x80); // 小时报警配置 每天报警一次
write_CFGbyte(PCF8563_DAY_ALARM_ADD,0x80); // 日报警配置 每月报警一次
write_CFGbyte(PCF8563_WEEK_ALARM_ADD,0x80); // 星期报警配置 每周报警一次
}
}
}
/****************************************************************************
FUNCTION : ymd_to_week
DESCRIPTION : 通过年月日计算出当天的星期数(专用函数)
INPUT : year:年份,month:月份,day:日期
OUTPUT : 星期数(0:周日 1:周一 …… 6:周六)
UPDATE :
AUTHOR : blust
DATE : 2018/03/30
*****************************************************************************/
uint8_t ymd_to_week(uint16_t year,uint8_t month,uint8_t day)
{
int8_t week;
int8_t c, y;
if (month <= 2)
{
month += 12;
year -= 1;
}
c = year/100;
y = year%100;
week = c/4 + y + y/4 + 13*(month+1)/5 + day - 2*c - 1;
week = week > 0 ? (week % 7):((week % 7) + 7);
return week;
}
/****************************************************************************
FUNCTION : time_to_real
DESCRIPTION : 将PCF8563读取的寄存器值转换为实时时间
INPUT : None
OUTPUT : None
UPDATE :
AUTHOR : blust
DATE : 2018/03/30
*****************************************************************************/
void time_to_real(void)
{
real_time_buff.val.seconds = ((pcf8563_time_buff.val.seconds)/16)*10 + pcf8563_time_buff.val.seconds % 16;
real_time_buff.val.minutes = ((pcf8563_time_buff.val.minutes)/16)*10 + pcf8563_time_buff.val.minutes % 16;
real_time_buff.val.hours = ((pcf8563_time_buff.val.hours)/16)*10 + pcf8563_time_buff.val.hours % 16;
real_time_buff.val.days = ((pcf8563_time_buff.val.days)/16)*10 + pcf8563_time_buff.val.days % 16;
real_time_buff.val.months = ((pcf8563_time_buff.val.months)/16)*10 + pcf8563_time_buff.val.months % 16;
real_time_buff.val.years = ((pcf8563_time_buff.val.years)/16)*10 + pcf8563_time_buff.val.years % 16;
real_time_buff.val.weeks = ymd_to_week(real_time_buff.val.years+2000,real_time_buff.val.months,real_time_buff.val.days);
}
/****************************************************************************
FUNCTION : time_to_pcf8563
DESCRIPTION : 将实时时间转换为PCF8563读取的寄存器值
INPUT : None
OUTPUT : None
UPDATE :
AUTHOR : blust
DATE : 2018/03/30
*****************************************************************************/
void time_to_pcf8563(void)
{
real_time_buff.val.weeks = ymd_to_week(real_time_buff.val.years+2000,real_time_buff.val.months,real_time_buff.val.days);
pcf8563_time_buff.val.seconds = (real_time_buff.val.seconds/10)*16 + real_time_buff.val.seconds%10;
pcf8563_time_buff.val.minutes = (real_time_buff.val.minutes/10)*16 + real_time_buff.val.minutes%10;
pcf8563_time_buff.val.hours = (real_time_buff.val.hours/10)*16 + real_time_buff.val.hours%10;
pcf8563_time_buff.val.days = (real_time_buff.val.days/10)*16 + real_time_buff.val.days%10;
pcf8563_time_buff.val.months = (real_time_buff.val.months/10)*16 + real_time_buff.val.months%10;
pcf8563_time_buff.val.years = (real_time_buff.val.years/10)*16 + real_time_buff.val.years%10;
pcf8563_time_buff.val.weeks = real_time_buff.val.weeks;
}
/****************************************************************************
FUNCTION : leap_year
DESCRIPTION : 判断是否是闰年(专用函数)
INPUT : year:需要判断的年份数
OUTPUT : 闰年返回1,否则返回0
UPDATE :
AUTHOR : blust
DATE : 2018/03/30
*****************************************************************************/
uint8_t leap_year(uint16_t year)
{
if ((year % 400) == 0)
{
return 1;
}
else if ((year % 100) == 0)
{
return 0;
}
else if ((year % 4) == 0)
{
return 1;
}
else
{
return 0;
}
}
/*
* 功能:
* 得到每个月有多少天
* 参数:
* month:需要得到天数的月份数
* year:该月所对应的年份数
*
* 返回值:
* 该月有多少天
*
*/
/****************************************************************************
FUNCTION : days_of_month
DESCRIPTION : 计算每月的天数(专用函数)
INPUT : month:月份,year:年份
OUTPUT : 返回该月份的天数
UPDATE :
AUTHOR : blust
DATE : 2018/03/30
*****************************************************************************/
uint8_t days_of_month(uint8_t month, uint16_t year)
{
const uint8_t day_per_month[MONTH_PER_YEAR] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
if ((month == 0) || (month > 12))
{
return day_per_month[1] + leap_year(year);
}
if (month != 2)
{
return day_per_month[month - 1];
}
else
{
return day_per_month[1] + leap_year(year);
}
}
/****************************************************************************
FUNCTION : ymd_to_week
DESCRIPTION : UNIX时间戳转换为实时时间(只适用于20xx年)
INPUT : UNIX时间戳
OUTPUT : 无(转换结果存储在 real_time_buff )
UPDATE : 如果需要扩大年份包含范围,需要将年份定义为U16,并将 “real_time_buff.val.years = y % 100”的“% 100”删掉
AUTHOR : blust
DATE : 2018/03/30
*****************************************************************************/
void unix_to_realtime(uint32_t utc_sec)
{
uint32_t temp;
uint16_t y, d;
uint8_t m;
utc_sec += SEC_PER_HOUR*8; // 时区调整
/* 小时 */
temp = utc_sec % SEC_PER_DAY;
real_time_buff.val.hours = temp / SEC_PER_HOUR;
/* 分钟 */
temp %= SEC_PER_HOUR;
real_time_buff.val.minutes = temp / SEC_PER_MIN;
/* 秒 */
real_time_buff.val.seconds = temp % SEC_PER_MIN;
/* 年 */
temp = utc_sec / SEC_PER_DAY;
for (y = UTC_BASE_YEAR; temp > 0; y++)
{
d = (DAY_PER_YEAR + leap_year(y));
if (temp >= d)
{
temp -= d;
}
else
{
break;
}
}
real_time_buff.val.years = y % 100;
/* 月 */
for (m = 1; m < MONTH_PER_YEAR; m++)
{
d = days_of_month(m, y);
if (temp >= d)
{
temp -= d;
}
else
{
break;
}
}
real_time_buff.val.months = m;
/* 日 */
real_time_buff.val.days = (uint8_t)(temp + 1);
real_time_buff.val.weeks = ymd_to_week(y, m, real_time_buff.val.days);
}