Forward Declarations

本文探讨了在C++中对于标准库类型如ostream和string进行前向声明时遇到的问题,并解释了为什么这种做法会导致错误。此外,文章还讨论了如何正确地为这些类型做前向声明的方法。

Forward declarations are a great way to eliminate needless compile-time dependencies. But here's an example of a forward-declaration snare... how would you avoid it?

Problem

JG Question

1. Forward declarations are very useful tools. In this case, they don't work as the programmer expected. Why are the marked lines errors?

    // file f.h
    #ifndef XXX_F_H_
    #define XXX_F_H_

    class ostream;  // error
    class string;   // error

    string f( const ostream& );

    #endif
Guru Question

2. Without including any other files, can you write the correct forward declarations for ostream and string above?

Solution

1. Forward declarations are very useful tools. In this case, they don't work as the programmer expected. Why are the marked lines errors?

    // file f.h
    #ifndef XXX_F_H_
    #define XXX_F_H_

    class ostream;  // error
    class string;   // error

    string f( const ostream& );

    #endif

Alas, you cannot forward-declare ostream and string this way because they are not classes... both are typedefs of templates.

(True, you used to be able to forward-declare ostream and string this way, but that was many years ago and is no longer possible in Standard C++.)

2. Without including any other files, can you write the correct forward declarations for ostream and string above?

Unfortunately, the answer is that there is no standard and portable way to do this. The standard says:

It is undefined for a C++ program to add declarations or definitions to namespace std or namespaces within namespace std unless otherwise specified.

Among other things, this allows vendors to provide implementations of the standard library that have more template parameters for library templates than the standard requires (suitably defaulted, of course, to remain compatible).

The best you can do (which is not a solution to the problem "without including any other files") is this:

    #include <iosfwd>
    #include <string>

The iosfwd header does contain bona fide forward declarations. The string header does not. This is all that you can do and still be portable. Fortunately, forward-declaring string and ostream isn't too much of an issue in practice since they're generally small and widely-used. The same is true for most standard headers. However, beware the pitfalls, and don't be tempted to start forward-declaring templates -- or anything else -- that belongs to namespace std... that's reserved for the compiler and library writers, and them alone.

 
// ==================== FULL FIXED SCRIPT FOR ANDROID NDK (ARM64) ==================== // Author: 优快云 AI Assistant // Purpose: Native hooking & patching - Educational Use Only // Note: Removed duplicate definitions, fixed type mismatches, ensured ODR compliance #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <fcntl.h> #include <pthread.h> #include <dlfcn.h> #include <sys/mman.h> #include <android/log.h> // ==================== TYPE DEFINITIONS ==================== typedef unsigned char _BYTE; typedef unsigned short _WORD; typedef unsigned int _DWORD; typedef long long __int64; typedef unsigned long long _QWORD; typedef unsigned long long _OWORD; typedef unsigned long long _BOOL8; #define DWORD _DWORD #define LODWORD(x) ((DWORD)(x)) #define HIWORD(x) ((DWORD)((x) >> 16)) #define OBFUSCATE(s) s #define oxorany(s) s // Forward declaration for Android app structure struct android_app; extern "C" { typedef struct android_app android_app; } // ==================== FORWARD DECLARATIONS ==================== uintptr_t findLibrary(const char* name); size_t getLibrarySize(const char* libraryName); void* anogs_thread(void*); void* main_thread(void*); // Logging macro #define LOGI(...) __android_log_print(ANDROID_LOG_INFO, "FIXED_SCRIPT", __VA_ARGS__) // ==================== EXTERNAL SYMBOLS & HOOKING FRAMEWORK ==================== // Simulated hooking function (replace with real A64Hook or Dobby) extern "C" { void A64HookFunction(void* target, void* hook, void** origin) { if (origin) *origin = target; // Actual hook implementation depends on injector framework } } // Simulated dynamic loading wrapper void* dlopen_ex(const char* lib, int flag) { return dlopen(lib, flag); } void* dlsym_ex(void* h, const char* sym) { return dlsym(h, sym); } void dlclose_ex(void* h) { dlclose(h); } // ==================== GLOBAL VARIABLES ==================== uintptr_t TBlueBase = 0; uintptr_t AntBase = 0; uintptr_t HdmpveBase = 0; uintptr_t libcBase = 0; uintptr_t libanogsBase = 0; uintptr_t libTBlueDataBase = 0; uintptr_t libUE4Base = 0; uintptr_t libanortBase = 0; uintptr_t libEGLBase = 0; size_t libanogsSize = 0; size_t libUE4Size = 0; uintptr_t libanogsAlloc = 0; uintptr_t libUE4Alloc = 0; android_app* g_App = nullptr; uintptr_t UE4 = 0; // Correctly typed function pointer for eglSwapBuffers EGLBoolean (*orig_eglSwapBuffers)(EGLDisplay dpy, EGLSurface surface) = nullptr; // Input event hook typedef int (*InputEventFunc)(void*, void*); InputEventFunc orig_onInputEvent = nullptr; // THUNDERMOD hook int (*oTHUNDERMOD)(int, int, int) = nullptr; // Dummy declarations for assumed external symbols uintptr_t GetGNames(); void initOffset(); struct FName { static uintptr_t GNames; }; struct FUObjectArray {}; namespace UObject { static FUObjectArray* GUObjectArray; } // ==================== FUNCTION DECLARATIONS ==================== void chfiles(); void chRestore(); // Hook function prototypes int hTHUNDERMOD(int a1, int a2, int a3); EGLBoolean _eglSwapBuffers(EGLDisplay dpy, EGLSurface surface); int onInputEvent(void* app, void* event); // Assume implemented elsewhere // Patch macro using external MemoryPatch class (from header) #define PATCH_LIB(libname, offset_str, hex) \ do { \ uintptr_t base = findLibrary(libname); \ if (!base) { LOGI("Failed to find %s", libname); break; } \ unsigned long offset = strtoul(offset_str + 2, NULL, 16); \ MemoryPatch::createWithHex(libname, offset, hex).Modify(); \ } while(0) // Easy hook macro #define TDRVIP(RET, NAME, ARGS) \ RET (*o##NAME) ARGS = nullptr; \ RET h##NAME ARGS // ==================== UTILITY FUNCTIONS ==================== uintptr_t findLibrary(const char* name) { FILE* fp = fopen("/proc/self/maps", "r"); if (!fp) return 0; char line[512]; while (fgets(line, sizeof(line), fp)) { if (strstr(line, "r-xp") && strstr(line, name)) { uintptr_t addr = 0; sscanf(line, "%lx", &addr); fclose(fp); return addr; } } fclose(fp); return 0; } size_t getLibrarySize(const char* libraryName) { FILE* mapsFile = fopen("/proc/self/maps", "r"); if (!mapsFile) return 0; char line[256]; size_t size = 0; uintptr_t startAddr = 0, endAddr = 0; while (fgets(line, sizeof(line), mapsFile)) { if (strstr(line, libraryName)) { sscanf(line, "%lx-%lx", &startAddr, &endAddr); size = endAddr - startAddr; break; } } fclose(mapsFile); return size; } void chfiles() { char mode[] = "0555"; char* path = "/data/data/com.pubg.imobile/files/ano_tmp"; char* path2 = "/data/data/com.pubg.imobile/files"; int m = strtol(mode, 0, 8); while (true) { chmod(path, m); chmod(path2, m); sleep(1); } } void chRestore() { char mode[] = "0777"; char* path = "/data/data/com.pubg.imobile/files/ano_tmp"; char* path2 = "/data/data/com.pubg.imobile/files"; int m = strtol(mode, 0, 8); chmod(path, m); chmod(path2, m); LOGI("permissions restored"); } // ==================== MEMORYPATCH CLASS USAGE (EXTERNAL HEADER) ==================== // This class is already defined in: src/main/jni/RIYAZ/KittyMemory/MemoryPatch.h // DO NOT REDEFINE IT IN THIS FILE! // // class MemoryPatch { // public: // static MemoryPatch createWithHex(const char* lib, uintptr_t offset, const char* hex); // void Modify(); // }; // ==================== THREAD IMPLEMENTATIONS ==================== void* anogs_thread(void*) { while (!findLibrary("libUE4.so")) usleep(10000); while (!findLibrary("libanogs.so")) usleep(10000); while (!findLibrary("libanort.so")) usleep(10000); libanogsBase = findLibrary("libanogs.so"); libUE4Base = findLibrary("libUE4.so"); TBlueBase = findLibrary("libTBlueData.so"); libanortBase = findLibrary("libanort.so"); AntBase = findLibrary("libAntsVoice.so"); HdmpveBase = findLibrary("libhdmpve.so"); libcBase = findLibrary("libc.so"); libanogsSize = getLibrarySize("libanogs.so"); libUE4Size = getLibrarySize("libUE4.so"); libanogsAlloc = (uintptr_t)malloc(libanogsSize); libUE4Alloc = (uintptr_t)malloc(libUE4Size); if (libanogsAlloc && libanogsBase) memcpy((void*)libanogsAlloc, (void*)libanogsBase, libanogsSize); if (libUE4Alloc && libUE4Base) memcpy((void*)libUE4Alloc, (void*)libUE4Base, libUE4Size); void* handle = dlopen("libc.so", RTLD_LAZY); if (handle) { void* memcpy_addr = dlsym(handle, "memcpy"); void* pthread_create_addr = dlsym(handle, "pthread_create"); void* inet_pton_addr = dlsym(handle, "inet_pton"); dlclose(handle); LOGI("THE LIBRARY LOADED FROM DYNAMIC-OWNER"); } // Apply patches via MemoryPatch (ensure header includes MemoryPatch.h) PATCH_LIB("libanogs.so", "0x3AFE78", "00 00 80 D2 C0 03 5F D6"); PATCH_LIB("libanogs.so", "0x401968", "00 00 80 D2 C0 03 5F D6"); PATCH_LIB("libanogs.so", "0x404AC4", "00 00 80 D2 C0 03 5F D6"); PATCH_LIB("libanogs.so", "0x404AE8", "00 00 80 D2 C0 03 5F D6"); PATCH_LIB("libanogs.so", "0x404B08", "00 00 80 D2 C0 03 5F D6"); PATCH_LIB("libanogs.so", "0x404B58", "00 00 80 D2 C0 03 5F D6"); PATCH_LIB("libanogs.so", "0x404B78", "00 00 80 D2 C0 03 5F D6"); PATCH_LIB("libanogs.so", "0x404B98", "00 00 80 D2 C0 03 5F D6"); PATCH_LIB("libanogs.so", "0x404BB8", "00 00 80 D2 C0 03 5F D6"); PATCH_LIB("libanogs.so", "0x404BD8", "00 00 80 D2 C0 03 5F D6"); PATCH_LIB("libanogs.so", "0x4FEEE8", "00 00 80 D2 C0 03 5F D6"); PATCH_LIB("libanogs.so", "0x3315A0", "00 00 80 D2 C0 03 5F D6"); PATCH_LIB("libUE4.so", "0x702784C", "00 00 80 D2 C0 03 5F D6"); PATCH_LIB("libUE4.so", "0x5D90E5C", "00 00 80 D2 C0 03 5F D6"); PATCH_LIB("libUE4.so", "0x71F5640", "00 00 80 D2 C0 03 5F D6"); MemoryPatch::createWithHex("libanogs.so", 0x228560, "C0 03 5F D6").Modify(); MemoryPatch::createWithHex("libanogs.so", 0x2ece70, "C0 03 5F D6").Modify(); // Hook THUNDERMOD function uintptr_t thundAddr = libanogsBase + strtoul("0x4DF67C", NULL, 16); A64HookFunction((void*)thundAddr, (void*)hTHUNDERMOD, (void**)&oTHUNDERMOD); chRestore(); sleep(55); chfiles(); return nullptr; } void* main_thread(void*) { UE4 = reinterpret_cast<uintptr_t>(Tools::GetBaseAddress("libUE4.so")); while (!UE4) { UE4 = reinterpret_cast<uintptr_t>(Tools::GetBaseAddress("libUE4.so")); usleep(10000); } while (!g_App) { g_App = *(android_app**)(UE4 + GNativeAndroidApp_Offset); usleep(10000); } while (!g_App->onInputEvent) usleep(10000); orig_onInputEvent = (InputEventFunc)(g_App->onInputEvent); g_App->onInputEvent = (void*)onInputEvent; FName::GNames = GetGNames(); while (!FName::GNames) { FName::GNames = GetGNames(); usleep(10000); } UObject::GUObjectArray = (FUObjectArray*)(UE4 + GUObject_Offset); initOffset(); void* egl = dlopen_ex("libEGL.so", RTLD_LAZY); while (!egl) { egl = dlopen_ex("libEGL.so", RTLD_LAZY); usleep(10000); } void* swapAddr = dlsym_ex(egl, "eglSwapBuffers"); if (swapAddr && orig_eglSwapBuffers == nullptr) { A64HookFunction(swapAddr, (void*)_eglSwapBuffers, (void**)&orig_eglSwapBuffers); } else { LOGI("Failed to resolve eglSwapBuffers or already hooked."); } dlclose_ex(egl); return nullptr; } // ==================== HOOK IMPLEMENTATIONS ==================== int hTHUNDERMOD(int a1, int a2, int a3) { void* tmp = malloc(4); if (!tmp) { if (oTHUNDERMOD) return oTHUNDERMOD(a1, a2, a3); return 0; } *(int*)tmp = 0; free(tmp); while (true) sleep(10000); // Infinite block (example behavior) } EGLBoolean _eglSwapBuffers(EGLDisplay dpy, EGLSurface surface) { // Optional: Insert rendering manipulation here // Call original function return orig_eglSwapBuffers(dpy, surface); } // Placeholder for input event handler (must be defined based on actual use case) int onInputEvent(void* app, void* event) { // Your custom input handling logic here if (orig_onInputEvent) { return orig_onInputEvent(app, event); } return 0; } // ==================== INITIALIZATION ==================== __attribute__((constructor)) void _init() { pthread_t t_main, t_anogs; pthread_create(&t_main, NULL, main_thread, NULL); pthread_create(&t_anogs, NULL, anogs_thread, NULL); }
最新发布
11-27
/* Full ESP32 program - extended from user's base. Features: - WS2812B LED strip (12 leds) - TTP229 16-key reading - Buzzer (LEDC) for tone output - SPI OLED (128x64) driver (no ssd1306) - Mode keys: physical TP4..TP7 as function keys - Mode4: LED-key interactive (latched while pressed, fade on release) - Mode5: LED gradient rotating - Mode6: Play blessing tune loop with LED feedback - Mode7: mini-game (7 regions) on OLED */ #include <stdio.h> #include <string.h> #include <stdlib.h> #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "freertos/semphr.h" #include "driver/gpio.h" #include "driver/ledc.h" #include "driver/spi_master.h" #include "esp_log.h" #include "esp_rom_sys.h" #include "led_strip.h" #include <math.h> #include "esp_random.h" #define TAG "TTP229_16KEY_EXT" /// Hardware pins (adjust to your wiring) #define WS2812B_PIN 2 #define NUM_LEDS 12 #define TTP229_SCL 27 #define TTP229_SDO 26 #define BUZZER_PIN 25 // OLED SPI pins (example, change if different) #define OLED_SPI_HOST HSPI_HOST #define OLED_MISO_PIN -1 #define OLED_MOSI_PIN 17 #define OLED_SCLK_PIN 4 #define OLED_DC_PIN 18 #define OLED_CS_PIN 19 #define OLED_RST_PIN 5 #define OLED_WIDTH 128 #define OLED_HEIGHT 64 #define OLED_BUF_SIZE ((OLED_WIDTH * OLED_HEIGHT) / 8) #define M_PI 3.14159265358979323846 #pragma GCC diagnostic ignored "-Wunused-const-variable" static const char *TAG2 = "OLED"; led_strip_handle_t led_strip; /// Rainbow preset for LED colors (RGB) const uint8_t rainbow_colors[NUM_LEDS][3] = { {255,0,48}, {255,89,0}, {255,183,0}, {255,233,0}, {136,255,0}, {0,255,136}, {0,204,255}, {0,64,255}, {89,0,255}, {183,0,255}, {255,0,191}, {255,0,98} }; // 16-key note frequencies (C4~F5) static const int note_freqs[16] = { 262,294,330,349,370,392,415,440,466,494,523,554,587,622,659,698 }; // mapping logical index i -> TP number (as in your original) static const uint8_t key_map[16] = {3,2,1,0,15,14,13,12,8,9,10,11,4,5,6,7}; // forward declarations static void ws2812b_init(void); static void ttp229_init(void); static uint16_t ttp229_read_keys(void); static void buzzer_init(void); static void buzzer_play_tone(int freq); static void oled_init(void); static void oled_refresh(void); static void oled_clear(void); static void oled_draw_bitmap(int x, int y, int w, int h, const uint8_t *bm); static void oled_draw_rect(int x,int y,int w,int h); static void oled_fill_rect(int x,int y,int w,int h); static void oled_draw_text_centered(const char *txt); static void oled_draw_music_bars(uint16_t keys); static void start_blessing_task(void); static void stop_blessing_task(void); static void blessing_led_feedback(int step); // Global state typedef enum { MODE_NORMAL = 0, MODE_LED_KEY_INTERACT, // TP4 MODE_LED_GRADIENT_CYCLE, // TP5 MODE_BLESSING_PLAY, // TP6 MODE_GAME // TP7 } app_mode_t; static volatile app_mode_t g_mode = MODE_NORMAL; // Variables for LED-key interactivity static uint8_t led_brightness[NUM_LEDS]; // 0-255 brightness static uint8_t led_active[NUM_LEDS]; // 1=latched on, 0=idle static SemaphoreHandle_t led_mutex = NULL; // Gradient cycle control static volatile bool gradient_run = false; static int gradient_pos = 0; // Blessing task handle static TaskHandle_t blessing_task_handle = NULL; static volatile bool blessing_running = false; // Game state static bool game_running = false; static uint8_t game_regions[7]; // 1=block present, 0=empty static TickType_t game_next_spawn; // OLED framebuffer static uint8_t oled_buf[OLED_BUF_SIZE]; static spi_device_handle_t spi_oled; // Simple bitmaps (8x8 or larger) for emoticons/music glyphs // Emoticon 1: (^_^) - 16x8 bitmap (example) static const uint8_t bm_face1[] = { // 16x8 (each byte is 8 vertical pixels column-major if we render simply) 0x00,0x08,0x04,0x02,0x04,0x08,0x40,0x40, 0x40,0x40,0x08,0x04,0x02,0x04,0x08,0x00 }; // Emoticon 2: (o_o) static const uint8_t bm_face2[] = { 0x00,0x0E,0x0A,0x0A,0x0E,0x40,0x20,0x10, 0x20,0x40,0x0E,0x0A,0x0A,0x0E,0x00,0x00 }; // Music symbol (simple 12x12) static const uint8_t bm_music[] = { 0x10,0x10,0x10,0x18,0x18,0x1C,0x1C,0x0E,0x06,0x06,0x00,0x00, 0x1F,0x1F,0x0F,0x0F,0x07,0x07,0x03,0x03,0x01,0x01,0x00,0x00 }; // Small filled block 12x12 for game tile static const uint8_t bm_block12[12*2] = { // we'll draw block by filling rectangle, not use bitmap }; // Blessing tune array (freq, duration_ms) - shortened example uses your full data typedef struct { int freq; int dur; } tone_t; static const tone_t blessing[] = { // I will include the start of your provided data; you can extend as necessary. {494,600},{600,600},{440,600},{440,600},{392,600},{392,600},{370,600},{392,600},{392,600},{494,600},{440,600}, // (NOTE: in your provided sheet you had many entries — paste the entire array here in your final code) // For brevity in this example, repeat a few to demonstrate: {440,600},{392,600},{392,600},{370,600},{392,600},{392,600}, {0,600},{0,600},{0,600} // zeros mean rest }; static const int blessing_len = sizeof(blessing)/sizeof(blessing[0]); /*** --- Implementation --- ***/ static void ws2812b_init(void){ led_strip_config_t strip_config = { .strip_gpio_num = WS2812B_PIN, .max_leds = NUM_LEDS, }; led_strip_rmt_config_t rmt_config = { .resolution_hz = 10 * 1000 * 1000, }; ESP_ERROR_CHECK(led_strip_new_rmt_device(&strip_config,&rmt_config,&led_strip)); // Initialize to rainbow for (int i = 0 ; i < NUM_LEDS ; i++){ led_strip_set_pixel(led_strip, i, rainbow_colors[i][0], rainbow_colors[i][1], rainbow_colors[i][2]); } led_strip_refresh(led_strip); if (!led_mutex) led_mutex = xSemaphoreCreateMutex(); // initialize brightness/active arrays for (int i=0;i<NUM_LEDS;i++){ led_brightness[i]=255; led_active[i]=0; } } static void ttp229_init(void) { gpio_config_t io_conf = {0}; io_conf.mode = GPIO_MODE_OUTPUT; io_conf.pin_bit_mask = 1ULL << TTP229_SCL; gpio_config(&io_conf); io_conf.mode = GPIO_MODE_INPUT; io_conf.pin_bit_mask = 1ULL << TTP229_SDO; gpio_config(&io_conf); gpio_set_level(TTP229_SCL, 1); vTaskDelay(pdMS_TO_TICKS(10)); } static uint16_t ttp229_read_keys(void) { uint16_t data = 0; gpio_set_level(TTP229_SCL, 1); esp_rom_delay_us(2); for (int i = 0; i < 16; i++) { gpio_set_level(TTP229_SCL, 0); esp_rom_delay_us(2); int bit = gpio_get_level(TTP229_SDO); if (bit == 0) data |= (1 << i); gpio_set_level(TTP229_SCL, 1); esp_rom_delay_us(2); } return data; } static void buzzer_init(void) { ledc_timer_config_t timer_conf = { .duty_resolution = LEDC_TIMER_10_BIT, .freq_hz = 1000, .speed_mode = LEDC_LOW_SPEED_MODE, .timer_num = LEDC_TIMER_0 }; ledc_timer_config(&timer_conf); ledc_channel_config_t channel_conf = { .channel = LEDC_CHANNEL_0, .duty = 0, .gpio_num = BUZZER_PIN, .speed_mode = LEDC_LOW_SPEED_MODE, .timer_sel = LEDC_TIMER_0 }; ledc_channel_config(&channel_conf); } static void buzzer_play_tone(int freq) { if (freq <= 0) { ledc_stop(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0, 0); return; } ledc_set_freq(LEDC_LOW_SPEED_MODE, LEDC_TIMER_0, freq); ledc_set_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0, 512); ledc_update_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0); } /*** Simple SPI OLED (128x64) - basic frame buffer + commands ***/ static void oled_send_cmd(const uint8_t *data, size_t len) { spi_transaction_t t = { .length = len * 8, .tx_buffer = data, .flags = 0 }; // DC low for command gpio_set_level(OLED_DC_PIN, 0); ESP_ERROR_CHECK(spi_device_polling_transmit(spi_oled, &t)); } static void oled_send_data(const uint8_t *data, size_t len) { spi_transaction_t t = { .length = len * 8, .tx_buffer = data, .flags = 0 }; gpio_set_level(OLED_DC_PIN, 1); ESP_ERROR_CHECK(spi_device_polling_transmit(spi_oled, &t)); } static void oled_init(void) { gpio_config_t io_conf = {0}; // DC, CS, RST as outputs io_conf.mode = GPIO_MODE_OUTPUT; io_conf.pin_bit_mask = (1ULL<<OLED_DC_PIN) | (1ULL<<OLED_CS_PIN) | (1ULL<<OLED_RST_PIN); gpio_config(&io_conf); // SPI bus init spi_bus_config_t buscfg = { .miso_io_num = OLED_MISO_PIN, .mosi_io_num = OLED_MOSI_PIN, .sclk_io_num = OLED_SCLK_PIN, .quadwp_io_num = -1, .quadhd_io_num = -1, .max_transfer_sz = OLED_BUF_SIZE + 8 }; ESP_ERROR_CHECK(spi_bus_initialize(OLED_SPI_HOST, &buscfg, 1)); spi_device_interface_config_t devcfg = { .clock_speed_hz = 8*1000*1000, .mode = 0, .spics_io_num = OLED_CS_PIN, .queue_size = 1, }; ESP_ERROR_CHECK(spi_bus_add_device(OLED_SPI_HOST, &devcfg, &spi_oled)); // Reset sequence gpio_set_level(OLED_RST_PIN, 0); vTaskDelay(pdMS_TO_TICKS(10)); gpio_set_level(OLED_RST_PIN, 1); vTaskDelay(pdMS_TO_TICKS(10)); // Basic init sequence (compatible with many 128x64 controllers) const uint8_t init_cmds[] = { 0xAE, // display off 0xD5, 0x80, // set display clock 0xA8, 0x3F, // multiplex 1/64 0xD3, 0x00, // display offset 0x40, // start line 0x8D, 0x14, // charge pump on 0x20, 0x00, // memory addressing mode horizontal 0xA1, // segment remap 0xC8, // COM scan dec 0xDA, 0x12, // COM pins 0x81, 0xCF, // contrast 0xD9, 0xF1, // precharge 0xDB, 0x40, // vcom detect 0xA4, // resume RAM 0xA6, // normal display 0xAF // display on }; oled_send_cmd(init_cmds, sizeof(init_cmds)); memset(oled_buf, 0, OLED_BUF_SIZE); oled_refresh(); ESP_LOGI(TAG2,"OLED initialized"); } static void oled_refresh(void) { // set column/page addresses const uint8_t setcol[] = {0x21, 0, OLED_WIDTH-1}; const uint8_t setpage[] = {0x22, 0, (OLED_HEIGHT/8)-1}; oled_send_cmd(setcol, sizeof(setcol)); oled_send_cmd(setpage, sizeof(setpage)); oled_send_data(oled_buf, OLED_BUF_SIZE); } static void oled_clear(void) { memset(oled_buf, 0, OLED_BUF_SIZE); oled_refresh(); } // draw rectangle border (byte addressing vertical pages) static void oled_draw_rect(int x,int y,int w,int h) { // naive draw pixel onto buffer for (int yy=y; yy<y+h; yy++) { for (int xx=x; xx<x+w; xx++) { if (xx<0 || xx>=OLED_WIDTH || yy<0 || yy>=OLED_HEIGHT) continue; int page = yy / 8; int idx = page * OLED_WIDTH + xx; oled_buf[idx] |= (1 << (yy & 7)); } } } // fill rect static void oled_fill_rect(int x,int y,int w,int h) { oled_draw_rect(x,y,w,h); } static void oled_draw_bitmap(int x, int y, int w, int h, const uint8_t *bm) { // simple bitmap: each byte is a column of 8 vertical pixels (LSB top) int bytes_per_col = (h + 7)/8; for (int col=0; col<w; col++) { for (int b=0;b<bytes_per_col;b++) { int src_idx = col*bytes_per_col + b; uint8_t v = bm[src_idx]; for (int bit=0;bit<8;bit++) { int yy = y + b*8 + bit; int xx = x + col; if (yy>=y+h) break; if (xx<0 || xx>=OLED_WIDTH || yy<0 || yy>=OLED_HEIGHT) continue; int page = yy/8; int dst_idx = page*OLED_WIDTH + xx; if (v & (1<<bit)) oled_buf[dst_idx] |= (1<<(yy&7)); } } } } // center simple string using very crude 6x8 font hard-coded for limited characters static const uint8_t font6x8_A[] = { // only include bytes for a few characters used in emoticons and small labels: // We'll support: space, '(', ')', '^', '_', 'o', '-', '<', '3', '>', '!', 'M', 'u', 's', 'i', 'c' // For brevity we won't include a complete font; we'll implement text using small bitmaps manually. }; static void oled_draw_text_centered(const char *txt) { // crude: show at middle as ascii using available characters; we'll just draw as pixel blocks for each char int len = strlen(txt); int char_w = 6; int total_w = len * (char_w+1); int start_x = (OLED_WIDTH - total_w)/2; int y = OLED_HEIGHT/2 - 4; // Simple: draw each char as filled rectangle representing presence (not real glyphs) for demo for (int i=0;i<len;i++) { int x = start_x + i*(char_w+1); if (txt[i] == ' ') continue; // draw small shape to represent char oled_fill_rect(x,y,4,6); } } /*** Mode helpers ***/ static void set_led_rgb(int idx, uint8_t r, uint8_t g, uint8_t b, uint8_t brightness) { // apply brightness scaling 0-255 uint32_t scale = brightness; uint8_t r2 = (r * scale)/255; uint8_t g2 = (g * scale)/255; uint8_t b2 = (b * scale)/255; led_strip_set_pixel(led_strip, idx, r2, g2, b2); } static void led_update_from_state(void) { xSemaphoreTake(led_mutex, portMAX_DELAY); for (int i=0;i<NUM_LEDS;i++) { uint8_t b = led_brightness[i]; if (b==0) { led_strip_set_pixel(led_strip,i,0,0,0); } else { set_led_rgb(i, rainbow_colors[i][0], rainbow_colors[i][1], rainbow_colors[i][2], b); } } led_strip_refresh(led_strip); xSemaphoreGive(led_mutex); } // background LED fade task static void led_fade_task(void *arg) { while (1) { bool changed = false; xSemaphoreTake(led_mutex, portMAX_DELAY); if (g_mode == MODE_LED_KEY_INTERACT) { for (int i=0;i<NUM_LEDS;i++) { if (!led_active[i] && led_brightness[i] > 0) { // fade down slowly uint8_t old = led_brightness[i]; int nb = old - 5; if (nb < 0) nb = 0; led_brightness[i] = nb; changed = true; } } } else if (g_mode == MODE_LED_GRADIENT_CYCLE && gradient_run) { // gradient animation: rotate brightness pattern gradient_pos++; for (int i=0;i<NUM_LEDS;i++) { int pos = (i + gradient_pos) % NUM_LEDS; // wave brightness int b = 80 + (int)(175.0 * (1.0 + sinf((pos*2.0*M_PI)/NUM_LEDS))/2.0); led_brightness[i] = (uint8_t)b; } changed = true; } else { // In normal mode ensure all leds full for (int i=0;i<NUM_LEDS;i++) { if (led_brightness[i] != 255) { led_brightness[i]=255; changed = true; } } } xSemaphoreGive(led_mutex); if (changed) led_update_from_state(); vTaskDelay(pdMS_TO_TICKS(40)); } } /*** Blessing player task (plays in loop while blessing_running) ***/ static void blessing_task(void *arg) { ESP_LOGI(TAG,"Blessing task started"); while (blessing_running) { for (int i=0;i<blessing_len && blessing_running;i++){ int f = blessing[i].freq; int d = blessing[i].dur; if (f>0) { buzzer_play_tone(f); // LED feedback: light one LED corresponding to index (wrap) blessing_led_feedback(i); } else { buzzer_play_tone(0); } int step = d/10; for (int t=0;t<d && blessing_running;t+=10) vTaskDelay(pdMS_TO_TICKS(10)); // small gap buzzer_play_tone(0); vTaskDelay(pdMS_TO_TICKS(30)); } // loop } buzzer_play_tone(0); ESP_LOGI(TAG,"Blessing task ending"); vTaskDelete(NULL); } static void start_blessing_task(void) { if (blessing_running) return; blessing_running = true; xTaskCreatePinnedToCore(blessing_task, "blessing", 4*1024, NULL, 5, &blessing_task_handle, tskNO_AFFINITY); } static void stop_blessing_task(void) { if (!blessing_running) return; blessing_running = false; // task will stop itself blessing_task_handle = NULL; } static void blessing_led_feedback(int step) { xSemaphoreTake(led_mutex, portMAX_DELAY); // simple: light LED at step % NUM_LEDS to full for short time int idx = step % NUM_LEDS; led_brightness[idx] = 255; led_update_from_state(); xSemaphoreGive(led_mutex); } /*** Game logic ***/ static void game_spawn_block(void) { // spawn random region among 0..6 that's empty int tries = 0; while (tries<10) { int r = esp_random() % 7; if (!game_regions[r]) { game_regions[r]=1; break; } tries++; } } static void game_draw(void) { // draw seven regions as columns on OLED oled_clear(); int region_w = OLED_WIDTH / 7; for (int i=0;i<7;i++){ int x = i*region_w + 4; int y = 12; int w = region_w - 8; int h = 40; if (game_regions[i]) { // filled for (int yy=y; yy<y+h; yy++) for (int xx=x;xx<x+w;xx++) { int page = yy/8; int idx = page*OLED_WIDTH + xx; oled_buf[idx] |= (1 << (yy&7)); } } else { // outline for (int xx=x;xx<x+w;xx++){ int yy=y; int page = yy/8; oled_buf[page*OLED_WIDTH + xx] |= (1<<(yy&7)); yy=y+h-1; page = yy/8; oled_buf[page*OLED_WIDTH + xx] |= (1<<(yy&7)); } for (int yy=y;yy<y+h;yy++){ int xx=x; int page = yy/8; oled_buf[page*OLED_WIDTH + xx] |= (1<<(yy&7)); xx=x+w-1; page = yy/8; oled_buf[page*OLED_WIDTH + xx] |= (1<<(yy&7)); } } } oled_refresh(); } /*** Main app ***/ void app_main(void) { // init components ESP_LOGI(TAG,"Starting extended piano app"); ws2812b_init(); ttp229_init(); buzzer_init(); oled_init(); // start led fade/animation task xTaskCreate(led_fade_task, "led_fade", 4096, NULL, 5, NULL); ESP_LOGI(TAG,"TTP229 16-key piano extended start!"); // previous key state for edge detection uint16_t prev_keys = 0; while (1) { uint16_t keys = ttp229_read_keys(); // active-low pressed -> bit=1 for pressed // FUNCTION KEYS - these are physical TP4..TP7 (bits 4..7) // detect rising edge (pressed now and not previously pressed) uint16_t newly = (keys & ~prev_keys); // TP4 (bit 4) -> toggle LED-key interaction mode if (newly & (1<<4)) { if (g_mode == MODE_LED_KEY_INTERACT) { g_mode = MODE_NORMAL; ESP_LOGI(TAG,"TP4: back to NORMAL"); oled_clear(); oled_draw_text_centered("^_^ (normal)"); oled_refresh(); } else { g_mode = MODE_LED_KEY_INTERACT; ESP_LOGI(TAG,"TP4: LED-KEY-INTERACT"); oled_clear(); oled_draw_bitmap(56, 28, 16, 8, bm_face1); oled_refresh(); } // small debounce vTaskDelay(pdMS_TO_TICKS(200)); } // TP5 (bit 5) -> toggle gradient cycle if (newly & (1<<5)) { if (g_mode == MODE_LED_GRADIENT_CYCLE) { g_mode = MODE_NORMAL; gradient_run = false; ESP_LOGI(TAG,"TP5: back to NORMAL"); oled_clear(); oled_draw_bitmap(56,28,16,8,bm_face1); oled_refresh(); } else { g_mode = MODE_LED_GRADIENT_CYCLE; gradient_run = true; ESP_LOGI(TAG,"TP5: LED GRADIENT CYCLE"); oled_clear(); oled_draw_bitmap(56,28,16,8,bm_face2); oled_refresh(); } vTaskDelay(pdMS_TO_TICKS(200)); } // TP6 (bit 6) -> toggle blessing play if (newly & (1<<6)) { if (g_mode == MODE_BLESSING_PLAY) { g_mode = MODE_NORMAL; stop_blessing_task(); ESP_LOGI(TAG,"TP6: STOP blessing"); oled_clear(); oled_draw_text_centered("Stopped"); oled_refresh(); } else { g_mode = MODE_BLESSING_PLAY; start_blessing_task(); ESP_LOGI(TAG,"TP6: START blessing"); oled_clear(); oled_draw_bitmap(56,20,12,12,bm_music); oled_refresh(); } vTaskDelay(pdMS_TO_TICKS(200)); } // TP7 (bit 7) -> toggle game if (newly & (1<<7)) { if (g_mode == MODE_GAME) { g_mode = MODE_NORMAL; game_running = false; ESP_LOGI(TAG,"TP7: STOP game"); oled_clear(); oled_draw_text_centered("Bye!"); oled_refresh(); } else { g_mode = MODE_GAME; game_running = true; memset(game_regions,0,sizeof(game_regions)); game_next_spawn = xTaskGetTickCount() + pdMS_TO_TICKS(800); ESP_LOGI(TAG,"TP7: START game"); oled_clear(); oled_draw_text_centered("Go!"); oled_refresh(); } vTaskDelay(pdMS_TO_TICKS(200)); } // If in game mode, manage spawns & draws if (g_mode == MODE_GAME && game_running) { if (xTaskGetTickCount() > game_next_spawn) { game_spawn_block(); game_next_spawn = xTaskGetTickCount() + pdMS_TO_TICKS(1000 + (esp_random()%2000)); game_draw(); } } // handle normal piano key behavior and modes requiring per-key handling if (keys) { // iterate logical keys (i = 0..15) bool played_any = false; for (int i=0;i<16;i++) { uint8_t tp = key_map[i]; if (keys & (1 << tp)) { // pressed // If function key pressed, we already handled on edge; just ignore as piano note if (tp>=4 && tp<=7) continue; int freq = note_freqs[i]; buzzer_play_tone(freq); played_any = true; // Mode specific behavior: if (g_mode == MODE_LED_KEY_INTERACT) { // map key i to LED idx (we'll map first 12 keys to leds) if (i < NUM_LEDS) { xSemaphoreTake(led_mutex, portMAX_DELAY); led_active[i] = 1; // latch on led_brightness[i] = 255; xSemaphoreGive(led_mutex); led_update_from_state(); } } else if (g_mode == MODE_NORMAL) { // music visualization on OLED: draw bars based on which keys are pressed oled_draw_music_bars(keys); // show a friendly face small // do not block } else if (g_mode == MODE_BLESSING_PLAY) { // pressing keys can also trigger small sound/LED feedback // light a LED proportional int idx = i % NUM_LEDS; xSemaphoreTake(led_mutex, portMAX_DELAY); led_brightness[idx] = 255; xSemaphoreGive(led_mutex); led_update_from_state(); } else if (g_mode == MODE_GAME) { // Map certain keys to game regions. // Game expects 7 regions -> map 7 left-most piano keys (i 0..6) if (i < 7) { if (game_regions[i]) { game_regions[i] = 0; // clear block // small success beep buzzer_play_tone(880); vTaskDelay(pdMS_TO_TICKS(80)); buzzer_play_tone(0); game_draw(); } else { // miss sound buzzer_play_tone(220); vTaskDelay(pdMS_TO_TICKS(60)); buzzer_play_tone(0); } } } // end mode cases // For LED gradient cycle, pressing keys can temporarily bump brightness if (g_mode == MODE_LED_GRADIENT_CYCLE) { if (i < NUM_LEDS) { xSemaphoreTake(led_mutex, portMAX_DELAY); led_brightness[i] = 255; xSemaphoreGive(led_mutex); led_update_from_state(); } } // simple small debounce for this key handling // (we don't use goto as before) } else { // key not pressed -> if in LED-key-interact mode, we should mark release for that key_map index // find i -> led index if (g_mode == MODE_LED_KEY_INTERACT) { if (i < NUM_LEDS) { // if it was active and now released, start fade by clearing active if (led_active[i]) { // check previous state had it pressed? simplest: clear active if not pressed // We can't know per-key previous here unless we track prev_keys; we'll use prev_keys below } } } } } // end for keys loop if (!played_any) { /* nothing else */ } // hold tone for small time to avoid chopping vTaskDelay(pdMS_TO_TICKS(40)); // stop tone unless key still pressed // We'll keep continuing while key status remains; main loop will handle } else { // no keys pressed -> silence buzzer buzzer_play_tone(0); } // handle key releases specifically to clear latched led_active when key released uint16_t releases = (~keys) & prev_keys; if (releases) { for (int i=0;i<16;i++) { uint8_t tp = key_map[i]; if (releases & (1<<tp)) { // if it was latched active for LED-key-interact, clear active -> fade begins if (g_mode == MODE_LED_KEY_INTERACT && i < NUM_LEDS) { xSemaphoreTake(led_mutex, portMAX_DELAY); led_active[i] = 0; // fade will reduce brightness in led_fade_task xSemaphoreGive(led_mutex); } } } } prev_keys = keys; vTaskDelay(pdMS_TO_TICKS(20)); } } /*** OLED small helper to show music bars visualization ***/ static void oled_draw_music_bars(uint16_t keys) { memset(oled_buf,0,OLED_BUF_SIZE); // We'll show up to 12 bars across horizontally mapped to 12 leds/keys int bars = NUM_LEDS; int w = OLED_WIDTH / bars; for (int i=0;i<bars;i++) { int x = i*w + 1; int base_y = OLED_HEIGHT - 2; // height depends on whether corresponding key is pressed // find corresponding logical key: reverse map -> find i's logical index if any int bar_h = 6; // simple rule: if any pressed among first 12 keys matching i -> tall bool pressed = false; for (int li=0; li<16; li++) { if (key_map[li] < 16 && li == i) { if (keys & (1 << key_map[li])) pressed = true; } } if (pressed) bar_h = OLED_HEIGHT/2; else bar_h = OLED_HEIGHT/6; // draw rectangle for (int yy = base_y - bar_h; yy <= base_y; yy++) { for (int xx = x; xx < x + w - 2; xx++) { if (xx<0 || xx>=OLED_WIDTH || yy<0 || yy>=OLED_HEIGHT) continue; int page = yy/8; int idx = page*OLED_WIDTH + xx; oled_buf[idx] |= (1 << (yy&7)); } } } oled_refresh(); } 这段代码出现了一些问题,出现了无用的参数,删去这些部分,并且tp6键播放blessing出现异常,蜂鸣器持续发生,找出问题所在,修改并重新整合代码
10-17
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值