ARM+SD2405 IIC_RTC驱动编写及IIC通讯协议

IIC通讯协议

协议简介

IIC(inter-integrated Circuit集成电路总线)总线支持设备之间的短距离通信,用于处理器和一些外围设备之间的接口,它需要两根信号线来完成信息交换,它是由数据线SDA和时钟线SCL构成的串行总线,可发送和接收数据。常见的外围设备如温湿度传感器,RTC模块、RFID等。IIC是半双工通信方式

IIC物理层

所有接到I2C总线设备上的串行数据SDA都接到总线的SDA上,各设备的时钟线SCL接到总线的SCL上。I2C总线上的每个设备都自己一个唯一的地址,来确保不同设备之间访问的准确性。
ARM+SD2405 IIC_RTC驱动编写及IIC通讯协议_第1张图片

软件IIC和硬件IIC

  • 软件IIC:软件IIC通信指的是用单片机的两个I/O端口模拟出来的IIC,用软件控制管脚状态以模拟I2C通信波形,软件模拟寄存器的工作方式。常见的软件IIC一般是单片机,STM32等
  • 硬件IIC:一块硬件电路,硬件I2C对应芯片上的I2C外设,有相应I2C驱动电路,其所使用的I2C管脚也是专用的,硬件(固件)I2C是直接调用内部寄存器进行配置。
  • 硬件I2C的效率要远高于软件的,而软件I2C由于不受管脚限制,接口比较灵活。

IIC 协议层

  • 空闲状态
    因为IIC的 SCL 和SDA 都需要接上拉电阻,保证空闲状态的稳定性,所以IIC总线在空闲状态下SCL 和SDA都保持高电平。I2C总线的SDA和SCL两条信号同时处于高电平时,规定为总线的空闲状态。此时各个器件的输出级场效管均处在截止状态,即释放总线,由两条信号线各自的上拉电阻把电平拉高。
  • 开始条件
    当 SCL 处于高电平时,SDA 由高电平变成低电平时构成一个开始条件,设备的所有操作均必须由开始条件开始。
  • 停止条件
    当 SCL 处于高电平时,SDA 由低电平变成高电平时构成一个停止条件,此时 SD2405AL 的所有操作均停止,系统进入待机状态。

ARM+SD2405 IIC_RTC驱动编写及IIC通讯协议_第2张图片

  • 数据有效性
    IIC信号在数据传输过程中,当SCL=1高电平时,数据线SDA必须保持稳定状态,不允许有电平跳变,只有在时钟线上的信号为低电平期间,数据线上的高电平或低电平状态才允许变化。
    SCL=1时 数据线SDA的任何电平变换会看做是总线的起始信号或者停止信号
    也就是在IIC传输数据的过程中,SCL时钟线会频繁的转换电平,以保证数据的传输
    ARM+SD2405 IIC_RTC驱动编写及IIC通讯协议_第3张图片
  • 应答信号
    每当主机向从机发送完一个字节的数据,主机总是需要等待从机给出一个应答信号,以确认从机是否成功接收到了数据
    应答信号:主机SCL拉高,读取从机SDA的电平,为低电平表示产生应答
    应答信号为低电平时,规定为有效应答位(ACK,简称应答位),表示接收器已经成功地接收了该字节;
    应答信号为高电平时,规定为非应答位(NACK),一般表示接收器接收该字节没有成功。
    ARM+SD2405 IIC_RTC驱动编写及IIC通讯协议_第4张图片

每发送一个字节(8个bit)**在一个字节传输的8个时钟后的第九个时钟期间,接收器接收数据后必须回一个ACK应答信号给发送器,这样才能进行数据传输
ARM+SD2405 IIC_RTC驱动编写及IIC通讯协议_第5张图片

数据传输

SDA线上的数据在SCL时钟“高”期间必须是稳定的,只有当SCL线上的时钟信号为低时,数据线上的“高”或“低”状态才可以改变。输出到SDA线上的每个字节必须是8位,数据传送时,先传送最高位(MSB),每一个被传送的字节后面都必须跟随一位应答位(即一帧共有9位)。

当一个字节按数据位从高位到低位的顺序传输完后,紧接着从设备将拉低SDA线,回传给主设备一个应答位ACK, 此时才认为一个字节真正的被传输完成 ,如果一段时间内没有收到从机的应答信号,则自动认为从机已正确接收到数据。
ARM+SD2405 IIC_RTC驱动编写及IIC通讯协议_第6张图片
ARM+SD2405 IIC_RTC驱动编写及IIC通讯协议_第7张图片
IIC通信原理参考

硬件RTC 驱动编写

#ifndef _T_SD2405_H_
#define _T_SD2405_H_

#include "common.h"
//时间计数器
#define SD2405_ADDR_YEAR   0x6				/*year:0-99*/
#define SD2405_ADDR_MONTH  0x5				/*month:1-12*/
#define SD2405_ADDR_DAY    0x4				/*day:1-31*/
#define SD2405_ADDR_WEEK   0x3				/*week:0-6*/
#define SD2405_ADDR_HOUR   0x2				/*hour:0-23*/
#define SD2405_ADDR_MINUTE 0x1				/*minute:0-59*/
#define SD2405_ADDR_SECOND 0x0				/*second:0-59*/
//闹钟计数器
#define SD2405_ADDR_ALARM_YEAR   0xd 		/*year:0-99*/
#define SD2405_ADDR_ALARM_MONTH  0xc		/*month:1-12*/
#define SD2405_ADDR_ALARM_DAY 	 0xb		/*day:1-31*/
#define SD2405_ADDR_ALARM_WEEK   0xa		/*week:0-6*/
#define SD2405_ADDR_ALARM_HOUR	 0x9		/*hour:0-23*/
#define SD2405_ADDR_ALARM_MINUTE 0x8		/*minute:0-59*/
#define SD2405_ADDR_ALARM_SECOND 0x7		/*second:0-59*/
//闹钟使能
#define SD2405_ADDR_ALARM_ENABLE 0xe		/*alarm enable reg*/
//RTC 控制寄存器
#define SD2405_ADDR_CTR1 0X0F				/*ctr1 reg*/
#define SD2405_ADDR_CTR2 0X10				/*ctr2 reg*/
#define SD2405_ADDR_CTR3 0X11				/*ctr3 reg*/
//时间调整
#define SD2405_ADDR_ADJ 0x12				/*timer adjustment*/
//倒计时定时器 count down
#define SD2405_ADDR_CT  0x13				/*Count down*/
//通用RAM 14-1F 12bytes
#define SD2405_ADDR_RAM 0x14				/*general ram*/

typedef enum{
	ALARM_YEAR_DISABLE	=0			,		/*disable*/
	ALARM_YEAR_ENABLE 	= (0x1<<0)	,		
	ALARM_MONTH_ENABLE	= (0x1<<1)	,
	ALARM_DAY_ENABLE 	= (0x1<<2)	,
	ALARM_WEEK_ENABLE 	= (0x1<<3)	,
	ALARM_HOUR_ENABLE 	= (0x1<<4)	,
	ALARM_MINUTE_ENABLE = (0x1<<5)	,
	ALARM_SECOND_ENABLE = (0x1<<6)	,		/*enable:-*/
	ALARM_ALL_ENABLE 	= 	0b0111111		/*enable:all*/
}ENUM_ALARM_ENABLE_TYPE;

typedef enum{
	CTRREG_INT_EN = 0,						/*中断使能位*/
	CTRREG_INTAF,							/*报警中断标志位*/
	CTRREG_INTDF,							/*倒计时中断标志位*/
	CTRREG_INTS,							/*中断类型*/
	CTRREG_FS,								/*频率中断*/
	CTRREG_TDS0,
	CTRREG_TDS1,
	CTRREG_WRTC1,
	CTRREG_WRTC2,
	CTRREG_WRTC3,
	CTRREG_ARST,
	CTRREG_FOBAT,
	CTRREG_RTCF,
	CTRREG_IM,
}ENUM_CTRREG_CMD;

typedef enum{			/*中断允许位*/
	DISABLE_ALL = 0,
	INTFE = (0x1<<0),	/*frequency int*/
	INTAE = (0x1<<1),	/*alarm int*/				
	INTDE = (0x1<<2),	/*count down int*/
	ENANLE_ALL = 0b111
}ENUM_INT_ENABLE;
	
typedef enum{
	NONE_INT 	= 0,	//禁止输出,高阻态
	AlARM_INT 	= 1,	//报警中断
	FREQ_INT 	= 2,	//频率中断
	CD_INT		= 3		//倒计时中断
}ENUM_INT_TYPE;

typedef enum{
	INTDF_FLAG = 0x1,
	INTAF_FLAG = 0x2
}ENUM_INT_FLAG;
 
 
#pragma pack(1)
typedef struct
{
	unsigned short year;
	unsigned char month;
	unsigned char day;
	unsigned char hour;
	unsigned char minute;
	unsigned char second;
	unsigned char week;
}SD2405_DateTypedef;
#pragma pack()

void sd2405_init(void);
void sd2405_write_time(SD2405_DateTypedef* date);
void sd2405_read_time(SD2405_DateTypedef* date);

void sd2405_test(void);

#endif



#include "t-sd2405.h"
#include "snps-i2c.h"
#include "board-snps-i2c.h"


//#define _SD2405_DBG_
/******************FUNCTION******************/
unsigned int _sd2405_read(unsigned int addr);
void _sd2405_write(unsigned int addr,unsigned char val);
void sd2405_print_curtime(void);


/*******************DEFINE*******************/

/*
BIT7 BIT6 BIT5 BIT4 BIT3 BIT2 BIT1 BIT0 
0	 1	  1    0    0    1    0    R/W
R:1
W:0
注意: IIC 地址支持7位
*/
#define	SD2405_RTC_ADDRESS		0x32

#define SD2405_IIC_ID 				0

#define sd2405_pr(format, args...) vsi_printf("[sd2405]: "format, ##args)
#ifdef _SD2405_DBG_
#define sd2405_dbg(format, args...) vsi_printf("[sd2405]: "format, ##args)
#else
#define sd2405_dbg(format, args...) do {} while (0)
#endif

#define WRTC1_ENABLE (0x1<<7)
#define WRTC2_ENABLE (0x1<<2)
#define WRTC3_ENABLE (0x1<<7)

#define ARST_ENABLE (0x1<<7)

//BCD码 十进制 互转
#define UChar2BCD(chr)  ((((chr) / 10) << 4) | ((chr) % 10))
#define BCD2UChar(bcd) ((((bcd) >> 4) * 10) + ((bcd) & 0X0F))

const char *Week[] = {"Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saterday"};
void _sd2405_reg_dump(void)
{
#ifdef _SD2405_DBG_
	unsigned char val = 0;
	val = _sd2405_read(SD2405_ADDR_CTR1);
	sd2405_dbg("CTR1[%x]: 0x%x\n",SD2405_ADDR_CTR1,val);
	val = _sd2405_read(SD2405_ADDR_CTR2);
	sd2405_dbg("CTR3[%x]: 0x%x\n",SD2405_ADDR_CTR2,val);
	val = _sd2405_read(SD2405_ADDR_CTR3);
	sd2405_dbg("CTR3[%x]: 0x%x\n",SD2405_ADDR_CTR3,val);
	sd2405_dbg("\n");
	unsigned char year,month,day,week,hour,minute,second;
	year = _sd2405_read(SD2405_ADDR_YEAR);
	month = _sd2405_read(SD2405_ADDR_MONTH);
	day = _sd2405_read(SD2405_ADDR_DAY);
	
	hour = _sd2405_read(SD2405_ADDR_HOUR);
	minute = _sd2405_read(SD2405_ADDR_MINUTE);
	second = _sd2405_read(SD2405_ADDR_SECOND);
	week = _sd2405_read(SD2405_ADDR_WEEK);

	sd2405_dbg("Date Hex[0x%x-0x%x]: 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n ",SD2405_ADDR_YEAR,SD2405_ADDR_SECOND,year,month,day,hour,minute,second,week);
#endif
	
}
void _sd2405_reset(void)
{
	unsigned char val = 0;
	val = _sd2405_read(SD2405_ADDR_CTR3);
	val = val | ARST_ENABLE;
	_sd2405_write(SD2405_ADDR_CTR3,val);
}

/*官方文档:
WRTC1、WRTC2、WRTC3 位: 寄存器(00H~1FH)写允许位。
即 WRTC1=1、WRTC2=1、WRTC3=1 时 写允许.注意置位有先后顺序,先置 WRTC1 为 1,后置 WRTC2、WRTC3 为 1;;
当 WRTC1=0、WRTC2=0、WRTC3=0 时则写禁止,同样置位有先后顺序,先置 WRTC2、WRTC3 为 0,后置 WRTC1 为 0。
当写禁止时,除了以上三位可以写以外,从 00H 到 1FH 所有的寄存器均不可以写。写禁止并不影响读操作。
*/
void _sd2405_ctr_wr_en(bool enable)
{
	unsigned char val = 0;
	if(enable)
	{	
		val = _sd2405_read(SD2405_ADDR_CTR2) | WRTC1_ENABLE;
		_sd2405_write(SD2405_ADDR_CTR2,val);
		mdelay(3);
		val = _sd2405_read(SD2405_ADDR_CTR1) | WRTC2_ENABLE | WRTC3_ENABLE ;
		_sd2405_write(SD2405_ADDR_CTR1,val);
	}
	else
	{
		val = _sd2405_read(SD2405_ADDR_CTR1);
		val = val & (~(WRTC2_ENABLE | WRTC3_ENABLE));
		_sd2405_write(SD2405_ADDR_CTR1,val);
		mdelay(3);
		val = _sd2405_read(SD2405_ADDR_CTR2);
		val = val & (~WRTC1_ENABLE);
		_sd2405_write(SD2405_ADDR_CTR2,val);
	}
	return;
}
unsigned char _sd2405_read_ctr(ENUM_CTRREG_CMD ctr_cmd)
{
	unsigned char val = 0;
	switch(ctr_cmd)
	{
		case CTRREG_INT_EN:
			//todo:
			val = _sd2405_read(SD2405_ADDR_CTR2);
			val = val & 0x7;
			break;
		case CTRREG_INTAF:
		case CTRREG_INTDF:
			//todo:
			val = _sd2405_read(SD2405_ADDR_CTR1);
			val = (val>>4) & 0x3;
			break;
		case CTRREG_FS:
			val = _sd2405_read(SD2405_ADDR_CTR3);
			val = val | 0xF;
			break;
		case CTRREG_INTS:
			val = _sd2405_read(SD2405_ADDR_CTR2);
			val = (val>>4) & 0x3;
			break;
		case CTRREG_TDS0:
		case CTRREG_TDS1:
			val = _sd2405_read(SD2405_ADDR_CTR3);
			val = (val>>4) & 0x3;
			break;
		case CTRREG_WRTC1:
		case CTRREG_WRTC2:
		case CTRREG_WRTC3:
			//nothing now
			break;
		case CTRREG_ARST:
			val = _sd2405_read(SD2405_ADDR_CTR3);
			val = (val>>7) & 0x1;
			break;
		case CTRREG_FOBAT:
			val = _sd2405_read(SD2405_ADDR_CTR2);
			val = (val>>3) & 0x1;
			break;
		case CTRREG_RTCF:
			val = _sd2405_read(SD2405_ADDR_CTR1);
			val = val & 0x1;
			break;
		case CTRREG_IM:
			break;
		default:
			break;
	}
	return val;
}

