添加补充光敏传感器驱动
This commit is contained in:
parent
e0946fbf36
commit
334c9ee96f
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
#include "GameObject.h"
|
||||
|
|
|
|||
|
|
@ -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<uint32_t>(manual_light_level_) + light_step_;
|
||||
manual_light_level_ = static_cast<uint16_t>(next > 4095 ? 4095 : next);
|
||||
has_manual_override_ = true;
|
||||
smoothed_light_q8_ = static_cast<int32_t>(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<int32_t>(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<int32_t>(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<uint16_t>(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)
|
||||
|
|
|
|||
|
|
@ -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_;
|
||||
|
|
|
|||
|
|
@ -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<Platform::SDLDisplay*>(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);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,151 @@
|
|||
#include "Ap3216cPhotoSensor.h"
|
||||
#include <iostream>
|
||||
|
||||
#if defined(__linux__)
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <linux/i2c-dev.h>
|
||||
#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<ssize_t>(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<uint16_t>(lo) |
|
||||
(static_cast<uint16_t>(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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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_; }
|
||||
};
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue