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

30 篇文章 2 订阅

IIC通讯协议

协议简介

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

IIC物理层

所有接到I2C总线设备上的串行数据SDA都接到总线的SDA上,各设备的时钟线SCL接到总线的SCL上。I2C总线上的每个设备都自己一个唯一的地址,来确保不同设备之间访问的准确性。
在这里插入图片描述

软件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 的所有操作均停止,系统进入待机状态。

在这里插入图片描述

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

每发送一个字节(8个bit)**在一个字节传输的8个时钟后的第九个时钟期间,接收器接收数据后必须回一个ACK应答信号给发送器,这样才能进行数据传输
在这里插入图片描述

数据传输

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

当一个字节按数据位从高位到低位的顺序传输完后,紧接着从设备将拉低SDA线,回传给主设备一个应答位ACK, 此时才认为一个字节真正的被传输完成 ,如果一段时间内没有收到从机的应答信号,则自动认为从机已正确接收到数据。
在这里插入图片描述
在这里插入图片描述
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. 设备地址的位数:
    在这里插入图片描述
    设备地址是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");
	}
}






  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
SD2405AL实时时钟模块介绍: SD2400系列是一种具有内置晶振、支持IIC串行接口的高精度实时时钟芯片,CPU可使用该接口通过5位地址寻址来读写片内32字节寄存器的数据(包括时间寄存器、报警寄存器、控制寄存器、通用SRAM寄存器)。 SD2400系列内置晶振,该芯片可保证时钟精度为±5ppm(在25℃下),即年误差小于2.5 分钟;该芯片内置时钟精度调整功能,可以在很宽的范围内校正时钟的偏差(分辨力3ppm),通过外置或内置的数字温度传感器可设定适应温度变化的调整值,实现在宽温范围内高精度的计时功能。 SD2400系列内置的一次性工业级电池或充电电池可保证在外部掉电情况下时钟使用寿命为5~8年时间;内部具备电源切换电路,当芯片检测到主电源VDD掉到电池电压以下,芯片会自动转为由备电电池供电。 SD2400系列内置单路定时/报警中断输出,报警中断时间最长可设至100年;内置频率中断输出和倒计时中断输出。 SD2400系列采用了多种提高芯片可靠性的技术,可满足对实时时钟芯片的各种需要,是在选用高精度实时时钟时的理想选择。 该模块采用Gadgeteer接口,同时很好的兼容Arduino(UNO、MegaDue等)和Maple系列控制板,也可与其他微控制器协同使用。 SD2405AL实时时钟模块特性: 低功耗: 1.0μA 典型值(时钟电路部分,Ta=25℃)。 工作电压:3.3V~5.5V,工作温度:民用级0℃~70℃,工业级-40℃~85℃。 标准IIC总线接口方式, 时钟电路最高速度400KHZ(4.5V~5.5V)。 年、月、日、星期、时、分、秒的BCD码输入/输出,并可通过独立的地址访问各时间寄存器 闰年自动调整功能(从2000年~2099年)。 可选择12/24小时制式. 内置年、月、日、星期、时、分、秒共7字节的报警数据寄存器及1字节的报警允许寄存器。 内置12字节通用SRAM寄存器可用于存储用户的一般数据。 三种中断均可选择从INT脚输出,并具有两个中断标志位. 可设定并自动重置的单路报警中断功能(时间范围最长设至100年),年、月、日、星期、时、分、秒报警共有96种组合方式,并有单事件报警和周期性报警两种中断输出模式. 周期性频率中断输出:从32768Hz~1/16Hz……1秒共十五种方波脉冲. 自动重置的8位倒计时定时器,可选的4种时钟源(4096HZ、64HZ、1HZ、1/60HZ)。 内置晶振,出厂前已对时钟进行校准,时钟精度为±5ppm(在25℃±1℃下),即年误差小于2.5 分钟。 内置时钟精度数字调整功能,可通过程序来调整走时的快慢。用户采用外置或内置的温度传感器,设定适应温度变化的调整值,可实现在宽温范围内高精度的计时功能(在-10℃~50℃小于5 ppm, 在-40℃~85℃小于10ppm)。 内置备电自动切换功能 ,芯片依据不同的电压自动从VDD切换到VBAT或从VBAT切换到VDD。 在VBAT模式下,芯片具有中断输出允许或禁止的功能,可满足在备用电池供电时输出中断的需要。 内置的充电电池及充电电路,累计电池电量超过550mAh,电池使用寿命为5~8年时间;内置的一次性民用级电池使用寿命为3~5年,一次性工业级电池使用寿命为5~8年时间。 内置的16kbit~256kbit非易失性SRAM(C/D/E型),其读写次数为100亿次,且内部写延时小于300ns。 内置的2kbit~256kbitE2PROM(F/B/C/D/E型),其擦写次数100万次 内置IIC总线0.5秒自动复位功能(从Start命令开始计时),保证时钟数据的有效性及可靠性,避免总线挂死问题。 内置三个时钟数据写保护位, 避免对数据的误写操作,可更好地保护时钟数据。 内置VBAT模式IIC总线通信禁止功能,从而避免在电池供电时CPU对时钟操作所消耗的电池电量,也可避免在主电源上、下电的过程中因CPU的I/O端口所输出的不受控的杂波信号对时钟芯片的误写操作,进一步提高时钟芯片的可靠性。 内置上电复位电路及指示位;内置电源稳压,内部计时电压可低至1.5V。 芯片管脚抗静电(ESD)>4KV。 外形尺寸:36x31x14mm 实物购买链接:https://item.taobao.com/item.htm?spm=2013.1.20141001.2.LgLOhp&id=17280765860&scm=1007.10115.36023.100200300000000&pvid=5ade1258-3a58-432a-90dd-c2c12ae31961&idnum=0

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

疯狂的蕉尼基

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值