unsigned int _sd2405_read(unsigned int addr)
{
	unsigned char val = 0;
	snps_i2c_read(SD2405_IIC_ID, addr, &val, 1, SD2405_RTC_ADDRESS, 1);
	return val;
}

void _sd2405_write(unsigned int addr,unsigned char val)
{
	unsigned char tmp = val;
	snps_i2c_write(SD2405_IIC_ID, addr, &tmp, 1, SD2405_RTC_ADDRESS, 1);
}

SD2405_DateTypedef _sd2405_build_time(u32 year, u32 mon, u32 day,
		u32 hour, u32 min, u32 sec, u32 week)
{
	SD2405_DateTypedef rtc_time = {0};

	if(year>99)
	{
		sd2405_pr("Warning:The year shoud be in 2000-2099\n");
		rtc_time.year = year%100;
	}else{
		rtc_time.year = year;
	}
		
	rtc_time.month = mon;
	rtc_time.day = day;
	rtc_time.hour = hour;
	rtc_time.minute = min;
	rtc_time.second = sec;
	rtc_time.week = week;

	return rtc_time;
}

void _sd2405_rtc_irq_handler()
{
	char int_type = _sd2405_read_ctr(CTRREG_INTS);
	if(AlARM_INT == int_type ){
		//to do something
	}
	else if(FREQ_INT == int_type){
		//to do something
	}
	else if(CD_INT == int_type){
		//to do something
	}else{
		//no interrupt
	}
	return;
}
void sd2405_init(void)
{
	sd2405_pr(" %s\n", __func__);
	snps_i2c_pin_init(SD2405_IIC_ID);
	snps_i2c_init(SD2405_IIC_ID, 100000, 0, 0);
	sd2405_pr("sd2405_init down\n");
	//reset
	_sd2405_reset();
	//init a time struct
	SD2405_DateTypedef load_time = _sd2405_build_time(2022, 2, 2, 2, 30, 0, 2);
	sd2405_write_time(&load_time);
	//test
	sd2405_print_curtime();
}
void sd2405_write_time(SD2405_DateTypedef* date)
{
	_sd2405_ctr_wr_en(1);
	_sd2405_write(SD2405_ADDR_YEAR,UChar2BCD(date->year));
	_sd2405_write(SD2405_ADDR_MONTH,UChar2BCD(date->month));
	_sd2405_write(SD2405_ADDR_DAY,UChar2BCD(date->day));
	_sd2405_write(SD2405_ADDR_HOUR,UChar2BCD(date->hour));
	_sd2405_write(SD2405_ADDR_MINUTE,UChar2BCD(date->minute));
	_sd2405_write(SD2405_ADDR_SECOND,UChar2BCD(date->second));
	_sd2405_write(SD2405_ADDR_WEEK,UChar2BCD(date->week));
	_sd2405_ctr_wr_en(0);
	return;
}
void sd2405_read_time(SD2405_DateTypedef* date)
{
	//SD2405_DateTypedef read_time = {0};
	date->year = BCD2UChar(_sd2405_read(SD2405_ADDR_YEAR));
	date->month = BCD2UChar(_sd2405_read(SD2405_ADDR_MONTH));
	date->day = BCD2UChar(_sd2405_read(SD2405_ADDR_DAY));
	date->hour = BCD2UChar(_sd2405_read(SD2405_ADDR_HOUR));
	date->minute = BCD2UChar(_sd2405_read(SD2405_ADDR_MINUTE));
	date->second = BCD2UChar(_sd2405_read(SD2405_ADDR_SECOND));
	date->week = BCD2UChar(_sd2405_read(SD2405_ADDR_WEEK));
	sd2405_dbg(" time: %d-%d-%d %d:%d:%d %s\n", date->year,
		date->month, date->day, date->hour,
		date->minute, date->second,Week[date->week]);
	return ;//read_time;
}

void sd2405_print_curtime(void)
{
	//SD2405_DateTypedef *curr_time = (SD2405_DateTypedef *)malloc(sizeof(SD2405_DateTypedef)); //当前gcc 不支持malloc
	SD2405_DateTypedef curr_time = {0};
	sd2405_read_time(&curr_time);
	vsi_printf(" time: %d-%d-%d %d:%d:%d %s\n", curr_time.year,
		curr_time.month, curr_time.day, curr_time.hour,
		curr_time.minute, curr_time.second, Week[curr_time.week]);
}
/*建议只使用一种闹钟,TODO:组合闹钟*/ //测试建议用s、m、h
//void as2405_set_alarm_enable(unsigned int int_type)
void as2405_set_alarm_enable(ENUM_ALARM_ENABLE_TYPE int_type)
{
	unsigned char val = 0;
	val = _sd2405_read(SD2405_ADDR_CTR2);
	val = val | INTAE;
	_sd2405_write(SD2405_ADDR_CTR2,val);
	_sd2405_write(SD2405_ADDR_ALARM_ENABLE,int_type);
}
void sd2405_set_alarm(SD2405_DateTypedef* date)
{
	//todo : 中断控制
	_sd2405_ctr_wr_en(1);
	_sd2405_write(SD2405_ADDR_ALARM_YEAR,UChar2BCD(date->year));
	_sd2405_write(SD2405_ADDR_ALARM_MONTH,UChar2BCD(date->month));
	_sd2405_write(SD2405_ADDR_ALARM_DAY,UChar2BCD(date->day));
	//年月日
	/*
	_sd2405_write(SD2405_ADDR_ALARM_YEAR,_sd2405_read(SD2405_ADDR_YEAR));
	_sd2405_write(SD2405_ADDR_ALARM_MONTH,_sd2405_read(SD2405_ADDR_MONTH));
	_sd2405_write(SD2405_ADDR_ALARM_DAY,_sd2405_read(SD2405_ADDR_DAY));
	*/
	_sd2405_write(SD2405_ADDR_ALARM_HOUR,UChar2BCD(date->hour));
	_sd2405_write(SD2405_ADDR_ALARM_MINUTE,UChar2BCD(date->minute));
	_sd2405_write(SD2405_ADDR_ALARM_SECOND,UChar2BCD(date->second));
	_sd2405_write(SD2405_ADDR_ALARM_WEEK,UChar2BCD(date->week));
	_sd2405_ctr_wr_en(0);
	return;
}

