Skip to content

Commit 8e7dfea

Browse files
deshipupfalcon
authored andcommitted
esp8266/modpybhspi: Add a HSPI module for hardware SPI support
This module uses ESP8266's SPI hardware, which allows much higher speeds. It uses a library from https://github.com/MetalPhreak/ESP8266_SPI_Driver
1 parent 49dd532 commit 8e7dfea

File tree

9 files changed

+962
-0
lines changed

9 files changed

+962
-0
lines changed

docs/esp8266/quickref.rst

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,20 @@ The SPI driver is implemented in software and works on all pins::
189189
spi.write_readinto(b'1234', buf) # write to MOSI and read from MISO into the buffer
190190
spi.write_readinto(buf, buf) # write buf to MOSI and read MISO back into buf
191191

192+
193+
Hardware SPI
194+
------------
195+
196+
The hardware SPI is faster (up to 80Mhz), but only works on following pins:
197+
``MISO`` is gpio2, ``MOSI`` is gpio13, and ``SCK`` is gpio14. It has the same
198+
methods as SPI, except for the pin parameters for the constructor and init
199+
(as those are fixed).
200+
201+
from machine import Pin, HSPI
202+
203+
hspi = HSPI(baudrate=800000000, polarity=0, phase=0)
204+
205+
192206
I2C bus
193207
-------
194208

esp8266/Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ SRC_C = \
7979
modpybadc.c \
8080
modpybuart.c \
8181
modpybspi.c \
82+
modpybhspi.c \
8283
modesp.c \
8384
modnetwork.c \
8485
modutime.c \
@@ -89,6 +90,7 @@ SRC_C = \
8990
$(BUILD)/frozen.c \
9091
fatfs_port.c \
9192
axtls_helpers.c \
93+
hspi.c \
9294
$(SRC_MOD)
9395

9496
STM_SRC_C = $(addprefix stmhal/,\

esp8266/esp8266.ld

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,8 @@ SECTIONS
142142
*modpybuart.o(.literal*, .text*)
143143
*modpybi2c.o(.literal*, .text*)
144144
*modpybspi.o(.literal*, .text*)
145+
*modpybhspi.o(.literal*, .text*)
146+
*hspi.o(.literal*, .text*)
145147
*modesp.o(.literal* .text*)
146148
*modnetwork.o(.literal* .text*)
147149
*moduos.o(.literal* .text*)

esp8266/hspi.c

Lines changed: 327 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,327 @@
1+
/*
2+
* The MIT License (MIT)
3+
*
4+
* Copyright (c) 2015 David Ogilvy (MetalPhreak)
5+
* Modified 2016 by Radomir Dopieralski
6+
*
7+
* Permission is hereby granted, free of charge, to any person obtaining a copy
8+
* of this software and associated documentation files (the "Software"), to deal
9+
* in the Software without restriction, including without limitation the rights
10+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11+
* copies of the Software, and to permit persons to whom the Software is
12+
* furnished to do so, subject to the following conditions:
13+
*
14+
* The above copyright notice and this permission notice shall be included in all
15+
* copies or substantial portions of the Software.
16+
*
17+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23+
* SOFTWARE.
24+
*/
25+
26+
#include "hspi.h"
27+
28+
/*
29+
Wrapper to setup HSPI/SPI GPIO pins and default SPI clock
30+
spi_no - SPI (0) or HSPI (1)
31+
Not used in Micropython.
32+
*/
33+
void spi_init(uint8_t spi_no) {
34+
spi_init_gpio(spi_no, SPI_CLK_USE_DIV);
35+
spi_clock(spi_no, SPI_CLK_PREDIV, SPI_CLK_CNTDIV);
36+
spi_tx_byte_order(spi_no, SPI_BYTE_ORDER_HIGH_TO_LOW);
37+
spi_rx_byte_order(spi_no, SPI_BYTE_ORDER_HIGH_TO_LOW);
38+
39+
SET_PERI_REG_MASK(SPI_USER(spi_no), SPI_CS_SETUP|SPI_CS_HOLD);
40+
CLEAR_PERI_REG_MASK(SPI_USER(spi_no), SPI_FLASH_MODE);
41+
}
42+
43+
44+
/*
45+
Configures SPI mode parameters for clock edge and clock polarity.
46+
spi_no - SPI (0) or HSPI (1)
47+
spi_cpha - (0) Data is valid on clock leading edge
48+
(1) Data is valid on clock trailing edge
49+
spi_cpol - (0) Clock is low when inactive
50+
(1) Clock is high when inactive
51+
For Micropython this version is different from original.
52+
*/
53+
void spi_mode(uint8_t spi_no, uint8_t spi_cpha, uint8_t spi_cpol) {
54+
if (spi_cpol) {
55+
SET_PERI_REG_MASK(SPI_PIN(HSPI), SPI_IDLE_EDGE);
56+
} else {
57+
CLEAR_PERI_REG_MASK(SPI_PIN(HSPI), SPI_IDLE_EDGE);
58+
}
59+
if (spi_cpha == spi_cpol) {
60+
// Mode 3 - MOSI is set on falling edge of clock
61+
// Mode 0 - MOSI is set on falling edge of clock
62+
CLEAR_PERI_REG_MASK(SPI_USER(HSPI), SPI_CK_OUT_EDGE);
63+
SET_PERI_REG_MASK(SPI_USER(HSPI), SPI_CK_I_EDGE);
64+
} else {
65+
// Mode 2 - MOSI is set on rising edge of clock
66+
// Mode 1 - MOSI is set on rising edge of clock
67+
SET_PERI_REG_MASK(SPI_USER(HSPI), SPI_CK_OUT_EDGE);
68+
CLEAR_PERI_REG_MASK(SPI_USER(HSPI), SPI_CK_I_EDGE);
69+
}
70+
}
71+
72+
73+
/*
74+
Initialise the GPIO pins for use as SPI pins.
75+
spi_no - SPI (0) or HSPI (1)
76+
sysclk_as_spiclk -
77+
SPI_CLK_80MHZ_NODIV (1) if using 80MHz for SPI clock.
78+
SPI_CLK_USE_DIV (0) if using divider for lower speed.
79+
*/
80+
void spi_init_gpio(uint8_t spi_no, uint8_t sysclk_as_spiclk) {
81+
uint32_t clock_div_flag = 0;
82+
if (sysclk_as_spiclk) {
83+
clock_div_flag = 0x0001;
84+
}
85+
if (spi_no == SPI) {
86+
// Set bit 8 if 80MHz sysclock required
87+
WRITE_PERI_REG(PERIPHS_IO_MUX, 0x005 | (clock_div_flag<<8));
88+
PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_CLK_U, 1);
89+
PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_CMD_U, 1);
90+
PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_DATA0_U, 1);
91+
PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_DATA1_U, 1);
92+
} else if (spi_no == HSPI) {
93+
// Set bit 9 if 80MHz sysclock required
94+
WRITE_PERI_REG(PERIPHS_IO_MUX, 0x105 | (clock_div_flag<<9));
95+
// GPIO12 is HSPI MISO pin (Master Data In)
96+
PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTDI_U, 2);
97+
// GPIO13 is HSPI MOSI pin (Master Data Out)
98+
PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTCK_U, 2);
99+
// GPIO14 is HSPI CLK pin (Clock)
100+
PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTMS_U, 2);
101+
// GPIO15 is HSPI CS pin (Chip Select / Slave Select)
102+
// In Micropython, we are handling CS ourself in drivers.
103+
// PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTDO_U, 2);
104+
}
105+
}
106+
107+
108+
/*
109+
Set up the control registers for the SPI clock
110+
spi_no - SPI (0) or HSPI (1)
111+
prediv - predivider value (actual division value)
112+
cntdiv - postdivider value (actual division value)
113+
Set either divider to 0 to disable all division (80MHz sysclock)
114+
*/
115+
void spi_clock(uint8_t spi_no, uint16_t prediv, uint8_t cntdiv) {
116+
if (prediv == 0 || cntdiv == 0) {
117+
WRITE_PERI_REG(SPI_CLOCK(spi_no), SPI_CLK_EQU_SYSCLK);
118+
} else {
119+
WRITE_PERI_REG(SPI_CLOCK(spi_no),
120+
(((prediv - 1) & SPI_CLKDIV_PRE) << SPI_CLKDIV_PRE_S) |
121+
(((cntdiv - 1) & SPI_CLKCNT_N) << SPI_CLKCNT_N_S) |
122+
(((cntdiv >> 1) & SPI_CLKCNT_H) << SPI_CLKCNT_H_S) |
123+
((0 & SPI_CLKCNT_L) << SPI_CLKCNT_L_S)
124+
);
125+
}
126+
}
127+
128+
129+
/*
130+
Setup the byte order for shifting data out of buffer
131+
spi_no - SPI (0) or HSPI (1)
132+
byte_order -
133+
SPI_BYTE_ORDER_HIGH_TO_LOW (1)
134+
Data is sent out starting with Bit31 and down to Bit0
135+
SPI_BYTE_ORDER_LOW_TO_HIGH (0)
136+
Data is sent out starting with the lowest BYTE, from MSB to LSB,
137+
followed by the second lowest BYTE, from MSB to LSB, followed by
138+
the second highest BYTE, from MSB to LSB, followed by the highest
139+
BYTE, from MSB to LSB 0xABCDEFGH would be sent as 0xGHEFCDAB.
140+
*/
141+
void spi_tx_byte_order(uint8_t spi_no, uint8_t byte_order) {
142+
if (byte_order) {
143+
SET_PERI_REG_MASK(SPI_USER(spi_no), SPI_WR_BYTE_ORDER);
144+
} else {
145+
CLEAR_PERI_REG_MASK(SPI_USER(spi_no), SPI_WR_BYTE_ORDER);
146+
}
147+
}
148+
149+
150+
/*
151+
Setup the byte order for shifting data into buffer
152+
spi_no - SPI (0) or HSPI (1)
153+
byte_order -
154+
SPI_BYTE_ORDER_HIGH_TO_LOW (1)
155+
Data is read in starting with Bit31 and down to Bit0
156+
SPI_BYTE_ORDER_LOW_TO_HIGH (0)
157+
Data is read in starting with the lowest BYTE, from MSB to LSB,
158+
followed by the second lowest BYTE, from MSB to LSB, followed by
159+
the second highest BYTE, from MSB to LSB, followed by the highest
160+
BYTE, from MSB to LSB 0xABCDEFGH would be read as 0xGHEFCDAB
161+
*/
162+
void spi_rx_byte_order(uint8_t spi_no, uint8_t byte_order) {
163+
if (byte_order) {
164+
SET_PERI_REG_MASK(SPI_USER(spi_no), SPI_RD_BYTE_ORDER);
165+
} else {
166+
CLEAR_PERI_REG_MASK(SPI_USER(spi_no), SPI_RD_BYTE_ORDER);
167+
}
168+
}
169+
170+
171+
/*
172+
SPI transaction function
173+
spi_no - SPI (0) or HSPI (1)
174+
cmd_bits - actual number of bits to transmit
175+
cmd_data - command data
176+
addr_bits - actual number of bits to transmit
177+
addr_data - address data
178+
dout_bits - actual number of bits to transmit
179+
dout_data - output data
180+
din_bits - actual number of bits to receive
181+
Returns: read data - uint32_t containing read in data only if RX was set
182+
0 - something went wrong (or actual read data was 0)
183+
1 - data sent ok (or actual read data is 1)
184+
Note: all data is assumed to be stored in the lower bits of the data variables
185+
(for anything <32 bits).
186+
*/
187+
uint32_t spi_transaction(uint8_t spi_no, uint8_t cmd_bits, uint16_t cmd_data,
188+
uint32_t addr_bits, uint32_t addr_data,
189+
uint32_t dout_bits, uint32_t dout_data,
190+
uint32_t din_bits, uint32_t dummy_bits) {
191+
while (spi_busy(spi_no)) {}; // Wait for SPI to be ready
192+
193+
// Enable SPI Functions
194+
// Disable MOSI, MISO, ADDR, COMMAND, DUMMY in case previously set.
195+
CLEAR_PERI_REG_MASK(SPI_USER(spi_no), SPI_USR_MOSI | SPI_USR_MISO |
196+
SPI_USR_COMMAND | SPI_USR_ADDR | SPI_USR_DUMMY);
197+
198+
// Enable functions based on number of bits. 0 bits = disabled.
199+
// This is rather inefficient but allows for a very generic function.
200+
// CMD ADDR and MOSI are set below to save on an extra if statement.
201+
if (din_bits) {
202+
SET_PERI_REG_MASK(SPI_USER(spi_no), SPI_USR_MISO);
203+
}
204+
if (dummy_bits) {
205+
SET_PERI_REG_MASK(SPI_USER(spi_no), SPI_USR_DUMMY);
206+
}
207+
208+
// Setup Bitlengths
209+
WRITE_PERI_REG(SPI_USER1(spi_no),
210+
// Number of bits in Address
211+
((addr_bits - 1) & SPI_USR_ADDR_BITLEN) << SPI_USR_ADDR_BITLEN_S |
212+
// Number of bits to Send
213+
((dout_bits - 1) & SPI_USR_MOSI_BITLEN) << SPI_USR_MOSI_BITLEN_S |
214+
// Number of bits to receive
215+
((din_bits - 1) & SPI_USR_MISO_BITLEN) << SPI_USR_MISO_BITLEN_S |
216+
// Number of Dummy bits to insert
217+
((dummy_bits - 1) & SPI_USR_DUMMY_CYCLELEN) << SPI_USR_DUMMY_CYCLELEN_S);
218+
219+
// Setup Command Data
220+
if (cmd_bits) {
221+
// Enable COMMAND function in SPI module
222+
SET_PERI_REG_MASK(SPI_USER(spi_no), SPI_USR_COMMAND);
223+
// Align command data to high bits
224+
uint16_t command = cmd_data << (16-cmd_bits);
225+
// Swap byte order
226+
command = ((command>>8)&0xff) | ((command<<8)&0xff00);
227+
WRITE_PERI_REG(SPI_USER2(spi_no), (
228+
(((cmd_bits - 1) & SPI_USR_COMMAND_BITLEN) << SPI_USR_COMMAND_BITLEN_S) |
229+
(command & SPI_USR_COMMAND_VALUE)
230+
));
231+
}
232+
233+
// Setup Address Data
234+
if (addr_bits) {
235+
// Enable ADDRess function in SPI module
236+
SET_PERI_REG_MASK(SPI_USER(spi_no), SPI_USR_ADDR);
237+
// Align address data to high bits
238+
WRITE_PERI_REG(SPI_ADDR(spi_no), addr_data << (32 - addr_bits));
239+
}
240+
241+
// Setup DOUT data
242+
if (dout_bits) {
243+
// Enable MOSI function in SPI module
244+
SET_PERI_REG_MASK(SPI_USER(spi_no), SPI_USR_MOSI);
245+
// Copy data to W0
246+
if (READ_PERI_REG(SPI_USER(spi_no))&SPI_WR_BYTE_ORDER) {
247+
WRITE_PERI_REG(SPI_W0(spi_no), dout_data << (32 - dout_bits));
248+
} else {
249+
uint8_t dout_extra_bits = dout_bits%8;
250+
251+
if (dout_extra_bits) {
252+
// If your data isn't a byte multiple (8/16/24/32 bits) and you
253+
// don't have SPI_WR_BYTE_ORDER set, you need this to move the
254+
// non-8bit remainder to the MSBs. Not sure if there's even a use
255+
// case for this, but it's here if you need it... For example,
256+
// 0xDA4 12 bits without SPI_WR_BYTE_ORDER would usually be output
257+
// as if it were 0x0DA4, of which 0xA4, and then 0x0 would be
258+
// shifted out (first 8 bits of low byte, then 4 MSB bits of high
259+
// byte - ie reverse byte order).
260+
// The code below shifts it out as 0xA4 followed by 0xD as you
261+
// might require.
262+
WRITE_PERI_REG(SPI_W0(spi_no), (
263+
(0xFFFFFFFF << (dout_bits - dout_extra_bits) & dout_data)
264+
<< (8-dout_extra_bits) |
265+
((0xFFFFFFFF >> (32 - (dout_bits - dout_extra_bits)))
266+
& dout_data)
267+
));
268+
} else {
269+
WRITE_PERI_REG(SPI_W0(spi_no), dout_data);
270+
}
271+
}
272+
}
273+
274+
// Begin SPI Transaction
275+
SET_PERI_REG_MASK(SPI_CMD(spi_no), SPI_USR);
276+
277+
// Return DIN data
278+
if (din_bits) {
279+
while (spi_busy(spi_no)) {}; // Wait for SPI transaction to complete
280+
if (READ_PERI_REG(SPI_USER(spi_no))&SPI_RD_BYTE_ORDER) {
281+
// Assuming data in is written to MSB. TBC
282+
return READ_PERI_REG(SPI_W0(spi_no)) >> (32 - din_bits);
283+
} else {
284+
// Read in the same way as DOUT is sent. Note existing contents of
285+
// SPI_W0 remain unless overwritten!
286+
return READ_PERI_REG(SPI_W0(spi_no));
287+
}
288+
return 0; // Something went wrong
289+
}
290+
291+
// Transaction completed
292+
return 1; // Success
293+
}
294+
295+
296+
/*
297+
Just do minimal work needed to send 8 bits.
298+
*/
299+
inline void spi_tx8fast(uint8_t spi_no, uint8_t dout_data) {
300+
while (spi_busy(spi_no)) {}; // Wait for SPI to be ready
301+
302+
// Enable SPI Functions
303+
// Disable MOSI, MISO, ADDR, COMMAND, DUMMY in case previously set.
304+
CLEAR_PERI_REG_MASK(SPI_USER(spi_no), SPI_USR_MOSI | SPI_USR_MISO |
305+
SPI_USR_COMMAND | SPI_USR_ADDR | SPI_USR_DUMMY);
306+
307+
// Setup Bitlengths
308+
WRITE_PERI_REG(SPI_USER1(spi_no),
309+
// Number of bits to Send
310+
((8 - 1) & SPI_USR_MOSI_BITLEN) << SPI_USR_MOSI_BITLEN_S |
311+
// Number of bits to receive
312+
((8 - 1) & SPI_USR_MISO_BITLEN) << SPI_USR_MISO_BITLEN_S);
313+
314+
315+
// Setup DOUT data
316+
// Enable MOSI function in SPI module
317+
SET_PERI_REG_MASK(SPI_USER(spi_no), SPI_USR_MOSI);
318+
// Copy data to W0
319+
if (READ_PERI_REG(SPI_USER(spi_no)) & SPI_WR_BYTE_ORDER) {
320+
WRITE_PERI_REG(SPI_W0(spi_no), dout_data << (32 - 8));
321+
} else {
322+
WRITE_PERI_REG(SPI_W0(spi_no), dout_data);
323+
}
324+
325+
// Begin SPI Transaction
326+
SET_PERI_REG_MASK(SPI_CMD(spi_no), SPI_USR);
327+
}

0 commit comments

Comments
 (0)