TP框架中的多种方法代码(C,G,L,T,I,N,D,M,A,R,B,U,W,S,F,E)

本文深入探讨了PHP框架中常用的核心方法,如配置管理、语言包处理、模型和控制器实例化等,为开发者提供了一个全面的理解框架内部工作原理的视角。

C方法

function C($name=null, $value=null,$default=null) {
    static $_config = array();
    // 无参数时获取所有
    if (empty($name)) {
        return $_config;
    }
    // 优先执行设置获取或赋值
    if (is_string($name)) {
        if (!strpos($name, '.')) {
            $name = strtolower($name);
            if (is_null($value))
                return isset($_config[$name]) ? $_config[$name] : $default;
            $_config[$name] = $value;
            return;
        }
        // 二维数组设置和获取支持
        $name = explode('.', $name);
        $name[0]   =  strtolower($name[0]);
        if (is_null($value))
            return isset($_config[$name[0]][$name[1]]) ? $_config[$name[0]][$name[1]] : $default;
        $_config[$name[0]][$name[1]] = $value;
        return;
    }
    // 批量设置
    if (is_array($name)){
        $_config = array_merge($_config, array_change_key_case($name));
        return;
    }
    return null; // 避免非法参数
}

G方法

function G($start,$end='',$dec=4) {
    static $_info       =   array();
    static $_mem        =   array();
    if(is_float($end)) { // 记录时间
        $_info[$start]  =   $end;
    }elseif(!empty($end)){ // 统计时间和内存使用
        if(!isset($_info[$end])) $_info[$end]       =  microtime(TRUE);
        if(MEMORY_LIMIT_ON && $dec=='m'){
            if(!isset($_mem[$end])) $_mem[$end]     =  memory_get_usage();
            return number_format(($_mem[$end]-$_mem[$start])/1024);
        }else{
            return number_format(($_info[$end]-$_info[$start]),$dec);
        }

    }else{ // 记录时间和内存使用
        $_info[$start]  =  microtime(TRUE);
        if(MEMORY_LIMIT_ON) $_mem[$start]           =  memory_get_usage();
    }
}

L方法

function L($name=null, $value=null) {
    static $_lang = array();
    // 空参数返回所有定义
    if (empty($name))
        return $_lang;
    // 判断语言获取(或设置)
    // 若不存在,直接返回全大写$name
    if (is_string($name)) {
        $name = strtoupper($name);
        if (is_null($value))
            return isset($_lang[$name]) ? $_lang[$name] : $name;
        $_lang[$name] = $value; // 语言定义
        return;
    }
    // 批量定义
    if (is_array($name))
        $_lang = array_merge($_lang, array_change_key_case($name, CASE_UPPER));
    return;
}

T方法

function T($template='',$layer=''){

    // 解析模版资源地址
    if(false === strpos($template,'://')){
        $template   =   'http://'.str_replace(':', '/',$template);
    }
    $info   =   parse_url($template);
    $file   =   $info['host'].(isset($info['path'])?$info['path']:'');
    $module =   isset($info['user'])?$info['user'].'/':MODULE_NAME.'/';
    $extend =   $info['scheme'];
    $layer  =   $layer?$layer:C('DEFAULT_V_LAYER');

    // 获取当前主题的模版路径
    $auto   =   C('AUTOLOAD_NAMESPACE');
    if($auto && isset($auto[$extend])){ // 扩展资源
        $baseUrl    =   $auto[$extend].$module.$layer.'/';
    }elseif(C('VIEW_PATH')){ // 指定视图目录
        $baseUrl    =   C('VIEW_PATH').$module.'/';
    }else{
        $baseUrl    =   APP_PATH.$module.$layer.'/';
    }

    // 获取主题
    $theme  =   substr_count($file,'/')<2 ? C('DEFAULT_THEME') : '';

    // 分析模板文件规则
    $depr   =   C('TMPL_FILE_DEPR');
    if('' == $file) {
        // 如果模板文件名为空 按照默认规则定位
        $file = CONTROLLER_NAME . $depr . ACTION_NAME;
    }elseif(false === strpos($file, '/')){
        $file = CONTROLLER_NAME . $depr . $file;
    }elseif('/' != $depr){
        if(substr_count($file,'/')>1){
            $file   =   substr_replace($file,$depr,strrpos($file,'/'),1);
        }else{
            $file   =   str_replace('/', $depr, $file);
        }
    }
    return $baseUrl.($theme?$theme.'/':'').$file.C('TMPL_TEMPLATE_SUFFIX');
}


I方法

function I($name,$default='',$filter=null) {
    if(strpos($name,'.')) { // 指定参数来源
        list($method,$name) =   explode('.',$name,2);
    }else{ // 默认为自动判断
        $method =   'param';
    }
    switch(strtolower($method)) {
        case 'get'     :   $input =& $_GET;break;
        case 'post'    :   $input =& $_POST;break;
        case 'put'     :   parse_str(file_get_contents('php://input'), $input);break;
        case 'param'   :
            switch($_SERVER['REQUEST_METHOD']) {
                case 'POST':
                    $input  =  $_POST;
                    break;
                case 'PUT':
                    parse_str(file_get_contents('php://input'), $input);
                    break;
                default:
                    $input  =  $_GET;
            }
            break;
        case 'request' :   $input =& $_REQUEST;   break;
        case 'session' :   $input =& $_SESSION;   break;
        case 'cookie'  :   $input =& $_COOKIE;    break;
        case 'server'  :   $input =& $_SERVER;    break;
        case 'globals' :   $input =& $GLOBALS;    break;
        default:
            return NULL;
    }
    if(empty($name)) { // 获取全部变量
        $data       =   $input;
        array_walk_recursive($data,'filter_exp');
        $filters    =   isset($filter)?$filter:C('DEFAULT_FILTER');
        if($filters) {
            $filters    =   explode(',',$filters);
            foreach($filters as $filter){
                $data   =   array_map_recursive($filter,$data); // 参数过滤
            }
        }
    }elseif(isset($input[$name])) { // 取值操作
        $data       =   $input[$name];
        is_array($data) && array_walk_recursive($data,'filter_exp');
        $filters    =   isset($filter)?$filter:C('DEFAULT_FILTER');
        if($filters) {
            $filters    =   explode(',',$filters);
            foreach($filters as $filter){
                if(function_exists($filter)) {
                    $data   =   is_array($data)?array_map_recursive($filter,$data):$filter($data); // 参数过滤
                }else{
                    $data   =   filter_var($data,is_int($filter)?$filter:filter_id($filter));
                    if(false === $data) {
                        return   isset($default)?$default:NULL;
                    }
                }
            }
        }
    }else{ // 变量默认值
        $data       =    isset($default)?$default:NULL;
    }
    return $data;
}

