| // 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 |