Implement OTP dumping

This commit is contained in:
BlackMark 2022-06-02 21:37:34 +02:00
parent 7a0f00ceab
commit 91b49cd536
2 changed files with 418 additions and 19 deletions

View File

@ -7,11 +7,11 @@
#include <cstddef> #include <cstddef>
#include <cstdint> #include <cstdint>
#include <avr/pgmspace.h>
#include "eink_spi.hpp" #include "eink_spi.hpp"
#include "otp.hpp"
#include "../clock.hpp" #include "../clock.hpp"
#include "../flash/flash.hpp"
#include "../io/io.hpp" #include "../io/io.hpp"
#include "../util/util.hpp" #include "../util/util.hpp"
@ -24,17 +24,6 @@ class Eink {
static io::Pin<RstPin> m_rst; static io::Pin<RstPin> m_rst;
static io::Pin<BusyPin> m_busy; static io::Pin<BusyPin> m_busy;
template <typename T>
static T pgm_load(const T &object)
{
auto buffer = T{};
auto rawBuffer = reinterpret_cast<std::byte *>(&buffer);
for (auto i = std::size_t{0}; i < sizeof(T); ++i) {
rawBuffer[i] = static_cast<std::byte>(pgm_read_byte(&reinterpret_cast<const std::byte *>(&object)[i]));
}
return buffer;
}
public: public:
enum class Cmd : std::uint8_t { enum class Cmd : std::uint8_t {
DRIVER_OUTPUT_CONTROL = 0x01, DRIVER_OUTPUT_CONTROL = 0x01,
@ -46,7 +35,10 @@ class Eink {
DISPLAY_UPDATE_CONTROL_2 = 0x22, DISPLAY_UPDATE_CONTROL_2 = 0x22,
WRITE_RAM_BLACK = 0x24, WRITE_RAM_BLACK = 0x24,
WRITE_RAM_RED = 0x26, WRITE_RAM_RED = 0x26,
READ_RAM = 0x27,
LOAD_OTP_TO_RAM = 0x31,
BORDER_WAVEFORM_CONTROL = 0x3C, BORDER_WAVEFORM_CONTROL = 0x3C,
READ_RAM_CHANNEL = 0x41,
SET_RAM_X_ADDR_POSITIONS = 0x44, SET_RAM_X_ADDR_POSITIONS = 0x44,
SET_RAM_Y_ADDR_POSITIONS = 0x45, SET_RAM_Y_ADDR_POSITIONS = 0x45,
SET_RAM_X_ADDR = 0x4E, SET_RAM_X_ADDR = 0x4E,
@ -54,9 +46,9 @@ class Eink {
}; };
enum class Color : std::uint8_t { enum class Color : std::uint8_t {
BLACK, BLACK = 0x00,
WHITE, RED = 0x01,
RED, WHITE = 0x02,
}; };
enum class RamDirection : std::uint8_t { enum class RamDirection : std::uint8_t {
@ -152,7 +144,7 @@ class Eink {
constexpr auto sendImageChannel = [](const auto command, const auto &image) { constexpr auto sendImageChannel = [](const auto command, const auto &image) {
sendCommand(command); sendCommand(command);
for (auto j = std::size_t{0}; j < image.size(); ++j) { for (auto j = std::size_t{0}; j < image.size(); ++j) {
const auto [count, data] = pgm_load(image[j]); const auto [count, data] = flash::load(image[j]);
for (auto i = std::uint16_t{0}; i < count; ++i) { for (auto i = std::uint16_t{0}; i < count; ++i) {
if (command == Cmd::WRITE_RAM_BLACK) { if (command == Cmd::WRITE_RAM_BLACK) {
sendData(data); sendData(data);
@ -176,12 +168,12 @@ class Eink {
{ {
constexpr auto getFillData = [](const auto &color) -> std::pair<std::uint8_t, std::uint8_t> { constexpr auto getFillData = [](const auto &color) -> std::pair<std::uint8_t, std::uint8_t> {
switch (color) { switch (color) {
case Color::WHITE:
return {0xFF, 0x00};
case Color::BLACK: case Color::BLACK:
return {0x00, 0x00}; return {0x00, 0x00};
case Color::RED: case Color::RED:
return {0xFF, 0xFF}; return {0xFF, 0xFF};
case Color::WHITE:
return {0xFF, 0x00};
} }
return {0xFF, 0x00}; return {0xFF, 0x00};
}; };
@ -264,6 +256,60 @@ class Eink {
sendData((pos >> 8) & 0b1); sendData((pos >> 8) & 0b1);
} }
template <typename PrintFn>
static void dumpOTP(PrintFn &&printFn)
{
constexpr auto byteWidth = Width / 8;
constexpr auto ramHeight = Height + 46;
constexpr auto xRamRange = std::pair<std::uint8_t, std::uint8_t>{0, byteWidth - 1};
constexpr auto yRamRange = std::pair<std::uint16_t, std::uint16_t>{0, ramHeight - 1};
setDataEntryMode(RamDirection::INCREMENT, RamDirection::INCREMENT, FastestMovingIndex::X);
setRamRange(xRamRange, yRamRange);
setRamXPos(0);
setRamYPos(0);
sendCommand(Cmd::WRITE_RAM_BLACK);
for (auto i = std::uint16_t{0}; i < byteWidth * ramHeight; i++) {
sendData(0xFF);
}
sendCommand(Cmd::WRITE_RAM_RED);
for (auto i = std::uint16_t{0}; i < byteWidth * ramHeight; i++) {
sendData(0x00);
}
sendCommand(Cmd::DISPLAY_UPDATE_CONTROL_2);
sendData(0xF7);
sendCommand(Cmd::UPDATE_DISPLAY);
waitUntilIdle();
//////////////////////////////////////////////////////////////////////////
sendCommand(Cmd::DISPLAY_UPDATE_CONTROL_2);
sendData(0x80);
sendCommand(Cmd::UPDATE_DISPLAY);
waitUntilIdle();
sendCommand(Cmd::LOAD_OTP_TO_RAM);
waitUntilIdle();
setRamXPos(0);
setRamYPos(0);
sendCommand(Cmd::READ_RAM_CHANNEL);
sendData(static_cast<word_t>(Color::BLACK));
sendCommand(Cmd::READ_RAM);
readData(); // First byte must be discarded
for (auto i = std::size_t{0}; i < sizeof(OTP); ++i) {
printFn(readData());
}
init();
}
static void sleep() static void sleep()
{ {
sendCommand(Cmd::DEEP_SLEEP_MODE); sendCommand(Cmd::DEEP_SLEEP_MODE);

353
otp.hpp Normal file
View File

@ -0,0 +1,353 @@
#pragma once
#include <cstddef>
#include <cstdint>
namespace eink {
// [SOURCE]_[VCOM]
enum class Voltage : std::uint8_t {
VSS_DCVCOM = 0b00,
VSH1_VSH1DCVCOM = 0b01,
VSL_VSLDCVCOM = 0b10,
VSH2_NA = 0b11,
};
struct [[gnu::packed]] Phases
{
Voltage phaseD : 2;
Voltage phaseC : 2;
Voltage phaseB : 2;
Voltage phaseA : 2;
};
static_assert(sizeof(Phases) == 1);
struct [[gnu::packed]] VoltageGroups
{
Phases group[12];
};
static_assert(sizeof(VoltageGroups) == 12);
struct [[gnu::packed]] Timings
{
std::uint8_t frameCountPhaseA;
std::uint8_t frameCountPhaseB;
std::uint8_t repeatSubPhaseAB;
std::uint8_t frameCountPhaseC;
std::uint8_t frameCountPhaseD;
std::uint8_t repeatSubPhaseCD;
std::uint8_t repeat;
};
static_assert(sizeof(Timings) == 7);
struct [[gnu::packed]] FrameRates
{
std::uint8_t group11 : 4;
std::uint8_t group10 : 4;
std::uint8_t group9 : 4;
std::uint8_t group8 : 4;
std::uint8_t group7 : 4;
std::uint8_t group6 : 4;
std::uint8_t group5 : 4;
std::uint8_t group4 : 4;
std::uint8_t group3 : 4;
std::uint8_t group2 : 4;
std::uint8_t group1 : 4;
std::uint8_t group0 : 4;
};
static_assert(sizeof(FrameRates) == 6);
struct [[gnu::packed]] SubPhaseGateStates
{
bool group11SubPhaseCD : 1;
bool group11SubPhaseAB : 1;
bool group10SubPhaseCD : 1;
bool group10SubPhaseAB : 1;
bool group9SubPhaseCD : 1;
bool group9SubPhaseAB : 1;
bool group8SubPhaseCD : 1;
bool group8SubPhaseAB : 1;
bool group7SubPhaseCD : 1;
bool group7SubPhaseAB : 1;
bool group6SubPhaseCD : 1;
bool group6SubPhaseAB : 1;
bool group5SubPhaseCD : 1;
bool group5SubPhaseAB : 1;
bool group4SubPhaseCD : 1;
bool group4SubPhaseAB : 1;
bool group3SubPhaseCD : 1;
bool group3SubPhaseAB : 1;
bool group2SubPhaseCD : 1;
bool group2SubPhaseAB : 1;
bool group1SubPhaseCD : 1;
bool group1SubPhaseAB : 1;
bool group0SubPhaseCD : 1;
bool group0SubPhaseAB : 1;
};
static_assert(sizeof(SubPhaseGateStates) == 3);
struct [[gnu::packed]] Waveform
{
VoltageGroups lut[5];
Timings timings[12];
FrameRates frameRates;
SubPhaseGateStates subPhaseGateStates;
};
static_assert(sizeof(Waveform) == 153);
enum class LutEndOption : std::uint8_t {
KEEP_PREVIOUS_SOURCE_OUTPUT_LEVEL_BEFORE_POWER_OFF = 0x07,
NORMAL = 0x22,
};
static_assert(sizeof(LutEndOption) == 1);
enum class GateVoltage : std::uint8_t {
V_20_DEFAULT = 0x00,
V_10 = 0x03,
V_10_5 = 0x04,
V_11 = 0x05,
V_11_5 = 0x06,
V_12 = 0x07,
V_12_5 = 0x08,
V_13 = 0x09,
V_13_5 = 0x0A,
V_14 = 0x0B,
V_14_5 = 0x0C,
V_15 = 0x0D,
V_15_5 = 0x0E,
V_16 = 0x0F,
V_16_5 = 0x10,
V_17 = 0x11,
V_17_5 = 0x12,
V_18 = 0x13,
V_18_5 = 0x14,
V_19 = 0x15,
V_19_5 = 0x16,
V_20 = 0x17,
};
static_assert(sizeof(GateVoltage) == 1);
enum class VoltageSourceHigh : std::uint8_t {
V_9 = 0x23,
V_9_2 = 0x24,
V_9_4 = 0x25,
V_9_6 = 0x26,
V_9_8 = 0x27,
V_10 = 0x28,
V_10_2 = 0x29,
V_10_4 = 0x2A,
V_10_6 = 0x2B,
V_10_8 = 0x2C,
V_11 = 0x2D,
V_11_2 = 0x2E,
V_11_4 = 0x2F,
V_11_6 = 0x30,
V_11_8 = 0x31,
V_12 = 0x32,
V_12_2 = 0x33,
V_12_4 = 0x34,
V_12_6 = 0x35,
V_12_8 = 0x36,
V_13 = 0x37,
V_13_2 = 0x38,
V_13_4 = 0x39,
V_13_6 = 0x3A,
V_13_8 = 0x3B,
V_14 = 0x3C,
V_14_2 = 0x3D,
V_14_4 = 0x3E,
V_14_6 = 0x3F,
V_14_8 = 0x40,
V_15 = 0x41,
V_15_2 = 0x42,
V_15_4 = 0x43,
V_15_6 = 0x44,
V_15_8 = 0x45,
V_16 = 0x46,
V_16_2 = 0x47,
V_16_4 = 0x48,
V_16_6 = 0x49,
V_16_8 = 0x4A,
V_17 = 0x4B,
V_2_4 = 0x8E,
V_2_5 = 0x8F,
V_2_6 = 0x90,
V_2_7 = 0x91,
V_2_8 = 0x92,
V_2_9 = 0x93,
V_3 = 0x94,
V_3_1 = 0x95,
V_3_2 = 0x96,
V_3_3 = 0x97,
V_3_4 = 0x98,
V_3_5 = 0x99,
V_3_6 = 0x9A,
V_3_7 = 0x9B,
V_3_8 = 0x9C,
V_3_9 = 0x9D,
V_4 = 0x9E,
V_4_1 = 0x9F,
V_4_2 = 0xA0,
V_4_3 = 0xA1,
V_4_4 = 0xA2,
V_4_5 = 0xA3,
V_4_6 = 0xA4,
V_4_7 = 0xA5,
V_4_8 = 0xA6,
V_4_9 = 0xA7,
V_5 = 0xA8,
V_5_1 = 0xA9,
V_5_2 = 0xAA,
V_5_3 = 0xAB,
V_5_4 = 0xAC,
V_5_5 = 0xAD,
V_5_6 = 0xAE,
V_5_7 = 0xAF,
V_5_8 = 0xB0,
V_5_9 = 0xB1,
V_6 = 0xB2,
V_6_1 = 0xB3,
V_6_2 = 0xB4,
V_6_3 = 0xB5,
V_6_4 = 0xB6,
V_6_5 = 0xB7,
V_6_6 = 0xB8,
V_6_7 = 0xB9,
V_6_8 = 0xBA,
V_6_9 = 0xBB,
V_7 = 0xBC,
V_7_1 = 0xBD,
V_7_2 = 0xBE,
V_7_3 = 0xBF,
V_7_4 = 0xC0,
V_7_5 = 0xC1,
V_7_6 = 0xC2,
V_7_7 = 0xC3,
V_7_8 = 0xC4,
V_7_9 = 0xC5,
V_8 = 0xC6,
V_8_1 = 0xC7,
V_8_2 = 0xC8,
V_8_3 = 0xC9,
V_8_4 = 0xCA,
V_8_5 = 0xCB,
V_8_6 = 0xCC,
V_8_7 = 0xCD,
V_8_8 = 0xCE,
};
static_assert(sizeof(VoltageSourceHigh) == 1);
enum class VoltageSourceLow : std::uint8_t {
NEG_5 = 0x0A,
NEG_5_5 = 0x0C,
NEG_6 = 0x0E,
NEG_6_5 = 0x10,
NEG_7 = 0x12,
NEG_7_5 = 0x14,
NEG_8 = 0x16,
NEG_8_5 = 0x18,
NEG_9 = 0x1A,
NEG_9_5 = 0x1C,
NEG_10 = 0x1E,
NEG_10_5 = 0x20,
NEG_11 = 0x22,
NEG_11_5 = 0x24,
NEG_12 = 0x26,
NEG_12_5 = 0x28,
NEG_13 = 0x2A,
NEG_13_5 = 0x2C,
NEG_14 = 0x2E,
NEG_14_5 = 0x30,
NEG_15 = 0x32,
NEG_15_5 = 0x34,
NEG_16 = 0x36,
NEG_16_5 = 0x38,
NEG_17 = 0x3A,
};
static_assert(sizeof(VoltageSourceLow) == 1);
struct [[gnu::packed]] SourceVoltage
{
VoltageSourceHigh vsh1;
VoltageSourceHigh vsh2;
VoltageSourceLow vsl;
};
static_assert(sizeof(SourceVoltage) == 3);
enum class CommonVoltage : std::uint8_t {
NEG_0_2 = 0x08,
NEG_0_3 = 0x0C,
NEG_0_4 = 0x10,
NEG_0_5 = 0x14,
NEG_0_6 = 0x18,
NEG_0_7 = 0x1C,
NEG_0_8 = 0x20,
NEG_0_9 = 0x24,
NEG_1 = 0x28,
NEG_1_1 = 0x2C,
NEG_1_2 = 0x30,
NEG_1_3 = 0x34,
NEG_1_4 = 0x38,
NEG_1_5 = 0x3C,
NEG_1_6 = 0x40,
NEG_1_7 = 0x44,
NEG_1_8 = 0x48,
NEG_1_9 = 0x4C,
NEG_2 = 0x50,
NEG_2_1 = 0x54,
NEG_2_2 = 0x58,
NEG_2_3 = 0x5C,
NEG_2_4 = 0x60,
NEG_2_5 = 0x64,
NEG_2_6 = 0x68,
NEG_2_7 = 0x6C,
NEG_2_8 = 0x70,
NEG_2_9 = 0x74,
NEG_3 = 0x78,
};
static_assert(sizeof(CommonVoltage) == 1);
struct [[gnu::packed]] WaveformSetting
{
Waveform waveform;
LutEndOption lutEndOption;
GateVoltage gateVoltage;
SourceVoltage sourceVoltage;
CommonVoltage commonVoltage;
};
static_assert(sizeof(WaveformSetting) == 159);
struct [[gnu::packed]] TemperatureRange
{
std::uint8_t lowerBoundLowBits;
std::uint8_t lowerBoundHighBits : 4;
std::uint8_t upperBoundLowBits : 4;
std::uint8_t upperBoundHighBits;
inline constexpr operator std::pair<float, float>() const
{
constexpr auto convertTemp = [](const auto &temp) {
auto signedTemp = static_cast<std::int16_t>(temp);
if (signedTemp >> 11 & 1) {
signedTemp |= 0b1111 << 12;
}
const auto tempDegrees = signedTemp / 16.0f;
return tempDegrees;
};
const auto lowerBound = static_cast<std::uint16_t>(lowerBoundHighBits) << 8 | lowerBoundLowBits;
const auto upperBound = static_cast<std::uint16_t>(upperBoundHighBits) << 4 | upperBoundLowBits;
return {convertTemp(lowerBound), convertTemp(upperBound)};
};
};
static_assert(sizeof(TemperatureRange) == 3);
struct [[gnu::packed]] OTP
{
WaveformSetting waveforms[36];
TemperatureRange temperatures[36];
};
static_assert(sizeof(OTP) == 5832);
} // namespace eink