Files
OpenEPaperLink/zbs243_shared/soc/zbs243/i2c.c
2023-03-27 15:32:37 +02:00

127 lines
3.4 KiB
C

#include "i2c.h"
#include "printf.h"
#include "uart.h"
#include "zbs243.h"
static volatile struct I2cTransaction __xdata *__xdata mCurTrans;
static volatile uint8_t __xdata mNumTrans;
static volatile uint8_t __xdata mResult;
#pragma callee_saves i2cInit
void i2cInit(void) {
uint8_t bkp;
CLKEN |= 0x10;
bkp = CFGPAGE;
CFGPAGE = 0;
IEN1 |= 4; // int on
I2CUNKNOWN |= 4;
I2CCTL = 0x43; // master mode
I2CSPEED = 0x1a; // 100KHz
I2CCTL |= 0x80; // irq on
CFGPAGE = bkp;
}
// this code assumes HW stat emachine acts as it should. for this chip that seems to be true
// this is an 8051 so we value speed & code size over defensive programming. So sue me!
void I2C_IRQ(void) __interrupt(8) {
uint8_t bkp, sta;
bkp = CFGPAGE;
CFGPAGE = 0;
sta = I2CSTATE;
//pr("state=%02X\n", sta);
switch (sta >> 3) {
case 0x08 / 8: // start completed
case 0x10 / 8: // restart completed
I2CBUF = mCurTrans->deviceAddr;
break;
case 0x30 / 8: // byte write was NAKed
if (mCurTrans->numBytes) { // we still had more bytes to send?
I2CCTL |= 0x10; // stop
mResult = I2cNonLastDataByteNAKed;
break;
}
// fallthough since it is ok to NAK last written byte
case 0x28 / 8: // byte write was ACKed
case 0x18 / 8: // addr ACKED in write mode
if (mCurTrans->numBytes) {
mCurTrans->numBytes--;
I2CBUF = *mCurTrans->bytes++;
break;
}
transaction_over:
if (!--mNumTrans) { // byte(s)written and no more transactions? STOP
mResult = I2cOK;
I2CCTL |= 0x10; // stop
} else { // we have another transaction? RESTART
mCurTrans++;
I2CCTL |= 0x20; // do a restart
}
break;
case 0x20 / 8: // addr NAKEd in write mode
mResult = I2cWrAddrNAKed;
I2CCTL |= 0x10; // stop
mNumTrans = 0;
break;
case 0x48 / 8: // addr NACKed in read mode
mResult = I2cRdAddrNAKed;
I2CCTL |= 0x10; // stop
mNumTrans = 0;
break;
case 0x58 / 8: // NAK to RXed byte sent (we still have th ebyte we got in buffer)
case 0x50 / 8: // got byte in read mode
*mCurTrans->bytes++ = I2CBUF;
mCurTrans->numBytes--;
// fallthrough
case 0x40 / 8: // addr ACKED in read mode
if (!mCurTrans->numBytes)
goto transaction_over;
else if (mCurTrans->numBytes == 1)
I2CCTL &= ~0x04; // NAK
else
I2CCTL |= 0x04; // ACK
break;
default:
mResult = I2cInternalError;
I2CCTL |= 0x10; // stop
mNumTrans = 0;
break;
}
I2CCTL &= ~0x08;
CFGPAGE = bkp;
}
#pragma callee_saves i2cTransact
enum I2cResult i2cTransact(struct I2cTransaction __xdata *trans, uint8_t nTrans) {
mCurTrans = trans;
mNumTrans = nTrans;
mResult = I2cOK;
if (nTrans) {
I2CCTL &= ~0x10; // clear stop
I2CCTL |= 0x20; // issue start
while (mNumTrans)
; // wait
}
return mResult;
}