📄 i2c_lib.c
字号:
#include "Types/BaseTypes.h"
#include "Rtos.H"
#include "i2c.h"
#pragma optimize 1
#pragma optimize Tw
#define IICIPL 6
#define S2RIPL 3
extern byte i2cBaudrate;
extern bool flgOSStarted;
// the following globals are shared and must be made "threadsafe"
extern byte i2cSlaveData[];
static byte slvCnt = 0;
bool flgRepeatedStart;
byte debugdat = stat_success;
// ;;; ***************************************************************************
// ;;; ***************************************************************************
// ;;; void iic_ini(R0L,A0)
// ;;; I2C Bus Initialization
// ;;; --------------------------------------------------------------------------
/*
* -----------------------------------------------------------------
* slaveWait:
* switch into a mode waiting for data from an other master
* -----------------------------------------------------------------
*/
_inline
void slaveWait( void ) // must be called with interrupts off
{
// UART2 receive interrupt enable.
S2RIC = S2RIPL;
// Disable STSP interrupts.
BCNIC = 0x00;
PRCR2 = 1;
PD7 &= 0xfc;
// Init function, SDA high, SCL wait after reception
U2SMR2 = 0xD5;
// set to I2C mode, external clock
U2MR = 0x0A; // external clock, i2c-mode
// clear arbitration bit
U2RB_ABT = 0;
// initialize transmit register with ack bit low, in case that I'm adressed
// if(U2C0_TXEPT)
// U2TB = 0x00FF;
U2TB = 0x00FF;
// clear slave flags
i2cContext.state=I2C_IDLE;
}
/*
* Interface function
*/
void iic_ini_lowlevel( void )
{
OS_IncDI();
// UART2 transmit interrupt disable
S2TIC = 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 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();
}
// ;;; ***************************************************************************
// ;;; ***************************************************************************
// ;;; unsigned char iic_stop(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();
S2TIC = 0x00;
S2RIC = 0x00;
BCNIC = 0x00;
P7 |= 0x03; // SDA/SCL High
// now deactivate I2C bus
PRCR2 = 1;
PD7 &= 0xFC;
U2C1 = 0x00;
U2MR = 0x00; // internal clock, Port mode
U2SMR = 0x00;
U2SMR2 = 0x80;
OS_DecRI();
return 0;
}
_inline
bool makeStart( byte addr )
{
word count = 0;
OS_IncDI();
// set SDA to high impedance before releasing clock for clean start condition
U2SMR2 = 0xC1;
// switch to port control
U2MR = 0x00; // port control
// turn transmission and reception off. If this is omitted, reception sometimes fails, I don't know why.
U2C1 = 0x00;
// delay for clean start condition
waitHalfCycle();
// generate start condition by pulling SDA down
P7_0 = 0;
PRCR2 = 1;
PD7_0 = 1;
// delay for clean start condition
waitCycles(1); // half cycle should be enough (tFall+t_HD_STA)=300+4us jk
//waitHalfCycle(); //jk
//check if SCL is already down, if yes we may have lost arbitration
// clear arbitration flag and pull clock down. Enable UART control.
U2RB_ABT = 0; // reset abort indication
U2SMR2 = 0xAF; // Pull down clk
U2MR = 0x02; // internal clk, i2c mode
i2cContext.state=I2C_MASTER;
// now clock is down, we are safe from interrupts
OS_DecRI();
// TIMING: at least two cycles for master synchronization
waitCycles(2);
// waitHalfCycle();
// switch interrupts on and close port which was opened to generate start condition
OS_IncDI();
S2RIC = S2RIPL;
PRCR2 = 1;
PD7_0 = 0;
P7_0 = 1;
OS_DecRI();
// turn transmission and reception on again. If this is omitted, reception sometimes fails,
// I don't know why.
U2C1 = 0x05;
// load address into send buffer. clear 9th bit for acknowledgement in case that I lose during
// device address arbitration.
U2TB = (word) addr | 0x0000;
// keep clock low for two cycles, which is the time needed for the transmission shift register
// to be loaded and before the clock starts
waitCycles(2);
// release clock
U2SMR2_SWC2 = 0; // release clock
return true;
}
_inline bool
makeRepStart(byte addr) {
I2C_Context *ctxt=&i2cContext;
word count = 0;
OS_IncDI();
// set SDA to high impedance before releasing clock for clean start condition
U2SMR2 = 0xC1;
// switch to port control
U2MR = 0x00; // port control
// turn transmission and reception off. If this is omitted, reception sometimes fails, I don't know why.
U2C1 = 0x00;
// in case of repeated start condition, wait until clock is high to synchronize masters
while( !P7_1 )
{
if( ++count == I2C_IRQ_TIMEOUT )
{
ctxt->result=stat_timeout_in_start;
ctxt->state=I2C_IDLE;
OS_DecRI();
return false;
}
}
// delay for clean start condition
waitHalfCycle();
// generate start condition by pulling SDA down
P7_0 = 0;
PRCR2 = 1;
PD7_0 = 1;
// delay for clean start condition
waitCycles(1);
// clear arbitration flag and pull clock down. Enable UART control.
U2RB_ABT = 0; // reset abort indication
U2SMR2 = 0xAF; // Pull down clk
U2MR = 0x02; // internal clk, i2c mode
ctxt->state=I2C_MASTER;
// now clock is down, we are safe from interrupts
OS_DecRI();
// TIMING: at least two cycles for master synchronization
waitCycles(2);
// waitHalfCycle();
// switch interrupts on and close port which was opened to generate start condition
OS_IncDI();
S2RIC = S2RIPL;
PRCR2 = 1;
PD7_0 = 0;
P7_0 = 1;
OS_DecRI();
// turn transmission and reception on again. If this is omitted, reception sometimes fails,
// I don't know why.
U2C1 = 0x05;
// load address into send buffer. clear 9th bit for acknowledgement in case that I lose during
// device address arbitration.
U2TB = (word) addr | 0x0000;
// keep clock low for two cycles, which is the time needed for the transmission shift register
// to be loaded and before the clock starts
waitCycles(2);
// release clock
U2SMR2_SWC2 = 0; // release clock
return true;
}
void
initForMaster( byte i2c_addr ,byte dir_in)
{
volatile word ttime1;
volatile word ttime2;
byte startadr;
I2C_Context *ctxt=&i2cContext;
// wait until bus free
startadr = i2c_addr << 1;
if( dir_in )
startadr|=1;
ttime1 = ( volatile word ) OS_GetTime();
while(true)
{
OS_DI();
if( !(U2SMR_BBS) )
break;
OS_EI();
ttime2 = (volatile word) OS_GetTime();
ttime2 = ( ttime2 > ttime1 ? (ttime2 - ttime1) : ~(ttime1 - ttime2) + 1);
if( ttime2 > I2C_TIMEOUT_MASTER ) {
ctxt->result=stat_timeout_in_init;
// script does not need to be aborted because we are still in idle state
OS_SignalCSema( &ctxt->i2cWaitSema );
return;
}
}
if( BCNIC & 0x08)
slaveWait();
// set flag for master action
// interrupts are reenabled in makeStart
if( !makeStart( startadr )) {
if( flgOSStarted )
OS_SignalCSema( &ctxt->i2cWaitSema );
}
}
static bool
makeStop( void )
{
word count = 0;
I2C_Context *ctxt=&i2cContext;
// first draw clock down
P7_1 = 0;
OS_IncDI();
PRCR2 = 1;
PD7_1 = 1;
OS_DecRI();
// switch to port control
U2MR = 0; // Port control
// now draw SDA down
P7_0 = 0;
OS_IncDI();
PRCR2 = 1;
PD7_0 = 1;
OS_DecRI();
// TIMING: half a cycle for well-defined stop condition
waitHalfCycle();
// now release clock
OS_IncDI();
PRCR2 = 1;
PD7_1 = 0;
P7_1 = 1;
OS_DecRI();
// wait until clock goes high for master synchronization
while(!P7_1) {
if( ++count == I2C_IRQ_TIMEOUT ) {
ctxt->result=stat_timeout_in_stop;
return false;
}
}
// TIMING: half a cycle for well-defined stop condition
waitHalfCycle();
// now release SDA. Disable interrupts to make sure that the I2C
// interface is set to slave mode by slaveWait right after the stop condition
// has been produced.
OS_IncDI();
PRCR2 = 1;
PD7_0 = 0;
P7_0 = 1;
count = 0;
while(!P7_0) {
if( ++count == I2C_IRQ_TIMEOUT ) {
OS_DecRI();
ctxt->result=stat_timeout_in_stop;
return false;
}
}
slaveWait();
OS_DecRI();
return true;
}
_inline
byte testAckFromDvc( void )
{
// this function is normally entered with U2SMR2 = 0x8F
word count = 0l;
byte ans = ack_OK;
//waitCycles(1); // not necessary because the clock already did this
// interrupts must not hit here
OS_IncDI();
// release clock
U2SMR2_SWC = 0;
// wait until clock goes high for synchronization with other master
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();
// draw clock down. Now we are safe again from interrupts
U2SMR2_SWC2 = 1;
OS_DecRI();
return ans;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -