4704 views|11 replies

931

Posts

3

Resources
The OP
 

[CH579M-R1] + Help: Simulating I2C to read data failed (solved) [Copy link]

 
 This post was last edited by hujj on 2020-9-22 15:39

After successfully driving the LCD5110 display, I started trying to drive the DS1307 calendar module last week. The following picture shows the module purchased from Taobao:

This module communicates via the I2C bus. It also comes with a 24C32 chip and a temperature and humidity sensor installation location. Below are pictures of my test process:

I searched for information about CH579, but could not find any description or examples of hardware I2C. So I had to try to set up software I2C. Because the LCD display and the devices on the I2C bus will not be used at the same time, I used the CLK and DIN pins that drive the LCD5110 and reused them for the SCK and SDA of I2C. The i2c.h file is as follows:


#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 */

The i2c.c code is as follows:



#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;
}


The code for data reading and writing and date conversion of the DS1307 module is as follows:


#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个字节数据
}

The above codes are all used normally in other programs. When porting before, it usually only needs to adjust the timing of reading and writing. However, this porting is not so smooth. After debugging for several days, there is no progress and no data can be read. The following is a screenshot of the logic analyzer:

From the timing diagram captured by the logic analyzer, the I2C read and write commands are issued correctly, and the I2C device also responds normally and sends correct data, but the code for reading the SDA pin is always 0, which means: the I2C write process is normal, the I2C device also responds normally, and the corresponding data is also reflected on the SDA pin, but the SDA pin level is always low. The problem should be in the code for reading the pin level or the code for setting the pin read and write mode. My macro definition for setting the SDA read and write mode is as follows:

#define SDA_1() GPIOB_SetBits(I2C_SDA) //Write I2C data port
#define SDA_0() GPIOB_ResetBits(I2C_SDA)
#define SDA_X() GPIOB_ReadPortPin(I2C_SDA) //Read I2C data port status

#define SDA_OUT() GPIOB_ModeCfg(I2C_SDA, GPIO_ModeOut_PP_5mA) //SDA push-pull output mode
#define SDA_IN() GPIOB_ModeCfg(I2C_SDA, GPIO_ModeIN_PU); //SDA input mode

There shouldn't be any problem with this macro definition, but it doesn't work when I actually test it. During the test, I changed the idle pin to SDA but it didn't work. I also changed the macro definition of changing the pin mode to set it in the function with code but it didn't work. I tried it over and over again for more than a week but it didn't work, so I was a little frustrated. The only way I can think of is to prepare to add an external pull-up resistor and test it again, but the module already has a pull-up resistor, so adding a pull-up resistor doesn't make much sense. But I can't think of any other way. Let's see if you can help analyze the cause and make suggestions. Thank you all in advance!

This post is from Domestic Chip Exchange

Latest reply

It seems that the real problem is not here  Details Published on 2020-10-24 09:16
 
 

931

Posts

3

Resources
2
 

I just changed the macro definition of the SDA pin to a floating input for testing, but still couldn't read any data. However, there was clearly data when I used a logic analyzer to capture it.

The macro definition code is as follows:

#define SDA_OUT() GPIOB_ModeCfg(I2C_SDA, GPIO_ModeOut_PP_5mA) //SDA push-pull output mode
//#define SDA_IN() GPIOB_ModeCfg(I2C_SDA, GPIO_ModeIN_PU) //SDA pull-up input mode
#define SDA_IN() GPIOB_ModeCfg(I2C_SDA, GPIO_ModeIN_Floating) //SDA floating input mode

This post is from Domestic Chip Exchange
 
 
 

7462

Posts

2

Resources
3
 

That shouldn't be the case, the device manual doesn't mention I2C?

This post is from Domestic Chip Exchange

Comments

Maybe I didn't see it.  Details Published on 2020-9-22 07:37
Personal signature

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

 
 
 

931

Posts

3

Resources
4
 
freebsder posted on 2020-9-21 20:29 That's not the case, the device manual doesn't mention I2C?

Maybe I didn't see it.

This post is from Domestic Chip Exchange
 
 
 

931

Posts

3

Resources
5
 

After repeated tests, I finally found the problem. It was the code problem of judging the level of I2C_SDA pin. I have always used if(1 == SDA_X) to judge in other projects, and there has been no problem. But it didn't work in this project. I changed it to if(SDA_X) and tested it successfully. I didn't expect that this small problem took me more than a week to solve.

This post is from Domestic Chip Exchange

Comments

Sometimes it's the details that bother people.  Details Published on 2020-10-23 21:45
Sometimes it's the details that bother people.  Details Published on 2020-9-27 10:57

赞赏

1

查看全部赞赏

 
 
 

1w

Posts

204

Resources
6
 

Qinheng CH579M-R1 Development Board Review

Summary post: https://bbs.eeworld.com.cn/thread-1140005-1-1.html

This post is from Domestic Chip Exchange
Add and join groups EEWorld service account EEWorld subscription account Automotive development circle
Personal signature

玩板看这里:

https://bbs.eeworld.com.cn/elecplay.html

EEWorld测评频道众多好板等你来玩,还可以来频道许愿树许愿说说你想要玩的板子,我们都在努力为大家实现!

 
 
 

6069

Posts

4

Resources
7
 
hujj posted on 2020-9-22 15:47 After repeated tests, I finally found the problem. It was still a problem with the code for judging the level of the I2C_SDA pin. I have used it in other projects...

Sometimes it's the details that bother people.

This post is from Domestic Chip Exchange
 
 
 

4

Posts

0

Resources
8
 

I2C is often configured as open drain. You can refer to this example.

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

This post is from Domestic Chip Exchange
 
 
 

9717

Posts

24

Resources
9
 
hujj posted on 2020-9-22 15:47 After repeated tests, I finally found the problem. It was still a problem with the code for judging the level of the I2C_SDA pin. I have used it in other projects...

Print SDA_X and see if it can output a value greater than 1?

This post is from Domestic Chip Exchange

Comments

At that time, I did modify this judgment and it could run correctly, but later I tried to change the judgment back to if(1 == SDA_X), and it also ran correctly. The specific reason is unclear and I didn't go into it further.  Details Published on 2020-10-24 08:50
Personal signature虾扯蛋,蛋扯虾,虾扯蛋扯虾
 
 
 

931

Posts

3

Resources
10
 
littleshrimp posted on 2020-10-23 21:45 Print SDA_X and see if it can output a value greater than 1?

At that time, I did modify this judgment and it could run correctly, but later I tried to change the judgment back to if(1 == SDA_X), and it also ran correctly. The specific reason is unclear and I didn't go into it further.

This post is from Domestic Chip Exchange

Comments

It seems that the real problem is not here  Details Published on 2020-10-24 09:16
 
 
 

9717

Posts

24

Resources
11
 
hujj posted on 2020-10-24 08:50 At that time, I did modify this judgment and it worked correctly, but later I tried to change the judgment back to if(1 == SDA_X), and it also worked correctly.

It seems that the real problem is not here

This post is from Domestic Chip Exchange

Comments

I was also very puzzled, but I really tried everything but it didn't work. Finally, I tried to modify this judgment, and the test passed, but this seems illogical.  Details Published on 2020-10-24 10:07
 
 
 

931

Posts

3

Resources
12
 
littleshrimp posted on 2020-10-24 09:16 It seems that the real problem is not here

I was also very puzzled, but I really tried everything but it didn't work. Finally, I tried to modify this judgment, and the test passed, but this seems illogical.

This post is from Domestic Chip Exchange
 
 
 

Just looking around
Find a datasheet?

EEWorld Datasheet Technical Support

Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号
快速回复 返回顶部 Return list