from pathlib import Path import re,subprocess,tempfile src=(Path(__file__).resolve().parents[1]/'components/platform/src/lcd_direct.c').read_text() def extract(name): m=re.search(r'static [^;{}]+\b'+name+r'\([^;{}]*\)\s*\{',src) a=m.start(); b=src.index('{',a)+1; depth=1 while depth: if src[b]=='{': depth+=1 if src[b]=='}': depth-=1 b+=1 return src[a:b] cache_table=re.search(r'static const uint8_t s_emote_cache_closures.*?\n};',src,re.S).group(0) prefix='''#include #include #include #include #include #include #define LCD_DIRECT_WIDTH 320 #define LCD_DIRECT_EMOTE_CACHE_COUNT 9 #define LCD_DIRECT_EMOTE_CLOSED_THRESHOLD 232 #define LCD_DIRECT_EMOTE_CACHE_X 39 #define LCD_DIRECT_EMOTE_CACHE_Y 63 #define LCD_DIRECT_EMOTE_CACHE_WIDTH 242 #define LCD_DIRECT_EMOTE_CACHE_HEIGHT 97 #define LCD_DIRECT_EMOTE_DMA_ROWS 16 #define LCD_DIRECT_TRANSFER_TIMEOUT_MS 100 #define portENTER_CRITICAL(x) ((void)0) #define portEXIT_CRITICAL(x) ((void)0) #define pdMS_TO_TICKS(x) (x) #define ESP_LOGE(...) ((void)0) #define ESP_OK 0 #define ESP_ERR_INVALID_SIZE 1 #define ESP_ERR_TIMEOUT 2 #define pdTRUE 1 typedef int esp_err_t; typedef int gfx_disp_t; static bool s_emote_active, s_emote_transfer_fault; static uint32_t s_emote_completed_regions; static uint64_t s_emote_copy_us, s_emote_spi_us; static int64_t fake_time; static int64_t esp_timer_get_time(void) {return ++fake_time;} static int s_emote_flush_error, s_panel, s_transfer_done_sem; static uint16_t *s_emote_capture_pixels; static uint16_t dma[5120]; static uint16_t *s_emote_dma_pixels=dma; static uint16_t capture[320*240]; static int calls,ready,fail_call,timeout_call,origin_y,width; static bool gfx_disp_flush_ready(gfx_disp_t *d,bool swap) {++ready;return true;} static int xSemaphoreTake(int sem,int ticks) {return ticks && calls!=timeout_call;} static int esp_lcd_panel_draw_bitmap(int panel,int x1,int y1,int x2,int y2,const void *data) { ++calls; assert(ready==0); assert((x2-x1)*(y2-y1)<=5120); assert(data==dma); if(calls==fail_call) return 3; memcpy(capture+(y1-origin_y)*width,data,(x2-x1)*(y2-y1)*2); return 0; } ''' test='''int main(void) { for(unsigned i=0;i<9;++i) assert(lcd_direct_emote_cache_index(s_emote_cache_closures[i])==(int)i); assert(lcd_direct_emote_cache_index(255)==8); assert(lcd_direct_emote_cache_index(1)==-1); assert(lcd_direct_emote_fps_x100(12,570000)==2105); assert(lcd_direct_emote_fps_x100(9,570000)==1578); assert(lcd_direct_emote_fps_x100(12,0)==0); assert(lcd_direct_emote_fps_x100(0,570000)==0); uint16_t source[320*240]; for(int i=0;i<320*240;++i) source[i]=(uint16_t)i; uint16_t cache[242*97+2]; cache[0]=cache[242*97+1]=0xabcd; s_emote_capture_pixels=cache+1; calls=ready=0; lcd_direct_emote_flush(NULL,0,0,320,240,source); assert(calls==0 && ready==1); for(int y=0;y<97;++y) for(int x=0;x<242;++x) assert(cache[1+y*242+x]==source[(y+63)*320+x+39]); assert(cache[0]==0xabcd && cache[242*97+1]==0xabcd); memset(cache+1,0,242*97*2); for(int y=0;y<240;y+=8) lcd_direct_emote_capture(cache+1,0,y,320,y+8,source+y*320); for(int y=0;y<97;++y) for(int x=0;x<242;++x) assert(cache[1+y*242+x]==source[(y+63)*320+x+39]); s_emote_capture_pixels=NULL; for(width=1;width<=320;++width) { calls=ready=fail_call=timeout_call=0;origin_y=65;s_emote_active=true; s_emote_transfer_fault=false;s_emote_flush_error=0; const uint32_t before=s_emote_completed_regions; lcd_direct_emote_flush(NULL,0,65,width,158,source); assert(s_emote_completed_regions==before+CONFIG_TQ_LCD_EMOTE_FPS_LOG); assert((s_emote_copy_us>0)==CONFIG_TQ_LCD_EMOTE_FPS_LOG); assert((s_emote_spi_us>0)==CONFIG_TQ_LCD_EMOTE_FPS_LOG); assert(ready==1 && s_emote_flush_error==0); assert(memcmp(source,capture,width*93*2)==0); assert(calls==(93+5120/width-1)/(5120/width)); } width=238;calls=ready=0;timeout_call=2;s_emote_flush_error=0; lcd_direct_emote_flush(NULL,41,65,279,158,source); assert(ready==1 && s_emote_transfer_fault && s_emote_flush_error==2 && calls==2); lcd_direct_emote_flush(NULL,41,65,279,158,source); assert(calls==2 && ready==2); s_emote_transfer_fault=false;timeout_call=0;fail_call=1;calls=ready=0; lcd_direct_emote_flush(NULL,41,65,279,158,source); assert(calls==1 && ready==1 && s_emote_flush_error==3); calls=ready=0;s_emote_active=false; lcd_direct_emote_flush(NULL,41,65,279,158,source);assert(calls==0 && ready==1); uint8_t eyes[238*14*3+2];memset(eyes,123,sizeof(eyes)); lcd_direct_emote_draw_closed_eyes(eyes+1,238,14,0xffe0); assert(eyes[0]==123 && eyes[sizeof(eyes)-1]==123); const uint8_t *alpha=eyes+1+238*14*2; for(int y=0;y<14;++y) for(int x=3;x<=95;++x) assert(alpha[y*238+x]==alpha[y*238+x+140]); assert(alpha[3*238+49]==255 && alpha[11*238+3]==255); puts("PASS: 320 transfer widths, DMA bounds/order, timeout latch, inactive/error completion and symmetric closed-eye raster"); }''' with tempfile.TemporaryDirectory(prefix='emote-transport-') as tmp: c=Path(tmp)/'test.c';exe=Path(tmp)/'test' for enabled in (0,1): c.write_text('#define CONFIG_TQ_LCD_EMOTE_FPS_LOG '+str(enabled)+'\n'+prefix+cache_table+'\n'+extract('lcd_direct_emote_cache_index')+'\n'+ extract('lcd_direct_emote_capture')+'\n'+extract('lcd_direct_emote_flush')+'\n'+extract('lcd_direct_emote_draw_closed_eyes')+'\n'+ extract('lcd_direct_emote_fps_x100')+'\n'+test) subprocess.run(['clang','-std=c11','-fsanitize=address,undefined',str(c),'-o',str(exe)],check=True) subprocess.run([str(exe)],check=True) print('PASS: cache mapping/capture without LCD transfer, FPS logging on/off and measured-rate arithmetic')