void sd2405_test(void)
{
	vsi_printf("Testing sd2405 iic rtc.\n");
	vsi_printf("TODO: interrupt function\\Alarm function \n");
	vsi_printf("input \n ");
	vsi_printf("0:init  1: get time  2: set time  3: Test\n ");
	u8 c = vsi_getc();
	if('0' == c){
		vsi_printf("sd2405 init...\n");
		sd2405_init();
		_sd2405_reg_dump();
	}
	else if('1' == c){
		vsi_printf("sd2405 print time.\n");
		sd2405_print_curtime();
	}
	else if('2' == c){
		SD2405_DateTypedef load_time = _sd2405_build_time(2055, 5, 5, 5, 30, 0, 5);
		sd2405_write_time(&load_time);
		sd2405_print_curtime();
	}
	else if('3' == c)
	{
		unsigned int counter = 0;
		while(1)
		{
			vsi_printf("[%d]: ",counter);
			sd2405_print_curtime();
			delay(60);
			counter++;
		}
	}
	else
	{
		vsi_printf("errors\n");
	}
}

BUG

  1. 设备地址的位数:
    ARM+SD2405 IIC_RTC驱动编写及IIC通讯协议_第8张图片
    设备地址是7位:并且设备具有唯一地址:所以是0x32, 而不是0x64什么的。

2.日期的存储是以BCD码的形式存储的,不能以16进制或者2进制换算日期。
3.读取的字节数为1.
2. YEAR 寄存器为8个字节,并且是以BCD存储,所以最大只能是99。 所以要注意时间的开始是2000年,2000要自己加上。
3. 代码是硬件IIC 电路, 有特定的IIC 驱动接口可以调用, 并不是软件模拟的GPIO控制。

代码更新

BUGFIX: 新增12hour 24hour 进制转换、

#include "t-sd2405.h"
#include "snps-i2c.h"
#include "board-snps-i2c.h"


//#define _SD2405_DBG_
/******************FUNCTION******************/
unsigned int _sd2405_read(unsigned int addr);
void _sd2405_write(unsigned int addr,unsigned char val);
void sd2405_print_curtime(void);


/*******************DEFINE*******************/

/*
BIT7 BIT6 BIT5 BIT4 BIT3 BIT2 BIT1 BIT0 
0	 1	  1    0    0    1    0    R/W
R:1
W:0
注意: IIC 地址支持7位
*/
#define	SD2405_RTC_ADDRESS		0x32

#define SD2405_IIC_ID 				0

#define sd2405_pr(format, args...) vsi_printf("[sd2405]: "format, ##args)
#ifdef _SD2405_DBG_
#define sd2405_dbg(format, args...) vsi_printf("[sd2405]: "format, ##args)
#else
#define sd2405_dbg(format, args...) do {} while (0)
#endif

#define WRTC1_ENABLE (0x1<<7)
#define WRTC2_ENABLE (0x1<<2)
#define WRTC3_ENABLE (0x1<<7)

#define ARST_ENABLE (0x1<<7)
#define AM_PM_BIT 5
#define H24_12_BIT 7

//BCD码 十进制 互转
#define UChar2BCD(chr)  ((((chr) / 10) << 4) | ((chr) % 10))
#define BCD2UChar(bcd) ((((bcd) >> 4) * 10) + ((bcd) & 0X0F))

/*AM: 0 PM: 1*/
unsigned int AM_PM_flag = 0;
unsigned int hour_24_flag = 0;