function array_map_recursive($filter, $data) {
     $result = array();
     foreach ($data as $key => $val) {
         $result[$key] = is_array($val)
             ? array_map_recursive($filter, $val)
             : call_user_func($filter, $val);
     }
     return $result;
 }

N方法

function N($key, $step=0,$save=false) {
    static $_num    = array();
    if (!isset($_num[$key])) {
        $_num[$key] = (false !== $save)? S('N_'.$key) :  0;
    }
    if (empty($step))
        return $_num[$key];
    else
        $_num[$key] = $_num[$key] + (int) $step;
    if(false !== $save){ // 保存结果
        S('N_'.$key,$_num[$key],$save);
    }
}

D方法

function D($name='',$layer='') {
    if(empty($name)) return new Think\Model;
    static $_model  =   array();
    $layer          =   $layer? $layer : C('DEFAULT_M_LAYER');
    if(isset($_model[$name.$layer]))
        return $_model[$name.$layer];
    $class          =   parse_res_name($name,$layer);
    if(class_exists($class)) {
        $model      =   new $class(basename($name));
    }elseif(false === strpos($name,'/')){
        // 自动加载公共模块下面的模型
        $class      =   '\\Common\\'.$layer.'\\'.$name.$layer;
        $model      =   class_exists($class)? new $class($name) : new Think\Model($name);
    }else {
        Think\Log::record('D方法实例化没找到模型类'.$class,Think\Log::NOTICE);
        $model      =   new Think\Model(basename($name));
    }
    $_model[$name.$layer]  =  $model;
    return $model;
}

M方法

function M($name='', $tablePrefix='',$connection='') {
    static $_model  = array();
    if(strpos($name,':')) {
        list($class,$name)    =  explode(':',$name);
    }else{
        $class      =   'Think\\Model';
    }
    $guid           =   (is_array($connection)?implode('',$connection):$connection).$tablePrefix . $name . '_' . $class;
    if (!isset($_model[$guid]))
        $_model[$guid] = new $class($name,$tablePrefix,$connection);
    return $_model[$guid];
}

A方法

function A($name,$layer='',$level='') {
    static $_action = array();
    $layer  =   $layer? $layer : C('DEFAULT_C_LAYER');
    $level  =   $level? $level : ($layer == C('DEFAULT_C_LAYER')?C('CONTROLLER_LEVEL'):1);
    if(isset($_action[$name.$layer]))
        return $_action[$name.$layer];
    $class  =   parse_res_name($name,$layer,$level);
    if(class_exists($class)) {
        $action             =   new $class();
        $_action[$name.$layer]     =   $action;
        return $action;
    }else {
        return false;
    }
}

R方法

function R($url,$vars=array(),$layer='') {
    $info   =   pathinfo($url);
    $action =   $info['basename'];
    $module =   $info['dirname'];
    $class  =   A($module,$layer);
    if($class){
        if(is_string($vars)) {
            parse_str($vars,$vars);
        }
        return call_user_func_array(array(&$class,$action.C('ACTION_SUFFIX')),$vars);
    }else{
        return false;
    }
}

B方法

function B($name, &$params=NULL) {
    if(strpos($name,'/')){
        list($name,$tag) = explode('/',$name);
    }else{
        $tag     =   'run';
    }
    return \Think\Hook::exec($name,$tag,$params);
}

U方法

