Товары из Китая

Электронная бумага: пишем драйвер методом научного тыка


Электронная бумага: пишем драйвер методом научного тыка

В предыдущей статье я рассказывал, как я затарился электроннобумажными дисплеями и даже сделал для них блок питания.

Наконец, пришли заказанные для них печатные платы и теперь можно заняться программным обеспечением. То, что описано ниже, касается только ED060SC4(LF) или LB060S01-RD02. Отличие в названии на одну букву, даже ту, что в скобке — и работать ничего не будет. Когда я задумал это безнадежное занятие, мне казалось, что я нашел кучу информации и исходников, и запустить этот дисплей проблемой не будет, несмотря на очень убогие спецификации от производителей. Не тут-то было, в каких-то местах описание оказалось на дисплей, название которого отличалась на одну букву, а в других — алгоритм просто не работал. В наше время верить нельзя никому, даже самому себе. Мне — можно.

В целях природы обуздания,

В целях рассеять неученья

Тьму

Берём картину мироздания – да!

И тупо смотрим, что к чему…

Целых два дня я тупо перебирал очередность разных сигналов и задержки между ними, не зная, жив ли еще дисплей. Не было даже намеков на появившуюся точку. И в один прекрасный момент — вуаля! — получите целую линию мусора. Всё, больше ничего и не надо, все остальное, дело техники, теперь он никуда не денется.

Электронная бумага: пишем драйвер методом научного тыка

Теперь расскажу, как все-таки удалось запустить это чудо. На истину в последней инстанции я не претендую, как заработало — так и опишу.

Для прорисовки экрана нужны следующие сигналы — сначала начало кадра, потом 600 строк, каждая имеет старт, потом данные и стоп. Завершается все концом кадра. За один пиксель отвечает два бита, комбинация 01 — черный цвет, 10 — белый, остальные — без изменения.

Таким образом, для одной строки из 800 пикселей нужно передать 200 байт.

Схема простая — источник питания, описанный в прошлой статье, STM32F103C8 для управления дисплеем и ESP32 на все про все, но пока его не используем.

Электронная бумага: пишем драйвер методом научного тыка

Ардуним для начала — определяем макросы, которые будут управлять сигналами.

Нажми меня
// ****************************************************

// power control
// en positive PB6
// en negative PB7
// en VDD PB8
//
#define INIT_POWER GPIOB->regs->CRL &= 0x00FFFFFF;
GPIOB->regs->CRL |= 0x11000000;
GPIOB->regs->CRH &= 0xFFFFFFF0;
GPIOB->regs->CRH |= 0x00000001;
GPIOB->regs->BRR = 0x01C0;
#define PWR_VDD_ON GPIOB->regs->BSRR = 0x0100;
#define PWR_VDD_OFF GPIOB->regs->BRR = 0x0100;
#define PWR_POS_ON GPIOB->regs->BSRR = 0x0040;
#define PWR_POS_OFF GPIOB->regs->BRR = 0x0040;
#define PWR_NEG_ON GPIOB->regs->BSRR = 0x0080;
#define PWR_NEG_OFF GPIOB->regs->BRR = 0x0080;
#define PWR_ALL_ON GPIOB->regs->BSRR = 0x01C0;
#define PWR_ALL_OFF GPIOB->regs->BRR = 0x01C0;
// ****************************************************
// SPV and SPH high, and all other pins low
// data
#define DATA_PORT GPIOA
// CL LE OE CPH GMODE SPV CKV
// CL PB9
#define CL_PORT GPIOB
#define CL_PIN 9
// LE PC13
#define LE_PORT GPIOC
#define LE_PIN 13
// OE PC14
#define OE_PORT GPIOC
#define OE_PIN 14
// CPH PC15
#define CPH_PORT GPIOC
#define CPH_PIN 15
// GMODE PB0
#define GMODE_PORT GPIOB
#define GMODE_PIN 0
// CKV PCB2
#define CKV_PORT GPIOB
#define CKV_PIN 2
// SPV PB1
#define SPV_PORT GPIOB
#define SPV_PIN 1
// DATA
// PC0...PC7
// PA0...PA7
#define INIT_DATA DATA_PORT->regs->CRL = 0x33333333; DATA_PORT->regs->BRR = 0xFF;
#define INIT_CL CL_PORT->regs->CRH &= ~(0xF << ((CL_PIN &0x7)<<2)); CL_PORT->regs->CRH |= 0x3 << ((CL_PIN &0x7)<<2);
#define CL_IDLE CL_PORT->regs->BRR = 1<<CL_PIN; // reset
#define CL_ACTIVE CL_PORT->regs->BSRR = 1<<CL_PIN; // set

#define INIT_LE LE_PORT->regs->CRH &= ~(0xF << ((LE_PIN &0x7)<<2)); LE_PORT->regs->CRH |= 0x3 << ((LE_PIN &0x7)<<2);
#define LE_IDLE LE_PORT->regs->BRR = 1<<LE_PIN; // reset
#define LE_ACTIVE LE_PORT->regs->BSRR = 1<<LE_PIN; // set

#define INIT_GMODE GMODE_PORT->regs->CRL &= ~(0xF << ((GMODE_PIN &0x7)<<2)); GMODE_PORT->regs->CRL |= 0x3 << ((GMODE_PIN &0x7)<<2);
#define GMODE_IDLE GMODE_PORT->regs->BRR = 1<<GMODE_PIN; // reset
#define GMODE_ACTIVE GMODE_PORT->regs->BSRR = 1<<GMODE_PIN; // set
#define INIT_CKV CKV_PORT->regs->CRL &= ~(0xF << ((CKV_PIN &0x7)<<2)); CKV_PORT->regs->CRL |= 0x3 << ((CKV_PIN &0x7)<<2);
#define CKV_IDLE CKV_PORT->regs->BRR = 1<<CKV_PIN; // reset
#define CKV_ACTIVE CKV_PORT->regs->BSRR = 1<<CKV_PIN; // set
#define INIT_SPV SPV_PORT->regs->CRL &= ~(0xF << ((SPV_PIN &0x7)<<2)); SPV_PORT->regs->CRL |= 0x3 << ((SPV_PIN &0x7)<<2);
#define SPV_IDLE SPV_PORT->regs->BRR = 1<<SPV_PIN; // reset
#define SPV_ACTIVE SPV_PORT->regs->BSRR = 1<<SPV_PIN; // set

#define INIT_OE OE_PORT->regs->CRH &= ~(0xF << ((OE_PIN &0x7)<<2)); OE_PORT->regs->CRH |= 0x3 << ((OE_PIN &0x7)<<2);
#define OE_IDLE OE_PORT->regs->BRR = 1<<OE_PIN; // reset
#define OE_ACTIVE OE_PORT->regs->BSRR = 1<<OE_PIN; // set
#define INIT_CPH CPH_PORT->regs->CRH &= ~(0xF << ((CPH_PIN &0x7)<<2)); CPH_PORT->regs->CRH |= 0x3 << ((CPH_PIN &0x7)<<2);
#define CPH_IDLE CPH_PORT->regs->BRR = 1<<CPH_PIN; // reset
#define CPH_ACTIVE CPH_PORT->regs->BSRR = 1<<CPH_PIN; // set

void Vclock()
{
CKV_ACTIVE
CKV_IDLE
}
void Hclock()
{
CL_ACTIVE
CL_IDLE
}
void PowerOn(void)
{
PWR_VDD_ON
delay(30);
PWR_NEG_ON
delay(5);
PWR_POS_ON
}
void PowerOff(void)
{
PWR_POS_OFF
delay(5);
PWR_NEG_OFF
delay(5);
PWR_VDD_OFF
}

Ведь правду говорят — ардуино это для совсем отстойных, которым лень думать и документацию читать.

Старт кадра выглядит так:

Электронная бумага: пишем драйвер методом научного тыка

и код:

void start_frame()

{
GMODE_ACTIVE
delay_us(1);
CKV_ACTIVE
delay_us(1);
SPV_IDLE
delay_us(1);
CKV_IDLE
delay_us(1);
CKV_ACTIVE
delay_us(1);
SPV_ACTIVE
delay_us(1);
}

Потом передаем 600 строк, каждая начинается так:

Электронная бумага: пишем драйвер методом научного тыка

Уж все безумно просто:

void start_row()

{
CPH_IDLE
}

Электронная бумага: пишем драйвер методом научного тыка

Завершение строки очень критично, чуть что не так — и никакого изображения не будет:

Электронная бумага: пишем драйвер методом научного тыка

Код, соответственно:

void stop_row()

{
CPH_ACTIVE
CKV_IDLE
Hclock();
CKV_ACTIVE
OE_IDLE
delay_us(2);
OE_ACTIVE
LE_ACTIVE
LE_IDLE
}

И завершаем кадр:

Электронная бумага: пишем драйвер методом научного тыка

Котд:

void end_frame()

{

GMODE_IDLE

delay_us(1);

Vclock();

}

Все вместе образует вот такое безобразие:

Электронная бумага: пишем драйвер методом научного тыка

И, наконец, тестовый код:

Нажми, если хочешь
<code>uint8_t data;


