ESPHome  2023.5.5
spi.cpp
Go to the documentation of this file.
1 #include "spi.h"
2 #include "esphome/core/log.h"
3 #include "esphome/core/helpers.h"
5 
6 namespace esphome {
7 namespace spi {
8 
9 static const char *const TAG = "spi";
10 
11 void IRAM_ATTR HOT SPIComponent::disable() {
12 #ifdef USE_SPI_ARDUINO_BACKEND
13  if (this->hw_spi_ != nullptr) {
14  this->hw_spi_->endTransaction();
15  }
16 #endif // USE_SPI_ARDUINO_BACKEND
17  if (this->active_cs_) {
18  this->active_cs_->digital_write(true);
19  this->active_cs_ = nullptr;
20  }
21 }
23  ESP_LOGCONFIG(TAG, "Setting up SPI bus...");
24  this->clk_->setup();
25  this->clk_->digital_write(true);
26 
27 #ifdef USE_SPI_ARDUINO_BACKEND
28  bool use_hw_spi = !this->force_sw_;
29  const bool has_miso = this->miso_ != nullptr;
30  const bool has_mosi = this->mosi_ != nullptr;
31  int8_t clk_pin = -1, miso_pin = -1, mosi_pin = -1;
32 
33  if (!this->clk_->is_internal())
34  use_hw_spi = false;
35  if (has_miso && !miso_->is_internal())
36  use_hw_spi = false;
37  if (has_mosi && !mosi_->is_internal())
38  use_hw_spi = false;
39  if (use_hw_spi) {
40  auto *clk_internal = (InternalGPIOPin *) clk_;
41  auto *miso_internal = (InternalGPIOPin *) miso_;
42  auto *mosi_internal = (InternalGPIOPin *) mosi_;
43 
44  if (clk_internal->is_inverted())
45  use_hw_spi = false;
46  if (has_miso && miso_internal->is_inverted())
47  use_hw_spi = false;
48  if (has_mosi && mosi_internal->is_inverted())
49  use_hw_spi = false;
50 
51  if (use_hw_spi) {
52  clk_pin = clk_internal->get_pin();
53  miso_pin = has_miso ? miso_internal->get_pin() : -1;
54  mosi_pin = has_mosi ? mosi_internal->get_pin() : -1;
55  }
56  }
57 #ifdef USE_ESP8266
58  if (!(clk_pin == 6 && miso_pin == 7 && mosi_pin == 8) &&
59  !(clk_pin == 14 && (!has_miso || miso_pin == 12) && (!has_mosi || mosi_pin == 13)))
60  use_hw_spi = false;
61 
62  if (use_hw_spi) {
63  this->hw_spi_ = &SPI;
64  this->hw_spi_->pins(clk_pin, miso_pin, mosi_pin, 0);
65  this->hw_spi_->begin();
66  return;
67  }
68 #endif // USE_ESP8266
69 #ifdef USE_ESP32
70  static uint8_t spi_bus_num = 0;
71  if (spi_bus_num >= 2) {
72  use_hw_spi = false;
73  }
74 
75  if (use_hw_spi) {
76  if (spi_bus_num == 0) {
77  this->hw_spi_ = &SPI;
78  } else {
79 #if defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3)
80  this->hw_spi_ = new SPIClass(FSPI); // NOLINT(cppcoreguidelines-owning-memory)
81 #else
82  this->hw_spi_ = new SPIClass(HSPI); // NOLINT(cppcoreguidelines-owning-memory)
83 #endif // USE_ESP32_VARIANT
84  }
85  spi_bus_num++;
86  this->hw_spi_->begin(clk_pin, miso_pin, mosi_pin);
87  return;
88  }
89 #endif // USE_ESP32
90 #endif // USE_SPI_ARDUINO_BACKEND
91 
92  if (this->miso_ != nullptr) {
93  this->miso_->setup();
94  }
95  if (this->mosi_ != nullptr) {
96  this->mosi_->setup();
97  this->mosi_->digital_write(false);
98  }
99 }
101  ESP_LOGCONFIG(TAG, "SPI bus:");
102  LOG_PIN(" CLK Pin: ", this->clk_);
103  LOG_PIN(" MISO Pin: ", this->miso_);
104  LOG_PIN(" MOSI Pin: ", this->mosi_);
105 #ifdef USE_SPI_ARDUINO_BACKEND
106  ESP_LOGCONFIG(TAG, " Using HW SPI: %s", YESNO(this->hw_spi_ != nullptr));
107 #endif // USE_SPI_ARDUINO_BACKEND
108 }
110 
111 void SPIComponent::cycle_clock_(bool value) {
112  uint32_t start = arch_get_cpu_cycle_count();
113  while (start - arch_get_cpu_cycle_count() < this->wait_cycle_)
114  ;
115  this->clk_->digital_write(value);
116  start += this->wait_cycle_;
117  while (start - arch_get_cpu_cycle_count() < this->wait_cycle_)
118  ;
119 }
120 
121 // NOLINTNEXTLINE
122 #ifndef CLANG_TIDY
123 #pragma GCC optimize("unroll-loops")
124 // NOLINTNEXTLINE
125 #pragma GCC optimize("O2")
126 #endif // CLANG_TIDY
127 
128 template<SPIBitOrder BIT_ORDER, SPIClockPolarity CLOCK_POLARITY, SPIClockPhase CLOCK_PHASE, bool READ, bool WRITE>
129 uint8_t HOT SPIComponent::transfer_(uint8_t data) {
130  // Clock starts out at idle level
131  this->clk_->digital_write(CLOCK_POLARITY);
132  uint8_t out_data = 0;
133 
134  for (uint8_t i = 0; i < 8; i++) {
135  uint8_t shift;
136  if (BIT_ORDER == BIT_ORDER_MSB_FIRST) {
137  shift = 7 - i;
138  } else {
139  shift = i;
140  }
141 
142  if (CLOCK_PHASE == CLOCK_PHASE_LEADING) {
143  // sampling on leading edge
144  if (WRITE) {
145  this->mosi_->digital_write(data & (1 << shift));
146  }
147 
148  // SAMPLE!
149  this->cycle_clock_(!CLOCK_POLARITY);
150 
151  if (READ) {
152  out_data |= uint8_t(this->miso_->digital_read()) << shift;
153  }
154 
155  this->cycle_clock_(CLOCK_POLARITY);
156  } else {
157  // sampling on trailing edge
158  this->cycle_clock_(!CLOCK_POLARITY);
159 
160  if (WRITE) {
161  this->mosi_->digital_write(data & (1 << shift));
162  }
163 
164  // SAMPLE!
165  this->cycle_clock_(CLOCK_POLARITY);
166 
167  if (READ) {
168  out_data |= uint8_t(this->miso_->digital_read()) << shift;
169  }
170  }
171  }
172 
173  App.feed_wdt();
174 
175  return out_data;
176 }
177 
178 // Generate with (py3):
179 //
180 // from itertools import product
181 // bit_orders = ['BIT_ORDER_LSB_FIRST', 'BIT_ORDER_MSB_FIRST']
182 // clock_pols = ['CLOCK_POLARITY_LOW', 'CLOCK_POLARITY_HIGH']
183 // clock_phases = ['CLOCK_PHASE_LEADING', 'CLOCK_PHASE_TRAILING']
184 // reads = [False, True]
185 // writes = [False, True]
186 // cpp_bool = {False: 'false', True: 'true'}
187 // for b, cpol, cph, r, w in product(bit_orders, clock_pols, clock_phases, reads, writes):
188 // if not r and not w:
189 // continue
190 // print(f"template uint8_t SPIComponent::transfer_<{b}, {cpol}, {cph}, {cpp_bool[r]}, {cpp_bool[w]}>(uint8_t
191 // data);")
192 
193 template uint8_t SPIComponent::transfer_<BIT_ORDER_LSB_FIRST, CLOCK_POLARITY_LOW, CLOCK_PHASE_LEADING, false, true>(
194  uint8_t data);
195 template uint8_t SPIComponent::transfer_<BIT_ORDER_LSB_FIRST, CLOCK_POLARITY_LOW, CLOCK_PHASE_LEADING, true, false>(
196  uint8_t data);
197 template uint8_t SPIComponent::transfer_<BIT_ORDER_LSB_FIRST, CLOCK_POLARITY_LOW, CLOCK_PHASE_LEADING, true, true>(
198  uint8_t data);
199 template uint8_t SPIComponent::transfer_<BIT_ORDER_LSB_FIRST, CLOCK_POLARITY_LOW, CLOCK_PHASE_TRAILING, false, true>(
200  uint8_t data);
201 template uint8_t SPIComponent::transfer_<BIT_ORDER_LSB_FIRST, CLOCK_POLARITY_LOW, CLOCK_PHASE_TRAILING, true, false>(
202  uint8_t data);
203 template uint8_t SPIComponent::transfer_<BIT_ORDER_LSB_FIRST, CLOCK_POLARITY_LOW, CLOCK_PHASE_TRAILING, true, true>(
204  uint8_t data);
205 template uint8_t SPIComponent::transfer_<BIT_ORDER_LSB_FIRST, CLOCK_POLARITY_HIGH, CLOCK_PHASE_LEADING, false, true>(
206  uint8_t data);
207 template uint8_t SPIComponent::transfer_<BIT_ORDER_LSB_FIRST, CLOCK_POLARITY_HIGH, CLOCK_PHASE_LEADING, true, false>(
208  uint8_t data);
209 template uint8_t SPIComponent::transfer_<BIT_ORDER_LSB_FIRST, CLOCK_POLARITY_HIGH, CLOCK_PHASE_LEADING, true, true>(
210  uint8_t data);
211 template uint8_t SPIComponent::transfer_<BIT_ORDER_LSB_FIRST, CLOCK_POLARITY_HIGH, CLOCK_PHASE_TRAILING, false, true>(
212  uint8_t data);
213 template uint8_t SPIComponent::transfer_<BIT_ORDER_LSB_FIRST, CLOCK_POLARITY_HIGH, CLOCK_PHASE_TRAILING, true, false>(
214  uint8_t data);
215 template uint8_t SPIComponent::transfer_<BIT_ORDER_LSB_FIRST, CLOCK_POLARITY_HIGH, CLOCK_PHASE_TRAILING, true, true>(
216  uint8_t data);
217 template uint8_t SPIComponent::transfer_<BIT_ORDER_MSB_FIRST, CLOCK_POLARITY_LOW, CLOCK_PHASE_LEADING, false, true>(
218  uint8_t data);
219 template uint8_t SPIComponent::transfer_<BIT_ORDER_MSB_FIRST, CLOCK_POLARITY_LOW, CLOCK_PHASE_LEADING, true, false>(
220  uint8_t data);
221 template uint8_t SPIComponent::transfer_<BIT_ORDER_MSB_FIRST, CLOCK_POLARITY_LOW, CLOCK_PHASE_LEADING, true, true>(
222  uint8_t data);
223 template uint8_t SPIComponent::transfer_<BIT_ORDER_MSB_FIRST, CLOCK_POLARITY_LOW, CLOCK_PHASE_TRAILING, false, true>(
224  uint8_t data);
225 template uint8_t SPIComponent::transfer_<BIT_ORDER_MSB_FIRST, CLOCK_POLARITY_LOW, CLOCK_PHASE_TRAILING, true, false>(
226  uint8_t data);
227 template uint8_t SPIComponent::transfer_<BIT_ORDER_MSB_FIRST, CLOCK_POLARITY_LOW, CLOCK_PHASE_TRAILING, true, true>(
228  uint8_t data);
229 template uint8_t SPIComponent::transfer_<BIT_ORDER_MSB_FIRST, CLOCK_POLARITY_HIGH, CLOCK_PHASE_LEADING, false, true>(
230  uint8_t data);
231 template uint8_t SPIComponent::transfer_<BIT_ORDER_MSB_FIRST, CLOCK_POLARITY_HIGH, CLOCK_PHASE_LEADING, true, false>(
232  uint8_t data);
233 template uint8_t SPIComponent::transfer_<BIT_ORDER_MSB_FIRST, CLOCK_POLARITY_HIGH, CLOCK_PHASE_LEADING, true, true>(
234  uint8_t data);
235 template uint8_t SPIComponent::transfer_<BIT_ORDER_MSB_FIRST, CLOCK_POLARITY_HIGH, CLOCK_PHASE_TRAILING, false, true>(
236  uint8_t data);
237 template uint8_t SPIComponent::transfer_<BIT_ORDER_MSB_FIRST, CLOCK_POLARITY_HIGH, CLOCK_PHASE_TRAILING, true, false>(
238  uint8_t data);
239 template uint8_t SPIComponent::transfer_<BIT_ORDER_MSB_FIRST, CLOCK_POLARITY_HIGH, CLOCK_PHASE_TRAILING, true, true>(
240  uint8_t data);
241 
242 } // namespace spi
243 } // namespace esphome
virtual void digital_write(bool value)=0
void cycle_clock_(bool value)
Definition: spi.cpp:111
uint32_t wait_cycle_
Definition: spi.h:268
The data is sampled on a leading clock edge. (CPHA=0)
Definition: spi.h:47
The most significant bit is transmitted/received first.
Definition: spi.h:23
virtual void setup()=0
void dump_config() override
Definition: spi.cpp:100
const float BUS
For communication buses like i2c/spi.
Definition: component.cpp:15
virtual bool is_internal()
Definition: gpio.h:62
Application App
Global storage of Application pointer - only one Application can exist.
SPIClass * hw_spi_
Definition: spi.h:266
virtual bool digital_read()=0
GPIOPin * active_cs_
Definition: spi.h:263
uint8_t transfer_(uint8_t data)
void setup() override
Definition: spi.cpp:22
Definition: a4988.cpp:4
uint32_t arch_get_cpu_cycle_count()
Definition: core.cpp:58
float get_setup_priority() const override
Definition: spi.cpp:109