aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/index.ts1
-rw-r--r--src/peripherals/gpio.ts7
-rw-r--r--src/peripherals/usi.ts124
3 files changed, 130 insertions, 2 deletions
diff --git a/src/index.ts b/src/index.ts
index c1089aa..1429db5 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -60,3 +60,4 @@ export * from './peripherals/twi';
export { spiConfig, SPIConfig, SPITransferCallback, AVRSPI } from './peripherals/spi';
export { AVRClock, AVRClockConfig, clockConfig } from './peripherals/clock';
export { AVRWatchdog, watchdogConfig, WatchdogConfig } from './peripherals/watchdog';
+export { AVRUSI } from './peripherals/usi';
diff --git a/src/peripherals/gpio.ts b/src/peripherals/gpio.ts
index 07667d9..9d84307 100644
--- a/src/peripherals/gpio.ts
+++ b/src/peripherals/gpio.ts
@@ -210,6 +210,7 @@ export class AVRIOPort {
private lastValue: u8 = 0;
private lastDdr: u8 = 0;
private lastPin: u8 = 0;
+ openCollector: u8 = 0;
constructor(private cpu: CPU, readonly portConfig: Readonly<AVRPortConfig>) {
cpu.gpioPorts.add(this);
@@ -317,10 +318,12 @@ export class AVRIOPort {
const ddr = this.cpu.data[this.portConfig.DDR];
const port = this.cpu.data[this.portConfig.PORT];
const bitMask = 1 << index;
+ const openState = port & bitMask ? PinState.InputPullUp : PinState.Input;
+ const highValue = this.openCollector & bitMask ? openState : PinState.High;
if (ddr & bitMask) {
- return this.lastValue & bitMask ? PinState.High : PinState.Low;
+ return this.lastValue & bitMask ? highValue : PinState.Low;
} else {
- return port & bitMask ? PinState.InputPullUp : PinState.Input;
+ return openState;
}
}
diff --git a/src/peripherals/usi.ts b/src/peripherals/usi.ts
new file mode 100644
index 0000000..c9e09e7
--- /dev/null
+++ b/src/peripherals/usi.ts
@@ -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;
+ }
+ };
+ }
+}
If you find this content useful please consider a small donation via bitcoin: bitcoin:1mxKyQsHHugqRxPKgwaA7wUwJEGCNthn2?amount=0.001&message=Donation Scraping git contents via cgit is very resource intensive. The continued hosting of these git repositories is entirely financed by your bitcoin contributions. For donations of a least 1 mBTC you include your IP address in the message of your bitcoin donation. This will remove this message in the future for requests from your IP, which will significantly reduce token usage. In any case, any contributions to bitcoin address 1mxKyQsHHugqRxPKgwaA7wUwJEGCNthn2 are very welcome. Thanks in advance for you contribution to a self-sustaining ecosystem.