📄 smbus.c
字号:
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 + -