const char *Week[] = {"Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saterday"};
const char *AMPM[] = {"AM","PM"};
void _sd2405_reg_dump(void)
{
#ifdef _SD2405_DBG_
	unsigned char val = 0;
	val = _sd2405_read(SD2405_ADDR_CTR1);
	sd2405_dbg("CTR1[%x]: 0x%x\n",SD2405_ADDR_CTR1,val);
	val = _sd2405_read(SD2405_ADDR_CTR2);
	sd2405_dbg("CTR3[%x]: 0x%x\n",SD2405_ADDR_CTR2,val);
	val = _sd2405_read(SD2405_ADDR_CTR3);
	sd2405_dbg("CTR3[%x]: 0x%x\n",SD2405_ADDR_CTR3,val);
	sd2405_dbg("\n");
	unsigned char year,month,day,week,hour,minute,second;
	year = _sd2405_read(SD2405_ADDR_YEAR);
	month = _sd2405_read(SD2405_ADDR_MONTH);
	day = _sd2405_read(SD2405_ADDR_DAY);
	
	hour = _sd2405_read(SD2405_ADDR_HOUR);
	minute = _sd2405_read(SD2405_ADDR_MINUTE);
	second = _sd2405_read(SD2405_ADDR_SECOND);
	week = _sd2405_read(SD2405_ADDR_WEEK);

	sd2405_dbg("Date Hex[0x%x-0x%x]: 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n ",SD2405_ADDR_YEAR,SD2405_ADDR_SECOND,year,month,day,hour,minute,second,week);
#endif
	
}
void _sd2405_reset(void)
{
	unsigned char val = 0;
	val = _sd2405_read(SD2405_ADDR_CTR3);
	val = val | ARST_ENABLE;
	_sd2405_write(SD2405_ADDR_CTR3,val);
}

/*官方文档:
WRTC1、WRTC2、WRTC3 位: 寄存器(00H~1FH)写允许位。
即 WRTC1=1、WRTC2=1、WRTC3=1 时 写允许.注意置位有先后顺序,先置 WRTC1 为 1,后置 WRTC2、WRTC3 为 1;;
当 WRTC1=0、WRTC2=0、WRTC3=0 时则写禁止,同样置位有先后顺序,先置 WRTC2、WRTC3 为 0,后置 WRTC1 为 0。
当写禁止时,除了以上三位可以写以外,从 00H 到 1FH 所有的寄存器均不可以写。写禁止并不影响读操作。
*/
void _sd2405_ctr_wr_en(bool enable)
{
	unsigned char val = 0;
	if(enable)
	{	
		val = _sd2405_read(SD2405_ADDR_CTR2) | WRTC1_ENABLE;
		_sd2405_write(SD2405_ADDR_CTR2,val);
		mdelay(3);
		val = _sd2405_read(SD2405_ADDR_CTR1) | WRTC2_ENABLE | WRTC3_ENABLE ;
		_sd2405_write(SD2405_ADDR_CTR1,val);
	}
	else
	{
		val = _sd2405_read(SD2405_ADDR_CTR1);
		val = val & (~(WRTC2_ENABLE | WRTC3_ENABLE));
		_sd2405_write(SD2405_ADDR_CTR1,val);
		mdelay(3);
		val = _sd2405_read(SD2405_ADDR_CTR2);
		val = val & (~WRTC1_ENABLE);
		_sd2405_write(SD2405_ADDR_CTR2,val);
	}
	return;
}

void _sd2405_clear_time_adj(void)
{
	_sd2405_write(SD2405_ADDR_ADJ,0);
}

unsigned char _sd2405_read_ctr(ENUM_CTRREG_CMD ctr_cmd)
{
	unsigned char val = 0;
	switch(ctr_cmd)
	{
		case CTRREG_INT_EN:
			//todo:
			val = _sd2405_read(SD2405_ADDR_CTR2);
			val = val & 0x7;
			break;
		case CTRREG_INTAF:
		case CTRREG_INTDF:
			//todo:
			val = _sd2405_read(SD2405_ADDR_CTR1);
			val = (val>>4) & 0x3;
			break;
		case CTRREG_FS:
			val = _sd2405_read(SD2405_ADDR_CTR3);
			val = val | 0xF;
			break;
		case CTRREG_INTS:
			val = _sd2405_read(SD2405_ADDR_CTR2);
			val = (val>>4) & 0x3;
			break;
		case CTRREG_TDS0:
		case CTRREG_TDS1:
			val = _sd2405_read(SD2405_ADDR_CTR3);
			val = (val>>4) & 0x3;
			break;
		case CTRREG_WRTC1:
		case CTRREG_WRTC2:
		case CTRREG_WRTC3:
			//nothing now
			break;
		case CTRREG_ARST:
			val = _sd2405_read(SD2405_ADDR_CTR3);
			val = (val>>7) & 0x1;
			break;
		case CTRREG_FOBAT:
			val = _sd2405_read(SD2405_ADDR_CTR2);
			val = (val>>3) & 0x1;
			break;
		case CTRREG_RTCF:
			val = _sd2405_read(SD2405_ADDR_CTR1);
			val = val & 0x1;
			break;
		case CTRREG_IM:
			break;
		default:
			break;
	}
	return val;
}

void _sd2405_set_24h(bool en)
{
	if(en)
		hour_24_flag = 1;
	else
		hour_24_flag = 0;
}

unsigned int _sd2405_read(unsigned int addr)
{


	unsigned char val = 0;
	snps_i2c_read(SD2405_IIC_ID, addr, &val, 1, SD2405_RTC_ADDRESS, 1);
	//BUGFIX: 小时的最高位 12_/24 是 12 或 24 小时制选择位。 
	//当 12_/24=1 时,24 小时制; 当 12_/24=0 时, 12 小时制。
	//12 小时制时,H20 为 AM/PM 指示位,H20=0 为 AM,H20=1 为 PM,
	//date->hour = BCD2UChar(_sd2405_read(SD2405_ADDR_HOUR));
	if(SD2405_ADDR_HOUR == addr)
	{
		sd2405_dbg("read addr: %x\n",val);
		if(val>>H24_12_BIT)
		{
			val &= 0x3F;
		}
		else
		{
			if(val>>AM_PM_BIT)
			{
				AM_PM_flag = 1;
			}
			else
			{
				AM_PM_flag = 0;
			}
			val &= 0x1F;
		}
		sd2405_dbg("return val: %x\n",val);
	}
	return val;
}

void _sd2405_write(unsigned int addr,unsigned char bcd_char)
{
	unsigned char tmp = 0;
	//hour  特殊处理
	if(SD2405_ADDR_HOUR == addr)
	{
		if(hour_24_flag)
		{
			tmp = bcd_char | (1<<H24_12_BIT);
		}
		else{
			if(AM_PM_flag)
				tmp = bcd_char | (1<<AM_PM_BIT);
			else
				tmp = bcd_char;
		}
		sd2405_dbg("write val: %x\n",bcd_char);
	}
	else
	{
		tmp = bcd_char;
	}
	snps_i2c_write(SD2405_IIC_ID, addr, &tmp, 1, SD2405_RTC_ADDRESS, 1);
}

SD2405_DateTypedef _sd2405_build_time(u32 year, u32 mon, u32 day,
		u32 hour, u32 min, u32 sec, u32 week)
{
	SD2405_DateTypedef rtc_time = {0};

	if(year>99)
	{
		//sd2405_pr("Warning:The year shoud be in 2000-2099\n");
		rtc_time.year = year%100;
	}else{
		rtc_time.year = year;
	}
		
	rtc_time.month = mon;
	rtc_time.day = day;
	rtc_time.hour = hour;
	rtc_time.minute = min;
	rtc_time.second = sec;
	rtc_time.week = week;

	return rtc_time;
}

void _sd2405_rtc_irq_handler()
{
	char int_type = _sd2405_read_ctr(CTRREG_INTS);
	if(AlARM_INT == int_type ){
		//to do something
	}
	else if(FREQ_INT == int_type){
		//to do something
	}
	else if(CD_INT == int_type){
		//to do something
	}else{
		//no interrupt
	}
	return;
}
void sd2405_init(void)
{
	sd2405_pr(" %s\n", __func__);
	snps_i2c_pin_init(SD2405_IIC_ID);
	snps_i2c_init(SD2405_IIC_ID, 100000, 0, 0);
	sd2405_pr("sd2405_init down\n");
	//reset
	_sd2405_reset();
	_sd2405_set_24h(1);
	//init a time struct
	SD2405_DateTypedef load_time = _sd2405_build_time(2022, 2, 2, 2, 30, 0, 0);
	sd2405_write_time(&load_time);
	//test
	sd2405_print_curtime();
}
void sd2405_write_time(SD2405_DateTypedef* date)
{
	_sd2405_clear_time_adj();
	_sd2405_ctr_wr_en(1);
	if(hour_24_flag){
		sd2405_dbg("Write: %d-%d-%d %d:%d:%d %s\n", date->year,
			date->month, date->day, date->hour,
			date->minute, date->second,Week[date->week]);
	}else{
		sd2405_dbg("Write: %d-%d-%d %d:%d:%d %s %s\n", date->year,
			date->month, date->day, date->hour,
			date->minute, date->second,AMPM[AM_PM_flag],Week[date->week]);
	}
	_sd2405_write(SD2405_ADDR_YEAR,UChar2BCD(date->year));
	_sd2405_write(SD2405_ADDR_MONTH,UChar2BCD(date->month));
	_sd2405_write(SD2405_ADDR_DAY,UChar2BCD(date->day));
	if( 0 == hour_24_flag )
	{
		if(date->hour >= 12){
			AM_PM_flag = 1;
			date->hour -= 12 ;
		}
	}
	_sd2405_write(SD2405_ADDR_HOUR,UChar2BCD(date->hour));
	_sd2405_write(SD2405_ADDR_MINUTE,UChar2BCD(date->minute));
	_sd2405_write(SD2405_ADDR_SECOND,UChar2BCD(date->second));
	_sd2405_write(SD2405_ADDR_WEEK,UChar2BCD(date->week));
	_sd2405_ctr_wr_en(0);
	return;

}

void sd2405_read_time(SD2405_DateTypedef* date)
{
	//SD2405_DateTypedef read_time = {0};
	date->year = BCD2UChar(_sd2405_read(SD2405_ADDR_YEAR));
	date->month = BCD2UChar(_sd2405_read(SD2405_ADDR_MONTH));
	date->day = BCD2UChar(_sd2405_read(SD2405_ADDR_DAY));
	date->hour = BCD2UChar(_sd2405_read(SD2405_ADDR_HOUR));
	date->minute = BCD2UChar(_sd2405_read(SD2405_ADDR_MINUTE));
	date->second = BCD2UChar(_sd2405_read(SD2405_ADDR_SECOND));
	date->week = BCD2UChar(_sd2405_read(SD2405_ADDR_WEEK));
	if(hour_24_flag){
		sd2405_dbg("Read: %d-%d-%d %d:%d:%d %s\n", date->year,
			date->month, date->day, date->hour,
			date->minute, date->second,Week[date->week]);
	}else{
		sd2405_dbg("Read: %d-%d-%d %d:%d:%d %s %s\n", date->year,
			date->month, date->day, date->hour,
			date->minute, date->second,AMPM[AM_PM_flag],Week[date->week]);
	}
	return ;//read_time;
}

void sd2405_print_curtime(void)
{
	//SD2405_DateTypedef *curr_time = (SD2405_DateTypedef *)malloc(sizeof(SD2405_DateTypedef)); //当前gcc 不支持malloc
	SD2405_DateTypedef curr_time = {0};
	sd2405_read_time(&curr_time);
	if(hour_24_flag)
	{
		vsi_printf("Time: %d-%d-%d %d:%d:%d %s\n", curr_time.year,
			curr_time.month, curr_time.day, curr_time.hour,
			curr_time.minute, curr_time.second, Week[curr_time.week]);
	}else{
		vsi_printf("Time: %d-%d-%d %d:%d:%d %s %s\n", curr_time.year,
			curr_time.month, curr_time.day, curr_time.hour,
			curr_time.minute, curr_time.second,AMPM[AM_PM_flag],Week[curr_time.week]);
	}
}
/*建议只使用一种闹钟,TODO:组合闹钟*/ //测试建议用s、m、h
//void as2405_set_alarm_enable(unsigned int int_type)
void as2405_set_alarm_enable(ENUM_ALARM_ENABLE_TYPE int_type)
{
	unsigned char val = 0;
	val = _sd2405_read(SD2405_ADDR_CTR2);
	val = val | INTAE;
	_sd2405_write(SD2405_ADDR_CTR2,val);
	_sd2405_write(SD2405_ADDR_ALARM_ENABLE,int_type);
}
void sd2405_set_alarm(SD2405_DateTypedef* date)
{
	//todo : 中断控制
	_sd2405_ctr_wr_en(1);
	_sd2405_write(SD2405_ADDR_ALARM_YEAR,UChar2BCD(date->year));
	_sd2405_write(SD2405_ADDR_ALARM_MONTH,UChar2BCD(date->month));
	_sd2405_write(SD2405_ADDR_ALARM_DAY,UChar2BCD(date->day));
	//年月日
	/*
	_sd2405_write(SD2405_ADDR_ALARM_YEAR,_sd2405_read(SD2405_ADDR_YEAR));
	_sd2405_write(SD2405_ADDR_ALARM_MONTH,_sd2405_read(SD2405_ADDR_MONTH));
	_sd2405_write(SD2405_ADDR_ALARM_DAY,_sd2405_read(SD2405_ADDR_DAY));
	*/
	_sd2405_write(SD2405_ADDR_ALARM_HOUR,UChar2BCD(date->hour));
	_sd2405_write(SD2405_ADDR_ALARM_MINUTE,UChar2BCD(date->minute));
	_sd2405_write(SD2405_ADDR_ALARM_SECOND,UChar2BCD(date->second));
	_sd2405_write(SD2405_ADDR_ALARM_WEEK,UChar2BCD(date->week));
	_sd2405_ctr_wr_en(0);
	return;
}

void sd2405_test(void)
{
	vsi_printf("Testing sd2405 iic rtc.\n");
	vsi_printf("TODO: interrupt function\\Alarm function \n");
	vsi_printf("input \n ");
	vsi_printf("0:init  1: get time  2: set time  3: Test\n ");
	u8 c = vsi_getc();
	if('0' == c){
		vsi_printf("sd2405 init...\n");
		sd2405_init();
		_sd2405_reg_dump();
	}
	else if('1' == c){
		vsi_printf("sd2405 print time.\n");
		sd2405_print_curtime();
	}
	else if('2' == c){
		SD2405_DateTypedef load_time ={0};

		_sd2405_set_24h(1);
		load_time = _sd2405_build_time(2025, 5, 5, 23, 30, 0, 0);
		sd2405_write_time(&load_time);
		sd2405_print_curtime();
		
		_sd2405_set_24h(0);
		load_time = _sd2405_build_time(2025, 5, 5, 23, 30, 0, 0);
		sd2405_write_time(&load_time);
		sd2405_print_curtime();

		//warning :  12小时进制转24进制, 天数会+1
		_sd2405_set_24h(1);
		load_time = _sd2405_build_time(2025, 5, 5, 5, 30, 0, 0);
		sd2405_write_time(&load_time);
		sd2405_print_curtime();

		_sd2405_set_24h(1);
		load_time = _sd2405_build_time(2025, 5, 5, 2, 30, 0, 0);
		sd2405_write_time(&load_time);
		sd2405_print_curtime();
	}
	else if('3' == c)
	{
		unsigned int counter = 0;
		while(1)
		{
			vsi_printf("[%d]: ",counter);
			sd2405_print_curtime();
			delay(60);
			counter++;
		}
	}
	else
	{
		vsi_printf("errors\n");
	}
}






你可能感兴趣的:(linux,其他,arm,单片机,iic)