Send frames out on the queue
This also required passing the camera index through the Teensy.
Change-Id: I73d380a01fd129919dba5ccfa04e41b0f02da767
diff --git a/y2019/BUILD b/y2019/BUILD
index f00f5ba..3fae9e5 100644
--- a/y2019/BUILD
+++ b/y2019/BUILD
@@ -72,6 +72,7 @@
"//frc971/wpilib:wpilib_robot_base",
"//third_party:phoenix",
"//third_party:wpilib",
+ "//y2019/control_loops/drivetrain:camera_queue",
"//y2019/control_loops/superstructure:superstructure_queue",
"//y2019/jevois:spi",
],
diff --git a/y2019/control_loops/drivetrain/BUILD b/y2019/control_loops/drivetrain/BUILD
index 677726e..de52215 100644
--- a/y2019/control_loops/drivetrain/BUILD
+++ b/y2019/control_loops/drivetrain/BUILD
@@ -88,6 +88,7 @@
srcs = [
"camera.q",
],
+ visibility = ["//visibility:public"],
)
cc_library(
diff --git a/y2019/control_loops/drivetrain/camera.q b/y2019/control_loops/drivetrain/camera.q
index 5add759..6ef6f49 100644
--- a/y2019/control_loops/drivetrain/camera.q
+++ b/y2019/control_loops/drivetrain/camera.q
@@ -1,7 +1,6 @@
package y2019.control_loops.drivetrain;
-// These structures have a nearly one-to-one correspondence to those in
-// //y2019/jevois:structures.h. Please refer to that file for details.
+// See the Target structure in //y2019/jevois:structures.h for documentation.
struct CameraTarget {
float distance;
float height;
@@ -10,17 +9,17 @@
};
message CameraFrame {
- // monotonic time in nanoseconds at which frame was taken (note structure.h
- // uses age).
+ // Number of nanoseconds since the aos::monotonic_clock epoch at which this
+ // frame was captured.
int64_t timestamp;
// Number of targets actually in this frame.
uint8_t num_targets;
- // Buffer for the targets
+ // Buffer for the targets.
CameraTarget[3] targets;
- // Index of the camera with which this frame was taken:
+ // Index of the camera position (not serial number) which this frame is from.
uint8_t camera;
};
diff --git a/y2019/jevois/spi.cc b/y2019/jevois/spi.cc
index b6e6632..a987ee7 100644
--- a/y2019/jevois/spi.cc
+++ b/y2019/jevois/spi.cc
@@ -32,9 +32,10 @@
// 2. 8 bits distance
// 3. 6 bits skew
// 4. 6 bits height
-// 5. 1 bit target valid (a present frame has all-valid targets)
-// 6. 1 bit target present (a present frame can have from 0 to 3
-// targets, depending on how many were found)
+// 5. 2 bits of quantity+index
+// The 6 bits of quantity+index (between all three targets) are:
+// 1. 4 bits camera index + 1 (0 means this isn't a valid frame)
+// 2. 2 bits target count
// Note that empty frames are still sent to indicate that the camera is
// still working even though it doesn't see any targets.
@@ -110,26 +111,29 @@
integer);
}
-constexpr int valid_bits() { return 1; }
-constexpr int valid_offset() { return height_offset() + height_bits(); }
-void valid_pack(bool valid, gsl::span<char> destination) {
- aos::PackBits<uint32_t, valid_bits(), valid_offset()>(valid, destination);
+constexpr int quantity_index_offset() { return height_offset() + height_bits(); }
+void camera_index_pack(int camera_index, gsl::span<char> destination) {
+ aos::PackBits<uint32_t, 2, quantity_index_offset()>(camera_index & 3,
+ destination);
+ aos::PackBits<uint32_t, 2, quantity_index_offset() + 32>(
+ (camera_index >> 2) & 3, destination);
}
-bool valid_unpack(gsl::span<const char> source) {
- return aos::UnpackBits<uint32_t, valid_bits(), valid_offset()>(source);
+int camera_index_unpack(gsl::span<const char> source) {
+ int result = 0;
+ result |= aos::UnpackBits<uint32_t, 2, quantity_index_offset()>(source);
+ result |= aos::UnpackBits<uint32_t, 2, quantity_index_offset() + 32>(source)
+ << 2;
+ return result;
+}
+void target_count_pack(int target_count, gsl::span<char> destination) {
+ aos::PackBits<uint32_t, 2, quantity_index_offset() + 32 * 2>(target_count,
+ destination);
+}
+int target_count_unpack(gsl::span<const char> source) {
+ return aos::UnpackBits<uint32_t, 2, quantity_index_offset() + 32 * 2>(source);
}
-constexpr int present_bits() { return 1; }
-constexpr int present_offset() { return valid_offset() + valid_bits(); }
-void present_pack(bool present, gsl::span<char> destination) {
- aos::PackBits<uint32_t, present_bits(), present_offset()>(present,
- destination);
-}
-bool present_unpack(gsl::span<const char> source) {
- return aos::UnpackBits<uint32_t, present_bits(), present_offset()>(source);
-}
-
-constexpr int next_offset() { return present_offset() + present_bits(); }
+constexpr int next_offset() { return quantity_index_offset() + 2; }
static_assert(next_offset() <= 32, "Target is too big");
} // namespace
@@ -138,14 +142,17 @@
SpiTransfer transfer;
gsl::span<char> remaining_space = transfer;
for (int frame = 0; frame < 3; ++frame) {
- for (int target = 0; target < 3; ++target) {
- remaining_space[0] = 0;
- remaining_space[1] = 0;
- remaining_space[2] = 0;
- remaining_space[3] = 0;
+ // Zero out all three targets and the age.
+ for (int i = 0; i < 3 * 4 + 1; ++i) {
+ remaining_space[i] = 0;
+ }
- if (static_cast<int>(message.frames.size()) > frame) {
- valid_pack(true, remaining_space);
+ if (static_cast<int>(message.frames.size()) > frame) {
+ camera_index_pack(message.frames[frame].camera_index + 1,
+ remaining_space);
+ target_count_pack(message.frames[frame].targets.size(), remaining_space);
+
+ for (int target = 0; target < 3; ++target) {
if (static_cast<int>(message.frames[frame].targets.size()) > target) {
heading_pack(message.frames[frame].targets[target].heading,
remaining_space);
@@ -155,23 +162,16 @@
remaining_space);
height_pack(message.frames[frame].targets[target].height,
remaining_space);
- present_pack(true, remaining_space);
- } else {
- present_pack(false, remaining_space);
}
- } else {
- valid_pack(false, remaining_space);
+ remaining_space = remaining_space.subspan(4);
}
- remaining_space = remaining_space.subspan(4);
- }
- if (static_cast<int>(message.frames.size()) > frame) {
const uint8_t age_count = message.frames[frame].age.count();
memcpy(&remaining_space[0], &age_count, 1);
+ remaining_space = remaining_space.subspan(1);
} else {
- remaining_space[0] = 0;
+ remaining_space = remaining_space.subspan(4 * 3 + 1);
}
- remaining_space = remaining_space.subspan(1);
}
{
uint16_t crc = jevois_crc_init();
@@ -191,13 +191,14 @@
TeensyToRoborio message;
gsl::span<const char> remaining_input = transfer;
for (int frame = 0; frame < 3; ++frame) {
- const bool have_frame = valid_unpack(remaining_input);
- if (have_frame) {
+ const int camera_index_plus = camera_index_unpack(remaining_input);
+ if (camera_index_plus > 0) {
message.frames.push_back({});
- }
- for (int target = 0; target < 3; ++target) {
- if (present_unpack(remaining_input)) {
- if (have_frame) {
+ message.frames.back().camera_index = camera_index_plus - 1;
+
+ const int target_count = target_count_unpack(remaining_input);
+ for (int target = 0; target < 3; ++target) {
+ if (target < target_count) {
message.frames.back().targets.push_back({});
message.frames.back().targets.back().heading =
heading_unpack(remaining_input);
@@ -208,16 +209,18 @@
message.frames.back().targets.back().height =
height_unpack(remaining_input);
}
+ remaining_input = remaining_input.subspan(4);
}
- remaining_input = remaining_input.subspan(4);
+ {
+ uint8_t age_count;
+ memcpy(&age_count, &remaining_input[0], 1);
+ message.frames.back().age = camera_duration(age_count);
+ }
+ remaining_input = remaining_input.subspan(1);
+ } else {
+ remaining_input = remaining_input.subspan(4 * 3 + 1);
}
- if (have_frame) {
- uint8_t age_count;
- memcpy(&age_count, &remaining_input[0], 1);
- message.frames.back().age = camera_duration(age_count);
- }
- remaining_input = remaining_input.subspan(1);
}
{
uint16_t calculated_crc = jevois_crc_init();
diff --git a/y2019/jevois/spi_test.cc b/y2019/jevois/spi_test.cc
index 32551db..84938f0 100644
--- a/y2019/jevois/spi_test.cc
+++ b/y2019/jevois/spi_test.cc
@@ -60,10 +60,13 @@
TeensyToRoborio input_message;
input_message.frames.push_back({});
input_message.frames.back().age = camera_duration(9);
+ input_message.frames.back().camera_index = 2;
input_message.frames.push_back({});
input_message.frames.back().age = camera_duration(7);
+ input_message.frames.back().camera_index = 5;
input_message.frames.push_back({});
input_message.frames.back().age = camera_duration(1);
+ input_message.frames.back().camera_index = 4;
const SpiTransfer transfer = SpiPackToRoborio(input_message);
const auto output_message = SpiUnpackToRoborio(transfer);
@@ -80,13 +83,16 @@
input_message.frames.back().targets.back().height = 1;
input_message.frames.back().targets.back().heading = 0.5;
input_message.frames.back().targets.back().skew = -0.5;
+ input_message.frames.back().camera_index = 0;
input_message.frames.push_back({});
input_message.frames.back().targets.push_back({});
input_message.frames.back().targets.push_back({});
+ input_message.frames.back().camera_index = 2;
input_message.frames.push_back({});
input_message.frames.back().targets.push_back({});
input_message.frames.back().targets.push_back({});
input_message.frames.back().targets.push_back({});
+ input_message.frames.back().camera_index = 3;
const SpiTransfer transfer = SpiPackToRoborio(input_message);
const auto output_message = SpiUnpackToRoborio(transfer);
@@ -103,6 +109,93 @@
output_message->frames.back().targets.back().heading, 0.1);
EXPECT_NEAR(input_message.frames.back().targets.back().skew,
output_message->frames.back().targets.back().skew, 0.1);
+ for (int i = 0; i < 3; ++i) {
+ EXPECT_EQ(input_message.frames[i].camera_index,
+ output_message->frames[i].camera_index);
+ }
+}
+
+// Tests that packing and unpacking two targets results in the same number on
+// the other side.
+TEST(SpiToRoborioPackTest, TwoTargets) {
+ TeensyToRoborio input_message;
+ input_message.frames.push_back({});
+ input_message.frames.back().targets.push_back({});
+ input_message.frames.back().targets.back().distance = 9;
+ input_message.frames.back().targets.back().height = 1;
+ input_message.frames.back().targets.back().heading = 0.5;
+ input_message.frames.back().targets.back().skew = -0.5;
+ input_message.frames.back().targets.push_back({});
+ input_message.frames.back().targets.back().distance = 1;
+ input_message.frames.back().targets.back().height = 0.9;
+ input_message.frames.back().targets.back().heading = 0.4;
+ input_message.frames.back().targets.back().skew = -0.4;
+ input_message.frames.back().age = camera_duration(9);
+ input_message.frames.back().camera_index = 2;
+
+ const SpiTransfer transfer = SpiPackToRoborio(input_message);
+ const auto output_message = SpiUnpackToRoborio(transfer);
+ ASSERT_TRUE(output_message);
+ ASSERT_EQ(1u, output_message->frames.size());
+ ASSERT_EQ(2u, output_message->frames[0].targets.size());
+ for (int i = 0; i < 2; ++i) {
+ EXPECT_NEAR(input_message.frames.back().targets[i].distance,
+ output_message->frames.back().targets[i].distance, 0.1);
+ EXPECT_NEAR(input_message.frames.back().targets[i].height,
+ output_message->frames.back().targets[i].height, 0.1);
+ EXPECT_NEAR(input_message.frames.back().targets[i].heading,
+ output_message->frames.back().targets[i].heading, 0.1);
+ EXPECT_NEAR(input_message.frames.back().targets[i].skew,
+ output_message->frames.back().targets[i].skew, 0.1);
+ EXPECT_EQ(input_message.frames.back().age,
+ output_message->frames.back().age);
+ EXPECT_EQ(input_message.frames.back().camera_index,
+ output_message->frames.back().camera_index);
+ }
+}
+
+// Tests that packing and unpacking three targets results in the same number on
+// the other side.
+TEST(SpiToRoborioPackTest, ThreeTargets) {
+ TeensyToRoborio input_message;
+ input_message.frames.push_back({});
+ input_message.frames.back().targets.push_back({});
+ input_message.frames.back().targets.back().distance = 9;
+ input_message.frames.back().targets.back().height = 1;
+ input_message.frames.back().targets.back().heading = 0.5;
+ input_message.frames.back().targets.back().skew = -0.5;
+ input_message.frames.back().targets.push_back({});
+ input_message.frames.back().targets.back().distance = 1;
+ input_message.frames.back().targets.back().height = 0.9;
+ input_message.frames.back().targets.back().heading = 0.4;
+ input_message.frames.back().targets.back().skew = -0.4;
+ input_message.frames.back().targets.push_back({});
+ input_message.frames.back().targets.back().distance = 2;
+ input_message.frames.back().targets.back().height = 0.7;
+ input_message.frames.back().targets.back().heading = 0.3;
+ input_message.frames.back().targets.back().skew = -0.3;
+ input_message.frames.back().age = camera_duration(1);
+ input_message.frames.back().camera_index = 1;
+
+ const SpiTransfer transfer = SpiPackToRoborio(input_message);
+ const auto output_message = SpiUnpackToRoborio(transfer);
+ ASSERT_TRUE(output_message);
+ ASSERT_EQ(1u, output_message->frames.size());
+ ASSERT_EQ(3u, output_message->frames[0].targets.size());
+ for (int i = 0; i < 3; ++i) {
+ EXPECT_NEAR(input_message.frames.back().targets[i].distance,
+ output_message->frames.back().targets[i].distance, 0.1);
+ EXPECT_NEAR(input_message.frames.back().targets[i].height,
+ output_message->frames.back().targets[i].height, 0.1);
+ EXPECT_NEAR(input_message.frames.back().targets[i].heading,
+ output_message->frames.back().targets[i].heading, 0.1);
+ EXPECT_NEAR(input_message.frames.back().targets[i].skew,
+ output_message->frames.back().targets[i].skew, 0.1);
+ EXPECT_EQ(input_message.frames.back().age,
+ output_message->frames.back().age);
+ EXPECT_EQ(input_message.frames.back().camera_index,
+ output_message->frames.back().camera_index);
+ }
}
// Tests packing and then unpacking an empty message.
diff --git a/y2019/jevois/structures.h b/y2019/jevois/structures.h
index 1f10dc0..b6db24e 100644
--- a/y2019/jevois/structures.h
+++ b/y2019/jevois/structures.h
@@ -85,8 +85,8 @@
// The information extracted from a single camera frame.
//
// This is all the information sent from each camera to the Teensy.
-struct Frame {
- bool operator==(const Frame &other) const {
+struct CameraFrame {
+ bool operator==(const CameraFrame &other) const {
if (other.targets != targets) {
return false;
}
@@ -95,7 +95,7 @@
}
return true;
}
- bool operator!=(const Frame &other) const {
+ bool operator!=(const CameraFrame &other) const {
return !(*this == other);
}
@@ -106,6 +106,34 @@
camera_duration age;
};
+// The information extracted from a single camera frame, from a given camera.
+struct RoborioFrame {
+ bool operator==(const RoborioFrame &other) const {
+ if (other.targets != targets) {
+ return false;
+ }
+ if (other.age != age) {
+ return false;
+ }
+ if (other.camera_index != camera_index) {
+ return false;
+ }
+ return true;
+ }
+ bool operator!=(const RoborioFrame &other) const {
+ return !(*this == other);
+ }
+
+ // The top most interesting targets found in this frame.
+ aos::SizedArray<Target, 3> targets;
+
+ // How long ago from the current time this frame was captured.
+ camera_duration age;
+ // Which camera this is from (which position on the robot, not a serial
+ // number).
+ int camera_index;
+};
+
enum class CameraCommand : char {
// Stay in normal mode.
kNormal,
@@ -170,7 +198,7 @@
// The newest frames received from up to three cameras. These will be the
// three earliest-received of all buffered frames.
- aos::SizedArray<Frame, 3> frames;
+ aos::SizedArray<RoborioFrame, 3> frames;
};
// This is all the information the RoboRIO sends to the Teensy.
@@ -182,6 +210,12 @@
if (other.light_rings != light_rings) {
return false;
}
+ if (other.realtime_now != realtime_now) {
+ return false;
+ }
+ if (other.camera_command != camera_command) {
+ return false;
+ }
return true;
}
bool operator!=(const RoborioToTeensy &other) const {
diff --git a/y2019/jevois/teensy.cc b/y2019/jevois/teensy.cc
index 539d7b6..84b98b0 100644
--- a/y2019/jevois/teensy.cc
+++ b/y2019/jevois/teensy.cc
@@ -269,9 +269,10 @@
FrameQueue(const FrameQueue &) = delete;
FrameQueue &operator=(const FrameQueue &) = delete;
- void UpdateFrame(int camera, const Frame &frame) {
+ void UpdateFrame(int camera, const CameraFrame &frame) {
frames_[camera].targets = frame.targets;
frames_[camera].capture_time = aos::monotonic_clock::now() - frame.age;
+ frames_[camera].camera_index = camera;
const aos::SizedArray<int, 3> old_last_frames = last_frames_;
last_frames_.clear();
for (int index : old_last_frames) {
@@ -302,6 +303,7 @@
aos::SizedArray<Target, 3> targets;
aos::monotonic_clock::time_point capture_time =
aos::monotonic_clock::min_time;
+ int camera_index;
};
std::array<FrameData, 5> frames_;
@@ -327,7 +329,7 @@
const FrameData &frame = frames_[index];
const auto age = aos::monotonic_clock::now() - frame.capture_time;
const auto rounded_age = aos::time::round<camera_duration>(age);
- message.frames.push_back({frame.targets, rounded_age});
+ message.frames.push_back({frame.targets, rounded_age, frame.camera_index});
last_frames_.push_back(index);
}
return SpiPackToRoborio(message);
@@ -658,7 +660,6 @@
UartUnpackToTeensy(packetizers[i].received_packet());
packetizers[i].clear_received_packet();
if (decoded) {
- printf("got one with %d\n", (int)decoded->targets.size());
frame_queue.UpdateFrame(i, *decoded);
}
}
diff --git a/y2019/jevois/uart.cc b/y2019/jevois/uart.cc
index 3621da8..63138a8 100644
--- a/y2019/jevois/uart.cc
+++ b/y2019/jevois/uart.cc
@@ -15,7 +15,7 @@
namespace frc971 {
namespace jevois {
-UartToTeensyBuffer UartPackToTeensy(const Frame &message) {
+UartToTeensyBuffer UartPackToTeensy(const CameraFrame &message) {
std::array<char, uart_to_teensy_size()> buffer;
gsl::span<char> remaining_space = buffer;
remaining_space[0] = message.targets.size();
@@ -55,7 +55,7 @@
return result;
}
-tl::optional<Frame> UartUnpackToTeensy(gsl::span<const char> encoded_buffer) {
+tl::optional<CameraFrame> UartUnpackToTeensy(gsl::span<const char> encoded_buffer) {
std::array<char, uart_to_teensy_size()> buffer;
if (static_cast<size_t>(
CobsDecode<uart_to_teensy_size()>(encoded_buffer, &buffer).size()) !=
@@ -63,7 +63,7 @@
return tl::nullopt;
}
- Frame message;
+ CameraFrame message;
gsl::span<const char> remaining_input = buffer;
const int number_targets = remaining_input[0];
remaining_input = remaining_input.subspan(1);
diff --git a/y2019/jevois/uart.h b/y2019/jevois/uart.h
index 2a4acec..b9e784b 100644
--- a/y2019/jevois/uart.h
+++ b/y2019/jevois/uart.h
@@ -29,8 +29,8 @@
using UartToCameraBuffer =
aos::SizedArray<char, CobsMaxEncodedSize(uart_to_camera_size())>;
-UartToTeensyBuffer UartPackToTeensy(const Frame &message);
-tl::optional<Frame> UartUnpackToTeensy(gsl::span<const char> buffer);
+UartToTeensyBuffer UartPackToTeensy(const CameraFrame &message);
+tl::optional<CameraFrame> UartUnpackToTeensy(gsl::span<const char> buffer);
UartToCameraBuffer UartPackToCamera(const CameraCalibration &message);
tl::optional<CameraCalibration> UartUnpackToCamera(
diff --git a/y2019/jevois/uart_test.cc b/y2019/jevois/uart_test.cc
index d669688..b8f25c1 100644
--- a/y2019/jevois/uart_test.cc
+++ b/y2019/jevois/uart_test.cc
@@ -10,7 +10,7 @@
// Tests packing and then unpacking a message with arbitrary values.
TEST(UartToTeensyTest, Basic) {
- Frame input_message;
+ CameraFrame input_message;
for (int i = 0; i < 3; ++i) {
input_message.targets.push_back({});
Target *const target = &input_message.targets.back();
@@ -29,7 +29,7 @@
// Tests packing and then unpacking a message with arbitrary values and no
// frames.
TEST(UartToTeensyTest, NoFrames) {
- Frame input_message;
+ CameraFrame input_message;
input_message.age = camera_duration(123);
const UartToTeensyBuffer buffer = UartPackToTeensy(input_message);
const auto output_message = UartUnpackToTeensy(buffer);
@@ -39,7 +39,7 @@
// Tests packing and then unpacking a message with just one frame.
TEST(UartToTeensyTest, OneFrame) {
- Frame input_message;
+ CameraFrame input_message;
{
input_message.targets.push_back({});
Target *const target = &input_message.targets.back();
@@ -75,7 +75,7 @@
// Tests that corrupting the data in various ways is handled properly.
TEST(UartToTeensyTest, CorruptData) {
- Frame input_message{};
+ CameraFrame input_message{};
{
UartToTeensyBuffer buffer = UartPackToTeensy(input_message);
buffer[0]++;
diff --git a/y2019/vision/target_sender.cc b/y2019/vision/target_sender.cc
index ad42ce4..5f2afb9 100644
--- a/y2019/vision/target_sender.cc
+++ b/y2019/vision/target_sender.cc
@@ -334,7 +334,7 @@
// TODO: Select top 3 (randomly?)
- frc971::jevois::Frame frame{};
+ frc971::jevois::CameraFrame frame{};
for (size_t i = 0; i < results.size() && i < frame.targets.max_size();
++i) {
diff --git a/y2019/wpilib_interface.cc b/y2019/wpilib_interface.cc
index 02269eb..564e8e1 100644
--- a/y2019/wpilib_interface.cc
+++ b/y2019/wpilib_interface.cc
@@ -28,6 +28,7 @@
#include "aos/util/log_interval.h"
#include "aos/util/phased_loop.h"
#include "aos/util/wrapping_counter.h"
+#include "ctre/phoenix/motorcontrol/can/TalonSRX.h"
#include "frc971/autonomous/auto.q.h"
#include "frc971/control_loops/drivetrain/drivetrain.q.h"
#include "frc971/wpilib/ADIS16448.h"
@@ -42,8 +43,8 @@
#include "frc971/wpilib/pdp_fetcher.h"
#include "frc971/wpilib/sensor_reader.h"
#include "frc971/wpilib/wpilib_robot_base.h"
-#include "ctre/phoenix/motorcontrol/can/TalonSRX.h"
#include "y2019/constants.h"
+#include "y2019/control_loops/drivetrain/camera.q.h"
#include "y2019/control_loops/superstructure/superstructure.q.h"
#include "y2019/jevois/spi.h"
@@ -319,7 +320,22 @@
return;
}
- // TODO(Brian): Do something useful with the targets.
+ const auto now = aos::monotonic_clock::now();
+ for (const auto &received : unpacked->frames) {
+ auto to_send = control_loops::drivetrain::camera_frames.MakeMessage();
+ to_send->timestamp =
+ std::chrono::nanoseconds((now + received.age).time_since_epoch())
+ .count();
+ to_send->num_targets = received.targets.size();
+ for (size_t i = 0; i < received.targets.size(); ++i) {
+ to_send->targets[i].distance = received.targets[i].distance;
+ to_send->targets[i].height = received.targets[i].height;
+ to_send->targets[i].heading = received.targets[i].heading;
+ to_send->targets[i].skew = received.targets[i].skew;
+ }
+ to_send->camera = received.camera_index;
+ to_send.Send();
+ }
if (dummy_spi_) {
uint8_t dummy_send, dummy_receive;