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 nowbreak;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 structSD2405_DateTypedef load_time = _sd2405_build_time(2022, 2, 2, 2, 30, 0, 2);sd2405_write_time(&load_time);//testsd2405_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 不支持mallocSD2405_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 nowbreak;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;elsehour_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);elsetmp = 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 structSD2405_DateTypedef load_time = _sd2405_build_time(2022, 2, 2, 2, 30, 0, 0);sd2405_write_time(&load_time);//testsd2405_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 不支持mallocSD2405_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");}
}

ARM+SD2405 IIC_RTC驱动编写及IIC通讯协议相关推荐

  1. 基于51单片机的OLED驱动方式(iic通讯方式)

    基于51单片机的OLED驱动方式(iic通讯方式) 前言: 本人从事硬件开发,自学软件,因为发现在学习过程中,有很多问题对于没有项目实战经验的新手来讲太难解决了,可以说基本上是无从下手.现将自己学习过 ...

  2. 示波器观察IIC通讯协议-STM32F4读写24C08EEPROM时序图-新人首更

    示波器观察时序不是因为好奇,是因为遇到了问题 STM32F4探索者开发板板载24C02EEPROM,自制了一块开发板,需要存储容量更大的24C08A芯片,焊接后发现开发板上能成功运行的程序,在自己的板 ...

  3. 【蓝桥杯单片机】IIC通讯协议与EEPROM(AT24C02)(官方驱动源码改写)

    实验开发板为CT107D蓝桥官方板,编译环境为MDK5 由蓝桥官方驱动改写 AT24C02: AT24C02为地址可编程器件(CT107D可编程位均为0),因此可于总线上挂载多个相同的器件. 最后一位 ...

  4. php如何编写通信协议,定制通讯协议

    ## 如何定制协议 实际上制定自己的协议是比较简单的事情.简单的协议一般包含两部分: * 区分数据边界的标识 * 数据格式定义 ## 一个例子 ### 协议定义 这里假设区分数据边界的标识为换行符&q ...

  5. STM32使用IIC总线通讯协议在OLED屏幕上显示字符串、汉字、图像(硬件IIC)

    参考:基于STM32-Oled(IIC)的使用 作者:奋斗的小殷 发布时间: 2021-05-07 13:09:26 网址:https://blog.csdn.net/boybs/article/de ...

  6. STM32之IIC通讯-SHT30温湿度采集

    一.实验目的:通过单片机普通IO口,模拟IIC时序,掌握IIC通讯协议. 二.实验用到的主控芯片:STM32F103RCT6,IIC通讯引脚IIC_SDA:PC11,IIC_SCL:PC12 三.SH ...

  7. php连接plc,PLC 几种常见的连接口和通讯协议

    四.ProfiBus 通讯作为众多现场总线家族的成员之一ProfiBus是在欧洲 工业界得到最普遍应用的一个现场总线规范,也是目前国际上通用的现场总线规范之一.ProfiBus是属 于单元级.现场级的 ...

  8. ARM(IMX6U)裸机汇编LED驱动实验——驱动编写、编译链接起始地址、烧写bin文件到SD卡中并运行

    参考:Linux之ARM(IMX6U)裸机汇编LED驱动实验–驱动编写 作者:一只青木呀 发布时间: 2020-08-07 09:13:48 网址:https://blog.csdn.net/weix ...

  9. Linux下IIC驱动编写,介绍IIC子系统框架的使用

    一.IIC协议介绍 说起IIC,搞单片机,嵌入式的那肯定是接触的比较多的.串口.IIC.SPI这3个协议在单片机阶段应该是用比较多的,很多的外设模块,芯片都是串口.IIC.SPI等协议与主控芯片进行通 ...

最新文章

  1. 自定义注解妙用,一行代码搞定用户操作日志记录,你学会了吗?
  2. AngularJS指令封装高德地图组件
  3. exit(0) exit(1) return() 3个的区别
  4. c语言printf函数很长时间,C语言学习之printf()函数特别注意事项
  5. List 与 Map的常用方法
  6. struts1:Struts的中央控制器
  7. 免费破解版Xshell和Xftp
  8. Android学习资料整理
  9. 各大搜索引擎蜘蛛名称大全
  10. Linux下配置DNS(Domain Name System)服务器
  11. import.os 文件操作
  12. MySQL 运维 - 从零开始学习 | 超详细
  13. DENO 1.10发行说明
  14. 2022卡塔尔世界杯引爆全球,跨境电商如何做好选品和营销?
  15. 非常有用的sql脚本
  16. A4. MTK开机流程
  17. 计算机应用基础网络统考操作,全国高校网络教育统考《计算机应用基础》操作题...
  18. 私有专辑 java,这是什么“专辑歌手”标签iTunes使用?任何方式使用java设置?
  19. “骚气”的开源壁纸项目
  20. Linux - 常见端口和服务的对照和解释

热门文章

  1. 我的python程序_我试着运行我的python程序,但当我运行它时什么也没有发生
  2. Shiro框架学习笔记、整合Springboot、redis缓存
  3. Android之使用HttpPost提交数据到服务器(Android手机客户端和后台服务器交互)
  4. 2021年服创国赛参赛小结
  5. 渗透测试php过程,利用骑士cms的一次纠结的渗透测试过程(两个潜在
  6. Excel技巧 - 长数字串如何筛选重复项
  7. 软件架构详解(附图)
  8. Linux下tmpfs与ramfs的区别
  9. 【海思篇】【Hi3516DV300】二、使用串口烧写映像
  10. 神舟战神g8r9和g9r9区别对比评测选哪个好