-
Notifications
You must be signed in to change notification settings - Fork 80
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(usi): ATtiny85 USI implementation
- Loading branch information
Showing
3 changed files
with
130 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,124 @@ | ||
import { AVRInterruptConfig, CPU } from '../cpu/cpu'; | ||
import { AVRIOPort } from './gpio'; | ||
|
||
const USICR = 0x2d; | ||
const USISR = 0x2e; | ||
const USIDR = 0x2f; | ||
const USIBR = 0x30; | ||
|
||
// USISR bits | ||
const USICNT_MASK = 0xf; | ||
const USIDC = 1 << 4; | ||
const USIPF = 1 << 5; | ||
const USIOIF = 1 << 6; | ||
const USISIF = 1 << 7; | ||
|
||
// USICR bits | ||
const USITC = 1 << 0; | ||
const USICLK = 1 << 1; | ||
const USICS0 = 1 << 2; | ||
const USICS1 = 1 << 3; | ||
const USIWM0 = 1 << 4; | ||
const USIWM1 = 1 << 5; | ||
const USIOIE = 1 << 6; | ||
const USISIE = 1 << 7; | ||
|
||
export class AVRUSI { | ||
// Interrupts | ||
private START: AVRInterruptConfig = { | ||
address: 0xd, | ||
flagRegister: USISR, | ||
flagMask: USISIF, | ||
enableRegister: USICR, | ||
enableMask: USISIE, | ||
}; | ||
|
||
private OVF: AVRInterruptConfig = { | ||
address: 0xe, | ||
flagRegister: USISR, | ||
flagMask: USIOIF, | ||
enableRegister: USICR, | ||
enableMask: USIOIE, | ||
}; | ||
|
||
constructor(cpu: CPU, port: AVRIOPort, portPin: number, dataPin: number, clockPin: number) { | ||
const PIN = portPin; | ||
const PORT = PIN + 2; | ||
port.addListener((value) => { | ||
const twoWire = (cpu.data[USICR] & USIWM1) === USIWM1; | ||
if (twoWire) { | ||
if (value & (1 << clockPin) && !(value & (1 << dataPin))) { | ||
// Start condition detected | ||
cpu.setInterruptFlag(this.START); | ||
} | ||
if (value & (1 << clockPin) && value & (1 << dataPin)) { | ||
// Stop condition detected | ||
cpu.data[USISR] |= USIPF; | ||
} | ||
} | ||
}); | ||
const updateOutput = () => { | ||
const oldValue = cpu.data[PORT]; | ||
const newValue = | ||
cpu.data[USIDR] & 0x80 ? oldValue | (1 << dataPin) : oldValue & ~(1 << dataPin); | ||
cpu.writeHooks[PORT](newValue, oldValue, PORT, 0xff); | ||
if (newValue & 0x80 && !(cpu.data[PIN] & 0x80)) { | ||
cpu.data[USISR] |= USIDC; // Shout output HIGH (pulled-up), but input is LOW | ||
} else { | ||
cpu.data[USISR] &= ~USIDC; | ||
} | ||
}; | ||
const count = () => { | ||
const counter = (cpu.data[USISR] + 1) & USICNT_MASK; | ||
cpu.data[USISR] = (cpu.data[USISR] & ~USICNT_MASK) | counter; | ||
if (!counter) { | ||
cpu.data[USIBR] = cpu.data[USIDR]; | ||
cpu.setInterruptFlag(this.OVF); | ||
} | ||
}; | ||
const shift = (inputValue: number) => { | ||
cpu.data[USIDR] = (cpu.data[USIDR] << 1) | inputValue; | ||
updateOutput(); | ||
}; | ||
cpu.writeHooks[USIDR] = (value: number) => { | ||
cpu.data[USIDR] = value; | ||
updateOutput(); | ||
return true; | ||
}; | ||
cpu.writeHooks[USISR] = (value: number) => { | ||
const writeClearMask = USISIF | USIOIF | USIPF; | ||
cpu.data[USISR] = (cpu.data[USISR] & writeClearMask & ~value) | (value & 0xf); | ||
cpu.clearInterruptByFlag(this.START, value); | ||
cpu.clearInterruptByFlag(this.OVF, value); | ||
return true; | ||
}; | ||
cpu.writeHooks[USICR] = (value: number) => { | ||
cpu.data[USICR] = value & ~(USICLK | USITC); | ||
cpu.updateInterruptEnable(this.START, value); | ||
cpu.updateInterruptEnable(this.OVF, value); | ||
const clockSrc = value & ((USICS1 | USICS0) >> 2); | ||
const mode = value & ((USIWM1 | USIWM0) >> 4); | ||
const usiClk = value & USICLK; | ||
port.openCollector = mode >= 2 ? 1 << dataPin : 0; | ||
const inputValue = cpu.data[PIN] & (1 << dataPin) ? 1 : 0; | ||
if (usiClk && !clockSrc) { | ||
shift(inputValue); | ||
count(); | ||
} | ||
if (value & USITC) { | ||
cpu.writeHooks[PIN](1 << clockPin, cpu.data[PIN], PIN, 0xff); | ||
const newValue = cpu.data[PIN] & (1 << clockPin); | ||
if (usiClk && (clockSrc === 2 || clockSrc === 3)) { | ||
if (clockSrc === 2 && newValue) { | ||
shift(inputValue); | ||
} | ||
if (clockSrc === 3 && !newValue) { | ||
shift(inputValue); | ||
} | ||
count(); | ||
} | ||
return true; | ||
} | ||
}; | ||
} | ||
} |