⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 i2c_timing_layer.c

📁 BlackFin处理器视频演示代码
💻 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 + -