blob: e3a4ce71fb5b178c607649124cc1db508925000c [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 <stdio.h>
#include <stdlib.h>
#include <cinttypes>
#include <cstdint>
#include "pw_display/display_backend.h"
#define LIB_CMSIS_CORE 0
#define LIB_PICO_STDIO_USB 0
#define LIB_PICO_STDIO_SEMIHOSTING 0
#include "hardware/gpio.h"
#include "pico/stdlib.h"
#include "pw_log/log.h"
namespace pw::display::backend {
namespace {
// Pico spi0 Pins
#define SPI_PORT spi0
constexpr int TFT_SCLK = 18; // SPI0 SCK
constexpr int TFT_MOSI = 19; // SPI0 TX
// Unused
// constexpr int TFT_MISO = 4; // SPI0 RX
constexpr int TFT_CS = 9; // SPI0 CSn
constexpr int TFT_DC = 10; // GP10
constexpr int TFT_RST = 11; // GP11
constexpr uint32_t kBaudRate = 31'250'000;
constexpr pw::spi::Config kSpiConfig{
.polarity = pw::spi::ClockPolarity::kActiveHigh,
.phase = pw::spi::ClockPhase::kFallingEdge,
.bits_per_word = pw::spi::BitsPerWord(8),
.bit_order = pw::spi::BitOrder::kMsbFirst,
};
} // namespace
Display::Display()
: chip_selector_gpio_(TFT_CS),
data_cmd_gpio_(TFT_DC),
reset_gpio_(TFT_RST),
spi_chip_selector_(chip_selector_gpio_),
spi_initiator_(SPI_PORT, kBaudRate),
borrowable_spi_initiator_(spi_initiator_, spi_initiator_mutex_),
spi_device_(borrowable_spi_initiator_, kSpiConfig, spi_chip_selector_),
driver_config_{
.data_cmd_gpio = data_cmd_gpio_,
.reset_gpio = &reset_gpio_,
.spi_device = spi_device_,
},
display_driver_(driver_config_) {}
Display::~Display() = default;
Status Display::Init() {
InitGPIO();
InitSPI();
return InitDisplayDriver();
}
void Display::Update(pw::framebuffer::FramebufferRgb565& frame_buffer) {
display_driver_.Update(&frame_buffer).IgnoreError();
}
void Display::UpdatePixelDouble(
pw::framebuffer::FramebufferRgb565* frame_buffer) {
display_driver_.UpdatePixelDouble(frame_buffer).IgnoreError();
}
Status Display::InitFramebuffer(
pw::framebuffer::FramebufferRgb565* framebuffer) {
framebuffer->SetFramebufferData(framebuffer_data_,
kDisplayWidth,
kDisplayHeight,
kDisplayWidth * sizeof(uint16_t));
return OkStatus();
}
void Display::InitGPIO() {
stdio_init_all();
// TODO: This should be a facade
setup_default_uart();
chip_selector_gpio_.Enable();
data_cmd_gpio_.Enable();
reset_gpio_.Enable();
}
void Display::InitSPI() {
uint actual_baudrate = spi_init(SPI_PORT, kBaudRate);
PW_LOG_DEBUG("Actual Baudrate: %u", actual_baudrate);
// Not currently used (not yet reading from display).
// gpio_set_function(TFT_MISO, GPIO_FUNC_SPI);
gpio_set_function(TFT_SCLK, GPIO_FUNC_SPI);
gpio_set_function(TFT_MOSI, GPIO_FUNC_SPI);
}
Status Display::InitDisplayDriver() {
auto s = display_driver_.Init();
if (!s.ok())
return s;
// From hereafter only display pixel updates are made, so switch to 16-bit
// mode which is expected by DisplayDriver::Update();
// TODO(b/251033990): Switch to pw_spi way to change word size.
spi_initiator_.SetOverrideBitsPerWord(pw::spi::BitsPerWord(16));
return OkStatus();
}
} // namespace pw::display::backend