#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: //0x60break;case TW_SR_ARB_LOST_SLA_ACK: //0x68break;case TW_SR_GCALL_ACK: //0x70break;case TW_SR_ARB_LOST_GCALL_ACK: //0x78break;case TW_SR_DATA_ACK: //0x80break;case TW_SR_DATA_NACK: //0x88break;case TW_SR_GCALL_DATA_ACK: //0x90break;case TW_SR_GCALL_DATA_NACK: //0x98break;case TW_ST_LAST_DATA: //0xC8case TW_SR_STOP: //0xA0State = MS_Idle;SETBIT(TWIControl,(1 << TWEA)); // turn address recognition back onbreak;default: // All non-handled states will basically terminate any processingState = MS_Idle;CLRBIT(TWIControl,(1 << TWEA)); // will return NACK, i.e. will pretend to be unaddressedbreak;}switch (TWOStatus) {case TW_ST_SLA_ACK: //0xA8break;case TW_ST_ARB_LOST_SLA_ACK: //0xB0break;case TW_ST_DATA_ACK: //0xB8break;case TW_ST_DATA_NACK: //0xC0break;case TW_ST_ARB_LOST: //0x38 - let's hope it worksbreak;case TW_ST_LAST_DATA: //0xC8State = MS_Idle;SETBIT(TWIControl,(1 << TWEA)); // turn address recognition back onbreak;default: // All non-handled states will basically terminate any processingState = MS_Idle;CLRBIT(TWIControl,(1 << TWEA)); // will return NACK, i.e. will pretend to be unaddressedbreak;}**********/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 stateState = 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_DBGUSART0::SendData('*');#endifTWDR = 0xff;SETBIT(TWIControl,(1 << TWEA) | (1 << TWSTO));}void TWI::ResetTWIReceive() {// Same as above, but also returns the statemachine to the idle stateState = 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_DBGUSART0::SendData('#');#endifTWDR = 0xff;SETBIT(TWIControl,(1 << TWSTO));CLRBIT(TWIControl,(1 << TWEA));}void TWI::ResetTWI() {switch (TWIStatus) {case TW_ST_LAST_DATA: //0xC8case 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 onCLRBIT(TWIControl,(1 << TWSTO)); // even if we did so, stop abortingbreak;case TW_BUS_ERROR:// This is the point where we re-enable the address recognitionResetTWITransmit();break;default:ResetTWIReceive();break;}}/*void TWI::UserInit() {}*/void TWI::HandleUserReceive() {#ifdef SERIAL_DBGUSART0::SendData('r');#endifResetTWI();}void TWI::HandleUserTransmit() {#ifdef SERIAL_DBGUSART0::SendData('t');#endifResetTWI();}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 stateTWIPrevData = sizeof(ConfigRecord);TWDR = TWIPrevData;State = AAP_CmdGetConfig_SendConfig;ConfigCnt = 0;//SETBIT(TWIControl,(1 << TWEA)); // require ACK} else {//Unknown read command: ignoreResetTWITransmit();}} else {HandleUserTransmit();}break;case TW_ST_LAST_DATA:case TW_SR_STOP:#ifdef SERIAL_DBGUSART0::SendData('.');#endifState = MS_Idle;SETBIT(TWIControl,(1 << TWEA)); // turn address recognition back onCLRBIT(TWIControl,(1 << TWSTO)); // even if we did so, stop abortingbreak;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 recognitionResetTWITransmit();break;default:State = MS_Idle;SETBIT(TWIControl,(1 << TWEA) | (1 << TWSTO)); // reset the TWIbreak;}}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 0break;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 modeif (SoftAddress != 0) {// We have a valid address, do not respond to this commandLastCmd = 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 restResetTWIReceive();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 hurryState = MS_Idle;SETBIT(TWIControl,(1 << TWEA)); // turn address recognition back onCLRBIT(TWIControl,(1 << TWSTO)); // even if we did so, stop abortingSETBIT(TWIControl,(1 << TWINT));TWCR = TWIControl;return;}#ifdef SERIAL_DBG// DUMP state to serial portUSART0::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('-');#endifswitch (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 TWIif (SoftAddress != TWIData) {// We have a valid address, or was not addressed -> do not respond to this commandLastCmd = 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 commandResetTWIReceive();}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 operationSETBIT(TWIControl,(1 << TWEA) | (1 << TWSTO)); // reset the TWILastCmd = TWI_AAPCmd_Reserved;break;default:ResetTWI();break;}break;// Read-oriented commandscase AAP_CmdGetConfig_SendConfig:switch (TWIStatus) {case TW_ST_DATA_ACK:if (TWIData != TWIPrevData) {// We've lost the arbitrationResetTWITransmit();break;}TWIPrevData = pgm_read_byte(((uint8_t*)(&ConfigRecord))+ConfigCnt);TWDR = TWIPrevData;ConfigCnt++;//At the last byte, we require NACKif (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 commandsdefault:HandleUserState();break;}// re-enable the TWI interruptsSETBIT(TWIControl,1 << TWINT);#ifdef SERIAL_DBGUSART0::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);#endifTWCR = 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;