买了个能转的显示器当副屏, 然后因为买的太便宜, 没有带旋转感应的(不知道别的有没有).
然后觉得好像不大方便, 就想着写一个脚本, 能够一键切换屏幕旋转状态.
最后配合 MultiMonitorTool 的加载配置文件功能实现了一键切换布局.
我找到了内心的平静.
直到我发现, 这实在是太蠢了.
我每次都要找到脚本在哪, 再双击他切换布局.
于是我找了一个右键菜单编辑器, 把这个脚本放进了桌面的右键菜单里.
然后在桌面右键点击两次就能切换旋转状态.
我找到了内心的平静.
直到我发现, 这实在是太蠢了.
为什么不能直接根据实际状态旋转呢?
(x
DIY 这样一个东西, 大约需要 30 来块钱.
我买了一块已经焊好 USB 的 CH341A 和一块 MMA8452Q 模块, 某宝大约二十拿下.
然后再买了一些很长的杜邦线(因为要把 CH341 藏到桌子下面), 这样一共就花了三十多.
把 CH341 调成 3.3v 输出和 I2C 模式, 接上 MMA8452 的 4 根 I2C 针脚.
然后用装上 CH341 的 I2C 模式驱动:驱动
从里面找到CH341DLL.DLL
, 之后要用.
使用测试工具验证连接是否正确, 驱动是否安装成功: CH341 测试工具
找到压缩包内的CH341PAR\VC\CH341PAR.EXE
, 然后再把之前从驱动压缩包里翻出来的CH341DLL.DLL
放在一起.
运行CH341PAR.EXE
, 切换到 EEPROM 配置.
按照文档说明, 只接四根线的话, MMA8452 的设备地址应该是0x1c
: MMA8452 文档
然后在0x0d
上能够读取的 MMA8452 的 WHO_AM_I 信息: 0x2a
到这里硬件部分算是连接成功了.
然后编写代码轮询传感器, 获取屏幕旋转状态并通过 MultiMonitorTool 自动切换布局.
MultiMonitorTool
从刚刚的 CH341 测试工具包里可以找到CH341DLL.h
和CH341DLL.lib
代码仅供参考, 我一个写 JS 的, 写出来的玩意能跑就行.
#include <windows.h>
#include <stdio.h>
#include <signal.h>
#include "CH341DLL.H"
#define DEVICE_ADDR 0x1c // MMA8452 设备地址
#define DEVICE_ID 0 // CH341 设备编号
#define getbit(x, y) ((x) >> (y)&1)
// 检查设备状态并自动初始化重连
int CheckDevice(int DeviceId) {
UCHAR read = 0;
// 尝试读取 MMA8452 的 WHO_AM_I 信息
if (!CH341ReadI2C(DeviceId, DEVICE_ADDR, 0x0d, &read)) {
// 如果读不到, 尝试重新打开 CH341, 并再次尝试读取 WHO_AM_I 信息
CH341CloseDevice(DeviceId);
if (CH341OpenDevice(DeviceId) == INVALID_HANDLE_VALUE || !CH341ReadI2C(DeviceId, DEVICE_ADDR, 0x0d, &read)) {
printf("Failed to open device #%d.\n", DeviceId);
return 0;
} else {
printf("Opened device #%d.\n", DeviceId);
}
}
// 根据读到的 WHO_AM_I 信息检查模块是否为 MMA8452, 如果没连接模块应该读到 0xFF
if (read != 0x2a) {
puts("Invalid device WHO_AM_I response.");
return 0;
}
// 读取模块状态
if (!CH341ReadI2C(DeviceId, DEVICE_ADDR, 0x2a, &read)) {
printf("Device #%d closed unexpected.\n", DeviceId);
return 0;
}
// 如果模块没有被启用
if (read != 0x01) {
if(
!CH341WriteI2C(DeviceId, DEVICE_ADDR, 0x2a, 0b00000000) // 禁用模块(必须禁用掉模块才能调整模块设置)
|| !CH341WriteI2C(DeviceId, DEVICE_ADDR, 0x11, 0b11000000) // 启用旋转方向检测
|| !CH341WriteI2C(DeviceId, DEVICE_ADDR, 0x2a, 0b00000001) // 启用模块
|| (!CH341ReadI2C(DeviceId, DEVICE_ADDR, 0x2a, &read) || read != 0x01) // 检查模块是否被正确启用
) {
printf("Failed to initialize device #%d.\n", DeviceId);
return 0;
} else {
printf("Device #%d initialized successfully.\n", DeviceId);
}
}
return 1;
}
int main() {
UCHAR mBuffer = 0;
UCHAR Last = 255;
UCHAR Curr = 255;
if (LoadLibrary("CH341DLL.DLL") == NULL) {
puts("Cannot load required Dynamic Library CH341DLL.DLL.");
return -1;
}
while (1) {
Sleep(1000);
if (!CheckDevice(DEVICE_ID)) {
continue;
}
// 读取模块旋转状态
if (!CH341ReadI2C(0, DEVICE_ADDR, 0x10, &mBuffer) || mBuffer == 0xff) {
puts("Device disconnected unexpected.");
continue;
}
// 取出数据
// 我的显示器只能旋转 90 度, 于是偷懒只取出了表示横屏还是竖屏的数据位
// 如果有需要, 可以根据文档取得更多数据
// 此数据第 2 位表示横屏竖屏
// 第 1 位表示旋转方向
// 这两位结合可以判断屏幕的四向旋转
// 具体请参考 MMA8452 文档对于 0x10 数据的解释
Curr = getbit(mBuffer, 2);
if (Curr != Last) {
if (Curr == 0) {
system("MultiMonitorTool.exe /LoadConfig landscape.cfg"); // 用 MMT 加载预先写好的显示器布局配置文件
} else {
system("MultiMonitorTool.exe /LoadConfig portrait.cfg");
}
}
Last = Curr;
}
}
代码必须使用 VC 去编译, 因为 CH341DLL.dll 就是用的 VC, mingw 弄半天发现跑不起来就是这个操蛋原因.
把编译出来的东西跟 MultiMonitorTool 还有 CH341DLL.dll 放在一起, 就能使用了.
编译的成品有需要可以分享出来, 但是写了这么久还是有点懒了, 晚点再传吧.
把模块粘在显示器背后, 再把这个轮询程序放到后台, 非常完美. 纯 C 编写的工具占用也非常小, 挂后台 0.3M 的内存占用, 基本不吃米.
这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。
V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。
V2EX is a community of developers, designers and creative people.