void setup()
{
INIT_POWER
GPIOB->regs->CRH &= 0xFFFFF0FF; //pinMode(PB10, OUTPUT); 2MHz
GPIOB->regs->CRH |= 0x00000200;
// USB pin open drain
GPIOA->regs->CRH &= 0xFFFFF0FFF;
GPIOA->regs->CRH |= 0x000000600;
GPIOA->regs->BRR = 1<<10; // digitalWrite(PA10,0);
delay(100);
GPIOA->regs->BSRR = 1<<10; // digitalWrite(PA10,1);
Serial.begin();
Serial.println("E-paper test");

INIT_DATA
INIT_CL
CL_IDLE
INIT_GMODE
GMODE_IDLE
INIT_CKV
CKV_IDLE
INIT_SPV
SPV_ACTIVE
INIT_OE
OE_IDLE
INIT_CPH
CPH_ACTIVE
INIT_LE
LE_IDLE
data=0xaa;
}

void loop()
{
uint8_t tdata;
GPIOB->regs->BSRR = 1<<10; // LED on
PowerOn();
delay(100);

for (uint16_t k=0; k<6; k++)
{
static bool even=false;
start_frame();
// write frame
for (uint16_t i=0; i<600; i++)
{
// wrire row
if (even) tdata = data;
else tdata = ~data;
if((i & 0x3f)==0) even = !even;
start_row();
//data = 0;
for (uint16_t j=0; j<200; j++)
{
if((j & 0x0f)==0) tdata = ~tdata;
DATA_PORT->regs->BRR = 0xFF;
DATA_PORT->regs->BSRR = tdata;
Hclock();
}
stop_row();
}
}
data = ~data;

end_frame();
delay(10);
PowerOff();
GPIOB->regs->BRR = 1<<10; // LED off
delay(2000);
}

Извиняйте, если уж сильно тривиально все выглядит. Если бы мне все это кто-то рассказал до того, как я эту возню затеял…

Может, кому другому поможет.

А это то, что этот тест делает — рисует шахматную доску. Смотрим скорость обновления — получается более, чем достойно, судите сами:

За один цикл прорисовки черные точки получить не удастся, получаются серые. У меня для получения черного цвета нужно повторить передачу кадра раз 6 — для пущей надежности я передаю его 8 раз.

В реальном устройстве присутствует lookup table, где хранятся данные о длительностях сигналов в зависимости от температуры и интенсивности черного. Почему-то производители сделали большой секрет из этих данных, поэтому имеем то, что имеем. У реального контроллера дисплея куча памяти, где он хранит изображение и следующее формируется в зависимости от предыдущего — не нужно полностью стирать изображение и потом рисовать все по новой — обновление происходит с учетом текущего состояния, поэтому его можно выполнить быстрее. Но оперативной памяти нужно много — ведь у экрана 480 000 пикселей.

По схеме видно, что микроконтроллер у меня дохленький, у него всего 20К ОЗУ и 64К флеш.

Но я использую буфер длиной всего 200 байт, и каждую строку просчитываю перед выводом.

Что-то подобное я использовал много лет назад, когда делал полетный контроллер для игрушечного самолетика и выводил изображение поверх картинки с видеокамеры — там, если склероз не подводит, вообще у контроллера было 4К ОЗУ. И сделано было еще интереснее — пока контроллер прямого доступа выбрасывал одну строку, процессор просчитывал следующую. В итоге использовалось 4 буфера — два для белого и 2 для черного.

Это было небольшое отступление. В реальных дисплеях производители обычно используют 16 градаций серого, но у любителей получается и 32 использовать.

Электронная бумага: пишем драйвер методом научного тыка

Но мне это не надо, двух уровней черного для моих целей достаточно и я легко это сделаю использованием количества перезаписываний кадра.

Электронная бумага: пишем драйвер методом научного тыка

Алгоритм с многократной перезаписью кадра выглядит убого, если у кто знает, как сделать лучше — пожалуйста, напишите в комментариях. Моя благодарность не будет иметь границ (в разумных пределах).

Следующим шагом будет добыча погоды из интернета и формирование полного кадра, но для этого будет использоваться ESP32, а STM32 будет играть роль тупого драйвера. Печатную плату тоже планирую переделать — контрольные точки уже не нужны, зато надо добавить управление питанием, планируется использовать литиевый аккумулятор. Нужно добавить зарядку и позаботиться о уменьшении потребления энергии устройством.

А пока можно немного размяться и с STM32:

Электронная бумага: пишем драйвер методом научного тыка

