Table of contents
SPI Buses on the ESP8266
The ESP8266 has two SPI interfaces. The first is connected to the on-board flash memory. Since the flash memory is a slave device it should be possible to connect other SPI slave devices to the same bus albeit with some complications. See the rather short SPI section in the ESP8266 Arudino Core Libraries documentation on that topic. I only want to familiarize myself with the "normal" operation of the protocol on the ESP8266 so I will restrict my investigation to the second interface called HSPI, presumably for hardware SPI, which is free and can be used in master or slave mode according to the SPI Communication User Guide of the ESP8266 Technical Reference, Version 1.5 by Espressif (2020.07) (see p. 21). The HSPI pins are shown in the Table 1-3, p. 2 of the same document.
| HSPI (Master/Slave) | ||||
|---|---|---|---|---|
| Chip Pin | Function Name | GPIO | Nodemcu/D1 Mini Arduino Pin | |
| Number | Name | |||
| 9 | MTMS | HSPICLK | 14 | D5 |
| 10 | MTDI | HSPIQ/MISO | 12 | D6 |
| 12 | MTCK | HSPID/MOSI | 13 | D7 |
| 13 | MTDO | HPSICS | 15 | D8 |
The chip pin number of the various signals is not very important from the software point of view, the GPIO or Arduino pin numbers are those used in the Arduino framework when programming the ESP8266.
The HSPI Master Says Hello
The SPI library supplied with the ESP8266 Arduino core "supports the entire Arduino SPI API including transactions, ... [except setting] the Clock polarity (CPOL)" (source). In other words, SPI modes 0 and 1 are supported but not modes 2 and 3. To get an idea of what functions are implemented, take a look at the SPI.h - SPI library for esp8266 header file.
To get started, I connected a USB logic analyzer to the ESP8266 SPI bus as shown in the following diagram.

The D0 to D3 labels at the start of each line in the legend are the names given to the signal lines in Pulseview by default, although it was possible to rename them as shown to the right of the dashes. Below is a very simple sketch that can be used in the Arduino IDE or PlatformIO to send data out with the ESP8266 serial peripheral interface.
As can be seen, there is really nothing to the set up of the SPI interface. The SPIClass object SPI is constructed by default in SPI.cpp, so that all that needs to be done is to initiate it with the SPI.begin() function. This is the console output when running this sketch in PlatformIO (it would be the same in the Serial monitor of the Arduino IDE except for some of the initial lines beginning with the double dash --).
At the same time, the following signals were captured with Pulseview (a GUI for the sigrok logic analyzer software).

The thing I want to point out is that the slave select signal is always active (i.e. LOW) eventhough a single byte is being transferred at a time. However if the hardware chip select attribute is set in the setup function, then the select signal is asserted when sending each byte and release between each tranferred byte.

Note how it took about 8 microseconds to send an 8 bit word which corresponds to the default ESP8266 SPI frequency of 1 MHz. It can also be seen that, by default, the bytes of data are sent most significant bit first.
The HSPI Slave Select Signal
/* Only two possible set of pins are allowed
if SPI.pins(6, 7, 8, 0) then HSPI OVERLAP is enabled
if SPI.pins(14, 12, 13, xx) then HSPI is enabled the xx value is ignored
and 15 is used for SS
*/
3 questions: can D3 (SS) be used for I/O if HwCS not enabled can its logic be inverted can another I/O pin be used for HwCS
I understand that some SPI slave devices don't really monitor the falling or rising edge of the select signal. All that matters is that the signal be at its "active level" which usually means it needs to be grounded. When that is the case and when the device is the only SPI slave on the bus, then it is possible to reduce the number of needed connections by one. If a slave select signal is no longer needed, can D8 (GPIO15) be used for another purpose? As it happens D8 can be used as a general purpose input or output pin


88 microseconds to send 11 bytes = 88 bits = 0.99799 MHz close enough to 1 MHz for our purposes!
And as one would expect, if SPI.setHWCs(false) (which the default) or if the method is never invoked then the pin is put in input mode
default frequency is 1MHz
https://github.com/esp8266/Arduino/blob/master/libraries/SPI/SPI.h
SPI.h
/ This defines are not representing the real Divider of the ESP8266
// the Defines match to an AVR Arduino on 16MHz for better compatibility
#define SPI_CLOCK_DIV2 0x00101001 //8 MHz
#define SPI_CLOCK_DIV4 0x00241001 //4 MHz
#define SPI_CLOCK_DIV8 0x004c1001 //2 MHz
#define SPI_CLOCK_DIV16 0x009c1001 //1 MHz
#define SPI_CLOCK_DIV32 0x013c1001 //500 KHz
#define SPI_CLOCK_DIV64 0x027c1001 //250 KHz
#define SPI_CLOCK_DIV128 0x04fc1001 //125 KHz
https://www.arduino.cc/en/Reference/SPI
from Master terminal
restarted master D1 mini
Master: Hello Slave!
Slave: Hello Master!
Master: Are you alive?
Slave: Alive for 3629 seconds!
Master: Are you alive?
Slave: Alive for 3630 seconds!
Master: Are you alive?
Slave: Alive for 3631 seconds!
Master: Are you alive?
Slave: Alive for 3632 seconds!
restarted slave nodemcu
Master: Are you alive?
Slave: Alive for 0 seconds!
Master: Are you alive?
Slave: Alive for 1 seconds!
Master: Are you alive?
Slave: Alive for 2 seconds!
Master: Are you alive?
Slave: Alive for 3 seconds!
Master: Are you alive?
Slave: Alive for 4 seconds!
Master: Are you alive?
Slave: Alive for 5 seconds!
Master: Are you alive?
Slave: Alive for 6 seconds!
from slave terminal
restarted slave
SPISlave_Test
Question: Are you alive?
Answer Sent
Question: Are you alive?
Answer Sent
Question: Are you alive?
Answer Sent
Question: Are you alive?
Answer Sent
Question: Are you alive?
Answer Sent
restarted master
Question: Hello Slave!
Answer Sent
Question: Are you alive?
Answer Sent
Question: Are you alive?
Answer Sent
Question: Are you alive?
Answer Sent
Question: Are you alive?
Answer Sent
Serial Peripheral Interface on the Raspberry Pi