esp-vfd-clock/firmware/main/vfd_driver.cpp

180 lines
5.5 KiB
C++

//
// Created by erki on 17/02/24.
//
#include "vfd_driver.hpp"
#include <cstring>
#include <esp_clk_tree.h>
#include <esp_log.h>
#include <utility_bytes.hpp>
namespace
{
template<typename T>
constexpr T bitValue(const T& shift)
{
return T(T(1) << shift);
}
constexpr auto SEG_A = bitValue<std::uint32_t>(0);
constexpr auto SEG_B = bitValue<std::uint32_t>(1);
constexpr auto SEG_C = bitValue<std::uint32_t>(2);
constexpr auto SEG_D = bitValue<std::uint32_t>(3);
constexpr auto SEG_E = bitValue<std::uint32_t>(4);
constexpr auto SEG_F = bitValue<std::uint32_t>(5);
constexpr auto SEG_G = bitValue<std::uint32_t>(6);
constexpr auto SEG_DEGREE = bitValue<std::uint32_t>(7);
constexpr auto SEG_MINUS = SEG_G;
constexpr auto SEG_DOT = bitValue<std::uint32_t>(7);
constexpr auto GRID_0 = bitValue<std::uint32_t>(8);
constexpr auto GRID_1 = bitValue<std::uint32_t>(9);
constexpr auto GRID_2 = bitValue<std::uint32_t>(10);
constexpr auto GRID_3 = bitValue<std::uint32_t>(11);
constexpr auto GRID_4 = bitValue<std::uint32_t>(12);
constexpr auto GRID_5 = bitValue<std::uint32_t>(13);
constexpr auto GRID_6 = bitValue<std::uint32_t>(14);
constexpr auto GRID_7 = bitValue<std::uint32_t>(15);
constexpr auto GRID_8 = bitValue<std::uint32_t>(16);
constexpr auto DIGIT_0 = SEG_A | SEG_B | SEG_C | SEG_D | SEG_E | SEG_F;
constexpr auto DIGIT_1 = SEG_B | SEG_C;
constexpr auto DIGIT_2 = SEG_A | SEG_B | SEG_D | SEG_E | SEG_G;
constexpr auto DIGIT_3 = SEG_A | SEG_B | SEG_C | SEG_D | SEG_G;
constexpr auto DIGIT_4 = SEG_B | SEG_C | SEG_F | SEG_G;
constexpr auto DIGIT_5 = SEG_A | SEG_C | SEG_D | SEG_F | SEG_G;
constexpr auto DIGIT_6 = SEG_A | SEG_C | SEG_D | SEG_E | SEG_F | SEG_G;
constexpr auto DIGIT_7 = SEG_A | SEG_B | SEG_C;
constexpr auto DIGIT_8 = SEG_A | SEG_B | SEG_C | SEG_D | SEG_E | SEG_F | SEG_G;
constexpr auto DIGIT_9 = SEG_A | SEG_B | SEG_C | SEG_D | SEG_F | SEG_G;
constexpr auto DIGIT_A = SEG_A | SEG_B | SEG_C | SEG_E | SEG_F | SEG_G;
constexpr auto DIGIT_B = SEG_C | SEG_D | SEG_E | SEG_F | SEG_G;
constexpr auto DIGIT_C = SEG_A | SEG_D | SEG_E | SEG_F;
constexpr auto DIGIT_D = SEG_B | SEG_C | SEG_D | SEG_E | SEG_G;
constexpr auto DIGIT_E = SEG_A | SEG_D | SEG_E | SEG_F | SEG_G;
constexpr auto DIGIT_F = SEG_A | SEG_E | SEG_F | SEG_G;
constexpr auto DIGIT_ALL = SEG_A | SEG_B | SEG_C | SEG_D | SEG_E | SEG_F | SEG_G | SEG_DOT;
}
VfdDriver::VfdDriver(spi_host_device_t spi_host, Hal::Gpio load, Hal::Gpio blank, transaction_cb_t spi_post_up_cb)
: load_(load)
, blank_(blank)
{
auto dev_config = Utility::zeroInitialized<spi_device_interface_config_t>();
dev_config.clock_source = SPI_CLK_SRC_DEFAULT;
dev_config.clock_speed_hz = 800000; // 800kHz
dev_config.spics_io_num = -1;
dev_config.queue_size = 3;
dev_config.pre_cb = nullptr;
dev_config.post_cb = spi_post_up_cb;
ESP_ERROR_CHECK(spi_bus_add_device(spi_host, &dev_config, &spi_device_));
display_data_.fill(0);
}
esp_err_t VfdDriver::defaultInitializeBus(spi_host_device_t spi_host, gpio_num_t clock, gpio_num_t mosi, std::optional<gpio_num_t> miso)
{
spi_bus_config_t bus_config;
bus_config.sclk_io_num = clock;
bus_config.mosi_io_num = mosi;
bus_config.miso_io_num = miso ? *miso : -1;
bus_config.quadwp_io_num = -1;
bus_config.quadhd_io_num = -1;
bus_config.max_transfer_sz = 0;
bus_config.flags = 0;
bus_config.intr_flags = 0;
return spi_bus_initialize(spi_host, &bus_config, SPI_DMA_CH_AUTO);
}
void VfdDriver::update()
{
load_.set(false);
const std::uint32_t raw = getRawCharAndAdvance_();
spiWrite_(raw);
load_.set(true);
}
void VfdDriver::updateNonBlocking()
{
load_.set(false);
const std::uint32_t raw = getRawCharAndAdvance_();
spiWriteNonBlocking_(raw);
}
void VfdDriver::spiWrite_(std::uint32_t raw)
{
std::memcpy(buffer_data_.data(), &raw, buffer_data_.size());
auto transaction = Utility::zeroInitialized<spi_transaction_t>();
transaction.length = DISPLAY_BUFFER_LEN * 8;
transaction.tx_buffer = buffer_data_.data();
transaction.rx_buffer = buffer_data_.data();
spi_device_polling_transmit(spi_device_, &transaction);
}
void VfdDriver::spiWriteNonBlocking_(std::uint32_t raw)
{
std::memcpy(buffer_data_.data(), &raw, buffer_data_.size());
auto transaction = Utility::zeroInitialized<spi_transaction_t>();
transaction.length = DISPLAY_BUFFER_LEN * 8;
transaction.tx_buffer = buffer_data_.data();
transaction.rx_buffer = buffer_data_.data();
spi_device_queue_trans(spi_device_, &transaction, 0);
}
std::uint32_t VfdDriver::getRawCharAndAdvance_()
{
constexpr std::uint32_t grids[] = {
GRID_8, GRID_7, GRID_6, GRID_5, GRID_4, GRID_3, GRID_2, GRID_1, GRID_0
};
auto char_to_code = [](char c) -> std::uint32_t
{
c = std::isalpha(c) ? std::tolower(c) : c;
constexpr std::uint32_t characters[] = {
DIGIT_A, DIGIT_B, DIGIT_C, DIGIT_D, DIGIT_E, DIGIT_F
};
constexpr std::uint32_t numbers[] = {
DIGIT_0, DIGIT_1, DIGIT_2, DIGIT_3, DIGIT_4, DIGIT_5, DIGIT_6, DIGIT_7, DIGIT_8, DIGIT_9
};
if (c <= 'f' && c >= 'a')
return characters[c - 'a'];
else if (c <= '9' && c >= '0')
return numbers[c - '0'];
else if (c == '.')
return SEG_DOT;
else if (c == '-')
return SEG_MINUS;
else if (c == '*')
return SEG_DEGREE;
else if (c == ' ')
return 0;
else
return 0;
};
const char& to_write = display_data_[display_index_];
const std::uint32_t raw = grids[display_index_] | char_to_code(to_write);
display_index_++;
if (display_index_ >= DISPLAY_BUFFER_LEN)
display_index_ = 0;
return raw;
}