5024|11

932

帖子

3

TA的资源

纯净的硅(中级)

楼主
 

【CH579M-R1】+求助:模拟I2C读取数据不成功(已解决) [复制链接]

 
  本帖最后由 hujj 于 2020-9-22 15:39 编辑

    继LCD5110显示屏驱动成功后,上周开始试图驱动DS1307日历模块,下图是从淘宝购买的模块:

 

    这个模块是通过I2C总线进行通讯的,模块上还附带一个24C32芯片,同时还留有温湿度传感器安装位置。下面是我测试过程的图片:

    查找CH579的资料,没有找到硬件I2C的叙述或范例,于是只好试图设置软件I2C,因为LCD显示屏与I2C总线上的设备不会同时使用,所以我用利用了驱动LCD5110的CLK和DIN引脚,重复用于I2C的SCK和SDA,i2c.h文件如下:


#ifndef I2C_H
#define I2C_H

#include "CH57x_common.h"
#include "CH57x_gpio.h"

/*************************** 宏定义 *****************************/

#define I2C_SCL           GPIO_Pin_5      //时钟信号脚⑤
#define I2C_SDA           GPIO_Pin_3      //数据输入④

#define SCL_1() GPIOB_SetBits(I2C_SCL)    //写I2C时钟端口
#define SCL_0() GPIOB_ResetBits(I2C_SCL)

#define SDA_1() GPIOB_SetBits(I2C_SDA)    //写I2C数据端口
#define SDA_0() GPIOB_ResetBits(I2C_SDA)
#define SDA_X() GPIOB_ReadPortPin(GPIO_Pin_3)//读I2C数据端口状态

#define SDA_OUT() GPIOB_ModeCfg(I2C_SDA, GPIO_ModeOut_PP_5mA) //SDA推挽输出模式 
#define SDA_IN()  GPIOB_ModeCfg(I2C_SDA, GPIO_ModeIN_PU);     //SDA上拉输入模式


/***************************函数声明*****************************/

void delay_us(uint8_t us);
void SI2C_DAT_Dir(uint8_t dir); //设置SDA的读(1)写(0)方向
void SI2C_Start(void);          //开始I2C通讯
void SI2C_Stop(void);           //停止I2C通讯
void SI2C_Send(uint8_t dat);    //向I2C总线发送一个字节
uint8_t SI2C_Receive(void);     //从I2C总结接收一个字节
void SI2CDoAck(void);           //发出应答信号
void SI2CNoAck(void);           //发出无应答信号
uint8_t SI2CIsAck(void);        //检测从机应答信号

#endif  /* I2C_H */

 

    i2c.c代码如下:



#include "CH57x_common.h"
#include "i2c.h"
//#include <stdio.h>

/***************************************************
函数功能:微秒延时
入口参数:延时的微秒数
***************************************************/
void delay_us(uint8_t us)  
{
    uint8_t x,y;
    for(x=us;x>0;x--)    
    for(y=4;y>0;y--);
}


/****************************************************************************************************************************************** 
* 函数名称: SI2C_DAT_Dir()
* 功能说明:	设置I2C_SDA引脚的读或写
* 输    入: 0=读入,1=写 
* 输    出: 无
******************************************************************************************************************************************/
void SI2C_DAT_Dir(uint8_t dir)
{
if(dir)
	GPIOB_ModeCfg(I2C_SDA, GPIO_ModeOut_PP_5mA); //SDA推挽输出模式 
else
	GPIOB_ModeCfg(I2C_SDA, GPIO_ModeIN_PU);      //SDA上拉输入模式
}


/****************************************************************************************************************************************** 
* 函数名称: I2C_Start()
* 功能说明:	产生I2C传输的Start信号
* 输    入: 无 
* 输    出: 无
******************************************************************************************************************************************/
void SI2C_Start(void)
{
    SI2C_DAT_Dir(1);   //SDA输出
    SDA_1();
    SCL_1();           //scl = 1;
	delay_us(5);
	SDA_0();           //sda = 0;	scl为高时sda的下降沿表示“起始”
	delay_us(3);
	SCL_0();           //scl = 0;钳住I2C总线,准备发送或接收数据 START:when CLK is high,DATA change form high to low 
}

/****************************************************************************************************************************************** 
* 函数名称:	I2C_Stop()
* 功能说明:	产生I2C传输的Stop信号
* 输    入: 无 
* 输    出: 无
******************************************************************************************************************************************/
void SI2C_Stop(void)
{
    SI2C_DAT_Dir(1);
    SCL_0();             // scl = 0;
    SDA_0();             // STOP:when CLK is high DATA change form low to high
    delay_us(5);
    SCL_1();             // scl = 1;
    delay_us(5);	
    SDA_1();             // sda = 1;	sclk为高时sdat的上升沿表示“停止”
}


/****************************************************************************************************************************************** 
* 函数名称: I2C_Send()
* 功能说明:	向IIC总线发送一个字节的数据
* 输    入: byte dat 	要发送的数据 
* 输    出: 无
******************************************************************************************************************************************/
void SI2C_Send(uint8_t dat)
{
	uint8_t i;
	SI2C_DAT_Dir(1);
	SCL_0();               //拉低时钟开始数据传输
	for(i=0;i<8;i++)
	{
//		if(((dat&0x80)>>7) == 1) SDA_1;//准备好SDA数据
        if((dat>>(7-i))&0x01) SDA_1();
		else SDA_0();
//		dat>>=1;
		delay_us(4);
		SCL_1();                         //拉高时钟等待从设备读取数据
		delay_us(5);
		SCL_0();                         //拉低时钟准备下一位数据
//		delay_us(1);
	}
}


/****************************************************************************************************************************************** 
* 函数名称:	I2C_Receive()
* 功能说明:	从IIC总线接收一个字节的数据
* 输    入: 无
* 输    出: byte		从IIC总线上接收到得数据
* 注意事项: 无
******************************************************************************************************************************************/
uint8_t SI2C_Receive(void)
{
	uint8_t i,dat;
	SI2C_DAT_Dir(0);        //设置为输入	
	for(i=0;i<8;i++)
	{
		SCL_0();
		delay_us(5);
		SCL_1();
		dat<<=1;
        if(1 == SDA_X())
            dat|=0x01;
		delay_us(4);
	}
	return dat;

/*
	ui08 i = 0;
	byte d = 0;
	byte dat = 0;

	for(i=0;i<8;i++)
    {
		scl = 0;
		DELAY();
		sda = 1;		//本语句必须有:于IIC,是释放SDA线;于51单片机,则是由于51的IO不是真双向口,在读之前必须写0
      	DELAY();

      	scl = 1;
		DELAY();
		d = sda;
      	DELAY();

		dat |= (d<<(7-i));
    }
    return dat;
*/
} 

/****************************************************************************************************************************************** 
* 函数名称: I2CDoAck()
* 功能说明:	在应答位位置产生应答,从而继续连续传输
* 输    入: 无 
* 输    出: 无 
******************************************************************************************************************************************/
void SI2CDoAck(void)
{ 
    SCL_0();
	SI2C_DAT_Dir(1);
    SDA_0();              //sda = 0;	/拉低数据线,即给于应答
    delay_us(3);
    SCL_1();              //scl = 1;
   	delay_us(5);
    SCL_0();              //scl = 0;
}

/****************************************************************************************************************************************** 
* 函数名称: I2CNoAck()
* 功能说明:	在应答位位置不产生应答,从而终止连续传输
* 输    入: 无 
* 输    出: 无 
******************************************************************************************************************************************/
void SI2CNoAck(void)
{ 
    SCL_0();
	SI2C_DAT_Dir(1);
    SDA_1();              // sda = 1;	不拉低数据线,即不给于应答
    delay_us(3); 
    SCL_1();              // scl = 1;
    delay_us(5);
    SCL_0();              // scl = 0; 
}

/****************************************************************************************************************************************** 
* 函数名称: I2CIsAck()
* 功能说明:	检测从机应答位
* 输    入: 无 
* 输    出: uint8_t	0=ACK_OK 从机产生了应答;1=ACK_NO 从机没有产生应答
******************************************************************************************************************************************/
uint8_t SI2CIsAck(void)
{ 
    uint8_t i;
    SI2C_DAT_Dir(1);
    SDA_1();              // sda = 1; 释放数据线
    delay_us(3);
    SI2C_DAT_Dir(0);
    SCL_1();              // scl = 1;
    delay_us(3);
	while(SDA_X()){
		i++;
		if(i>250){
			SI2C_Stop();//数据线未被拉低,即未收到应答
			return 1;
		}
	}
    SCL_0();
    return 0;
}


 

    DS1307模块的数据读写及日期转换的代码如下:


#include "I2C.h"
#include "ds1307.h"
#include "lcd_5110.h"

extern uint16_t year;              //年
extern uint8_t week,month,day,hour,minute,second,DS_Buff[8];//月日时分秒


/****************************************************************************************** 
* 函数名称: DS1307_Read()
* 功能说明: 从DS1307地址addr开始获取size个字节的数据,获取的数据存储在全局变量DS_Buff中
* 输    入: uint8_t addr	获取数据从addr开始
*			uint8_t size	要获取的数据个数(1~8)
* 输    出: ui08    0=RET_OK 成功从DS1307获取数据  1=RET_ERR 从DS1307获取数据过程中出现错误
******************************************************************************************/
uint8_t DS1307_Read(uint8_t addr,uint8_t size)
{
    uint8_t i = 0;
//    SCL_1();                        //拉起时钟引脚,准备发出开始信号

    SI2C_Start();                   //产生起始信号
    SI2C_Send(DS1307_ADDR);         //发送DS1307芯片地址及读写位,0表示写
    if(1 == SI2CIsAck())            //检测DS1307是否有响应
    {
        SI2C_Stop();                //产生停止信号
		return 2;
    }
    SI2C_Send(addr);                //发送读取数据的起始地址
    if(1 == SI2CIsAck())            //检测DS1307是否有响应
    {
        SI2C_Stop();	            //产生停止信号
        return 3;
    }

    SI2C_Start();                   //产生Repeated Start
    SI2C_Send(DS1307_ADDR|1);       //发送DS1307芯片地址及读写位,1表示读
    if(1 == SI2CIsAck())            //检测DS1307是否有响应
    {
        SI2C_Stop();                //产生停止信号
        return 4;
    }

    for(i=0;i<size;i++)	            //从addr处读取size个字节的数据
    {
        DS_Buff = SI2C_Receive();
        SI2CDoAck();
    }

    SI2C_Receive();                 //DS1307要求必须使用NOAck来结束数据读取
    SI2CNoAck();                    //DS1307要求必须使用NOAck来结束数据读取

    SI2C_Stop();                    //产生停止信号
	SDA_OUT();                      //将SDA引脚恢复为输出模式
	
	return 0;
}


/********************************************************************************************** 
* 函数名称:	DS1307_Write()
* 功能说明: 向DS1307地址addr开始写入size个字节的数据,将要写入的数据存储在全局变量DS_Buff中
* 输    入: uint8_t addr	数据被写入从addr开始的地址处
*			uint8_t size	要设置的数据个数(1~8)
* 输    出: uint8_t		0=RET_OK 成功向DS1307设置数据  1=RET_ERR 向DS1307设置数据过程中出现错误
**********************************************************************************************/
uint8_t DS1307_Write(uint8_t addr,uint8_t size)
{
    uint8_t i = 0;
//    SCL_1();                        //拉起时钟引脚,准备发出开始信号
	
    SI2C_Start();                   //产生起始信号
    SI2C_Send(DS1307_ADDR|0);       //发送DS1307芯片地址及读写位,0表示写
    if(1 == SI2CIsAck())            //检测DS1307是否有响应
    {
        SI2C_Stop();                //产生停止信号
        return 1;
	}

    SI2C_Send(addr);                //发送数据要写入的地址
    if(1 == SI2CIsAck())            //检测DS1307是否有响应
    {
        SI2C_Stop();                //产生停止信号
        return 2;
    }

    for(i=0;size>0;i++,size--)
    {
        SI2C_Send(DS_Buff);
        if(1 == SI2CIsAck())        //检测DS1307是否有响应
        {
            SI2C_Stop();            //产生停止信号
            return 3;
        }
    }

    SI2C_Stop();                    //产生停止信号
	SDA_OUT();                      //将SDA引脚恢复为输出模式
	
    return 0;
}


/****************************************************************************************** 
* 函数名称:	DS1307_Init()
* 功能说明: 用当前日期(yesr,month,day,hour,minute)初始化DS1307
* 输    入: 无
* 输    出: uint8_t	0=RET_OK 初始化成功	1=RET_ERR 初始化出错
******************************************************************************************/
uint8_t DS1307_Init(void)
{
    uint8_t temp;

	temp = DS1307_Read(0,1);
	
	LCD_write_value(0,3,3,0,0,temp);//监测读取值
	
	if(temp>127){

        year = 2020;
		month = 9;
		day = 15;
		week = 3;
		hour = 18;
		minute = 10;
		
        DS_Buff[0] = 0;                  //秒
        temp = ((minute/10)<<4|(minute%10));
        DS_Buff[1] = temp;               //分
        temp = ((hour/10)<<4|(hour%10));
        DS_Buff[2] = temp;               //时

        DS_Buff[3] = week;               //星期

        temp = ((day/10)<<4|(day%10));
        DS_Buff[4] = temp;               //日
        temp = ((month/10)<<4|(month%10));
	    DS_Buff[5] = temp;               //月
        temp = ((year%100)/10<<4|(year%10));
        DS_Buff[6] = temp;               //年
	
        DS_Buff[7] = 32;                 //0010 0000 = 允许按1Hz输出方波

        return DS1307_Write(0,8);
    }
	else
		return 4;
}



/****************************************************************************************** 
* 函数名称:	DS1307_read_date()
* 功能说明: 读取DS1307日期时间数据
* 输    入: 无
* 输    出: 无
******************************************************************************************/
void DS1307_read_date(void)
{
    uint8_t info;
    info = DS1307_Read(0,7);                                   //读取前7个字节数据
//    LCD_write_value(30,3,3,0,0,info);
	
    second = ((DS_Buff[0]&0x70)>>4)*10 + (DS_Buff[0]&0x0F);//秒,屏蔽秒的第7位的标志
	minute = ((DS_Buff[1]&0x70)>>4)*10 + (DS_Buff[1]&0x0F);//分(取低7位)
	hour = ((DS_Buff[2]&0x10)>>4)*10 + (DS_Buff[2]&0x0F);  //时(取低5位)
	week = (DS_Buff[3]&0x07);                              //周(取低3位)
    day = ((DS_Buff[4]&0x30)>>4)*10 + (DS_Buff[4]&0x0F);   //日(取低6位)
    month = ((DS_Buff[5]&0x10)>>4)*10 + (DS_Buff[5]&0x0F); //月(取低5位)
	year = 2000 + (DS_Buff[6]>>4)*10 + (DS_Buff[6]&0x0F);  //年
   
	LCD_write_value(0,3,3,0,0,DS_Buff[0]);
	LCD_write_value(25,3,3,0,0,DS_Buff[1]);
	LCD_write_value(50,3,3,0,0,DS_Buff[2]);
	LCD_write_value(0,4,3,0,0,DS_Buff[3]);
	LCD_write_value(25,4,3,0,0,DS_Buff[4]);
	LCD_write_value(50,4,3,0,0,DS_Buff[5]);
}


/****************************************************************************************** 
* 函数名称:	DS1307_write_date()
* 功能说明: 读取DS1307日期时间数据
* 输    入: 无
* 输    出: 无
******************************************************************************************/
void DS1307_write_date(void)
{
    uint8_t temp;
	
    DS_Buff[0] = 0;                      //秒
    temp = ((minute/10)<<4|(minute%10));
    DS_Buff[1] = temp;                   //分
    temp = ((hour/10)<<4|(hour%10));
    DS_Buff[2] = temp;                   //时

    DS_Buff[3] = week;                   //星期
    temp = ((day/10)<<4|(day%10));
    DS_Buff[4] = temp;                   //日
    temp = ((month/10)<<4|(month%10));
    DS_Buff[5] = temp;                   //月
    temp = ((year%100)/10<<4|(year%10));
    DS_Buff[6] = temp;                   //年
	
    DS1307_Write(0,7);                   //写入前7个字节数据
//    DS1307_I2C_Write(0,7);               //写入前7个字节数据
}

 

    上面的这些代码都是在其他程序中正常使用的,之前移植时通常只需要调整一下读写的时序就行,可是在这次移植却不那么顺利,调试了几天都毫无进展,就是读取不到数据。下面是逻辑分析仪的截图:

 

    从逻辑分析仪抓取的时序图上看,I2C的读写命令都正确发出,I2C器件也作出了正常回应,发出了正确的数据,但读SDA引脚的代码却始终为0,也就说:I2C的写过程正常,I2C器件也正常回应,相应的数据也反映在SDA引脚上了,但读取SDA引脚电平却始终为低电平。问题应该是出在读取引脚电平的代码或者是设置引脚读写模式的代码上。我的设置SDA读写模式的宏定义如下:

#define SDA_1() GPIOB_SetBits(I2C_SDA)    //写I2C数据端口
#define SDA_0() GPIOB_ResetBits(I2C_SDA)
#define SDA_X() GPIOB_ReadPortPin(I2C_SDA)//读I2C数据端口状态

 

#define SDA_OUT()  GPIOB_ModeCfg(I2C_SDA, GPIO_ModeOut_PP_5mA) //SDA推挽输出模式 
#define SDA_IN()   GPIOB_ModeCfg(I2C_SDA, GPIO_ModeIN_PU);     //SDA输入模式
 

    这段宏定义应该不会有问题,但实际测试时就行不通。在测试过程中,我更换了空闲的引脚来作SDA也没成功,将变换引脚模式的宏定义改为在函数中用代码设置也不行。反复折腾了一个多星期都没有成功,以致于我有点崩溃了。我所能想到的办法就是准备外部添加上拉电阻再测试,但模块上本身已经有上拉电阻,再加上拉电阻的意义不大,不过我也想不到其他办法了,看看诸位能帮助分析原因,提出建议。在此先谢谢大家!

 

最新回复

看来真正的问题不在这儿  详情 回复 发表于 2020-10-24 09:16
点赞 关注
 
 

回复
举报

932

帖子

3

TA的资源

纯净的硅(中级)

沙发
 

    刚才我将SDA引脚的宏定义改成浮空输入进行测试,结果仍然照旧没有读到数据,用逻辑分析仪抓取明明有数据。

    宏定义代码如下:

#define SDA_OUT() GPIOB_ModeCfg(I2C_SDA, GPIO_ModeOut_PP_5mA) //SDA推挽输出模式 
//#define SDA_IN()  GPIOB_ModeCfg(I2C_SDA, GPIO_ModeIN_PU)      //SDA上拉输入模式
#define SDA_IN()  GPIOB_ModeCfg(I2C_SDA, GPIO_ModeIN_Floating)//SDA浮空输入模式
 

 
 
 

回复

7671

帖子

18

TA的资源

五彩晶圆(高级)

板凳
 

不至于吧,器件手册上不讲I2C的?

点评

也许是我没看到吧。  详情 回复 发表于 2020-9-22 07:37
个人签名

默认摸鱼,再摸鱼。2022、9、28

 
 
 

回复

932

帖子

3

TA的资源

纯净的硅(中级)

4
 
freebsder 发表于 2020-9-21 20:29 不至于吧,器件手册上不讲I2C的?

