⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 i2c_hw.c

📁 Sigma SMP8634 Mrua v. 2.8.2.0
💻 C
字号:
/* * * Copyright (c) Sigma Designs, Inc. 2004. All rights reserved. * *//**	@file i2c_hw.c	@brief implemetation for I2C - using hardware I2C block	@author YH Lin, Markus Brenner*/#include "../../../rmdef/rmdef.h"#include "../../../llad/include/gbus.h"#include "../../include/emhwlib_registers.h"#include "../include/i2c_hw.h"#ifdef RMFEATURE_HAS_HWI2C/* * low level functions */#if 0#define HWI2C_DEBUG ENABLE#else#define HWI2C_DEBUG DISABLE#endif#define ASSUMED_SYSCLK 200  // assumed system clock (MHz)  -  TODO should be actual sys clk#define ENGINE_CLK     400  // running clock of the I2C engine (kHz)#define XTAL_REG (REG_BASE_system_block + SYS_xtal_in_cnt)  // Location of the XTal counter register#define XTAL_MHZ 27       // XTal frequency (in MHz)#define I2S_MASTER_CLK_DIV ((ASSUMED_SYSCLK * 1000) / (2 * ENGINE_CLK))#define NUMBER_OF_CYCLES 4096  // ACK Timeout: NUMBER_OF_CYCLES * DelayUs#define MIN_CYCLE_DELAY_US 2/* * note 1 * * current statemachine of hardware i2c block does not send * stop condition (and therefore never gets idle) after an * ack error until all requested bytes were read. jumping * out of the loop right after an ack error would leave the * statemachine in a non-idle state. disabling would not * complete the transfer (no stop bit would be send). */static void HWI2C_RMMicroSecondSleep(struct gbus* pGBus, RMuint32 t0, RMuint32 us){	// implementation safe for delays smaller than 0xFFFFFFFF/27 us = 159 sec	// if SYS_xtal_in_cnt doesn't work - hang here !! 	RMuint32 t1, d;	do {		t1 = gbus_read_uint32(pGBus, XTAL_REG);		if (t1 >= t0) d = t1 - t0;		else d = 0xFFFFFFFF - t0 + t1 + 1;	} while (d < XTAL_MHZ * us);}static RMuint32 HWI2C_WaitStatus(struct emhwi2c* pI2C, RMuint32 status){	RMuint32 i, stat, t0;		if (pI2C->DelayUs < MIN_CYCLE_DELAY_US)		pI2C->DelayUs = MIN_CYCLE_DELAY_US;		for (i = 0; i < NUMBER_OF_CYCLES; i++) {		t0 = gbus_read_uint32(pI2C->pGBus, XTAL_REG);		stat = gbus_read_uint32(pI2C->pGBus, pI2C->RegBase + 			SYS_gpio_dir + I2C_MASTER_STATUS);		if ((stat & status) != 0)			return stat;		HWI2C_RMMicroSecondSleep(pI2C->pGBus, t0, pI2C->DelayUs);	}		return 0;}static RMstatus HWI2C_Write_Addr(struct emhwi2c* pI2C, RMuint8 SubAddress, RMuint8* pData, RMuint32 n, RMbool UseSubAddress){	RMuint32 i;	if (n < 1)		return RM_INVALID_PARAMETER;	//wait for idle (in case of concurrent access)	if (HWI2C_WaitStatus(pI2C, 0x1) == 0) {		RMDBGLOG((ENABLE, "wait for idle failed (concurrent access)\n"));		//EmhwI2CDisable(pI2C); //reset i2c block (set idle flag)		return RM_BUSY;	}	//config device (I2CEnable 8: 1b, REGADRLEN 5-7: 111b, DEVADRLEN 2-4: 110b, REGADRDIS 1: 0b or 1b if UseSubAddress)	gbus_write_uint32(pI2C->pGBus,		pI2C->RegBase + SYS_gpio_dir + I2C_MASTER_CONFIG, UseSubAddress ? 0x1f8 : 0x1fa);	gbus_write_uint32(pI2C->pGBus,		pI2C->RegBase + SYS_gpio_dir + I2C_MASTER_CLK_DIV, pI2C->SysClk / (2000 * pI2C->Speed)); 	//setup transfer	gbus_write_uint32(pI2C->pGBus,		pI2C->RegBase + SYS_gpio_dir + I2C_MASTER_BYTE_CNT, n - 1);	gbus_write_uint32(pI2C->pGBus,		pI2C->RegBase + SYS_gpio_dir + I2C_MASTER_DEV_ADDR, pI2C->DevAddr >> 1);	//write data	for (i = 0; i < n; i++) {		//set/update subaddress		if (UseSubAddress)			gbus_write_uint32(pI2C->pGBus, 				pI2C->RegBase + SYS_gpio_dir + I2C_MASTER_ADDR, SubAddress++);		//write actual data byte		gbus_write_uint32(pI2C->pGBus, 			pI2C->RegBase + SYS_gpio_dir + I2C_MASTER_DATA_OUT, *pData++);		//start write transfer on first byte		if (!i)			gbus_write_uint32(pI2C->pGBus,				pI2C->RegBase + SYS_gpio_dir + I2C_MASTER_STARTXFER, 0);		//wait till dataout is empty		if (HWI2C_WaitStatus(pI2C, 0x2) == 0) {			RMDBGLOG((HWI2C_DEBUG, "wait till dataout is empty failed at byte %d\n", i + 1));			EmhwI2CDisable(pI2C); //reset i2c block (set idle flag)			return RM_TIMEOUT;		}		//don't check for ack error (and break) - first check after loop, see note 1 in the beginning	}	//check for ack error (do not use HWI2C_WaitStatus which would only slow down)	if ((gbus_read_uint32(pI2C->pGBus,		pI2C->RegBase + SYS_gpio_dir + I2C_MASTER_STATUS) & 0x8) != 0) {		RMDBGLOG((HWI2C_DEBUG, "ack error (somewhere during transfer)\n"));		EmhwI2CDisable(pI2C); //reset i2c block (set idle flag)		return RM_ERROR; //do not wait for idle anymore!	}	//wait for idle (indicates that transfer was completed successfully)	if (HWI2C_WaitStatus(pI2C, 0x1) == 0) {		RMDBGLOG((HWI2C_DEBUG, "wait for idle after last byte failed\n"));		EmhwI2CDisable(pI2C); //reset i2c block (set idle flag)		return RM_TIMEOUT;	}	return EmhwI2CDisable(pI2C); //reset i2c block (set idle flag)}static RMstatus HWI2C_Read_Addr(struct emhwi2c* pI2C, RMuint8 SubAddress, RMuint8* pData, RMuint32 n, RMbool UseSubAddress){	RMuint32 i, stat, ackerr;	if (n < 1)		return RM_INVALID_PARAMETER;	//wait for idle (in case of concurrent access)	if (HWI2C_WaitStatus(pI2C, 0x1) == 0) {		RMDBGLOG((ENABLE, "wait for idle failed (concurrent access)\n"));		//EmhwI2CDisable(pI2C); //reset i2c block (set idle flag)		return RM_BUSY;	}	//config device (I2CEnable 8: 1b, REGADRLEN 5-7: 111b, DEVADRLEN 2-4: 110b, REGADRDIS 1: 0b or 1b if UseSubAddress)	gbus_write_uint32(pI2C->pGBus,		pI2C->RegBase + SYS_gpio_dir + I2C_MASTER_CLK_DIV, pI2C->SysClk / (2000 * pI2C->Speed)); 		if (UseSubAddress) {		gbus_write_uint32(pI2C->pGBus, 			pI2C->RegBase + SYS_gpio_dir + I2C_MASTER_CONFIG, 0x1fa);		gbus_write_uint32(pI2C->pGBus, 			pI2C->RegBase + SYS_gpio_dir + I2C_MASTER_BYTE_CNT, 0);		gbus_write_uint32(pI2C->pGBus, 			pI2C->RegBase + SYS_gpio_dir + I2C_MASTER_DEV_ADDR, 			pI2C->DevAddr >> 1);		gbus_write_uint32(pI2C->pGBus, 			pI2C->RegBase + SYS_gpio_dir + I2C_MASTER_DATA_OUT, 			SubAddress);		gbus_write_uint32(pI2C->pGBus, 			pI2C->RegBase + SYS_gpio_dir + I2C_MASTER_STARTXFER, 4);				if ((stat = HWI2C_WaitStatus(pI2C, 0x2)) == 0) {			RMDBGLOG((HWI2C_DEBUG, "failed to write sub address\n"));			return RM_ERROR;		} else if ((stat = HWI2C_WaitStatus(pI2C, 0x1)) == 0) {			RMDBGLOG((HWI2C_DEBUG, "failed to wait for idle state after sub address\n"));			return RM_ERROR;		} else if ((stat & 0x08) != 0) {			RMDBGLOG((HWI2C_DEBUG, "ack error at sub address\n"));			return RM_ERROR;		}	}		//setup transfer	gbus_write_uint32(pI2C->pGBus,		pI2C->RegBase + SYS_gpio_dir + I2C_MASTER_CONFIG, 0x1fa);	gbus_write_uint32(pI2C->pGBus,		pI2C->RegBase + SYS_gpio_dir + I2C_MASTER_BYTE_CNT, n - 1);	gbus_write_uint32(pI2C->pGBus,		pI2C->RegBase + SYS_gpio_dir + I2C_MASTER_DEV_ADDR, pI2C->DevAddr >> 1);	gbus_write_uint32(pI2C->pGBus,		pI2C->RegBase + SYS_gpio_dir + I2C_MASTER_STARTXFER, 1);	//read back data	ackerr = 0;	for (i = 0; i < n; i++) {		//wait till data byte ready		if ((stat = HWI2C_WaitStatus(pI2C, 0x4)) == 0) {			RMDBGLOG((HWI2C_DEBUG, "wait till data byte ready failed at byte %d\n", i + 1));			EmhwI2CDisable(pI2C); //reset i2c block (set idle flag)			return RM_TIMEOUT;		}				//get actual data byte		*pData++ = gbus_read_uint32(pI2C->pGBus,			pI2C->RegBase + SYS_gpio_dir + I2C_MASTER_DATA_IN);		//check for ack error		if (stat & 0x8) {			if (! ackerr)				ackerr = i + 1;			//don't break in case of ack error, see note 1 in the beginning		}	}		//check for ack error, see note 1 in the beginning	if (ackerr) {		RMDBGLOG((HWI2C_DEBUG, "ack error at byte %d\n", ackerr));		EmhwI2CDisable(pI2C); //reset i2c block (set idle flag)		return RM_ERROR; //do not wait for idle anymore!	}		//wait for idle (indicates that transfer was completed successfully)	if ((stat = HWI2C_WaitStatus(pI2C, 0x1)) == 0) {		RMDBGLOG((HWI2C_DEBUG, "wait for idle after last byte failed\n"));		EmhwI2CDisable(pI2C); //reset i2c block (set idle flag)		return RM_TIMEOUT;	}	//check for ack error	if (stat & 0x8) {		RMDBGLOG((HWI2C_DEBUG, "ack error waiting for stop condition\n"));		EmhwI2CDisable(pI2C); //reset i2c block (set idle flag)		return RM_ERROR; //do not wait for idle anymore!	}	return EmhwI2CDisable(pI2C); //reset i2c block (set idle flag)}/* * high level functions */RMstatus HWI2C_Select_Segment(struct hwi2c* pI2C, RMuint8 SegmentAddr, RMuint8 Segment){	struct emhwi2c i2c;		i2c.APIVersion = 1;	i2c.pGBus = pI2C->pGBus;	i2c.RegBase = pI2C->RegBase;	i2c.DelayUs = pI2C->DelayUs;	i2c.SysClk = ASSUMED_SYSCLK * 1000 * 1000;	i2c.Speed = ENGINE_CLK;	i2c.DevAddr = pI2C->WrAddr;	return EmhwI2CSelectSegment(&i2c, SegmentAddr, Segment);}	RMstatus EmhwI2CSelectSegment(struct emhwi2c* pI2C, RMuint8 SegmentAddr, RMuint8 Segment){	if (pI2C->APIVersion == 0) return RM_ERROR;	if (pI2C->APIVersion > 1) return RM_ERROR;		//wait for idle (in case of concurrent access)	if (HWI2C_WaitStatus(pI2C, 0x1) == 0) {		RMDBGLOG((ENABLE, "wait for idle failed (concurrent access)\n"));		//EmhwI2CDisable(pI2C); //reset i2c block (set idle flag)		return RM_BUSY;	}	//config device (I2CEnable 8: 1b, REGADRLEN 5-7: 111b, DEVADRLEN 2-4: 110b, REGADRDIS 1: 1b)	gbus_write_uint32(pI2C->pGBus,		pI2C->RegBase + SYS_gpio_dir + I2C_MASTER_CONFIG, 0x1fa);	gbus_write_uint32(pI2C->pGBus,		pI2C->RegBase + SYS_gpio_dir + I2C_MASTER_CLK_DIV, pI2C->SysClk / (2000 * pI2C->Speed)); 	//setup transfer	gbus_write_uint32(pI2C->pGBus,		pI2C->RegBase + SYS_gpio_dir + I2C_MASTER_BYTE_CNT, 0);	gbus_write_uint32(pI2C->pGBus,		pI2C->RegBase + SYS_gpio_dir + I2C_MASTER_DEV_ADDR, SegmentAddr >> 1);	gbus_write_uint32(pI2C->pGBus,		pI2C->RegBase + SYS_gpio_dir + I2C_MASTER_DATA_OUT, Segment);	gbus_write_uint32(pI2C->pGBus,		pI2C->RegBase + SYS_gpio_dir + I2C_MASTER_STARTXFER, 0x4); //omit stop after sending data	//wait till dataout is empty	if (HWI2C_WaitStatus(pI2C, 0x2) == 0) {		RMDBGLOG((HWI2C_DEBUG, "wait till dataout is empty failed\n"));		EmhwI2CDisable(pI2C); //reset i2c block (set idle flag)		return RM_TIMEOUT;	}	//check for ack error	if ((gbus_read_uint32(pI2C->pGBus,		pI2C->RegBase + SYS_gpio_dir + I2C_MASTER_STATUS) & 0x8) != 0) {		RMDBGLOG((HWI2C_DEBUG, "ack error\n"));		EmhwI2CDisable(pI2C); //reset i2c block (set idle flag)		return RM_ERROR; //don't wait for idle anymore!	}	//wait for idle (indicates that transfer was completed successfully)	if (HWI2C_WaitStatus(pI2C, 0x1) == 0) {		RMDBGLOG((HWI2C_DEBUG, "wait for idle failed\n"));		EmhwI2CDisable(pI2C); //reset i2c block (set idle flag)		return RM_TIMEOUT;	}	return EmhwI2CDisable(pI2C); //reset i2c block (set idle flag)}RMstatus HWI2C_Disable(struct hwi2c* pI2C){	struct emhwi2c i2c;		i2c.APIVersion = 1;	i2c.pGBus = pI2C->pGBus;	i2c.RegBase = pI2C->RegBase;	i2c.DelayUs = pI2C->DelayUs;	i2c.SysClk = ASSUMED_SYSCLK * 1000 * 1000;	i2c.Speed = ENGINE_CLK;	i2c.DevAddr = pI2C->WrAddr;	return EmhwI2CDisable(&i2c);}RMstatus EmhwI2CDisable(struct emhwi2c* pI2C){	if (pI2C->APIVersion == 0) return RM_ERROR;	if (pI2C->APIVersion > 1) return RM_ERROR;		if (! gbus_read_uint32(pI2C->pGBus, pI2C->RegBase + SYS_gpio_dir + I2C_MASTER_CONFIG)) {		return RM_OK;	}		if (HWI2C_WaitStatus(pI2C, 0x1) == 0) {		RMDBGLOG((HWI2C_DEBUG, "wait for idle in disable failed\n"));	}		//on main HWI2C master, Tri-State GPIO 0 and 1 before handing pin control back to them	if (pI2C->RegBase == REG_BASE_system_block)		gbus_write_uint32(pI2C->pGBus, REG_BASE_system_block + SYS_gpio_dir, 0x00030000); //input	//clear data ready flag by reading data-in register	gbus_read_uint32(pI2C->pGBus, pI2C->RegBase + SYS_gpio_dir + I2C_MASTER_DATA_IN);		//reset and disable HWI2C block	gbus_write_uint32(pI2C->pGBus, pI2C->RegBase + SYS_gpio_dir + I2C_MASTER_CONFIG, 0);		return RM_OK;}RMstatus HWI2C_Write(struct hwi2c* pI2C, RMuint8 SubAddress, RMuint8* pData, RMuint32 n){	struct emhwi2c i2c;		i2c.APIVersion = 1;	i2c.pGBus = pI2C->pGBus;	i2c.RegBase = pI2C->RegBase;	i2c.DelayUs = pI2C->DelayUs;	i2c.SysClk = ASSUMED_SYSCLK * 1000 * 1000;	i2c.Speed = ENGINE_CLK;	i2c.DevAddr = pI2C->WrAddr;	return EmhwI2CWrite(&i2c, TRUE, SubAddress, pData, n);}RMstatus HWI2C_Write_NoSubAddr(struct hwi2c* pI2C, RMuint8* pData, RMuint32 n){	struct emhwi2c i2c;		i2c.APIVersion = 1;	i2c.pGBus = pI2C->pGBus;	i2c.RegBase = pI2C->RegBase;	i2c.DelayUs = pI2C->DelayUs;	i2c.SysClk = ASSUMED_SYSCLK * 1000 * 1000;	i2c.Speed = ENGINE_CLK;	i2c.DevAddr = pI2C->WrAddr;	return EmhwI2CWrite(&i2c, FALSE, 0, pData, n);}RMstatus EmhwI2CWrite(struct emhwi2c* pI2C, RMbool UseSubAddr, RMuint8 SubAddress, RMuint8* pData, RMuint32 n){	if (pI2C->APIVersion == 0) return RM_ERROR;	if (pI2C->APIVersion > 1) return RM_ERROR;		//allow dummy write to set subaddress register without sending any data bytes	if (UseSubAddr && (n == 0)) {		return HWI2C_Write_Addr(pI2C, 0, &SubAddress, 1, 0); //send subaddress as data byte (dummy bit in i2c block does not work!)	} else {		return HWI2C_Write_Addr(pI2C, SubAddress, pData, n, UseSubAddr);	}}RMstatus HWI2C_Read(struct hwi2c* pI2C, RMuint8 SubAddress, RMuint8* pData, RMuint32 n){	struct emhwi2c i2c;		i2c.APIVersion = 1;	i2c.pGBus = pI2C->pGBus;	i2c.RegBase = pI2C->RegBase;	i2c.DelayUs = pI2C->DelayUs;	i2c.SysClk = ASSUMED_SYSCLK * 1000 * 1000;	i2c.Speed = ENGINE_CLK;	i2c.DevAddr = pI2C->RdAddr;	return EmhwI2CRead(&i2c, TRUE, SubAddress, pData, n);}RMstatus HWI2C_Read_NoSubAddr(struct hwi2c* pI2C, RMuint8* pData, RMuint32 n){	struct emhwi2c i2c;		i2c.APIVersion = 1;	i2c.pGBus = pI2C->pGBus;	i2c.RegBase = pI2C->RegBase;	i2c.DelayUs = pI2C->DelayUs;	i2c.SysClk = ASSUMED_SYSCLK * 1000 * 1000;	i2c.Speed = ENGINE_CLK;	i2c.DevAddr = pI2C->RdAddr;	return EmhwI2CRead(&i2c, FALSE, 0, pData, n);}RMstatus EmhwI2CRead(struct emhwi2c* pI2C, RMbool UseSubAddr, RMuint8 SubAddr, RMuint8* pData, RMuint32 n){	if (pI2C->APIVersion == 0) return RM_ERROR;	if (pI2C->APIVersion > 1) return RM_ERROR;		return HWI2C_Read_Addr(pI2C, SubAddr, pData, n, UseSubAddr);}#endif //RMFEATURE_HAS_HWI2C

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -