Add Pico/STM32F329I pw_spi implementations
Add two pw_spi target implementations:
1. pw_spi_pico
2. pw_spi_stm32f429i_disc1_stm32cube
Change-Id: I95c84c7c45693e2311e21f72d7c201eba7963784
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/experimental/+/113077
Commit-Queue: Chris Mumford <cmumford@google.com>
Reviewed-by: Anthony DiGirolamo <tonymd@google.com>
diff --git a/build_overrides/pigweed.gni b/build_overrides/pigweed.gni
index 74646ba..fcb35b4 100644
--- a/build_overrides/pigweed.gni
+++ b/build_overrides/pigweed.gni
@@ -52,6 +52,9 @@
get_path_info("//pw_graphics/pw_display_teensy_ili9341", "abspath")
dir_pw_draw = get_path_info("//pw_graphics/pw_draw", "abspath")
dir_pw_framebuffer = get_path_info("//pw_graphics/pw_framebuffer", "abspath")
+ dir_pw_spi_pico = get_path_info("//pw_spi_pico", "abspath")
+ dir_pw_spi_stm32f429i_disc1_stm32cube =
+ get_path_info("//pw_spi_stm32f429i_disc1_stm32cube", "abspath")
dir_pw_spin_delay = get_path_info("//pw_spin_delay", "abspath")
dir_pw_spin_delay_arduino =
get_path_info("//pw_spin_delay_arduino", "abspath")
diff --git a/pw_spi_pico/BUILD.gn b/pw_spi_pico/BUILD.gn
new file mode 100644
index 0000000..7b3d67a
--- /dev/null
+++ b/pw_spi_pico/BUILD.gn
@@ -0,0 +1,44 @@
+# Copyright 2022 The Pigweed Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+import("//build_overrides/pi_pico.gni")
+import("//build_overrides/pigweed.gni")
+import("$dir_pw_build/target_types.gni")
+
+config("default_config") {
+ include_dirs = [ "public" ]
+}
+
+pw_source_set("pw_spi_pico") {
+ public_configs = [ ":default_config" ]
+ public = [
+ "public/pw_spi_pico/chip_selector.h",
+ "public/pw_spi_pico/initiator.h",
+ ]
+ public_deps = [ "$dir_pw_status" ]
+ deps = [
+ "$PICO_ROOT/src/common/pico_base",
+ "$PICO_ROOT/src/common/pico_stdlib",
+ "$PICO_ROOT/src/rp2_common/hardware_spi",
+ "$dir_pw_digital_io",
+ "$dir_pw_log",
+ "$dir_pw_spi:chip_selector",
+ "$dir_pw_spi:initiator",
+ ]
+ sources = [
+ "chip_selector.cc",
+ "initiator.cc",
+ ]
+ remove_configs = [ "$dir_pw_build:strict_warnings" ]
+}
diff --git a/pw_spi_pico/chip_selector.cc b/pw_spi_pico/chip_selector.cc
new file mode 100644
index 0000000..54d9552
--- /dev/null
+++ b/pw_spi_pico/chip_selector.cc
@@ -0,0 +1,28 @@
+// Copyright 2022 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+#include "pw_spi_pico/chip_selector.h"
+
+using pw::digital_io::State;
+
+namespace pw::spi {
+
+PicoChipSelector::PicoChipSelector(pw::digital_io::DigitalOut& cs_pin)
+ : cs_pin_(cs_pin) {}
+
+Status PicoChipSelector::SetActive(bool active) {
+ return cs_pin_.SetState(active ? State::kInactive : State::kActive);
+}
+
+} // namespace pw::spi
diff --git a/pw_spi_pico/initiator.cc b/pw_spi_pico/initiator.cc
new file mode 100644
index 0000000..cff4e5f
--- /dev/null
+++ b/pw_spi_pico/initiator.cc
@@ -0,0 +1,134 @@
+// Copyright 2022 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+#include "pw_spi_pico/initiator.h"
+
+#include <algorithm>
+
+#include "hardware/spi.h"
+#include "pico/stdlib.h"
+#include "pw_assert/check.h"
+#include "pw_log/log.h"
+#include "pw_status/try.h"
+
+namespace pw::spi {
+
+namespace {
+
+constexpr spi_order_t GetBitOrder(BitOrder bit_order) {
+ switch (bit_order) {
+ case BitOrder::kLsbFirst:
+ return SPI_LSB_FIRST;
+ case BitOrder::kMsbFirst:
+ return SPI_MSB_FIRST;
+ }
+ PW_CRASH("Unknown bit order");
+ return SPI_LSB_FIRST;
+}
+
+constexpr spi_cpha_t GetPhase(ClockPhase phase) {
+ switch (phase) {
+ case ClockPhase::kFallingEdge:
+ return SPI_CPHA_1;
+ case ClockPhase::kRisingEdge:
+ return SPI_CPHA_0;
+ }
+ PW_CRASH("Unknown phase");
+ return SPI_CPHA_0;
+}
+
+constexpr spi_cpol_t GetPolarity(ClockPolarity polarity) {
+ switch (polarity) {
+ case ClockPolarity::kActiveHigh:
+ return SPI_CPOL_1;
+ case ClockPolarity::kActiveLow:
+ return SPI_CPOL_0;
+ }
+ PW_CRASH("Unknown polarity");
+ return SPI_CPOL_0;
+}
+
+} // namespace
+
+PicoInitiator::PicoInitiator(spi_inst_t* spi, uint32_t baud_rate)
+ : spi_(spi),
+ baud_rate_(baud_rate),
+ config_{
+ .polarity = ClockPolarity::kActiveHigh,
+ .phase = ClockPhase::kRisingEdge,
+ .bits_per_word = BitsPerWord(8),
+ .bit_order = BitOrder::kMsbFirst,
+ },
+ desired_bits_per_word_(8) {}
+
+void PicoInitiator::SetOverrideBitsPerWord(BitsPerWord bits_per_word) {
+ // TODO(b/251033990): Remove once changing SPI device config is added.
+ desired_bits_per_word_ = bits_per_word;
+ override_bits_per_word_ = true;
+}
+
+Status PicoInitiator::LazyInit() {
+ // Already initialized - nothing to do.
+ // The Pico SDK needs to call spi_init() earlier so that the
+ // various GPIO pins (MISO, etc.) can be assigned to the SPI
+ // bus.
+ return OkStatus();
+}
+
+Status PicoInitiator::Configure(const Config& config) {
+ config_ = config;
+ // TODO(b/251033990): Remove once changing SPI device config is added.
+ if (override_bits_per_word_) {
+ config_.bits_per_word = desired_bits_per_word_;
+ }
+ spi_set_format(spi_,
+ config_.bits_per_word(),
+ GetPolarity(config_.polarity),
+ GetPhase(config_.phase),
+ GetBitOrder(config_.bit_order));
+
+ return OkStatus();
+}
+
+Status PicoInitiator::WriteRead(ConstByteSpan write_buffer,
+ ByteSpan read_buffer) {
+ PW_TRY(LazyInit());
+
+ if (!write_buffer.empty()) {
+ if (!read_buffer.empty()) {
+ PW_CRASH("Not implemented");
+ } else {
+ if (config_.bits_per_word() == 16) {
+ spi_write16_blocking(
+ spi_,
+ reinterpret_cast<const uint16_t*>(write_buffer.data()),
+ write_buffer.size());
+ } else {
+ spi_write_blocking(
+ spi_,
+ reinterpret_cast<const uint8_t*>(write_buffer.data()),
+ write_buffer.size());
+ }
+ }
+ } else {
+ spi_read_blocking(spi_,
+ /*repeated_tx_data=*/0,
+ reinterpret_cast<uint8_t*>(read_buffer.data()),
+ read_buffer.size());
+ }
+
+ return OkStatus();
+}
+
+} // namespace pw::spi
diff --git a/pw_spi_pico/public/pw_spi_pico/chip_selector.h b/pw_spi_pico/public/pw_spi_pico/chip_selector.h
new file mode 100644
index 0000000..d06f3ca
--- /dev/null
+++ b/pw_spi_pico/public/pw_spi_pico/chip_selector.h
@@ -0,0 +1,34 @@
+// Copyright 2022 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+#pragma once
+
+#include "pw_digital_io/digital_io.h"
+#include "pw_spi/chip_selector.h"
+
+namespace pw::spi {
+
+// Pico SDK implementation of SPI ChipSelector.
+class PicoChipSelector : public ChipSelector {
+ public:
+ PicoChipSelector(pw::digital_io::DigitalOut& cs_pin);
+
+ // Implements pw::spi::ChipSelector:
+ Status SetActive(bool active) override;
+
+ private:
+ pw::digital_io::DigitalOut& cs_pin_;
+};
+
+} // namespace pw::spi
diff --git a/pw_spi_pico/public/pw_spi_pico/initiator.h b/pw_spi_pico/public/pw_spi_pico/initiator.h
new file mode 100644
index 0000000..7781c0d
--- /dev/null
+++ b/pw_spi_pico/public/pw_spi_pico/initiator.h
@@ -0,0 +1,46 @@
+// Copyright 2022 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+#pragma once
+
+#include <cstdint>
+
+#include "hardware/spi.h"
+#include "pw_spi/initiator.h"
+
+namespace pw::spi {
+
+// Pico SDK userspace implementation of the SPI Initiator
+class PicoInitiator : public Initiator {
+ public:
+ PicoInitiator(spi_inst_t* spi, uint32_t baud_rate);
+
+ void SetOverrideBitsPerWord(BitsPerWord bits_per_word);
+
+ // Implements pw::spi::Initiator:
+ Status Configure(const Config& config) override;
+ Status WriteRead(ConstByteSpan write_buffer, ByteSpan read_buffer) override;
+
+ private:
+ Status LazyInit();
+
+ spi_inst_t* spi_;
+ uint32_t baud_rate_;
+ Status init_status_; // The saved LazyInit() status.
+ Config config_;
+ BitsPerWord desired_bits_per_word_;
+ bool override_bits_per_word_ = false;
+};
+
+} // namespace pw::spi
diff --git a/pw_spi_stm32f429i_disc1_stm32cube/BUILD.gn b/pw_spi_stm32f429i_disc1_stm32cube/BUILD.gn
new file mode 100644
index 0000000..0994580
--- /dev/null
+++ b/pw_spi_stm32f429i_disc1_stm32cube/BUILD.gn
@@ -0,0 +1,43 @@
+# Copyright 2022 The Pigweed Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+import("//build_overrides/pigweed.gni")
+
+import("$dir_pw_build/target_types.gni")
+import("$dir_pw_third_party/stm32cube/stm32cube.gni")
+
+config("default_config") {
+ include_dirs = [ "public" ]
+}
+
+pw_source_set("pw_spi_stm32f429i_disc1_stm32cube") {
+ public_configs = [ ":default_config" ]
+ public = [
+ "public/pw_spi_stm32f429i_disc1_stm32cube/chip_selector.h",
+ "public/pw_spi_stm32f429i_disc1_stm32cube/initiator.h",
+ ]
+ public_deps = [ "$dir_pw_status" ]
+ deps = [
+ "$dir_pw_digital_io",
+ "$dir_pw_log",
+ "$dir_pw_spi:chip_selector",
+ "$dir_pw_spi:initiator",
+ "$dir_pw_third_party/stm32cube",
+ ]
+ sources = [
+ "chip_selector.cc",
+ "initiator.cc",
+ ]
+ remove_configs = [ "$dir_pw_build:strict_warnings" ]
+}
diff --git a/pw_spi_stm32f429i_disc1_stm32cube/chip_selector.cc b/pw_spi_stm32f429i_disc1_stm32cube/chip_selector.cc
new file mode 100644
index 0000000..e3075b6
--- /dev/null
+++ b/pw_spi_stm32f429i_disc1_stm32cube/chip_selector.cc
@@ -0,0 +1,30 @@
+// Copyright 2022 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+#include "pw_spi_stm32f429i_disc1_stm32cube/chip_selector.h"
+
+#include "stm32f4xx_hal.h"
+
+using pw::digital_io::State;
+
+namespace pw::spi {
+
+Stm32CubeChipSelector::Stm32CubeChipSelector(pw::digital_io::DigitalOut& cs_pin)
+ : cs_pin_(cs_pin) {}
+
+Status Stm32CubeChipSelector::SetActive(bool active) {
+ return cs_pin_.SetState(active ? State::kInactive : State::kActive);
+}
+
+} // namespace pw::spi
\ No newline at end of file
diff --git a/pw_spi_stm32f429i_disc1_stm32cube/initiator.cc b/pw_spi_stm32f429i_disc1_stm32cube/initiator.cc
new file mode 100644
index 0000000..8776158
--- /dev/null
+++ b/pw_spi_stm32f429i_disc1_stm32cube/initiator.cc
@@ -0,0 +1,211 @@
+// Copyright 2022 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+#include "pw_spi_stm32f429i_disc1_stm32cube/initiator.h"
+
+#include <algorithm>
+
+#include "pw_log/log.h"
+#include "pw_status/try.h"
+#include "stm32cube/stm32cube.h"
+#include "stm32f4xx_hal.h"
+#include "stm32f4xx_hal_spi.h"
+
+namespace pw::spi {
+
+namespace {
+
+constexpr uint32_t kTimeout = 10000;
+
+constexpr Status ConvertStatus(HAL_StatusTypeDef status) {
+ switch (status) {
+ case HAL_OK:
+ return OkStatus();
+ case HAL_ERROR:
+ return Status::Internal();
+ case HAL_BUSY:
+ return Status::Unavailable();
+ case HAL_TIMEOUT:
+ return Status::DeadlineExceeded();
+ }
+ return Status::NotFound(); // Unreachable.
+}
+
+uint32_t GetDataSize(BitsPerWord bits_per_word) {
+ if (bits_per_word() == 8) {
+ return SPI_DATASIZE_8BIT;
+ } else if (bits_per_word() == 16) {
+ return SPI_DATASIZE_16BIT;
+ }
+ PW_ASSERT(false);
+ return SPI_DATASIZE_8BIT;
+}
+
+constexpr uint32_t GetBitOrder(BitOrder bit_order) {
+ switch (bit_order) {
+ case BitOrder::kLsbFirst:
+ return SPI_FIRSTBIT_LSB;
+ case BitOrder::kMsbFirst:
+ return SPI_FIRSTBIT_MSB;
+ }
+ PW_ASSERT(false);
+ return SPI_FIRSTBIT_MSB;
+}
+
+constexpr uint32_t GetPhase(ClockPhase phase) {
+ switch (phase) {
+ case ClockPhase::kFallingEdge:
+ return SPI_PHASE_1EDGE;
+ case ClockPhase::kRisingEdge:
+ return SPI_PHASE_2EDGE;
+ }
+ PW_ASSERT(false);
+ return SPI_PHASE_1EDGE;
+}
+
+constexpr uint32_t GetPolarity(ClockPolarity polarity) {
+ switch (polarity) {
+ case ClockPolarity::kActiveHigh:
+ return SPI_POLARITY_HIGH;
+ case ClockPolarity::kActiveLow:
+ return SPI_POLARITY_LOW;
+ }
+ PW_ASSERT(false);
+ return SPI_POLARITY_HIGH;
+}
+
+} // namespace
+
+// A collection of instance variables isolated into a separate structure
+// so that clients of Stm32CubeInitiator aren't forced to have a compile-time
+// dependency on the STM32 header files.
+struct Stm32CubeInitiator::PrivateInstanceData {
+ bool initialized = false;
+ Status init_status; // The saved LazyInit() status.
+ Config config_;
+ BitsPerWord desired_bits_per_word_;
+ bool override_bits_per_word_ = false;
+ SPI_HandleTypeDef spi_handle;
+
+ PrivateInstanceData()
+ : desired_bits_per_word_(8),
+ config_{
+ .polarity = ClockPolarity::kActiveHigh,
+ .phase = ClockPhase::kRisingEdge,
+ .bits_per_word = desired_bits_per_word_,
+ .bit_order = BitOrder::kMsbFirst,
+ },
+ spi_handle {
+ .Instance = SPI5, .Init {
+ .Mode = SPI_MODE_MASTER, .Direction = SPI_DIRECTION_2LINES,
+ .DataSize = SPI_DATASIZE_8BIT, .CLKPolarity = SPI_POLARITY_LOW,
+ .CLKPhase = SPI_PHASE_1EDGE, .NSS = SPI_NSS_SOFT,
+ .BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2,
+ .FirstBit = SPI_FIRSTBIT_MSB, .TIMode = SPI_TIMODE_DISABLE,
+ .CRCCalculation = SPI_CRCCALCULATION_DISABLE, .CRCPolynomial = 7
+ }
+ }
+ {}
+
+ Status InitSpi() {
+ auto s = HAL_SPI_Init(&spi_handle);
+ auto status = ConvertStatus(s);
+ PW_LOG_INFO("HAL_SPI_Init =>: %s", status.str());
+ return status;
+ }
+};
+
+Stm32CubeInitiator::Stm32CubeInitiator()
+ : instance_data_(new PrivateInstanceData) {}
+
+Stm32CubeInitiator::~Stm32CubeInitiator() { delete instance_data_; }
+
+Status Stm32CubeInitiator::LazyInit() {
+ if (instance_data_->initialized)
+ return instance_data_->init_status;
+ instance_data_->init_status = instance_data_->InitSpi();
+ instance_data_->initialized = true;
+ PW_LOG_INFO("Stm32CubeInitiator::LazyInit: %s",
+ instance_data_->init_status.str());
+ return instance_data_->init_status;
+}
+
+void Stm32CubeInitiator::SetOverrideBitsPerWord(BitsPerWord bits_per_word) {
+ // TODO(b/251033990): Remove once changing SPI device config is added.
+ instance_data_->desired_bits_per_word_ = bits_per_word;
+ instance_data_->override_bits_per_word_ = true;
+ instance_data_->initialized = false;
+}
+
+Status Stm32CubeInitiator::Configure(const Config& config) {
+ instance_data_->config_ = config;
+ // TODO(b/251033990): Remove once changing SPI device config is added.
+ if (instance_data_->override_bits_per_word_) {
+ instance_data_->config_.bits_per_word =
+ instance_data_->desired_bits_per_word_;
+ }
+ instance_data_->spi_handle.Init.DataSize =
+ GetDataSize(instance_data_->config_.bits_per_word);
+ instance_data_->spi_handle.Init.FirstBit =
+ GetBitOrder(instance_data_->config_.bit_order);
+ instance_data_->spi_handle.Init.CLKPhase =
+ GetPhase(instance_data_->config_.phase);
+ instance_data_->spi_handle.Init.CLKPolarity =
+ GetPolarity(instance_data_->config_.polarity);
+ PW_TRY(LazyInit());
+
+ return OkStatus();
+}
+
+Status Stm32CubeInitiator::WriteRead(ConstByteSpan write_buffer,
+ ByteSpan read_buffer) {
+ PW_TRY(LazyInit());
+
+ HAL_StatusTypeDef status;
+
+ if (!write_buffer.empty()) {
+ if (!read_buffer.empty()) {
+ // TODO(cmumford): Not yet conforming to the WriteRead contract.
+ uint16_t size = std::min(write_buffer.size(), read_buffer.size());
+ status = HAL_SPI_TransmitReceive(
+ &instance_data_->spi_handle,
+ reinterpret_cast<uint8_t*>(
+ const_cast<std::byte*>(write_buffer.data())),
+ reinterpret_cast<uint8_t*>(read_buffer.data()),
+ size,
+ kTimeout);
+ } else {
+ status =
+ HAL_SPI_Transmit(&instance_data_->spi_handle,
+ reinterpret_cast<uint8_t*>(
+ const_cast<std::byte*>(write_buffer.data())),
+ write_buffer.size(),
+ kTimeout);
+ if (status != HAL_OK) {
+ PW_LOG_ERROR("Stm32CubeInitiator::WriteRead: write:%ld B, s:%s",
+ write_buffer.size(),
+ ConvertStatus(status).str());
+ }
+ }
+ } else {
+ status = HAL_SPI_Receive(&instance_data_->spi_handle,
+ reinterpret_cast<uint8_t*>(read_buffer.data()),
+ read_buffer.size(),
+ kTimeout);
+ }
+
+ return ConvertStatus(status);
+}
+
+} // namespace pw::spi
diff --git a/pw_spi_stm32f429i_disc1_stm32cube/public/pw_spi_stm32f429i_disc1_stm32cube/chip_selector.h b/pw_spi_stm32f429i_disc1_stm32cube/public/pw_spi_stm32f429i_disc1_stm32cube/chip_selector.h
new file mode 100644
index 0000000..52abba3
--- /dev/null
+++ b/pw_spi_stm32f429i_disc1_stm32cube/public/pw_spi_stm32f429i_disc1_stm32cube/chip_selector.h
@@ -0,0 +1,34 @@
+// Copyright 2022 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+#pragma once
+
+#include "pw_digital_io/digital_io.h"
+#include "pw_spi/chip_selector.h"
+
+namespace pw::spi {
+
+// STM32 implementation of SPI ChipSelector
+class Stm32CubeChipSelector : public ChipSelector {
+ public:
+ Stm32CubeChipSelector(pw::digital_io::DigitalOut& cs_pin);
+
+ // Implements pw::spi::ChipSelector:
+ Status SetActive(bool active) override;
+
+ private:
+ pw::digital_io::DigitalOut& cs_pin_;
+};
+
+} // namespace pw::spi
diff --git a/pw_spi_stm32f429i_disc1_stm32cube/public/pw_spi_stm32f429i_disc1_stm32cube/initiator.h b/pw_spi_stm32f429i_disc1_stm32cube/public/pw_spi_stm32f429i_disc1_stm32cube/initiator.h
new file mode 100644
index 0000000..04e2603
--- /dev/null
+++ b/pw_spi_stm32f429i_disc1_stm32cube/public/pw_spi_stm32f429i_disc1_stm32cube/initiator.h
@@ -0,0 +1,40 @@
+// Copyright 2022 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+#pragma once
+
+#include "pw_spi/initiator.h"
+
+namespace pw::spi {
+
+// STM32 userspace implementation of the SPI Initiator
+class Stm32CubeInitiator : public Initiator {
+ public:
+ Stm32CubeInitiator();
+ ~Stm32CubeInitiator();
+
+ void SetOverrideBitsPerWord(BitsPerWord bits_per_word);
+
+ // Implements pw::spi::Initiator
+ Status Configure(const Config& config) override;
+ Status WriteRead(ConstByteSpan write_buffer, ByteSpan read_buffer) override;
+
+ private:
+ struct PrivateInstanceData;
+ Status LazyInit();
+
+ PrivateInstanceData* instance_data_;
+};
+
+} // namespace pw::spi