#include "spi.h" #include "esphome/core/log.h" #include "esphome/core/application.h" namespace esphome::spi { const char *const TAG = "spi"; SPIDelegate *const SPIDelegate::NULL_DELEGATE = // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) new SPIDelegateDummy(); // https://bugs.llvm.org/show_bug.cgi?id=48040 bool SPIDelegate::is_ready() { return true; } GPIOPin *const NullPin::NULL_PIN = new NullPin(); // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) SPIDelegate *SPIComponent::register_device(SPIClient *device, SPIMode mode, SPIBitOrder bit_order, uint32_t data_rate, GPIOPin *cs_pin, bool release_device, bool write_only) { if (this->devices_.contains(device)) { ESP_LOGE(TAG, "Device already registered"); return this->devices_[device]; } SPIDelegate *delegate = this->spi_bus_->get_delegate(data_rate, bit_order, mode, cs_pin, release_device, write_only); // NOLINT this->devices_[device] = delegate; return delegate; } void SPIComponent::unregister_device(SPIClient *device) { if (!this->devices_.contains(device)) { esph_log_e(TAG, "Device not registered"); return; } delete this->devices_[device]; // NOLINT this->devices_.erase(device); } void SPIComponent::setup() { if (this->sdo_pin_ == nullptr) this->sdo_pin_ = NullPin::NULL_PIN; if (this->sdi_pin_ == nullptr) this->sdi_pin_ = NullPin::NULL_PIN; if (this->clk_pin_ == nullptr) { ESP_LOGE(TAG, "No clock pin"); this->mark_failed(); return; } if (this->using_hw_) { this->spi_bus_ = SPIComponent::get_bus(this->interface_, this->clk_pin_, this->sdo_pin_, this->sdi_pin_, this->data_pins_); if (this->spi_bus_ == nullptr) { ESP_LOGE(TAG, "Unable to allocate SPI interface"); this->mark_failed(); } } else { this->spi_bus_ = new SPIBus(this->clk_pin_, this->sdo_pin_, this->sdi_pin_); // NOLINT this->clk_pin_->setup(); this->clk_pin_->digital_write(true); this->sdo_pin_->setup(); this->sdi_pin_->setup(); } } void SPIComponent::dump_config() { ESP_LOGCONFIG(TAG, "SPI bus:"); LOG_PIN(" CLK Pin: ", this->clk_pin_); LOG_PIN(" SDI Pin: ", this->sdi_pin_); LOG_PIN(" SDO Pin: ", this->sdo_pin_); for (size_t i = 0; i != this->data_pins_.size(); i++) { ESP_LOGCONFIG(TAG, " Data pin %zu: GPIO%d", i, this->data_pins_[i]); } if (this->spi_bus_->is_hw()) { ESP_LOGCONFIG(TAG, " Using HW SPI: %s", this->interface_name_); } else { ESP_LOGCONFIG(TAG, " Using software SPI"); } } void SPIDelegateDummy::begin_transaction() { ESP_LOGE(TAG, "SPIDevice not initialised - did you call spi_setup()?"); } uint8_t SPIDelegateBitBash::transfer(uint8_t data) { return this->transfer_(data, 8); } void SPIDelegateBitBash::write(uint16_t data, size_t num_bits) { this->transfer_(data, num_bits); } uint16_t SPIDelegateBitBash::transfer_(uint16_t data, size_t num_bits) { // Clock starts out at idle level this->clk_pin_->digital_write(clock_polarity_); uint16_t out_data = 0; for (uint8_t i = 0; i != num_bits; i++) { uint8_t shift; if (bit_order_ == BIT_ORDER_MSB_FIRST) { shift = num_bits - 1 - i; } else { shift = i; } if (clock_phase_ == CLOCK_PHASE_LEADING) { // sampling on leading edge this->sdo_pin_->digital_write(data & (1 << shift)); this->cycle_clock_(); out_data |= uint16_t(this->sdi_pin_->digital_read()) << shift; this->clk_pin_->digital_write(!this->clock_polarity_); this->cycle_clock_(); this->clk_pin_->digital_write(this->clock_polarity_); } else { // sampling on trailing edge this->cycle_clock_(); this->clk_pin_->digital_write(!this->clock_polarity_); this->sdo_pin_->digital_write(data & (1 << shift)); this->cycle_clock_(); out_data |= uint16_t(this->sdi_pin_->digital_read()) << shift; this->clk_pin_->digital_write(this->clock_polarity_); } } App.feed_wdt(); return out_data; } #if !defined(USE_ESP32) && !defined(USE_ARDUINO) // Stub for unsupported platforms (host, Zephyr, etc.) - hardware SPI is unavailable SPIBus *SPIComponent::get_bus(SPIInterface interface, GPIOPin *clk, GPIOPin *sdo, GPIOPin *sdi, const std::vector &data_pins) { return nullptr; } #endif } // namespace esphome::spi