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

📄 smbus.c

📁 iic for avr very good
💻 C
📖 第 1 页 / 共 4 页
字号:

    case TW_Wait4Stop:
      if(TWS_RSTOP == Status)	//Saw a Stop, possibly left over from previous cmd.
      {
        TWCR = (1<<TWINT) | (1<<TWEA) | (1<<TWEN) | (1<<TWIE);	//must re-enable ACKing
        TWISR_state = TW_IDLE;			//Reset the state machine.
      }
      else
        TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWIE);	//must NOT re-enable ACKing yet!
      break;


    //SLAVE-mode states follow.

    case TW_Wait4Cmd:	//upon entry, we expect to have received a Cmd byte.
      if(TWS_RCMD == Status)		//It appears that we have received a Command byte now.
      {
        tmp = TWDR;
        if(tmp <= HIGHEST_SMB_CMD)	//Is the Cmd within valid range?
        {
          CurrentCmd = tmp;		//Save a copy.
          tmp = SM_Cmd_Table[tmp][0];	//Grab the Command Characteristics/Features flags.
          if(tmp & SMBslave)		//Is the Command valid for Slaves?
          {				//The command appears to be valid.
            TWISR_CmdFeatures = tmp;	//Save the Feature flags for use in Wait4RW state.
            TWISR_state = TW_Wait4RW;	//set up next state
            TWCR = (1<<TWINT) | (1<<TWEA) | (1<<TWEN) | (1<<TWIE);	//enable ACKing
            return;
          }
        }
      }
      //In all cases except those that 'return' (above), it's an error.
      SMBvariables[SMBV_BattStatus][lobyte] |= SMBerr_UnknownError;
      TWI_CmdFlags = SMB_GenBusTimeout;	//generate a bus timeout.
      TWCR = (1<<TWEA) | (1<<TWEN);     //disable int, and DON'T clear the TWINT flag!
      TWISR_state = TW_IDLE;		//Reset the state machine.
      return;
//    break;


    case TW_Wait4RW:	//We will now find out if we will RX more, or we need to TX a reply instead.
      if(TWS_RDATA == Status)		//It is a WRITE-type command. Prep the RX buffer to accept more data.
      {	//NOTE: except for OptionalMfgFunction5, all WRITE cmds are 2-byte, plus optional PEC.
      	//Place all bytes of the transaction into the buffer so we can do a PEC on it if needed.
      	TW_RxBuf[0] = TWAR & 0xFE;	//store everything incl. the slave address for computing PEC.
      	TW_RxBuf[1] = CurrentCmd;	//store the previously-send Command.
        TW_RxBuf[2] = TWDR;		//store this first DATA byte
        TW_RxBufIndex = 3;		//use RxBufIndex as the index to store data in the buffer.
        if(TWISR_CmdFeatures & SCWW)	//is it a Write-WORD command type?
        {
            TW_RxBufCnt = 1;		//We expect 1 more data byte, and possibly PEC after that.
        }
        else
        if(TWISR_CmdFeatures & SCWG)	//is it a write-BLOCK command (must be OptionalMfgFunction5 then)
        {
          tmp = TWDR;
          if((tmp >= 1) && (tmp <= 32))
            TW_RxBufCnt = TWDR;
          else
          {
            SMBvariables[SMBV_BattStatus][lobyte] |= SMBerr_BadSize;
            TWI_CmdFlags = SMB_GenBusTimeout;	//generate a bus timeout.
            TWCR = (1<<TWEA) | (1<<TWEN);       //disable int, and DON'T clear the TWINT flag!
            TWISR_state = TW_IDLE;		//Reset the state machine.
            return;
          }
        }
        else	//this Command doesn't allow EITHER word OR group/block Writes! It's Read-only!
        {
          SMBvariables[SMBV_BattStatus][lobyte] |= SMBerr_AccessDenied;
          TWI_CmdFlags = SMB_GenBusTimeout;	//Not a WRITE-type cmd, so generate a bus timeout.
          TWCR = (1<<TWEA) | (1<<TWEN);         //disable int, and DON'T clear the TWINT flag!
          TWISR_state = TW_IDLE;		//Reset the state machine.
          return;
        }
        TWISR_state = TW_Wait4Data;
        TWCR = (1<<TWINT) | (1<<TWEA) | (1<<TWEN) | (1<<TWIE);	//enable ACKing
      }
      else
      if(TWS_REPEAT == Status)	//We saw a re-Start, so must be getting ready for a Read cmd.
      {	//Must now interpret previously-sent CurrentCmd & set up Reply data.
        if(TWISR_CmdFeatures & (SCRW | SCRG))	//Is it a 'ReadWord' or 'ReadGroup' command type?
        {
          TWI_CmdFlags = SMB_SetUpReply;	//Foreground decoder will set up TWCR.
          TWISR_state = TW_ReplyData;		//Move to next state.
        }
        else
        {
          SMBvariables[SMBV_BattStatus][lobyte] |= SMBerr_UnknownError;
          TWI_CmdFlags = SMB_GenBusTimeout;	//Not a READ-type cmd, so generate a bus timeout.
          TWCR = (1<<TWEA) | (1<<TWEN);		//disable int, and DON'T clear the TWINT flag!
          TWISR_state = TW_IDLE;		//Reset the state machine.
          return;
        }
        TWCR = (1<<TWEA) | (1<<TWEN);         //disable int, and DON'T clear the TWINT flag!
        return;					
      }
      else  //some type of error!
      {
        SMBvariables[SMBV_BattStatus][lobyte] |= SMBerr_UnknownError;
        TWI_CmdFlags = SMB_GenBusTimeout;	//Generate a bus timeout.
        TWCR = (1<<TWEA) | (1<<TWEN);		//disable int, and DON'T clear the TWINT flag!
        TWISR_state = TW_IDLE;			//Reset the state machine.
        return;
      }
      break;


    case TW_Wait4Data:	//We are in Slave Receive operating mode.
      if(TWS_RDATA == Status)			//received a data byte
      {
        tmp = TWDR;
        if(--TW_RxBufCnt < -1)			//Are we past the PEC byte and still getting more data?
        {
          SMBvariables[SMBV_BattStatus][lobyte] |= SMBerr_BadSize;	//throw away the data & flag error
          TWI_CmdFlags = SMB_GenBusTimeout;	//Generate a bus timeout.
          TWCR = (1<<TWEA) | (1<<TWEN);		//disable int, and DON'T clear the TWINT flag!
          TWISR_state = TW_IDLE;			//Reset the state machine.
          return;
        }
        TW_RxBuf[TW_RxBufIndex++] = TWDR;	//store the byte
      }

      else
      if(TWS_RSTOP == Status)			//got a STOP; all done RXing data now.
      { //Note: if we get a STOP prematurely, then we simply ignore the command,
      	//  since it is too late to inform the Master of the error.
      	if((TW_RxBufCnt > 0) || (TW_RxBufCnt < -1))	//We got a premature STOP or too much data; ERROR!
      	{
          SMBvariables[SMBV_BattStatus][lobyte] |= SMBerr_BadSize;	//throw away the data.
        }
        else
        {
          if(0 == TW_RxBufCnt)
            UsePEC = 0;				//there is no PEC coming for this packet.
          else
          if(-1 == TW_RxBufCnt)
            UsePEC = 1;				//PEC was included.

          TW_RxBufCnt = TW_RxBufIndex;		//re-use the Write Index's value as the Valid Byte Count.
          TW_RxBufIndex = 0;			//clear the index in preparation for interpreting it.
          TWI_CmdFlags = SMB_GotCmdData;	//tell Foreground to process this now.
          //Note that when (TWI_CmdFlags == SMB_GotCmdData), TWI ISR will respond with BUSY condition.
        }
        TWISR_state = TW_IDLE;		//Reset the state machine in all cases.
      }

      else  //some type of error during transmission!
      {
        SMBvariables[SMBV_BattStatus][lobyte] |= SMBerr_UnknownError;
        TWI_CmdFlags = SMB_GenBusTimeout;	//Not a WRITE-type cmd, so generate a bus timeout.
        TWCR = (1<<TWEA) | (1<<TWEN);		//disable int, and DON'T clear the TWINT flag!
        TWISR_state = TW_IDLE;			//Reset the state machine.
        return;
      }

      TWCR = (1<<TWINT) | (1<<TWEA) | (1<<TWEN) | (1<<TWIE);	//enable ACKing
      break;


    case TW_ReplyData:	//We are now in Slave Transmit operating mode.
      //The foreground code has set up the response that we are now sending.
      //Note: TW_TxBufCnt *always* includes the PEC byte! Since we don't
      // know whether the Master actually WANTS the PEC byte or not, we will
      // always TRY to send it, regardless of the state of the UsePEC flag.
      // If the Master does NOT want it, we will get a NAK while the PEC
      // byte is still in the buffer.  In the rare case where we send it all,
      // including the PEC byte, but we still get an ACK back, the TWI module
      // will be off-line anyway due to not setting the TWEA bit after sending PEC,
      // and we will therefore be unable to flag that an error has occurred.
      if((TWS_SLA_R == Status) || (TWS_RACK == Status))	//send out Reply data
      {
        TWDR = TW_TxBuf[TW_TxBufIndex++];	//send data out
        if(--TW_TxBufCnt == 0)			//Have we emptied the buffer, incl. PEC?
          TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWIE);		// Yes, so don't set TWEA.
        else
          TWCR = (1<<TWINT) | (1<<TWEA) | (1<<TWEN) | (1<<TWIE);	// No, so assert TWEA.
      }
      else
      if(TWS_RNAK == Status)	//We may have gotten this validly or as an Error.
      {
        if(TW_TxBufCnt == 1)	//Not an error. Master didn't want PEC; clear UsePEC flag!
        {
          TW_TxBufCnt = 0;	//clear the buffer too.
          UsePEC = 0;
        }
        else
        if(TW_TxBufCnt == 0)	//Not an error. Master wanted PEC (and got it); assert UsePEC.
          UsePEC = 1;
        else			//some kind of error occurred; we got NAK too early!
        { //Note: the TWI module is now OFF-LINE, so we can't inform Host of this error!!
          SMBvariables[SMBV_BattStatus][lobyte] |= SMBerr_UnknownError;	//flag it later
        }
        TWISR_state = TW_IDLE;			//In all cases, go back to IDLE.
        TWCR = (1<<TWINT) | (1<<TWEA) | (1<<TWEN) | (1<<TWIE);
      }
      else
