1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
|
// SPDX-License-Identifier: MIT
// Copyright (c) Uri Shaked and contributors
import { AVRInterruptConfig, CPU } from '../cpu/cpu';
import { u8 } from '../types';
export interface SPIConfig {
spiInterrupt: u8;
SPCR: u8;
SPSR: u8;
SPDR: u8;
}
// Register bits:
const SPCR_SPIE = 0x80; // SPI Interrupt Enable
const SPCR_SPE = 0x40; // SPI Enable
const SPCR_DORD = 0x20; // Data Order
const SPCR_MSTR = 0x10; // Master/Slave Select
const SPCR_CPOL = 0x8; // Clock Polarity
const SPCR_CPHA = 0x4; // Clock Phase
const SPCR_SPR1 = 0x2; // SPI Clock Rate Select 1
const SPCR_SPR0 = 0x1; // SPI Clock Rate Select 0
const SPSR_SPR_MASK = SPCR_SPR1 | SPCR_SPR0;
const SPSR_SPIF = 0x80; // SPI Interrupt Flag
const SPSR_WCOL = 0x40; // Write COLlision Flag
const SPSR_SPI2X = 0x1; // Double SPI Speed Bit
export const spiConfig: SPIConfig = {
spiInterrupt: 0x22,
SPCR: 0x4c,
SPSR: 0x4d,
SPDR: 0x4e,
};
export type SPITransferCallback = (value: u8) => number;
export type SPIByteTransferCallback = (value: u8) => void;
const bitsPerByte = 8;
export class AVRSPI {
/** @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;
// Interrupts
private SPI: AVRInterruptConfig = {
address: this.config.spiInterrupt,
flagRegister: this.config.SPSR,
flagMask: SPSR_SPIF,
enableRegister: this.config.SPCR,
enableMask: SPCR_SPIE,
};
constructor(
private cpu: CPU,
private config: SPIConfig,
private freqHz: number,
) {
const { SPCR, SPSR, SPDR } = config;
cpu.writeHooks[SPDR] = (value: u8) => {
if (!(cpu.data[SPCR] & SPCR_SPE)) {
// SPI not enabled, ignore write
return;
}
// Write collision
if (this.transmissionActive) {
cpu.data[SPSR] |= SPSR_WCOL;
return true;
}
// Clear write collision / interrupt flags
cpu.data[SPSR] &= ~SPSR_WCOL;
this.cpu.clearInterrupt(this.SPI);
this.transmissionActive = true;
this.onByte(value);
return true;
};
cpu.writeHooks[SPCR] = (value: u8) => {
this.cpu.updateInterruptEnable(this.SPI, value);
};
cpu.writeHooks[SPSR] = (value: u8) => {
this.cpu.data[SPSR] = value;
this.cpu.clearInterruptByFlag(this.SPI, value);
};
}
reset() {
this.transmissionActive = false;
}
/**
* 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() {
return this.cpu.data[this.config.SPCR] & SPCR_MSTR ? true : false;
}
get dataOrder() {
return this.cpu.data[this.config.SPCR] & SPCR_DORD ? 'lsbFirst' : 'msbFirst';
}
get spiMode() {
const CPHA = this.cpu.data[this.config.SPCR] & SPCR_CPHA;
const CPOL = this.cpu.data[this.config.SPCR] & SPCR_CPOL;
return ((CPHA ? 2 : 0) | (CPOL ? 1 : 0)) as 0 | 1 | 2 | 3;
}
/**
* The clock divider is only relevant for Master mode
*/
get clockDivider() {
const base = this.cpu.data[this.config.SPSR] & SPSR_SPI2X ? 2 : 4;
switch (this.cpu.data[this.config.SPCR] & SPSR_SPR_MASK) {
case 0b00:
return base;
case 0b01:
return base * 4;
case 0b10:
return base * 16;
case 0b11:
return base * 32;
}
// We should never get here:
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.
*/
get spiFrequency() {
return this.freqHz / this.clockDivider;
}
}
|