| // 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_stm32cube/initiator.h" |
| |
| #include <algorithm> |
| |
| #include "pw_log/log.h" |
| #include "pw_status/try.h" |
| #include "stm32cube/stm32cube.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_UNREACHABLE; |
| 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_UNREACHABLE; |
| 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_UNREACHABLE; |
| 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_UNREACHABLE; |
| return SPI_POLARITY_HIGH; |
| } |
| |
| } // namespace |
| |
| Stm32CubeInitiator::Stm32CubeInitiator() |
| : 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}} {} |
| |
| Stm32CubeInitiator::~Stm32CubeInitiator() = default; |
| |
| Status Stm32CubeInitiator::LazyInit() { |
| if (initialized) |
| return init_status; |
| init_status = InitSPI(); |
| initialized = true; |
| PW_LOG_INFO("Stm32CubeInitiator::LazyInit: %s", init_status.str()); |
| return init_status; |
| } |
| |
| Status Stm32CubeInitiator::InitSPI() { |
| auto s = HAL_SPI_Init(&spi_handle); |
| auto status = ConvertStatus(s); |
| PW_LOG_INFO("HAL_SPI_Init =>: %s", status.str()); |
| return status; |
| } |
| |
| Status Stm32CubeInitiator::Configure(const Config& config) { |
| spi_handle.Init.DataSize = GetDataSize(config.bits_per_word); |
| spi_handle.Init.FirstBit = GetBitOrder(config.bit_order); |
| spi_handle.Init.CLKPhase = GetPhase(config.phase); |
| spi_handle.Init.CLKPolarity = GetPolarity(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( |
| &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(&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(&spi_handle, |
| reinterpret_cast<uint8_t*>(read_buffer.data()), |
| read_buffer.size(), |
| kTimeout); |
| } |
| |
| return ConvertStatus(status); |
| } |
| |
| } // namespace pw::spi |