From 334c9ee96fb3cbd34198fef0049697d1e9bb28f7 Mon Sep 17 00:00:00 2001 From: SepComet <202308010230@stu.csust.edu.cn> Date: Mon, 15 Jun 2026 20:20:49 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E8=A1=A5=E5=85=85=E5=85=89?= =?UTF-8?q?=E6=95=8F=E4=BC=A0=E6=84=9F=E5=99=A8=E9=A9=B1=E5=8A=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CMakeLists.txt | 2 +- cmake/toolchain-arm-linux-gnueabihf.cmake | 8 +- src/Apps/LightGame/CMakeLists.txt | 10 +- src/Apps/LightGame/src/engine/Level.h | 1 + .../LightGame/src/engine/LightGameApp.cpp | 26 ++- src/Apps/LightGame/src/engine/LightGameApp.h | 5 + src/Apps/LightGame/src/main.cpp | 9 +- src/Core/Platform/Ap3216cPhotoSensor.cpp | 151 ++++++++++++++++++ src/Core/Platform/Ap3216cPhotoSensor.h | 35 ++++ src/Core/Platform/DefaultHardware.h | 4 +- src/Core/Platform/EvdevButtonInput.cpp | 8 +- src/Core/Platform/EvdevButtonInput.h | 2 +- 12 files changed, 245 insertions(+), 16 deletions(-) create mode 100644 src/Core/Platform/Ap3216cPhotoSensor.cpp create mode 100644 src/Core/Platform/Ap3216cPhotoSensor.h diff --git a/CMakeLists.txt b/CMakeLists.txt index c21f308..85669eb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -32,7 +32,7 @@ set(CORE_SOURCES if(USE_FRAMEBUFFER) list(APPEND CORE_SOURCES src/Core/Platform/FBDisplay.cpp - src/Core/Platform/LinuxPhotoSensor.cpp + src/Core/Platform/Ap3216cPhotoSensor.cpp ) else() list(APPEND CORE_SOURCES diff --git a/cmake/toolchain-arm-linux-gnueabihf.cmake b/cmake/toolchain-arm-linux-gnueabihf.cmake index 34c1d25..f3bf49a 100644 --- a/cmake/toolchain-arm-linux-gnueabihf.cmake +++ b/cmake/toolchain-arm-linux-gnueabihf.cmake @@ -1,8 +1,12 @@ set(CMAKE_SYSTEM_NAME Linux) set(CMAKE_SYSTEM_PROCESSOR arm) -set(CMAKE_C_COMPILER arm-linux-gnueabihf-gcc) -set(CMAKE_CXX_COMPILER arm-linux-gnueabihf-g++) +# Linaro GCC 4.9.4 2017.01,与 IMX6U 开发板原厂一致 +# 解压:tar -xJf gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf.tar.xz -C ~/toolchains +set(IMX6U_TOOLCHAIN_ROOT "$ENV{HOME}/toolchains/gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf") +set(CMAKE_C_COMPILER "${IMX6U_TOOLCHAIN_ROOT}/bin/arm-linux-gnueabihf-gcc") +set(CMAKE_CXX_COMPILER "${IMX6U_TOOLCHAIN_ROOT}/bin/arm-linux-gnueabihf-g++") +set(CMAKE_SYSROOT "${IMX6U_TOOLCHAIN_ROOT}/arm-linux-gnueabihf/libc") set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) diff --git a/src/Apps/LightGame/CMakeLists.txt b/src/Apps/LightGame/CMakeLists.txt index ad6be33..4437913 100644 --- a/src/Apps/LightGame/CMakeLists.txt +++ b/src/Apps/LightGame/CMakeLists.txt @@ -9,10 +9,16 @@ set(LIGHTGAME_SOURCES src/engine/LightGameApp.cpp src/engine/RoomLayout.cpp src/systems/LightEffectSystem.cpp - src/editor/LevelEditor.cpp src/main.cpp ) +if(NOT USE_FRAMEBUFFER) + list(APPEND LIGHTGAME_SOURCES src/editor/LevelEditor.cpp) +endif() + add_executable(IMX6U-LightGame ${LIGHTGAME_SOURCES}) -target_include_directories(IMX6U-LightGame PRIVATE src/engine src/systems src/levels src/editor generated) +target_include_directories(IMX6U-LightGame PRIVATE src/engine src/systems src/levels generated) +if(NOT USE_FRAMEBUFFER) + target_include_directories(IMX6U-LightGame PRIVATE src/editor) +endif() imx6u_configure_app_target(IMX6U-LightGame) diff --git a/src/Apps/LightGame/src/engine/Level.h b/src/Apps/LightGame/src/engine/Level.h index ff7b8f4..ecf24ba 100644 --- a/src/Apps/LightGame/src/engine/Level.h +++ b/src/Apps/LightGame/src/engine/Level.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include "GameObject.h" diff --git a/src/Apps/LightGame/src/engine/LightGameApp.cpp b/src/Apps/LightGame/src/engine/LightGameApp.cpp index cd2ed29..d0f69ec 100644 --- a/src/Apps/LightGame/src/engine/LightGameApp.cpp +++ b/src/Apps/LightGame/src/engine/LightGameApp.cpp @@ -41,6 +41,7 @@ namespace LightGame level_total_loaded_(false), manual_light_level_(2048), light_step_(256), + smoothed_light_q8_(2048 << 8), has_manual_override_(false), debug_mode_(false), death_processed_(false) @@ -74,6 +75,7 @@ namespace LightGame const uint32_t next = static_cast(manual_light_level_) + light_step_; manual_light_level_ = static_cast(next > 4095 ? 4095 : next); has_manual_override_ = true; + smoothed_light_q8_ = static_cast(manual_light_level_) << 8; } if (keyboard_input_->is_key_down(Platform::KEY_S)) { @@ -81,13 +83,25 @@ namespace LightGame ? manual_light_level_ - light_step_ : 0; has_manual_override_ = true; + smoothed_light_q8_ = static_cast(manual_light_level_) << 8; } } if (photo_sensor_ && photo_sensor_->is_open() && !has_manual_override_) { photo_sensor_->update(); - manual_light_level_ = photo_sensor_->read_level(); + const uint16_t raw = photo_sensor_->read_level(); + + // 一阶 EMA:smoothed += (raw - smoothed) * alpha + // alpha = 1/16 → -3dB 截止频率约为 fs/100,30fps 下约 0.3Hz, + // 足以滤掉 ADC 量化抖动,又不会让光照变化拖太久(90% 收敛 ~0.7s) + const int32_t target_q8 = static_cast(raw) << 8; + smoothed_light_q8_ += (target_q8 - smoothed_light_q8_) >> 4; + + int32_t out = smoothed_light_q8_ >> 8; + if (out < 0) out = 0; + if (out > 4095) out = 4095; + manual_light_level_ = static_cast(out); } const GameState prev_state = state_manager_.get_state(); @@ -142,7 +156,15 @@ namespace LightGame bool LightGameApp::is_action_pressed() { - return button_input_ && button_input_->was_pressed(); + if (button_input_ && button_input_->was_pressed()) + { + return true; + } + if (pointer_input_ && pointer_input_->was_pressed()) + { + return true; + } + return false; } void LightGameApp::update_title(uint32_t dt_ms) diff --git a/src/Apps/LightGame/src/engine/LightGameApp.h b/src/Apps/LightGame/src/engine/LightGameApp.h index aa776d4..df2e192 100644 --- a/src/Apps/LightGame/src/engine/LightGameApp.h +++ b/src/Apps/LightGame/src/engine/LightGameApp.h @@ -59,6 +59,11 @@ namespace LightGame uint16_t manual_light_level_; uint16_t light_step_; + // 光敏读数缓动用的 Q8 定点内部状态(real_value << 8) + // 用 Q8 是为了让 alpha=1/16 时低 4 位的累积分量不会被整数除法吞掉 + // 仅在 sensor 自动模式生效;手动模式直接赋值,按键响应保持瞬时 + int32_t smoothed_light_q8_; + bool has_manual_override_; bool debug_mode_; bool death_processed_; diff --git a/src/Apps/LightGame/src/main.cpp b/src/Apps/LightGame/src/main.cpp index e817089..760f5b9 100644 --- a/src/Apps/LightGame/src/main.cpp +++ b/src/Apps/LightGame/src/main.cpp @@ -12,7 +12,6 @@ #include "TimeSource.h" #include "Timer.h" #include "LightGameApp.h" -#include "LevelEditor.h" #include "font_atlas.h" #include "LevelTotalData.h" @@ -20,6 +19,7 @@ #include "FBDisplay.h" #else #include "SDLDisplay.h" +#include "LevelEditor.h" #include "imgui.h" #include "backends/imgui_impl_sdl2.h" #include "backends/imgui_impl_sdlrenderer2.h" @@ -97,9 +97,9 @@ int main(int argc, char* argv[]) &pointerInput, &photoSensor); +#ifndef USE_FRAMEBUFFER LightGame::LevelEditor editor(ScreenWidth, ScreenHeight); -#ifndef USE_FRAMEBUFFER Platform::SDLDisplay* sdl_display = static_cast(display); IMGUI_CHECKVERSION(); ImGui::CreateContext(); @@ -156,6 +156,7 @@ int main(int argc, char* argv[]) buttonInput.update(); pointerInput.update(); +#ifndef USE_FRAMEBUFFER if (editor.is_active()) { if (editor.get_pending_load() >= 0) @@ -172,6 +173,10 @@ int main(int argc, char* argv[]) app.update(timer.fixed_delta_ms()); app.draw(ctx, font); } +#else + app.update(timer.fixed_delta_ms()); + app.draw(ctx, font); +#endif #ifdef USE_FRAMEBUFFER ctx.present(display); diff --git a/src/Core/Platform/Ap3216cPhotoSensor.cpp b/src/Core/Platform/Ap3216cPhotoSensor.cpp new file mode 100644 index 0000000..eccfcbe --- /dev/null +++ b/src/Core/Platform/Ap3216cPhotoSensor.cpp @@ -0,0 +1,151 @@ +#include "Ap3216cPhotoSensor.h" +#include + +#if defined(__linux__) +#include +#include +#include +#include +#include +#endif + +namespace +{ + // AP3216C i2c 寄存器 + const uint8_t kRegSysConfig = 0x00; + const uint8_t kRegAlsConfig = 0x10; + const uint8_t kRegAlsLow = 0x0c; + const uint8_t kRegAlsHigh = 0x0d; + + // 模式 / 增益 + const uint8_t kSysAlsContinuous = 0x01; // ALS-only 持续测量;避免与 PS 共享 ADC 导致读数被打断 + const uint8_t kAlsRange323Lux = 0x30; // bit[5:4]=11,最高灵敏度,对应 323 lux 满量程 + + const uint8_t kAp3216cAddr = 0x1e; + +#if defined(__linux__) + bool I2cWriteByte(int fd, uint8_t reg, uint8_t value) + { + uint8_t buf[2] = { reg, value }; + return write(fd, buf, sizeof(buf)) == static_cast(sizeof(buf)); + } + + bool I2cReadByte(int fd, uint8_t reg, uint8_t& value) + { + if (write(fd, ®, 1) != 1) + { + return false; + } + return read(fd, &value, 1) == 1; + } +#endif +} + +namespace Platform +{ + Ap3216cPhotoSensor::Ap3216cPhotoSensor() + : device_path_("/dev/i2c-0"), + opened_(false), + level_(0), + max_value_(4095) +#if defined(__linux__) + , + fd_(-1) +#endif + { + } + + Ap3216cPhotoSensor::~Ap3216cPhotoSensor() + { + shutdown(); + } + + bool Ap3216cPhotoSensor::init(const std::string& device_path) + { + shutdown(); + + // 注意:这里 device_path 指 i2c 总线节点,不再是字符设备 /dev/ap3216c + // 板子上 AP3216C 挂在 i2c-0 上(DT: i2c@021a0000,地址 0x1e) + device_path_ = device_path.empty() ? "/dev/i2c-0" : device_path; + level_ = 0; + +#if defined(__linux__) + fd_ = open(device_path_.c_str(), O_RDWR); + if (fd_ < 0) + { + std::cerr << "Ap3216cPhotoSensor open failed: " << device_path_ << std::endl; + return false; + } + + // 用 I2C_SLAVE_FORCE 是因为内置驱动已经把 0x1e 占住了(i2cdetect 显示 UU) + // 我们绕过那个驱动直接通信,所以必须 force + if (ioctl(fd_, I2C_SLAVE_FORCE, kAp3216cAddr) < 0) + { + std::cerr << "Ap3216cPhotoSensor I2C_SLAVE_FORCE failed: " << errno << std::endl; + close(fd_); + fd_ = -1; + return false; + } + + // 切换到 ALS-only 持续模式 + 最高灵敏度 + // 内置驱动 probe 时设的是 0x03(ALS+PS+IR),ALS 会被 PS 中断采样而读到 0 + if (!I2cWriteByte(fd_, kRegSysConfig, kSysAlsContinuous)) + { + std::cerr << "Ap3216cPhotoSensor: write SysConfig failed." << std::endl; + close(fd_); + fd_ = -1; + return false; + } + if (!I2cWriteByte(fd_, kRegAlsConfig, kAlsRange323Lux)) + { + std::cerr << "Ap3216cPhotoSensor: write AlsConfig failed." << std::endl; + close(fd_); + fd_ = -1; + return false; + } + + opened_ = true; + return true; +#else + std::cerr << "Ap3216cPhotoSensor is only available on Linux." << std::endl; + return false; +#endif + } + + void Ap3216cPhotoSensor::update() + { + if (!opened_) + { + return; + } + +#if defined(__linux__) + uint8_t lo = 0; + uint8_t hi = 0; + // ALS 数据寄存器:0x0c (低 8 位) + 0x0d (高 8 位) + if (!I2cReadByte(fd_, kRegAlsLow, lo) || !I2cReadByte(fd_, kRegAlsHigh, hi)) + { + // 单次失败保留上次的 level_,避免亮度抖到 0 + return; + } + + const uint16_t als = static_cast(lo) | + (static_cast(hi) << 8); + // 实测 323 lux 增益下强光接近 4095,直接 clamp 即可 + level_ = als > max_value_ ? max_value_ : als; +#endif + } + + void Ap3216cPhotoSensor::shutdown() + { +#if defined(__linux__) + if (fd_ >= 0) + { + close(fd_); + fd_ = -1; + } +#endif + opened_ = false; + level_ = 0; + } +} diff --git a/src/Core/Platform/Ap3216cPhotoSensor.h b/src/Core/Platform/Ap3216cPhotoSensor.h new file mode 100644 index 0000000..4df4e50 --- /dev/null +++ b/src/Core/Platform/Ap3216cPhotoSensor.h @@ -0,0 +1,35 @@ +#pragma once + +#include "IPhotoSensor.h" + +namespace Platform +{ + // 直接走 i2c 总线读 AP3216C,绕过 /dev/ap3216c 字符设备驱动。 + // 设备路径默认 /dev/i2c-0,DT 中传感器挂在 i2c@021a0000 0x1e。 + // 初始化时会强制切换到 ALS-only 持续测量 + 323 lux 满量程(最高灵敏度), + // 因为内置驱动 probe 时配的 0x03 模式会让 ALS 被 PS 共享 ADC 中断采样而归零。 + class Ap3216cPhotoSensor : public IPhotoSensor + { + private: + std::string device_path_; + bool opened_; + uint16_t level_; + uint16_t max_value_; +#if defined(__linux__) + int fd_; +#endif + + public: + Ap3216cPhotoSensor(); + ~Ap3216cPhotoSensor(); + + bool init(const std::string& device_path = "/dev/i2c-0") override; + void update() override; + void shutdown() override; + + bool is_open() const override { return opened_; } + uint16_t read_level() const override { return level_; } + const std::string& get_device_path() const override { return device_path_; } + uint16_t get_max_value() const { return max_value_; } + }; +} diff --git a/src/Core/Platform/DefaultHardware.h b/src/Core/Platform/DefaultHardware.h index df34c2f..9735bd6 100644 --- a/src/Core/Platform/DefaultHardware.h +++ b/src/Core/Platform/DefaultHardware.h @@ -6,7 +6,7 @@ #include "EvdevButtonInput.h" #include "EvdevKeyboardState.h" #include "EvdevTouchInput.h" -#include "LinuxPhotoSensor.h" +#include "Ap3216cPhotoSensor.h" #else #include "SdlAudioInput.h" #include "SdlAudioOutput.h" @@ -24,7 +24,7 @@ namespace Platform typedef EvdevButtonInput DefaultButtonInput; typedef EvdevKeyboardState DefaultKeyboardState; typedef EvdevTouchInput DefaultPointerInput; - typedef LinuxPhotoSensor DefaultPhotoSensor; + typedef Ap3216cPhotoSensor DefaultPhotoSensor; #else typedef SdlAudioInput DefaultAudioInput; typedef SdlAudioOutput DefaultAudioOutput; diff --git a/src/Core/Platform/EvdevButtonInput.cpp b/src/Core/Platform/EvdevButtonInput.cpp index b646c2e..482b502 100644 --- a/src/Core/Platform/EvdevButtonInput.cpp +++ b/src/Core/Platform/EvdevButtonInput.cpp @@ -11,8 +11,8 @@ namespace Platform { EvdevButtonInput::EvdevButtonInput() - : device_path_("/dev/input/event0"), - key_code_(28), + : device_path_("/dev/input/event2"), + key_code_(114), opened_(false), down_(false), pressed_(false), @@ -33,8 +33,8 @@ namespace Platform { shutdown(); - device_path_ = device_path.empty() ? "/dev/input/event0" : device_path; - key_code_ = key_code == 0 ? 28 : key_code; + device_path_ = device_path.empty() ? "/dev/input/event2" : device_path; + key_code_ = key_code == 0 ? 114 : key_code; down_ = false; pressed_ = false; released_ = false; diff --git a/src/Core/Platform/EvdevButtonInput.h b/src/Core/Platform/EvdevButtonInput.h index c29a2a8..7e02208 100644 --- a/src/Core/Platform/EvdevButtonInput.h +++ b/src/Core/Platform/EvdevButtonInput.h @@ -21,7 +21,7 @@ namespace Platform EvdevButtonInput(); ~EvdevButtonInput(); - bool init(const std::string& device_path = "/dev/input/event0", int key_code = 28) override; + bool init(const std::string& device_path = "/dev/input/event2", int key_code = 114) override; void update() override; void shutdown() override;