当前位置:网站首页>ARM+SD2405 IIC_RTC驱动编写及IIC通讯协议
ARM+SD2405 IIC_RTC驱动编写及IIC通讯协议
2022-07-21 22:46:00 【疯狂的蕉尼基】
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{
/*中断允许位*/
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");
}
}
边栏推荐
- el-input 失去焦点事件
- Acl-ijcai-sigir top conference paper report meeting (AIS 2022) Note 1: recommendation system
- How to select the type of ankerui intelligent miniature circuit breaker?
- How does am5se-is anti islanding protection device solve the impact in the process of distributed photovoltaic power generation?
- MATLAB数字图像处理 大作业:人脸表情识别
- Ten year structure five year Life-04 the first turning point of work
- ECCV 2022 | generalized long tail classification based on invariant feature learning
- 2022.07.18
- How to solve the problem that PIP command display is not internal or external command in Anaconda
- priority_ Queue delete
猜你喜欢
ACL-IJCAI-SIGIR顶级会议论文报告会(AIS 2022)笔记2:分析与可解释性
C # read and write data to PLC in conjunction with kepserver
Huawei wireless device configuration attack detection function
[建议收藏]缓存雪崩的处理办法
ROS entry level tutorial
超干货!彻底搞懂单工、半双工、全双工的区别与联系
智能仪器仪表行业数字化供应链管理系统:加速企业智慧供应链平台转型
携手HMS Core分析服务,以数据助力游戏高效增长
4种 Redis 集群方案及优缺点对比
并发程序的噩梦——数据竞争
随机推荐
百度飞桨EasyDL X 韦士肯:看轴承质检如何装上“AI之眼”
[pan micro ecology] compiling_ jsp/_ login/_ upgrademessage...
如何在页面中添加地图
AM5SE-IS防孤岛保护装置如何解决分布式光伏发电过程中的影响?
4种 Redis 集群方案及优缺点对比
[建议收藏]缓存雪崩的处理办法
ES6 common syntax
[ kitex 源码解读 ] Kitex 扩展性设计思路
为什么RISC-V架构前进势头铆足
2022.07.18
Differential privacy budget optimization method based on deep learning in Internet of things environment
mapState
李彦宏:无人驾驶是人类创造价值最大的创新
C # read and write data to PLC in conjunction with kepserver
ACL-IJCAI-SIGIR顶级会议论文报告会(AIS 2022)笔记1:推荐系统
:style在项目中的使用
The four trends and three key points of the data platform construction mode are summarized
Force deduction solution summary 814 binary tree pruning
2022.07.18
2022 Shandong Health Exhibition, health industry exhibition, DJK China Health Expo, sleep health exhibition