Skip to content

Commit

Permalink
Merge branch 'main' into single_class_for_filesystems
Browse files Browse the repository at this point in the history
  • Loading branch information
frostmorn committed Mar 30, 2024
2 parents ae845e2 + 862b256 commit 268d250
Show file tree
Hide file tree
Showing 8 changed files with 121 additions and 26 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

DIY-консоль, яку можна зібрати з дешевих готових модулів.

![Лілка v2](./img/v2.jpg)
![Лілка v2](./img/v21.jpg)

## УВАГА!

Expand Down
5 changes: 4 additions & 1 deletion firmware/doom/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@

Це - порт Doom для Лілки.

[lib/doomgeneric](./lib/doomgeneric) містить модифікований код рушія Doom Generic для запуску на ESP32-S3. Я додав динамічну алокацію пам'яті для деяких "товстих" масивів, щоб використовувати PSRAM. Таким чином весь основний код Doom тепер спокійно поміщається в 400 КБ основної RAM. Крім цього, є ще деякі зміни (див. [README](./lib/doomgeneric)).
[lib/doomgeneric](./lib/doomgeneric) містить модифікований код рушія Doom Generic для запуску на ESP32-S3.
Я додав динамічну алокацію пам'яті для деяких "товстих" масивів, щоб використовувати PSRAM. Таким чином весь основний код Doom тепер спокійно поміщається в 400 КБ основної RAM. Крім цього, є ще деякі зміни (див. [README](./lib/doomgeneric)).

[src/main.cpp](./src/main.cpp) містить код, специфічний для Лілки (малювання екрана, ініціалізація, etc).

Решта файлів в `src` - це звукові драйвери для I2S (з простим міксуванням звуку) та п'єзодинаміка.

Звісно, для того, щоб пограти в Doom, вам додатково потрібно мати файл `doom.wad`. Його можна купити або завантажити в інтернеті. Його потрібно покласти в корінь SD-карти.
74 changes: 51 additions & 23 deletions firmware/doom/src/i_i2ssound.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ SemaphoreHandle_t soundMutexHandle = NULL;

// Звук - це складно! :D /AD
// Ring buffer for mixing sound
uint16_t* mixerBuffer;
int16_t* mixerBuffer;
uint32_t mixerBufferStart = 0;
uint32_t mixerBufferEnd = 0;

Expand All @@ -41,7 +41,7 @@ static boolean I_I2S_InitSound(bool _use_sfx_prefix) {
use_sfx_prefix = _use_sfx_prefix;
soundMutexHandle = xSemaphoreCreateMutex();

mixerBuffer = static_cast<uint16_t*>(ps_malloc(65536 * sizeof(uint16_t)));
mixerBuffer = static_cast<int16_t*>(ps_malloc(65536 * sizeof(int16_t)));

xSemaphoreTake(
backBufferMutex, portMAX_DELAY
Expand Down Expand Up @@ -83,7 +83,6 @@ static void I_I2S_ShutdownSound(void) {
xSemaphoreTake(soundMutexHandle, portMAX_DELAY);
vTaskDelete(soundTaskHandle);
i2s_driver_uninstall(esp_i2s::I2S_NUM_0);
// free(soundTaskStack);
free(mixerBuffer);
xSemaphoreGive(soundMutexHandle);
vSemaphoreDelete(soundMutexHandle);
Expand Down Expand Up @@ -167,26 +166,48 @@ static int I_I2S_StartSound(sfxinfo_t* sfxinfo, int channel, int vol, int sep) {

#define MIN(a, b) ((a) < (b) ? (a) : (b))

// Mix sound data into the ring buffer
void queueSound(const uint8_t* data, uint32_t length, uint32_t sample_rate, uint8_t vol) {
// Here comes the fun part! /AD
xSemaphoreTake(soundMutexHandle, portMAX_DELAY);

// TODO: Resample to 11025 if needed? Most sounds are 11025, but not all...

// Number of samples to mix with buffered samples
int mixedLength;
if (mixerBufferStart <= mixerBufferEnd) {
// Buffer is contiguous
mixedLength = mixerBufferEnd - mixerBufferStart;
} else {
// Buffer is split
mixedLength = 65536 - mixerBufferStart;
}
mixedLength = MIN(mixedLength, length);
// Number of samples to add unmixed beyond buffered samples
int unmixedLength = length - mixedLength;

uint32_t pos = mixerBufferStart;
// lilka::serial_log("Buffer: %d -> %d", mixerBufferStart, mixerBufferEnd);
bool mixing = true;
for (int i = 0; i < length; i++) {
uint16_t sample = (data[i] << 6) * vol / 127;
if (pos == mixerBufferEnd) {
mixing = false;
for (int i = 0; i < mixedLength; i++) {
uint16_t rawSample =
data[i] << 6; // Beef up the sample to 16-bit (minus 2 bits to avoid clipping), range will be [0;16384]
int16_t sample = rawSample - 8192; // Center the sample around 0, range will be [-8192;8192]
mixerBuffer[pos] = ((mixerBuffer[pos] * (64 - vol)) >> 6) + ((sample * vol) >> 6); // vol is [0;64]
pos++;
if (pos == 65536) {
pos = 0;
}
mixerBuffer[pos] =
mixing ? (mixerBuffer[pos] * (127 - vol) / 127 + sample * vol / 127) / 2 : sample * vol / 127;
}
for (int i = 0; i < unmixedLength; i++) {
uint16_t rawSample = data[mixedLength + i] << 6; // ditto
int16_t sample = rawSample - 8192; // ditto
mixerBuffer[pos] = (sample * vol) >> 6; // ditto
pos++;
if (pos == 65536) {
pos = 0;
}
}
if (!mixing) {
if (unmixedLength > 0) {
mixerBufferEnd = pos;
// lilka::serial_log("New end: %d", mixerBufferEnd);
}
xSemaphoreGive(soundMutexHandle);
}
Expand All @@ -197,13 +218,16 @@ void soundTask(void* param) {
xSemaphoreTake(soundMutexHandle, portMAX_DELAY);
size_t written = 0;
TickType_t xLastWakeTime = xTaskGetTickCount();
esp_i2s::i2s_write(
esp_i2s::I2S_NUM_0,
mixerBuffer + mixerBufferStart,
MIN(mixerBufferEnd - mixerBufferStart, 512) * 2,
&written,
portMAX_DELAY
);
uint32_t chunkSize;
if (mixerBufferStart < mixerBufferEnd) {
// Buffer is contiguous
chunkSize = mixerBufferEnd - mixerBufferStart;
} else {
// Buffer is split
chunkSize = 65536 - mixerBufferStart;
}
chunkSize = MIN(chunkSize, 512) * 2; // 512 samples. Multiply by 2 because i2s_write expects bytes.
esp_i2s::i2s_write(esp_i2s::I2S_NUM_0, mixerBuffer + mixerBufferStart, chunkSize, &written, portMAX_DELAY);
mixerBufferStart += written / 2;
if (mixerBufferStart >= 65536) {
mixerBufferStart = 0;
Expand All @@ -213,9 +237,13 @@ void soundTask(void* param) {
// We do this to prevent blocking in i2s_write and thus acquiring the mutex for too long.
// This is done by calculating delay from sample rate & bytes written.
// TODO: BTW - ring buffer mixing sucks with variable sample rates... /AD
vTaskDelayUntil(
&xLastWakeTime, written / 2 * 1000 / 11025 / portTICK_PERIOD_MS * 4 / 5
); // Wait 4/5 of the time to prevent buffer underrun. Not the best solution, but works for now.
uint64_t delay = written / 2 * 1000 / 11025 / portTICK_PERIOD_MS;
// Wait 7/8 of the time to prevent buffer underrun. Not the best solution, but works for now.
delay = delay * 7 / 8;
if (delay) {
// vTaskDelayUntil doesn't like zero delays
vTaskDelayUntil(&xLastWakeTime, delay);
}
}
taskYIELD();
}
Expand Down
3 changes: 3 additions & 0 deletions firmware/doom/src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,9 @@ void setup() {
name.toLowerCase();
lilka::serial_log("Checking file: %s\n", name.c_str());
if (name.startsWith("doom") && name.endsWith(".wad")) {
if (firmwareDir.endsWith("/")) {
firmwareDir = firmwareDir.substring(0, firmwareDir.length() - 1);
}
strcpy(arg3, (lilka::fileutils.getSDRoot() + firmwareDir + "/" + file.name()).c_str());
lilka::serial_log("Found .WAD file: %s\n", arg3);
found = true;
Expand Down
40 changes: 39 additions & 1 deletion firmware/keira/src/apps/wifi_config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,30 @@

WiFiConfigApp::WiFiConfigApp() : App("WiFi") {
}
String WiFiConfigApp::getEncryptionTypeStr(uint8_t encryptionType) {
switch (encryptionType) {
case WIFI_AUTH_OPEN:
return "Open";
case WIFI_AUTH_WEP:
return "WEP";
case WIFI_AUTH_WPA_PSK:
return "WPA PSK";
case WIFI_AUTH_WPA2_PSK:
return "WPA2 PSK";
case WIFI_AUTH_WPA_WPA2_PSK:
return "WPA WPA2 PSK";
case WIFI_AUTH_WPA2_ENTERPRISE:
return "ENTERPRISE";
case WIFI_AUTH_WPA3_PSK:
return "WPA3 PSK";
case WIFI_AUTH_WPA2_WPA3_PSK:
return "WPA2 WPA3 PSK";
case WIFI_AUTH_WAPI_PSK:
return "WAPI PSK";
default:
return "Unknown";
}
}

void WiFiConfigApp::run() {
lilka::Canvas buffer(canvas->width(), canvas->height());
Expand Down Expand Up @@ -87,6 +111,7 @@ void WiFiConfigApp::run() {
);
}
menu.addItem("<< Назад");
menu.addActivationButton(lilka::Button::C);
count++;
while (1) {
while (!menu.isFinished()) {
Expand All @@ -98,7 +123,20 @@ void WiFiConfigApp::run() {
if (cursor == count - 1) {
return;
}

if (menu.getButton() == lilka::Button::C) {
int16_t index = menu.getCursor();
String networkInfo = "Канал: " + String(WiFi.channel(index)) + "\n";
networkInfo += "Сила сигналу: " + String(WiFi.RSSI(index)) + "db\n";
networkInfo += "MAC: " + WiFi.BSSIDstr(index) + "\n";
networkInfo += "Захист: " + getEncryptionTypeStr(WiFi.encryptionType(index)) + "\n";
lilka::Alert info(networks[menu.getCursor()], networkInfo);
while (!info.isFinished()) {
info.update();
info.draw(canvas);
queueDraw();
}
continue;
}
String ssid = networks[cursor];

String password = "";
Expand Down
1 change: 1 addition & 0 deletions firmware/keira/src/apps/wifi_config.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,6 @@ class WiFiConfigApp : public App {
WiFiConfigApp();

private:
String getEncryptionTypeStr(uint8_t encryptionType);
void run() override;
};
22 changes: 22 additions & 0 deletions hardware/v2/main.kicad_pcb
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,17 @@
(pintype "passive")
(uuid "d0877e75-fec3-44ab-85e2-84cecde024dc")
)
(model "${KICAD8_3DMODEL_DIR}/Buzzer_Beeper.3dshapes/Buzzer_TDK_PS1240P02BT_D12.2mm_H6.5mm.step"
(offset
(xyz 0 0 0)
)
(scale
(xyz 1 1 1)
)
(rotate
(xyz 0 0 0)
)
)
)
(footprint "Display:TFT_240x280_2pads"
(layer "F.Cu")
Expand Down Expand Up @@ -4462,6 +4473,17 @@
(xyz 0 0 0)
)
)
(model "${KICAD8_3DMODEL_DIR}/Button_Switch_THT.3dshapes/SW_CuK_JS202011AQN_DPDT_Angled.wrl"
(offset
(xyz 5 -3 0)
)
(scale
(xyz 1 1 1)
)
(rotate
(xyz 0 0 180)
)
)
)
(footprint "Capacitor_THT:C_Disc_D4.3mm_W1.9mm_P5.00mm"
(layer "F.Cu")
Expand Down
Binary file added img/v21.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 268d250

Please sign in to comment.