0,0 → 1,375 |
#include <avr/pgmspace.h> |
#include <avr/interrupt.h> |
#include <avr/signal.h> |
#include <avr/sleep.h> |
|
#include "common.h" |
#include "usart.h" |
#include "twi_aap.h" |
#include "eeprom.h" |
|
/********* |
switch (TWOStatus) { |
case TW_SR_SLA_ACK: //0x60 |
break; |
case TW_SR_ARB_LOST_SLA_ACK: //0x68 |
break; |
case TW_SR_GCALL_ACK: //0x70 |
break; |
case TW_SR_ARB_LOST_GCALL_ACK: //0x78 |
break; |
case TW_SR_DATA_ACK: //0x80 |
break; |
case TW_SR_DATA_NACK: //0x88 |
break; |
case TW_SR_GCALL_DATA_ACK: //0x90 |
break; |
case TW_SR_GCALL_DATA_NACK: //0x98 |
break; |
case TW_ST_LAST_DATA: //0xC8 |
case TW_SR_STOP: //0xA0 |
State = MS_Idle; |
SETBIT(TWIControl,(1 << TWEA)); // turn address recognition back on |
break; |
default: // All non-handled states will basically terminate any processing |
State = MS_Idle; |
CLRBIT(TWIControl,(1 << TWEA)); // will return NACK, i.e. will pretend to be unaddressed |
break; |
} |
|
switch (TWOStatus) { |
case TW_ST_SLA_ACK: //0xA8 |
break; |
case TW_ST_ARB_LOST_SLA_ACK: //0xB0 |
break; |
case TW_ST_DATA_ACK: //0xB8 |
break; |
case TW_ST_DATA_NACK: //0xC0 |
break; |
case TW_ST_ARB_LOST: //0x38 - let's hope it works |
break; |
case TW_ST_LAST_DATA: //0xC8 |
State = MS_Idle; |
SETBIT(TWIControl,(1 << TWEA)); // turn address recognition back on |
break; |
default: // All non-handled states will basically terminate any processing |
State = MS_Idle; |
CLRBIT(TWIControl,(1 << TWEA)); // will return NACK, i.e. will pretend to be unaddressed |
break; |
} |
**********/ |
|
namespace TWI { |
extern const sConfigRecord ConfigRecord PROGMEM __attribute__ ((weak)) = {UniqueIDUnassigned,DevClassUnassigned,DevUnassigned}; |
|
// void TWI::UserInit() __attribute__ ((weak)); |
void TWI::HandleUserReceive() __attribute__ ((weak)); |
void TWI::HandleUserTransmit() __attribute__ ((weak)); |
void TWI::HandleUserState() __attribute__ ((weak)); |
static inline void TWI::HandleState_MS_Idle(); |
static inline void TWI::HandleState_MS_Addressed(); |
static inline void TWI::StateMachine(); |
|
uint8_t ConfigCnt; |
} |
|
void TWI::ResetTWITransmit() { |
// Same as above, but also returns the statemachine to the idle state |
State = MS_Idle; |
LastCmd = TWI_AAPCmd_Reserved; |
// In slave-transmitter mode it's not possible to reset the TWI. |
// But at least we can transmit all '1'-s so we won't interfere with the bus. |
#ifdef SERIAL_DBG |
USART0::SendData('*'); |
#endif |
TWDR = 0xff; |
SETBIT(TWIControl,(1 << TWEA) | (1 << TWSTO)); |
} |
void TWI::ResetTWIReceive() { |
// Same as above, but also returns the statemachine to the idle state |
State = MS_Idle; |
LastCmd = TWI_AAPCmd_Reserved; |
// In slave-transmitter mode it's not possible to reset the TWI. |
// But at least we can transmit all '1'-s so we won't interfere with the bus. |
#ifdef SERIAL_DBG |
USART0::SendData('#'); |
#endif |
TWDR = 0xff; |
SETBIT(TWIControl,(1 << TWSTO)); |
CLRBIT(TWIControl,(1 << TWEA)); |
} |
void TWI::ResetTWI() { |
switch (TWIStatus) { |
case TW_ST_LAST_DATA: //0xC8 |
case TW_SR_STOP: |
case TW_ST_DATA_NACK: |
case TW_SR_DATA_NACK: |
case TW_SR_GCALL_DATA_NACK: |
State = MS_Idle; |
SETBIT(TWIControl,(1 << TWEA)); // turn address recognition back on |
CLRBIT(TWIControl,(1 << TWSTO)); // even if we did so, stop aborting |
break; |
case TW_BUS_ERROR: |
// This is the point where we re-enable the address recognition |
ResetTWITransmit(); |
break; |
default: |
ResetTWIReceive(); |
break; |
} |
} |
|
/*void TWI::UserInit() { |
}*/ |
|
void TWI::HandleUserReceive() { |
#ifdef SERIAL_DBG |
USART0::SendData('r'); |
#endif |
ResetTWI(); |
} |
|
void TWI::HandleUserTransmit() { |
#ifdef SERIAL_DBG |
USART0::SendData('t'); |
#endif |
ResetTWI(); |
} |
|
void TWI::HandleUserState() { |
} |
|
|
static inline void TWI::HandleState_MS_Idle() { |
switch (TWIStatus) { |
case TW_SR_SLA_ACK: |
LastCmd = TWI_AAPCmd_Reserved; |
HandleUserReceive(); |
break; |
case TW_SR_GCALL_ACK: |
State = MS_Addressed; |
break; |
case TW_ST_SLA_ACK: |
if ((TWIData & 0xfe) == GeneralCallAddr) { |
// TWI_AAP read command: things depend on what the last (write) command was: |
if (LastCmd == TWI_AAPCmd_GetConfig) { |
// This is the read part of the GetUDID command: Send the byte count and change state |
TWIPrevData = sizeof(ConfigRecord); |
TWDR = TWIPrevData; |
State = AAP_CmdGetConfig_SendConfig; |
ConfigCnt = 0; |
//SETBIT(TWIControl,(1 << TWEA)); // require ACK |
} else { |
//Unknown read command: ignore |
ResetTWITransmit(); |
} |
} else { |
HandleUserTransmit(); |
} |
break; |
case TW_ST_LAST_DATA: |
case TW_SR_STOP: |
#ifdef SERIAL_DBG |
USART0::SendData('.'); |
#endif |
State = MS_Idle; |
SETBIT(TWIControl,(1 << TWEA)); // turn address recognition back on |
CLRBIT(TWIControl,(1 << TWSTO)); // even if we did so, stop aborting |
break; |
case TW_ST_DATA_ACK: |
case TW_SR_DATA_ACK: |
case TW_SR_GCALL_DATA_ACK: |
ResetTWIReceive(); |
break; |
case TW_BUS_ERROR: |
case TW_ST_DATA_NACK: |
case TW_SR_DATA_NACK: |
case TW_SR_GCALL_DATA_NACK: |
// This is the point where we re-enable the address recognition |
ResetTWITransmit(); |
break; |
default: |
State = MS_Idle; |
SETBIT(TWIControl,(1 << TWEA) | (1 << TWSTO)); // reset the TWI |
break; |
} |
} |
|
static inline void TWI::HandleState_MS_Addressed() { |
switch (TWIStatus) { |
case TW_SR_GCALL_DATA_ACK: |
switch(TWIData) { |
case TWI_AAPCmd_ResetDevices: |
LastCmd = TWIData; |
State = MS_Idle; |
SoftAddress = 0; |
TWAR = SoftAddress | 1; // General call recognition and slave-receiver mode through address being 0 |
break; |
case TWI_AAPCmd_ResetToPermAddress: |
LastCmd = TWIData; |
State = MS_Idle; |
SoftAddress = EEPROM::GetByte(EEPROM_layout::TWI_SoftAddr_Ofs); |
TWAR = SoftAddress | 1; // General call recognition and slave-receiver mode |
if (SoftAddress != 0) { |
// We have a valid address, do not respond to this command |
LastCmd = TWI_AAPCmd_Reserved; |
} |
break; |
case TWI_AAPCmd_GetConfig: |
LastCmd = TWIData; |
State = AAP_CmdGetConfig_GetAddress; |
break; |
case TWI_AAPCmd_AssignAddress: |
LastCmd = TWIData; |
ConfigCnt = 0; |
State = AAP_CmdAssignAddress_GetUniqueID; |
break; |
default: |
// unknown command - ignore the rest |
ResetTWIReceive(); |
break; |
} |
break; |
default: |
ResetTWI(); |
break; |
} |
} |
|
static inline void TWI::StateMachine() { |
TWIStatus = TWSR & TW_STATUS_MASK; |
TWIControl = TWCR; |
if (TWIStatus != TW_SR_STOP) { |
TWIData = TWDR; |
} else { |
// Fast short-circuit for stop-bit detection |
// This condition cannot use clock-stretching so we might miss the next start if we don't hurry |
State = MS_Idle; |
SETBIT(TWIControl,(1 << TWEA)); // turn address recognition back on |
CLRBIT(TWIControl,(1 << TWSTO)); // even if we did so, stop aborting |
SETBIT(TWIControl,(1 << TWINT)); |
TWCR = TWIControl; |
return; |
} |
|
#ifdef SERIAL_DBG |
// DUMP state to serial port |
USART0::SendData('s'); USART0::SendData(' '); USART0::SendHexData(TWIStatus); USART0::SendData(' '); |
USART0::SendData('d'); USART0::SendData(' '); USART0::SendHexData(TWIData); USART0::SendData(' '); |
USART0::SendData('S'); USART0::SendData(' '); USART0::SendHexData(State); USART0::SendData(' '); |
USART0::SendData('L'); USART0::SendData(' '); USART0::SendHexData(LastCmd); USART0::SendData(' '); |
USART0::SendData('A'); USART0::SendData(' '); USART0::SendHexData(SoftAddress); USART0::SendData('-'); |
#endif |
switch (State) { |
case MS_Idle: |
HandleState_MS_Idle(); |
break; |
case MS_Addressed: |
HandleState_MS_Addressed(); |
break; |
|
case AAP_CmdGetConfig_GetAddress: |
switch (TWIStatus) { |
case TW_SR_GCALL_DATA_ACK: |
State = MS_Idle; |
SETBIT(TWIControl,(1 << TWEA) | (1 << TWSTO)); // reset the TWI |
if (SoftAddress != TWIData) { |
// We have a valid address, or was not addressed -> do not respond to this command |
LastCmd = TWI_AAPCmd_Reserved; |
} |
break; |
default: |
ResetTWI(); |
break; |
} |
break; |
|
case AAP_CmdAssignAddress_GetUniqueID: |
switch (TWIStatus) { |
case TW_SR_GCALL_DATA_ACK: |
if (TWIData == pgm_read_byte(ConfigRecord.UniqueID+ConfigCnt)) { |
++ConfigCnt; |
if (ConfigCnt == UniqueID_Size) State = AAP_CmdAssignAddress_GetAddress; |
} else { |
// UDID is not ours, abort command |
ResetTWIReceive(); |
} |
break; |
default: |
ResetTWI(); |
break; |
} |
break; |
case AAP_CmdAssignAddress_GetAddress: |
switch (TWIStatus) { |
case TW_SR_GCALL_DATA_ACK: |
SoftAddress = TWIData & 0xfe; |
if (TWIData & 0x01 == 1) { |
EEPROM::SetByte(EEPROM_layout::TWI_SoftAddr_Ofs,SoftAddress); |
} |
State = MS_Idle; |
TWAR = SoftAddress | 1; // General call recognition and normal operation |
SETBIT(TWIControl,(1 << TWEA) | (1 << TWSTO)); // reset the TWI |
LastCmd = TWI_AAPCmd_Reserved; |
break; |
default: |
ResetTWI(); |
break; |
} |
break; |
|
|
|
// Read-oriented commands |
case AAP_CmdGetConfig_SendConfig: |
switch (TWIStatus) { |
case TW_ST_DATA_ACK: |
if (TWIData != TWIPrevData) { |
// We've lost the arbitration |
ResetTWITransmit(); |
break; |
} |
TWIPrevData = pgm_read_byte(((uint8_t*)(&ConfigRecord))+ConfigCnt); |
TWDR = TWIPrevData; |
ConfigCnt++; |
//At the last byte, we require NACK |
if (ConfigCnt == sizeof(ConfigRecord)) { |
State = MS_Idle; |
CLRBIT(TWIControl,(1 << TWEA)); // require NACK |
} else { |
State = AAP_CmdGetConfig_SendConfig; |
SETBIT(TWIControl,(1 << TWEA)); // require ACK |
} |
break; |
|
default: |
ResetTWI(); |
break; |
} |
break; |
|
// User commands |
default: |
HandleUserState(); |
break; |
} |
// re-enable the TWI interrupts |
SETBIT(TWIControl,1 << TWINT); |
#ifdef SERIAL_DBG |
USART0::SendData('S'); USART0::SendData(' '); USART0::SendHexData(State); USART0::SendData(' '); |
USART0::SendData('L'); USART0::SendData(' '); USART0::SendHexData(LastCmd); USART0::SendData(' '); |
USART0::SendData('A'); USART0::SendData(' '); USART0::SendHexData(SoftAddress); USART0::SendData(' '); |
USART0::SendData('C'); USART0::SendData(' '); USART0::SendHexData(TWIControl); USART0::SendData(0x0d); USART0::SendData(0x0a); |
#endif |
TWCR = TWIControl; |
} |
|
SIGNAL(SIG_TWI) {TWI::StateMachine();} |
|
uint8_t TWI::State; |
uint8_t TWI::LastCmd; |
uint8_t TWI::SoftAddress; |
uint8_t TWI::TWIStatus; |
uint8_t TWI::TWIData; |
uint8_t TWI::TWIPrevData; |
uint8_t TWI::TWIControl; |