//    if(TWS_FINAL == Status)	//ERROR: we got an ACK but we have no more data!
      { //Since the TWI module is now in "Not Addressed Slave" mode, we can't flag the error
      	// back to the Master DURING this transaction; we WILL assert an error status though.
        SMBvariables[SMBV_BattStatus][lobyte] |= SMBerr_BadSize;
        TWISR_state = TW_IDLE;	//Reset the state machine.
        TWCR = (1<<TWINT) | (1<<TWEA) | (1<<TWEN) | (1<<TWIE);
      }
      break;



    //MASTER-mode states follow.

    case TW_MSLA_W:	//we just tried to send the SLAVE ADDRESS in Master Mode.
      if(TWS_WRITE_ACK == Status) 	// we got an ACK back from the desired SLA+W transmission
      {
        TWDR = TW_MTxBuf[TW_MTxBufIndex++];	//send out Cmd
        TWISR_state = TW_MCMD_W;
        TWCR = (1<<TWINT) | (1<<TWEA) | (1<<TWEN) | (1<<TWIE);
      }
      else  //anything else is Unexpected or an Error result.
      { //We simply delete msgs that couldn't be sent as they will be resent in 10secs anyway.
        TWCR = (1<<TWINT) | (1<<TWEA) | (1<<TWSTO) | (1<<TWEN) | (1<<TWIE);
        TWISR_state = TW_IDLE;	//Reset the state machine.
        TW_MTxBufCnt = 0;
        TW_MTxBufIndex = 0;
        TXmsgDelete;                  //Delete this just-sent msg from the TX buffer.
        SMLOCK = 0;
      }
      break;

    case TW_MCMD_W:	//we just sent a Master COMMAND byte or a Data byte
      if(TWS_TXDATA_ACK == Status) 	// we got an ACK from data we sent.
      {
        if(TW_MTxBufCnt > TW_MTxBufIndex)
        {
          TWDR = TW_MTxBuf[TW_MTxBufIndex++];	//send out Data byte
          TWISR_state = TW_MCMD_W;
          TWCR = (1<<TWINT) | (1<<TWEA) | (1<<TWEN) | (1<<TWIE);
        }
        else  //we've sent everything in the buffer, so send STOP now.
        {
          TWCR = (1<<TWINT) | (1<<TWEA) | (1<<TWSTO) | (1<<TWEN) | (1<<TWIE);
          TWISR_state = TW_IDLE;
          TW_MTxBufCnt = 0;
          TW_MTxBufIndex = 0;
          TXmsgDelete;                  //Delete this just-sent msg from the TX buffer.
          SMLOCK = 0;
        }
      }
      else  //Unexpected or Error response.
      {
          TWCR = (1<<TWINT) | (1<<TWEA) | (1<<TWSTO) | (1<<TWEN) | (1<<TWIE);
          TWISR_state = TW_IDLE;
          TW_MTxBufCnt = 0;
          TW_MTxBufIndex = 0;
          TXmsgDelete;                  //Delete this just-sent msg from the TX buffer.
          SMLOCK = 0;
      }
      break;

  } // end of switch(TWISR_state)
}



