自制超酷的USB游戏机

2020-02-04 09:17发布

看看俺最近完成的“大作”----超酷游戏机,制作挺容易的。先在网上下载一个FC模拟器,
随便一种都行,然后把模拟器的按键设置改一下,具体是:上-下-左-右 对应 W-S-A-D,开始、选择、A键、
B键 对应 U-I-O-P,接下来做一块电路板,读取游戏手柄的按键,再通过USB发送到电脑上。电脑里也要有个上位机配合啦,比如按下手柄上的开始键,上位机就触发一个字母“I”的按下事件,达到控制模拟器的目的。
先看下截图吧~~

       
(原文件名:游戏.JPG)

    图中上位机的主界面是一个大手柄,可以设置模拟器路径,这样每次打开我们这个超酷游戏机时,也会自动运行模拟器。插上电路板和手柄后,按下哪个键,屏幕上的手柄图里对应键上的字母会变颜 {MOD},也能控制模拟器玩游戏,太酷啦~~

    下面我要详细讲下这个游戏机的制作过程了,后面有全部的源代码包括上位机和下位机的,还有免费的
技术支持哦~~  赶紧做一个玩玩吧,重温下童年的感觉~
   
    首先,在proteus里画出原理图,如下:

        
(原文件名:原理图.JPG)

    ISIS里没有CH372芯片元件,估计将来也不可能有~~ 自己做个CH372元件吧。还有那个游戏手柄插座也
没有现成的,不过我试了下,可以用串口的DB9插座代替。然后生成飞线图,各个元件的位置仔细地调整下,
最后的飞线图:

        
(原文件名:飞线图.JPG)


然后选单层板自动布线,等了一分钟,终于布完了,我这个笔记本是在废品站一狠心花300块买回来的,
运行跟蜗牛似的,但是舍不得仍,穷学生啊~~  不过玩模拟器游戏一点都不卡,这就很知足了。
    因为是单层板,电路也挺繁琐,所以最后还剩几根飞线,用手动布线连上。OK,布好线的电路图如下:

        
(原文件名:PCB图.JPG)

    接下来要打印到热转印纸上。如果你没有打印机,就要从网上下载一个虚拟打印机,生成一个PDF文件。
然后拿到打印店去。一般是5角钱一张。打印之前一定要让店里老板把打印缩放那项设置为“无”,否则打出来
大小不对。再裁一块合适的覆铜板,先放在腐蚀液里等十秒钟,拿出来擦干净,就不要用手碰啦。
把刚才打好的转印纸盖到覆铜板上对齐了,边上拿胶布粘住,用电熨斗加热压。因为是老古董的电熨斗,温度
也不知道多少度,稍微调高点,别把纸烫焦的温度就行,轻轻地压五分钟。然后自然冷却,慢慢撕下转印纸,
就差不多了。再检查下哪断了,用油性签字笔描通。剩下就是放到小盆里腐蚀了。等铜都化掉了,拿出来
冲干净,用钢丝球把上面的墨擦下去,我们的PCB板就做好啦,这样的:

        
(原文件名:PCB实物.JPG)

    开始焊接了。先焊芯片。用热的烙铁尖沾点松香涂到芯片的焊盘上,再给每个焊盘镀上锡,然后把芯片
放到焊盘上对齐了,用烙铁尖垂直顶住芯片第一个引脚,稍微用力向下压,引脚下面的锡就化了,这时能
感觉到芯片引脚向下动了一下,把烙铁拿开,一个引脚就焊好了。再依次焊上其他引脚。当然还有其他
更好的方法的,大家慢慢摸索吧。做好的电路板如下:

        
(原文件名:实物1.JPG)

        
(原文件名:实物2.jpg)

        
(原文件名:实物3.jpg)

    接下来就是软件部分了。  我要很为难的跟大家说,这个程序还是用我杜撰的那个unitedC编程语言,所以
在Keil里不能直接编译,您可以在我的博客主页myrobot.2ic.org下载相应的编译器去编译这个程序。
编译器支持MCS51、ATMEGA16和凌阳的SPCE061A。其中ATMEGA16的变量用堆栈存放,可以定义重入函数,
库函数中还有个超小的嵌入式操作系统uCORE,不过最近改了编译器,导致uCORE不能用了,在老版本uC IDE
可以用的。以后再改正。对于MCS51和SPCE061A,变量的分配采用覆盖算法,所以函数不可重入,其实
凌阳的单片机有基址寄存器,硬件上可以支持重入的。我还打算花些时间让这个编译器支持ARM。。。呵呵
幼稚的小孩啊~~
这是编译器界面:

        
(原文件名:编译器.JPG)

我后面要给的项目文档压缩包里有HEX文件,您可以直接写到芯片里。下面是完整的源程序:

        
//晶振: 11.0592MHz

#cpu "mcs51"
#include "mcs51_sfr.c"
#include "game_pad.c"

//********************************************
//游戏手柄配置元件
unit game_pad_config
{
        public bit cp = @sfr.P2.0;
        public bit reset = @sfr.P2.1;
        public bit d = @sfr.P2.2;
}
//********************************************
//主控制元件
unit main
{
        //主函数
        void main()
        {
                //发光二极管初始化
                led.init();
               
                //游戏手柄初始化
                game_pad.init();
               
                //CH375初始化
                ch375.init();
                //发送CH375硬件复位命令
                ch375.write_command( ch375.RESET_ALL );
                time.delay_10ms( 10 );
               
                //检查CH372存在
                ch375.write_command( ch375.CHECK_EXIST );
                ch375.write_data( 0xaa );
                time.delay_10ms( 1 );
                //判断模式设置是否成功
                if( ch375.read_data() == 0x55 ) {
                        led.red_flash( 1 );
                }
                else {
                        led.red_flash( 10 );
                }
                time.delay_10ms( 5 );
               
                //设置CH375工作在模式2
                ch375.write_command( ch375.SET_USB_MODE );
                ch375.write_data( 0x02 );
                time.delay_10ms( 1 );
                //判断模式设置是否成功
                if( ch375.read_data() == 0x51 ) {
                        led.red_flash( 1 );
                }
                else {
                        led.red_flash( 10 );
                }
                time.delay_10ms( 50 );
               
                //无限循环
                uint8 i = 0;
                forever {
                        uint8 d = game_pad.read();
                        ch375.write_command( ch375.WRITE_DATA2 );
                        ch375.write_data( 1 );
                        ch375.write_data( d );
                        while( ch375.INT == high ) {}
                        ch375.write_command( ch375.GET_STATUS );
                        uint8 d0 = ch375.read_data();
                        ch375.write_command( ch375.UNLOCK_USB );
                }
        }
}
//********************************************
//CH375元件
unit ch375
{
        public const uint8 GET_IC_VER = 0x01;
        public const uint8 ENTER_SLEEP = 0x03;
        public const uint8 RESET_ALL = 0x05;
        public const uint8 CHECK_EXIST = 0x06;
        public const uint8 CHK_SUSPEND = 0x0b;
        public const uint8 SET_USB_ID = 0x12;
        public const uint8 SET_USB_MODE = 0x15;
        public const uint8 GET_STATUS = 0x22;
        public const uint8 UNLOCK_USB = 0x23;
        public const uint8 READ_DATA_NOT_REL = 0x27;
        public const uint8 READ_DATA_REL = 0x28;
        public const uint8 WRITE_DATA1 = 0x2a;
        public const uint8 WRITE_DATA2 = 0x2b;
       
        //初始化
        public void init()
        {
                WR = high;
                RD = high;
                A0 = low;
                INT = high;
        }
        //写一个命令
        public void write_command( uint8 cmd )
        {
                DATA = cmd;
                A0 = high;
                WR = low;
                WR = high;
        }
        //写一个数据
        public void write_data( uint8 data )
        {
                DATA = data;
                A0 = low;
                WR = low;
                WR = high;
        }
        //读一个数据
        public uint8 read_data()
        {
                A0 = low;
                RD = low;
                uint8 data = DATA;
                RD = high;
                return data;
        }
       
        uint8 DATA = @sfr.P0;
        public bit INT = @sfr.P2.7;
        bit WR = @sfr.P2.6;
        bit RD = @sfr.P2.5;
        bit A0 = @sfr.P2.4;
}
//********************************************
//延时类
unit time
{
        //延时10ms
        public void delay_10ms( uint8 data )
        {
                loop( data ) loop( 11 ) loop( 250 ) {}
        }
}
//********************************************
//LED发光二极管元件
unit led
{
        //初始化
        public void init()
        {
                set_red_LED( false );
        }
        //红灯闪烁
        public void red_flash( uint8 t )
        {
                loop( t ) {
                        set_red_LED( true );
                        time.delay_10ms( 10 );
                        set_red_LED( false );
                        time.delay_10ms( 10 );
                }
        }
        //设置红 {MOD}LED状态
        public void set_red_LED( bool is_light )
        {
                if( is_light ) {
                        RED_LED = low;
                }
                else {
                        RED_LED = high;
                }
        }
        //端口
        bit RED_LED = @sfr.P2.3;
}

    最后是C#上位机编程了。这个就好说了,主程序中根据下位机发送的USB数据模拟Windows键盘按键事件,
就用SendKey函数,先用记事本验证下,打开一个记事本,当按下手柄的“上”键时,记事本的光标位置就多了一个字符“W”。但是不幸的事降临了,模拟器里却没有任何反应,超级玛丽就是不动啊~~  试了一些别的
程序都行,比如可以用手柄控制幻灯片播放下一个等等,唯独模拟器对C#模拟的按键事件没反应。然后
在网上一搜,原来模拟器的键盘输入不是从Windows的标准按键事件里获取的,而是一个底层的什么openGL东东,反正没搞懂。后来在网上转了一圈,下载了一个Winio.dll,照猫画虎地改了几个地方,一运行,嘿嘿,
模拟器有反应了,魂斗罗战士会动了!  不过我现在还对这个Winio.dll感觉迷迷糊糊的,而且每次玩游戏,
360就报错,说有个dll绕过了Windows安全机制,。。。非要点允许才行。   哎,太深奥了,不懂。。
浅尝辄止,不去深究了,马马虎虎过日子。  祝朋友们天天喜乐,没有烦恼,哈哈

所有程序和资料:

点击此处下载 ourdev_600298O5G08M.rar(文件大小:4.32M) (原文件名:游戏机.rar)
友情提示: 此问题已得到解决,问题已经关闭,关闭后问题禁止继续编辑,回答。