也许是我没看到吧。

 
 
 

回复

932

帖子

3

TA的资源

纯净的硅(中级)

5
 

    经过反复测试,终于找到了问题所在,还是判断I2C_SDA引脚电平的代码问题,我在其他项目中一直都是用 if(1 == SDA_X) 来判断,没有出现过问题,但在此项目却不行,将其修改成 if(SDA_X) 再测试就成功了。没想到这个小问题却花费了我一周多的时间才解决。

 

 

点评

打印一下SDA_X看看,难道能输出大于1的值?  详情 回复 发表于 2020-10-23 21:45
有时候就是细节折腾人。  详情 回复 发表于 2020-9-27 10:57

赞赏

1

查看全部赞赏

 
 
 

回复

1万

帖子

203

TA的资源

管理员

6
 

沁恒CH579M-R1开发板测评

汇总贴:https://bbs.eeworld.com.cn/thread-1140005-1-1.html

加EE小助手好友,
入技术交流群
EE服务号
精彩活动e手掌握
EE订阅号
热门资讯e网打尽
聚焦汽车电子软硬件开发
认真关注技术本身
个人签名玩板看这里:
https://bbs.eeworld.com.cn/elecplay.html
EEWorld测评频道众多好板等你来玩,还可以来频道许愿树许愿说说你想要玩的板子,我们都在努力为大家实现!
 
 
 

回复

6107

帖子

4

TA的资源

版主

7
 
hujj 发表于 2020-9-22 15:47     经过反复测试,终于找到了问题所在,还是判断I2C_SDA引脚电平的代码问题,我在其他项目中一 ...

有时候就是细节折腾人。

 
 
 

回复

4

帖子

0

TA的资源

一粒金砂(初级)

8
 

I2C 往往要配置成开漏,可以参考下这个例子,

https://www.cnblogs.com/iot-fan/p/13473877.html

 
 
 

回复

9803

帖子

24

TA的资源

版主

9
 
hujj 发表于 2020-9-22 15:47     经过反复测试,终于找到了问题所在,还是判断I2C_SDA引脚电平的代码问题,我在其他项目中一 ...

打印一下SDA_X看看,难道能输出大于1的值?

点评

当时确实将这个判断修改了就能正确运行,但后来我又尝试将判断改回if(1 == SDA_X),竟然也能正确运行,具体原因不清楚,也没有去深究。  详情 回复 发表于 2020-10-24 08:50
个人签名虾扯蛋,蛋扯虾,虾扯蛋扯虾
 
 
 

回复

932

帖子

3

TA的资源

纯净的硅(中级)

10
 
littleshrimp 发表于 2020-10-23 21:45 打印一下SDA_X看看,难道能输出大于1的值?

当时确实将这个判断修改了就能正确运行,但后来我又尝试将判断改回if(1 == SDA_X),竟然也能正确运行,具体原因不清楚,也没有去深究。

点评

看来真正的问题不在这儿  详情 回复 发表于 2020-10-24 09:16
 
 
 

回复

9803

帖子

24

TA的资源

版主

11
 
hujj 发表于 2020-10-24 08:50 当时确实将这个判断修改了就能正确运行,但后来我又尝试将判断改回if(1 == SDA_X),竟然也能正确运行,具 ...

看来真正的问题不在这儿

点评

    我也挺纳闷的,可当初确实穷尽办法都不行,最后中有尝试修改这个判断,再测试就通过了,但这个似乎不合逻辑。  详情 回复 发表于 2020-10-24 10:07
 
 
 

回复

932

帖子

3

TA的资源

纯净的硅(中级)

12
 
littleshrimp 发表于 2020-10-24 09:16 看来真正的问题不在这儿

    我也挺纳闷的,可当初确实穷尽办法都不行,最后中有尝试修改这个判断,再测试就通过了,但这个似乎不合逻辑。

 
 
 

回复
您需要登录后才可以回帖 登录 | 注册

随便看看
查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
关闭
站长推荐上一条 1/6 下一条

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2025 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表