Skip to content

Commit 65973e8

Browse files
Replace external SPIRAM lib w/internal optimized
Instead of requiring everyone to download and install another Arduino library to use this lib, include an optimized, self-contained one from the 8266 VM PR: esp8266/Arduino#6994 Does change the API as it now requires a HW chip select, so that parameter is removed from the SpiRAM buffer object.
1 parent aba310d commit 65973e8

File tree

4 files changed

+205
-11
lines changed

4 files changed

+205
-11
lines changed

examples/StreamMP3FromHTTP_SPIRAM/StreamMP3FromHTTP_SPIRAM.ino

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ void setup()
7373
file = new AudioFileSourceICYStream(URL);
7474
file->RegisterMetadataCB(MDCallback, (void*)"ICY");
7575
// Initialize 23LC1024 SPI RAM buffer with chip select ion GPIO4 and ram size of 128KByte
76-
buff = new AudioFileSourceSPIRAMBuffer(file, 4, 131072);
76+
buff = new AudioFileSourceSPIRAMBuffer(file, 131072);
7777
buff->RegisterStatusCB(StatusCallback, (void*)"buffer");
7878
out = new AudioOutputI2SNoDAC();
7979
mp3 = new AudioGeneratorMP3();

src/AudioFileSourceSPIRAMBuffer.cpp

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,9 @@
2424

2525
#pragma GCC optimize ("O3")
2626

27-
AudioFileSourceSPIRAMBuffer::AudioFileSourceSPIRAMBuffer(AudioFileSource *source, uint8_t csPin, uint32_t buffSizeBytes)
27+
AudioFileSourceSPIRAMBuffer::AudioFileSourceSPIRAMBuffer(AudioFileSource *source, uint32_t buffSizeBytes)
2828
{
29-
Spiram = new ESP8266Spiram(csPin, 40e6);
30-
Spiram->begin();
31-
Spiram->setSeqMode();
29+
ram.begin(20);
3230
ramSize = buffSizeBytes;
3331
writePtr = 0;
3432
readPtr = 0;
@@ -41,6 +39,7 @@ AudioFileSourceSPIRAMBuffer::AudioFileSourceSPIRAMBuffer(AudioFileSource *source
4139

4240
AudioFileSourceSPIRAMBuffer::~AudioFileSourceSPIRAMBuffer()
4341
{
42+
ram.end();
4443
}
4544

4645
bool AudioFileSourceSPIRAMBuffer::seek(int32_t pos, int dir)
@@ -85,7 +84,7 @@ uint32_t AudioFileSourceSPIRAMBuffer::read(void *data, uint32_t len)
8584
while (bytesAvailable!=ramSize) {
8685
length = src->read(buffer, toRead);
8786
if(length>0) {
88-
Spiram->write(writePtr, buffer, length);
87+
ram.writeBytes(writePtr, buffer, length);
8988
bytesAvailable+=length;
9089
writePtr = bytesAvailable % ramSize;
9190
if ((ramSize-bytesAvailable)<toRead) {
@@ -107,7 +106,7 @@ uint32_t AudioFileSourceSPIRAMBuffer::read(void *data, uint32_t len)
107106
uint32_t toReadFromBuffer = (len < bytesAvailable) ? len : bytesAvailable;
108107
if (toReadFromBuffer>0) {
109108
// Pull from buffer until we've got none left or we've satisfied the request
110-
Spiram->read(readPtr, ptr, toReadFromBuffer);
109+
ram.readBytes(readPtr, ptr, toReadFromBuffer);
111110
readPtr = (readPtr+toReadFromBuffer) % ramSize;
112111
bytes = toReadFromBuffer;
113112
bytesAvailable-=toReadFromBuffer;
@@ -138,7 +137,7 @@ void AudioFileSourceSPIRAMBuffer::fill()
138137
}
139138
uint16_t cnt = src->readNonBlock(buffer, sizeof(buffer));
140139
if (cnt) {
141-
Spiram->write(writePtr, buffer, cnt);
140+
ram.writeBytes(writePtr, buffer, cnt);
142141
bytesAvailable+=cnt;
143142
writePtr = (writePtr + cnt) % ramSize;
144143
#ifdef SPIBUF_DEBUG

src/AudioFileSourceSPIRAMBuffer.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,14 @@
2424

2525
#include "AudioFileSource.h"
2626
#include <SPI.h>
27-
#include <ESP8266Spiram.h> // https://github.com/Gianbacchio/ESP8266_Spiram
27+
#include "spiram-fast.h"
2828

2929
// #define SPIBUF_DEBUG
3030

3131
class AudioFileSourceSPIRAMBuffer : public AudioFileSource
3232
{
3333
public:
34-
AudioFileSourceSPIRAMBuffer(AudioFileSource *in, uint8_t csPin, uint32_t bufferBytes);
34+
AudioFileSourceSPIRAMBuffer(AudioFileSource *in, uint32_t bufferBytes);
3535
virtual ~AudioFileSourceSPIRAMBuffer() override;
3636

3737
virtual uint32_t read(void *data, uint32_t len) override;
@@ -47,7 +47,7 @@ class AudioFileSourceSPIRAMBuffer : public AudioFileSource
4747

4848
private:
4949
AudioFileSource *src;
50-
ESP8266Spiram *Spiram;
50+
ESP8266SPIRAM ram;
5151
uint32_t ramSize;
5252
uint32_t writePtr;
5353
uint32_t readPtr;

src/spiram-fast.h

Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
/*
2+
spiram-fast - Fast, hardcoded interface for SPI-based RAMs, allowing DIO
3+
mode to be used and speeding up individual SPI operations
4+
significantly.
5+
6+
Copyright (c) 2020 Earle F. Philhower, III All rights reserved.
7+
8+
This library is free software; you can redistribute it and/or
9+
modify it under the terms of the GNU Lesser General Public
10+
License as published by the Free Software Foundation; either
11+
version 2.1 of the License, or (at your option) any later version.
12+
13+
This library is distributed in the hope that it will be useful,
14+
but WITHOUT ANY WARRANTY; without even the implied warranty of
15+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16+
Lesser General Public License for more details.
17+
18+
You should have received a copy of the GNU Lesser General Public
19+
License along with this library; if not, write to the Free Software
20+
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
21+
22+
*/
23+
24+
class ESP8266SPIRAM {
25+
private:
26+
typedef struct {
27+
volatile uint32_t spi_cmd; // The SPI can change this behind our backs, so volatile!
28+
uint32_t spi_addr;
29+
uint32_t spi_ctrl;
30+
uint32_t spi_ctrl1; // undocumented? Not shown in the reg map
31+
uint32_t spi_rd_status;
32+
uint32_t spi_ctrl2;
33+
uint32_t spi_clock;
34+
uint32_t spi_user;
35+
uint32_t spi_user1;
36+
uint32_t spi_user2;
37+
uint32_t spi_wr_status;
38+
uint32_t spi_pin;
39+
uint32_t spi_slave;
40+
uint32_t spi_slave1;
41+
uint32_t spi_slave2;
42+
uint32_t spi_slave3;
43+
uint32_t spi_w[16]; // NOTE: You need a memory barrier before reading these after a read xaction
44+
uint32_t spi_ext3;
45+
} spi_regs;
46+
spi_regs *spi1 = (spi_regs*)&SPI1CMD;
47+
48+
// The standard HSPI bus pins are used
49+
static constexpr uint8_t cs = 15;
50+
static constexpr uint8_t miso = 12;
51+
static constexpr uint8_t mosi = 13;
52+
static constexpr uint8_t sck = 14;
53+
54+
uint32_t spi_clkval;
55+
56+
typedef enum { sio = 0, dio = 1 } iotype;
57+
static constexpr iotype hspi_mode = dio;
58+
59+
static constexpr int read_delay = (hspi_mode == dio) ? 4-1 : 0;
60+
61+
void spi_init()
62+
{
63+
pinMode(sck, SPECIAL);
64+
pinMode(miso, SPECIAL);
65+
pinMode(mosi, SPECIAL);
66+
pinMode(cs, SPECIAL);
67+
spi1->spi_cmd = 0;
68+
GPMUX &= ~(1 << 9);
69+
spi1->spi_clock = spi_clkval;
70+
spi1->spi_ctrl = 0 ; // MSB first + plain SPI mode
71+
spi1->spi_ctrl1 = 0; // undocumented, clear for safety?
72+
spi1->spi_ctrl2 = 0; // No add'l delays on signals
73+
spi1->spi_user2 = 0; // No insn or insn_bits to set
74+
}
75+
76+
// The SPI hardware cannot make the "command" portion dual or quad, only the addr and data
77+
// So using the command portion of the cycle will not work. Comcatenate the address
78+
// and command into a single 32-bit chunk "address" which will be sent across both bits.
79+
void spi_writetransaction(int addr, int addr_bits, int dummy_bits, int data_bits, iotype dual)
80+
{
81+
// Ensure no writes are still ongoing
82+
while (spi1->spi_cmd & SPIBUSY) { /* busywait */ }
83+
84+
spi1->spi_addr = addr;
85+
spi1->spi_user = (addr_bits? SPIUADDR : 0) | (dummy_bits ? SPIUDUMMY : 0) | (data_bits ? SPIUMOSI : 0) | (dual ? SPIUFWDIO : 0);
86+
spi1->spi_user1 = (addr_bits << 26) | (data_bits << 17) | dummy_bits;
87+
// No need to set spi_user2, insn field never used
88+
__asm ( "" ::: "memory" );
89+
spi1->spi_cmd = SPIBUSY;
90+
// The write may continue on in the background
91+
}
92+
93+
uint32_t spi_readtransaction(int addr, int addr_bits, int dummy_bits, int data_bits, iotype dual)
94+
{
95+
// Ensure no writes are still ongoing
96+
while (spi1->spi_cmd & SPIBUSY) { /* busywait */ }
97+
98+
spi1->spi_addr = addr;
99+
spi1->spi_user = (addr_bits? SPIUADDR : 0) | (dummy_bits ? SPIUDUMMY : 0) | SPIUMISO | (dual ? SPIUFWDIO : 0);
100+
spi1->spi_user1 = (addr_bits << 26) | (data_bits << 8) | dummy_bits;
101+
// No need to set spi_user2, insn field never used
102+
__asm ( "" ::: "memory" );
103+
spi1->spi_cmd = SPIBUSY;
104+
while (spi1->spi_cmd & SPIBUSY) { /* busywait */ }
105+
__asm ( "" ::: "memory" );
106+
return spi1->spi_w[0];
107+
}
108+
109+
public:
110+
ESP8266SPIRAM()
111+
{
112+
/* noop */
113+
}
114+
~ESP8266SPIRAM()
115+
{
116+
end();
117+
}
118+
void readBytes(uint32_t addr, void *destV, int count)
119+
{
120+
uint8_t *dest = (uint8_t*)destV;
121+
while (count > 0) {
122+
int toRead = std::min(count, 64);
123+
spi_readtransaction((0x03 << 24) | addr, 32-1, read_delay, toRead * 8 - 1, hspi_mode);
124+
memcpy(dest, spi1->spi_w, toRead);
125+
count -= toRead;
126+
dest += toRead;
127+
addr += toRead;
128+
}
129+
}
130+
131+
void writeBytes(uint32_t addr, const void *srcV, int count)
132+
{
133+
const uint8_t *src = (const uint8_t*)srcV;
134+
while (count > 0) {
135+
int toWrite = std::min(count, 64);
136+
memcpy(spi1->spi_w, src, toWrite);
137+
spi_writetransaction((0x02 << 24) | addr, 32-1, 0, toWrite * 8 - 1, hspi_mode);
138+
count -= toWrite;
139+
src += toWrite;
140+
addr += toWrite;
141+
}
142+
}
143+
144+
void begin(int freqMHz = 20)
145+
{
146+
if (freqMHz >= 40) {
147+
spi_clkval = 0x00001001;
148+
} else if (freqMHz >= 30) {
149+
spi_clkval = 0x00002001;
150+
} else if (freqMHz >= 20) {
151+
spi_clkval = 0x00041001;
152+
} else if (freqMHz >= 10) {
153+
spi_clkval = 0x000c1001;
154+
} else { // 5 MHz
155+
spi_clkval = 0x001c1001;
156+
}
157+
// Manually reset chip from DIO to SIO mode (HW SPI has issues with <8 bits/clocks total output)
158+
digitalWrite(cs, HIGH);
159+
digitalWrite(mosi, HIGH);
160+
digitalWrite(miso, HIGH);
161+
digitalWrite(sck, LOW);
162+
pinMode(cs, OUTPUT);
163+
pinMode(miso, OUTPUT);
164+
pinMode(mosi, OUTPUT);
165+
pinMode(sck, OUTPUT);
166+
digitalWrite(cs, LOW);
167+
for (int i = 0; i < 4; i++) {
168+
digitalWrite(sck, HIGH);
169+
digitalWrite(sck, LOW);
170+
}
171+
digitalWrite(cs, HIGH);
172+
173+
// Set up the SPI regs
174+
spi_init();
175+
176+
// Enable streaming read/write mode
177+
spi1->spi_w[0] = 0x40;
178+
spi_writetransaction(0x01<<24, 8-1, 0, 8-1, sio);
179+
180+
if (hspi_mode == dio) {
181+
// Ramp up to DIO mode
182+
spi_writetransaction(0x3b<<24, 8-1, 0, 0, sio);
183+
spi1->spi_ctrl |= SPICDIO | SPICFASTRD;
184+
}
185+
}
186+
187+
void end()
188+
{
189+
pinMode(cs, INPUT);
190+
pinMode(miso, INPUT);
191+
pinMode(mosi, INPUT);
192+
pinMode(sck, INPUT);
193+
}
194+
195+
};

0 commit comments

Comments
 (0)