风筝
发表于: 2018-8-25 09:09:48 | 显示全部楼层

本文主要介绍如何制作一个设备,用于检测计算机显示器何时物理旋转,并自动告知计算机相应地旋转显示器。


如今很多数字内容都是狭窄而高大的(纵向),但大多数计算机屏幕都是短而长(横向)。这意味着很多屏幕的空间浪费了。比较下面的两个截图。在横向视图中只能看到一个半的帖子,而在纵向视图中可以看到3个帖子。

landscape.png


portrait.png


因此,在浏览社交媒体时将计算机显示器旋转90º是一个好主意,但是来回切换显示设置非常繁琐。如果您有多台显示器或标准键盘快捷键不起作用的计算机,则尤其如此。今天我将介绍如何构建一个设备,以便在显示器物理旋转时自动告诉计算机旋转显示器。


工作原理

该设备将基于Arduino开发板,并将安装在显示器的背面。我们将使用加速度计来感知方向。静止时,加速度计将重力解释为向上加速度为9.8m / s2。加速度计将使用I2C协议与Arduino通信。 Arduino将不断查看(轮询)加速度计认为它正在加速的方向。当改变时,它将通过USB向计算机发送信号。计算机上运行的python脚本将读取更改,并将告诉计算机相应地旋转显示。

completed.jpg


本文介绍如何在Windows和Linux上实现此功能。它也可以在Mac上完成。 (唯一的区别是Python脚本调用的系统命令。)


所需的材料

●    Arduino  Uno开发板

●    MMA7455加速度计

●    固定胶水

●    按钮

●    100kΩ电阻

parts.jpg


当然,您还需要一台可以旋转的显示器。如果您有一个非旋转显示器,请检查它是否有VESA安装孔(背面的正方形上有4个或8个螺孔)。您可以购买VESA兼容的显示器支架,旋转价格约为70美元。


制作过程

如果您有很多电子方面的经验,加速度计上的一些引脚名称应该是熟悉的。 VCC和GND将连接到Arduino的电源。 CS表示“芯片选择”。由于我们在这个项目中只有一个芯片,我们将其物理连接到5V。 SDA和SCL是I2C协议中使用的通信线。如果您不使用Arduino Uno,SDA和SCL的引脚可能会有所不同。

加速度计引脚
Arduino Uno引脚
VCC
5V
GND
GND
CS
5V
SDA
4
SCL
5

above-cropped.jpg


对于按钮,我们将以低电平有效配置连接它。由于我们正在使用软件去抖,因此不需要电容。 BUTTON_PIN在代码中定义为引脚12。

button_diagram.png


Arduino代码

将扩展板插入Arduino,然后通过USB将Arduino开发板插入计算机。


如前所述,我们可以使用加速度计来检测方向,通过测量哪个方向读取到9.8m / s2的加速度。由于显示器只能垂直或水平,因此这很容易。


编程时将代码的不同功能分成不同的文件是一种很好的做法。我们根据官方MMA7455库编写了一个感知方向的库。这允许我们抽象出如何完成此操作的细节,因此我们的主文件可以保持简单,通过一个函数调用检查方向,并在计算机发生更改时将消息写入计算机。


在Arduino IDE中打开monitor.ino。编译并运行代码。 使用 Tools > Serial Monitor或使用ctrl + shift + M打开串口监视器。

您应该看到类似于以下的内容。

  1. Freescale MMA7455 accelerometer
  2. May 2012
  3. The MMA7455 is okay
  4. STATUS : 0
  5. WHOAMI : 55
  6. Assuming the device is Y_POS
复制代码

现在握住Arduino,使其处于垂直平面。旋转它(仍然在垂直平面内)。每次旋转90º,你应该看到这样的东西:

  1. Rotate Monitor
  2. change detected
  3. Rotate Monitor
  4. change detected
  5. Rotate Monitor
  6. change detected
  7. Rotate Monitor
复制代码

将设备安装到显示器背面,然后使用USB电缆将其插入计算机。

mounted.jpg


我们需要校准加速度计,以便它知道它在打开时最初指向的方向。只要Arduino打开,这个初始化就会运行。在显示器上使用时,这可能是在您打开计算机时。 (取决于计算机关闭时USB端口是否供电。)因此,当您打开计算机时,此设备希望您的显示器处于您定义的默认位置。如果不是这样,Arduino将告诉您的计算机以错误的方式旋转显示器,否则它将变得迷失方向,并且不会告诉计算机完全旋转。

这就是为什么我们有按钮。按下按钮后,Arduino会重新运行设置程序并告诉计算机将显示旋转到START_ORIENT


将设备安装在显示器背面时,请记下显示器处于默认方向时的方向。

monitor.ino中有一行代码需要相应更改。如果加速度计上的y箭头指向上方,则将其保留为Y_POS。如果y箭头指向下方,请将其更改为Y_NEG。类似地,对于x轴,使用X_POS或X_NEG。

  1. #define START_ORIENTATION Y_POS
复制代码

跳转到指定楼层
风筝
发表于: 2018-8-25 09:30:46 | 显示全部楼层

代码说明

首先我们先来看一下MMA7455.h。

  1. typedef enum {Y_POS, Y_NEG, X_POS, X_NEG, Z_POS, Z_NEG, NOT_SURE} orientation;
复制代码

枚举enum是C中的数据类型,就像int或char一样。它用于变量,这些变量可以是非数字变量的短列表中的一个值。在这种情况下,当我们将设备的方向保存到变量中时,我们使用枚举。


现在我们来看看加速度计。你会看到一个x箭头和一个y箭头。当x箭头指向时,我们称之为X_POS。当x箭头指向下方时,我们称之为X_NEG。 z轴指向板外或板内。当电路板平放在桌子上时就是Z_POS。

当安装在显示器背面时,设备应该只有y轴或x轴向上或向下。我们在monitor.ino中创建了一个名为VALID_ORIENTATION的宏来检查给定的方向是否与我们期望的4个选项之一匹配。如您所见,我们可以像任何其他数据类型一样检查枚举的值。

  1. #define VALID_ORIENTATION(o) ((o==Y_POS) || (o==Y_NEG) || (o==X_POS) || (o==X_NEG))
复制代码

setup函数主要是诊断。有3个关键点。

  1. Serial.begin(9600);
复制代码

上面的行启动了串行通信,这是我们与计算机通信的方式。


  1. Wire.begin();
复制代码

这初始化了I2C通信,这就是我们与加速度计通信的方式。它只是初始化Arduino端的设置。我们使用以下行初始化加速度计。

  1. error = MMA7455_init(START_ORIENTATION);
复制代码

按下物理按钮时,触点不会完全关闭。它们会抖动,输出信号看起来好像用户在几毫秒内按下按钮几次。我们等待100ms以确保代码不会将这些抖动误认为是多次按下。我们可以采取两种方法来解决这个问题。我们可以实现硬件去抖或软件去抖动。硬件方法包括在电路中添加低通滤波器,无论是有源还是无源。由于这涉及购买和焊接额外的组件,我选择了软件去抖动。


我们使用的加速度计有一些中断功能,但它不适用于我们的用例。通常,当您希望按钮执行某些操作时,最好使用中断。但是,由于我们无论如何都在轮询加速度计,我们也可以轮询按钮。


每次循环,我们检查按钮当前是否被按下。如果是,并且上次未按下它,那么我们知道用户刚刚按下按钮,因此我们再次运行例程,并将START_ORIENTATION发送到计算机。然后我们等待100ms再继续。这种延迟是为了让Arduino在继续之前等待按钮触点稳定下来。不可否认,这是一种相当草率和懒惰的软件去抖方法,因为Arduino在延迟期间什么都不做。更复杂的方法将涉及计时器,以便在Arduino等待按钮时可以继续执行其他代码。在这种特殊情况下,我们唯一错过的是使用延迟而不是计时器,这是加速度计的读数变化。因为当用户按下按钮时不应该发生这种情况,所以这种草率方法是可以接受的。延迟方法的唯一优点是它很简单。硬件去抖动需要更多组件,编写中断和延迟代码会变得棘手。

  1. }else if(!digitalRead(BUTTON_PIN) // button has been pressed
  2.     && previous_button_state)    // and this is the first time it's been
  3.                                   // pressed
  4. {
  5.    // send even if orientation hasn't changed
  6.    setup(); //re-calibrate
  7.    sendOrientation(START_ORIENTATION);
  8.    delay(100); //software debounce
  9.    previous_button_state = 0;
  10. }
复制代码

Windows计算机代码

Arduino将使用我们用于编程和调试的相同串行接口向计算机发送信息。为避免冲突,请确保在运行此脚本时关闭Arduino IDE中的串行监视器。如果它是打开的,一些Arduino的消息将被串行监视器,并且脚本将不会看到它们,或者只会看到它们中难以理解的部分。

我们将使用Python作为我们的计算机端代码。这将听取Arduino写入串口的内容,并进行系统调用以旋转显示器。


这里为您的计算机下载Python 3。在安装过程中询问是否要将python添加到path中时,请选择“yes”。

在之前下载的zip文件中找到windows-script.py。

打开Arduino IDE。单击Tools > Serial Port。看一下列出的端口,例如COM3。

如果你看到COM3在文本编辑器中打开windows-script.py。找到以下行,并将其更改为与您看到的一致。

  1. possibleDevices = ["COM3"]
复制代码

搜索以下行。您需要更改这些数字以适合您的设置。对于每个可能的方向,数字是顺时针旋转的度数。

  1. translation = {"Y_POS":"90",
  2.                "X_POS":"180",
  3.                "X_NEG":"0",
  4.                "Y_NEG":"270"}
复制代码

现在我们需要安装pySerial模块,用于从USB端口读取。打开命令提示符。在Windows 8及更高版本中,搜索cmd。在Windows 7及更早版本中,在开始菜单中找到run实用程序并输入cmd。

在现在出现的终端中,键入:

  1. python -m pip install pyserial
复制代码

安装python模块可能很麻烦。如果未成功安装,请不要继续。

现在,在运行脚本之前,我们还需要做一件事。找到上述zip文件的Windows文件夹中包含的显示实用程序。


如果您有多台显示器,请执行以下操作。如果您只有一台显示器,请跳过此段落。打开保存display.exe的命令提示符。 在命令提示符下输入display.exe / listdevices。您应该看到每个监视器的索引。记下要旋转的索引的内容。


python脚本将打开与USB端口的连接。当它检测到来自Arduino的消息时,它会将相应的命令发送到display.exe,这将旋转显示。找到以下行,并将目录更改为指向display.exe。

  1. command = "C:/.../display.exe /rotate:" + translation[direction]
复制代码

对于具有多个监视器的用户,还要添加/ device x(其中x是监视器的索引)。所以类似于:

  1. command = "C:/.../display.exe /device 2 /rotate:" + translation[direction]
复制代码

在命令提示符中,导航到保存display.exe和windows-script.py的文件夹。使用cd命令执行此操作。例如,如果它们保存在Documents / rotate中,请使用:

  1. cd Documents/rotate
复制代码

现在输入dir以查看该目录中的文件列表。仔细检查您是否看到这两个文件。现在让我们运行python脚本。

  1. python windows-script.py
复制代码

尝试旋转设备。屏幕应相应旋转。


现在我们需要告诉Windows每次启动计算机时都在后台运行该程序。默认情况下,启动程序会明显打开。我们不希望一直看到可见的命令窗口。因此,我们将创建一个批处理文件,它将在后台启动python脚本,然后批处理文件将退出。

首先,打开您的启动文件夹。此文件夹的位置取决于您使用的Windows版本。按Windows键,然后按R.应显示运行窗口。键入shell:startup并按Enter键。现在您应该看到您的启动文件夹。创建一个名为monitor.bat的新文本文件,并将以下代码粘贴到该文件中。修改路径,使其指向您的python脚本。

  1. @echo off
  2. START pythonw C:\...\windows-script.py
复制代码

我们使用pythonw而不是python,因为这是隐藏python脚本的命令窗口所必需的。

现在重新启动计算机并检查它是否有效。

回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

主题 700 | 回复: 1479



手机版|

GMT+8, 2024-3-28 20:33 , Processed in 0.094704 second(s), 6 queries , Gzip On, MemCache On. Powered by Discuz! X3.5

YiBoard一板网 © 2015-2022 地址:河北省石家庄市长安区高营大街 ( 冀ICP备18020117号 )

快速回复 返回顶部 返回列表