📄 i2c_hw.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 + -