aboutsummaryrefslogtreecommitdiff
path: root/pw_spi
diff options
context:
space:
mode:
Diffstat (limited to 'pw_spi')
-rw-r--r--pw_spi/BUILD.bazel17
-rw-r--r--pw_spi/BUILD.gn11
-rw-r--r--pw_spi/CMakeLists.txt59
-rw-r--r--pw_spi/docs.rst62
-rw-r--r--pw_spi/public/pw_spi/chip_selector.h2
-rw-r--r--pw_spi/public/pw_spi/device.h18
-rw-r--r--pw_spi/public/pw_spi/initiator.h7
-rw-r--r--pw_spi/public/pw_spi/responder.h56
-rw-r--r--pw_spi/spi_test.cc15
9 files changed, 215 insertions, 32 deletions
diff --git a/pw_spi/BUILD.bazel b/pw_spi/BUILD.bazel
index c50f61a0b..bb8335a61 100644
--- a/pw_spi/BUILD.bazel
+++ b/pw_spi/BUILD.bazel
@@ -36,6 +36,19 @@ pw_cc_library(
)
pw_cc_library(
+ name = "responder",
+ hdrs = [
+ "public/pw_spi/responder.h",
+ ],
+ includes = ["public"],
+ deps = [
+ "//pw_bytes",
+ "//pw_function",
+ "//pw_status",
+ ],
+)
+
+pw_cc_library(
name = "chip_selector",
hdrs = [
"public/pw_spi/chip_selector.h",
@@ -101,6 +114,7 @@ pw_cc_test(
],
deps = [
":device",
+ ":responder",
"//pw_sync:mutex",
"//pw_unit_test",
],
@@ -112,6 +126,9 @@ pw_cc_library(
srcs = ["linux_spi.cc"],
hdrs = ["public/pw_spi/linux_spi.h"],
includes = ["public"],
+ target_compatible_with = [
+ "@platforms//os:linux",
+ ],
deps = [
":device",
"//pw_bytes",
diff --git a/pw_spi/BUILD.gn b/pw_spi/BUILD.gn
index 5a683629d..cc1d0b2cc 100644
--- a/pw_spi/BUILD.gn
+++ b/pw_spi/BUILD.gn
@@ -43,6 +43,16 @@ pw_source_set("initiator") {
]
}
+pw_source_set("responder") {
+ public_configs = [ ":public_include_path" ]
+ public = [ "public/pw_spi/responder.h" ]
+ public_deps = [
+ "$dir_pw_bytes",
+ "$dir_pw_function",
+ "$dir_pw_status",
+ ]
+}
+
pw_source_set("chip_selector") {
public_configs = [ ":public_include_path" ]
public = [ "public/pw_spi/chip_selector.h" ]
@@ -107,6 +117,7 @@ pw_test("spi_test") {
sources = [ "spi_test.cc" ]
deps = [
":device",
+ ":responder",
"$dir_pw_sync:mutex",
]
}
diff --git a/pw_spi/CMakeLists.txt b/pw_spi/CMakeLists.txt
new file mode 100644
index 000000000..3ce4c3f1e
--- /dev/null
+++ b/pw_spi/CMakeLists.txt
@@ -0,0 +1,59 @@
+# Copyright 2023 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($ENV{PW_ROOT}/pw_build/pigweed.cmake)
+
+pw_add_library(pw_spi.initiator INTERFACE
+ HEADERS
+ public/pw_spi/initiator.h
+ PUBLIC_INCLUDES
+ public
+ PUBLIC_DEPS
+ pw_assert
+ pw_bytes
+ pw_status
+)
+
+pw_add_library(pw_spi.responder INTERFACE
+ HEADERS
+ public/pw_spi/responder.h
+ PUBLIC_INCLUDES
+ public
+ PUBLIC_DEPS
+ pw_bytes
+ pw_function
+ pw_status
+)
+
+pw_add_library(pw_spi.chip_selector INTERFACE
+ HEADERS
+ public/pw_spi/chip_selector.h
+ PUBLIC_INCLUDES
+ public
+ PUBLIC_DEPS
+ pw_status
+)
+
+pw_add_library(pw_spi.device INTERFACE
+ HEADERS
+ public/pw_spi/device.h
+ PUBLIC_INCLUDES
+ public
+ PUBLIC_DEPS
+ pw_bytes
+ pw_spi.chip_selector
+ pw_spi.initiator
+ pw_status
+ pw_sync.borrow
+)
diff --git a/pw_spi/docs.rst b/pw_spi/docs.rst
index d1ba66823..b23ae887d 100644
--- a/pw_spi/docs.rst
+++ b/pw_spi/docs.rst
@@ -4,26 +4,28 @@
pw_spi
======
Pigweed's SPI module provides a set of interfaces for communicating with SPI
-peripherals attached to a target.
+responders attached to a target. It also provides an interface for implementing
+SPI responders.
--------
Overview
--------
The ``pw_spi`` module provides a series of interfaces that facilitate the
-development of SPI peripheral drivers that are abstracted from the target's
-SPI hardware implementation. The interface consists of three main classes:
+development of SPI responder drivers that are abstracted from the target's
+SPI hardware implementation. The interface consists of these main classes:
- ``pw::spi::Initiator`` - Interface for configuring a SPI bus, and using it
to transmit and receive data.
- ``pw::spi::ChipSelector`` - Interface for enabling/disabling a SPI
- peripheral attached to the bus.
+ responder attached to the bus.
- ``pw::spi::Device`` - primary HAL interface used to interact with a SPI
- peripheral.
+ responder.
+- ``pw::spi::Responder`` - Interface for implementing a SPI responder.
``pw_spi`` relies on a target-specific implementations of
``pw::spi::Initiator`` and ``pw::spi::ChipSelector`` to be defined, and
injected into ``pw::spi::Device`` objects which are used to communicate with a
-given peripheral attached to a target's SPI bus.
+given responder attached to a target's SPI bus.
Example - Constructing a SPI Device:
@@ -37,10 +39,11 @@ Example - Constructing a SPI Device:
};
auto initiator = pw::spi::MyInitator();
+ auto mutex = pw::sync::VirtualMutex();
auto selector = pw::spi::MyChipSelector();
- auto borrowable_initiator = pw::sync::Borrowable<Initiator&>(initiator);
- auto device = pw::spi::Device(borrowable_initiator, kConfig, selector);
+ auto device = pw::spi::Device(
+ pw::sync::Borrowable<Initiator>(initiator, mutex), kConfig, selector);
This example demonstrates the construction of a ``pw::spi::Device`` from its
object dependencies and configuration data; where ``MyDevice`` and
@@ -53,7 +56,7 @@ that transactions cannot be interrupted or corrupted by other concurrent
workloads making use of the same SPI bus.
Once constructed, the ``device`` object can then be passed to functions used to
-perform SPI transfers with a target peripheral.
+perform SPI transfers with a target responder.
Example - Performing a Transfer:
@@ -117,6 +120,7 @@ The SPI API consists of the following components:
structs.
- The ``pw::spi::ChipSelector`` interface.
- The ``pw::spi::Device`` class.
+- The ``pw::spi::Responder`` interface.
pw::spi::Initiator
------------------
@@ -141,15 +145,11 @@ method.
.. Note:
- Throughout ``pw_spi``, the terms "controller" and "peripheral" are used to
+ Throughout ``pw_spi``, the terms "initiator" and "responder" are used to
describe the two roles SPI devices can implement. These terms correspond
to the "master" and "slave" roles described in legacy documentation
related to the SPI protocol.
- ``pw_spi`` only supports SPI transfers where the target implements the
- "controller" role, and does not support the target acting in the
- "peripheral" role.
-
.. inclusive-language: enable
.. cpp:class:: pw::spi::Initiator
@@ -179,7 +179,7 @@ method.
pw::spi::ChipSelector
---------------------
The ChipSelector class provides an abstract interface for controlling the
-chip-select signal associated with a specific SPI peripheral.
+chip-select signal associated with a specific SPI responder.
This interface provides a ``SetActive()`` method, which activates/deactivates
the device based on the value of the `active` parameter. The associated
@@ -187,7 +187,7 @@ the device based on the value of the `active` parameter. The associated
``SetActive(true)`` and ``SetActive(false)``, respectively.
A concrete implementation of this interface class must be provided in order to
-use the SPI HAL to communicate with a peripheral.
+use the SPI HAL to communicate with a responder.
.. Note::
@@ -250,7 +250,7 @@ the ``pw::sync::Borrowable`` object, where the ``pw::spi::Initiator`` object is
.. cpp:function:: Status Read(Bytespan read_buffer)
- Synchronously read data from the SPI peripheral until the provided
+ Synchronously read data from the SPI responder until the provided
`read_buffer` is full.
This call will configure the bus and activate/deactivate chip select
for the transfer
@@ -263,7 +263,7 @@ the ``pw::sync::Borrowable`` object, where the ``pw::spi::Initiator`` object is
.. cpp:function:: Status Write(ConstByteSpan write_buffer)
- Synchronously write the contents of `write_buffer` to the SPI peripheral.
+ Synchronously write the contents of `write_buffer` to the SPI responder.
This call will configure the bus and activate/deactivate chip select
for the transfer
@@ -275,7 +275,7 @@ the ``pw::sync::Borrowable`` object, where the ``pw::spi::Initiator`` object is
.. cpp:function:: Status WriteRead(ConstByteSpan write_buffer, ByteSpan read_buffer)
- Perform a synchronous read/write transfer with the SPI peripheral. Data
+ Perform a synchronous read/write transfer with the SPI responder. Data
from the `write_buffer` object is written to the bus, while the
`read_buffer` is populated with incoming data on the bus. In the event
the read buffer is smaller than the write buffer (or zero-size), any
@@ -306,7 +306,7 @@ the ``pw::sync::Borrowable`` object, where the ``pw::spi::Initiator`` object is
.. cpp:function:: Status Read(Bytespan read_buffer)
- Synchronously read data from the SPI peripheral until the provided
+ Synchronously read data from the SPI responder until the provided
`read_buffer` is full.
Returns OkStatus() on success, and implementation-specific values on
@@ -314,7 +314,7 @@ the ``pw::sync::Borrowable`` object, where the ``pw::spi::Initiator`` object is
.. cpp:function:: Status Write(ConstByteSpan write_buffer)
- Synchronously write the contents of `write_buffer` to the SPI peripheral
+ Synchronously write the contents of `write_buffer` to the SPI responder
Returns OkStatus() on success, and implementation-specific values on
failure.
@@ -371,3 +371,23 @@ list. An example of this is shown below:
// Alternatively this is also called from MockInitiator::~MockInitiator().
EXPECT_EQ(spi_mock.Finalize(), OkStatus());
+pw::spi::Responder
+------------------
+The common interface for implementing a SPI responder. It provides a way to
+respond to SPI transactions coming from a SPI initiator in a non-target specific
+way. A concrete implementation of the ``Responder`` class should be provided for
+the target hardware. Applications can then use it to implement their specific
+protocols.
+
+.. code-block:: cpp
+
+ MyResponder responder;
+ responder.SetCompletionHandler([](ByteSpan rx_data, Status status) {
+ // Handle incoming data from initiator.
+ // ...
+ // Prepare data to send back to initiator during next SPI transaction.
+ responder.WriteReadAsync(tx_data, rx_data);
+ });
+
+ // Prepare data to send back to initiator during next SPI transaction.
+ responder.WriteReadAsync(tx_data, rx_data)
diff --git a/pw_spi/public/pw_spi/chip_selector.h b/pw_spi/public/pw_spi/chip_selector.h
index 3290f507d..50588b637 100644
--- a/pw_spi/public/pw_spi/chip_selector.h
+++ b/pw_spi/public/pw_spi/chip_selector.h
@@ -30,7 +30,7 @@ enum class ChipSelectBehavior : uint8_t {
};
// The ChipSelector class provides an abstract interface for controlling the
-// chip-select signal associated with a specific SPI peripheral.
+// chip-select signal associated with a specific SPI responder.
class ChipSelector {
public:
virtual ~ChipSelector() = default;
diff --git a/pw_spi/public/pw_spi/device.h b/pw_spi/public/pw_spi/device.h
index 3f0449653..c9e50c6b0 100644
--- a/pw_spi/public/pw_spi/device.h
+++ b/pw_spi/public/pw_spi/device.h
@@ -25,21 +25,21 @@
namespace pw::spi {
-// The Device class enables data transfer with a specific SPI peripheral.
+// The Device class enables data transfer with a specific SPI responder.
// This class combines an Initiator (representing the physical SPI bus), its
// configuration data, and the ChipSelector object to uniquely address a device.
// Transfers to a selected initiator are guarded against concurrent access
// through the use of the `Borrowable` object.
class Device {
public:
- Device(sync::Borrowable<Initiator>& initiator,
+ Device(sync::Borrowable<Initiator> initiator,
const Config config,
ChipSelector& selector)
: initiator_(initiator), config_(config), selector_(selector) {}
~Device() = default;
- // Synchronously read data from the SPI peripheral until the provided
+ // Synchronously read data from the SPI responder until the provided
// `read_buffer` is full.
// This call will configure the bus and activate/deactivate chip select
// for the transfer
@@ -50,7 +50,7 @@ class Device {
// failure.
Status Read(ByteSpan read_buffer) { return WriteRead({}, read_buffer); }
- // Synchronously write the contents of `write_buffer` to the SPI peripheral.
+ // Synchronously write the contents of `write_buffer` to the SPI responder.
// This call will configure the bus and activate/deactivate chip select
// for the transfer
//
@@ -62,7 +62,7 @@ class Device {
return WriteRead(write_buffer, {});
}
- // Perform a synchronous read/write transfer with the SPI peripheral. Data
+ // Perform a synchronous read/write transfer with the SPI responder. Data
// from the `write_buffer` object is written to the bus, while the
// `read_buffer` is populated with incoming data on the bus. In the event
// the read buffer is smaller than the write buffer (or zero-size), any
@@ -93,7 +93,7 @@ class Device {
(behavior_ == ChipSelectBehavior::kPerTransaction) &&
(!first_write_read_)) {
selector_->Deactivate()
- .IgnoreError(); // TODO(b/242598609): Handle Status properly
+ .IgnoreError(); // TODO: b/242598609 - Handle Status properly
}
}
@@ -120,14 +120,14 @@ class Device {
Transaction(const Transaction&) = delete;
Transaction& operator=(const Transaction&) = delete;
- // Synchronously read data from the SPI peripheral until the provided
+ // Synchronously read data from the SPI responder until the provided
// `read_buffer` is full.
//
// Returns OkStatus() on success, and implementation-specific values on
// failure.
Status Read(ByteSpan read_buffer) { return WriteRead({}, read_buffer); }
- // Synchronously write the contents of `write_buffer` to the SPI peripheral
+ // Synchronously write the contents of `write_buffer` to the SPI responder
//
// Returns OkStatus() on success, and implementation-specific values on
// failure.
@@ -200,7 +200,7 @@ class Device {
}
private:
- sync::Borrowable<Initiator>& initiator_;
+ sync::Borrowable<Initiator> initiator_;
const Config config_;
ChipSelector& selector_;
};
diff --git a/pw_spi/public/pw_spi/initiator.h b/pw_spi/public/pw_spi/initiator.h
index a750b03be..0935d7e92 100644
--- a/pw_spi/public/pw_spi/initiator.h
+++ b/pw_spi/public/pw_spi/initiator.h
@@ -67,6 +67,11 @@ struct Config {
ClockPhase phase;
BitsPerWord bits_per_word;
BitOrder bit_order;
+
+ bool operator==(const Config& rhs) const {
+ return polarity == rhs.polarity && phase == rhs.phase &&
+ bits_per_word() == rhs.bits_per_word() && bit_order == rhs.bit_order;
+ }
};
static_assert(sizeof(Config) == sizeof(uint32_t),
"Ensure that the config struct fits in 32-bits");
@@ -77,7 +82,7 @@ class Initiator {
public:
virtual ~Initiator() = default;
- // Configure the SPI bus to communicate with peripherals using a given set of
+ // Configure the SPI bus to communicate with responders using a given set of
// properties, including the clock polarity, clock phase, bit-order, and
// bits-per-word.
// Returns OkStatus() on success, and implementation-specific values on
diff --git a/pw_spi/public/pw_spi/responder.h b/pw_spi/public/pw_spi/responder.h
new file mode 100644
index 000000000..283c341a7
--- /dev/null
+++ b/pw_spi/public/pw_spi/responder.h
@@ -0,0 +1,56 @@
+// Copyright 2023 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_bytes/span.h"
+#include "pw_function/function.h"
+#include "pw_status/status.h"
+
+namespace pw::spi {
+
+// The Responder class provides an abstract interface used to receive and
+// transmit data on the responder side of a SPI bus.
+class Responder {
+ public:
+ virtual ~Responder() = default;
+
+ // Set `callback` to be called when SPI transaction completes. `callback` can
+ // be called in an interrupt context. `callback` should not be changed during
+ // execution of a completion.
+ //
+ // A value of CANCELLED for the Status parameter indicates Abort() was called.
+ // Partially transferred data may be passed in that case as well.
+ // Other Status values are implementer defined.
+ void SetCompletionHandler(Function<void(ByteSpan, Status)> callback);
+
+ // `tx_data` is queued for tx when called, but only transmitted when
+ // the initiator starts the next transaction. It's up to the implementer to
+ // define how stuffing bytes are handled.
+ // `rx_data` is populated as the initiator transfers data. A slice of
+ // `rx_data` is passed as a span to the completion callback.
+ //
+ // Only one outstanding request should be active. UNAVAILABLE will be returned
+ // if a transaction is already established.
+ //
+ // The completion handler will always be invoked, even in the case of an
+ // Abort(). In that case a Status value of CANCELLED will be passed.
+ Status WriteReadAsync(ConstByteSpan tx_data, ByteSpan rx_data);
+
+ // Cancel the outstanding `WriteReadAsync` call. The completion handler will
+ // be called with a Status of CANCELLED after this is called.
+ void Abort();
+};
+
+} // namespace pw::spi
diff --git a/pw_spi/spi_test.cc b/pw_spi/spi_test.cc
index f3b42ae61..9deb7d5a4 100644
--- a/pw_spi/spi_test.cc
+++ b/pw_spi/spi_test.cc
@@ -19,6 +19,7 @@
#include "pw_spi/chip_selector.h"
#include "pw_spi/device.h"
#include "pw_spi/initiator.h"
+#include "pw_spi/responder.h"
#include "pw_status/status.h"
#include "pw_sync/borrow.h"
#include "pw_sync/mutex.h"
@@ -63,6 +64,17 @@ class SpiTestDevice : public ::testing::Test {
Device device_;
};
+class SpiResponderTestDevice : public ::testing::Test {
+ public:
+ SpiResponderTestDevice() : responder_() {}
+
+ private:
+ // Stub SPI Responder, used to exercise public API surface.
+ class TestResponder : public Responder {};
+
+ TestResponder responder_;
+};
+
// Simple test ensuring the SPI HAL compiles
TEST_F(SpiTestDevice, CompilationSucceeds) {
// arrange
@@ -71,5 +83,8 @@ TEST_F(SpiTestDevice, CompilationSucceeds) {
EXPECT_TRUE(true);
}
+// Simple test ensuring the SPI Responder HAL compiles
+TEST_F(SpiResponderTestDevice, CompilationSucceeds) { EXPECT_TRUE(true); }
+
} // namespace
} // namespace pw::spi