С ESP32 тоже не все так просто — при попытке выделить массив под кадр больше 96К, линкер отказывается работать, заявляя что все, памяти у него для вас больше нет. А где же ваши хваленые 320K Data RAM? При попытке выбросить SPI пакет, непрерывного потока не получается — после каждых 64 байта, кажется, идет разрыв несколько микросекунд. Но кому сейчас легко — будем бороться.

Небольшое скакание по исходникам показало, что посылка действительно разбивается, и это происходит в HAL — наверно, разработчики контроллера имели какую-то причину? Или они просто злобные Буратины, вся цель жизни которых — нагадить мне?

Дополнительная информация
void spiWriteNL(spi_t * spi, const void * data_in, uint32_t len){

if(!spi) {
return;
}
size_t longs = len >> 2;
if(len & 3){
longs++;
}
uint32_t * data = (uint32_t*)data_in;
size_t c_len = 0, c_longs = 0;
while(len){
c_len = (len>64)?64:len;
c_longs = (longs > 16)?16:longs;
spi->dev->mosi_dlen.usr_mosi_dbitlen = (c_len*8)-1;
#if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32
spi->dev->miso_dlen.usr_miso_dbitlen = 0;
#endif
for (size_t i=0; i<c_longs; i++) {
spi->dev->data_buf[i] = data[i];
}
#if CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S3
spi->dev->cmd.update = 1;
while (spi->dev->cmd.update);
#endif
spi->dev->cmd.usr = 1;
while(spi->dev->cmd.usr);
data += c_longs;
longs -= c_longs;
len -= c_len;
}
}

Библиотеку можно подправить, но при первом же обновлении мои правки накроются медным тазом. Пусть будет, как есть — не особо мешает.

Еще вариант — поставить дополнительную SPI RAM к дохленькому контоллеру. Где ее купить недорого — пока не нашел. А ставить мощный контроллер в качестве драйвера не особо хочется — у них и цена кусается, и слишком много ног, одни проблемы с пайкой.

RP2040 с его 264KB SRAM и программируемыми выводами выглядит хорошо, только там SRAM сегментирована по 64KB — опяь могут быть проблемы с буфером кадра. И кушать много изволит в спящем режиме. Что-то душа к нему не лежит. Короче, куда не кинь — везде клин, нет в жизни счастья 🙂

Гда брать погоду — еще одна проблема. Меньше всего ограничений у open-meteo.com, но они, как настоящие джентельмены, меняют правила по ходу игры. А поначалу смотрелось неплохо — регистрация не нужна, информацию можно брать по максимуму и безо всяких ограничений на запросы. Прогноз, надо сказать, не особо точный и еще и глюки имеются — как-то за окном шел дождь, а они на осадки на текущий день прогнозировали отрицательную величину. Интересно, это как?

Наверно, самый популярный сайт — это openweathermap.org, но они очень хотят денег и всячески затрудняют жизнь халявщиков. И необходимость регистрации и ограничение на запросы не радуют, хотя ограничения на запросы вполне разумные. Но информации open-meteo дает больше, хоть и неправильной.

Dark Sky куплена Apple, так что ловить там уже нечего.

Первые тесты выглядят так (прогноз с open-meteo):

Электронная бумага: пишем драйвер методом научного тыка

Пока статья писалась — и новая схема поспела, можете покритиковать при желании, только не сильно:

Электронная бумага: пишем драйвер методом научного тыка

Электронная бумага: пишем драйвер методом научного тыка

Антенна оказалась рядом с металлом дисплея. Он весь покрыт металлом, а плата будет к нему приклеена.

Когда коту делать нечего — он платы разводит. Переделываем все так, чтобы антенна выступала из-за экрана:

Электронная бумага: пишем драйвер методом научного тыка

Почему эта статья не на Хабре? Причин много — во-первых, там все рисуют какого-то бегемота в колпачке, а я даже не знаю, откуда он взялся, неудобно как-то.

Электронная бумага: пишем драйвер методом научного тыка

Кроме того, там какие-то страшные джуны, мидлы и тимлиды проходят интервью и получают оферы. Может, это больно?

Электронная бумага: пишем драйвер методом научного тыка

На английском, было дело, я наваял кучку сайтов, доверившись Гуглу и его репутации — через какое-то время их прихлопнули. Больше не хочется время терять.

А здесь хоть плюсик поставят 🙂 — уже видно, что не зря старался.

В комментариях часто здравую идею подскажут — тоже польза.

Ну и писать на английском — это все-таки труд, а на русском — развлечение. Графоманы подтвердят.

Кстати, анонс — Марио таки ограбил банк, так что впереди много интересного.

Электронная бумага: пишем драйвер методом научного тыка

Что это и с чем это едят — можно посмотреть здесь и здесь.


СМОТРИ ТАКЖЕ

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *