📄 i2c.c
字号:
/*-------------------------------------------------------------------------------**
** Driver: I2C Bus Device Driver Package for M16C/62 serires MCUs.
**
** (c) 1999 Mitsubishi Electronics America, Inc.
**
** Three Diamond Lane
**
** Durham, North Carolina 27704 USA
**
** Ph 919-479-3333
**
** File: i2c.c
**
** Discription: This multi-master driver provides the software interface to the
**
** I2C Bus hardware of the M3062x series of Mitsubishi抯 MCU.
**
**-------------------------------------------------------------------------------*/
/*-------------------------------------------------------------------------------**
** Mitsubishi Electronics America, INC.
**
** Design Engineering Center-East
**
**
**
** (c) 1999 Mitsubishi Electronics America, Inc
**
** By: Bruce Embry
**
** July, 1999
**
** Version 1.00
**
**
**
**
**
**-------------------------------------------------------------------------------*/
#include "Rtos.h"
#include "Types/BaseTypes.h"
#include "i2c.h"
#include <stdio.h>
#include <string.h>
#define I2C_MAX_NACKS 3 // enough, except for EEPROM
#define I2C_MAX_ARBLOSS 37
#define EEPROM_MAX_NACKS 37 // enough for the promised 10 ms flash cycle (12 was enough in experiment, 11 not)
#define SCAN_MAX_NACKS 3
#define MAX_REVIV_CNT 3
#define I2C_INIT_DELAY 50
// the following globals are initialized once at bootup and are constant afterwards
extern bool flgOSStarted;
byte i2cBaudrate;
// the following globals are shared and must be "threadsafe"
unsigned char i2cSlaveData[ SLV_MEM_SIZE ];
void initForMaster( byte adr,byte dir_in);
void writeI2C( byte *pData, byte nBytes );
void readI2C( byte *pData, byte nBytes );
void init_i2c_mas_func(void)
{
byte i;
I2C_Context *ctxt=&i2cContext;
memset(ctxt,0,sizeof(I2C_Context));
for(i=0;i<MAX_NR_MULTIPLEXERS;i++) {
ctxt->muxAddrs[i]=-1;
}
ctxt->currBus=-1; // state of mux unknown
i2cBaudrate = I2C_BUS_BAUDRATE;
for(i = 0; i< SLV_MEM_SIZE; i++)
i2cSlaveData[i] = i*2+1;
OS_CREATERSEMA( &ctxt->i2cLockSema );
OS_CREATECSEMA( &ctxt->i2cWaitSema );
}
bool i2cBusy( void )
{
if( i2cContext.state == I2C_IDLE ) return false;
return true;
}
bool reviveBus( void )
{
byte i;
volatile word ttime1;
volatile word ttime2;
iic_stop();
// clock for max. twenty times until both lines are free
for(i=0; i<20; i++)
{
ttime1 = (volatile word) OS_GetTime();
while(!P7_1) {
// SCL line presumably dead
ttime2 = (volatile word) OS_GetTime();
ttime2 = ( ttime2 > ttime1 ? (ttime2 - ttime1) : ~(ttime1 - ttime2) + 1);
if( ttime2 > I2C_TIMEOUT ) {
i=20;
break;
}
}
waitHalfCycle();
if( P7_0 ) break;
waitHalfCycle();
OS_IncDI();
PRCR2 = 1; // enable writing to P7/9 direction register
PD7_1 = 1; // SCL=output
P7_1 = 0; // SCL=Low
OS_DecRI();
waitCycles(1);
OS_IncDI();
PRCR2 = 1;
PD7_1 = 0; // SCL=input
OS_DecRI();
}
// SCL or SDA line presumably dead
if ( i >= 20 )
{
iic_ini_lowlevel();
return false;
}
// OK, we have both lines high, make start/stop combination
OS_IncDI();
PRCR2 = 1;
PD7_0 = 1; // SDA=out
P7_0 = 0; // SDA=low
waitHalfCycle();
PRCR2 = 1; //
PD7_0 = 0; // SDA=in (high)
OS_DecRI();
iic_ini_lowlevel();
return true;
}
void iic_ini(byte gotIdAdr)
{
I2C_Context *ctxt=&i2cContext;
byte failcnt = 0;
byte i=0;
ctxt->my_i2cAddr=gotIdAdr&0x7f;
OS_Use( &ctxt->i2cLockSema );
iic_ini_lowlevel();
do {
failcnt += 1;
if( failcnt == 4 ) break;
OS_Delay(I2C_INIT_DELAY);
for(i = 0; i<I2C_INIT_DELAY; i++)
{
if( !U2SMR_BBS ) break; // BusBusy ?
OS_Delay(1);
}
// if not free, try to make it free
if( i >= I2C_INIT_DELAY )
{
reviveBus();
continue;
}
// und nochmal testen ob frei
for(i = 0; i<I2C_INIT_DELAY; i++)
{
if( P7_0 && P7_1 ) break;
OS_Delay(1);
}
if (i >= I2C_INIT_DELAY ) {
reviveBus();
continue;
}
break;
} while( true );
U2C1 = 0x05; // transmit enable, receive enable
OS_Unuse( &ctxt->i2cLockSema );
}
/*
* ---------------------------------------------------------
* Register an I2C-Bus needs a Multiplexer address
* and a choosen multiplexer number (0..15) as argument.
* A multiplexer may have up to 16 buses
* ---------------------------------------------------------
*/
void registerI2CMux( byte mdvcadr ,byte mux_nr)
{
if(mux_nr<MAX_NR_MULTIPLEXERS) {
i2cContext.muxAddrs[mux_nr]=mdvcadr;
switchMux(mdvcadr,0);
}
}
void sendI2cContent( byte len, byte* data )
{
memcpy(i2cSlaveData, data, len);
}
void doScanI2C( byte* data,byte bus_nr)
{
byte i, cnt = 0;
memset( data, 0xff, MAX_I2C_DEVICES );
for( i=0; i< 128; i++)
{
if( scanAddressI2C( i | (bus_nr<<8)) == stat_success )
{
data[cnt++] = i;
}
if( cnt == MAX_I2C_DEVICES) break;
}
}
/*
* --------------------------------------------------------------------------
* Check if the EEProm needs 16 Bit address or 8 Bit access by trying
* to write a 16 Bit address with Write Control disabled. If it is a
* device with 8 Bit address writing the second byte will fail.
* --------------------------------------------------------------------------
*/
byte eepromRecognition( word devAdr, byte* enablePort, byte enablePortMask )
{
byte pData[2] = { 0 };
byte ans;
byte memPort;
// for this test, the write control line must be high. Otherwise, data is corrupted.
memPort = *enablePort;
*enablePort |= enablePortMask;
if( masterWriteI2C( 2, pData, devAdr ) == stat_data_error_write )
ans = eeprom_Small;
else
ans = eeprom_Large;
*enablePort = memPort;
return ans;
}
/*
* --------------------------------------------------------------------------------
* Wait until the interrupthandlers are ready with executing the I2C-master script
* or until a timeout occurs
* --------------------------------------------------------------------------------
*/
byte
i2c_wait_for_script(void) {
volatile word ttime1;
volatile word ttime2;
I2C_Context *ctxt=&i2cContext;
if( flgOSStarted )
{
OS_WaitCSemaTimed( &ctxt->i2cWaitSema, I2C_TIMEOUT );
} else {
ttime1 = (volatile word) OS_GetTime();
while( ctxt->result == stat_in_progress) {
ttime2 = (volatile word) OS_GetTime();
ttime2 = ( ttime2 > ttime1 ? (ttime2 - ttime1) : ~(ttime1 - ttime2) + 1);
if( ttime2 > I2C_TIMEOUT )
{
break;
}
}
}
if(ctxt->result==stat_in_progress) {
return stat_timeout_at_address;
}
return ctxt->result;
}
/*
* -----------------------------------------------------------------------------------------------------
* Start a transaction as master and write the I2C-address to the Bus,
* Then wait until the interrupt handlers
* have executed the I2C-script.
* Automatically retry on arbitration loss or on error
* -----------------------------------------------------------------------------------------------------
*/
static byte
i2c_execute_master_transaction(byte devAdr,word do_read,word max_errcount,word max_nackcount) {
byte revivCnt=0;
byte nackCnt=0;
byte readCnt=0;
byte narbCnt=0;
byte flgFinished=false;
byte retstat=stat_bus_broken;
byte status;
do {
i2c_reset_script(&i2cContext);
initForMaster(devAdr,do_read);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -