forked from esp8266/Arduino
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcore_esp8266_spi_utils.cpp
197 lines (163 loc) · 6.18 KB
/
core_esp8266_spi_utils.cpp
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
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
/*
core_esp8266_spi_utils.cpp
Copyright (c) 2019 Mike Nix. All rights reserved.
This file is part of the esp8266 core for Arduino environment.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <stdint.h>
#include <string.h>
// register names
#include "esp8266_peri.h"
// for flashchip
#include "spi_flash.h"
// for PRECACHE_*
#include "core_esp8266_features.h"
#include "spi_utils.h"
extern "C" uint32_t Wait_SPI_Idle(SpiFlashChip *fc);
namespace experimental {
/*
* critical part of SPICommand.
* Kept in a separate function to aid with precaching
* PRECACHE_* saves having to make the function IRAM_ATTR.
*
* spiIfNum needs to be volatile to keep the optimiser from
* deciding it can be treated as a constant (due to this being a
* static function only called with spiIfNum set to 0)
*
* Note: if porting to ESP32 mosi/miso bits are set in 2 registers, not 1.
*/
static SpiOpResult PRECACHE_ATTR
_SPICommand(volatile uint32_t spiIfNum,
uint32_t spic,uint32_t spiu,uint32_t spiu1,uint32_t spiu2,
uint32_t *data,uint32_t writeWords,uint32_t readWords)
{
if (spiIfNum>1)
return SPI_RESULT_ERR;
// force SPI register access via base+offset.
// Prevents loading individual address constants from flash.
uint32_t *spibase = (uint32_t*)(spiIfNum ? &(SPI1CMD) : &(SPI0CMD));
#define SPIREG(reg) (*((volatile uint32_t *)(spibase+(&(reg) - &(SPI0CMD)))))
// preload any constants and functions we need into variables
// Everything defined here must be volatile or the optimizer can
// treat them as constants, resulting in the flash reads we're
// trying to avoid
uint32_t (* volatile Wait_SPI_Idlep)(SpiFlashChip *) = Wait_SPI_Idle;
volatile SpiFlashChip *fchip=flashchip;
volatile uint32_t spicmdusr=SPICMDUSR;
uint32_t saved_ps=0;
if (!spiIfNum) {
// Only need to disable interrupts and precache when using SPI0
saved_ps = xt_rsil(15);
PRECACHE_START();
Wait_SPI_Idlep((SpiFlashChip *)fchip);
}
// preserve essential controller state such as incoming/outgoing
// data lengths and IO mode.
uint32_t oldSPI0U = SPIREG(SPI0U);
uint32_t oldSPI0U2= SPIREG(SPI0U2);
uint32_t oldSPI0C = SPIREG(SPI0C);
//SPI0S &= ~(SPISE|SPISBE|SPISSE|SPISCD);
SPIREG(SPI0C) = spic;
SPIREG(SPI0U) = spiu;
SPIREG(SPI0U1)= spiu1;
SPIREG(SPI0U2)= spiu2;
if (writeWords>0) {
// copy the outgoing data to the SPI hardware
uint32_t *src=data;
volatile uint32_t *dst=&SPIREG(SPI0W0);
for (uint32_t i=0; i<writeWords; i++)
*dst++ = *src++;
}
// Start the transfer
SPIREG(SPI0CMD) = spicmdusr;
// wait for the command to complete (typically only 1-3 iterations)
uint32_t timeout = 1000;
while ((SPIREG(SPI0CMD) & spicmdusr) && timeout--);
if ((readWords>0) && (timeout>0)) {
// copy the response back to the buffer
uint32_t *dst=data;
volatile uint32_t *src=&SPIREG(SPI0W0);
for (uint32_t i=0; i<readWords; i++)
*dst++ = *src++;
}
// Restore saved registers
SPIREG(SPI0U) = oldSPI0U;
SPIREG(SPI0U2)= oldSPI0U2;
SPIREG(SPI0C) = oldSPI0C;
PRECACHE_END();
if (!spiIfNum) {
xt_wsr_ps(saved_ps);
}
return (timeout>0 ? SPI_RESULT_OK : SPI_RESULT_TIMEOUT);
}
/* SPI0Command: send a custom SPI command.
* This part calculates register values and passes them to _SPI0Command().
* Parameters:
* cmd The command byte (first 8 bits) to send to the SPI device
* *data The buffer containing the outgoing data for the SPI bus.
* The data is expected to be mosi_bits long, and the buffer
* is overwritten by the incoming bus data, which will be
* miso_bits long.
* mosi_bits
* Number of bits to be sent after the command byte.
* miso_bits
* Number of bits to read from the SPI bus after the outgoing
* data has been sent.
*
* Note: This code has only been tested with SPI bus 0, but should work
* equally well with other buses. The ESP8266 has bus 0 and 1,
* newer chips may have more one day.
*/
SpiOpResult SPI0Command(uint8_t cmd, uint32_t *data, uint32_t mosi_bits, uint32_t miso_bits) {
if (mosi_bits>(64*8))
return SPI_RESULT_ERR;
if (miso_bits>(64*8))
return SPI_RESULT_ERR;
// Calculate the number of data words (aka registers) that need to be copied
// to/from the SPI controller.
uint32_t mosi_words=mosi_bits/32;
uint32_t miso_words=miso_bits/32;
if (mosi_bits % 32 != 0)
mosi_words++;
if (miso_bits % 32 != 0)
miso_words++;
// Select user defined command mode in the controller
uint32_t spiu=SPIUCOMMAND; //SPI_USR_COMMAND
// Set the command byte to send
uint32_t spiu2 = ((7 & SPIMCOMMAND)<<SPILCOMMAND) | cmd;
uint32_t spiu1 = 0;
if (mosi_bits>0) {
// set the number of outgoing data bits to send
spiu1 |= ((mosi_bits-1) & SPIMMOSI) << SPILMOSI;
spiu |= SPIUMOSI; // SPI_USR_MOSI
}
if (miso_bits>0) {
// set the number of incoming bits to read
spiu1 |= ((miso_bits-1) & SPIMMISO) << SPILMISO;
spiu |= SPIUMISO; // SPI_USR_MISO
}
uint32_t spic = SPI0C;
// Select the most basic IO mode for maximum compatibility
// Some flash commands are only available in this mode.
spic &= ~(SPICQIO | SPICDIO | SPICQOUT | SPICDOUT | SPICAHB | SPICFASTRD);
spic |= (SPICRESANDRES | SPICSHARE | SPICWPR | SPIC2BSE);
SpiOpResult rc =_SPICommand(0,spic,spiu,spiu1,spiu2,data,mosi_words,miso_words);
if (rc==SPI_RESULT_OK) {
// clear any bits we did not read in the last word.
if (miso_bits % 32) {
data[miso_bits/32] &= ~(0xFFFFFFFF << (miso_bits % 32));
}
}
return rc;
}
} // namespace experimental