Skip to content

Commit 253905c

Browse files
committed
feat(usi): ATtiny85 USI implementation
1 parent b9a0876 commit 253905c

File tree

3 files changed

+130
-2
lines changed

3 files changed

+130
-2
lines changed

src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,3 +60,4 @@ export * from './peripherals/twi';
6060
export { spiConfig, SPIConfig, SPITransferCallback, AVRSPI } from './peripherals/spi';
6161
export { AVRClock, AVRClockConfig, clockConfig } from './peripherals/clock';
6262
export { AVRWatchdog, watchdogConfig, WatchdogConfig } from './peripherals/watchdog';
63+
export { AVRUSI } from './peripherals/usi';

src/peripherals/gpio.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,7 @@ export class AVRIOPort {
210210
private lastValue: u8 = 0;
211211
private lastDdr: u8 = 0;
212212
private lastPin: u8 = 0;
213+
openCollector: u8 = 0;
213214

214215
constructor(private cpu: CPU, readonly portConfig: Readonly<AVRPortConfig>) {
215216
cpu.gpioPorts.add(this);
@@ -317,10 +318,12 @@ export class AVRIOPort {
317318
const ddr = this.cpu.data[this.portConfig.DDR];
318319
const port = this.cpu.data[this.portConfig.PORT];
319320
const bitMask = 1 << index;
321+
const openState = port & bitMask ? PinState.InputPullUp : PinState.Input;
322+
const highValue = this.openCollector & bitMask ? openState : PinState.High;
320323
if (ddr & bitMask) {
321-
return this.lastValue & bitMask ? PinState.High : PinState.Low;
324+
return this.lastValue & bitMask ? highValue : PinState.Low;
322325
} else {
323-
return port & bitMask ? PinState.InputPullUp : PinState.Input;
326+
return openState;
324327
}
325328
}
326329

src/peripherals/usi.ts

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
import { AVRInterruptConfig, CPU } from '../cpu/cpu';
2+
import { AVRIOPort } from './gpio';
3+
4+
const USICR = 0x2d;
5+
const USISR = 0x2e;
6+
const USIDR = 0x2f;
7+
const USIBR = 0x30;
8+
9+
// USISR bits
10+
const USICNT_MASK = 0xf;
11+
const USIDC = 1 << 4;
12+
const USIPF = 1 << 5;
13+
const USIOIF = 1 << 6;
14+
const USISIF = 1 << 7;
15+
16+
// USICR bits
17+
const USITC = 1 << 0;
18+
const USICLK = 1 << 1;
19+
const USICS0 = 1 << 2;
20+
const USICS1 = 1 << 3;
21+
const USIWM0 = 1 << 4;
22+
const USIWM1 = 1 << 5;
23+
const USIOIE = 1 << 6;
24+
const USISIE = 1 << 7;
25+
26+
export class AVRUSI {
27+
// Interrupts
28+
private START: AVRInterruptConfig = {
29+
address: 0xd,
30+
flagRegister: USISR,
31+
flagMask: USISIF,
32+
enableRegister: USICR,
33+
enableMask: USISIE,
34+
};
35+
36+
private OVF: AVRInterruptConfig = {
37+
address: 0xe,
38+
flagRegister: USISR,
39+
flagMask: USIOIF,
40+
enableRegister: USICR,
41+
enableMask: USIOIE,
42+
};
43+
44+
constructor(cpu: CPU, port: AVRIOPort, portPin: number, dataPin: number, clockPin: number) {
45+
const PIN = portPin;
46+
const PORT = PIN + 2;
47+
port.addListener((value) => {
48+
const twoWire = (cpu.data[USICR] & USIWM1) === USIWM1;
49+
if (twoWire) {
50+
if (value & (1 << clockPin) && !(value & (1 << dataPin))) {
51+
// Start condition detected
52+
cpu.setInterruptFlag(this.START);
53+
}
54+
if (value & (1 << clockPin) && value & (1 << dataPin)) {
55+
// Stop condition detected
56+
cpu.data[USISR] |= USIPF;
57+
}
58+
}
59+
});
60+
const updateOutput = () => {
61+
const oldValue = cpu.data[PORT];
62+
const newValue =
63+
cpu.data[USIDR] & 0x80 ? oldValue | (1 << dataPin) : oldValue & ~(1 << dataPin);
64+
cpu.writeHooks[PORT](newValue, oldValue, PORT, 0xff);
65+
if (newValue & 0x80 && !(cpu.data[PIN] & 0x80)) {
66+
cpu.data[USISR] |= USIDC; // Shout output HIGH (pulled-up), but input is LOW
67+
} else {
68+
cpu.data[USISR] &= ~USIDC;
69+
}
70+
};
71+
const count = () => {
72+
const counter = (cpu.data[USISR] + 1) & USICNT_MASK;
73+
cpu.data[USISR] = (cpu.data[USISR] & ~USICNT_MASK) | counter;
74+
if (!counter) {
75+
cpu.data[USIBR] = cpu.data[USIDR];
76+
cpu.setInterruptFlag(this.OVF);
77+
}
78+
};
79+
const shift = (inputValue: number) => {
80+
cpu.data[USIDR] = (cpu.data[USIDR] << 1) | inputValue;
81+
updateOutput();
82+
};
83+
cpu.writeHooks[USIDR] = (value: number) => {
84+
cpu.data[USIDR] = value;
85+
updateOutput();
86+
return true;
87+
};
88+
cpu.writeHooks[USISR] = (value: number) => {
89+
const writeClearMask = USISIF | USIOIF | USIPF;
90+
cpu.data[USISR] = (cpu.data[USISR] & writeClearMask & ~value) | (value & 0xf);
91+
cpu.clearInterruptByFlag(this.START, value);
92+
cpu.clearInterruptByFlag(this.OVF, value);
93+
return true;
94+
};
95+
cpu.writeHooks[USICR] = (value: number) => {
96+
cpu.data[USICR] = value & ~(USICLK | USITC);
97+
cpu.updateInterruptEnable(this.START, value);
98+
cpu.updateInterruptEnable(this.OVF, value);
99+
const clockSrc = value & ((USICS1 | USICS0) >> 2);
100+
const mode = value & ((USIWM1 | USIWM0) >> 4);
101+
const usiClk = value & USICLK;
102+
port.openCollector = mode >= 2 ? 1 << dataPin : 0;
103+
const inputValue = cpu.data[PIN] & (1 << dataPin) ? 1 : 0;
104+
if (usiClk && !clockSrc) {
105+
shift(inputValue);
106+
count();
107+
}
108+
if (value & USITC) {
109+
cpu.writeHooks[PIN](1 << clockPin, cpu.data[PIN], PIN, 0xff);
110+
const newValue = cpu.data[PIN] & (1 << clockPin);
111+
if (usiClk && (clockSrc === 2 || clockSrc === 3)) {
112+
if (clockSrc === 2 && newValue) {
113+
shift(inputValue);
114+
}
115+
if (clockSrc === 3 && !newValue) {
116+
shift(inputValue);
117+
}
118+
count();
119+
}
120+
return true;
121+
}
122+
};
123+
}
124+
}

0 commit comments

Comments
 (0)