# 简介
本项目为RT-Thread旗下开源硬件ART-Pi的扩展板。为ART-Pi扩展了VGA和USB接口。可以用来连接VGA显示器和键盘,将ART-Pi打造成一台类似“PC”的设备。或者作为简单的GUI开发平台。
# 特性
* 支持标准VGA接口(D-Sub),由LTDC控制器驱动
* 最高支持XGA(1024x768)的分辨率
* 支持DDC协议,使用硬件I2C控制器,可用来提供EDID的支持
* 专用视频DAC,支持最高1600万色(16.7M色,RGB888)
* D-Sub上具有专有ESD保护,防止静电损伤
* 支持USB 2.0 Full-Speed
* USB接口上带有专用ESD防护和过流保护,以及一个过流保护指示灯
* 一个额外的电源接口,可以为扩展板和ART-Pi供电,输入电压为7~12V
* 通孔型扩展板,可继续连接其他扩展板模块
# 原理详解
## VGA
首先,强烈推荐大家去看这个视频,链接:[这可能是世界上最差的显卡](https://www.bilibili.com/video/BV1xJ411z7Mz) ,这个视频中作者在面包板上制作了一个可以输出VGA信号的“显卡”。作者对VGA信号的解释非常简单易懂,适合从来没有了解过VGA的朋友观看。
VGA的信号中有控制信号(同步信号)和数据信号,对于我们这个项目而言,我们首先要注意的是VGA控制信号的时序:
![image.png](//image.lceda.cn/pullimage/1FyQgfeCN3sJwDh5XG1Xo7Ek8yDwrcYRDiVUPn2K.png)
这张时序图也解释了VGA的具体工作方式:显示的内容是逐行发送的,在一行的内容发送完毕后,通过一个名为Horizontal Sync Pulse(HSP)的脉冲来通知显示设备当前行的内容传输完毕,然后传输下一行。如此循环,直到整屏的内容传输完毕后发送一个Vertical Sync Pulse(VSP)脉冲,至此一帧传输完毕。然后继续传输下一帧。
VGA是纯模拟信号,它是为CRT显示器准备的,显示设备完全是通过HSP和VSP两个脉冲的长度以及脉冲之间的间隔来确定信号的格式。因为没有数字通讯的过程,所以对时序的准确度要求较高。而且由于显示器的限制,事实上我们并不能自由决定HSP和VSP的信号长度和间隔,下面则是一些常用的信号格式:
![image.png](//image.lceda.cn/pullimage/5EyBgre6IlZt55JPh6HdRZD6yZv7WDNEs88afCcz.png)
知道了信号的格式,我们该如何产生精确的信号时序呢。可能有同学会说使用Timer定时器就可以。这的确也是一种方法, 但是实现起来比较麻烦。这里我们不用它而是使用ART-Pi的主控STM32H750XB的一个外设——LTDC来实现VGA信号的生成。
LTDC全称是“LCD-TFT display controller”即LCD显示设备控制器。故名思意,LTDC是用来控制LCD液晶显示器的外设,它有两种工作模式:DE模式和HV模式,区别是DE模式仅使用DE信号进行帧同步,而HV模式则需要使用HSYNC和VSYNC两个信号来进行数据帧同步。
看过VGA的信号工作原理后,相信你一定会对HV模式的HSYNC和VSYNC比较眼熟。事实上,LTDC的HV模式的工作原理和VGA信号的原理是高度一致的。只是LTDC传输的是数字信号而已。这也是我们使用LTDC产生VGA信号的理论基础。
我们来看LTDC的信号时序图(HV模式):
![image.png](//image.lceda.cn/pullimage/yMO3LCnUNS74b7FDABN2orFj9LzSPSeACDzkY37O.png)
咋看一下似乎跟VGA信号的时序完全不同,其实仔细分析就会发现。其工作原理跟VGA的工作原理是完全一致的,只是这张图中把SYNC PULSE画到了前面,所以只是看起来有些不一致。
所以我们只需要将LTDC控制器的参数时序调整到与期望的VGA信号时序一致即可生成准确的VGA同步信号。接下来的问题是,我们使用那个信号格式?回到那张常用的VGA信号格式表,可以看到,大部分的信号的频率都是非整数倍,而生成非整数倍的信号是非常费劲的一件事,需要我们在STM32的时钟配置上做出较大的妥协才行。所以,最适合的信号就是这个:
![image.png](//image.lceda.cn/pullimage/7RZKChF1epC4Am3YD2gm5tz1ep7atZ5dQHXqBOFl.png)
40MHz的信号是一个非常容易实现的整数。然后800x600@60Hz的分辨率和刷新率也都比较合适。
然后我们可以查看这个信号格式的具体参数:
![image.png](//image.lceda.cn/pullimage/wNRLY9ld9UfTNzVxqmSeUFK83uCnxykvH6XIrFef.png)
我们需要通过这些参数我们来配置STM32H750的LTDC控制器。这里推荐通过CubeMX配置,方便而且不容易出错:
![image.png](//image.lceda.cn/pullimage/KrnOuxqm5uB3iBcTAh6JGyIY5PGDLuPLicip3QCc.png)
![image.png](//image.lceda.cn/pullimage/0KNggIUIQwdxVOYf4S31XyknmSaGrP9B983zywkL.png)
LTDC的其他的配置与正常使用LCD屏时的配置相同,此处不再赘述。
控制信号满足要求了,接下来是显示内容的发送。LTDC发送的是数字信号,而VGA显示设备需要接收模拟信号。显然,我们需要一个DAC(数模转换器)来完成这个工作。
我们先看VGA物理接口的标准:
![image.png](//image.lceda.cn/pullimage/zLVw6kzrZlh2lNfjWhisMPTqQr4qLNpxq7zNK4zB.png)
其中的HSYNC和VSYNC是同步信号,我们刚刚已经介绍了它,现在要关注的是被标记为RGB的Pin1,2,3三个引脚,这三个引脚正是用来发送对应的颜色数据的信号引脚。
我们注意到,信号在终端和源端是有75Ω的阻抗的。在简易模型中,我们可以认为就是信号对地有一个75Ω的电阻。然后信号的电压范围是0~0.7V。也就是我们需要把RGB通道的数字信号都转为0~0.7V的模拟信号,并通过这三条线传输出去。
我们可以使用电阻分压网络来实现RGB565数字信号到模拟信号的转换。这种方式成本低廉但是显示效果不是很理想。为了画质的提升,我们可以使用ADV7123这颗专用的VGA信号DAC来实现数字信号到模拟信号的转换。
![image.png](//image.lceda.cn/pullimage/aNCPgArrfS8t2YmV3I4oclY4x4qqWGU8bDMCCFpH.png)
相对于使用电阻网络实现数模转换。ADV7123需要一个额外的像素时钟信号来进行数据的同步,我们直接使用LTDC控制器输出的像素时钟信号即可。然后就没什么好说的了,根据ADV7123的数据表进行电路的设计即可,最终电路如下:
![image.png](//image.lceda.cn/pullimage/DK0EcPtIPulaTG1PpTuCMSswu16pfnN7jj883IQS.png)
由于需要连接至外部VGA设备,考虑到可能带来的ESD损伤,这里在DAC和最终的VGA输出端子之前,我们额外添加一颗专用ESD保护芯片
![image.png](//image.lceda.cn/pullimage/r1lRLRHctPjeEDul5yihlwBw8VJ6DLOnYMHq084P.png)
![image.png](//image.lceda.cn/pullimage/XLh9uS2utRVBBO1jLx51x5W5BN8cV1IAiVXk628Y.png)
至此,我们完成了输出VGA信号的全部准备工作。
## USB
为了连接USB键盘,我们需要一个USB Host接口。这块儿其实没有什么好说的,网上有大把的资料。电气连接也比较简单。
由于STM32H750的USB接口相对比较脆弱,这里为了防护可能出现的短路和静电击穿情况,额外添加了专用的ESD防护芯片和USB短路保护芯片。
![image.png](//image.lceda.cn/pullimage/uMRphECB3KkvgmeNYCGZSAkGfCLZPfe7jx9gXcjI.png)
## 电源
考虑到ART-Pi本体的供电可能不足,此处添加了一个额外的供电电路,带有反接保护和过流保护,可以直接接受6~12V的输入电压来为ART-Pi和扩展板供电。
![image.png](//image.lceda.cn/pullimage/eiWZTh8G27uF0A4eebtuFnhfOrrvigItYFwBZg1g.png)
## PCB设计
PCB直接使用ART-Pi的外形。需要注意的是,由于ART-Pi的LTDC接口是通过FPC座引出的,所以,扩展板的LTDC接口会使用FPC软排线于ART-Pi本体连接
最终设计效果图:
![image.png](//image.lceda.cn/pullimage/3LKyO9dTZP4xXN0I9Iw8UqilHXitQKcQZDP9b5AA.png)
![image.png](//image.lceda.cn/pullimage/wJ66T0HpCaHLPi23PrIsOKXT8EOlaEns5dOva2jY.png)
# 软件说明
本扩展板的设计思路是为实现一个类似[ColorMixmate](https://geoffg.net/maximite.html)的类PC设备提供硬件基础(当然你也可以完全不按照我的设计思路来使用这个扩展板)。目前的Demo中,我实现了一个小型控制台,移植了一个命令行库,并嵌入了一个小型的BASIC解释器。在命令行环境中,你可以使用我内置的一些命令来实现挂载SD卡,切换文件路径,查看图片以及播放视频的功能。在BASIC环境中,你可以使用BASIC语言来进行编码。我在BASIC环境中实现了对GPIO的控制,所以你也可以使用BASIC来实现一些基本的GPIO操作。
Demo的代码是在trueStudio中开发的,完全开源,请跳转至附件下载。
## shell环境的内置命令操作说明
* help
效果:列出所有支持的命令
* mount
效果:挂载SD卡。注意,在使用任何文件操作之间,必须先执行mount命令挂载SD卡
* clear
效果:清空屏幕
* ls
效果:列出当前目录下所有的文件
* cd [path]
效果:进入/退出目录。示例:进入test子目录:`cd test`;返回根目录: `cd /`;返回上一级目录:`cd ..`
* op [file]
效果:打开文件,目前只支持查看JPEG图片和播放MJPEG编码的AVI格式视频。
以数字开头的文件名需要用双引号括住
示例:打开图片123.jpg(注意引号不能少):`op "123.jpg"`;打开视频aerith.avi:`op aerith.avi`
JPEG图片文件限制:不大于屏幕分辨率(800x600)
AVI视频文件限制:MJPEG编码,无音频,25FPS以下,分辨率不高于800x600
* basic
进入BASIC环境,注意,进入BASIC环境后不能返回命令行环境。
## 在命令行环境中添加自己的命令
首先,我们需要创建一个命令的入口函数,函数可以为以下类型中的一种:
#### main函数形式
使用此方式,一个函数定义的例子如下:
``` C
int func(int argc, char *agrv[])
{
printf("%dparameter(s)\r\n", argc);
for (char i = 1; i < argc; i++)
{
printf("%s\r\n", argv[i]);
}
}
```
然后,在函数外部使用宏注册命令:
``` C
SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_MAIN), testcmd, func, test);
```
第一个参数为固定形式,第二个参数为命令名称,即在命令行中执行的命令名称,第三个参数为命令入口函数名称,第四个参数为注释。
然后我们就可以在终端调用这个命令:
``` sh
letter:/$ testcmd "hello world"
2 parameter(s)
hello world
```
#### 普通C函数形式
使用此方式,shell会自动对参数进行转化处理,目前支持二进制,八进制,十进制,十六进制整形,字符,字符串的自动处理,例子如下:
``` C
int func(int i, char ch, char *str)
{
printf("input int: %d, char: %c, string: %s\r\n", i, ch, str);
}
SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC), func, func, test);
```
终端调用
``` sh
letter:/$ func 666 'A' "hello world"
input int: 666, char: A, string: hello world
```
###
## BASIC环境中的GPIO操作
当前Demo只实现了GPIO的输出功能。要操作某个GPIO之前,需要使用如下命令将对应的GPIO设置为输出模式:
``` basic
PINMODE 2,1
```
第一个参数是GPIO的编号,第二个参数是模式,1表示输出模式。GPIO编号与真实的GPIO对应关系在host.c文件中被定义:
``` C
static const BASICPinDef supportedOutputPins[] = {
{2, GPIOH, GPIO_PIN_12},
{3, GPIOH, GPIO_PIN_11},
{4, GPIOA, GPIO_PIN_8},
{17, GPIOA, GPIO_PIN_15},
{22, GPIOH, GPIO_PIN_13},
{10, GPIOI, GPIO_PIN_3},
{9, GPIOI, GPIO_PIN_2},
{11, GPIOI, GPIO_PIN_1},
{5, GPIOH, GPIO_PIN_7},
{6, GPIOH, GPIO_PIN_9},
{13, GPIOB, GPIO_PIN_13},
{19, GPIOI, GPIO_PIN_7},
{26, GPIOI, GPIO_PIN_4},
};
```
如果想增加GPIO支持,也可以在此数组中添加。
设定输出模式后,就可以使用如下命令控制GPIO的输出状态
``` basic
PIN 2,1
```
第一个参数是GPIO的编号,第二个参数是输出电平,0表示低电平,1表示高电平
# 成品效果图展示
## PCB空板图
![image.png](//image.lceda.cn/pullimage/S9ATWHN4tDwjMyH2OA73FMT3eSD51HwokbMSAar9.png)
## 焊接成品图
![image.png](//image.lceda.cn/pullimage/alIf7sLaOzYS5NGphGX3X7wvaGZmLOYXjdZ4CVtI.png)
![image.png](//image.lceda.cn/pullimage/aj3KDLq4z4m9QYtSPT4RbrcU8NY2CvUux5Labm3I.png)
## 安装图
![image.png](//image.lceda.cn/pullimage/nScrOcdmn7hLUBRBMPvDADpJD9LIyTiwVVVEqRGW.png)
![image.png](//image.lceda.cn/pullimage/2YJHaclKto0NxjJ7EwYkEFzlzfumvVPf5kcMDZ03.png)
## 基本命令行环境
![image.png](//image.lceda.cn/pullimage/lhfbjPyMNCnWSn0pg15QJDdgV7bqhdmZoqjw0LDi.png)
## 显示图片
![image.png](//image.lceda.cn/pullimage/r2PJ9fn1aqkgsnibfQ1TEr1YPL0mbamXvORW8ApC.png)
## 播放视频
![image.png](//image.lceda.cn/pullimage/tmJVYASY9zrOXtyzo08675AqZLfdjB2C7e39lvu3.png)
## BASIC环境
![image.png](//image.lceda.cn/pullimage/JXAXIOv0m9Pjqs0yYvCtFXGUDZ4i7t3nYuukLrAa.png)
## 通过BASIC代码控制GPIO
![image.png](//image.lceda.cn/pullimage/aYhF9tHuDoeBNtzOKDXzBfMJ9n7hn29UkpqtdHSL.png)