📄 i2c_timing_layer.c
字号:
/**
*@file i2c_timing_layer.c
*@author Zlatan Stanojevic
*/
/**
* Controls IVG used for internal timing.
*/
#define TIMING_INT 10
#define FRAMING_INT 11
#if defined( __ADSPBF533__ ) || defined( __ADSPBF561__ )
#include "i2c_timing_layer.h"
#ifdef __ADSPBF533__
#include <defBF533.h>
#include <cdefBF533.h>
#else
#ifdef __ADSPBF561__
#include <defBF561.h>
#include <cdefBF561.h>
#else
#error Target not supported by I2C emulator.
#endif
#endif
#include <sys/exception.h>
#include "../../misc/clock_query.h"
#include <services/services.h>
#include "timers.h"
inline void raise(int ivg)
{
switch(ivg)
{
case 7: asm( "raise 7;" ); break;
case 8: asm( "raise 8;" ); break;
case 9: asm( "raise 9;" ); break;
case 10: asm( "raise 10;" ); break;
case 11: asm( "raise 11;" ); break;
case 12: asm( "raise 12;" ); break;
case 13: asm( "raise 13;" ); break;
case 14: asm( "raise 14;" ); break;
case 15: asm( "raise 15;" ); break;
}
}
void (*pTimingCallback)( I2CTimingStatus, I2CByte * );
#define SCL_PIN_MASK (unsigned short)scl_pin_mask
#define SDA_PIN_MASK (unsigned short)sda_pin_mask
#ifdef __ADSPBF533__
#define pSCL_DIR_REG pFIO_DIR
#define pSCL_D_REG pFIO_FLAG_D
#define pSCL_S_REG pFIO_FLAG_S
#define pSCL_C_REG pFIO_FLAG_C
#define pSCL_INEN_REG pFIO_INEN
#define pSDA_DIR_REG pFIO_DIR
#define pSDA_D_REG pFIO_FLAG_D
#define pSDA_S_REG pFIO_FLAG_S
#define pSDA_C_REG pFIO_FLAG_C
#define pSDA_INEN_REG pFIO_INEN
#define scl_mask_s_reg pFIO_MASKA_S
#define scl_mask_c_reg pFIO_MASKA_C
#else
#define pSCL_DIR_REG scl_dir_reg
#define pSCL_D_REG scl_d_reg
#define pSCL_S_REG scl_s_reg
#define pSCL_C_REG scl_c_reg
#define pSCL_INEN_REG scl_inen_reg
#define pSDA_DIR_REG sda_dir_reg
#define pSDA_D_REG sda_d_reg
#define pSDA_S_REG sda_s_reg
#define pSDA_C_REG sda_c_reg
#define pSDA_INEN_REG sda_inen_reg
#endif
#define IS_SCL_HIGH ( *pSCL_D_REG & SCL_PIN_MASK )
#define IS_SDA_HIGH ( *pSDA_D_REG & SDA_PIN_MASK )
#define IS_SCL_LOW !IS_SCL_HIGH
#define IS_SDA_LOW !IS_SDA_HIGH
#define SET_SCL_HIGH ( *pSCL_S_REG = SCL_PIN_MASK )
#define SET_SDA_HIGH ( *pSDA_S_REG = SDA_PIN_MASK )
#define SET_SCL_LOW ( *pSCL_C_REG = SCL_PIN_MASK )
#define SET_SDA_LOW ( *pSDA_C_REG = SDA_PIN_MASK )
#define SET_SCL_INPUT ( *pSCL_DIR_REG &= ~SCL_PIN_MASK )
#define SET_SDA_INPUT ( *pSDA_DIR_REG &= ~SDA_PIN_MASK )
#define SET_SCL_OUTPUT ( *pSCL_DIR_REG |= SCL_PIN_MASK )
#define SET_SDA_OUTPUT ( *pSDA_DIR_REG |= SDA_PIN_MASK )
#ifdef __ADSPBF533__
#define SCL_PULLDOWN ( *pSCL_INEN_REG &= ~SCL_PIN_MASK, *pSCL_DIR_REG |= SCL_PIN_MASK, *pSCL_C_REG = SCL_PIN_MASK )
#define SDA_PULLDOWN ( *pSDA_INEN_REG &= ~SDA_PIN_MASK, *pSDA_DIR_REG |= SDA_PIN_MASK, *pSDA_C_REG = SDA_PIN_MASK )
#define SCL_RELEASE ( *pSCL_DIR_REG &= ~SCL_PIN_MASK, *pSCL_INEN_REG |= SCL_PIN_MASK )
#define SDA_RELEASE ( *pSDA_DIR_REG &= ~SDA_PIN_MASK, *pSDA_INEN_REG |= SDA_PIN_MASK )
#else
#define SCL_PULLDOWN ( *pSCL_INEN_REG &= ~SCL_PIN_MASK, *pSCL_DIR_REG |= SCL_PIN_MASK, *pSCL_C_REG = SCL_PIN_MASK )
#define SDA_PULLDOWN ( *pSDA_INEN_REG &= ~SDA_PIN_MASK, *pSDA_DIR_REG |= SDA_PIN_MASK, *pSDA_C_REG = SDA_PIN_MASK )
#define SCL_RELEASE ( *pSCL_DIR_REG &= ~SCL_PIN_MASK, *pSCL_INEN_REG |= SCL_PIN_MASK )
#define SDA_RELEASE ( *pSDA_DIR_REG &= ~SDA_PIN_MASK, *pSDA_INEN_REG |= SDA_PIN_MASK )
#endif
#define IS_BUSY busy_flag
#define PERIOD *pTPERIOD
static unsigned short scl_pin_mask = 1 << 8;
static unsigned short sda_pin_mask = 1 << 9;
#ifdef __ADSPBF561__
static volatile unsigned short *scl_dir_reg;
static volatile unsigned short *scl_d_reg;
static volatile unsigned short *scl_s_reg;
static volatile unsigned short *scl_c_reg;
static volatile unsigned short *scl_inen_reg;
static volatile unsigned short *scl_mask_s_reg;
static volatile unsigned short *scl_mask_c_reg;
static volatile unsigned short *sda_dir_reg;
static volatile unsigned short *sda_d_reg;
static volatile unsigned short *sda_s_reg;
static volatile unsigned short *sda_c_reg;
static volatile unsigned short *sda_inen_reg;
#endif
unsigned char busy_flag;
ADI_INT_HANDLER_RESULT TimingISR( void * );
ADI_INT_HANDLER_RESULT FramingISR( void * );
void stdCallback( I2CTimingStatus, I2CByte * );
struct
{
I2CByte *last;
I2CByte *msg;
enum
{
START_COND,
DATA_SECTION,
DATA_SECTION_READ,
RAISE,
LOWER,
WAIT,
ACK_BIT,
ACK_RELEASE,
ACK_BIT_WRITE,
STUFF,
STUFF_RESTART,
STOP_COND,
FREE
} state : 4;
unsigned char bitno : 3;
unsigned char success : 1;
unsigned char t_scale;
unsigned long t_period;
int timer;
} tx;
void I2CInitTiming( unsigned char pa_iSclPin, unsigned char pa_iSdaPin,
int pa_iTimer, unsigned long pa_lFrequency )
{
#ifdef __ADSPBF533__
scl_pin_mask = 1 << pa_iSclPin; //generate bitmasks
sda_pin_mask = 1 << pa_iSdaPin;
*pFIO_INEN |= SCL_PIN_MASK | SDA_PIN_MASK; //enable input buffers
adi_int_SICEnable( ADI_INT_PFA );
adi_int_SICSetIVG( ADI_INT_PFA, TIMING_INT );
//*pSIC_IMASK |= 1 << 19;
//*pSIC_IAR2 &= ~( 0xf << 12 );
#endif
#ifdef __ADSPBF561__
if( pa_iSclPin >= 32 )
{
scl_dir_reg = pFIO2_DIR; //assign system registers
scl_d_reg = pFIO2_FLAG_D;
scl_s_reg = pFIO2_FLAG_S;
scl_c_reg = pFIO2_FLAG_C;
scl_inen_reg = pFIO2_INEN;
scl_mask_s_reg = pFIO2_MASKA_S;
scl_mask_c_reg = pFIO2_MASKA_C;
pa_iSclPin -= 32;
scl_pin_mask = 1 << pa_iSclPin; //generate bitmasks
*pFIO2_INEN |= SCL_PIN_MASK; //enable input buffers
//*pSICA_IMASK1 |= 1 << 19;
adi_int_SICEnable( ADI_INT_PF32_47_A );
adi_int_SICSetIVG( ADI_INT_PF32_47_A, TIMING_INT );
}
else if( pa_iSclPin >= 16 )
{
scl_dir_reg = pFIO1_DIR; //assign system registers
scl_d_reg = pFIO1_FLAG_D;
scl_s_reg = pFIO1_FLAG_S;
scl_c_reg = pFIO1_FLAG_C;
scl_inen_reg = pFIO1_INEN;
scl_mask_s_reg = pFIO1_MASKA_S;
scl_mask_c_reg = pFIO1_MASKA_C;
pa_iSclPin -= 16;
scl_pin_mask = 1 << pa_iSclPin; //generate bitmasks
*pFIO1_INEN |= SCL_PIN_MASK; //enable input buffers
//*pSICA_IMASK1 |= 1 << 17;
adi_int_SICEnable( ADI_INT_PF16_31_A );
adi_int_SICSetIVG( ADI_INT_PF16_31_A, TIMING_INT );
}
else
{
scl_dir_reg = pFIO0_DIR; //assign system registers
scl_d_reg = pFIO0_FLAG_D;
scl_s_reg = pFIO0_FLAG_S;
scl_c_reg = pFIO0_FLAG_C;
scl_inen_reg = pFIO0_INEN;
scl_mask_s_reg = pFIO0_MASKA_S;
scl_mask_c_reg = pFIO0_MASKA_C;
scl_pin_mask = 1 << pa_iSclPin; //generate bitmasks
*pFIO0_INEN |= SCL_PIN_MASK; //enable input buffers
//*pSICA_IMASK1 |= 1 << 15;
adi_int_SICEnable( ADI_INT_PF0_15_A );
adi_int_SICSetIVG( ADI_INT_PF0_15_A, TIMING_INT );
}
if( pa_iSdaPin >= 32 )
{
sda_dir_reg = pFIO2_DIR;
sda_d_reg = pFIO2_FLAG_D;
sda_s_reg = pFIO2_FLAG_S;
sda_c_reg = pFIO2_FLAG_C;
sda_inen_reg = pFIO2_INEN;
pa_iSdaPin -= 32;
sda_pin_mask = 1 << pa_iSdaPin; //generate bitmasks
*pFIO2_INEN |= SDA_PIN_MASK; //enable input buffers
//*pSICA_IMASK1 |= 1 << 19;
}
else if( pa_iSdaPin >= 16 )
{
sda_dir_reg = pFIO1_DIR;
sda_d_reg = pFIO1_FLAG_D;
sda_s_reg = pFIO1_FLAG_S;
sda_c_reg = pFIO1_FLAG_C;
sda_inen_reg = pFIO1_INEN;
pa_iSdaPin -= 16;
sda_pin_mask = 1 << pa_iSdaPin; //generate bitmasks
*pFIO1_INEN |= SDA_PIN_MASK; //enable input buffers
//*pSICA_IMASK1 |= 1 << 17;
}
else
{
sda_dir_reg = pFIO0_DIR;
sda_d_reg = pFIO0_FLAG_D;
sda_s_reg = pFIO0_FLAG_S;
sda_c_reg = pFIO0_FLAG_C;
sda_inen_reg = pFIO0_INEN;
sda_pin_mask = 1 << pa_iSdaPin; //generate bitmasks
*pFIO0_INEN |= SDA_PIN_MASK; //enable input buffers
//*pSICA_IMASK1 |= 1 << 15;
}
//*pSICA_IAR5 &= ~( 0xf << 28 );
#endif
//register_handler( ik_timer, TimingISR ); //timer driven interrupt
//adi_int_CECHook( 6, TimingISR, 0, FALSE );
adi_int_CECHook( FRAMING_INT, FramingISR, 0, TRUE );
pTimingCallback = stdCallback;
tx.timer = pa_iTimer;
//using pf int a @ int level 8
//register_handler( ik_ivg7, TimingISR ); //wait-state interrupt
adi_int_SICEnable( getTimerPeripheralID( tx.timer ) );
adi_int_SICSetIVG( getTimerPeripheralID( tx.timer ), TIMING_INT );
adi_int_CECHook( TIMING_INT, TimingISR, 0, FALSE );
//*pSIC_IAR2 |= 1 << 12;
//*pIMASK |= 1 << 7;
// tx.t_scale = 1;
tx.t_period = querySystemClock() / pa_lFrequency / 2;
/* while( ( tx.t_scale << 1 ) < 0xff )
{
tx.t_scale <<= 1;
tx.t_period >>= 1;
}
*/
SDA_PULLDOWN;
SCL_PULLDOWN;
SDA_RELEASE;
SCL_RELEASE;
busy_flag = 0;
}
void stdCallback( I2CTimingStatus stat, I2CByte *msg ) {} //does nothing
static inline int scl_interrupt_enabled( void )
{
return *scl_mask_s_reg & SCL_PIN_MASK;
}
static inline void enable_scl_interrupt( void )
{
*scl_mask_s_reg = SCL_PIN_MASK; //unmask scl-pin-interrupt
}
static inline void disable_scl_interrupt( void )
{
*scl_mask_c_reg = SCL_PIN_MASK; //mask scl-pin-interrupt
}
static inline void reset_timer( void )
{
/* *pTSCALE = tx.t_scale;
*pTPERIOD = tx.t_period;
*pTCOUNT = *pTPERIOD;
*/
timerDisable( tx.timer );
while( timerIsRunning( tx.timer ) );
*(timerConfig( tx.timer )) = EMU_RUN | OUT_DIS | IRQ_ENA | PERIOD_CNT | PWM_OUT;
*(timerPeriod( tx.timer )) = tx.t_period;
// *(timerCounter( tx.timer )) = 0;
timerEnable( tx.timer );
}
static int i2c_interrupt_pending = 0;
static ADI_INT_HANDLER_RESULT FramingISR( void *param )
{
if( ! i2c_interrupt_pending )
return ADI_INT_RESULT_NOT_PROCESSED;
pTimingCallback( tx.success ? I2CSTAT_SUCCESS : I2CSTAT_FAILURE, tx.last );
while( ! ( tx.last->last_byte || (tx.last++)->callback ) );
i2c_interrupt_pending = 0;
return ADI_INT_RESULT_PROCESSED;
}
struct
{
} rx;
void I2CTransmit( I2CByte *pa_pMsg )
{
tx.msg = pa_pMsg;
//prepare for transmission
tx.state = DATA_SECTION; //first byte (address) is always read by slave
tx.bitno = 0; //reset bit-counter
tx.last = tx.msg;
while( IS_BUSY ); //wait for free line
IS_BUSY = !0;
SDA_PULLDOWN; //generate start condition
SCL_RELEASE;
reset_timer(); //set up core timer
// *pTCNTL |= 0x7;
}
//EX_INTERRUPT_HANDLER( TimingISR )
static ADI_INT_HANDLER_RESULT TimingISR( void *param )
{
{
if( ! ( timerClearInterrupt( tx.timer ) || scl_interrupt_enabled() ) )
return ADI_INT_RESULT_NOT_PROCESSED;
}
switch( tx.state )
{
case RAISE: //delay sda setup
SDA_RELEASE;
tx.state = DATA_SECTION;
break;
case LOWER: //delay sda setup
SDA_PULLDOWN;
tx.state = DATA_SECTION;
break;
case START_COND: //generate a (re)start condition
SCL_RELEASE;
SDA_PULLDOWN;
tx.state = DATA_SECTION;
tx.bitno = 0;
break;
case WAIT: //waiting for slave to release scl line
disable_scl_interrupt();
reset_timer();
tx.state = tx.msg->read_byte ? DATA_SECTION_READ : DATA_SECTION;
//*pTCNTL |= 0x7;
break;
case DATA_SECTION: //state of writing one byte to line
if( IS_SCL_HIGH )
{
*(timerPeriod( tx.timer )) /= 2;//PERIOD /= 2;
SCL_PULLDOWN;
if( ( tx.msg->data << tx.bitno ) & 0x80 )
tx.state = RAISE;
else
tx.state = LOWER;
}
else
{
*(timerPeriod( tx.timer )) *= 2;//PERIOD *= 2;
switch( tx.bitno++ )
{
case 0:
enable_scl_interrupt();
tx.state = WAIT;
timerDisable( tx.timer );//*pTCNTL &= 0xc;
break;
case 7:
tx.state = ACK_BIT;
}
SCL_RELEASE;
}
break;
case DATA_SECTION_READ: //state of reading a byte from line
if( IS_SCL_HIGH )
{
SDA_RELEASE;
SCL_PULLDOWN;
}
else
{
tx.msg->data <<= 1;
if( IS_SDA_HIGH )
tx.msg->data |= 0x01;
if( tx.bitno++ == 7 )
tx.state = ACK_BIT_WRITE;
SCL_RELEASE;
}
break;
case ACK_RELEASE: //delay sda ack-setup
SDA_RELEASE;
tx.state = ACK_BIT;
break;
case ACK_BIT: //expect ack from slave
if( IS_SCL_HIGH )
{
SCL_PULLDOWN;
tx.state = ACK_RELEASE;
*(timerPeriod( tx.timer )) /= 2;//PERIOD /= 2;
}
else
{
i2c_interrupt_pending = tx.msg->callback;
*(timerPeriod( tx.timer )) *= 2;//PERIOD *= 2;
if( tx.msg->ack = ( ( !IS_SDA_LOW ) == tx.msg->ack ) )
{ //acknowledged
tx.success = !0;
if( tx.msg->last_byte )
{
tx.state = STUFF;
}
else if( (++tx.msg)->restart )
{
tx.state = STUFF_RESTART;
}
else
{
tx.state = tx.msg->read_byte ? DATA_SECTION_READ : DATA_SECTION;
}
}
else
{ //not acknowledged - retry
tx.success = 0;
if( tx.msg->retry || (++tx.msg)->restart )
tx.state = STUFF_RESTART;
else
{
tx.state = STUFF;
//transmission failure
}
}
tx.bitno = 0;
SCL_RELEASE;
if( i2c_interrupt_pending )
asm( "raise 14;" );
}
break;
case ACK_BIT_WRITE: //acknowledge byte from slave
if( IS_SCL_HIGH )
{
SCL_PULLDOWN; //pull down line for acknowledgement
if( tx.msg->ack )
SDA_RELEASE;
else
SDA_PULLDOWN;
}
else
{
i2c_interrupt_pending = tx.msg->callback;
if( tx.msg->last_byte )
{
tx.state = STUFF;
}
else
{
tx.state = (++tx.msg)->read_byte ? DATA_SECTION_READ : DATA_SECTION;
tx.bitno = 0;
}
SCL_RELEASE;
if( i2c_interrupt_pending )
raise( FRAMING_INT );
//asm( "raise 11;" );
}
break;
case STUFF: //set up line state so a stop condition can be generated
if( IS_SCL_HIGH )
{
SCL_PULLDOWN;
}
else
{
SCL_RELEASE;
SDA_PULLDOWN;
tx.state = STOP_COND;
}
break;
case STUFF_RESTART: //set up line state so a restart condition can be generated
SDA_RELEASE;
if( IS_SCL_HIGH )
{
SCL_PULLDOWN;
}
else
{
SCL_RELEASE;
tx.state = START_COND;
}
break;
case STOP_COND: //generate stop condition
SDA_RELEASE; //release lines
SCL_RELEASE;
*(timerPeriod( tx.timer )) /= 2;//PERIOD /= 2;
tx.state = FREE;
break;
case FREE: //declare line as free
timerDisable( tx.timer );
//*pTCNTL &= 0xc; //stop timer
*(timerPeriod( tx.timer )) *= 2;//PERIOD *= 2;
IS_BUSY = 0;
break;
}
return ADI_INT_RESULT_PROCESSED;
}
/*
notes:
implement wait for slave to release clock <done>
set ack bit individually after ack-check <done>
real open drain emulation? two states: SDA_PULLDOWN (=input, low), SDA_RELEASE (=output) <done>
*/
#endif
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -