本帖最后由 anananjjj 于 2018-12-15 18:54 编辑
14年设计的扩展板(
https://bbs.eeworld.com.cn/thread-440975-1-1.html)最初是为了搞一个掌机模拟器来玩玩,但是当时驱动没有调好,再加上理解不到位,始终没有实现啊!最近有时间了,在网上检索发现了一篇文章:访问了他的Github:
https://github.com/bear24rw, 将用得到的源码fork到自己的账户下,在他移植开发的基础上进行了修改,最终实现了三款模拟器在我的开发板上的运行和测试:
执行make前一定要修改系统时间。因为电路板没有添加RTC模块,每次开机时时间都会重置,直接执行make的话,会出现:
make[1]: warning: Clock skew detected. Your build may be incomplete.的提示,就是因为系统时间可能比文件的修改时间还要早。make是靠文件时间的先后决定某个文件是否需要编译的,所以不先修改系统时间的话基本上一定会导致make不完全。
修改系统时间的方法:执行:
date 0918141618.30
1、首先是fc模拟器:FCEUX(支持nes ):
https://github.com/bear24rw/gamingcape_fceu
这里用到了scons这个编译工具,scons的安装是基于python的,所以按顺序整个安装过程分三步:下载Python,用Python安装scons,用scons安装fceux。最新官方的SDK文件系统中支持python,所以python不必安装。
python setup.py install稍等一段时间,终端会显示编译安装完成,此时电路板上就支持scons工具了。接下来就要利用scons编译FCEUX模拟器:cd到fceux所在目录执行:scons等待20分钟或更长(在电路板上编译程序感觉就是慢),编译完成后在scons所在目录/src/目录内会生成可执行文件fceux,就是我们要的模拟器。将此文件拷贝至 /usr/bin下,就可以在任意目录下执行命令打开游戏,比如:fceux -fullscreen 1 -xres 320 -yres 240 -bpp 32 -sound 1 --volume 250 Snow\ Bros.nes具体打印:
具体执行效果:运行的是雪人兄弟
此处介绍的流程为我已经完成SDL库文件的编译的基础上,话说最坎坷的过程就是编译这个SDL,嵌套了好几个工具的编译:SDL(Simple DirectMedia Layer)是一个自由的跨平台的多媒体开发包,适用于 游戏、游戏SDK、演示软件、模拟器、MPEG播放器和其他应用软件。
基于SDL库目前已提供丰富的函数库,非常方便开发者使用。常用的如下:
1、SDL_Image:提供显示多种格式的图像显示接口,它支持bmp,png,jpeg,gif,tiff等.
2、SDL_Draw:提供画点线圆等几何图形的接口.
3、SDL_ttf:提供显示TTF文字的接口.
4、SDL_mixer:提供播放各种声音文件的接口.
5、SDL_net:提供网络通信的接口.
1、SDL的移植1.1配置./c./configure --prefix=/home/anananjjj/sdl_arm --disable-pulseaudio--disable-video-nanox \-disable-video-qtopia --disable-static --enable-shared--disable-video-photon --disable-video-ggi \--disable-video-svga--disable-video-aalib --disable-video-dummy --disable-video-dga --disable-arts\--disable-esd --enable-alsa --enable-video-x11 --disable-nasm --enable-joystick--enable-input-tslib \-enable-video-fbcon --host=arm-linux-gnueabihf3
关键参数解释
--enable-input-tslib #支持tslib 触摸屏
--disable-x11 --enable-video-fbcon #关闭X11支持,打开framebuffer LCD输出
--disable-alsa #音频使用oss,如果使用--enable-alsa,必须先编译alsa-libs
--host=arm-linux #使用arm-linux-gcc编译工具.
--prefix=$PWD/../../output/arm_linux #SDL安装目录
--disable-joystick #去掉游戏杠支持
CPPFLAGS,LDFLAGS主要是为 --enable-input-tslib准备。否则在依赖文件里(.libs/libSDL.lai)会采用 dependency_libs=' -lm/usr/local/lib/libts.la -ldl -lpthread'这样在ARM版明显错误。如果对触摸屏输入不敏感,可以直接 --disable-input-tslib
而-liconv因为在扫描目录也有libiconv,这样自动加入依赖。以免编译其它SDL依赖库出错。
我自己碰到的错误具体过程:
编译fau的时候报错:X11
所以要重新编译SDL:enable-video-x11
这里编译报了错误,提示没有joystick的定义,所以必须加enable-joystick
下载libX11进行编译:
./configure --prefix=/home/anananjjj/libX11_install --host=arm-linux-gnueabihf
make
file:///C:/Users/ANANAN~1/AppData/Local/Temp/msohtmlclip1/01/clip_image004.jpg错误: X11/Xtrans/Xtrans.h:No such file or directory
./configure --prefix=/home/anananjjj/libX11-1.5.0 --host=arm-linux-gnueabihf
make
make install
然后,继续编译libX11即可解决此问题。
又出现错误:
file:///C:/Users/ANANAN~1/AppData/Local/Temp/msohtmlclip1/01/clip_image006.jpg
X11/extensions/XKBproto.h:No such file or directory
./configure --prefix=/home/anananjjj/libX11-1.5.0 --host=arm-linux-gnueabihf
make
make install
然后,继续编译libX11即可解决此问题。
file:///C:/Users/ANANAN~1/AppData/Local/Temp/msohtmlclip1/01/clip_image008.jpg
3.6 X11/extensions/XI.h:No such file or directory
./configure --prefix=/home/anananjjj/libX11-1.5.0 --host=arm-linux-gnueabihf
make
make install
然后,继续编译libX11即可解决此问题。
file:///C:/Users/ANANAN~1/AppData/Local/Temp/msohtmlclip1/01/clip_image010.jpg
出现错误: xcb/xcb.h:No such file or directory
file:///C:/Users/ANANAN~1/AppData/Local/Temp/msohtmlclip1/01/clip_image012.jpg
No package 'xcb-proto' found
然后,继续配置libxcb即可解决此问题。但是编译时还提示错误:
先执行:
./configure--prefix=/home/anananjjj/libxcb-1.9.1 --host=arm-linux-gnueabihf
make
make install
exportPKG_CONFIG_PATH=/home/anananjjj/libxcb-1.9.1/lib/pkgconfig
./configure--prefix=/home/anananjjj/libX11-1.5.0 --host=arm-linux-gnueabihf
make
make install
file:///C:/Users/ANANAN~1/AppData/Local/Temp/msohtmlclip1/01/clip_image014.jpg
接着出现错误: X11/Xauth.h: No suchfile or directory
完成libXau的安装后,再编译libxcb,此时出现链接错误。
./configure--prefix=/home/anananjjj/libxcb-1.9.1 --host=arm-linux-gnueabihf
make
make install
export LD_LIBRARY_PATH=/home/anananjjj/libxcb-1.9.1/lib
export PKG_CONFIG_PATH=/home/anananjjj/libxcb-1.9.1/lib/pkgconfig
export C_INCLUDE_PATH=/home/anananjjj/libxcb-1.9.1/include
然后,重新编译,libX11!
file:///C:/Users/ANANAN~1/AppData/Local/Temp/msohtmlclip1/01/clip_image016.jpg
export LD_LIBRARY_PATH=/home/anananjjj/libX11-1.5.0/lib
export PKG_CONFIG_PATH=/home/anananjjj/libX11-1.5.0/lib/pkgconfig
export C_INCLUDE_PATH=/home/anananjjj/libX11-1.5.0/include
然后将上面的插件库libXau和libxcb重新编译一遍,以保证库文件的全套,否则编译模拟器还是会报错!!,路径为:
./configure --prefix=/home/anananjjj/libX11_install --host=arm-linux-gnueabihf
make
make install
好了,至此,在目录下:/home/anananjjj/libX11_install得到了完整的libX11库文件,这样libX11的交叉编译彻底完成,考入usr的各个目录下可进行工程的编译了!!
然后继续编译SDL并安装即可: make
make install
至此,将SDL及其部分扩展库安装到/home/horo/arm/software/sdl_arm目录下,将目录下的所有文件拷贝到开发板中。
另外,编译完fceux后,运行还需要注意:交叉编译了SDL后将其动态库放入ARM板中,在运行测试程序员时出现:“No video mode large enough for XXXXX” 错误。
解决办法:
1.首先产看“/etc/fb.modes”文件,看其内容是否为空
如果为空,运行命令#:fbset
将会输出类似于以下的内容:
mode"800x480"
geometry 800 480 800 480 16
timings 0 0 0 0 0 0 0
rgba 5/11,6/5,5/0,0/0
endmode
将这段信息粘贴在/etc/fb.modes中,然后重新运行程序。
Fceux报错,说找不到cal.txt文件,在源代码中搜索,找到了beagleboy.cpp中对cal.txt的描述,需要将cal.txt文件放在home/root目录下!
这个cal.txt文件实际上ADC遥杆的校准值,以保证ADC遥杆能够正常使用!
调整系统声音可使用alsamixer工具
添加板载按键和遥杆的支持:
对于生成的“cal.txt”的方法,是calibrate.c测试文件生成的可执行文件生成的,具体测试程序为:
#include
#include
#include
#include
#include
#include
#include
int fd_x = -1;
int fd_y = -1;
int fd_a = -1;
int fd_b = -1;
int a = 0;
int b = 0;
int raw_joy_x = -1;
int raw_joy_y = -1;
// compensated values
int joy_x = -1;
int joy_y = -1;
// calibration values
int min_x = 0, max_x = 1024;
int min_y = 0, max_y = 1024;
int center_x = 512, center_y = 512;
void get_data() {
char buf_x[100];
char buf_y[100];
char buf_a[100];
char buf_b[100];
memset(buf_x, 0, 100);
memset(buf_y, 0, 100);
memset(buf_a, 0, 100);
memset(buf_b, 0, 100);
int ret_x = read(fd_x,buf_x, sizeof(buf_x));
int ret_y = read(fd_y,buf_y, sizeof(buf_y));
int ret_a = read(fd_a,buf_a, sizeof(buf_a));
int ret_b = read(fd_b,buf_b, sizeof(buf_b));
if ((ret_x != -1)&&
(ret_y != -1) &&
(ret_a != -1) &&
(ret_b != -1)) {
buf_x[ret_x-1]= '\0';
buf_y[ret_y-1]= '\0';
buf_a[ret_a-1]= '\0';
buf_b[ret_b-1]= '\0';
raw_joy_x =atoi(buf_x);
raw_joy_y =atoi(buf_y);
a =!atoi(buf_a);
b =!atoi(buf_b);
//compensate using calibration data
joy_x =(raw_joy_x - center_x) * 1000;
joy_y =(raw_joy_y - center_y) * 1000;
if (joy_x> 0) joy_x /= (max_x - center_x);
else joy_x /= (center_x - min_x);
if (joy_y> 0) joy_y /= (max_y - center_y);
else joy_y /= (center_y - min_y);
lseek(fd_x,0,0);
lseek(fd_y,0,0);
lseek(fd_a,0,0);
lseek(fd_b,0,0);
}
}
void cal() {
FILE *file;
file =fopen("cal.txt", "w");
if (!file)printf("Failed to open cal.txt\n");
get_data();
max_x = raw_joy_x;
max_y = raw_joy_y;
min_x = raw_joy_x;
min_y = raw_joy_y;
printf("Move allaround edge. Press A to start. Press B when finished.\n");
while (!a) {get_data(); }
while (!b) {
get_data();
if(raw_joy_x > max_x) {max_x = raw_joy_x; printf("X: %d %d Y: %d%d\n", min_x, max_x, min_y, max_y);}
if(raw_joy_y > max_y) {max_y = raw_joy_y; printf("X: %d %d Y: %d%d\n", min_x, max_x, min_y, max_y);}
if(raw_joy_x < min_x) {min_x = raw_joy_x; printf("X: %d %d Y: %d%d\n", min_x, max_x, min_y, max_y);}
if(raw_joy_y < min_y) {min_y = raw_joy_y; printf("X: %d %d Y: %d%d\n", min_x, max_x, min_y, max_y);}
}
printf("Let go ofjoystick. Press A to start. Press B with finished.\n");
while (!a) {get_data(); }
while (!b) {
get_data();
center_x =raw_joy_x;
center_y =raw_joy_y;
printf("%d%d\n", center_x, center_y);
}
printf("Results.Press A to continue..\n");
printf("Min X:%d\tCenter X: %d\tMax X: %d\n", min_x, center_x, max_x);
printf("Min Y:%d\tCenter Y: %d\tMax Y: %d\n", min_y, center_y, max_y);
printf("Range X:%d\tRange Y: %d\n", (max_x-min_y), (max_y-min_y));
while (!a) {get_data(); }
fprintf(file,"%d,%d,%d,%d,%d,%d\n", min_x, center_x, max_x, min_y, center_y,max_y);
fclose(file);
}
void load_cal() {
FILE *file;
file =fopen("cal.txt", "r");
if (!file)printf("Failed to open cal.txt\n");
fscanf(file,"%i,%i,%i,%i,%i,%i", &min_x, ¢er_x, &max_x,&min_y, ¢er_y, &max_y);
fclose(file);
}
int main() {
fd_x =open("/sys/bus/iio/devices/iio:device0/in_voltage4_raw", O_RDONLY);
fd_y =open("/sys/bus/iio/devices/iio:device0/in_voltage5_raw", O_RDONLY);
fd_a =open("/sys/class/gpio/gpio65/value", O_RDONLY);
fd_b =open("/sys/class/gpio/gpio112/value", O_RDONLY);
cal();
//load_cal();
while (1) {
get_data();
printf("%6d%6d %6d %6d\n", joy_x, joy_y, a, b);
}
close(fd_x);
close(fd_y);
close(fd_a);
close(fd_b);
return 0;
}
标黄部分为修改的地方,前提为:要将按键在设备数中的配置注释掉,否则系统无法在/sys/class/gpio/目录下加载GPIO65和GPIO112;
具体:echo “65”> export
echo “112” > export
由于设备树中添加了adc的配置,所以不用执行ADC的初始化
执行
gcc calibrate.c -o calibrate
./calibrate
2、
OSMOSE(支持.sms ): https://github.com/bear24rw/gamingcape_osmoseosmose安装比较直接,在文件目录下执行make,就会生成可执行文件osmose。可将工程放入beaglebone板的文件系统中,开始make,
由于已经安装SDL,所以编译会很容易。这里不再赘述:
将
osmose拷贝至 /usr/bin下,执行
osmose -scale2x Aladdin\ \(UE\)\ \[\!\].sms
运行的是波斯王子:
3、接下来是我最喜欢的模拟器:
Visual Boy Advance(版本1.7.2)
最好的GBA模拟器,加入了类似 Winkawaks 记录按键顺序和CPU 态的录像功能,相比 AVI 容量可是很小的~添加了 rewind 支持~修正了 DMA hack 为 Bios 调用 0x2a 添加了简易模拟,添加了增强记忆检测功能.
同样也需要SDL库的支持,下载源代码后可直接编译:
首先配置:
./configure --enable-sdl --enable-c-core --disable-profiling--disable-dev --disable-sdltest --target=arm --build=arm --host=arm--prefix=/home/vba CC=arm-linux-gnueabihf-gcc CXX=arm-linux-gnueabihf-g++
然后执行make
将src/sdl/VisualBoyAdvance拷贝至 /usr/bin下,执行
VisualBoyAdvance rom/XXX.gba
这里我测试的是瓦里奥世界,画面和音质很不错:
编译VisualBoyAdvance 碰到一个很奇葩的问题,就是最开始显示的画面一直是反色的,还以为是版本的问题,结果编译了VisualBoyAdvance 1.8.0后还是这样,无奈去查代码,最后找到在SDL.cpp中代码:
surface =SDL_SetVideoMode(destWidth, destHeight, 16,
SDL_ANYFORMAT|SDL_HWSURFACE|SDL_DOUBLEBUF|
(fullscreen ?SDL_FULLSCREEN : 0));
if(surface == NULL) {
systemMessage(0,"Failed to set video mode");
SDL_Quit();
exit(-1);
}
systemRedShift =sdlCalculateShift(surface->format->Rmask);
systemGreenShift =sdlCalculateShift(surface->format->Gmask);
systemBlueShift =sdlCalculateShift(surface->format->Bmask);
systemColorDepth =surface->format->BitsPerPixel;
if(systemColorDepth == 15)
systemColorDepth = 16;
if(yuv) {
Init_Overlay(surface,yuvType);
systemColorDepth = 32;
systemRedShift = 11;
systemGreenShift = 5;
systemBlueShift = 0;
}
if(systemColorDepth != 16&& systemColorDepth != 24 &&
systemColorDepth != 32) {
fprintf(stderr,"Unsupported color depth '%d'.\nOnly 16, 24 and 32bit color depths are supported\n", systemColorDepth);
exit(-1);
}
#ifndef C_CORE
sdlMakeStretcher(srcWidth);
#else
switch(systemColorDepth) {
case 16:
sdlStretcher =sdlStretcher16[sizeOption];
break;
case 24:
sdlStretcher =sdlStretcher24[sizeOption];
break;
case 32:
sdlStretcher =sdlStretcher32[sizeOption];
break;
default:
fprintf(stderr,"Unsupported resolution: %d\n", systemColorDepth);
exit(-1);
}
#endif
fprintf(stderr,"Colordepth: %d\n", systemColorDepth);
fprintf(stderr,"Rmask: %d\n",surface->format->Rmask);
fprintf(stderr,"Gmask: %d\n",surface->format->Gmask);
fprintf(stderr,"Bmask: %d\n",surface->format->Bmask);
fprintf(stderr,"Rmask width: %d\n",sdlCalculateMaskWidth(surface->format->Rmask));
fprintf(stderr,"Gmask width: %d\n",sdlCalculateMaskWidth(surface->format->Gmask));
fprintf(stderr,"Bmask width: %d\n",sdlCalculateMaskWidth(surface->format->Bmask));
if(systemColorDepth == 16) {
if(sdlCalculateMaskWidth(surface->format->Gmask) == 6) {
Init_2xSaI(565);
RGB_LOW_BITS_MASK =0x821;
} else {
Init_2xSaI(555);
RGB_LOW_BITS_MASK =0x421;
}
打印出RGB的值发现:原来默认是的RGB555模式!
运行fbset发现我的是RGB565模式!
这可不行,所以定义了:
surface->format->Rmask=RGB565_MASK_RED;
surface->format->Gmask=RGB565_MASK_GREEN;
surface->format->Bmask=RGB565_MASK_BLUE;
强制转换了一下,重新编译后,画质果然完美了!!声音也是完美!!
具体:
RGB555
RGB555是另一种16位的RGB格式,RGB分量都用5位表示(剩下的1位不用)。使用一个字读出一个像素后,这个字的各个位意义如下:
高字节 低字节
X R R R R R G G G G G B B B B B (X表示不用,可以忽略)
可以组合使用屏蔽字和移位操作来得到RGB各分量的值:
#define RGB555_MASK_RED 0x7C00
#define RGB555_MASK_GREEN 0x03E0
#define RGB555_MASK_BLUE 0x001F
R = (wPixel & RGB555_MASK_RED) >> 10; // 取值范围0-31
G = (wPixel & RGB555_MASK_GREEN) >> 5; // 取值范围0-31
B = wPixel & RGB555_MASK_BLUE; // 取值范围0-31
RGB565
RGB565使用16位表示一个像素,这16位中的5位用于R,6位用于G,5位用于B。程序中通常使用一个字(WORD,一个字等于两个字节)来操作一个像素。当读出一个像素后,这个字的各个位意义如下:
高字节 低字节
R R R R R G G G G G G B B B B B
可以组合使用屏蔽字和移位操作来得到RGB各分量的值:
#define RGB565_MASK_RED 0xF800
#define RGB565_MASK_GREEN 0x07E0
#define RGB565_MASK_BLUE 0x001F
R = (wPixel & RGB565_MASK_RED) >> 11; // 取值范围0-31
G = (wPixel & RGB565_MASK_GREEN) >> 5; // 取值范围0-63
B = wPixel & RGB565_MASK_BLUE; // 取值范围0-31
#define RGB(r,g,b) (unsigned int)( (r|0x08 <<11) | (g|0x08 << 6) | b|0x08 )
#define RGB(r,g,b) (unsigned int)( (r|0x08 <<10) | (g|0x08 << 5) | b|0x08 )
该代码可以解决24位与16位相互转换的问题。
OK!至此我几年前的一个未完成的东西算是有个比较好的收尾!!
此内容由EEWORLD论坛网友anananjjj原创,如需转载或用于商业用途需征得作者同意并注明出处