经过反复调试,今天总算成功驱动了DS1307日历模块,可以对日历模块进行读写操作了。
DS1307模块是通过I2C进行通讯的,开始准备使用硬件I2C进行驱动的,但尝试了多次均未成功,不得已改用软件驱动,但软件驱动也不顺利,前后花了一周多的时间来调试,为此还发帖求助。
软件I2C的代码是从其他项目里移植过来的,首先就是通过逻辑学分析仪抓取的时序来调整时钟的延时,这一步比较容易,时钟的翻转为人微秒左右:
时钟频率调整好了,但器件没有响应,得不到ACK:
初步分析可能是数据引脚SDA的方向设置问题,一连几天反复修改引脚方向设置的代码都不见效果,更换其他引脚也无济于事,于是便暂停了I2C通讯的测试,先测试按键的轮询方式和中断方式。今天再恢复测试,发现器件竟然有回应了,基本上能够读取到数据:
但数据引脚好象存在干扰,出现异常跳变现象,下图中黄色圈内就是,原因尚不得知:
这些不规则的跳变现象尚未找出规律,还有待于进一步调试解决:
跳变有时会引起误读,象下面的时序图,跳变产生了一个STOP信号:
但不管如果,总算是基本上读取到的数据,还需要继续调试。下面是调试过程的照片:
这是显示屏的特定镜头:
这是测试效果的动画:
这是SI2C的头文件:
#ifndef I2C_H
#define I2C_H
#include "gd32f30x.h"
/*************************** 宏定义 *****************************/
#define SCL_PIN GPIO_PIN_12
#define SCL_GPIO_PORT GPIOD
#define SCL_GPIO_CLK RCU_GPIOD
#define SDA_PIN GPIO_PIN_14
#define SDA_GPIO_PORT GPIOD
#define SDA_GPIO_CLK RCU_GPIOD
#define SCL_1() {gpio_bit_set(SCL_GPIO_PORT, SCL_PIN);} //写I2C时钟端口
#define SCL_0() {gpio_bit_reset(SCL_GPIO_PORT, SCL_PIN);}
#define SDA_1() {gpio_bit_set(SDA_GPIO_PORT, SDA_PIN);} //写I2C数据端口
#define SDA_0() {gpio_bit_reset(SDA_GPIO_PORT, SDA_PIN);}
#define SDA_X() gpio_input_bit_get(SDA_GPIO_PORT, SDA_PIN)//读I2C数据端口状态
//#define SDA_OUT() {gpio_init(SDA_GPIO_PORT, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, SDA_PIN);}
//#define SDA_IN() {gpio_init(SDA_GPIO_PORT, GPIO_MODE_IPU, GPIO_OSPEED_50MHZ, SDA_PIN);}
#define SDA_OUT() gpio_init(SDA_GPIO_PORT, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, SDA_PIN) //SDA推挽输出模式
#define SDA_IN() gpio_init(SDA_GPIO_PORT, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, SDA_PIN); //SDA浮空输入模式
/***************************函数声明*****************************/
void delay_us(uint8_t us);
void SI2C_Init(void);
uint8_t SI2C_8bitByteWrite(uint8_t I2C_addr,uint8_t addr,uint8_t data); //向指定的器件及地址写入1个字节
uint8_t SI2C_8bitByteRead(uint8_t I2C_addr,uint8_t addr); //从指定的器件及地址读取1个字节
uint8_t SI2C_8bitBuffWrite(uint8_t I2C_addr,uint8_t addr,uint8_t size,uint8_t *buf); //向指定的器件及地址开始写入多个字节
uint8_t SI2C_8bitBuffRead(uint8_t I2C_addr,uint8_t addr,uint8_t size,uint8_t *buf); //从指定的器件及地址开始读出多个字节
uint8_t SI2C_16bitBuffWrite(uint8_t I2C_addr,uint16_t addr,uint8_t size,uint8_t *buf);//写多个数据到16位地址的I2C设备
uint8_t SI2C_16bitBuffRead(uint8_t I2C_addr,uint16_t addr,uint8_t size,uint8_t *buf); //读取16位地址的多个数据
#endif /* I2C_H */
这是C文件,其中双字节地址的读写尚未测试:
#include "si2c.h"
extern uint8_t I2C_Buff[16];
void SI2C_Start(void); //开始SI2C通讯
void SI2C_Stop(void); //停止SI2C通讯
void SI2C_Send(uint8_t dat); //向SI2C总线发送一个字节
uint8_t SI2C_Receive(void); //从SI2C总结接收一个字节
void SI2CDoAck(void); //发出应答信号
void SI2CNoAck(void); //发出无应答信号
uint8_t SI2CIsAck(void); //检测从机应答信号
/***************************************************
函数功能:微秒延时
入口参数:延时的微秒数
***************************************************/
void delay_us(uint8_t us)
{
uint8_t x,y;
for(x=us;x>0;x--)
for(y=22;y>0;y--); //120MHz=22,
}
/******************************************************************************************************************************************
* 函数名称: I2C_Init()
* 功能说明: I2C_Init
* 输 入: 无
* 输 出: 无
******************************************************************************************************************************************/
void SI2C_Init(void)
{
rcu_periph_clock_enable(SCL_GPIO_CLK);
gpio_init(SCL_GPIO_PORT, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, SCL_PIN | SDA_PIN); // SCL推挽输出模式
}
/******************************************************************************************************************************************
* 函数名称: I2C_Start()
* 功能说明: 产生I2C传输的Start信号
* 输 入: 无
* 输 出: 无
******************************************************************************************************************************************/
void SI2C_Start(void)
{
SDA_OUT(); //SDA输出
SDA_1();
SCL_1(); //scl = 1;
delay_us(4);
SDA_0(); //sda = 0; scl为高时sda的下降沿表示“起始”
delay_us(4);
SCL_0(); //scl = 0;钳住I2C总线,准备发送或接收数据 START:when CLK is high,DATA change form high to low
}
/******************************************************************************************************************************************
* 函数名称: I2C_Stop()
* 功能说明: 产生I2C传输的Stop信号
* 输 入: 无
* 输 出: 无
******************************************************************************************************************************************/
void SI2C_Stop(void)
{
SDA_OUT(); // SDA写
SCL_0(); // scl = 0;
SDA_0(); // STOP:when CLK is high DATA change form low to high
delay_us(4);
SCL_1(); // scl = 1;
delay_us(4);
SDA_1(); // sda = 1; sclk为高时sdat的上升沿表示“停止”
}
/******************************************************************************************************************************************
* 函数名称: I2C_Send()
* 功能说明: 向IIC总线发送一个字节的数据
* 输 入: byte dat 要发送的数据
* 输 出: 无
******************************************************************************************************************************************/
void SI2C_Send(uint8_t dat)
{
uint8_t i = 8;
SDA_OUT();
while(i--)
{
SCL_0(); //拉低时钟开始数据传输
delay_us(2);
if(dat&0x80){
SDA_1();
}else{
SDA_0();
}
dat<<=1;
delay_us(3);
SCL_1(); //拉高时钟等待从设备读取数据
delay_us(5);
}
SCL_0();
}
/******************************************************************************************************************************************
* 函数名称: I2C_Receive()
* 功能说明: 从IIC总线接收一个字节的数据
* 输 入: 无
* 输 出: byte 从IIC总线上接收到得数据
* 注意事项: 无
******************************************************************************************************************************************/
uint8_t SI2C_Receive(void)
{
uint8_t i = 8,dat;
SDA_OUT();
SDA_1();
SDA_IN(); //设置为输入
while(i--)
{
dat<<=1;
SCL_0();
delay_us(5);
SCL_1();
delay_us(4);
if(1 == SDA_X())
dat|=0x01;
}
SCL_0();
return dat;
}
/******************************************************************************************************************************************
* 函数名称: I2CDoAck()
* 功能说明: 在应答位位置产生应答,从而继续连续传输
* 输 入: 无
* 输 出: 无
******************************************************************************************************************************************/
void SI2CDoAck(void)
{
SCL_0();
SDA_OUT();
SDA_0(); //sda = 0; /拉低数据线,即给于应答
delay_us(3);
SCL_1(); //scl = 1;
delay_us(6);
SCL_0(); //scl = 0;
}
/******************************************************************************************************************************************
* 函数名称: I2CNoAck()
* 功能说明: 在应答位位置不产生应答,从而终止连续传输
* 输 入: 无
* 输 出: 无
******************************************************************************************************************************************/
void SI2CNoAck(void)
{
SCL_0();
SDA_OUT();
SDA_1(); // sda = 1; 不拉低数据线,即不给于应答
delay_us(3);
SCL_1(); // scl = 1;
delay_us(6);
SCL_0(); // scl = 0;
}
/******************************************************************************************************************************************
* 函数名称: I2CIsAck()
* 功能说明: 检测从机应答位
* 输 入: 无
* 输 出: uint8_t 0=ACK_OK 从机产生了应答;1=ACK_NO 从机没有产生应答
******************************************************************************************************************************************/
uint8_t SI2CIsAck(void)
{
uint8_t i;
SDA_OUT();
SDA_1(); // sda = 1; 释放数据线
delay_us(3);
SDA_IN();
SCL_1(); // scl = 1;
delay_us(3);
while(1 == SDA_X()){
i++;
if(i>250){
SI2C_Stop(); //数据线未被拉低,即未收到应答
return 1;
}
}
SCL_0();
return 0;
}
/**********************************************************************************************
* 函数名称: I2C_8bitByteWrite()
* 功能说明: 向I2C器件的地址addr开始写入一个字节的数据
* 输 入: uint8_t I2C_addr 器件地址
* uint8_t addr 写入数据的地址
* uint8_t data 要写入的数据
* 输 出: uint8_t 0=成功向器件写入数据 大于1=向器件写入数据过程中出现错误
**********************************************************************************************/
uint8_t SI2C_8bitByteWrite(uint8_t I2C_addr,uint8_t addr,uint8_t data)
{
SI2C_Start(); //产生起始信号
SI2C_Send(I2C_addr|0);//发送器件地址及读写位,0表示写
if(SI2CIsAck()) //检测器件是否有响应
{
SI2C_Stop(); //产生停止信号
return 1;
}
SI2C_Send(addr); //发送数据要写入的地址
if(SI2CIsAck()) //检测器件是否有响应
{
SI2C_Stop(); //产生停止信号
return 2;
}
SI2C_Send(data); //写入数据
if(SI2CIsAck()) //检测器件是否有响应
{
SI2C_Stop(); //产生停止信号
return 3;
}
SI2C_Stop(); //产生停止信号
return 0;
}
/**********************************************************************************************
* 函数名称: I2C_8bitByteRead()
* 功能说明: 从I2C器件的地址addr读取1字节数据
* 输 入: uint8_t I2C_addr 器件地址
* uint8_t addr 读取数据的地址
* 输 出: 读取的数据
**********************************************************************************************/
uint8_t SI2C_8bitByteRead(uint8_t I2C_addr,uint8_t addr)
{
uint8_t data;
SI2C_Start(); //产生起始信号
SI2C_Send(I2C_addr); //发送器件地址及读写位,0表示写
if(SI2CIsAck()) //检测器件是否有响应
{
SI2C_Stop(); //产生停止信号
return 2;
}
SI2C_Send(addr); //发送读取数据的起始地址
if(SI2CIsAck()) //检测器件是否有响应
{
SI2C_Stop(); //产生停止信号
return 3;
}
SI2C_Start(); //产生Repeated Start
SI2C_Send(I2C_addr|1);//发送器件地址及读写位,1表示读
if(SI2CIsAck()) //检测器件是否有响应
{
SI2C_Stop(); //产生停止信号
return 4;
}
data = SI2C_Receive();//从addr处读取1个字节的数据
SI2CNoAck(); //器件要求必须使用NOAck来结束数据读取
SI2C_Stop(); //产生停止信号
return data;
}
/**********************************************************************************************
* 函数名称: I2C_8bitBuffWrite()
* 功能说明: 向I2C器件的地址addr开始写入size个字节的数据,将要写入的数据存储在全局变量I2C_Buff中
* 输 入: uint8_t I2C_addr 器件地址
* uint8_t addr 写入数据开始的地址
* uint8_t size 要设置的数据个数(1~8)
* 输 出: uint8_t 0=成功向器件写入数据 大于1=向器件写入数据过程中出现错误
**********************************************************************************************/
uint8_t SI2C_8bitBuffWrite(uint8_t I2C_addr,uint8_t addr,uint8_t size,uint8_t *buf)
{
uint8_t i = 0;
SI2C_Start(); //产生起始信号
SI2C_Send(I2C_addr|0);//发送器件地址及读写位,0表示写
if(SI2CIsAck()) //检测器件是否有响应
{
SI2C_Stop(); //产生停止信号
return 1;
}
SI2C_Send(addr); //发送数据要写入的地址
if(SI2CIsAck()) //检测器件是否有响应
{
SI2C_Stop(); //产生停止信号
return 2;
}
for(i=0; i<size; i++)
{
SI2C_Send(buf[i]);
if(SI2CIsAck()) //检测器件是否有响应
{
SI2C_Stop(); //产生停止信号
return 3;
}
}
SI2C_Stop(); //产生停止信号
return 0;
}
/******************************************************************************************
* 函数名称: I2C_8bitBuffRead()
* 功能说明: 从指定的I2C器件地址addr开始获取size个字节的数据,获取的数据存储在全局变量I2C_Buff中
* 输 入: uint8_t I2C_addr I2C器件地址
* uint8_t addr 获取数据从addr开始
* uint8_t size 要获取的数据个数(1~8)
* uint8_t *buff 数据缓存
* 输 出: 从器件获取数据(数组指针)
******************************************************************************************/
uint8_t SI2C_8bitBuffRead(uint8_t I2C_addr,uint8_t addr,uint8_t size,uint8_t *buf)
{
uint8_t i = 0;
SI2C_Start(); //产生起始信号
SI2C_Send(I2C_addr); //发送器件地址及读写位,0表示写
if(SI2CIsAck()) //检测器件是否有响应
{
SI2C_Stop(); //产生停止信号
return 2;
}
SI2C_Send(addr); //发送读取数据的起始地址
if(SI2CIsAck()) //检测器件是否有响应
{
SI2C_Stop(); //产生停止信号
return 3;
}
SI2C_Start(); //产生Repeated Start
SI2C_Send(I2C_addr|1);//发送器件地址及读写位,1表示读
if(SI2CIsAck()) //检测器件是否有响应
{
SI2C_Stop(); //产生停止信号
return 4;
}
for(i=0;i<size;i++) //从addr处开始读取size个字节的数据
{
buf[i] = SI2C_Receive();
SI2CDoAck();
}
SI2CNoAck(); //器件要求必须使用NOAck来结束数据读取
SI2C_Stop(); //产生停止信号
return *buf;
}
/**********************************************************************************************
* 函数名称: I2C_16bitBuffWrite()
* 功能说明: 写多个数据到16位地址的I2C设备
* 输 入: uint8_t I2C_addr 器件地址
* uint8_t addr 写入数据开始的地址
* uint8_t size 要写入的数据个数(1~8)
* uint8_t *buf 要写入的数据
* 输 出: uint8_t 0=成功向器件写入数据 大于1=向器件写入数据过程中出现错误
**********************************************************************************************/
uint8_t SI2C_16bitBuffWrite(uint8_t I2C_addr,uint16_t addr,uint8_t size,uint8_t *buf)
{
uint8_t i = 0;
SI2C_Start(); //产生起始信号
SI2C_Send(I2C_addr|0);//发送器件地址及读写位,0表示写
if(SI2CIsAck()) //检测器件是否有响应
{
SI2C_Stop(); //产生停止信号
return 1;
}
SI2C_Send(addr>>8); //发送读取数据的起始地址高8位
SI2CIsAck(); //等待应答
SI2C_Send(addr%256); //发送低8位地址
SI2CIsAck(); //等待应答
for(i=0;i<size;i++)
{
SI2C_Send(buf[i]);
if(SI2CIsAck()) //检测器件是否有响应
{
SI2C_Stop(); //产生停止信号
return 3;
}
}
SI2C_Stop(); //产生停止信号
return 0;
}
/******************************************************************************************
* 函数名称: I2C_16bitBuffRead()
* 功能说明: 从指定的16位I2C器件地址addr开始获取size个字节的数据,返回数组变量指针
* 输 入: uint8_t I2C_addr I2C器件地址
* uint8_t addr 获取数据从addr开始
* uint8_t size 要获取的数据个数(1~8)
* uint8_t *buff 数据缓存
* 输 出: 从器件获取数据(数组指针)
******************************************************************************************/
uint8_t SI2C_16bitBuffRead(uint8_t I2C_addr,uint16_t addr,uint8_t size,uint8_t *buf)
{
uint8_t i = 0;
SI2C_Start(); //产生起始信号
SI2C_Send(I2C_addr); //发送器件地址及读写位,0表示写
if(SI2CIsAck()) //检测器件是否有响应
{
SI2C_Stop(); //产生停止信号
return 2;
}
SI2C_Send(addr>>8); //发送读取数据的起始地址高8位
SI2CIsAck(); //等待应答
SI2C_Send(addr%256); //发送低8位地址
SI2CIsAck(); //等待应答
SI2C_Start(); //产生Repeated Start
SI2C_Send(I2C_addr|1);//送器件地址及读写位,1表示读
if(SI2CIsAck()) //检测器件是否有响应
{
SI2C_Stop(); //产生停止信号
return 4;
}
for(i=0;i<size;i++) //从addr处开始读取size个字节的数据
{
buf[i] = SI2C_Receive();
SI2CDoAck();
}
SI2CNoAck(); //器件要求必须使用NOAck来结束数据读取
SI2C_Stop(); //产生停止信号
return *buf;
}
楼主这IIC通信之路真是非常坎坷啊,不过能看出楼主的毅力!!!
引用: w494143467 发表于 2020-11-20 19:41 楼主这IIC通信之路真是非常坎坷啊,不过能看出楼主的毅力!!!
关键问题是还没有找到不成功的原因。
引用: hujj 发表于 2020-11-21 08:49 关键问题是还没有找到不成功的原因。
可惜你没有两块板子,要不就能知道是不是板子的问题了。