function U($url='',$vars='',$suffix=true,$domain=false) {
    // 解析URL
    $info   =  parse_url($url);
    $url    =  !empty($info['path'])?$info['path']:ACTION_NAME;
    if(isset($info['fragment'])) { // 解析锚点
        $anchor =   $info['fragment'];
        if(false !== strpos($anchor,'?')) { // 解析参数
            list($anchor,$info['query']) = explode('?',$anchor,2);
        }        
        if(false !== strpos($anchor,'@')) { // 解析域名
            list($anchor,$host)    =   explode('@',$anchor, 2);
        }
    }elseif(false !== strpos($url,'@')) { // 解析域名
        list($url,$host)    =   explode('@',$info['path'], 2);
    }
    // 解析子域名
    if(isset($host)) {
        $domain = $host.(strpos($host,'.')?'':strstr($_SERVER['HTTP_HOST'],'.'));
    }elseif($domain===true){
        $domain = $_SERVER['HTTP_HOST'];
        if(C('APP_SUB_DOMAIN_DEPLOY') ) { // 开启子域名部署
            $domain = $domain=='localhost'?'localhost':'www'.strstr($_SERVER['HTTP_HOST'],'.');
            // '子域名'=>array('模块[/控制器]');
            foreach (C('APP_SUB_DOMAIN_RULES') as $key => $rule) {
                $rule   =   is_array($rule)?$rule[0]:$rule;
                if(false === strpos($key,'*') && 0=== strpos($url,$rule)) {
                    $domain = $key.strstr($domain,'.'); // 生成对应子域名
                    $url    =  substr_replace($url,'',0,strlen($rule));
                    break;
                }
            }
        }
    }

    // 解析参数
    if(is_string($vars)) { // aaa=1&bbb=2 转换成数组
        parse_str($vars,$vars);
    }elseif(!is_array($vars)){
        $vars = array();
    }
    if(isset($info['query'])) { // 解析地址里面参数 合并到vars
        parse_str($info['query'],$params);
        $vars = array_merge($params,$vars);
    }
    
    // URL组装
    $depr       =   C('URL_PATHINFO_DEPR');
    $urlCase    =   C('URL_CASE_INSENSITIVE');
    if($url) {
        if(0=== strpos($url,'/')) {// 定义路由
            $route      =   true;
            $url        =   substr($url,1);
            if('/' != $depr) {
                $url    =   str_replace('/',$depr,$url);
            }
        }else{
            if('/' != $depr) { // 安全替换
                $url    =   str_replace('/',$depr,$url);
            }
            // 解析模块、控制器和操作
            $url        =   trim($url,$depr);
            $path       =   explode($depr,$url);
            $var        =   array();
            $varModule      =   C('VAR_MODULE');
            $varController  =   C('VAR_CONTROLLER');
            $varAction      =   C('VAR_ACTION');
            $var[$varAction]       =   !empty($path)?array_pop($path):ACTION_NAME;
            $var[$varController]   =   !empty($path)?array_pop($path):CONTROLLER_NAME;
            if($maps = C('URL_ACTION_MAP')) {
                if(isset($maps[strtolower($var[$varController])])) {
                    $maps    =   $maps[strtolower($var[$varController])];
                    if($action = array_search(strtolower($var[$varAction]),$maps)){
                        $var[$varAction] = $action;
                    }
                }
            }
            if($maps = C('URL_CONTROLLER_MAP')) {
                if($controller = array_search(strtolower($var[$varController]),$maps)){
                    $var[$varController] = $controller;
                }
            }
            if($urlCase) {
                $var[$varController]   =   parse_name($var[$varController]);
            }
            $module =   '';
            
            if(!empty($path)) {
                $var[$varModule]    =   array_pop($path);
            }else{
                if(C('MULTI_MODULE')) {
                    if(MODULE_NAME != C('DEFAULT_MODULE') || !C('MODULE_ALLOW_LIST')){
                        $var[$varModule]=   MODULE_NAME;
                    }
                }
            }
            if($maps = C('URL_MODULE_MAP')) {
                if($_module = array_search(strtolower($var[$varModule]),$maps)){
                    $var[$varModule] = $_module;
                }
            }
            if(isset($var[$varModule])){
                $module =   $var[$varModule];
                unset($var[$varModule]);
            }
            
        }
    }

    if(C('URL_MODEL') == 0) { // 普通模式URL转换
        $url        =   __APP__.'?'.C('VAR_MODULE')."={$module}&".http_build_query(array_reverse($var));
        if($urlCase){
            $url    =   strtolower($url);
        }        
        if(!empty($vars)) {
            $vars   =   http_build_query($vars);
            $url   .=   '&'.$vars;
        }
    }else{ // PATHINFO模式或者兼容URL模式
        if(isset($route)) {
            $url    =   __APP__.'/'.rtrim($url,$depr);
        }else{
            $module =   defined('BIND_MODULE') ? '' : $module;
            $url    =   __APP__.'/'.($module?$module.MODULE_PATHINFO_DEPR:'').implode($depr,array_reverse($var));
        }
        if($urlCase){
            $url    =   strtolower($url);
        }
        if(!empty($vars)) { // 添加参数
            foreach ($vars as $var => $val){
                if('' !== trim($val))   $url .= $depr . $var . $depr . urlencode($val);
            }                
        }
        if($suffix) {
            $suffix   =  $suffix===true?C('URL_HTML_SUFFIX'):$suffix;
            if($pos = strpos($suffix, '|')){
                $suffix = substr($suffix, 0, $pos);
            }
            if($suffix && '/' != substr($url,-1)){
                $url  .=  '.'.ltrim($suffix,'.');
            }
        }
    }
    if(isset($anchor)){
        $url  .= '#'.$anchor;
    }
    if($domain) {
        $url   =  (is_ssl()?'https://':'http://').$domain.$url;
    }
    return $url;
}

w方法

function W($name, $data=array()) {
    return R($name,$data,'Widget');
}

S方法

function S($name,$value='',$options=null) {
    static $cache   =   '';
    if(is_array($options) && empty($cache)){
        // 缓存操作的同时初始化
        $type       =   isset($options['type'])?$options['type']:'';
        $cache      =   Think\Cache::getInstance($type,$options);
    }elseif(is_array($name)) { // 缓存初始化
        $type       =   isset($name['type'])?$name['type']:'';
        $cache      =   Think\Cache::getInstance($type,$name);
        return $cache;
    }elseif(empty($cache)) { // 自动初始化
        $cache      =   Think\Cache::getInstance();
    }
    if(''=== $value){ // 获取缓存
        return $cache->get($name);
    }elseif(is_null($value)) { // 删除缓存
        return $cache->rm($name);
    }else { // 缓存数据
        if(is_array($options)) {
            $expire     =   isset($options['expire'])?$options['expire']:NULL;
        }else{
            $expire     =   is_numeric($options)?$options:NULL;
        }
        return $cache->set($name, $value, $expire);
    }
}

F方法

function F($name, $value='', $path=DATA_PATH) {
    static $_cache  =   array();
    $filename       =   $path . $name . '.php';
    if ('' !== $value) {
        if (is_null($value)) {
            // 删除缓存
            if(false !== strpos($name,'*')){
                return false; // TODO 
            }else{
                unset($_cache[$name]);
                return Think\Storage::unlink($filename,'F');
            }
        } else {
            Think\Storage::put($filename,serialize($value),'F');
            // 缓存数据
            $_cache[$name]  =   $value;
            return ;
        }
    }
    // 获取缓存数据
    if (isset($_cache[$name]))
        return $_cache[$name];
    if (Think\Storage::has($filename,'F')){
        $value      =   unserialize(Think\Storage::read($filename,'F'));
        $_cache[$name]  =   $value;
    } else {
        $value          =   false;
    }
    return $value;
}

E方法

