ESPHome  2023.3.1
spi.h
Go to the documentation of this file.
1 #pragma once
2 
4 #include "esphome/core/hal.h"
5 #include <vector>
6 
7 #ifdef USE_ARDUINO
8 #define USE_SPI_ARDUINO_BACKEND
9 #endif
10 
11 #ifdef USE_SPI_ARDUINO_BACKEND
12 #include <SPI.h>
13 #endif
14 
15 namespace esphome {
16 namespace spi {
17 
24 };
40 };
50 };
59 enum SPIDataRate : uint32_t {
61  DATA_RATE_75KHZ = 75000,
62  DATA_RATE_200KHZ = 200000,
63  DATA_RATE_1MHZ = 1000000,
64  DATA_RATE_2MHZ = 2000000,
65  DATA_RATE_4MHZ = 4000000,
66  DATA_RATE_8MHZ = 8000000,
67  DATA_RATE_10MHZ = 10000000,
68  DATA_RATE_20MHZ = 20000000,
69  DATA_RATE_40MHZ = 40000000,
70 };
71 
72 class SPIComponent : public Component {
73  public:
74  void set_clk(GPIOPin *clk) { clk_ = clk; }
75  void set_miso(GPIOPin *miso) { miso_ = miso; }
76  void set_mosi(GPIOPin *mosi) { mosi_ = mosi; }
77 
78  void setup() override;
79 
80  void dump_config() override;
81 
82  template<SPIBitOrder BIT_ORDER, SPIClockPolarity CLOCK_POLARITY, SPIClockPhase CLOCK_PHASE> uint8_t read_byte() {
83 #ifdef USE_SPI_ARDUINO_BACKEND
84  if (this->hw_spi_ != nullptr) {
85  return this->hw_spi_->transfer(0x00);
86  }
87 #endif // USE_SPI_ARDUINO_BACKEND
88  return this->transfer_<BIT_ORDER, CLOCK_POLARITY, CLOCK_PHASE, true, false>(0x00);
89  }
90 
91  template<SPIBitOrder BIT_ORDER, SPIClockPolarity CLOCK_POLARITY, SPIClockPhase CLOCK_PHASE>
92  void read_array(uint8_t *data, size_t length) {
93 #ifdef USE_SPI_ARDUINO_BACKEND
94  if (this->hw_spi_ != nullptr) {
95  this->hw_spi_->transfer(data, length);
96  return;
97  }
98 #endif // USE_SPI_ARDUINO_BACKEND
99  for (size_t i = 0; i < length; i++) {
100  data[i] = this->read_byte<BIT_ORDER, CLOCK_POLARITY, CLOCK_PHASE>();
101  }
102  }
103 
104  template<SPIBitOrder BIT_ORDER, SPIClockPolarity CLOCK_POLARITY, SPIClockPhase CLOCK_PHASE>
105  void write_byte(uint8_t data) {
106 #ifdef USE_SPI_ARDUINO_BACKEND
107  if (this->hw_spi_ != nullptr) {
108 #ifdef USE_RP2040
109  this->hw_spi_->transfer(data);
110 #else
111  this->hw_spi_->write(data);
112 #endif
113  return;
114  }
115 #endif // USE_SPI_ARDUINO_BACKEND
116  this->transfer_<BIT_ORDER, CLOCK_POLARITY, CLOCK_PHASE, false, true>(data);
117  }
118 
119  template<SPIBitOrder BIT_ORDER, SPIClockPolarity CLOCK_POLARITY, SPIClockPhase CLOCK_PHASE>
120  void write_byte16(const uint16_t data) {
121 #ifdef USE_SPI_ARDUINO_BACKEND
122  if (this->hw_spi_ != nullptr) {
123 #ifdef USE_RP2040
124  this->hw_spi_->transfer16(data);
125 #else
126  this->hw_spi_->write16(data);
127 #endif
128  return;
129  }
130 #endif // USE_SPI_ARDUINO_BACKEND
131 
132  this->write_byte<BIT_ORDER, CLOCK_POLARITY, CLOCK_PHASE>(data >> 8);
133  this->write_byte<BIT_ORDER, CLOCK_POLARITY, CLOCK_PHASE>(data);
134  }
135 
136  template<SPIBitOrder BIT_ORDER, SPIClockPolarity CLOCK_POLARITY, SPIClockPhase CLOCK_PHASE>
137  void write_array16(const uint16_t *data, size_t length) {
138 #ifdef USE_SPI_ARDUINO_BACKEND
139  if (this->hw_spi_ != nullptr) {
140  for (size_t i = 0; i < length; i++) {
141 #ifdef USE_RP2040
142  this->hw_spi_->transfer16(data[i]);
143 #else
144  this->hw_spi_->write16(data[i]);
145 #endif
146  }
147  return;
148  }
149 #endif // USE_SPI_ARDUINO_BACKEND
150  for (size_t i = 0; i < length; i++) {
151  this->write_byte16<BIT_ORDER, CLOCK_POLARITY, CLOCK_PHASE>(data[i]);
152  }
153  }
154 
155  template<SPIBitOrder BIT_ORDER, SPIClockPolarity CLOCK_POLARITY, SPIClockPhase CLOCK_PHASE>
156  void write_array(const uint8_t *data, size_t length) {
157 #ifdef USE_SPI_ARDUINO_BACKEND
158  if (this->hw_spi_ != nullptr) {
159  auto *data_c = const_cast<uint8_t *>(data);
160 #ifdef USE_RP2040
161  this->hw_spi_->transfer(data_c, length);
162 #else
163  this->hw_spi_->writeBytes(data_c, length);
164 #endif
165  return;
166  }
167 #endif // USE_SPI_ARDUINO_BACKEND
168  for (size_t i = 0; i < length; i++) {
169  this->write_byte<BIT_ORDER, CLOCK_POLARITY, CLOCK_PHASE>(data[i]);
170  }
171  }
172 
173  template<SPIBitOrder BIT_ORDER, SPIClockPolarity CLOCK_POLARITY, SPIClockPhase CLOCK_PHASE>
174  uint8_t transfer_byte(uint8_t data) {
175  if (this->miso_ != nullptr) {
176 #ifdef USE_SPI_ARDUINO_BACKEND
177  if (this->hw_spi_ != nullptr) {
178  return this->hw_spi_->transfer(data);
179  } else {
180 #endif // USE_SPI_ARDUINO_BACKEND
181  return this->transfer_<BIT_ORDER, CLOCK_POLARITY, CLOCK_PHASE, true, true>(data);
182 #ifdef USE_SPI_ARDUINO_BACKEND
183  }
184 #endif // USE_SPI_ARDUINO_BACKEND
185  }
186  this->write_byte<BIT_ORDER, CLOCK_POLARITY, CLOCK_PHASE>(data);
187  return 0;
188  }
189 
190  template<SPIBitOrder BIT_ORDER, SPIClockPolarity CLOCK_POLARITY, SPIClockPhase CLOCK_PHASE>
191  void transfer_array(uint8_t *data, size_t length) {
192 #ifdef USE_SPI_ARDUINO_BACKEND
193  if (this->hw_spi_ != nullptr) {
194  if (this->miso_ != nullptr) {
195  this->hw_spi_->transfer(data, length);
196  } else {
197 #ifdef USE_RP2040
198  this->hw_spi_->transfer(data, length);
199 #else
200  this->hw_spi_->writeBytes(data, length);
201 #endif
202  }
203  return;
204  }
205 #endif // USE_SPI_ARDUINO_BACKEND
206 
207  if (this->miso_ != nullptr) {
208  for (size_t i = 0; i < length; i++) {
209  data[i] = this->transfer_byte<BIT_ORDER, CLOCK_POLARITY, CLOCK_PHASE>(data[i]);
210  }
211  } else {
212  this->write_array<BIT_ORDER, CLOCK_POLARITY, CLOCK_PHASE>(data, length);
213  }
214  }
215 
216  template<SPIBitOrder BIT_ORDER, SPIClockPolarity CLOCK_POLARITY, SPIClockPhase CLOCK_PHASE, uint32_t DATA_RATE>
217  void enable(GPIOPin *cs) {
218 #ifdef USE_SPI_ARDUINO_BACKEND
219  if (this->hw_spi_ != nullptr) {
220  uint8_t data_mode = SPI_MODE0;
221  if (!CLOCK_POLARITY && CLOCK_PHASE) {
222  data_mode = SPI_MODE1;
223  } else if (CLOCK_POLARITY && !CLOCK_PHASE) {
224  data_mode = SPI_MODE2;
225  } else if (CLOCK_POLARITY && CLOCK_PHASE) {
226  data_mode = SPI_MODE3;
227  }
228 #ifdef USE_RP2040
229  SPISettings settings(DATA_RATE, static_cast<BitOrder>(BIT_ORDER), data_mode);
230 #else
231  SPISettings settings(DATA_RATE, BIT_ORDER, data_mode);
232 #endif
233  this->hw_spi_->beginTransaction(settings);
234  } else {
235 #endif // USE_SPI_ARDUINO_BACKEND
236  this->clk_->digital_write(CLOCK_POLARITY);
237  uint32_t cpu_freq_hz = arch_get_cpu_freq_hz();
238  this->wait_cycle_ = uint32_t(cpu_freq_hz) / DATA_RATE / 2ULL;
239 #ifdef USE_SPI_ARDUINO_BACKEND
240  }
241 #endif // USE_SPI_ARDUINO_BACKEND
242 
243  if (cs != nullptr) {
244  this->active_cs_ = cs;
245  this->active_cs_->digital_write(false);
246  }
247  }
248 
249  void disable();
250 
251  float get_setup_priority() const override;
252 
253  protected:
254  inline void cycle_clock_(bool value);
255 
256  template<SPIBitOrder BIT_ORDER, SPIClockPolarity CLOCK_POLARITY, SPIClockPhase CLOCK_PHASE, bool READ, bool WRITE>
257  uint8_t transfer_(uint8_t data);
258 
260  GPIOPin *miso_{nullptr};
261  GPIOPin *mosi_{nullptr};
262  GPIOPin *active_cs_{nullptr};
263 #ifdef USE_SPI_ARDUINO_BACKEND
264  SPIClass *hw_spi_{nullptr};
265 #endif // USE_SPI_ARDUINO_BACKEND
266  uint32_t wait_cycle_;
267 };
268 
269 template<SPIBitOrder BIT_ORDER, SPIClockPolarity CLOCK_POLARITY, SPIClockPhase CLOCK_PHASE, SPIDataRate DATA_RATE>
270 class SPIDevice {
271  public:
272  SPIDevice() = default;
273  SPIDevice(SPIComponent *parent, GPIOPin *cs) : parent_(parent), cs_(cs) {}
274 
275  void set_spi_parent(SPIComponent *parent) { parent_ = parent; }
276  void set_cs_pin(GPIOPin *cs) { cs_ = cs; }
277 
278  void spi_setup() {
279  if (this->cs_) {
280  this->cs_->setup();
281  this->cs_->digital_write(true);
282  }
283  }
284 
285  void enable() { this->parent_->template enable<BIT_ORDER, CLOCK_POLARITY, CLOCK_PHASE, DATA_RATE>(this->cs_); }
286 
287  void disable() { this->parent_->disable(); }
288 
289  uint8_t read_byte() { return this->parent_->template read_byte<BIT_ORDER, CLOCK_POLARITY, CLOCK_PHASE>(); }
290 
291  void read_array(uint8_t *data, size_t length) {
292  return this->parent_->template read_array<BIT_ORDER, CLOCK_POLARITY, CLOCK_PHASE>(data, length);
293  }
294 
295  template<size_t N> std::array<uint8_t, N> read_array() {
296  std::array<uint8_t, N> data;
297  this->read_array(data.data(), N);
298  return data;
299  }
300 
301  void write_byte(uint8_t data) {
302  return this->parent_->template write_byte<BIT_ORDER, CLOCK_POLARITY, CLOCK_PHASE>(data);
303  }
304 
305  void write_byte16(uint16_t data) {
306  return this->parent_->template write_byte16<BIT_ORDER, CLOCK_POLARITY, CLOCK_PHASE>(data);
307  }
308 
309  void write_array16(const uint16_t *data, size_t length) {
310  this->parent_->template write_array16<BIT_ORDER, CLOCK_POLARITY, CLOCK_PHASE>(data, length);
311  }
312 
313  void write_array(const uint8_t *data, size_t length) {
314  this->parent_->template write_array<BIT_ORDER, CLOCK_POLARITY, CLOCK_PHASE>(data, length);
315  }
316 
317  template<size_t N> void write_array(const std::array<uint8_t, N> &data) { this->write_array(data.data(), N); }
318 
319  void write_array(const std::vector<uint8_t> &data) { this->write_array(data.data(), data.size()); }
320 
321  uint8_t transfer_byte(uint8_t data) {
322  return this->parent_->template transfer_byte<BIT_ORDER, CLOCK_POLARITY, CLOCK_PHASE>(data);
323  }
324 
325  void transfer_array(uint8_t *data, size_t length) {
326  this->parent_->template transfer_array<BIT_ORDER, CLOCK_POLARITY, CLOCK_PHASE>(data, length);
327  }
328 
329  template<size_t N> void transfer_array(std::array<uint8_t, N> &data) { this->transfer_array(data.data(), N); }
330 
331  protected:
332  SPIComponent *parent_{nullptr};
333  GPIOPin *cs_{nullptr};
334 };
335 
336 } // namespace spi
337 } // namespace esphome
virtual void digital_write(bool value)=0
void write_array16(const uint16_t *data, size_t length)
Definition: spi.h:309
void transfer_array(uint8_t *data, size_t length)
Definition: spi.h:325
void cycle_clock_(bool value)
Definition: spi.cpp:111
void transfer_array(uint8_t *data, size_t length)
Definition: spi.h:191
void set_miso(GPIOPin *miso)
Definition: spi.h:75
uint32_t wait_cycle_
Definition: spi.h:266
SPIDataRate
The SPI clock signal data rate.
Definition: spi.h:59
std::array< uint8_t, N > read_array()
Definition: spi.h:295
The data is sampled on a leading clock edge. (CPHA=0)
Definition: spi.h:47
The clock signal idles on HIGH.
Definition: spi.h:39
The most significant bit is transmitted/received first.
Definition: spi.h:23
void set_cs_pin(GPIOPin *cs)
Definition: spi.h:276
uint8_t transfer_byte(uint8_t data)
Definition: spi.h:321
The clock signal idles on LOW.
Definition: spi.h:34
void write_array(const std::vector< uint8_t > &data)
Definition: spi.h:319
void write_byte(uint8_t data)
Definition: spi.h:301
void read_array(uint8_t *data, size_t length)
Definition: spi.h:92
SPIClockPolarity
The SPI clock signal polarity,.
Definition: spi.h:29
uint8_t read_byte()
Definition: spi.h:289
void dump_config() override
Definition: spi.cpp:100
void write_array(const std::array< uint8_t, N > &data)
Definition: spi.h:317
void read_array(uint8_t *data, size_t length)
Definition: spi.h:291
uint8_t read_byte()
Definition: spi.h:82
The data is sampled on a trailing clock edge. (CPHA=1)
Definition: spi.h:49
void set_mosi(GPIOPin *mosi)
Definition: spi.h:76
uint32_t arch_get_cpu_freq_hz()
Definition: core.cpp:66
SPIClass * hw_spi_
Definition: spi.h:264
SPIDevice(SPIComponent *parent, GPIOPin *cs)
Definition: spi.h:273
void write_byte(uint8_t data)
Definition: spi.h:105
GPIOPin * active_cs_
Definition: spi.h:262
void transfer_array(std::array< uint8_t, N > &data)
Definition: spi.h:329
uint8_t transfer_(uint8_t data)
void setup() override
Definition: spi.cpp:22
SPIBitOrder
The bit-order for SPI devices. This defines how the data read from and written to the device is inter...
Definition: spi.h:19
void write_array(const uint8_t *data, size_t length)
Definition: spi.h:313
Definition: a4988.cpp:4
The least significant bit is transmitted/received first.
Definition: spi.h:21
void set_spi_parent(SPIComponent *parent)
Definition: spi.h:275
SPIClockPhase
The SPI clock signal phase.
Definition: spi.h:45
void write_byte16(uint16_t data)
Definition: spi.h:305
void write_byte16(const uint16_t data)
Definition: spi.h:120
void write_array16(const uint16_t *data, size_t length)
Definition: spi.h:137
void set_clk(GPIOPin *clk)
Definition: spi.h:74
uint8_t transfer_byte(uint8_t data)
Definition: spi.h:174
void enable(GPIOPin *cs)
Definition: spi.h:217
void write_array(const uint8_t *data, size_t length)
Definition: spi.h:156
float get_setup_priority() const override
Definition: spi.cpp:109