import { AVRInterruptConfig , CPU } from '../cpu/cpu' ; import { u8 } from '../types' ; export interface SPIConfig { spiInterrupt : u8 ; SPCR : u8 ; SPSR : u8 ; SPDR : u8 ; } const SPCR_SPIE = 0x80 ; const SPCR_SPE = 0x40 ; const SPCR_DORD = 0x20 ; const SPCR_MSTR = 0x10 ; const SPCR_CPOL = 0x8 ; const SPCR_CPHA = 0x4 ; const SPCR_SPR1 = 0x2 ; const SPCR_SPR0 = 0x1 ; const SPSR_SPR_MASK = SPCR_SPR1 | SPCR_SPR0 ; const SPSR_SPIF = 0x80 ; const SPSR_WCOL = 0x40 ; const SPSR_SPI2X = 0x1 ; export const spiConfig = { spiInterrupt : 0x22 , SPCR : 0x4c , SPSR : 0x4d , SPDR : 0x4e , } ; export type SPITransferCallback = ( value : u8 ) => number ; export type SPIByteTransferCallback = ( value : u8 ) => void ; const bitsPerByte = 8 ; export class AVRSPI { deprecated public onTransfer : SPITransferCallback = ( ) => 0 ; /**
     * SPI byte transfer callback. Invoked whenever the user code starts an SPI transaction.
     * You can override this with your own SPI handler logic.
     *
     * The callback receives a argument: the byte sent over the SPI MOSI line.
     * It should call `completeTransfer()` within `transferCycles` CPU cycles.
     */ public onByte : SPIByteTransferCallback = ( value ) => { const valueIn = this . onTransfer ( value ) ; this . cpu . addClockEvent ( ( ) => this . completeTransfer ( valueIn ) , this . transferCycles ) ; } ; private transmissionActive = false ; private SPI : AVRInterruptConfig = { address : this . config . spiInterrupt , flagRegister : this . config . SPSR , flagMask : SPSR_SPIF , enableRegister : this . config . SPCR , enableMask : SPCR_SPIE , } ; constructor ( private cpu , private config , private freqHz ) { const { SPCR , SPSR , SPDR } = config ; cpu . writeHooks [ SPDR ] = ( value ) => { if ( ! ( cpu . data [ SPCR ] & SPCR_SPE ) ) { return ; } if ( this . transmissionActive ) { cpu . data [ SPSR ] |= SPSR_WCOL ; return true ; } cpu . data [ SPSR ] &= ~ SPSR_WCOL ; this . cpu . clearInterrupt ( this . SPI ) ; this . transmissionActive = true ; this . onByte ( value ) ; return true ; } ; cpu . writeHooks [ SPCR ] = ( value ) => { this . cpu . updateInterruptEnable ( this . SPI , value ) ; } ; cpu . writeHooks [ SPSR ] = ( value ) => { this . cpu . data [ SPSR ] = value ; this . cpu . clearInterruptByFlag ( this . SPI , value ) ; } ; } reset ( ) { this . transmissionActive = false ; } param receivedByte completeTransfer ( receivedByte ) { const { SPDR } = this . config ; this . cpu . data [ SPDR ] = receivedByte ; this . cpu . setInterruptFlag ( this . SPI ) ; this . transmissionActive = false ; } get isMaster ( ) { return this . cpu . data [ this . config . SPCR ] & SPCR_MSTR ? true : false ; } get dataOrder ( ) { return this . cpu . data [ this . config . SPCR ] & SPCR_DORD ? 'lsbFirst' : 'msbFirst' ; } get spiMode ( ) { const CPHA = this . cpu . data [ this . config . SPCR ] & SPCR_CPHA ; const CPOL = this . cpu . data [ this . config . SPCR ] & SPCR_CPOL ; return ( ( CPHA ? 2 : 0 ) | ( CPOL ? 1 : 0 ) ) as 0 | 1 | 2 | 3 ; } /**
     * The clock divider is only relevant for Master mode
     */ get clockDivider ( ) { const base = this . cpu . data [ this . config . SPSR ] & SPSR_SPI2X ? 2 : 4 ; switch ( this . cpu . data [ this . config . SPCR ] & SPSR_SPR_MASK ) { case 0b00 : return base ; case 0b01 : return base * 4 ; case 0b10 : return base * 16 ; case 0b11 : return base * 32 ; } throw new Error ( 'Invalid divider value!' ) ; } /** Number of cycles to complete a single byte SPI transaction */ get transferCycles ( ) { return this . clockDivider * bitsPerByte ; } /**
     * The SPI freqeuncy is only relevant to Master mode.
     * In slave mode, the frequency can be as high as F(osc) / 4.
     */ get spiFrequency ( ) { return this . freqHz / this . clockDivider ; } }