I2C協議代碼的實現關鍵

    I2C總線我已經用很久了,也用了很多次,但每到下一次使用時,都會或多或少的發現一些小問題,比如讀寫單個字節時沒有問題,在連續讀寫大量數據時卻出現讀寫不正確的現象,下面來總結一下模擬I2C驅動代碼的實現關鍵

1、起始信號start:這個一般不會出錯,在SCL=1時,讓SDA出現一個下降沿,即SDA=1 --->SDA=0;

2、停止信號stop:這個一般也不會出錯,在SCL=1時,讓SDA出現一個上升沿,即SDA=0 --->SDA=1;

3、主機(如單片機)檢測應答信號:I2C要求,接收方在接收到一個字節後,在第9個時鐘回一個應答信號,主機檢測這個應答信號應這樣操作,SCL=0時,釋放SDA(即SDA=1),然後讓SCL=1,接收方在檢測到SCL=1後會讓SDA=0作爲應答信號,隨後發送方就可檢測SDA的狀態;

4、主機回覆應答信號:主機在接收從機發來的一個字節後,要回一個應答信號,主機應這樣操作,在SCL=0時,使SDA=0,然後讓SCL=1,隨後從機會檢測到SDA的狀態;

5、主機回覆非應答信號:主機在接收數據後,在stop信號前,回一個非應答信號,主機應該這樣操作,在SCL=0時,使SDA=1,然後讓SCL=1,隨後從機會檢測到SDA的狀態;

    以上5點用過幾次I2C後一般都會掌握。

 

以下幾點最爲容易忽視,導致I2C時序出錯,連續讀寫大量數據時容易丟包

1、特別注意SCL=1時,SDA的任何變化都會使時序出錯,從機將視爲起始信號或停止信號,所以要特別注意start 、write/read、ack/no_ack、stop銜接的前後操作,不要出現有SCL=1改變SDA狀態的情況;爲此,建議在以上幾個函數的開始處後退出前,使SLC=0,這樣在函數銜接時不會出現SCL跳變的情況,也不會在SCL=1時誤操作SDA. 

2、start 、write/read、ack/no_ack、stop函數前後銜接時,要保持SCL的狀態一致性,否則讀寫過程中可能會導致時鐘個數錯誤,這個問題在讀寫單個字節時可能不會出現,但在多次讀寫中就會出現。

3、部分用戶在連續讀時沒發現出錯,覺得自己的時序是正確的,但連續寫時就會出錯,感覺莫名其妙,這是因爲大多數器件的數據讀出要比寫入的速度要快,在連續寫時,應在中間要延時一段時間;

4、 由於硬件上要求SCL和SDA都接上拉電阻,stop信號時在讀寫時最後的一個操作,所以,stop退出前,建議使SDA=1,SCL=1,從而不讓上拉電阻兩端有高低電位差,以降低功耗,當然,也要保證SLC=1時不要改變SDA,否則設備會認爲是一個新的起始或停止信號。

 

下面時實用例程C代碼


void I2C_delay(void)
{
	u16 i;
	/*以STM32  72MHz爲例
        循環10次,SCL頻率爲20K
        循環7次,SCL頻率爲340K
        循環5次,SCL頻率爲420K
	*/
	for(i=0;i<5;i++);
}


void E2PROM_I2C_start(void)
{
	
	I2C_SetOut_Mode();	
	E2PROM_I2C_SCL=0;
	E2PROM_I2C_SDA_WR=1;
	E2PROM_I2C_SCL=1;
	I2C_delay();
	E2PROM_I2C_SDA_WR=0;
	E2PROM_I2C_SCL=0;
	I2C_delay();
}

void E2PROM_I2C_stop(void)
{
	
	I2C_SetOut_Mode();
	E2PROM_I2C_SCL=0;
	E2PROM_I2C_SDA_WR=0;
	E2PROM_I2C_SCL=1;
	I2C_delay();
	E2PROM_I2C_SDA_WR=1;
	E2PROM_I2C_SCL=0;
	I2C_delay();
	E2PROM_I2C_SDA_WR=1;//拉高數據和時鐘線,以降低功耗
	E2PROM_I2C_SCL=1;  

}

u8 E2PROM_I2C_check_ack(void)
{
	u8 ack_status=1;
	I2C_SetIn_Mode();
	E2PROM_I2C_SCL=0;
	E2PROM_I2C_SDA_WR=1;
	E2PROM_I2C_SCL=1;
	I2C_delay();

	ack_status=0x01&E2PROM_I2C_SDA_RE;
	E2PROM_I2C_SCL=0;
	I2C_delay();

	return ack_status;
}


void E2PROM_I2C_ack(void)
{
	I2C_SetOut_Mode();
	E2PROM_I2C_SCL=0;
	E2PROM_I2C_SDA_WR=0;
	E2PROM_I2C_SCL=1;
	I2C_delay();
	E2PROM_I2C_SCL=0;
	I2C_delay();
	E2PROM_I2C_SDA_WR=1;
	
}

void E2PROM_I2C_NoAck(void)
{
	I2C_SetOut_Mode();
	E2PROM_I2C_SCL=0;
	E2PROM_I2C_SDA_WR=1;
	I2C_delay();
	E2PROM_I2C_SCL=1;
	I2C_delay();
	E2PROM_I2C_SCL=0;
	I2C_delay();
	
}


void E2PROM_I2C_write_char(u8 dat)
{
	u8 i=0;
	
	E2PROM_I2C_SCL=0;
	I2C_SetOut_Mode();
	for(i=0;i<8;i++)
	{
		
		if(dat&0x80) E2PROM_I2C_SDA_WR=1;
		else E2PROM_I2C_SDA_WR=0;
		I2C_delay();
		E2PROM_I2C_SCL=1;
		I2C_delay();
		E2PROM_I2C_SCL=0;
		dat<<=1;
	}
	
}

u8 E2PROM_I2C_read_char(void)
{
	u8 i=0,dat=0;
	E2PROM_I2C_SCL=0;
	I2C_SetIn_Mode();
	for(i=0;i<8;i++)
	{
	
		dat<<=1;
		E2PROM_I2C_SCL=1;
		I2C_delay();
		if(E2PROM_I2C_SDA_RE) dat|=0x01;
		E2PROM_I2C_SCL=0;
		I2C_delay();
	
	}
	return dat;
}



void AT24CXX_Write(u8 DeviceAddr,u8 ByteAddr,u8 dat)
{
	E2PROM_I2C_start();
	E2PROM_I2C_write_char(DeviceAddr);
	E2PROM_I2C_check_ack();
	E2PROM_I2C_write_char(ByteAddr);
	E2PROM_I2C_check_ack();
	E2PROM_I2C_write_char(dat);
	E2PROM_I2C_check_ack();
	E2PROM_I2C_stop();
	
	
}

unsigned char AT24CXX_Read(u8 DeviceAddr,u8 ByteAdrr)
{
	unsigned char dat=0;
	E2PROM_I2C_start();
	E2PROM_I2C_write_char(DeviceAddr);
	E2PROM_I2C_check_ack();
	E2PROM_I2C_write_char(ByteAdrr);
	E2PROM_I2C_check_ack();
	
	
	E2PROM_I2C_start();
	E2PROM_I2C_write_char(DeviceAddr|0x01);
	E2PROM_I2C_check_ack();
	dat=E2PROM_I2C_read_char();
	E2PROM_I2C_NoAck();
	E2PROM_I2C_stop();
	return dat;
	
}


u8 E2PROM_buffer[256];

int main(void)
{
    u16 i,j,k;
    for(i=0;i<256;i++)
	{
		AT24CXX_Write(I2C_24C08_write_cmd,i,i);
		Delay_ms(2);
	}
	
	k=0;
	printf("\r\n");
	for(i=0;i<16;i++)
	{
		for(j=0;j<16;j++)
		{
			E2PROM_buffer[k]=AT24CXX_Read(I2C_24C08_write_cmd,k);
			printf("%d ",E2PROM_buffer[k]);
			k++;

		}
		printf("\r\n");
	}

	printf("\r\n");
	
	for(i=0;i<255;i++)
	{
		if(E2PROM_buffer[i+1]-E2PROM_buffer[i] != 1) 
        {
            printf(" \r\n E2PROM TEST error !!! \r\n" );
            break;
	    }
     }
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章