Skip to content

Commit

Permalink
Implement variable length single word SPI writes. (esphome#5678)
Browse files Browse the repository at this point in the history
clydebarrow authored Nov 26, 2023

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
1 parent dbdcb39 commit 2e6d01d
Showing 3 changed files with 46 additions and 14 deletions.
14 changes: 9 additions & 5 deletions esphome/components/spi/spi.cpp
Original file line number Diff line number Diff line change
@@ -77,15 +77,19 @@ void SPIComponent::dump_config() {

void SPIDelegateDummy::begin_transaction() { ESP_LOGE(TAG, "SPIDevice not initialised - did you call spi_setup()?"); }

uint8_t SPIDelegateBitBash::transfer(uint8_t data) {
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_);
uint8_t out_data = 0;

for (uint8_t i = 0; i < 8; i++) {
for (uint8_t i = 0; i != num_bits; i++) {
uint8_t shift;
if (bit_order_ == BIT_ORDER_MSB_FIRST) {
shift = 7 - i;
shift = num_bits - 1 - i;
} else {
shift = i;
}
@@ -94,7 +98,7 @@ uint8_t SPIDelegateBitBash::transfer(uint8_t data) {
// sampling on leading edge
this->sdo_pin_->digital_write(data & (1 << shift));
this->cycle_clock_();
out_data |= uint8_t(this->sdi_pin_->digital_read()) << shift;
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_);
@@ -104,7 +108,7 @@ uint8_t SPIDelegateBitBash::transfer(uint8_t data) {
this->clk_pin_->digital_write(!this->clock_polarity_);
this->sdo_pin_->digital_write(data & (1 << shift));
this->cycle_clock_();
out_data |= uint8_t(this->sdi_pin_->digital_read()) << shift;
out_data |= uint16_t(this->sdi_pin_->digital_read()) << shift;
this->clk_pin_->digital_write(this->clock_polarity_);
}
}
16 changes: 16 additions & 0 deletions esphome/components/spi/spi.h
Original file line number Diff line number Diff line change
@@ -199,6 +199,15 @@ class SPIDelegate {
rxbuf[i] = this->transfer(txbuf[i]);
}

/**
* write a variable length data item, up to 16 bits.
* @param data The data to send. Should be LSB-aligned (i.e. top bits will be discarded.)
* @param num_bits The number of bits to send
*/
virtual void write(uint16_t data, size_t num_bits) {
esph_log_e("spi_device", "variable length write not implemented");
}

// write 16 bits
virtual void write16(uint16_t data) {
if (this->bit_order_ == BIT_ORDER_MSB_FIRST) {
@@ -270,6 +279,10 @@ class SPIDelegateBitBash : public SPIDelegate {

uint8_t transfer(uint8_t data) override;

void write(uint16_t data, size_t num_bits) override;

void write16(uint16_t data) override { this->write(data, 16); };

protected:
GPIOPin *clk_pin_;
GPIOPin *sdo_pin_;
@@ -284,6 +297,7 @@ class SPIDelegateBitBash : public SPIDelegate {
continue;
this->last_transition_ += this->wait_cycle_;
}
uint16_t transfer_(uint16_t data, size_t num_bits);
};

class SPIBus {
@@ -408,6 +422,8 @@ class SPIDevice : public SPIClient {

void read_array(uint8_t *data, size_t length) { return this->delegate_->read_array(data, length); }

void write(uint16_t data, size_t num_bits) { this->delegate_->write(data, num_bits); };

void write_byte(uint8_t data) { this->delegate_->write_array(&data, 1); }

void transfer_array(uint8_t *data, size_t length) { this->delegate_->transfer(data, length); }
30 changes: 21 additions & 9 deletions esphome/components/spi/spi_esp_idf.cpp
Original file line number Diff line number Diff line change
@@ -72,7 +72,11 @@ class SPIDelegateHw : public SPIDelegate {
desc.rxlength = this->write_only_ ? 0 : partial * 8;
desc.tx_buffer = txbuf;
desc.rx_buffer = rxbuf;
esp_err_t const err = spi_device_transmit(this->handle_, &desc);
// polling is used as it has about 10% less overhead than queuing an interrupt transfer
esp_err_t err = spi_device_polling_start(this->handle_, &desc, portMAX_DELAY);
if (err == ESP_OK) {
err = spi_device_polling_end(this->handle_, portMAX_DELAY);
}
if (err != ESP_OK) {
ESP_LOGE(TAG, "Transmit failed - err %X", err);
break;
@@ -85,6 +89,21 @@ class SPIDelegateHw : public SPIDelegate {
}
}

void write(uint16_t data, size_t num_bits) override {
spi_transaction_ext_t desc = {};
desc.command_bits = num_bits;
desc.base.flags = SPI_TRANS_VARIABLE_CMD;
desc.base.cmd = data;
esp_err_t err = spi_device_polling_start(this->handle_, (spi_transaction_t *) &desc, portMAX_DELAY);
if (err == ESP_OK) {
err = spi_device_polling_end(this->handle_, portMAX_DELAY);
}

if (err != ESP_OK) {
ESP_LOGE(TAG, "Transmit failed - err %X", err);
}
}

void transfer(uint8_t *ptr, size_t length) override { this->transfer(ptr, ptr, length); }

uint8_t transfer(uint8_t data) override {
@@ -93,14 +112,7 @@ class SPIDelegateHw : public SPIDelegate {
return rxbuf;
}

void write16(uint16_t data) override {
if (this->bit_order_ == BIT_ORDER_MSB_FIRST) {
uint16_t txbuf = SPI_SWAP_DATA_TX(data, 16);
this->transfer((uint8_t *) &txbuf, nullptr, 2);
} else {
this->transfer((uint8_t *) &data, nullptr, 2);
}
}
void write16(uint16_t data) override { this->write(data, 16); }

void write_array(const uint8_t *ptr, size_t length) override { this->transfer(ptr, nullptr, length); }

0 comments on commit 2e6d01d

Please sign in to comment.