blob: 4ae7fb03b688014add0012c0572d2d7f9d5270a5 [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_display/display.h"
#include <stdio.h>
#include <stdlib.h>
#include <cinttypes>
#include <cstdint>
#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_color/color.h"
#include "pw_digital_io_pico/digital_io.h"
#include "pw_display_driver_ili9341/display_driver.h"
#include "pw_framebuffer/rgb565.h"
#include "pw_log/log.h"
#include "pw_spi_pico/chip_selector.h"
#include "pw_spi_pico/initiator.h"
#include "pw_spin_delay/delay.h"
#include "pw_sync/borrow.h"
#include "pw_sync/mutex.h"
using pw::display_driver::DisplayDriverILI9341;
namespace pw::display {
namespace {
// Pico spi0 Pins
#define SPI_PORT spi0
constexpr int TFT_SCLK = 18; // SPI0 SCK
constexpr int TFT_MOSI = 19; // SPI0 TX
// Unused
// const 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 int kDisplayWidth = 320;
constexpr int kDisplayHeight = 240;
constexpr int kNumDisplayPixels = kDisplayWidth * kDisplayHeight;
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(3),
.bit_order = pw::spi::BitOrder::kMsbFirst,
};
class InstanceData {
public:
InstanceData()
: 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_) {}
void Init() {
InitGPIO();
InitSPI();
InitDisplayDriver();
}
void Update(pw::framebuffer::FramebufferRgb565* frame_buffer) {
display_driver_.Update(frame_buffer);
}
void UpdatePixelDouble(pw::framebuffer::FramebufferRgb565* frame_buffer) {
display_driver_.UpdatePixelDouble(frame_buffer);
}
private:
void InitGPIO() {
stdio_init_all();
chip_selector_gpio_.Enable();
data_cmd_gpio_.Enable();
reset_gpio_.Enable();
}
void InitSPI() {
uint actual_baudrate = spi_init(SPI_PORT, kBaudRate);
PW_LOG_DEBUG("Actual Baudrate: %u", actual_baudrate);
spi_set_format(SPI_PORT, 8, SPI_CPOL_1, SPI_CPHA_1, SPI_MSB_FIRST);
// 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 InitDisplayDriver() {
auto s = display_driver_.Init();
if (!s.ok())
return s;
// From hereon only display pixel updates are made, so switch to 16-bit
// mode which is expected by DisplayDriver::Update();
spi_set_format(SPI_PORT, 16, SPI_CPOL_1, SPI_CPHA_1, SPI_MSB_FIRST);
return OkStatus();
}
pw::digital_io::PicoDigitalOut chip_selector_gpio_;
pw::digital_io::PicoDigitalOut data_cmd_gpio_;
pw::digital_io::PicoDigitalOut reset_gpio_;
pw::spi::PicoChipSelector spi_chip_selector_;
pw::spi::PicoInitiator spi_initiator_;
pw::sync::VirtualMutex spi_initiator_mutex_;
pw::sync::Borrowable<pw::spi::Initiator> borrowable_spi_initiator_;
pw::spi::Device spi_device_;
DisplayDriverILI9341::Config driver_config_;
DisplayDriverILI9341 display_driver_;
}; // namespace
InstanceData s_instance_data;
} // namespace
void Init() { s_instance_data.Init(); }
int GetWidth() { return kDisplayWidth; }
int GetHeight() { return kDisplayHeight; }
void Update(pw::framebuffer::FramebufferRgb565* frame_buffer) {
s_instance_data.Update(frame_buffer);
}
void UpdatePixelDouble(pw::framebuffer::FramebufferRgb565* frame_buffer) {
s_instance_data.UpdatePixelDouble(frame_buffer);
}
bool TouchscreenAvailable() { return false; }
bool NewTouchEvent() { return false; }
pw::coordinates::Vec3Int GetTouchPoint() {
return pw::coordinates::Vec3Int{0, 0, 0};
}
} // namespace pw::display