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
|
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) => u8;
const bitsPerByte = 8;
export class AVRSPI {
public onTransfer: SPITransferCallback | null = null;
private transmissionActive = false;
private receivedByte: u8 = 0;
// 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.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);
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;
this.receivedByte = 0;
}
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!');
}
/**
* 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;
}
}
|