(摘) EPS32+VGA输出

声明:内容源自网络,版权归原作者所有。若有侵权请在网页聊天中联系我

找这个资料源于我的眼镜显示器,一直考虑如何能让它便携的显示一些简单内容。便携就意味着要小,要节能。

现在考虑的方案应该是ESP32+VGA,VGA转HDMI,HDMI再输出到眼镜显示器。它可以实现通过网络控制显示内容到眼镜显示器上。

以下摘一些相关资料。

VGA转HDMI,价格25

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。

用ESP32复制经典街机游戏

VGA库

ESP32VGA

ESP32Lib ESP32Lib是ESP32的集合功能,包括装在Arduino库中的最高性能的VGA图形(子画面,3D),声音和游戏控制器。

此库可能的最高分辨率为800x600。 许多常见的分辨率(如320x240)已预先配置,无需费力即可使用。

vga.init(vga.MODE320x200, vga.VGABlackEdition);

B站视频,讲得似乎比较详细,奈何是英语

ESP32 VGA Black Edition

让Arduino 输出VGA信号,这里讲解了一点VGA显示的原理,虽然已是2014年的文章,且是针对Arduino的。

这里还有另一种ESP32 VGA板子


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);
}

相关文章