DOOM Watch на ESP32. Часть 1

Попробовав разработку с готовыми модулями ESP32 захотелось сделать что-то маленькое и нативное. Решил сделать часы. Сначала подумал о ESP32-PICO-D4. Поскольку в ней только 4Mb flash под программу, решил сделать полноценную версию с расширением да 16Mb flash и 8Mb SRAM. Что бы на часах можно запустить первый Doom. В общем чтобы было все на полном фарше!

DOOM Watch на ESP32. Часть 1 — IT-МИР. ПОМОЩЬ В IT-МИРЕ 2020

Что не сделано или требует доработки:

  1. Индикатор батареи
  2. Схема барьера зарядки реализована на Schottky диоде
  3. Антена расположена не совсем удачно и на другом слое от ESP32

Не туториал!

DOOM Watch на ESP32. Часть 1 — IT-МИР. ПОМОЩЬ В IT-МИРЕ 2020

По дисплею

DOOM Watch на ESP32. Часть 1 — IT-МИР. ПОМОЩЬ В IT-МИРЕ 2020

Я применил цветной дисплей на контроллере ST7789 с разрешением 240×240. Он довольно компактный и дешевый. В сети все больше появляется драйверов и портов. Например есть порт для LittlevGL, но на этом дисплее нет тача. Думаю его можно докупить и приклеить. Может у кого-то есть опыт? Поделитесь

Я разработал на ST7789 одну плату и она «завелась» без каких либо проблем

DOOM Watch на ESP32. Часть 1 — IT-МИР. ПОМОЩЬ В IT-МИРЕ 2020

LittlevGL esp32

По заливке программ и debug

Я использовал USB-TO-UART BRIDGE преобразователь CP2102. Во первых он компактный и во вторых схема подключения очень простая и почти не требуется дополнительных деталей.

Option 2: A 4.7 μF capacitor can be added if powering other devices from the on-chip regulator.

Если подключить REGIN и VBUS к питанию USB 5V то потребуется только шунтирующий конденсатор на входе. Хотя думаю может работать и без него. VDD на чипе в этом случае это выход! Я сделал ошибку и подключил его к 3.3V питания схемы и не мог понять почему у меня на питании 4.65V ?!

По ссылке в документации внизу есть варианты подключения к 3.3V питания. Но думаю совершенно не надо питать CP2102 когда нам не надо заливать или дебажить девайс

DOOM Watch на ESP32. Часть 1 — IT-МИР. ПОМОЩЬ В IT-МИРЕ 2020

По модулю зарядки батареи

Резистор на LTC4054 устанавливает ток зарядки:

DOOM Watch на ESP32. Часть 1 — IT-МИР. ПОМОЩЬ В IT-МИРЕ 2020

По питанию

Питание осуществляется от батарей 3.7V со стабилизатором HT7833. Выходной ток 500mA. Он имеет малое ~300mV падение напряжения. LD1117-3.3 имеет «немного» больше.

Замечание про выводы VDD_SDIO. Этот пин выход питания 1.8V или 3.3V в зависимости от того в каком состоянии находится IO12 микроконтроллера при старте. 3.3V GPIO12 is 0 (default)

VDD_SDIO works as the power supply for the related IO, and also for an external device.

When VDD_SDIO operates at 1.8 V, it can be generated from ESP32’s internal LDO. The maximum currentthis LDO can offer is 40 mA, and the output voltage range is 1.65 V~2.0 V.

When the VDD_SDIO outputs 1.8 V, the value of GPIO12 should be set to 1 when the chip boots and it is recommended that users add a2 kΩ ground resistor and a 4.7 mF filter capacitor close to VDD_SDIO.

When VDD_SDIO operates at 3.3 V, it is driven directly by VDD3P3_RTC through a 6Ωresistor, therefore,there will be some voltage drop from VDD3P3_RTC.

When the VDD_SDIO outputs 3.3 V, the value of GPIO12 is 0 (default) when the chip boots and it is recommended that users add a 1mF capacitor close to VDD_SDIO

Это очень удобно если у вас flash 1.8V. Но в моем случае я забил на этот вывод и подключил мою 3V3 flash и PSRAM к общему питанию

Некоторые модули ESP32 используют VDD_SDIO поэтому на IO12 нельзя ничего вешать из перефирии при старте. Можно например повесить кнопку. В одном из моих решений я повесил на IO12 ногу SPI и модуль не стартовал. Видимо на IO12 попала единица с этого порта SPI, а нужен был 0 или наоборот. Это надо учитывать!

All together:

image

8MB PSRAM

8MB PSRAM Upgrade Mod

PCB antenna

Я использовал Small Size 2.4 GHz PCB antenna. Она есть в библиотеке Eagle Autodesk и занимает небольшую площадь. Можно наверное применить CERAMIC DIELECTRIC ANTENNA но ее надо покупать, а цена PCB антенны это немного бОльшее занимаемое место. Однако керамическая антенна менее эффективна. Для проверки концепта пойдет

image

Немного о с согласовании антенны

DOOM Watch на ESP32. Часть 1 — IT-МИР. ПОМОЩЬ В IT-МИРЕ 2020

В документации ESP32 Hardware Design Guidelines на стр 7 дается рекомендация по реализации RF фильтра:

The output impedance of the RF pins of ESP32 (QFN 6*6) and ESP32 (QFN 5*5) are (30+j10) Ω and (35+j10) Ω, respectively

Для расчета воспользуемся Online Smith Chart Tool. Основная идея попасть в центр круга при (30+j10). Однако это расчетные данные и на реальное использование параметров может повлиять толщина дорожек и текстолита, а также расположение относительно других компонентов схемы

DOOM Watch на ESP32. Часть 1 — IT-МИР. ПОМОЩЬ В IT-МИРЕ 2020
Это не единственная схема согласования антенны. Например согласование на плате esp32-pic выполнено немного иначе:

esp32-pico-kit-v4_schematic

Smith Chart and Impedance Matching

Так же важно расположение антенны и свободная область вокруг нее. Как я написал ранее, в моем случае положение выбрано не оптимально. Но для первой итерации платы и проверки концепта пойдет

DOOM Watch на ESP32. Часть 1 — IT-МИР. ПОМОЩЬ В IT-МИРЕ 2020

Вторая часть будет посвящена самой плате а третья портированию софта. Возможно все уместится в одну.

Я немного забегу вперед по порту Doom от компании производителя Espressif Systems. В порте применяется ILI9341 у нас ST7789. Но поскольку инициализация и вывод буфера вынесен в отдельный файл и разбит на отдельные методы, адаптация под мой дисплей не должно вызвать большие сложности.

  • За инициализацию дисплея отвечает ili_init и displayTask
  • За отображения на дисплее отвечает displayTask

displayTask

void IRAM_ATTR displayTask(void *arg) {
	int x, i;
	int idx=0;
	int inProgress=0;
	static uint16_t *dmamem[NO_SIM_TRANS];
	spi_transaction_t trans[NO_SIM_TRANS];
	spi_transaction_t *rtrans;

    esp_err_t ret;
    spi_bus_config_t buscfg={
        .miso_io_num=-1,
        .mosi_io_num=PIN_NUM_MOSI,
        .sclk_io_num=PIN_NUM_CLK,
        .quadwp_io_num=-1,
        .quadhd_io_num=-1,
        .max_transfer_sz=(MEM_PER_TRANS*2)+16
    };
    spi_device_interface_config_t devcfg={
        .clock_speed_hz=26000000,               //Clock out at 26 MHz. Yes, that's heavily overclocked.
        .mode=0,                                //SPI mode 0
        .spics_io_num=PIN_NUM_CS,               //CS pin
        .queue_size=NO_SIM_TRANS,               //We want to be able to queue this many transfers
        .pre_cb=ili_spi_pre_transfer_callback,  //Specify pre-transfer callback to handle D/C line
    };

	printf("*** Display task starting.n");

    //Initialize the SPI bus
    ret=spi_bus_initialize(HSPI_HOST, &buscfg, 1);
    assert(ret==ESP_OK);
    //Attach the LCD to the SPI bus
    ret=spi_bus_add_device(HSPI_HOST, &devcfg, &spi);
    assert(ret==ESP_OK);
    //Initialize the LCD
    ili_init(spi);

	//We're going to do a fair few transfers in parallel. Set them all up.
	for (x=0; x<NO_SIM_TRANS; x++) {
		dmamem[x]=pvPortMallocCaps(MEM_PER_TRANS*2, MALLOC_CAP_DMA);
		assert(dmamem[x]);
		memset(&trans[x], 0, sizeof(spi_transaction_t));
		trans[x].length=MEM_PER_TRANS*2;
		trans[x].user=(void*)1;
		trans[x].tx_buffer=&dmamem[x];
	}
	xSemaphoreGive(dispDoneSem);

	while(1) {
		xSemaphoreTake(dispSem, portMAX_DELAY);
//		printf("Display task: frame.n");
#ifndef DOUBLE_BUFFER
		uint8_t *myData=(uint8_t*)currFbPtr;
#endif

		send_header_start(spi, 0, 0, 320, 240);
		send_header_cleanup(spi);
		for (x=0; x<320*240; x+=MEM_PER_TRANS) {
#ifdef DOUBLE_BUFFER
			for (i=0; i<MEM_PER_TRANS; i+=4) {
				uint32_t d=currFbPtr[(x+i)/4];
				dmamem[idx][i+0]=lcdpal[(d>>0)&0xff];
				dmamem[idx][i+1]=lcdpal[(d>>8)&0xff];
				dmamem[idx][i+2]=lcdpal[(d>>16)&0xff];
				dmamem[idx][i+3]=lcdpal[(d>>24)&0xff];
			}
#else
			for (i=0; i<MEM_PER_TRANS; i++) {
				dmamem[idx][i]=lcdpal[myData[i]];
			}
			myData+=MEM_PER_TRANS;
#endif
			trans[idx].length=MEM_PER_TRANS*16;
			trans[idx].user=(void*)1;
			trans[idx].tx_buffer=dmamem[idx];
			ret=spi_device_queue_trans(spi, &trans[idx], portMAX_DELAY);
			assert(ret==ESP_OK);

			idx++;
			if (idx>=NO_SIM_TRANS) idx=0;

			if (inProgress==NO_SIM_TRANS-1) {
				ret=spi_device_get_trans_result(spi, &rtrans, portMAX_DELAY);
				assert(ret==ESP_OK);
			} else {
				inProgress++;
			}
		}
#ifndef DOUBLE_BUFFER
		xSemaphoreGive(dispDoneSem);
#endif
		while(inProgress) {
			ret=spi_device_get_trans_result(spi, &rtrans, portMAX_DELAY);
			assert(ret==ESP_OK);
			inProgress--;
		}
	}
}

Заказ на плату отправлен на фабрику Jlcpcb.

Челенж запущен!

Специально для сайта ITWORLD.UZ. Новость взята с сайта Хабр