📄 i2c_lib.c
字号:
_inline
void sendByte( byte dat )
{
// here arbitration functionality bit must be set
U2RB_ABT = 0;
U2SMR2 = 0xAF; // clock low, clock sync on, scl wait 9th, sda output stop on arb. loss
U2TB = (word) dat | 0x0100;
// TIMING: two cycles to allow clock to prepare sending
waitCycles(2);
// release clock
U2SMR2_SWC2 = 0;
}
_inline
void decoupleFromBus( void )
{
// no reception interrupts
OS_IncDI();
BCNIC = IICIPL;
S2RIC = 0x00; // Interrupt Level priority 0 (denied)
// Ports to input mode. This is garanted to be high
PRCR2 = 1;
PD7_0 &= 0xfc;
U2MR = 0; // port control
U2SMR2 = 0x81;
i2cContext.state=I2C_DECOUPLED;
OS_DecRI();
}
/*
* ---------------------------------------------
* Test Ack as Slave
* returns with SCL low forced by SWC2
* ---------------------------------------------
*/
_inline
byte testAckAsSlave( void )
{
word count = 0;
byte ret = ack_OK;
OS_IncDI();
U2RB_ABT = 0;
// release clock
U2SMR2_SWC = 0;
// wait until clock goes high, timeout after ~200 ms
while(!P7_1) {
if( ++count == I2C_IRQ_TIMEOUT )
{
OS_DecRI();
return ack_Timeout;
}
}
if( P7_0 ) ret = ack_NoAck;
count = 0;
while(P7_1) {
if( ++count == I2C_IRQ_TIMEOUT )
{
OS_DecRI();
return ack_Timeout;
}
}
// stop clock
U2SMR2_SWC2 = 1; // disable clock
OS_DecRI();
return ret;
}
/*
* Re-allow clock to run for one byte
*/
_inline
void waitForNextByte( void )
{
// here arbitration functionality bit must be set
U2RB_ABT = 0; // clear arbitration loss
U2SMR2 = 0xB5; // no sda-output, scl-wait
// prepare to receive next byte. Bit 8 is low for acknowledgement.
U2TB = 0x00FF; // sda=high except acknowledge
waitCycles(2);
U2SMR2_SWC2 = 0; // enable clock
}
_inline
void provideByteAsSlave( void )
{
// here arbitration functionality bit must be set
U2RB_ABT = 0;
U2SMR2 = 0xB5;
U2TB = (word) i2cSlaveData[slvCnt] | 0x0100;
slvCnt = (slvCnt + 1) & SLV_CNT_MASK;
// wait until transmission module is ready
waitCycles(2);
// clear arbitration flag, because otherwise SDA is blocked by the "SDA output stop" function
// release clock
U2SMR2_SWC2 = 0;
}
void new_uart2Receive(void);
I2C_Context i2cContext;
_interrupt(10)
void stspInt( void )
{
I2C_Context *ctxt;
OS_EnterInterrupt();
ctxt=&i2cContext;
// start or stop condition received. reenable receive interrupt and prepare UART for next reception
if( U2SMR_BBS )
{
// may only happen on repeated start as slave because this interrupt is off during IDLE
} else {
slaveWait();
}
OS_LeaveInterruptNoSwitch();
}
_inline
void triggerNextReceptionNew( bool nack )
{
// Normally this function is entered with U2SMR2 = 0xA1.
U2RB_ABT = 0;
U2SMR2 = 0xA7;
// no ack on last
if(nack)
U2TB = 0x01FF;
else
U2TB = 0x00FF;
// TIMING: two cycles to allow clock to prepare sending
waitCycles(2); // This is neccessary I maybe clock is not stopped because tx is not empty
U2SMR2_SWC2 = 0;
}
_inline byte
read_rxbyte(void) {
byte b = (U2RB<<1);
if(U2RB&0x100)
b++;
return b;
}
/*
* ----------------------------------------------------------------
* Interpreter for MASTER scripts
* executes commands until a command needs to wait for UART or
* CMD_I2C_SCRIPT_END is in the script
* ----------------------------------------------------------------
*/
static void
i2c_interpreter(void) {
byte ack;
int cmd;
word *countP;
I2C_Context *ctxt=&i2cContext;
while(1) {
cmd = ctxt->script[ctxt->instrP++];
switch(cmd) {
case CMD_I2C_TRIGGER_RX:
triggerNextReceptionNew(0);
return;
case CMD_I2C_TRIGGER_RX_NOACK:
triggerNextReceptionNew(1);
return;
case CMD_I2C_WRITEB:
sendByte(ctxt->script[ctxt->instrP++]);
return;
case CMD_I2C_WRITEDATA:
sendByte(ctxt->data[ctxt->dataP++]);
return;
case CMD_I2C_LOOP:
ctxt->scriptstack[ctxt->stackP++]=ctxt->script[ctxt->instrP++];
ctxt->scriptstack[ctxt->stackP++]=ctxt->script[ctxt->instrP++];
ctxt->scriptstack[ctxt->stackP++]=ctxt->instrP;
break;
case CMD_I2C_ENDLOOP:
countP=(word*)(ctxt->scriptstack+ctxt->stackP-3);
if(--(*countP)) {
ctxt->instrP=ctxt->scriptstack[ctxt->stackP-1];
} else {
ctxt->stackP-=3;
}
break;
case CMD_I2C_WAKEUP:
if( flgOSStarted )
OS_SignalCSema( &ctxt->i2cWaitSema );
break;
case CMD_I2C_READDATA:
ctxt->data[ctxt->dataP++]=read_rxbyte();
break;
case CMD_I2C_SEND_NACKBIT:
ack=testAckFromDvc();
if(ack!=ack_NoAck) {
ctxt->result = stat_arb_lost_in_nack; // maybe this is a success
decoupleFromBus();
if(flgOSStarted)
OS_SignalCSema(&ctxt->i2cWaitSema);
return;
}
break;
case CMD_I2C_SEND_ACKBIT:
testAckFromDvc();
break;
case CMD_I2C_CHECK_ACK:
U2SMR2_SDHI = 1; // allow slave to give ack/nack
ack = testAckFromDvc();
switch( ack )
{
case ack_Timeout:
ctxt->result = stat_timeout_in_write;
if( flgOSStarted )
OS_SignalCSema( &ctxt->i2cWaitSema );
return;
case ack_NoAck:
ctxt->result = stat_data_error_write;
makeStop();
if( flgOSStarted )
OS_SignalCSema( &ctxt->i2cWaitSema );
return;
}
break;
case CMD_I2C_CHECK_ADDR_ARB:
if(U2RB_ABT) {
ctxt->result = stat_arb_lost_in_address;
U2MR = 0x0A; // external clock
// SDA is automatically released
if( flgOSStarted )
OS_SignalCSema( &ctxt->i2cWaitSema );
return;
}
break;
case CMD_I2C_CHECK_ARB:
if( U2RB_ABT ) {
ctxt->result = stat_arb_lost_in_write;
decoupleFromBus();
if( flgOSStarted )
OS_SignalCSema( &ctxt->i2cWaitSema );
return;
}
break;
case CMD_I2C_REPSTART:
makeRepStart(ctxt->script[ctxt->instrP++] );
return;
case CMD_I2C_STOP:
makeStop();
break;
default:
ctxt->result=stat_script_syntax_error;
case CMD_I2C_SCRIPT_END:
if(ctxt->result==stat_in_progress)
ctxt->result=stat_success;
ctxt->stackP=0;
ctxt->instrP--; // do not advance past end
return;
}
}
}
/*
* ------------------------------------------
* Interrupthandler runs the state machine
* while in master mode we are under control
* of the interpreter
* ------------------------------------------
*/
_interrupt(16)
void uart2Rcv( void )
{
I2C_Context *ctxt;
OS_EnterNestableInterrupt();
ctxt=&i2cContext;
switch(ctxt->state) {
case I2C_MASTER:
i2c_interpreter();
if(ctxt->result != stat_arb_lost_in_address) {
break;
}
case I2C_IDLE:
if((U2RB&0x7f)!=ctxt->my_i2cAddr) {
decoupleFromBus();
} else {
OS_IncDI();
BCNIC = IICIPL; // enable Start/Stop Interrupt
U2SMR2 = 0x85; // no sda output, no init on start, scl clock out enabled
U2RB_ABT = 0; // Clear Arbitration loss flag
OS_DecRI();
testAckAsSlave();
if(!U2SMR_BBS) {
slaveWait();
break;
}
if(U2RB & 0x100) { // true means read
slvCnt=0;
ctxt->state=I2C_SLAVE_READ;
provideByteAsSlave();
} else {
ctxt->state=I2C_SLAVE_WAITADDR;
waitForNextByte();
}
}
break;
case I2C_SLAVE_WAITADDR:
slvCnt=read_rxbyte() & SLV_CNT_MASK;
testAckAsSlave(); // never should fail because I was acknowledger
if(ctxt->state==I2C_SLAVE_WAITADDR) {
ctxt->state=I2C_SLAVE_WRITE;
waitForNextByte();
}
break;
case I2C_SLAVE_WRITE:
i2cSlaveData[slvCnt]=read_rxbyte();
slvCnt=(slvCnt+1) & SLV_CNT_MASK;
testAckAsSlave(); // never should fail because I was acknowledger
if(ctxt->state==I2C_SLAVE_WRITE) {
waitForNextByte();
}
break;
case I2C_SLAVE_READ:
if(testAckAsSlave()==ack_OK) {
if(ctxt->state==I2C_SLAVE_READ) {
provideByteAsSlave();
}
} else {
if(ctxt->state==I2C_SLAVE_READ) {
decoupleFromBus();
}
}
break;
case I2C_DECOUPLED:
default:
break;
}
OS_LeaveNestableInterrupt();
}
#pragma endoptimize
#pragma endoptimize
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -