import { AVRInterruptConfig , CPU } from '../cpu/cpu' ; import { u8 } from '../types' ; export interface TWIEventHandler { start ( repeated ) : void ; stop ( ) : void ; connectToSlave ( addr , write ) : void ; writeByte ( value ) : void ; readByte ( ack ) : void ; } export interface TWIConfig { twiInterrupt : u8 ; TWBR : u8 ; TWCR : u8 ; TWSR : u8 ; TWDR : u8 ; TWAR : u8 ; TWAMR : u8 ; } const TWCR_TWINT = 0x80 ; const TWCR_TWEA = 0x40 ; const TWCR_TWSTA = 0x20 ; const TWCR_TWSTO = 0x10 ; const TWCR_TWWC = 0x8 ; const TWCR_TWEN = 0x4 ; const TWCR_TWIE = 0x1 ; const TWSR_TWS_MASK = 0xf8 ; const TWSR_TWPS1 = 0x2 ; const TWSR_TWPS0 = 0x1 ; const TWSR_TWPS_MASK = TWSR_TWPS1 | TWSR_TWPS0 ; const TWAR_TWA_MASK = 0xfe ; const TWAR_TWGCE = 0x1 ; const STATUS_BUS_ERROR = 0x0 ; const STATUS_TWI_IDLE = 0xf8 ; const STATUS_START = 0x08 ; const STATUS_REPEATED_START = 0x10 ; const STATUS_SLAW_ACK = 0x18 ; const STATUS_SLAW_NACK = 0x20 ; const STATUS_DATA_SENT_ACK = 0x28 ; const STATUS_DATA_SENT_NACK = 0x30 ; const STATUS_DATA_LOST_ARBITRATION = 0x38 ; const STATUS_SLAR_ACK = 0x40 ; const STATUS_SLAR_NACK = 0x48 ; const STATUS_DATA_RECEIVED_ACK = 0x50 ; const STATUS_DATA_RECEIVED_NACK = 0x58 ; export const twiConfig = { twiInterrupt : 0x30 , TWBR : 0xb8 , TWSR : 0xb9 , TWAR : 0xba , TWDR : 0xbb , TWCR : 0xbc , TWAMR : 0xbd , } ; export class NoopTWIEventHandler implements TWIEventHandler { constructor ( protected twi ) { } start ( ) { this . twi . completeStart ( ) ; } stop ( ) { this . twi . completeStop ( ) ; } connectToSlave ( ) { this . twi . completeConnect ( false ) ; } writeByte ( ) { this . twi . completeWrite ( false ) ; } readByte ( ) { this . twi . completeRead ( 0xff ) ; } } export class AVRTWI { public eventHandler : TWIEventHandler = new NoopTWIEventHandler ( this ) ; private busy = false ; private TWI : AVRInterruptConfig = { address : this . config . twiInterrupt , flagRegister : this . config . TWCR , flagMask : TWCR_TWINT , enableRegister : this . config . TWCR , enableMask : TWCR_TWIE , } ; constructor ( private cpu , private config , private freqHz ) { this . updateStatus ( STATUS_TWI_IDLE ) ; this . cpu . writeHooks [ config . TWCR ] = ( value ) => { this . cpu . data [ config . TWCR ] = value ; const clearInt = value & TWCR_TWINT ; this . cpu . clearInterruptByFlag ( this . TWI , value ) ; this . cpu . updateInterruptEnable ( this . TWI , value ) ; const { status } = this ; if ( clearInt && value & TWCR_TWEN && ! this . busy ) { const twdrValue = this . cpu . data [ this . config . TWDR ] ; this . cpu . addClockEvent ( ( ) => { if ( value & TWCR_TWSTA ) { this . busy = true ; this . eventHandler . start ( status !== STATUS_TWI_IDLE ) ; } else if ( value & TWCR_TWSTO ) { this . busy = true ; this . eventHandler . stop ( ) ; } else if ( status === STATUS_START || status === STATUS_REPEATED_START ) { this . busy = true ; this . eventHandler . connectToSlave ( twdrValue >> 1 , twdrValue & 0x1 ? false : true ) ; } else if ( status === STATUS_SLAW_ACK || status === STATUS_DATA_SENT_ACK ) { this . busy = true ; this . eventHandler . writeByte ( twdrValue ) ; } else if ( status === STATUS_SLAR_ACK || status === STATUS_DATA_RECEIVED_ACK ) { this . busy = true ; const ack = ! ! ( value & TWCR_TWEA ) ; this . eventHandler . readByte ( ack ) ; } } , 0 ) ; return true ; } } ; } get prescaler ( ) { switch ( this . cpu . data [ this . config . TWSR ] & TWSR_TWPS_MASK ) { case 0 : return 1 ; case 1 : return 4 ; case 2 : return 16 ; case 3 : return 64 ; } throw new Error ( 'Invalid prescaler value!' ) ; } get sclFrequency ( ) { return this . freqHz / ( 16 + 2 * this . cpu . data [ this . config . TWBR ] * this . prescaler ) ; } completeStart ( ) { this . busy = false ; this . updateStatus ( this . status === STATUS_TWI_IDLE ? STATUS_START : STATUS_REPEATED_START ) ; } completeStop ( ) { this . busy = false ; this . cpu . data [ this . config . TWCR ] &= ~ TWCR_TWSTO ; this . updateStatus ( STATUS_TWI_IDLE ) ; } completeConnect ( ack ) { this . busy = false ; if ( this . cpu . data [ this . config . TWDR ] & 0x1 ) { this . updateStatus ( ack ? STATUS_SLAR_ACK : STATUS_SLAR_NACK ) ; } else { this . updateStatus ( ack ? STATUS_SLAW_ACK : STATUS_SLAW_NACK ) ; } } completeWrite ( ack ) { this . busy = false ; this . updateStatus ( ack ? STATUS_DATA_SENT_ACK : STATUS_DATA_SENT_NACK ) ; } completeRead ( value ) { this . busy = false ; const ack = ! ! ( this . cpu . data [ this . config . TWCR ] & TWCR_TWEA ) ; this . cpu . data [ this . config . TWDR ] = value ; this . updateStatus ( ack ? STATUS_DATA_RECEIVED_ACK : STATUS_DATA_RECEIVED_NACK ) ; } private get status ( ) { return this . cpu . data [ this . config . TWSR ] & TWSR_TWS_MASK ; } private updateStatus ( value ) { const { TWSR } = this . config ; this . cpu . data [ TWSR ] = ( this . cpu . data [ TWSR ] & ~ TWSR_TWS_MASK ) | value ; this . cpu . setInterruptFlag ( this . TWI ) ; } }