📄 i2c1.c
字号:
/************************************************************* * * Copyright @ Motorola, 1999 * ************************************************************/#include "i2c_export.h"#include "i2c.h"/* Define a macro to use an optional application-layer print function, if * one was passed to the I2C library during initialization. If there was * no function pointer passed, this protects against calling it. Also define * the global variable that holds the passed pointer. */#define PRINT if ( app_print ) app_printstatic int (*app_print)(char *,...);/******************* Internal to I2C Driver *****************/static unsigned int ByteToXmit = 0;static unsigned int XmitByte = 0;static unsigned char *XmitBuf = 0;static unsigned int XmitBufEmptyStop =0;static unsigned int ByteToRcv = 0;static unsigned int RcvByte = 0;static unsigned char *RcvBuf = 0;static unsigned int RcvBufFulStop = 0;static unsigned int MasterRcvAddress = 0;/* Set by call to get_eumbbar during I2C_Initialize. * This could be globally available to the I2C library, but there is * an advantage to passing it as a parameter: it is already in a register * and doesn't have to be loaded from memory. Also, that is the way the * I2C library was already implemented and I don't want to change it without * a more detailed analysis. * It is being set as a global variable in I2C_Initialize to hide it from * the DINK application layer, because it is Kahlua-specific. I think that * get_eumbbar, load_runtime_reg, and store_runtime_reg should be defined in * a Kahlua-specific library dealing with the embedded utilities memory block. * Right now, get_eumbbar is defined in dink32/kahlua.s. The other two are * defined in dink32/drivers/i2c/i2c2.s. */static unsigned int Global_eumbbar = 0;extern unsigned int get_eumbbar();extern unsigned int load_runtime_reg( unsigned int eumbbar, unsigned int reg );#pragma Alias( load_runtime_reg, "load_runtime_reg" )extern unsigned int store_runtime_reg( unsigned int eumbbar, unsigned int reg, unsigned int val );#pragma Alias( store_runtime_reg, "store_runtime_reg" )/************************** API *****************//* Application Program Interface (API) are the calls provided by the I2C * library to upper layer applications (i.e., DINK) to access the Kahlua * I2C bus interface. The functions and values that are part of this API * are declared in i2c_export.h. *//* Initialize I2C unit with the following: * driver's slave address * interrupt enabled * optional pointer to application layer print function * * These parameters may be added: * desired clock rate * digital filter frequency sampling rate * * This function must be called before I2C unit can be used. */I2C_Status I2C_Initialize( unsigned char addr, I2C_INTERRUPT_MODE en_int, int (*p)(char *,...)){ I2CStatus status; /* establish the pointer, if there is one, to the application's "printf" */ app_print = p; /* If this is the first call, get the embedded utilities memory block * base address. I'm not sure what to do about error handling here: * if a non-zero value is returned, accept it. */ if ( Global_eumbbar == 0) Global_eumbbar = get_eumbbar(); if ( Global_eumbbar == 0) { PRINT( "I2C_Initialize: can't find EUMBBAR\n" ); return I2C_ERROR; } /* validate the I2C address */ if (addr & 0x80) { PRINT( "I2C_Initialize, I2C address invalid: %d 0x%x\n", (unsigned int)addr, (unsigned int)addr ); return I2C_ERROR; } /* Call the internal I2C library function to perform work. * Accept the default frequency sampling rate (no way to set it currently, * via I2C_Init) and set the clock frequency to something reasonable. */ status = I2C_Init( Global_eumbbar, (unsigned char)0x31, addr, en_int); if (status != I2CSUCCESS) { PRINT( "I2C_Initialize: error in initiation\n" ); return I2C_ERROR; } /* all is well */ return I2C_SUCCESS;}/* Perform the given I2C transaction, only MASTER_XMIT and MASTER_RCV * are implemented. Both are only in polling mode. * * en_int controls interrupt/polling mode * act is the type of transaction * i2c_addr is the I2C address of the slave device * data_addr is the address of the data on the slave device * len is the length of data to send or receive * buffer is the address of the data buffer * stop = I2C_NO_STOP, don't signal STOP at end of transaction * I2C_STOP, signal STOP at end of transaction * retry is the timeout retry value, currently ignored * rsta = I2C_NO_RESTART, this is not continuation of existing transaction * I2C_RESTART, this is a continuation of existing transaction */I2C_Status I2C_do_transaction( I2C_INTERRUPT_MODE en_int, I2C_TRANSACTION_MODE act, unsigned char i2c_addr, unsigned char data_addr, int len, char *buffer, I2C_STOP_MODE stop, int retry, I2C_RESTART_MODE rsta){ I2C_Status status; unsigned char data_addr_buffer[1];#if 1/* This is a temporary work-around. The I2C library breaks the protocol * if it attempts to handle a data transmission in more than one * transaction, so the data address and the actual data bytes are put * into a single buffer before sending it to the library internal functions. * The problem is related to being able to restart a transaction without * sending the I2C device address or repeating the data address. It may take * a day or two to sort it all out, so I'll have to get back to it later. * Look at I2C_Start to see about using some status flags (I'm not sure that * "stop" and "rsta" are enough to reflect the states, maybe so; but the logic * in the library is insufficient) to control correct handling of the protocol. */unsigned char dummy_buffer[257];if (act == I2C_MASTER_XMIT){int i;for (i=1;i<=len;i++)dummy_buffer[i]=buffer[i-1];dummy_buffer[0]=data_addr; status = I2C_do_buffer(en_int, act, i2c_addr, 1 + len, dummy_buffer, stop, retry, rsta); if (status != I2C_SUCCESS) { PRINT( "I2C_do_transaction: can't perform data transfer\n"); return I2C_ERROR; }return I2C_SUCCESS;}#endif /* end of temp work-around */ /* validate requested transaction type */ if ((act != I2C_MASTER_XMIT) && (act != I2C_MASTER_RCV)) { PRINT( "I2C_do_transaction, invalid transaction request: %d\n", act); return I2C_ERROR; } /* range check the I2C address */ if (i2c_addr & 0x80) { PRINT( "I2C_do_transaction, I2C address out of range: %d 0x%x\n", (unsigned int)i2c_addr, (unsigned int)i2c_addr ); return I2C_ERROR; } else { data_addr_buffer[0] = data_addr; } /* We first have to contact the slave device and transmit the data address. * Be careful about the STOP and restart stuff. We don't want to signal STOP * after sending the data address, but this could be a continuation if the * application didn't release the bus after the previous transaction, by * not sending a STOP after it. */ status = I2C_do_buffer(en_int, I2C_MASTER_XMIT, i2c_addr, 1, data_addr_buffer, I2C_NO_STOP, retry, rsta); if (status != I2C_SUCCESS) { PRINT( "I2C_do_transaction: can't send data address for read\n"); return I2C_ERROR; } /* The data transfer will be a continuation. */ rsta = I2C_RESTART; /* now handle the user data */ status = I2C_do_buffer(en_int, act, i2c_addr, len, buffer, stop, retry, rsta); if (status != I2C_SUCCESS) { PRINT( "I2C_do_transaction: can't perform data transfer\n"); return I2C_ERROR; } /* all is well */ return I2C_SUCCESS;}/* This function performs the work for I2C_do_transaction. The work is * split into this function to enable I2C_do_transaction to first transmit * the data address to the I2C slave device without putting the data address * into the first byte of the buffer. * * en_int controls interrupt/polling mode * act is the type of transaction * i2c_addr is the I2C address of the slave device * len is the length of data to send or receive * buffer is the address of the data buffer * stop = I2C_NO_STOP, don't signal STOP at end of transaction * I2C_STOP, signal STOP at end of transaction * retry is the timeout retry value, currently ignored * rsta = I2C_NO_RESTART, this is not continuation of existing transaction * I2C_RESTART, this is a continuation of existing transaction */static I2C_Status I2C_do_buffer( I2C_INTERRUPT_MODE en_int, I2C_TRANSACTION_MODE act, unsigned char i2c_addr, int len, unsigned char *buffer, I2C_STOP_MODE stop, int retry, I2C_RESTART_MODE rsta){ I2CStatus rval; unsigned int dev_stat; if (act == I2C_MASTER_RCV) { /* set up for master-receive transaction */ rval = I2C_get(Global_eumbbar,i2c_addr,buffer,len,stop,rsta); } else { /* set up for master-transmit transaction */ rval = I2C_put(Global_eumbbar,i2c_addr,buffer,len,stop,rsta); } /* validate the setup */ if ( rval != I2CSUCCESS ) { dev_stat = load_runtime_reg( Global_eumbbar, I2CSR ); PRINT( "Error(I2C_do_buffer): control phase, code(0x%08x), status(0x%08x)\n", rval, dev_stat); I2C_Stop( Global_eumbbar ); return I2C_ERROR; } if (en_int == 1) { /* this should not happen, no interrupt handling yet */ return I2C_SUCCESS; } /* this performs the polling action, when the transfer is completed, * the status returned from I2C_Timer_Event will be I2CBUFFFULL or * I2CBUFFEMPTY (rcv or xmit), I2CSUCCESS or I2CADDRESS indicates the * transaction is not yet complete, anything else is an error. */ while ( rval == I2CSUCCESS || rval == I2CADDRESS ) { /* poll the device until something happens */ do { rval = I2C_Timer_Event( Global_eumbbar, 0 ); } while ( rval == I2CNOEVENT ); /* check for error condition */ if ( rval == I2CSUCCESS || rval == I2CBUFFFULL || rval == I2CBUFFEMPTY || rval == I2CADDRESS ) { ; /* do nothing */ } else { /* report the error condition */ dev_stat = load_runtime_reg( Global_eumbbar, I2CSR ); PRINT( "Error(I2C_do_buffer): code(0x%08x), status(0x%08x)\n", rval, dev_stat ); return I2C_ERROR; } } /* all is well */ return I2C_SUCCESS;}/** * Note: * * In all following functions, * the caller shall pass the configured embedded utility memory * block base, EUMBBAR. **//*********************************************************** * function: I2C_put * * description: Send a buffer of data to the intended rcv_addr. * If stop_flag is set, after the whole buffer * is sent, generate a STOP signal provided that the * receiver doesn't signal the STOP in the middle. * I2C is the master performing transmitting. If * no STOP signal is generated at the end of current * transaction, the master can generate a START signal * to another slave addr. * * note: this is master xmit API *********************************************************/static I2CStatus I2C_put( unsigned int eumbbar, unsigned char rcv_addr, /* receiver's address */ unsigned char *buffer_ptr, /* pointer of data to be sent */ unsigned int length, /* number of byte of in the buffer */ unsigned int stop_flag, /* 1 - signal STOP when buffer is empty * 0 - no STOP signal when buffer is empty */ unsigned int is_cnt ) /* 1 - this is a restart, don't check MBB * 0 - this is a new start, check MBB */{ if ( buffer_ptr == 0 || length == 0 ) { return I2CERROR; }#ifdef I2CDBG0 PRINT( "%s(%d): I2C_put\n", __FILE__, __LINE__ );#endif XmitByte = 0; ByteToXmit = length; XmitBuf = buffer_ptr; XmitBufEmptyStop = stop_flag; RcvByte = 0; ByteToRcv = 0; RcvBuf = 0; /* we are the master, start transaction */ return I2C_Start( eumbbar, rcv_addr, XMIT, is_cnt );}/*********************************************************** * function: I2C_get * * description: * Receive a buffer of data from the desired sender_addr * If stop_flag is set, when the buffer is full and the * sender does not signal STOP, generate a STOP signal. * I2C is the master performing receiving. If no STOP signal * is generated, the master can generate a START signal * to another slave addr. * * note: this is master receive API **********************************************************/static I2CStatus I2C_get( unsigned int eumbbar, unsigned char rcv_from, /* sender's address */ unsigned char *buffer_ptr, /* pointer of receiving buffer */ unsigned int length, /* length of the receiving buffer */ unsigned int stop_flag, /* 1 - signal STOP when buffer is full * 0 - no STOP signal when buffer is full */ unsigned int is_cnt ) /* 1 - this is a restart, don't check MBB * 0 - this is a new start, check MBB */{
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -