IIC(inter-integrated Circuit集成电路总线)总线支持设备之间的短距离通信,用于处理器和一些外围设备之间的接口,它需要两根信号线来完成信息交换,它是由数据线SDA和时钟线SCL构成的串行总线,可发送和接收数据。常见的外围设备如温湿度传感器,RTC模块、RFID等。IIC是半双工通信方式。
所有接到I2C总线设备上的串行数据SDA都接到总线的SDA上,各设备的时钟线SCL接到总线的SCL上。I2C总线上的每个设备都自己一个唯一的地址,来确保不同设备之间访问的准确性。




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

SDA线上的数据在SCL时钟“高”期间必须是稳定的,只有当SCL线上的时钟信号为低时,数据线上的“高”或“低”状态才可以改变。输出到SDA线上的每个字节必须是8位,数据传送时,先传送最高位(MSB),每一个被传送的字节后面都必须跟随一位应答位(即一帧共有9位)。
当一个字节按数据位从高位到低位的顺序传输完后,紧接着从设备将拉低SDA线,回传给主设备一个应答位ACK, 此时才认为一个字节真正的被传输完成 ,如果一段时间内没有收到从机的应答信号,则自动认为从机已正确接收到数据。


IIC通信原理参考
#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{ /*中断允许位*/
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 char 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);
SD2405_DateTypedef sd2405_read_time(void);
void sd2405_test(void);
#endif
#include "t-sd2405.h"
#include "snps-i2c.h"
#include "board-snps-i2c.h"
/******************FUNCTION******************/
unsigned int _sd2405_read(unsigned int addr);
void _sd2405_write(unsigned int addr,unsigned char val);
/*******************DEFINE*******************/
/*
BIT7 BIT6 BIT5 BIT4 BIT3 BIT2 BIT1 BIT0
0 1 1 0 0 1 0 R/W
R:1
W:0
*/
#define SD2405_RTC_ADDRESS 0x64
#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))
void _sd2405_reset(void)
{
unsigned int 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 int 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 int _sd2405_read(unsigned int addr)
{
unsigned char val = 0;
snps_i2c_read(SD2405_IIC_ID, addr, &val, 1, SD2405_RTC_ADDRESS, 2);
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, 2);
}
SD2405_DateTypedef _sd2405_build_time(u32 year, u32 mon, u32 day,
u32 hour, u32 min, u32 sec, u32 week)
{
SD2405_DateTypedef rtc_time = {0};
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_init(void)
{
sd2405_pr(">>> %s\n", __func__);
snps_i2c_pin_init(SD2405_IIC_ID); //io复用
snps_i2c_init(SD2405_IIC_ID, 100000, 0, 0); //iic初始化
//reset
_sd2405_reset();
//init a time struct
SD2405_DateTypedef load_time = _sd2405_build_time(2022, 2, 2, 2, 30, 10, 800);
sd2405_write_time(&load_time);
}
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;
}
SD2405_DateTypedef sd2405_read_time(void)
{
SD2405_DateTypedef read_time = {0};
read_time.year = BCD2UChar(_sd2405_read(SD2405_ADDR_YEAR));
read_time.month = BCD2UChar(_sd2405_read(SD2405_ADDR_MONTH));
read_time.day = BCD2UChar(_sd2405_read(SD2405_ADDR_DAY));
read_time.hour = BCD2UChar(_sd2405_read(SD2405_ADDR_HOUR));
read_time.minute = BCD2UChar(_sd2405_read(SD2405_ADDR_MINUTE));
read_time.second = BCD2UChar(_sd2405_read(SD2405_ADDR_SECOND));
read_time.week = BCD2UChar(_sd2405_read(SD2405_ADDR_WEEK));
return read_time;
}
void sd2405_print_curtime(void)
{
SD2405_DateTypedef curr_time = sd2405_read_time();
sd2405_pr("\ttime: %d-%d-%d %d:%d:%d\t week:%d\n", curr_time.year,
curr_time.month, curr_time.day, curr_time.hour,
curr_time.minute, curr_time.second, curr_time.week);
}
/*建议只使用一种闹钟,TODO:组合闹钟*/
void as2405_set_alarm_enable(ENUM_ALARM_ENABLE_TYPE int_type)
{
}
void sd2405_set_alarm(SD2405_DateTypedef* date)
{
}
void sd2405_test(void)
{
vsi_printf("Testing sd2405 iic rtc.\n");
vsi_printf("input 0:init 1:read 2: while test\n");
u8 c = vsi_getc();
if('0' == c)
vsi_printf("sd2405 init...\n");
//sd2405_init();
else if('1' == c){
vsi_printf("sd2405 print time.\n");
//sd2405_print_curtime();
}
else if('2' == c){
while(1)
{
vsi_printf("sd2405 while test...\n");
//sd2405_print_curtime();
delay(60);
}
}
else
{
vsi_printf("errors\n");
}
}