| // 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 <cstdint> |
| |
| #include "app_common/common.h" |
| #include "pw_digital_io_stm32cube/digital_io.h" |
| #include "pw_display_driver_ili9341/display_driver.h" |
| #include "pw_spi_stm32cube/chip_selector.h" |
| #include "pw_spi_stm32cube/initiator.h" |
| #include "pw_sync/borrow.h" |
| #include "pw_sync/mutex.h" |
| |
| using pw::Status; |
| using pw::digital_io::Stm32CubeDigitalOut; |
| using pw::display::Display; |
| using pw::display_driver::DisplayDriverILI9341; |
| using pw::framebuffer::PixelFormat; |
| using pw::framebuffer_pool::FramebufferPool; |
| using pw::spi::Device; |
| using pw::spi::Initiator; |
| using pw::spi::Stm32CubeChipSelector; |
| using pw::spi::Stm32CubeInitiator; |
| using pw::sync::Borrowable; |
| using pw::sync::VirtualMutex; |
| |
| namespace { |
| |
| #define _CAT(A, B) A##B |
| #define CAT(A, B) _CAT(A, B) |
| |
| #define LCD_CS_PORT CAT(GPIO, LCD_CS_PORT_CHAR) |
| #define LCD_CS_PIN CAT(GPIO_PIN_, LCD_CS_PIN_NUM) |
| |
| #define LCD_DC_PORT CAT(GPIO, LCD_DC_PORT_CHAR) |
| #define LCD_DC_PIN CAT(GPIO_PIN_, LCD_DC_PIN_NUM) |
| |
| struct SpiValues { |
| SpiValues(pw::spi::Config config, |
| pw::spi::ChipSelector& selector, |
| pw::sync::VirtualMutex& initiator_mutex); |
| |
| pw::spi::Stm32CubeInitiator initiator; |
| pw::sync::Borrowable<pw::spi::Initiator> borrowable_initiator; |
| pw::spi::Device device; |
| }; |
| |
| static_assert(DISPLAY_WIDTH > 0); |
| static_assert(DISPLAY_HEIGHT > 0); |
| |
| constexpr uint16_t kFramebufferWidth = |
| FRAMEBUFFER_WIDTH >= 0 ? FRAMEBUFFER_WIDTH : DISPLAY_WIDTH; |
| constexpr uint16_t kFramebufferHeight = DISPLAY_HEIGHT; |
| |
| constexpr size_t kNumPixels = kFramebufferWidth * kFramebufferHeight; |
| constexpr uint16_t kDisplayRowBytes = sizeof(uint16_t) * kFramebufferWidth; |
| constexpr pw::geometry::Size<uint16_t> kDisplaySize = {DISPLAY_WIDTH, |
| DISPLAY_HEIGHT}; |
| constexpr pw::spi::Config kSpiConfig8Bit{ |
| .polarity = pw::spi::ClockPolarity::kActiveHigh, |
| .phase = pw::spi::ClockPhase::kFallingEdge, |
| .bits_per_word = pw::spi::BitsPerWord(8), |
| .bit_order = pw::spi::BitOrder::kMsbFirst, |
| }; |
| constexpr pw::spi::Config kSpiConfig16Bit{ |
| .polarity = pw::spi::ClockPolarity::kActiveHigh, |
| .phase = pw::spi::ClockPhase::kFallingEdge, |
| .bits_per_word = pw::spi::BitsPerWord(16), |
| .bit_order = pw::spi::BitOrder::kMsbFirst, |
| }; |
| |
| Stm32CubeDigitalOut s_display_dc_pin({ |
| .port = LCD_DC_PORT, |
| .pin = LCD_DC_PIN, |
| .polarity = pw::digital_io::Polarity::kActiveHigh, |
| }); |
| Stm32CubeDigitalOut s_display_cs_pin({ |
| .port = LCD_CS_PORT, |
| .pin = LCD_CS_PIN, |
| .polarity = pw::digital_io::Polarity::kActiveLow, |
| }); |
| Stm32CubeChipSelector s_spi_chip_selector(s_display_cs_pin); |
| Stm32CubeInitiator s_spi_initiator; |
| VirtualMutex s_spi_initiator_mutex; |
| Borrowable<Initiator> s_borrowable_spi_initiator(s_spi_initiator, |
| s_spi_initiator_mutex); |
| SpiValues s_spi_8_bit(kSpiConfig8Bit, |
| s_spi_chip_selector, |
| s_spi_initiator_mutex); |
| SpiValues s_spi_16_bit(kSpiConfig16Bit, |
| s_spi_chip_selector, |
| s_spi_initiator_mutex); |
| uint16_t s_pixel_data[kNumPixels]; |
| const pw::Vector<void*, 1> s_pixel_buffers{s_pixel_data}; |
| pw::framebuffer_pool::FramebufferPool s_fb_pool({ |
| .fb_addr = s_pixel_buffers, |
| .dimensions = {kFramebufferWidth, kFramebufferHeight}, |
| .row_bytes = kDisplayRowBytes, |
| .pixel_format = PixelFormat::RGB565, |
| }); |
| DisplayDriverILI9341 s_display_driver({ |
| .data_cmd_gpio = s_display_dc_pin, |
| .reset_gpio = nullptr, |
| .spi_device_8_bit = s_spi_8_bit.device, |
| .spi_device_16_bit = s_spi_16_bit.device, |
| }); |
| Display s_display(s_display_driver, kDisplaySize, s_fb_pool); |
| |
| SpiValues::SpiValues(pw::spi::Config config, |
| pw::spi::ChipSelector& selector, |
| pw::sync::VirtualMutex& initiator_mutex) |
| : borrowable_initiator(initiator, initiator_mutex), |
| device(borrowable_initiator, config, selector) {} |
| |
| void InitSPIPins() { |
| // SPI5 GPIO Configuration: |
| // PF7 SPI5_SCK |
| // PF8 SPI5_MISO |
| // PF9 SPI5_MOSI |
| GPIO_InitTypeDef spi_pin_config = { |
| .Pin = GPIO_PIN_7 | GPIO_PIN_8 | GPIO_PIN_9, |
| .Mode = GPIO_MODE_AF_PP, |
| .Pull = GPIO_NOPULL, |
| .Speed = GPIO_SPEED_FREQ_VERY_HIGH, |
| .Alternate = GPIO_AF5_SPI5, |
| }; |
| HAL_GPIO_Init(GPIOF, &spi_pin_config); |
| } |
| |
| } // namespace |
| |
| // static |
| Status Common::Init() { |
| __HAL_RCC_GPIOA_CLK_ENABLE(); |
| __HAL_RCC_GPIOB_CLK_ENABLE(); |
| __HAL_RCC_GPIOC_CLK_ENABLE(); |
| __HAL_RCC_GPIOD_CLK_ENABLE(); |
| __HAL_RCC_GPIOE_CLK_ENABLE(); |
| __HAL_RCC_GPIOF_CLK_ENABLE(); |
| __HAL_RCC_GPIOG_CLK_ENABLE(); |
| __HAL_RCC_GPIOH_CLK_ENABLE(); |
| |
| __HAL_RCC_SPI5_CLK_ENABLE(); |
| |
| s_display_cs_pin.Enable(); |
| s_display_dc_pin.Enable(); |
| |
| InitSPIPins(); |
| |
| return s_display_driver.Init(); |
| } |
| |
| // static |
| pw::display::Display& Common::GetDisplay() { return s_display; } |