Skip to content

Commit

Permalink
feat(usi): ATtiny85 USI implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
urish committed May 17, 2022
1 parent b9a0876 commit 253905c
Show file tree
Hide file tree
Showing 3 changed files with 130 additions and 2 deletions.
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
7 changes: 5 additions & 2 deletions src/peripherals/gpio.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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;
}
}

Expand Down
124 changes: 124 additions & 0 deletions src/peripherals/usi.ts
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;
}
};
}
}

0 comments on commit 253905c

Please sign in to comment.