📄 i2c_lib_ok_before_opt.c
字号:
#include "Types/Sfr.h"
#include "i2c.h"
#include "Rtos.H"
#define IICIPL 4
#define S2RIPL ( IICIPL - 1 )
//#define S2RIPL 7
// extern OS_TIMER i2cTimeoutTimer;
// extern OS_CSEMA i2cWaitSema;
extern byte i2cSlaveData[];
extern byte id_adr;
extern byte i2cBaudrate;
extern word miic;
static byte* datPtr;
static byte numBytes;
static byte datCnt;
static byte slvCnt = 0;
// ;;; ***************************************************************************
// ;;; ***************************************************************************
// ;;; void iic_ini(R0L,A0)
// ;;; I2C Bus Initialization
// ;;; --------------------------------------------------------------------------
_inline
void slaveWait( void )
{
// Disable STSP interrupts.
U2MR = 0x00;
BCNIC = 0x00;
S2RIC = 0x00;
_nop();
_nop();
PRCR2=1;
PD7 &= 0xfc;
// external clock mode
U2SMR = 0x01;
U2SMR2 = 0xD5;
P7_0 = 1;
U2MR = 0x0A;
// initialize transmit register with ack bit low, in case that I'm adressed
if(U2C0_TXEPT) U2TB = 0x00FF;
// clear arbitration bit
U2RB_ABT = 0;
// UART2 receive interrupt enable.
S2RIC = S2RIPL;
miic = 0;
// master action is over
}
void iic_ini(byte gotIdAdr)
{
OS_IncDI();
// set ID "Device" address for slave mode and save ID as well as data buffer address
id_adr = gotIdAdr & 0x7f;
// UART2 transmit interrupt disable
S2TIC = 0x00;
S2RIC = 0x00;
BCNIC = 0x00;
// set PD7 to input mode SDA and SCL
PRCR2 = 1;
PD7 &= 0xfc;
// set U2SMR to I2C mode, clock synch disable, SCL wait output disable,
// SDA output stop bit disable, UART2 init. bit enabled, SCL wait output
// bit 2 to UART2 clock, SDA output disable to high impedance, and
// Start/stop condition control bit to 1.
U2SMR = 0x01;
// set SDA output to high impedance
P7 |= 0x01;
// Set MSB format for I2C mode and set outputs to open drain
U2C0 = 0xB0;
// Set baudrate. _i2c_baudrate is define in i2c.c and setup in i2c.h.
U2BRG = i2cBaudrate;
// init slave wait mode, master data and slave data counters
// allow sending and receiving
slaveWait();
OS_DecRI();
OS_Delay(100);
while(U2SMR_BBS);
U2C1 = 0x05;
}
// ;;; ***************************************************************************
// ;;; ***************************************************************************
// ;;; unsigned char iic_kill(void)
// ;; Disable I2C Bus. Return values are;
// ;; 0: Stop I2C function completed.
// ;; 1: Cannot stop I2C operation, bus is busy.
// ;; M16C is currently performing Master Transmission
// ;;; --------------------------------------------------------------------------
byte iic_stop(void)
{
OS_IncDI();
P7 |= 0x03; // SDA/SCL High
// now deactivate I2C bus
PRCR2 = 1;
PD7 &= 0xFC;
U2C1 = 0x00;
U2MR = 0x00;
U2SMR = 0x00;
U2SMR2 = 0x80;
BCNIC = 0x00;
S2TIC = 0x00;
S2RIC = 0x00;
_nop();
_nop();
OS_DecRI();
return 0;
}
void makeStart( byte addr )
{
word count = 0;
OS_IncDI();
// clear arbitration flag
U2SMR2 = 0x8f;
// TIMING: SEEMS TO BE NOT NECESSARY
while(!P7_1) {
if( ++count == I2C_IRQ_TIMEOUT ) break;
}
if( !P7_1 )
{
miic = 0;
status = stat_timeout;
OS_DecRI();
return;
}
P7_0 = 0;
PRCR2 = 1;
PD7_0 = 1;
U2MR = 0;
// TIMING: at least two cycles for master synchronization
waitCycles(2);
U2C1 = 0x00;
U2SMR2_SWC2 = 1;
U2MR = 0x02;
U2C1 = 0x05;
// load address into send buffer.
U2TB = (word) addr | 0x0100;
U2SMR2_CSC = 1;
// TIMING: at least two cycles for allowing clock to prepare for sending
waitCycles(2);
S2RIC = S2RIPL;
U2SMR2_SWC2 = 0;
PRCR2 = 1;
PD7_0 = 0;
U2RB_ABT = 0;
miic |= miic_start_cond_done | miic_address_sent;
OS_DecRI();
}
//;;; --------------------------------------------------------------------------
//;; Master start write/read function; Make Start.
//;;; --------------------------------------------------------------------------
void initForMaster( byte addr )
{
int ttime;
// wait until bus free
ttime = OS_GetTime();
while(true)
{
OS_DI();
if( !(U2SMR & 0x04) ) break;
OS_EI();
if( OS_GetTime() - ttime > I2C_TIMEOUT ) {
miic = 0;
status = stat_timeout;
return;
}
}
if( BCNIC & 0x08) slaveWait();
// set flag for master action
miic |= miic_i_am_master;
datCnt = 0;
// interrupts are reenabled in makeStart
makeStart( addr );
}
bool makeStop( void )
{
word count = 0;
// first draw clock down
OS_IncDI();
P7_1 = 0;
PRCR2 = 1;
PD7 |= 0x02;
U2MR = 0;
// TIMING: Seems to be not necessary
// waitCycles(3);
// now draw SDA down
P7_0 = 0;
PRCR2 = 1;
PD7 |= 0x01;
// TIMING: half a cycle for well-defined stop condition
waitHalfCycle();
// now release clock
PRCR2 = 1;
PD7 &= ~0x02;
P7_1 = 1;
while(!P7_1) {
if( ++count == I2C_IRQ_TIMEOUT ) {
OS_DecRI();
return false;
}
}
// TIMING: half a cycle for well-defined stop condition
waitHalfCycle();
// now release SDA
PRCR2 = 1;
PD7 &= ~0x01;
P7_0 = 1;
count = 0;
while(!P7_0) {
if( ++count == I2C_IRQ_TIMEOUT ) {
OS_DecRI();
return false;
}
}
slaveWait();
OS_DecRI();
return true;
}
// enable I2C in external clock mode (slave)
_inline
byte testAckFromDvc( void )
{
word count = 0l;
byte ans = ack_OK;;
waitCycles(1);
OS_IncDI();
U2SMR2 = 0x83;
// timeout after ~200 ms
while(!P7_1) {
if( ++count == I2C_IRQ_TIMEOUT )
{
OS_DecRI();
return ack_Timeout;
}
}
// TIMING: set the sampling point to the middle of a one-cycle interval
waitHalfCycle();
if( P7_0 )
ans = ack_NoAck;
// TIMING: half a cycle is necessary for master synchronization
waitHalfCycle();
U2SMR2 = 0xA3;
OS_DecRI();
return ans;
}
_inline
void sendByte( byte dat )
{
// clear arbitration flag, because otherwise SDA is blocked by the "SDA output stop" function
U2RB_ABT = 0;
// put next data byte to transmission register
OS_IncDI();
U2TB = (word) dat | 0x0100;
// TIMING: two cycles to allow clock to prepare sending
waitCycles(2);
// release clock
U2SMR2 = 0x8F;
OS_DecRI();
}
_inline
void triggerNextReception( void )
{
U2RB_ABT = 0;
// if the last byte is going to be received, then set bit 9 of transmission buffer for "no ack".
OS_IncDI();
if( datCnt == numBytes - 1)
U2TB = 0x01FF;
else
U2TB = 0x00FF;
// TIMING: two cycles to allow clock to prepare sending
waitCycles(2);
U2SMR2 = 0x87;
OS_DecRI();
}
void writeI2C( byte *pData, byte nBytes )
{
miic |= miic_opmode_write;
datPtr = pData;
numBytes = nBytes;
datCnt = 0;
sendByte( datPtr[datCnt++] );
}
void readI2C( byte* pData, byte nBytes )
{
miic |= miic_opmode_read;
datPtr = pData;
numBytes = nBytes;
datCnt = 0;
triggerNextReception();
}
_interrupt(10)
void stspInt( void )
{
OS_EnterInterrupt();
// start or stop condition received. reenable receive interrupt and prepare UART for next reception
if(U2SMR_BBS && ( miic & miic_slave_work_in_progress ))
{
miic = 0;
}
if(!U2SMR_BBS)
{
slaveWait();
}
OS_LeaveInterruptNoSwitch();
}
_inline
byte testAckAsSlave( void )
{
word count = 0;
byte ret = ack_OK;
// set acknowledge bit by releasing clock. Since bit 8 of the transmission register is 0,
// SDA is drawn to low by the next clock cycle from the master.
OS_IncDI();
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;
OS_DecRI();
return ret;
}
_inline
void decoupleFromBus( void )
{
// no reception interrupts
S2RIC = 0x00;
BCNIC = IICIPL;
// put outputs to high impedance and switch to port control
P7 |= 0x03;
PRCR2 = 1;
PD7 &= ~0x03;
U2MR = 0;
U2SMR2_SWC2 = 0;
_nop();
_nop();
}
_inline
void waitForNextByte( void )
{
// prepare to receive next byte. Bit 8 is low for acknowledgement.
OS_IncDI();
U2TB = 0x00FF;
waitCycles(2);
U2RB_ABT = 0;
U2SMR2_SWC = 1;
U2SMR2_SWC2 = 0;
OS_DecRI();
}
_inline
void provideByteAsSlave( void )
{
// put next data byte to transmission register
OS_IncDI();
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
U2RB_ABT = 0;
// release clock
U2SMR2_SWC = 1;
U2SMR2_SWC2 = 0;
OS_DecRI();
}
//;; UART2 RECV INTERRUPT.
//;;; ******************************************************************************
_interrupt(16)
void uart2Rcv( void )
{
byte ack;
OS_EnterNestableInterrupt();
if(miic & miic_i_am_master )
{
if(miic & miic_address_sent)
{
if( !U2RB_ABT )
{
miic &= ~miic_address_sent;
ack = testAckFromDvc();
switch( ack )
{
case ack_Timeout:
status = stat_timeout;
miic = 0;
break;
case ack_OK:
status = stat_success;
break;
default:
status = stat_no_ack_from_device;
miic = 0;
if( !makeStop() ) status = stat_timeout;
break;
}
OS_LeaveNestableInterrupt();
return;
} else {
// arbitration lost. I could be addressed, so set to slave mode and go to address recognition
status = stat_arb_lost;
miic = 0;
}
}
if(miic & miic_opmode_write)
{
if( !U2RB_ABT )
{
// OK, starting to transmit data. We always expect an acknowledgement when writing.
ack = testAckFromDvc();
switch( ack )
{
case ack_Timeout:
status = stat_timeout;
miic = 0;
OS_LeaveNestableInterrupt();
return;
case ack_NoAck:
status = stat_data_error;
miic = 0;
if( !makeStop() ) status = stat_timeout;
OS_LeaveNestableInterrupt();
return;
default:
break;
}
// all data sent? then make stop condition.
if( datCnt == numBytes )
{
status = stat_success;
miic &= ~miic_opmode_write;
OS_LeaveNestableInterrupt();
return;
}
sendByte( datPtr[datCnt++] );
OS_LeaveNestableInterrupt();
return;
} else {
// disable SDA output
testAckAsSlave();
decoupleFromBus();
miic = 0;
status = stat_arb_lost;
OS_LeaveNestableInterrupt();
return;
}
}
if( miic & miic_opmode_read )
{
// save byte received
datPtr[datCnt] = (byte) (U2RB << 1);
if(U2RB & 0x0100) datPtr[datCnt] += 1;
if(datPtr[datCnt] == 0x80)
{
_nop();
} else {
_nop();
}
++datCnt;
// test acknowledgment: if no ack, reception is complete.
ack = testAckFromDvc();
switch( ack )
{
case ack_Timeout:
status = stat_timeout;
miic = 0;
OS_LeaveNestableInterrupt();
return;
case ack_NoAck:
if( datCnt != numBytes )
{
status = stat_data_error;
miic = 0;
} else {
status = stat_success;
miic &= ~miic_opmode_read;
}
OS_LeaveNestableInterrupt();
return;
default:
break;
}
triggerNextReception();
OS_LeaveNestableInterrupt();
return;
}
}
// OK, I'm slave and receiving data. If I2C address doesn't fit, go to taiki.
// otherwise initialize slave routines.
// switch on start/stop interrupt
BCNIC = IICIPL;
if( miic & miic_slave_work_in_progress )
{
if( miic & miic_opmode_slave_write )
{
i2cSlaveData[ slvCnt ] = (byte) (U2RB << 1);
if( U2RB & 0x0100 ) i2cSlaveData[ slvCnt ] += 1;
slvCnt = (slvCnt + 1) & SLV_CNT_MASK;
testAckAsSlave();
waitForNextByte();
}
if( miic & miic_opmode_slave_read )
{
if( testAckAsSlave() == ack_OK )
provideByteAsSlave();
else
{
// slaveWait();
// clear arbitration flag, because otherwise SDA is blocked by the "SDA output stop" function
U2RB_ABT = 0;
// release clock
U2SMR2_SWC = 1;
U2SMR2_STAC = 1;
U2SMR2_ASL = 1;
U2SMR2_SDHI = 0;
U2TB = 0x00FF;
U2SMR2_SWC2 = 0;
// P2_3 = 1;
// P2_3 = 0;
// P2_3 = 1;
// P2_3 = 0;
}
}
if( miic & miic_opmode_wait_for_data_adr )
{
slvCnt = (byte) ((U2RB & SLV_CNT_MASK) << 1);
if( U2RB & 0x0100 ) slvCnt += 1;
miic &= ~miic_opmode_wait_for_data_adr;
miic |= miic_opmode_slave_write;
testAckAsSlave();
waitForNextByte();
}
} else {
if( ((byte) (U2RB & 0x007f)) != id_adr )
{
// switch to external clock mode, wait for ack and decouple
testAckAsSlave();
decoupleFromBus();
} else {
// I'm addressed.
miic |= miic_slave_work_in_progress;
// enable SDA output
U2SMR2_CSC = 0;
U2SMR2_SDHI = 0;
PRCR2 = 1;
PD7_0 = 1;
P7_0 = 0;
U2MR = 0;
testAckAsSlave();
PRCR2 = 1;
PD7_0 = 0;
P7_0 = 1;
U2MR = 0x0A;
U2SMR2_ASL = 0;
U2SMR2_STAC = 1;
if( U2RB & 0x0100 )
{
miic |= miic_opmode_slave_read;
provideByteAsSlave();
} else {
miic |= miic_opmode_wait_for_data_adr;
waitForNextByte();
}
}
}
OS_LeaveNestableInterrupt();
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -