blob: 877615804738d14071ffd9846051abd5c3029f4d [file] [log] [blame]
// 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