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 + -
显示快捷键?