找这个资料源于我的眼镜显示器,一直考虑如何能让它便携的显示一些简单内容。便携就意味着要小,要节能。
现在考虑的方案应该是ESP32+VGA,VGA转HDMI,HDMI再输出到眼镜显示器。它可以实现通过网络控制显示内容到眼镜显示器上。
以下摘一些相关资料。
ESP32 Basic PC With VGA Output 这里有如何接线
通过270欧姆电阻将ESP32 GPIO引脚2、15和21分别连接到VGA红色、绿色和蓝色。
分别将VGA Hsync和Vsync连接到ESP32 GPIO引脚17和4上。
连接DSUB15引脚5,6,7,8和10至ESP32的GND。
ESP32Lib ESP32Lib是ESP32的集合功能,包括装在Arduino库中的最高性能的VGA图形(子画面,3D),声音和游戏控制器。
此库可能的最高分辨率为800x600。 许多常见的分辨率(如320x240)已预先配置,无需费力即可使用。
vga.init(vga.MODE320x200, vga.VGABlackEdition);
让Arduino 输出VGA信号,这里讲解了一点VGA显示的原理,虽然已是2014年的文章,且是针对Arduino的。
2020.12.24
使用库ESP32Lib,并按以上方式接线,并没有连接电阻,使用示例程序,依然实现了VGA的显示。不过转到vufine眼镜失败了。
1
以下基本就是示例程序,没作什么修改。分辨率最多400x300就不能再设置了。示例程序是小环在屏幕上跳动。
#include <ESP32Lib.h>
#include <Ressources/CodePage437_8x16.h>
//pin configuration
const int redPin = 2;
const int greenPin = 15;
const int bluePin = 21;
const int hsyncPin = 17;
const int vsyncPin = 4;
VGA3Bit vga;
void setup()
{
Serial.begin(115200);
//enabling double buffering
vga.setFrameBufferCount(2);
//Mode::custom(xres, yres, fixedYDivider = 1) calculates the parameters for our custom resolution.
//the y resolution is only scaling integer divisors (yet).
//if you don't like to let it scale automatically pass a fixed parameter with a fixed divider.
Mode myMode = vga.MODE400x300;
//print the parameters
myMode.print<HardwareSerial>(Serial);
//use the mode
vga.init(myMode, redPin, greenPin, bluePin, hsyncPin, vsyncPin);
//setting the font
vga.setFont(CodePage437_8x16);
}
void balls()
{
//some basic gravity physics
static VGA3BitI::Color c[4] = {vga.RGB(0, 255, 0), vga.RGB(0, 255, 255), vga.RGB(255, 0, 255), vga.RGB(255, 255, 0)};
static float y[4] = {20, 20, 20, 20};
static float x[4] = {20, 20, 20, 20};
static float vx[4] = {.01, -0.07, .05, -.03};
static float vy[4] = {0, 1, 2, 3};
static unsigned long lastT = 0;
unsigned long t = millis();
float dt = (t - lastT) * 0.001f;
lastT = t;
const int r = 6;
for (int i = 0; i < 4; i++)
{
int rx = r;
int ry = r;
vy[i] += -9.81f * dt * 100;
x[i] += vx[i];
y[i] += vy[i] * dt;
//check for boundaries and bounce back
if (y[i] < r && vy[i] < 0)
{
vy[i] = 200 + i * 10;
ry = y[i];
}
if (x[i] < r && vx[i] < 0)
{
vx[i] = -vx[i];
rx = x[i];
}
if (x[i] >= vga.xres - r && vx[i] > 0)
{
vx[i] = -vx[i];
rx = vga.xres - x[i];
}
//draw a filled ellipse
vga.fillEllipse(x[i], vga.yres - y[i] - 1, rx, ry, c[i]);
vga.ellipse(x[i], vga.yres - y[i] - 1, rx, ry, 0);
}
}
void loop()
{
//draw a background
for (int y = 0; y * 10 < vga.yres; y++)
for (int x = 0; x * 10 < vga.xres; x++)
vga.fillRect(x * 10, y * 10, 10, 10, (x + y) & 1 ? vga.RGB(255, 0, 0) : vga.RGB(255, 255, 255));
vga.setCursor(2, 2);
vga.setTextColor(vga.RGB(0));
//show the remaining memory
vga.print(vga.xres);
vga.print("x");
vga.println(vga.yres);
vga.print("free memory: ");
vga.print((int)heap_caps_get_free_size(MALLOC_CAP_DEFAULT));
balls();
vga.show();
}
2
在另一显示字库的示例中,分辨率可设置达640x400。在我的显示器上是达到了满屏。可喜的是眼镜有一点点反映:闪,有乱图象,不稳定。是否与分辨率不匹配有关?
#include <ESP32Lib.h>
#include <Ressources/CodePage437_8x8.h>
#include <Ressources/CodePage437_8x14.h>
#include <Ressources/CodePage437_8x16.h>
#include <Ressources/CodePage437_8x19.h>
#include <Ressources/CodePage437_9x16.h>
#include <Ressources/Font6x8.h>
//pin configuration
const int redPin = 2;
const int greenPin = 15;
const int bluePin = 21;
const int hsyncPin = 17;
const int vsyncPin = 4;
//VGA Device
VGA3Bit vga;
void setup()
{
//initializing vga at the specified pins
vga.init(vga.MODE640x400, redPin, greenPin, bluePin, hsyncPin, vsyncPin);
//selecting the font
vga.setFont(Font6x8);
//set color
vga.setTextColor(vga.RGB(255, 0, 0), vga.RGB(0, 0, 255));
//displaying the character set
vga.println("Font6x8");
for (int i = 0; i < 256; i++)
vga.print((char)i);
vga.println();
vga.setFont(CodePage437_8x8);
vga.setTextColor(vga.RGB(0, 255, 0), vga.RGB(255, 0, 0));
vga.println("CodePage437_8x8");
for (int i = 0; i < 256; i++)
vga.print((char)i);
vga.println();
vga.setFont(CodePage437_8x14);
vga.setTextColor(vga.RGB(0, 0, 255), vga.RGB(0, 255, 0));
vga.println("CodePage437_8x14");
for (int i = 0; i < 256; i++)
vga.print((char)i);
vga.println();
vga.setFont(CodePage437_8x16);
vga.setTextColor(vga.RGB(255, 255, 0), vga.RGB(0, 255, 255));
vga.println("CodePage437_8x16");
for (int i = 0; i < 256; i++)
vga.print((char)i);
vga.println();
vga.setFont(CodePage437_8x19);
vga.setTextColor(vga.RGB(255, 0, 255), vga.RGB(255, 255, 0));
vga.println("CodePage437_8x19");
for (int i = 0; i < 256; i++)
vga.print((char)i);
vga.println();
vga.setFont(CodePage437_9x16);
vga.setTextColor(vga.RGB(0, 255, 255), vga.RGB(255, 0, 255));
vga.println("CodePage437_9x16");
for (int i = 0; i < 256; i++)
vga.print((char)i);
vga.println();
}
3
在接下来VGAHighRes示例中,分辨率达到800x600,但眼镜没有反映了。说明与分辨率高无关。
#include <ESP32Lib.h>
#include <Ressources/CodePage437_9x16.h>
const int redPin = 2;
const int greenPin = 15;
const int bluePin = 21;
const int hsyncPin = 17;
const int vsyncPin = 4;
VGA3BitI vga;
///draws the bitluni logo
void bitluni(int x, int y, int s)
{
vga.fillCircle(x + 2 * s, y + 2 * s, 2 * s, vga.RGB(128, 0, 0));
vga.fillCircle(x + 22 * s, y + 2 * s, 2 * s, vga.RGB(128, 0, 0));
vga.fillCircle(x + 2 * s, y + 22 * s, 2 * s, vga.RGB(128, 0, 0));
vga.fillCircle(x + 22 * s, y + 22 * s, 2 * s, vga.RGB(128, 0, 0));
vga.fillRect(x, y + 2 * s, 24 * s, 20 * s, vga.RGB(128, 0, 0));
vga.fillRect(x + 2 * s, y, 20 * s, 24 * s, vga.RGB(128, 0, 0));
vga.fillCircle(x + 7 * s, y + 4 * s, 2 * s, vga.RGB(255, 255, 255));
vga.fillCircle(x + 15 * s, y + 6 * s, 2 * s, vga.RGB(255, 255, 255));
vga.fillCircle(x + 11 * s, y + 16 * s, 6 * s, vga.RGB(255, 255, 255));
vga.fillCircle(x + 13 * s, y + 16 * s, 6 * s, vga.RGB(255, 255, 255));
vga.fillCircle(x + 11 * s, y + 16 * s, 2 * s, vga.RGB(128, 0, 0));
vga.fillCircle(x + 13 * s, y + 16 * s, 2 * s, vga.RGB(128, 0, 0));
vga.fillRect(x + 11 * s, y + 14 * s, 2 * s, 4 * s, vga.RGB(128, 0, 0));
vga.fillRect(x + 9 * s, y + 14 * s, 2 * s, 2 * s, vga.RGB(128, 0, 0));
vga.fillRect(x + 5 * s, y + 4 * s, 4 * s, 12 * s, vga.RGB(255, 255, 255));
vga.fillRect(x + 9 * s, y + 10 * s, 4 * s, s, vga.RGB(255, 255, 255));
}
void setup()
{
vga.init(vga.MODE800x600, redPin, greenPin, bluePin, hsyncPin, vsyncPin);
vga.setFont(CodePage437_9x16);
vga.clear(vga.RGB(0xffffff)); //clearing with white background
vga.setCursor(10, 10); //text position
vga.setTextColor(vga.RGB(0)); //black text color no background color
vga.print("free memory: "); vga.print((int)heap_caps_get_free_size(MALLOC_CAP_DEFAULT)); //show the remaining memory
bitluni(150, 60, 20); //draw the logo
}
4
这个示例正好是我设想的,但可惜的是眼镜依然没反映。
它实现了开启AP,连接AP后,可以通过浏览器,发送信息到显示器上。当然, 这里暂时只有ASCII码。如果有价值的话,可以研究如何显示中文。
VGA3BitI vga; 原示例为VGA3Bit, VGA3BitI是隔行扫描? 改为VGA3BitI后,分辨可以调到720x400(虽然显示效果不是太好),否则只最多400*300
在注意到了剩余内存
VGA3BitI 720x400 11880
VGA3BitI 640x480 1484
分析:内存没有了,估计就不能增加分辨率了。另外,Web服务占用了部份内存,所以分辨率达不到之前一个例子中的分辨率。在VGA3Bit模式下,分辨率低得多。
按此逻辑,或许增加ESP32的内存能提升分辨率达到更高。
在实例中,将分辨调高后,导致AP不能连接,Web服务不能正常使用。不得以,改回400x300就正常了….
#include <stdio.h>
#include <WiFi.h>
#include <WebServer.h>
#include <ESP32Lib.h>
#include <Ressources/CodePage437_9x16.h>
//true: 自己创建AP, false: 接入其它AP
const bool AccessPointMode = true;
const char *ssid = "VGA";
const char *password = "";
const int redPin = 2;
const int greenPin = 15;
const int bluePin = 21;
const int hsyncPin = 17;
const int vsyncPin = 4;
WebServer server(80);
VGA3BitI vga;
//显示的网页,这是包括在另一个文件中。这不重要。
const char *page =
#include "page.h"
;
//Web根目录服务
void sendPage() {
server.send(200, "text/html", page);
}
///把返回的内容显示在屏幕上
void text()
{
server.send(200, "text/plain", "ok");
vga.println(server.arg(0).c_str());
}
void setup()
{
Serial.begin(115200);
if (AccessPointMode)
{
Serial.println("Creating access point...");
WiFi.softAP(ssid, password, 6, 0);
}
else
{
Serial.print("Connecting to SSID ");
Serial.println(ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED)
{
delay(500);
vga.print(".");
}
}
vga.init(vga.MODE400x300, redPin, greenPin, bluePin, hsyncPin, vsyncPin);
vga.clear(vga.RGBA(0, 0, 255)); //蓝色背景
vga.backColor = vga.RGB(0, 0, 255);
vga.setFont(CodePage437_9x16);
server.on("/", sendPage); // Web /
server.on("/text", text); // Web /text
server.begin();
//显示一些信息到屏幕上
vga.clear(vga.RGBA(0, 0, 255));
vga.setCursor(0, 0);
vga.println("----------------------");
vga.println("bitluni's VGA Terminal");
if (AccessPointMode)
{
vga.print("SSID: ");
vga.println(ssid);
if (strlen(password))
{
vga.print("password: ");
vga.println(password);
}
vga.println("http://192.168.4.1");
}
else
{
vga.print("http://");
vga.println(WiFi.localIP().toString().c_str());
}
vga.println("----------------------");
}
void loop()
{
server.handleClient();
delay(10);
}