function E($msg, $code=0) {
    throw new Think\Exception($msg, $code);
}
#include <stdio.h> #include <string.h> #include <stdlib.h> #include <math.h> #include <stdbool.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_system.h" #include "esp_random.h" #include "esp_rom_sys.h" #include "led_strip.h" #define TAG "TTP229_EXT_FIXED" #define WS2812B_PIN 2 #define NUM_LEDS 12 #define TTP229_SCL 27 #define TTP229_SDO 26 #define BUZZER_PIN 25 #define OLED_SPI_HOST HSPI_HOST #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) #ifndef M_PI #define M_PI 3.14159265358979323846 #endif /* ---------- 全局与声明 ---------- */ led_strip_handle_t led_strip; static uint8_t led_brightness[NUM_LEDS]; static uint8_t led_active[NUM_LEDS]; static SemaphoreHandle_t led_mutex = NULL; typedef enum { MODE_NORMAL = 0, MODE_LED_KEY_INTERACT, MODE_LED_GRADIENT_CYCLE, MODE_BLESSING_PLAY, MODE_GAME } app_mode_t; static volatile app_mode_t g_mode = MODE_NORMAL; /* gradient */ static volatile bool gradient_run = false; static int gradient_pos = 0; /* blessing task control */ 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]; static TickType_t game_next_spawn; /* OLED */ static uint8_t oled_buf[OLED_BUF_SIZE]; static spi_device_handle_t spi_oled; /* 菜单状态 */ typedef enum { MENU_NONE, MENU_MUSIC_SELECT, MENU_FUNC_SELECT } menu_state_t; static volatile menu_state_t current_menu = MENU_NONE; static int selected_item = 0; static int brightness_level = 4; // 亮度等级:0~4(4=最大) static int pitch_offset_semitones = 0; // 音高偏移(半音) /* 映射:TP -> LED 索引 */ static const int8_t tp_to_led_idx[16] = { /* TP0 */ 8, /* TP1 */ 9, /* TP2 */ 10, /* TP3 */ 11, /* TP4 */ -1, /* TP5 */ -1, /* TP6 */ -1, /* TP7 */ -1, /* TP8 */ 3, /* TP9 */ 2, /* TP10 */ 1, /* TP11 */ 0, /* TP12 */ 4, /* TP13 */ 5, /* TP14 */ 6, /* TP15 */ 7 }; /* 游戏键映射 */ static const int8_t tp_to_game_region[16] = { /* TP0 */ -1, /* TP1 */ 1, /* TP2 */ -1, /* TP3 */ 0, /* TP4 */ -1, /* TP5 */ -1, /* TP6 */ -1, /* TP7 */ -1, /* TP8 */ -1, /* TP9 */ 5, /* TP10 */ -1, /* TP11 */ 6, /* TP12 */ 4, /* TP13 */ -1, /* TP14 */ 3, /* TP15 */ 2 }; /* 彩虹颜色 */ 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 键基础频率 */ static const int note_freqs[16] = { 262,294,330,349,370,392,415,440,466,494,523,554,587,622,659,698 }; /* TTP 按键重映射 */ static const uint8_t key_map[16] = {3,2,1,0,15,14,13,12,8,9,10,11,4,5,6,7}; /* 小音符位图 (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 }; /* 祝福曲调 */ typedef struct { int freq; int dur; } tone_t; static const tone_t blessing[] = { {494, 600}, {440, 600}, {523, 150}, {440, 150}, {392, 150}, {492, 150}, {523, 150}, {494, 150}, {440, 300}, {440, 150}, {392, 150}, {440, 150}, {587, 150}, {494, 150}, {494, 150}, {523, 150}, {587, 150}, {587, 150}, {659, 150}, {392, 150}, {659, 150}, {587, 150}, {523, 150}, {494, 150}, {392, 300}, {494, 150}, {523, 150}, {587, 150}, {523, 600}, {0, 600}, {0, 600}, {0, 600}, {0, 600}, {0, 600}, {0, 600}, {494, 150}, {523, 150}, {587, 150}, {494, 150}, {587, 150}, {494, 150}, {587, 150}, {659, 150}, {587, 150}, {587, 150}, {587, 150}, {523, 150}, {494, 150}, {440, 150}, {440, 150}, {392, 150}, {494, 150}, {494, 150}, {0, 150}, {392, 150}, {440, 150}, {494, 150}, {523, 150}, {494, 150}, {494, 150}, {392, 150}, {440, 150}, {494, 150}, {494, 150}, {523, 150}, {494, 150}, {494, 150}, {440, 150}, {440, 150}, {494, 150}, {494, 150}, {440, 150}, {392, 150}, {494, 150}, {494, 150}, {0, 150}, {494, 150}, {523, 150}, {587, 150}, {494, 150}, {587, 150}, {494, 150}, {587, 150}, {392, 150}, {440, 150}, {392, 150}, {440, 150}, {392, 150}, {440, 150}, {440, 150}, {494, 150}, {494, 150}, {440, 150}, {392, 150}, {494, 150}, {494, 150}, {0, 150}, {494, 150}, {523, 150}, {587, 150}, {494, 150}, {587, 150}, {494, 150}, {587, 150}, {659, 150}, {587, 150}, {659, 150}, {587, 150}, {659, 150}, {587, 150}, {523, 150}, {523, 150}, {587, 150}, {587, 150}, {523, 150}, {494, 150}, {587, 150}, {587, 150}, {0, 150}, {494, 150}, {523, 150}, {440, 150}, {440, 150}, {392, 150}, {392, 150}, {440, 150}, {392, 150}, {392, 150}, {392, 150}, {659, 150}, {740, 150}, {440, 150}, {494, 300}, {392, 150}, {440, 150}, {440, 150}, {440, 150}, {494, 150}, {440, 150}, {440, 150}, {440, 150}, {523, 150}, {440, 150}, {659, 150}, {659, 150}, {740, 150}, {440, 150}, {494, 300}, {392, 150}, {440, 150}, {440, 150}, {494, 150}, {440, 150}, {440, 150}, {440, 150}, {523, 150}, {440, 150}, {740, 150}, {659, 150}, {587, 300}, {392, 150}, {659, 150}, {740, 150}, {392, 150}, {392, 150}, {494, 150}, {392, 150}, {392, 150}, {392, 150}, {392, 150}, {392, 150}, {659, 150}, {392, 150}, {659, 150}, {659, 150}, {523, 300}, {494, 300}, {440, 300}, {392, 300}, {440, 300}, {494, 300}, {523, 300}, {494, 300}, {0, 300}, {626, 150}, {626, 150}, {392, 300}, {392, 300}, {392, 300}, {392, 300}, {523, 300}, {392, 300}, {659, 300}, {440, 300}, {494, 300}, {440, 300}, {494, 300}, {392, 150}, {392, 150}, {440, 150}, {392, 150}, {0, 300}, {494, 300}, {494, 300}, {392, 150}, {494, 150}, {494, 150}, {659, 150}, {523, 150}, {494, 300}, {659, 300}, {392, 150}, {659, 150}, {587, 150}, {587, 150}, {494, 300}, {494, 150}, {392, 150}, {440, 150}, {494, 150}, {523, 150}, {494, 150}, {440, 150}, {392, 150}, {494, 300}, {392, 150}, {392, 150}, {440, 150}, {392, 150}, {0, 300}, {494, 300}, {494, 300}, {392, 150}, {494, 150}, {494, 150}, {659, 150}, {523, 150}, {494, 300}, {659, 300}, {392, 150}, {659, 150}, {587, 150}, {587, 150}, {494, 300}, {523, 300}, {392, 150}, {659, 150}, {587, 150}, {523, 150}, {494, 150}, {440, 150}, {440, 150}, {392, 150}, {440, 150}, {392, 150}, {0, 300}, {0, 300}, {0, 600}, {0, 600}, {0, 600}, {0, 450}, {494, 150}, {494, 150}, {392, 150}, {392, 150}, {392, 150}, {494, 150}, {494, 150}, {392, 150}, {392, 150}, {392, 150}, {494, 150}, {494, 150}, {392, 150}, {392, 150}, {392, 150}, {494, 150}, {523, 150}, {494, 150}, {494, 150}, {494, 150}, {392, 150}, {392, 150}, {392, 150}, {494, 150}, {494, 150}, {392, 150}, {392, 150}, {392, 150}, {494, 150}, {494, 150}, {392, 150}, {392, 150}, {392, 150}, {440, 150}, {392, 150}, {392, 150}, {494, 150} }; static const int blessing_len = sizeof(blessing) / sizeof(blessing[0]); // 音乐列表 static const char* music_names[3] = {"Blessing", "Song 2", "Song 3"}; static const tone_t* music_tracks[3] = {blessing, NULL, NULL}; static const int music_lengths[3] = {blessing_len, 1, 1}; /* 字体数据:6x8 ASCII */ const uint8_t font6x8[95 * 6] = { 0x00,0x00,0x00,0x00,0x00,0x00, // ' ' 0x00,0x00,0x5F,0x00,0x00,0x00, // ! 0x00,0x03,0x00,0x03,0x00,0x00, // " 0x14,0x3E,0x14,0x3E,0x14,0x00, // # 0x24,0x2A,0x7F,0x2A,0x12,0x00, // $ 0x43,0x33,0x08,0x66,0x61,0x00, // % 0x36,0x49,0x55,0x22,0x50,0x00, // & 0x00,0x05,0x03,0x00,0x00,0x00, // ' 0x00,0x1C,0x22,0x41,0x00,0x00, // ( 0x00,0x41,0x22,0x1C,0x00,0x00, // ) 0x14,0x08,0x3E,0x08,0x14,0x00, // * 0x08,0x08,0x3E,0x08,0x08,0x00, // + 0x00,0x50,0x30,0x00,0x00,0x00, // , 0x08,0x08,0x08,0x08,0x08,0x00, // - 0x00,0x60,0x60,0x00,0x00,0x00, // . 0x20,0x10,0x08,0x04,0x02,0x00, // / 0x3E,0x51,0x49,0x45,0x3E,0x00, // 0 0x00,0x42,0x7F,0x40,0x00,0x00, // 1 0x42,0x61,0x51,0x49,0x46,0x00, // 2 0x21,0x41,0x45,0x4B,0x31,0x00, // 3 0x18,0x14,0x12,0x7F,0x10,0x00, // 4 0x27,0x45,0x45,0x45,0x39,0x00, // 5 0x3C,0x4A,0x49,0x49,0x30,0x00, // 6 0x01,0x71,0x09,0x05,0x03,0x00, // 7 0x36,0x49,0x49,0x49,0x36,0x00, // 8 0x06,0x49,0x49,0x29,0x1E,0x00, // 9 0x00,0x36,0x36,0x00,0x00,0x00, // : 0x00,0x56,0x36,0x00,0x00,0x00, // ; 0x08,0x14,0x22,0x41,0x00,0x00, // < 0x14,0x14,0x14,0x14,0x14,0x00, // = 0x00,0x41,0x22,0x14,0x08,0x00, // > 0x02,0x01,0x51,0x09,0x06,0x00, // ? 0x3E,0x41,0x5D,0x55,0x1E,0x00, // @ 0x7C,0x12,0x11,0x12,0x7C,0x00, // A 0x7F,0x49,0x49,0x49,0x36,0x00, // B 0x3E,0x41,0x41,0x41,0x22,0x00, // C 0x7F,0x41,0x41,0x22,0x1C,0x00, // D 0x7F,0x49,0x49,0x49,0x41,0x00, // E 0x7F,0x09,0x09,0x09,0x01,0x00, // F 0x3E,0x41,0x49,0x49,0x7A,0x00, // G 0x7F,0x08,0x08,0x08,0x7F,0x00, // H 0x00,0x41,0x7F,0x41,0x00,0x00, // I 0x20,0x40,0x41,0x3F,0x01,0x00, // J 0x7F,0x08,0x14,0x22,0x41,0x00, // K 0x7F,0x40,0x40,0x40,0x40,0x00, // L 0x7F,0x02,0x0C,0x02,0x7F,0x00, // M 0x7F,0x04,0x08,0x10,0x7F,0x00, // N 0x3E,0x41,0x41,0x41,0x3E,0x00, // O 0x7F,0x09,0x09,0x09,0x06,0x00, // P 0x3E,0x41,0x51,0x21,0x5E,0x00, // Q 0x7F,0x09,0x19,0x29,0x46,0x00, // R 0x46,0x49,0x49,0x49,0x31,0x00, // S 0x01,0x01,0x7F,0x01,0x01,0x00, // T 0x3F,0x40,0x40,0x40,0x3F,0x00, // U 0x1F,0x20,0x40,0x20,0x1F,0x00, // V 0x3F,0x40,0x38,0x40,0x3F,0x00, // W 0x63,0x14,0x08,0x14,0x63,0x00, // X 0x07,0x08,0x70,0x08,0x07,0x00, // Y 0x61,0x51,0x49,0x45,0x43,0x00, // Z 0x00,0x7F,0x41,0x41,0x00,0x00, // [ 0x02,0x04,0x08,0x10,0x20,0x00, // '\\' 0x00,0x41,0x41,0x7F,0x00,0x00, // ] 0x04,0x02,0x01,0x02,0x04,0x00, // ^ 0x40,0x40,0x40,0x40,0x40,0x00, // _ 0x00,0x01,0x02,0x04,0x00,0x00, // ` 0x20,0x54,0x54,0x54,0x78,0x00, // a 0x7F,0x44,0x44,0x44,0x38,0x00, // b 0x38,0x44,0x44,0x44,0x20,0x00, // c 0x38,0x44,0x44,0x44,0x7F,0x00, // d 0x38,0x54,0x54,0x54,0x18,0x00, // e 0x08,0x7E,0x09,0x01,0x02,0x00, // f 0x18,0xA4,0xA4,0xA4,0x7C,0x00, // g 0x7F,0x08,0x04,0x04,0x78,0x00, // h 0x00,0x44,0x7D,0x40,0x00,0x00, // i 0x40,0x80,0x84,0x7D,0x00,0x00, // j 0x7F,0x10,0x28,0x44,0x00,0x00, // k 0x00,0x41,0x7F,0x40,0x00,0x00, // l 0x7C,0x04,0x18,0x04,0x78,0x00, // m 0x7C,0x08,0x04,0x04,0x78,0x00, // n 0x38,0x44,0x44,0x44,0x38,0x00, // o 0xFC,0x24,0x24,0x24,0x18,0x00, // p 0x18,0x24,0x24,0x18,0xFC,0x00, // q 0x7C,0x08,0x04,0x04,0x08,0x00, // r 0x48,0x54,0x54,0x54,0x20,0x00, // s 0x04,0x04,0x3F,0x44,0x40,0x00, // t 0x3C,0x40,0x40,0x20,0x7C,0x00, // u 0x1C,0x20,0x40,0x20,0x1C,0x00, // v 0x3C,0x40,0x30,0x40,0x3C,0x00, // w 0x44,0x28,0x10,0x28,0x44,0x00, // x 0x1C,0xA0,0xA0,0x7C,0x00,0x00, // y 0x44,0x64,0x54,0x4C,0x44,0x00, // z 0x00,0x08,0x36,0x41,0x00,0x00, // { 0x00,0x00,0x77,0x00,0x00,0x00, // | 0x00,0x41,0x36,0x08,0x00,0x00, // } 0x02,0x01,0x02,0x04,0x02,0x00 // ~ }; /* ---------- 前向声明 ---------- */ 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_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_face_happy(void); static void oled_draw_music_symbol(void); static void led_update_from_state(void); static void led_fade_task(void *arg); static void blessing_task(void *arg); static void start_blessing_task(void); static void stop_blessing_task(void); static void game_spawn_block(void); static void game_draw(void); void run_animation(void); static void oled_draw_menu(const char* title, const char* items[], int item_count); /* ---------- 实现 ---------- */ 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)); if (!led_mutex) led_mutex = xSemaphoreCreateMutex(); for (int i=0;i<NUM_LEDS;i++){ led_brightness[i]=255; led_active[i]=0; led_strip_set_pixel(led_strip, i, rainbow_colors[i][0], rainbow_colors[i][1], rainbow_colors[i][2]); } led_strip_refresh(led_strip); } 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); } static void oled_send_cmd(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, 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_refresh(void) { 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(); } static void oled_draw_bitmap(int x, int y, int w, int h, const uint8_t *bm) { 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)); } } } } static void oled_draw_face_happy(void) { memset(oled_buf, 0, OLED_BUF_SIZE); for (int yy = 20; yy <= 22; yy++) { for (int xx = 30; xx <= 40; xx++) { if (yy == 21 || yy == 22) { int page = yy / 8; int idx = page * OLED_WIDTH + xx; oled_buf[idx] |= (1 << (yy & 7)); } } } for (int yy = 20; yy <= 22; yy++) { for (int xx = 85; xx <= 95; xx++) { if (yy == 21 || yy == 22) { int page = yy / 8; int idx = page * OLED_WIDTH + xx; oled_buf[idx] |= (1 << (yy & 7)); } } } for (int xx = 46; xx < 82; xx++) { int yy = 42 + (int)(4.0f * sinf(((xx - 46) / 36.0f) * M_PI)); if (yy < 0 || yy >= OLED_HEIGHT) continue; int page = yy / 8; int idx = page * OLED_WIDTH + xx; oled_buf[idx] |= (1 << (yy & 7)); if (yy + 1 < OLED_HEIGHT) { int page2 = (yy + 1) / 8; oled_buf[page2 * OLED_WIDTH + xx] |= (1 << ((yy + 1) & 7)); } } oled_refresh(); } static void oled_draw_music_symbol(void) { memset(oled_buf,0,OLED_BUF_SIZE); oled_draw_bitmap(56, 20, 12, 12, bm_music); oled_refresh(); } static void oled_draw_menu(const char* title, const char* items[], int item_count) { memset(oled_buf, 0, OLED_BUF_SIZE); int len = strlen(title) * 6; int x = (OLED_WIDTH - len) / 2; int y = 0; for (int i = 0; title[i]; i++) { uint8_t ch = title[i] - ' '; const uint8_t* font = &font6x8[ch * 6]; for (int col = 0; col < 6; col++) { if (col >= 6) break; uint8_t b = font[col]; for (int bit = 0; bit < 8; bit++) { if (b & (1 << bit)) { int py = y + bit, px = x + i * 6 + col; if (px < OLED_WIDTH && py < OLED_HEIGHT) { oled_buf[(py / 8) * OLED_WIDTH + px] |= (1 << (py & 7)); } } } } } y = 16; for (int i = 0; i < item_count; i++) { if (i == selected_item) { oled_buf[(y / 8) * OLED_WIDTH + 2] |= (1 << (y & 7)); } len = strlen(items[i]); for (int j = 0; j < len; j++) { int px = 10 + j * 6, py = y; uint8_t ch = items[i][j] - ' '; const uint8_t* font = &font6x8[ch * 6]; for (int col = 0; col < 6; col++) { uint8_t b = font[col]; for (int bit = 0; bit < 8; bit++) { if (b & (1 << bit)) { int ppy = py + bit; if (px + col < OLED_WIDTH && ppy < OLED_HEIGHT) { oled_buf[(ppy / 8) * OLED_WIDTH + px + col] |= (1 << (ppy & 7)); } } } } } y += 10; } oled_refresh(); } static void set_led_rgb(int idx, uint8_t r, uint8_t g, uint8_t b, uint8_t brightness) { uint8_t r2 = (r * brightness)/255; uint8_t g2 = (g * brightness)/255; uint8_t b2 = (b * brightness)/255; led_strip_set_pixel(led_strip, idx, r2, g2, b2); } static void led_update_from_state(void) { if (!led_mutex) return; 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); } 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) { int nb = (int)led_brightness[i] - 8; if (nb < 0) nb = 0; led_brightness[i] = (uint8_t)nb; changed = true; } } } else if (g_mode == MODE_LED_GRADIENT_CYCLE && gradient_run) { gradient_pos++; for (int i=0;i<NUM_LEDS;i++){ int pos = (i + gradient_pos) % NUM_LEDS; float v = (1.0f + sinf((pos*2.0f*M_PI)/NUM_LEDS)) * 0.5f; int b = 80 + (int)(175.0f * v); if (b < 0) b = 0; if (b>255) b = 255; led_brightness[i] = (uint8_t)b; } changed = true; } else { 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)); } } static void blessing_task(void *arg) { ESP_LOGI(TAG, "Blessing task started"); const tone_t* track = music_tracks[selected_item]; int len = music_lengths[selected_item]; while (blessing_running) { for (int i=0;i<len && blessing_running;i++){ int f = track[i].freq; int d = track[i].dur; if (f > 0) buzzer_play_tone(f); else buzzer_play_tone(0); int led_idx = i % NUM_LEDS; xSemaphoreTake(led_mutex, portMAX_DELAY); led_brightness[led_idx] = 255; xSemaphoreGive(led_mutex); led_update_from_state(); int chunks = d / 20; if (chunks < 1) chunks = 1; for (int c=0;c<chunks && blessing_running;c++){ vTaskDelay(pdMS_TO_TICKS(d / chunks)); } buzzer_play_tone(0); vTaskDelay(pdMS_TO_TICKS(40)); } } buzzer_play_tone(0); ESP_LOGI(TAG, "Blessing task exit"); blessing_task_handle = NULL; vTaskDelete(NULL); } static void start_blessing_task(void) { if (blessing_running) return; blessing_running = true; BaseType_t r = xTaskCreate(blessing_task, "blessing", 4096, NULL, 5, &blessing_task_handle); if (r != pdPASS) { ESP_LOGW(TAG,"Create blessing task failed"); blessing_running = false; blessing_task_handle = NULL; } } static void stop_blessing_task(void) { if (!blessing_running) return; blessing_running = false; int t = 0; while (blessing_task_handle != NULL && t < 50) { vTaskDelay(pdMS_TO_TICKS(50)); t++; } buzzer_play_tone(0); } static void game_spawn_block(void) { 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) { memset(oled_buf,0,OLED_BUF_SIZE); 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]) { 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 { 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(); } void run_animation(void) { float phase = 0.0f; for (int frame = 0; frame < 100; frame++) { xSemaphoreTake(led_mutex, portMAX_DELAY); for (int i = 0; i < NUM_LEDS; i++) { float angle = phase + i * (2 * M_PI / NUM_LEDS); float v = (1.0f + sinf(angle)) * 0.5f; int b = (int)(v * 255); set_led_rgb(i, rainbow_colors[i][0], rainbow_colors[i][1], rainbow_colors[i][2], b); } xSemaphoreGive(led_mutex); led_strip_refresh(led_strip); phase += 0.1f; vTaskDelay(pdMS_TO_TICKS(50)); uint16_t k = ttp229_read_keys(); if (k & ((1<<4)|(1<<5)|(1<<6)|(1<<7))) break; } oled_clear(); oled_draw_face_happy(); } void app_main(void) { ESP_LOGI(TAG,"App start"); ws2812b_init(); ttp229_init(); buzzer_init(); gpio_config_t io_conf = {0}; 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_config_t buscfg = { .miso_io_num = -1, .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)); gpio_set_level(OLED_RST_PIN, 0); vTaskDelay(pdMS_TO_TICKS(10)); gpio_set_level(OLED_RST_PIN, 1); vTaskDelay(pdMS_TO_TICKS(10)); const uint8_t init_cmds[] = { 0xAE,0xD5,0x80,0xA8,0x3F,0xD3,0x00,0x40,0x8D,0x14,0x20,0x00,0xA1,0xC8,0xDA,0x12,0x81,0xCF,0xD9,0xF1,0xDB,0x40,0xA4,0xA6,0xAF }; oled_send_cmd(init_cmds, sizeof(init_cmds)); oled_clear(); xTaskCreate(led_fade_task, "led_fade", 4096, NULL, 5, NULL); uint16_t prev_keys = 0; ESP_LOGI(TAG,"Main loop start"); while (1) { uint16_t keys = ttp229_read_keys(); uint16_t newly = (keys & ~prev_keys); uint16_t releases = (~keys) & prev_keys; bool tp4_pressed = keys & (1 << 4); bool tp5_pressed = keys & (1 << 5); // === 功能键单独处理 === if (newly & (1 << 4)) { if (current_menu == MENU_NONE && !(tp5_pressed)) { if (g_mode == MODE_LED_KEY_INTERACT) { g_mode = MODE_NORMAL; oled_clear(); oled_draw_face_happy(); } else { g_mode = MODE_LED_KEY_INTERACT; oled_clear(); oled_draw_face_happy(); } vTaskDelay(pdMS_TO_TICKS(200)); } } if (newly & (1 << 5)) { if (current_menu == MENU_NONE && !(tp4_pressed)) { if (g_mode == MODE_LED_GRADIENT_CYCLE) { g_mode = MODE_NORMAL; gradient_run = false; oled_clear(); oled_draw_face_happy(); } else { g_mode = MODE_LED_GRADIENT_CYCLE; gradient_run = true; oled_clear(); oled_draw_face_happy(); } vTaskDelay(pdMS_TO_TICKS(200)); } } if (newly & (1 << 6)) { if (current_menu == MENU_NONE) { current_menu = MENU_MUSIC_SELECT; selected_item = 0; g_mode = MODE_NORMAL; stop_blessing_task(); oled_draw_menu("Music", music_names, 3); } else if (current_menu == MENU_MUSIC_SELECT) { current_menu = MENU_NONE; oled_clear(); oled_draw_face_happy(); } vTaskDelay(pdMS_TO_TICKS(200)); } if (newly & (1 << 7)) { if (current_menu == MENU_NONE) { current_menu = MENU_FUNC_SELECT; selected_item = 0; static const char* func_items[] = {"Game", "Animation"}; oled_draw_menu("Func", func_items, 2); } else if (current_menu == MENU_FUNC_SELECT) { current_menu = MENU_NONE; oled_clear(); oled_draw_face_happy(); } vTaskDelay(pdMS_TO_TICKS(200)); } // === 菜单内导航 === if (current_menu == MENU_MUSIC_SELECT) { if (newly & (1 << 5)) { selected_item = (selected_item - 1 + 3) % 3; oled_draw_menu("Music", music_names, 3); } else if (newly & (1 << 7)) { selected_item = (selected_item + 1) % 3; oled_draw_menu("Music", music_names, 3); } else if (newly & (1 << 4)) { if (music_tracks[selected_item]) { stop_blessing_task(); start_blessing_task(); g_mode = MODE_BLESSING_PLAY; oled_clear(); oled_draw_music_symbol(); } current_menu = MENU_NONE; } vTaskDelay(pdMS_TO_TICKS(150)); prev_keys = keys; continue; } if (current_menu == MENU_FUNC_SELECT) { if (newly & (1 << 4)) { selected_item = (selected_item - 1 + 2) % 2; static const char* items[] = {"Game", "Animation"}; oled_draw_menu("Func", items, 2); } else if (newly & (1 << 6)) { selected_item = (selected_item + 1) % 2; static const char* items[] = {"Game", "Animation"}; oled_draw_menu("Func", items, 2); } else if (newly & (1 << 5)) { if (selected_item == 0) { g_mode = MODE_GAME; game_running = true; memset(game_regions, 0, sizeof(game_regions)); game_next_spawn = xTaskGetTickCount() + pdMS_TO_TICKS(800); oled_clear(); game_draw(); } else { g_mode = MODE_NORMAL; run_animation(); } current_menu = MENU_NONE; } vTaskDelay(pdMS_TO_TICKS(150)); prev_keys = keys; continue; } // === 游戏模式:可随时按 TP7 退出 === 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(); } // 新增:TP7 可退出游戏 if (newly & (1 << 7)) { g_mode = MODE_NORMAL; game_running = false; oled_clear(); oled_draw_face_happy(); } } // === 处理琴键输入 === if (keys) { bool played_any = false; for (int i=0;i<16;i++){ uint8_t tp = key_map[i]; if (keys & (1 << tp)) { if (tp>=4 && tp<=7) continue; if (g_mode == MODE_GAME) { int region = tp_to_game_region[tp]; if (region >= 0 && region < 7) { if (game_regions[region]) { game_regions[region] = 0; buzzer_play_tone(880); vTaskDelay(pdMS_TO_TICKS(80)); buzzer_play_tone(0); game_draw(); } else { buzzer_play_tone(220); vTaskDelay(pdMS_TO_TICKS(60)); buzzer_play_tone(0); } } } else { float ratio = powf(2.0f, pitch_offset_semitones / 12.0f); int adjusted_freq = (int)(note_freqs[i] * ratio); if (g_mode != MODE_BLESSING_PLAY) { buzzer_play_tone(adjusted_freq); played_any = true; } int led_idx = tp_to_led_idx[tp]; if (g_mode == MODE_LED_KEY_INTERACT && led_idx >= 0 && led_idx < NUM_LEDS) { xSemaphoreTake(led_mutex, portMAX_DELAY); led_active[led_idx] = 1; led_brightness[led_idx] = 255; xSemaphoreGive(led_mutex); led_update_from_state(); } if (g_mode == MODE_LED_GRADIENT_CYCLE && led_idx >= 0 && led_idx < NUM_LEDS) { xSemaphoreTake(led_mutex, portMAX_DELAY); led_brightness[led_idx] = 255; xSemaphoreGive(led_mutex); led_update_from_state(); } if (g_mode == MODE_NORMAL) { memset(oled_buf,0,OLED_BUF_SIZE); int bars = NUM_LEDS; int w = OLED_WIDTH / bars; for (int bi=0;bi<bars;bi++){ int x = bi*w + 1; int base_y = OLED_HEIGHT - 2; int bar_h = (bi == i) ? (OLED_HEIGHT/2) : (OLED_HEIGHT/6); 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(); } } } } vTaskDelay(pdMS_TO_TICKS(40)); } else { if (g_mode != MODE_BLESSING_PLAY) buzzer_play_tone(0); } if (releases) { for (int i=0;i<16;i++){ uint8_t tp = key_map[i]; if (releases & (1<<tp)) { int led_idx = tp_to_led_idx[tp]; if (g_mode == MODE_LED_KEY_INTERACT && led_idx >=0 && led_idx < NUM_LEDS) { xSemaphoreTake(led_mutex, portMAX_DELAY); led_active[led_idx] = 0; xSemaphoreGive(led_mutex); } } } } if (newly & (1 << 5)) { if (current_menu == MENU_NONE && g_mode == MODE_NORMAL) { oled_clear(); oled_draw_face_happy(); } } prev_keys = keys; vTaskDelay(pdMS_TO_TICKS(20)); } }你要彻彻底底从头到尾剖析这段代码的逻辑,分块阐述哪一块是在实现什么样的功能,哪一块和哪一块互相联动,再在分块中逐行详细阐释语法含义作用逻辑,不需要说明硬件知识,尤其详细阐明oled部分,以及各功能(尤其是呼吸灯效果,渐变灯光,tp键与oled联动方式,小游戏实现逻辑)实现的原理逻辑,软硬交互机制,详细具体,通俗易懂
最新发布
10-23
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值