180 lines
5.5 KiB
C++
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;
|
|
}
|