aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorUri Shaked2021-10-30 21:55:31 +0300
committerUri Shaked2021-10-30 21:55:31 +0300
commit64736a76f0989dbc86e86df85349b1ee7e51ee8f (patch)
tree1eaddd2144ea35d97cb33f919a3f52bd40171534
parent0.18.5 (diff)
downloadavr8js-64736a76f0989dbc86e86df85349b1ee7e51ee8f.tar.gz
avr8js-64736a76f0989dbc86e86df85349b1ee7e51ee8f.tar.bz2
avr8js-64736a76f0989dbc86e86df85349b1ee7e51ee8f.zip
feat(spi): add `onByte` callback
a more versatile alternative to the `onTransfer` callback. Depracate `onTransfer()`.
Diffstat (limited to '')
-rw-r--r--src/peripherals/spi.spec.ts18
-rw-r--r--src/peripherals/spi.ts45
2 files changed, 44 insertions, 19 deletions
diff --git a/src/peripherals/spi.spec.ts b/src/peripherals/spi.spec.ts
index 9e6dac5..da429fe 100644
--- a/src/peripherals/spi.spec.ts
+++ b/src/peripherals/spi.spec.ts
@@ -96,26 +96,26 @@ describe('SPI', () => {
expect(spi.isMaster).toBe(true);
});
- it('should call the `onTransfer` callback when initiating an SPI trasfer by writing to SPDR', () => {
+ it('should call the `onByteTransfer` callback when initiating an SPI trasfer by writing to SPDR', () => {
const cpu = new CPU(new Uint16Array(1024));
const spi = new AVRSPI(cpu, spiConfig, FREQ_16MHZ);
- spi.onTransfer = jest.fn();
+ spi.onByte = jest.fn();
cpu.writeData(SPCR, SPE | MSTR);
cpu.writeData(SPDR, 0x8f);
- expect(spi.onTransfer).toHaveBeenCalledWith(0x8f);
+ expect(spi.onByte).toHaveBeenCalledWith(0x8f);
});
it('should ignore SPDR writes when the SPE bit in SPCR is clear', () => {
const cpu = new CPU(new Uint16Array(1024));
const spi = new AVRSPI(cpu, spiConfig, FREQ_16MHZ);
- spi.onTransfer = jest.fn();
+ spi.onByte = jest.fn();
cpu.writeData(SPCR, MSTR);
cpu.writeData(SPDR, 0x8f);
- expect(spi.onTransfer).not.toHaveBeenCalled();
+ expect(spi.onByte).not.toHaveBeenCalled();
});
it('should transmit a byte successfully (integration)', () => {
@@ -155,9 +155,9 @@ describe('SPI', () => {
let byteReceivedFromAsmCode: number | null = null;
- spi.onTransfer = (value) => {
+ spi.onByte = (value) => {
byteReceivedFromAsmCode = value;
- return 0x5b; // we copy this byte to
+ cpu.addClockEvent(() => spi.completeTransfer(0x5b), spi.transferCycles);
};
const runner = new TestProgramRunner(cpu, () => 0);
@@ -228,7 +228,9 @@ describe('SPI', () => {
it('should should only update SPDR when tranfer finishes (double buffering)', () => {
const cpu = new CPU(new Uint16Array(1024));
const spi = new AVRSPI(cpu, spiConfig, FREQ_16MHZ);
- spi.onTransfer = jest.fn(() => 0x88);
+ spi.onByte = () => {
+ cpu.addClockEvent(() => spi.completeTransfer(0x88), spi.transferCycles);
+ };
cpu.writeData(SPCR, SPE | MSTR);
cpu.writeData(SPDR, 0x8f);
diff --git a/src/peripherals/spi.ts b/src/peripherals/spi.ts
index 7b753ab..3167c07 100644
--- a/src/peripherals/spi.ts
+++ b/src/peripherals/spi.ts
@@ -31,15 +31,28 @@ export const spiConfig: SPIConfig = {
SPDR: 0x4e,
};
-export type SPITransferCallback = (value: u8) => u8;
+export type SPITransferCallback = (value: u8) => number;
+export type SPIByteTransferCallback = (value: u8) => void;
const bitsPerByte = 8;
export class AVRSPI {
- public onTransfer: SPITransferCallback | null = null;
+ /** @deprecated Use onByte() instead */
+ 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 receivedByte: u8 = 0;
// Interrupts
private SPI: AVRInterruptConfig = {
@@ -68,14 +81,8 @@ export class AVRSPI {
cpu.data[SPSR] &= ~SPSR_WCOL;
this.cpu.clearInterrupt(this.SPI);
- this.receivedByte = this.onTransfer?.(value) ?? 0;
- const cyclesToComplete = this.clockDivider * bitsPerByte;
this.transmissionActive = true;
- this.cpu.addClockEvent(() => {
- this.cpu.data[SPDR] = this.receivedByte;
- this.cpu.setInterruptFlag(this.SPI);
- this.transmissionActive = false;
- }, cyclesToComplete);
+ this.onByte(value);
return true;
};
cpu.writeHooks[SPCR] = (value: u8) => {
@@ -89,7 +96,18 @@ export class AVRSPI {
reset() {
this.transmissionActive = false;
- this.receivedByte = 0;
+ }
+
+ /**
+ * Completes an SPI transaction. Call this method only from the `onByte` callback.
+ *
+ * @param receivedByte Byte read from the SPI MISO line.
+ */
+ completeTransfer(receivedByte: number) {
+ const { SPDR } = this.config;
+ this.cpu.data[SPDR] = receivedByte;
+ this.cpu.setInterruptFlag(this.SPI);
+ this.transmissionActive = false;
}
get isMaster() {
@@ -128,6 +146,11 @@ export class AVRSPI {
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.
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.