/* *************************************************************************
 *
 *   Foreground SMBus Command Interpreter
 *
 ************************************************************************* */




//This attempts to initiate Master Mode if a message is in the buffer.
void SMB_Master(void)
{
  unsigned char * ptr;
  unsigned char PEC;

  if(!(TW_MTxBufCnt))   //is there an active message
  {
    if(TXmsgEmpty)
      return;

    ptr = TXmsg[TXmsgTail];
    if(UsePEC)
    {
      PEC = FastCRC( 0,  TW_MTxBuf[0] = *ptr++);
      PEC = FastCRC(PEC, TW_MTxBuf[1] = *ptr++);
      PEC = FastCRC(PEC, TW_MTxBuf[2] = *ptr++);
      PEC = FastCRC(PEC, TW_MTxBuf[3] = *ptr);
      TW_MTxBuf[4] = PEC;
      TW_MTxBufCnt = 5;
    }
    else
    {
      TW_MTxBuf[0] = *ptr++;
      TW_MTxBuf[1] = *ptr++;
      TW_MTxBuf[2] = *ptr++;
      TW_MTxBuf[3] = *ptr;
      TW_MTxBufCnt = 4;
    }

    TW_MTxBufIndex = 0;
    SMLOCK = 0;
    TEST50US = 0;
  }

  if(SMLOCK)  //if asserted, we already started a TX action.
    return;

  if(TEST50US)  //if asserted when get here, we've waited at least 50uS & saw no bus activity.
  {
    __disable_interrupt();
    if((PINA & (1<<6)) && (TWISR_state == TW_IDLE))
    {
      SMLOCK = 1;
      TWBR = 12;   //12 yields 100KHz
      TWCR = ((1<<TWINT) | (1<<TWSTA) | (1<<TWEN) | (1<<TWIE)); //send a START
      PCICR = 0;   //disable PCINT0 / PA6
      TEST50US = 0;   //clear this flag, so that PCINT6 gets re-enabled if must re-try.
    }
    __enable_interrupt();
    return;
  }

  if((PINA & (1<<6)) && (TWISR_state == TW_IDLE))
  {
    TEST50US = 1;
    //enable PinChange Interrupt for PA6 here.  PA6 is PCINT6.
    PCIFR = (1<<PCIF1) | (1<<PCIF0); //clear any flags present
    PCMSK0 = (1<<6);   //enable PA6 pin-change
    PCICR = (1<<PCIE0);   //enable PCINT0
  }
}





void Check50uS(void)
{
  if(TEST50US)
    SMB_Master();
}






void SMB_CmdInterpreter(void)
{
  unsigned char temp;

  if(SMB_GenBusTimeout == TWI_CmdFlags)	//The ISR detected an error condition.
  {
    TWI_CmdFlags = 0;			//clear the flag that brought us here.
    //start the 26mS timer.  When it is done, the Timer handler will re-init the TWI peripheral.
    SetGenericTimer(SMBfaultTimer, 26);
    return;
  }
  else
  if(SMB_SetUpReply == TWI_CmdFlags)	//interpret a 'Read' Command.
  {
    TWI_CmdFlags = 0;			//clear the flag that brought us here.
    TW_TxBufIndex = 0;			//initialize
    TW_TxBufCnt = 0;			//initialize
    if(0 != SMB_ReadCmd[CurrentCmd]())	//After interpreting, was there an error??
    {
      SMBvariables[SMBV_BattStatus][lobyte] |= SMBerr_UnsuptdCommand;
      SetGenericTimer(SMBfaultTimer, 26); //generate a bus timeout error.
      TW_TxBufIndex = 0;		  //Wipe out anything in the buffer, just in case.
      TW_TxBufCnt = 0;			
      return;
    }
    else //generate PEC now for the *entire* transaction, including the original request!
    {
      //Assume (TW_TxBufIndex == 0) and (TW_TxBufCtr == # of bytes of data, not incl PEC).
      temp = FastCRC(0, (TWAR & 0xFE));	//use the SLA+W address
      temp = FastCRC(temp, CurrentCmd);
      temp = FastCRC(temp, (TWAR | 1));	//use the SLA+R address

      do {temp = FastCRC(temp, TW_TxBuf[TW_TxBufIndex++]);}
      while(TW_TxBufIndex != TW_TxBufCnt);

      TW_TxBuf[TW_TxBufIndex] = temp;	//append the CRC value on the end.
      TW_TxBufCnt++;			//increase the byte count for the PEC.
      TW_TxBufIndex = 0;		//Reset the buffer pointer.
      TWCR = (1<<TWINT) | (1<<TWEA) | (1<<TWEN) | (1<<TWIE);	//have TWI continue from where it stalled.
    }
    return;
  }
  else
  if(SMB_GotCmdData == TWI_CmdFlags)	//process some received command+data.
  {
    //NOTE: as of 6/17/2005, TWI_CmdFlags should NOT be cleared until we are
    // completely done processing this message, else the TWI ISR will overwrite
    // the RX buffer and won't respond with BUSY as it should.  Note also that
    // the TWI is fully re-enabled when entering here, so we MUST NOT write
    // to any of the TWI h/w registers or we could create havoc.  -rgf

    if(UsePEC)				//check the CRC of the received packet.
    {
      temp = 0;				//use this as our CRC value.

      do { temp = FastCRC(temp, TW_RxBuf[TW_RxBufIndex++]); }
      while(TW_RxBufCnt != TW_RxBufIndex);

      if(temp)	//The result of a CRC check SHOULD be =0 if all was ok.
      {
      	SMBvariables[SMBV_BattStatus][lobyte] |= SMBerr_UnknownError;

      	//start the 26mS timer to generate a bus timeout error.
      	SetGenericTimer(SMBfaultTimer, 26);
        TWI_CmdFlags = 0;		//clear the flag that brought us here.
        return;
      }

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -