mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-30 22:53:59 +00:00 
			
		
		
		
	Add HW SPI support (#623)
* Add HW SPI support * Update spi.cpp * Lint * ESP32 Compile Fix
This commit is contained in:
		| @@ -8,20 +8,10 @@ namespace spi { | |||||||
|  |  | ||||||
| static const char *TAG = "spi"; | static const char *TAG = "spi"; | ||||||
|  |  | ||||||
| template<SPIClockPolarity CLOCK_POLARITY> void SPIComponent::enable(GPIOPin *cs, uint32_t wait_cycle) { |  | ||||||
|   this->debug_enable(cs->get_pin()); |  | ||||||
|   this->wait_cycle_ = wait_cycle; |  | ||||||
|  |  | ||||||
|   this->clk_->digital_write(CLOCK_POLARITY); |  | ||||||
|  |  | ||||||
|   this->active_cs_ = cs; |  | ||||||
|   this->active_cs_->digital_write(false); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| template void SPIComponent::enable<CLOCK_POLARITY_LOW>(GPIOPin *cs, uint32_t wait_cycle); |  | ||||||
| template void SPIComponent::enable<CLOCK_POLARITY_HIGH>(GPIOPin *cs, uint32_t wait_cycle); |  | ||||||
|  |  | ||||||
| void ICACHE_RAM_ATTR HOT SPIComponent::disable() { | void ICACHE_RAM_ATTR HOT SPIComponent::disable() { | ||||||
|  |   if (this->hw_spi_ != nullptr) { | ||||||
|  |     this->hw_spi_->endTransaction(); | ||||||
|  |   } | ||||||
|   ESP_LOGVV(TAG, "Disabling SPI Chip on pin %u...", this->active_cs_->get_pin()); |   ESP_LOGVV(TAG, "Disabling SPI Chip on pin %u...", this->active_cs_->get_pin()); | ||||||
|   this->active_cs_->digital_write(true); |   this->active_cs_->digital_write(true); | ||||||
|   this->active_cs_ = nullptr; |   this->active_cs_ = nullptr; | ||||||
| @@ -30,6 +20,53 @@ void SPIComponent::setup() { | |||||||
|   ESP_LOGCONFIG(TAG, "Setting up SPI bus..."); |   ESP_LOGCONFIG(TAG, "Setting up SPI bus..."); | ||||||
|   this->clk_->setup(); |   this->clk_->setup(); | ||||||
|   this->clk_->digital_write(true); |   this->clk_->digital_write(true); | ||||||
|  |  | ||||||
|  |   bool use_hw_spi = true; | ||||||
|  |   if (this->clk_->is_inverted()) | ||||||
|  |     use_hw_spi = false; | ||||||
|  |   const bool has_miso = this->miso_ != nullptr; | ||||||
|  |   const bool has_mosi = this->mosi_ != nullptr; | ||||||
|  |   if (has_miso && this->miso_->is_inverted()) | ||||||
|  |     use_hw_spi = false; | ||||||
|  |   if (has_mosi && this->mosi_->is_inverted()) | ||||||
|  |     use_hw_spi = false; | ||||||
|  |   int8_t clk_pin = this->clk_->get_pin(); | ||||||
|  |   int8_t miso_pin = has_miso ? this->miso_->get_pin() : -1; | ||||||
|  |   int8_t mosi_pin = has_mosi ? this->mosi_->get_pin() : -1; | ||||||
|  | #ifdef ARDUINO_ARCH_ESP8266 | ||||||
|  |   if (clk_pin == 6 && miso_pin == 7 && mosi_pin == 8) { | ||||||
|  |     // pass | ||||||
|  |   } else if (clk_pin == 14 && miso_pin == 12 && mosi_pin == 13) { | ||||||
|  |     // pass | ||||||
|  |   } else { | ||||||
|  |     use_hw_spi = false; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   if (use_hw_spi) { | ||||||
|  |     this->hw_spi_ = &SPI; | ||||||
|  |     this->hw_spi_->pins(clk_pin, miso_pin, mosi_pin, 0); | ||||||
|  |     this->hw_spi_->begin(); | ||||||
|  |     return; | ||||||
|  |   } | ||||||
|  | #endif | ||||||
|  | #ifdef ARDUINO_ARCH_ESP32 | ||||||
|  |   static uint8_t spi_bus_num = 0; | ||||||
|  |   if (spi_bus_num >= 2) { | ||||||
|  |     use_hw_spi = false; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   if (use_hw_spi) { | ||||||
|  |     if (spi_bus_num == 0) { | ||||||
|  |       this->hw_spi_ = &SPI; | ||||||
|  |     } else { | ||||||
|  |       this->hw_spi_ = new SPIClass(VSPI); | ||||||
|  |     } | ||||||
|  |     spi_bus_num++; | ||||||
|  |     this->hw_spi_->begin(clk_pin, miso_pin, mosi_pin); | ||||||
|  |     return; | ||||||
|  |   } | ||||||
|  | #endif | ||||||
|  |  | ||||||
|   if (this->miso_ != nullptr) { |   if (this->miso_ != nullptr) { | ||||||
|     this->miso_->setup(); |     this->miso_->setup(); | ||||||
|   } |   } | ||||||
| @@ -43,6 +80,7 @@ void SPIComponent::dump_config() { | |||||||
|   LOG_PIN("  CLK Pin: ", this->clk_); |   LOG_PIN("  CLK Pin: ", this->clk_); | ||||||
|   LOG_PIN("  MISO Pin: ", this->miso_); |   LOG_PIN("  MISO Pin: ", this->miso_); | ||||||
|   LOG_PIN("  MOSI Pin: ", this->mosi_); |   LOG_PIN("  MOSI Pin: ", this->mosi_); | ||||||
|  |   ESP_LOGCONFIG(TAG, "  Using HW SPI: %s", YESNO(this->hw_spi_ != nullptr)); | ||||||
| } | } | ||||||
| float SPIComponent::get_setup_priority() const { return setup_priority::BUS; } | float SPIComponent::get_setup_priority() const { return setup_priority::BUS; } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -2,6 +2,7 @@ | |||||||
|  |  | ||||||
| #include "esphome/core/component.h" | #include "esphome/core/component.h" | ||||||
| #include "esphome/core/esphal.h" | #include "esphome/core/esphal.h" | ||||||
|  | #include <SPI.h> | ||||||
|  |  | ||||||
| namespace esphome { | namespace esphome { | ||||||
| namespace spi { | namespace spi { | ||||||
| @@ -67,11 +68,18 @@ class SPIComponent : public Component { | |||||||
|   void dump_config() override; |   void dump_config() override; | ||||||
|  |  | ||||||
|   template<SPIBitOrder BIT_ORDER, SPIClockPolarity CLOCK_POLARITY, SPIClockPhase CLOCK_PHASE> uint8_t read_byte() { |   template<SPIBitOrder BIT_ORDER, SPIClockPolarity CLOCK_POLARITY, SPIClockPhase CLOCK_PHASE> uint8_t read_byte() { | ||||||
|  |     if (this->hw_spi_ != nullptr) { | ||||||
|  |       return this->hw_spi_->transfer(0x00); | ||||||
|  |     } | ||||||
|     return this->transfer_<BIT_ORDER, CLOCK_POLARITY, CLOCK_PHASE, true, false>(0x00); |     return this->transfer_<BIT_ORDER, CLOCK_POLARITY, CLOCK_PHASE, true, false>(0x00); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   template<SPIBitOrder BIT_ORDER, SPIClockPolarity CLOCK_POLARITY, SPIClockPhase CLOCK_PHASE> |   template<SPIBitOrder BIT_ORDER, SPIClockPolarity CLOCK_POLARITY, SPIClockPhase CLOCK_PHASE> | ||||||
|   void read_array(uint8_t *data, size_t length) { |   void read_array(uint8_t *data, size_t length) { | ||||||
|  |     if (this->hw_spi_ != nullptr) { | ||||||
|  |       this->hw_spi_->transfer(data, length); | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|     for (size_t i = 0; i < length; i++) { |     for (size_t i = 0; i < length; i++) { | ||||||
|       data[i] = this->read_byte<BIT_ORDER, CLOCK_POLARITY, CLOCK_PHASE>(); |       data[i] = this->read_byte<BIT_ORDER, CLOCK_POLARITY, CLOCK_PHASE>(); | ||||||
|     } |     } | ||||||
| @@ -79,11 +87,20 @@ class SPIComponent : public Component { | |||||||
|  |  | ||||||
|   template<SPIBitOrder BIT_ORDER, SPIClockPolarity CLOCK_POLARITY, SPIClockPhase CLOCK_PHASE> |   template<SPIBitOrder BIT_ORDER, SPIClockPolarity CLOCK_POLARITY, SPIClockPhase CLOCK_PHASE> | ||||||
|   void write_byte(uint8_t data) { |   void write_byte(uint8_t data) { | ||||||
|  |     if (this->hw_spi_ != nullptr) { | ||||||
|  |       this->hw_spi_->write(data); | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|     this->transfer_<BIT_ORDER, CLOCK_POLARITY, CLOCK_PHASE, false, true>(data); |     this->transfer_<BIT_ORDER, CLOCK_POLARITY, CLOCK_PHASE, false, true>(data); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   template<SPIBitOrder BIT_ORDER, SPIClockPolarity CLOCK_POLARITY, SPIClockPhase CLOCK_PHASE> |   template<SPIBitOrder BIT_ORDER, SPIClockPolarity CLOCK_POLARITY, SPIClockPhase CLOCK_PHASE> | ||||||
|   void write_array(const uint8_t *data, size_t length) { |   void write_array(const uint8_t *data, size_t length) { | ||||||
|  |     if (this->hw_spi_ != nullptr) { | ||||||
|  |       auto *data_c = const_cast<uint8_t *>(data); | ||||||
|  |       this->hw_spi_->writeBytes(data_c, length); | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|     for (size_t i = 0; i < length; i++) { |     for (size_t i = 0; i < length; i++) { | ||||||
|       this->write_byte<BIT_ORDER, CLOCK_POLARITY, CLOCK_PHASE>(data[i]); |       this->write_byte<BIT_ORDER, CLOCK_POLARITY, CLOCK_PHASE>(data[i]); | ||||||
|     } |     } | ||||||
| @@ -91,17 +108,39 @@ class SPIComponent : public Component { | |||||||
|  |  | ||||||
|   template<SPIBitOrder BIT_ORDER, SPIClockPolarity CLOCK_POLARITY, SPIClockPhase CLOCK_PHASE> |   template<SPIBitOrder BIT_ORDER, SPIClockPolarity CLOCK_POLARITY, SPIClockPhase CLOCK_PHASE> | ||||||
|   uint8_t transfer_byte(uint8_t data) { |   uint8_t transfer_byte(uint8_t data) { | ||||||
|  |     if (this->hw_spi_ != nullptr) { | ||||||
|  |       return this->hw_spi_->transfer(data); | ||||||
|  |     } | ||||||
|     return this->transfer_<BIT_ORDER, CLOCK_POLARITY, CLOCK_PHASE, true, true>(data); |     return this->transfer_<BIT_ORDER, CLOCK_POLARITY, CLOCK_PHASE, true, true>(data); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   template<SPIBitOrder BIT_ORDER, SPIClockPolarity CLOCK_POLARITY, SPIClockPhase CLOCK_PHASE> |   template<SPIBitOrder BIT_ORDER, SPIClockPolarity CLOCK_POLARITY, SPIClockPhase CLOCK_PHASE> | ||||||
|   void transfer_array(uint8_t *data, size_t length) { |   void transfer_array(uint8_t *data, size_t length) { | ||||||
|  |     if (this->hw_spi_ != nullptr) { | ||||||
|  |       this->hw_spi_->transfer(data, length); | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|     for (size_t i = 0; i < length; i++) { |     for (size_t i = 0; i < length; i++) { | ||||||
|       data[i] = this->transfer_byte<BIT_ORDER, CLOCK_POLARITY, CLOCK_PHASE>(data[i]); |       data[i] = this->transfer_byte<BIT_ORDER, CLOCK_POLARITY, CLOCK_PHASE>(data[i]); | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   template<SPIClockPolarity CLOCK_POLARITY> void enable(GPIOPin *cs, uint32_t wait_cycle); |   template<SPIBitOrder BIT_ORDER, SPIClockPolarity CLOCK_POLARITY, SPIClockPhase CLOCK_PHASE, uint32_t DATA_RATE> | ||||||
|  |   void enable(GPIOPin *cs) { | ||||||
|  |     SPIComponent::debug_enable(cs->get_pin()); | ||||||
|  |  | ||||||
|  |     if (this->hw_spi_ != nullptr) { | ||||||
|  |       uint8_t data_mode = (uint8_t(CLOCK_POLARITY) << 1) | uint8_t(CLOCK_PHASE); | ||||||
|  |       SPISettings settings(DATA_RATE, BIT_ORDER, data_mode); | ||||||
|  |       this->hw_spi_->beginTransaction(settings); | ||||||
|  |     } else { | ||||||
|  |       this->clk_->digital_write(CLOCK_POLARITY); | ||||||
|  |       this->wait_cycle_ = uint32_t(F_CPU) / DATA_RATE / 2ULL; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     this->active_cs_ = cs; | ||||||
|  |     this->active_cs_->digital_write(false); | ||||||
|  |   } | ||||||
|  |  | ||||||
|   void disable(); |   void disable(); | ||||||
|  |  | ||||||
| @@ -121,6 +160,7 @@ class SPIComponent : public Component { | |||||||
|   GPIOPin *miso_{nullptr}; |   GPIOPin *miso_{nullptr}; | ||||||
|   GPIOPin *mosi_{nullptr}; |   GPIOPin *mosi_{nullptr}; | ||||||
|   GPIOPin *active_cs_{nullptr}; |   GPIOPin *active_cs_{nullptr}; | ||||||
|  |   SPIClass *hw_spi_{nullptr}; | ||||||
|   uint32_t wait_cycle_; |   uint32_t wait_cycle_; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| @@ -138,7 +178,7 @@ class SPIDevice { | |||||||
|     this->cs_->digital_write(true); |     this->cs_->digital_write(true); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   void enable() { this->parent_->template enable<CLOCK_POLARITY>(this->cs_, uint32_t(F_CPU) / DATA_RATE / 2ULL); } |   void enable() { this->parent_->template enable<BIT_ORDER, CLOCK_POLARITY, CLOCK_PHASE, DATA_RATE>(this->cs_); } | ||||||
|  |  | ||||||
|   void disable() { this->parent_->disable(); } |   void disable() { this->parent_->disable(); } | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user