i2cloader.c
来自「This driver supports both master mode, s」· C语言 代码 · 共 1,758 行 · 第 1/5 页
C
1,758 行
{
return g_Revision;
}
/******************************************************************************
Name : I2cStart
Description : Generates an I2c start condition on the bus through the I2c
hardware. The START is generated by software if macro
STI2C_SW_START_STOP is defined, or else by hardware.
Parameters : Pointer to the I2C Device Control block.
******************************************************************************/
static void I2cStart (i2c_param_block_s *Params_p)
{
#ifdef STI2C_SW_START_STOP
DU32 *PIOSDA = (DU32*) Params_p->BaseAddressforSDA;
DU32 *PIOSCL = (DU32*) Params_p->BaseAddressforSCL;
PIOSCL [PIO_OUT_SET] = Params_p->PIOforSCL.BitMask; /* Set clock high */
PIOSCL [PIO_C2_CLR] = Params_p->PIOforSCL.BitMask; /* then enable pin */
task_delay (0);
PIOSDA [PIO_OUT_CLR] = Params_p->PIOforSDA.BitMask; /* Set data low */
PIOSDA [PIO_C2_CLR] = Params_p->PIOforSDA.BitMask; /* then enable pin */
task_delay (0);
PIOSCL [PIO_C2_SET] = Params_p->PIOforSCL.BitMask; /* Allow alt func */
PIOSDA [PIO_C2_SET] = Params_p->PIOforSDA.BitMask; /* to drive pins */
#else /* !STI2C_SW_START_STOP */
DU16 *I2cRegPtr = (DU16*) Params_p->I2cBase;
I2cRegPtr[SSC_I2C] = ( SSC_I2C_ENABLE |
SSC_I2C_GENERATE_ACKS |
SSC_I2C_GENERATE_START );
#endif /* STI2C_SW_START_STOP */
}
/******************************************************************************
Name : I2cStop
Description : Generates an I2c stop condition on the bus through the I2c
hardware. The STOP is generated by software if macro
STI2C_SW_START_STOP is defined, or else by hardware.
Parameters : Pointer to the I2C Device Control block.
******************************************************************************/
static void I2cStop (i2c_param_block_s *Params_p)
{
#ifdef STI2C_SW_START_STOP
DU32 *PIOSDA = (DU32*) Params_p->BaseAddressforSDA;
DU32 *PIOSCL = (DU32*) Params_p->BaseAddressforSCL;
/* Note that the delays at the beginning and end have been found to be
needed in practice. Reducing any of the delays may result in occasional
'ack' failures. */
task_delay (0);
PIOSDA [PIO_OUT_CLR] = Params_p->PIOforSDA.BitMask; /* Set data low */
PIOSDA [PIO_C2_CLR] = Params_p->PIOforSDA.BitMask; /* then enable pin */
task_delay (0);
PIOSCL [PIO_OUT_SET] = Params_p->PIOforSCL.BitMask; /* Transition clk hi*/
PIOSCL [PIO_C2_CLR] = Params_p->PIOforSCL.BitMask; /* then enable pin */
task_delay (0);
PIOSDA [PIO_OUT_SET] = Params_p->PIOforSDA.BitMask; /* Transition data hi*/
task_delay (0);
#else /* !STI2C_SW_START_STOP */
DU16 *I2cRegPtr = (DU16 *) Params_p->I2cBase;
/* Tell the I2C control register to generate a a stop condition */
I2cRegPtr[SSC_I2C] = ( SSC_I2C_ENABLE |
SSC_I2C_GENERATE_ACKS |
SSC_I2C_GENERATE_STOP );
#endif /* STI2C_SW_START_STOP */
}
/******************************************************************************
Name : isInitialised
Description : Performs some tests prior to initialising a new control block
Parameters : 1) A pointer to the name of the device about to be initalised
2) A pointer to the parameter block of the device to be
initialised
******************************************************************************/
static BOOL isInitialised( const ST_DeviceName_t Name,
const STI2C_InitParams_t *Params )
{
i2c_param_block_s *ListPtr = NULL;
BOOL Match = FALSE;
ListPtr = g_CtrlHead;
while ( (ListPtr != NULL) && (Match == FALSE) )
{
/* Check name, PIO base, interrupt number are unused */
if ( ( strcmp( ListPtr->Name, Name ) == 0 ) ||
( ListPtr->I2cBase == Params->BaseAddress ) ||
( ListPtr->I2cInterruptNum == Params->InterruptNumber ) )
Match = TRUE;
else
ListPtr = (i2c_param_block_s*)(ListPtr->Next);
}
return( Match );
}
/******************************************************************************
Name : BusStuck
Description : Tests if the I2C bus is in the correct state. The result
depends on whether the last transfer had a Stop.
Parameters : A pointer to the control structure for the i2c device
******************************************************************************/
static BOOL BusStuck ( i2c_param_block_s *InitBlock )
{
#ifdef STI2C_SW_START_STOP
return FALSE;
#else
DU16 *I2cRegPtr = (DU16 *) InitBlock->I2cBase;
U16 Status = I2cRegPtr [SSC_STATUS];
I2C_TRACE_LOCK(InitBlock,0xC000,Status);
Status &= SSC_STAT_BUSY;
/* If last transfer had STOP, bus should be free. If not, should be busy.*/
if (InitBlock->LastTransferHadStop)
return (Status ? TRUE : FALSE);
else
return (Status ? FALSE : TRUE);
#endif
}
/******************************************************************************
Name : AllocateI2cCtrlBlk
Description : Allocates a new i2c driver block, sticks it on the end
of the linked list, and returns a pointer to it. If this process
fails a null pointer is returned.
Parameters : None
******************************************************************************/
static i2c_param_block_s *AllocateI2cCtrlBlock (ST_Partition_t *partition)
{
i2c_param_block_s *BlockPtr=NULL;
i2c_param_block_s *Next =NULL;
i2c_param_block_s *Current =NULL;
/* Allocate a new block */
BlockPtr = memory_allocate( partition, sizeof(i2c_param_block_s) );
/* Check allocation worked */
if ( BlockPtr != NULL )
{
/* Find the end of the current list */
if ( g_CtrlHead == NULL )
{
/* There is no current list so set it up */
g_CtrlHead = BlockPtr;
}
else
{
Next = g_CtrlHead;
do
{
/* Move though list until our block or the list end is found */
Current = Next;
Next = (i2c_param_block_s*) (Current->Next);
}
while ( Next != NULL );
/* Stick this block to the end of the list */
Current->Next = (void*)BlockPtr;
}
/* Fix all the other pointers */
BlockPtr->Next = NULL;
/* Increment the ctrl block count */
g_CtrlBlkCnt++;
}
return( BlockPtr );
}
/******************************************************************************
Name : GetPioPin
Description : Takes a PIO bitmask for one PIO pin and resolves it to a pin
number between zero and seven. Returns UCHAR_MAX on error.
Parameters : A bitmask for a PIO pin
******************************************************************************/
static S32 GetPioPin( U8 Value )
{
S32 i = 0;
while ( (Value != 1) && (i < 8) )
{
i++;
Value >>= 1;
}
if ( i > 7 )
i = UCHAR_MAX;
return( i );
}
/******************************************************************************
Name : I2cHandler
Description : The interrupt service routine which moves all data after the read
or write routine send the first byte (the i2c address).
Parameters : A pointer to a i2c device control block
******************************************************************************/
static void I2cHandler( void *Param )
{
i2c_param_block_s *CtrlBlk = Param;
DU16 *I2cRegPtr = (DU16*) CtrlBlk->I2cBase;
U16 IntEnables = CtrlBlk->InterruptMask;
U16 Status;
U16 RxData;
U32 TxData;
I2C_TRACE_INT_INC;
/* Turn off interrupts while in ISR */
I2cRegPtr [SSC_INT_ENABLE] = 0;
/* Get h/w status */
Status = I2cRegPtr [SSC_STATUS];
I2C_TRACE(CtrlBlk,0xA00,Status);
/* Read recieve buffer & mask off top 7 bits - NB may clear status bits */
if (Status & SSC_STAT_RX_BUFFER_FULL)
RxData = I2cRegPtr [SSC_RX_BUFFER] & 0x01FF;
else
RxData = 0;
#ifndef STI2C_MASTER_ONLY
/* Check for repeat start when in Slave mode (i.e. other master has
started another transfer to us without doing a stop. If so, switch to
Idle state so it gets treated like an ordinary AAS event. */
if ( (Status & SSC_STAT_AAS) && ( SLAVE_STATE(CtrlBlk) ) )
{
I2C_TRACE(CtrlBlk,0x01,0);
CtrlBlk->State = STI2C_IDLE;
}
/* Check for Addressed As Slave while in Master mode. This means not
only have we lost arbitration, but the other master is addressing us.
If so, abort the transfer - we will go into Slave mode later on. */
if ( (Status & SSC_STAT_AAS) && ( MASTER_STATE(CtrlBlk) ) )
{
CtrlBlk->State = STI2C_IDLE;
CtrlBlk->HandlerCondition = STI2C_ERROR_BUS_IN_USE;
Status &= ~SSC_STAT_ARBL; /* as we have aleady handled this! */
I2C_TRACE(CtrlBlk,0xF01,0);
semaphore_signal ( &CtrlBlk->IOSemaphore );
}
/* Check for loss of arbitration (other than above). If so we don't
need to enter the state logic below. If we are in Master mode, abort
the transfer. */
if (Status & SSC_STAT_ARBL)
{
if ( MASTER_STATE(CtrlBlk) )
{
CtrlBlk->State = STI2C_IDLE;
CtrlBlk->HandlerCondition = STI2C_ERROR_BUS_IN_USE;
I2cRegPtr [SSC_TX_BUFFER] = 0x1FF;
I2C_TRACE(CtrlBlk,0xF02,0);
semaphore_signal ( &CtrlBlk->IOSemaphore );
}
else
{
if (Status & SSC_STAT_STRETCH)
I2cRegPtr [SSC_TX_BUFFER] = 0x1FF;
I2C_TRACE(CtrlBlk,0xF03,0);
}
}
else
#endif /* STI2C_MASTER_ONLY */
/* Check for errors, but only in Master mode (as there is nothing we can
really do in Slave mode */
if ( ( MASTER_STATE(CtrlBlk) ) &&
(Status & (SSC_STAT_RX_ERROR | SSC_STAT_PHASE_ERROR |
SSC_STAT_TX_ERROR)) )
{
/* There were status errors - set status & terminate transfer */
CtrlBlk->State = STI2C_IDLE;
CtrlBlk->HandlerCondition = STI2C_ERROR_STATUS;
I2C_TRACE_ERR_INC;
I2C_TRACE(CtrlBlk,0xF04,RxData);
semaphore_signal ( &CtrlBlk->IOSemaphore );
}
else /* no Master mode error */
{
switch (CtrlBlk->State)
{
case STI2C_MASTER_ADDRESSING_WRITE:
#ifndef STI2C_SW_START_STOP
/* Clear Start bit in i2c control register */
I2cRegPtr[SSC_I2C] = SSC_I2C_ENABLE | SSC_I2C_GENERATE_ACKS;
#endif /* STI2C_SW_START_STOP */
if (RxData & ACKNOWLEDGE_BIT)
{
/* Invalid ack so raise an error and terminate transfer */
CtrlBlk->State = STI2C_IDLE;
CtrlBlk->HandlerCondition = STI2C_ERROR_ADDRESS_ACK;
I2C_TRACE_ERR_INC;
I2C_TRACE(CtrlBlk,0xF50,RxData);
semaphore_signal( &CtrlBlk->IOSemaphore );
}
else /* good ack */
{
/* Set state to Writing & put 1st char into tx buffer */
CtrlBlk->State = STI2C_MASTER_WRITING;
TxData = ((*(CtrlBlk->Buffer++)) << 1) | ACKNOWLEDGE_BIT;
I2cRegPtr [SSC_TX_BUFFER] = (U16) TxData;
CtrlBlk->BufferCnt++;
I2C_TRACE(CtrlBlk,0x50,TxData);
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?