演示程序之打游戏 -- 慕司板IAP15

上位机和协议制定我的大学舍友(他的微博:http://weibo.com/lesshst?

topnav=1&wvr=5&topsug=1)毕业前百忙之中使用Python花了一个下午完毕的,先对他表示谢意。

演示视频例如以下:
http://v.youku.com/v_show/id_XODExMjY3ODY0.html

以下介绍演示程序的使用步骤:
①把慕司板演示程序下载进“慕司板”中,右边的摇杆拨到上面。

②打开lizhibo目录下的config.ini文件,改动串口号和波特率。

③打开浏览器的魂斗罗游戏界面:http://www.4399.com/flash/59630_2.htm
或者超级玛丽: http://www.3366.com/flash/69976.shtml
j01

慕司板打游戏事实上就是串口虚拟电脑键盘。

④打开http://www.cnblogs.com/longfan/articles/1841136.html
查看上图中WASDJKFH几个按键相应的键值
87 W 上 按键1
65 A 左 按键2
68 D 右 按键3
83 S 下(文中写错了)按键4
74 J 射击 按键6
75 K 跳跃 按键7
70 F 选择 按键5
72H 跳跃 按键10
假设须要双人游戏,再插上一个慕司板。改动config.ini的串口号,波特率。键值(上下左右?>)执行软件就可以。

附上main.c代码:

/**************************************************************************************
作    者:weifengdq(新浪微博:weifengdq)
注意事项:按ALT+F7 配置->Target->Memory Mode->Large:variables in XDATA,假设须要用到仿真,
          Debug中设置好仿真和串口号;
          初始化有液晶背光调节演示。须要把P19的跳线帽插到上面,使LCD背光引脚与P26相连,使
          用PWM1来调节液晶背光亮度;
          使用串口1的1通道发送数据打游戏:在lkb.h中添加了KEYMSG结构体
          mpu6050.c中Accel_x、Accel_y、Accel_z升为全局变量,添加;
          Accel_y  = (float)GetData(ACCEL_YOUT_H)/286.0f;     //读取Y轴加速度
          ADC初始化把AD採样时间改小了
改动日期:2014/10/17
*************************************************************************************/
#include "config0.h"
#include "delay.h"
#include "lkb.h"
#include "timer.h"
#include "uart.h"
#include <stdio.h>                                       
#include "lcd.h"
#include "adc.h"
#include "mpu6050.h"
#include "image0.h"
#include "pca_pwm.h"
#include "hmc5883l.h"
#include "nrf24l01.h"
#include <math.h>
#include "int.h"

unsigned int adc0,adc1,adc2,adc3,adc4;
float li_voltage=0;

KEYMSG left;
KEYMSG right;
KEYMSG up;
KEYMSG down;
KEYMSG kb;

KEYMSG a_jump; //跳跃
KEYMSG a_shoot;//射击
KEYMSG a_left;
KEYMSG a_right;

void Encoder_Task();
void MPU6050_Task();
void HMC5883_Task();
void KEY_Task();
void ADC_Task();

void Encoder_Task()
{
    LCD_Show2Num(0,112,Freq,6);
    printf("%d\n",Cnt_Now); 
}

void MPU6050_Task()
{
    static unsigned int j=0;
    Angle_Calcu();      //倾角计算 将得到以下两个全局变量,使用了网上摘抄的卡尔曼滤波
                        //float Angle;   //终于倾斜角度
                        //float Gyro_y;  //Y轴角速度
    if(Accel_x >=20 && Accel_x<=90) 
    {
        sendMsg(&a_jump, 7, 1);
    }
    else if(Accel_x >=-90 && Accel_x<=-20)  
    {
        sendMsg(&a_shoot, 6, 1);                     
    }
    else 
    {
        if(a_jump.msg == 1)
        {
            sendMsg(&a_jump, 7, 0);
        }
        else if(a_shoot.msg == 1)
        {
            sendMsg(&a_shoot, 6, 0);
        }
    }

    if(Accel_y >=20 && Accel_y<=90) 
    {
        sendMsg(&a_right, 3, 1);
    }
    else if(Accel_y >=-90 && Accel_y<=-20)  
    {
        sendMsg(&a_left, 2, 1);                  
    }
    else 
    {
        if(a_right.msg == 1)
        {
            sendMsg(&a_right, 3, 0);
        }
        else if(a_left.msg == 1)
        {
            sendMsg(&a_left, 2, 0);
        }
    }



    //printf("%.2f,%.2f\n",Angle,Gyro_y);
    j++;
    if(j>=240)
    {
        j=0;
        LCD_Fill(0,140,239,318,Black);
        POINT_COLOR = Red;
        BACK_COLOR=Black;
        LCD_DrawLine(0,229,239,229);
        POINT_COLOR = Green;
    }
    if((int)Angle>-90 && (int)Angle<90 )      //排除初始不稳定杂波
    {
        LCD_DrawPoint(j,229-(int)Angle);
        //通过NRF24L01发送出去
        if(Angle>=0)
        {
            Tx_Buf[2] = '+';
            Tx_Buf[3] =(unsigned char)Angle;    
        }
        else
        {
            Tx_Buf[2] = '-';
            Tx_Buf[3] = (unsigned char)(Angle*(-1));
        }       
        Transmit(Tx_Buf);           //24L01发送缓冲数组
        sta=SPI_Read(READ_REG +  STATUS);   //读24L01的状态寄存器
        if(TX_DS)   //sbit TX_DS =sta^5;数据发送完毕中断。接收到应答信号时此位置1,写1清除中断
        {  
            //Delay_ms(5); 
            SPI_RW_Reg(WRITE_REG + STATUS,sta);  //写状态寄存器,清除中断
        }
        if(MAX_RT)  //假设是发送超时  sbit MAX_RT =sta^4;
        {           //达到最多次重发中断,MAX_RT中断产生则必须清除后系统才干进行通讯,写1清除中断
            //Delay_ms(5);  
            SPI_RW_Reg(WRITE_REG + STATUS,sta);
        }
    }

}

void HMC5883_Task()
{
    //static unsigned int m=0;
    static float Last_HMC5883_Angle=0;
//  float Avg_HMC5883_Angle=0;
//  float Last_Avg_HMC5883_Angle=0;
//  float temp_HMC5883_Angle=0;

    Multiple_Read_HMC5883();                  //角度的变化似乎并非线性的
    LCD_Show2Num(32,80,(int)HMC5883_Angle,3); 
    POINT_COLOR=BACK_COLOR;                    //三角函数使用弧度而不是度!
    LCD_DrawLine(120,100,120+(int)(28.0*cos((Last_HMC5883_Angle-180.0)/57.3)),100+(int)(28.0*sin((Last_HMC5883_Angle-180.0)/57.3)));
    POINT_COLOR=Yellow;
    LCD_DrawLine(120,100,120+(int)(28.0*cos((HMC5883_Angle-180.0)/57.3)),100+(int)(28.0*sin((HMC5883_Angle-180.0)/57.3)));
    Last_HMC5883_Angle=HMC5883_Angle;
//          //平均值滤波
//          m++;
//          temp_HMC5883_Angle+=HMC5883_Angle;  
//          if(m==5)
//          {
//              m=0;
//              Avg_HMC5883_Angle=temp_HMC5883_Angle/5.0;    //与前面四个值相关联
//              //printf("\n\n%.2f\n\n",HMC5883_Angle);   //HMC5883_Angle: float型。0~360
//              LCD_Show2Num(32,80,(int)Avg_HMC5883_Angle,3); 
//              POINT_COLOR=BACK_COLOR;
//              LCD_DrawLine(120,100,120+28*cos((Last_Avg_HMC5883_Angle-180.0)/57.3),100+28*sin((Last_Avg_HMC5883_Angle-180.0)/57.3));
//              POINT_COLOR=Yellow;
//              LCD_DrawLine(120,100,120+28*cos((Avg_HMC5883_Angle-180.0)/57.3),100+28*sin((Avg_HMC5883_Angle-180.0)/57.3));
//              Last_Avg_HMC5883_Angle=Avg_HMC5883_Angle;
//          } 
}

void ADC_Task()
{
    static unsigned int i=0;
    static unsigned int k=0;

    POINT_COLOR= Green;
    i++;
    switch(i)
    {
        case 1: adc0=(int)ADC_GetResult(0);LCD_Show2Num(48,0,adc0,3); break;
        case 2: adc1=(int)ADC_GetResult(1);LCD_Show2Num(48,16,adc1,3); break;
        case 3: adc2=(int)ADC_GetResult(2);LCD_Show2Num(168,0,adc2,3); break;
        case 4: adc3=(int)ADC_GetResult(3);LCD_Show2Num(168,16,adc3,3); break;
        case 5: li_voltage=(float)ADC_GetResult(4)*0.019f; break; //adc4*3.3/256*(6.8+3.3)/6.8=adc4*0.019
        default:
            i=0;
            LCD_Show2Num(96,32,(int)li_voltage,1);              //显示整数部分
            LCD_Show2Num(112,32,((int)(li_voltage*100))%100,2); //显示小数部分
            //printf("%d,%d,%d,%d,%.2f\n",adc0,adc1,adc2,adc3,li_voltage);
     }//switch 结束

    k++;
    //printf("%d,%d,", adc1, adc0);
    if(k%2)
    {
        if(adc1>=0 && adc1<=80) 
        {
            sendMsg(&up, 1, 1);
        }
        else if(adc1>=170 && adc1<=255) 
        {
            sendMsg(&down, 4, 1);                    
        }
        else 
        {
            if(up.msg == 1)
            {
                sendMsg(&up, 1, 0);
            }
            else if(down.msg == 1)
            {
                sendMsg(&down, 4, 0);
            }
        }
    }
    else
    {
        if(adc0>=0 && adc0<=80) 
        {
            sendMsg(&left, 3, 1);
        }
        else if(adc0>=170 && adc0<=255) 
        {
            sendMsg(&right, 2, 1);                   
        }
        else 
        {
            if(left.msg == 1)
            {
                sendMsg(&left, 3, 0);
            }
            else if(right.msg == 1)
            {
                sendMsg(&right, 2, 0);
            }
        }
    }
}

//void KEY_Task()
//{
//  if (key_up==0)
//  {
//      sendMsg(&kb, Key_Num, 1);    //sendMsg()函数里面有printf
//  }
//  else  //if(key_up == 1) 
//  {
//
//      if (kb.msg == 1) ;
//          sendMsg(&kb, kb.key, 0);
//  }
//  //printf("%d\n",Key_Num);
//}

/*能够使用单步仿真一步步查看效果*/
void main()
{   
    unsigned int i;

    Delay_ms(10);   
    Delay_us(10);
    PWM2=0;         //电机制动。能够下拉10k设置引脚为推挽制动

    /*2.2寸、320*240分辨率、SPI接口的LCD初始化,使用STC的硬件SPI,LCD驱动芯片为ILI9340C*/
    LCD_LED=0;      //LCD背光最亮
    SPI_Init();     //IAP15硬件SPI初始化,切换到了SPI的第二通道:SS/P24、MOSI/P23、MISO/P22、SCK/P21
    LCD_Init();     //LCD初始化,厂家提供

    /*LCD休眠演示*/
    LCD_Sleep();    //LCD进入休眠状态
    Delay_ms(100);  
    LCD_ExitSleep();//LCD退出休眠状态
    /*LCD刷屏演示:红橙黄绿青蓝紫 白黑*/
    LCD_Clear(Red); 
    LCD_Clear(Orange);
    LCD_Clear(Yellow);
    LCD_Clear(Green);
    LCD_Clear(Cyan);
    LCD_Clear(Blue);
    LCD_Clear(Purple);
    LCD_Clear(White);
    LCD_Clear(Black);
    /*LCD画线、英文、汉字、数字显示測试*/
    /*液晶屏左上角坐标为(0,0),右下角坐标为(239,319)*/
    POINT_COLOR=Green; //全局变量
    BACK_COLOR=Black;   //前景色相似于在黑板写字的各种颜色的粉笔。黑板的颜色就是背景色    
    LCD_ShowString(0,0,"Hello,world!"); //从(0,0)坐标開始显示字符串“Hello,World!”
    POINT_COLOR=Blue;                   //能够随时更改画笔颜色,显示不同颜色的数字
    LCD_DrawLine(100,0,240,15);         //画一条线段。起始坐标(100,0),终点坐标(240,15)
    LCD_Show2Num(0,16,65535,5);         //从(0,16)坐标開始显示0~65535的数字,5为数字位数,即显示5位数字
    LCD_ShowNum(100,16,987654321,9);    //从(100,16)坐标開始显示unsigned long型数字(0~4294967295),10为数字位数。即10位数字
    POINT_COLOR=Yellow;
    for(i=32;i<320;i+=16)
    {
        LCD_ShowString(0,i,"abcdefghijklmnopqrstuvwxyz!");
    }
    LCD_LED=1;      //LCD背光关闭
    Delay_ms(500);
    LCD_LED=0;      //LCD背光最亮
    Delay_ms(500);
    LCD_Clear(Black);                   //清屏:黑色
    //LCD_Fill(120,120,160,160,Yellow); //在矩形区域填充色块。在函数处按F12能够查看函数原型
    LCD_ShowImage(gImage_musi,80,120,78,80);//显示彩色图像: 慕司LOGO
    Delay_ms(2000);
    LCD_Clear(Black);
    POINT_COLOR=Red;
    LCD_DrawRectangle(0,0,239,60);   //画矩形:对角坐标(左上、右下)为(0,0),(239,60)
    POINT_COLOR=Purple;
    LCD_ShowString(80,2,"慕司");   //显示16*16汉字
    LCD_ShowString(16,22,"为一线微茫憧憬一意孤行,");
    LCD_ShowString(8,42,"这众生芸芸谁不曾如此任性!");
    POINT_COLOR=Cyan;
    LCD_ShowGB3232(80,160,"青春");
    POINT_COLOR=Green;
    LCD_DrawCircle(112,176,48);
    Delay_ms(500);

    PCA_PWM_Init();     //PWM初始化。切换到PWM的第三通道P25/P26/P27,这里仅使用了PWM1(P26,即LCD_LED引脚)
    LCD_LED=0;          //LCD背光最亮
    for(i=255;i<=0;i--) //液晶背光256级亮度调节演示:由亮到暗
    {
        CCAP1H = CCAP1L = i;      //通过调节占空比来调节屏幕亮度
        Delay_ms(20);
    }
    LCD_LED=1;          //LCD背光关闭
    for(i=0;i<255;i++)  //液晶背光256级亮度调节演示:由暗到亮
    {
        CCAP1H = CCAP1L = i;      //通过调节占空比来调节屏幕亮度
        Delay_ms(20);
    }
    Delay_ms(1000);
    LCD_Clear(Black);

    LKB_Init();             //LED Key Buzzer:LED、按键、蜂鸣器 初始化(设置蜂鸣器port为推挽输出)
    Buzzer_Func(500,1000);  //蜂鸣器500Hz@1000ms

    ADC_Init();            //AD初始化:设置P1的0 1 2 3(摇杆)4(锂电池电压)口为AD口。
    POINT_COLOR = Green;       //液晶屏画笔颜色
    BACK_COLOR=Black;      //背景色
    LCD_ShowString(0,0 ,"adc0:     ");
    LCD_ShowString(0,16,"adc1:     ");
    LCD_ShowString(120,0,"adc2:     ");
    LCD_ShowString(120,16,"adc3:     ");
    LCD_ShowString(0,32,"li_voltage:      V");
    LCD_ShowString(104,32,".");

    NRF24L01_Init();
    TX_Mode();           //发送模式
    LCD_ShowString(0,48,"NRF24L01_State: Tx_Mode");

    MPU6050_Init();
    Delay_ms(100);
    POINT_COLOR = Red;
    LCD_DrawLine(0,319,239,319);
    LCD_DrawLine(0,139,239,139);
    LCD_DrawLine(0,229,239,229);

    HMC5883_Init();
    Delay_ms(100);
    POINT_COLOR = Green;
    LCD_DrawCircle(120,100,31);
    LCD_ShowString(0,64,"direction:");

    LCD_ShowString(0,96,"speed:");

    Timer_Init(100);     //每10ms中断一次,參数为百us:100*100us = 10ms

    UART_Init(115200);   //串口初始化。使用了串口1的第一通道,设置波特率115200。用于仿真时能够切换到第二通道P36/P37
    TI=1;
    SBUF=0;              //这一句使用printf有时是必须的

    INT_Init(); 

    while(1)
    {
        if(LED_flag)         //每1s切换一次LED的状态
        {LED_flag=0;LED1 = ~LED1;}

        if(ADC_flag)
        {ADC_flag=0;ADC_Task();}

        if(adc2 >=80 && Encoder_flag)    //使用adc2作开关
        {
            Encoder_flag=0;
            CCAP2H = CCAP2L = (adc3 - 10)*2;    //使用摇杆的ADC3调节电机转速
            Encoder_Task();
        }
        else  if(adc2 <=70)
        {
            if(MPU6050_flag)      //MPU6050的几个函数使用了Delay_us(5)
            {MPU6050_flag=0;MPU6050_Task();}

            if(HMC5883_flag)
            {HMC5883_flag=0;HMC5883_Task();}

            //interrupt.c中把Key_Scan()改成支持连按
            //uart.c中把串口1切换回P30/P31
            if(KEY_flag)
            {
//              KEY_Task();
                if (key_up==0)
                {
                    sendMsg(&kb, Key_Num, 1);    //sendMsg()函数里面有printf
                }
                else  //if(key_up == 1) 
                {
                    KEY_flag=0;
                    if (kb.msg == 1) ;
                        sendMsg(&kb, kb.key, 0);
                }
                //printf("%d\n",Key_Num);
            }
        }
        else
            {;}
    }       
}

interrup.c代码:

#include "config0.h"
#include "lkb.h"

bit LED_flag=0;
bit BEEP_flag=0;
bit KEY_flag=0;
bit ADC_flag=0;
bit MPU6050_flag=0;
bit HMC5883_flag=0;
bit Encoder_flag=0;

bit Rotate_Dir=0;     //用于推断编码器旋转方向
int Cnt_Last;                      //记录上一次的捕获值
int Cnt_Now=500;                       //记录本次的捕获值
unsigned int Freq=0;

//仅作參考。并未使用
void exint0() interrupt 0  using 2     //INT0中断入口
{
    //先推断转向
    if(B_Phase) //假设B相脉冲为正电压。表示正转
    {
        Rotate_Dir=1;
        Cnt_Now++;
        if(Cnt_Now>=1000)   Cnt_Now=0;
        Cnt_Last=Cnt_Now;
    }
    else
    {
        Rotate_Dir=0;
        Cnt_Now--;
        if(Cnt_Now<=0)  Cnt_Now=1000;
    }
}


//外部中断2服务程序
//void exint2() interrupt 10          //INT2中断入口    (下降沿)
//{
//  //先推断转向
//  if(B_Phase) //假设B相脉冲为正电压,表示正转
//  {
//      Rotate_Dir=1;
//      Cnt_Now++;
//      if(Cnt_Now>=1000)   Cnt_Now=0;
//      Cnt_Last=Cnt_Now;
//  }
//  else
//  {
//      Rotate_Dir=0;
//      Cnt_Now--;
//      if(Cnt_Now<=0)  Cnt_Now=1000;
//  }   
//      
////  INT_CLKO &= 0xEF;               //若须要手动清除中断标志,可先关闭中断,此时系统会自己主动清除内部的中断标志
////  INT_CLKO |= 0x10;               //然后再开中断就可以
//}

/*********************************************
* Timer0中断服务程序
*********************************************/
void tm0_isr() interrupt 1 using 1     //10ms中断一次
{
    static unsigned int i=0;    //用于LED
    static unsigned int j=0;    //用于蜂鸣器
    static unsigned int k=0;    //用于MPU6050
//  static unsigned int m=0;    //用于HMC5883L
    static unsigned int n=0;    //用于ADC
    static unsigned int p=0;    //用于编码器

    //使用Aslong的JGA25-371直流减速电机:334线编码器,减速比为 21.3,12V额定电压。额定转速201rpm
    //那么额定转速下10ms输出脉冲数:201*21.3*334/60/100=238.3257个脉冲
    unsigned char ch,cl;
    static unsigned int temp=0;
    static unsigned int temp_1=0;   //上次的值
    cl=TL1; //先读低位(高位变得没那么快)
    ch=TH1;
    temp_1=temp;
    temp=ch*256+cl; //用左移怎么实现? ch<<8+cl
    //if(temp>=temp_1) Freq=(temp-temp_1)/5;            // *200/1000 kHz              //20kHz 每5ms 计100个数
    //else Freq=(65536-temp_1 + temp)/5;
    if(temp>=temp_1) Freq=(temp-temp_1)*100;            //1s的脉冲数,即频率
    else Freq=(65536-temp_1 + temp)*100;

    p++;
    if(p>=1)
    {
        p=0;
        Encoder_flag=1;
    }

    i++;
    if(i>=100)
    {
        i=0;
        LED_flag=1;
    }

    Key_Scan(1,1);  //支持连按;按键消抖时间,10ms
    if(Key_Num)
    {
        //if(Key_Num==10) BEEP_flag=1;  //按键10按下。蜂鸣器鸣响
        KEY_flag=1;
    }   
    if(BEEP_flag)
    {
        j++;
        BEEP=~BEEP;    //10ms翻转一次,50Hz
        if(j>10)    //响100ms
        {
            j=0;
            BEEP_flag=0;
            BEEP=0;        //关闭蜂鸣器
        }
    }

    k++;
    if(k%2) //10ms採样一次
    {
        //k=0;
        MPU6050_flag=1;
    }
    else
        HMC5883_flag=1;

//  m++;
//  if(m>=1)
//  {
//      m=0;
//      HMC5883_flag=1;
//  }

    n++;
    if(n>=1)    //ad: 10ms採样一次
    {
        n=0;
        ADC_flag=1;
    }

}

完整project參见资源汇总帖.

原作于 2014年10月
CSDN发表于2016年4月
weifengdq

posted @ 2017-08-03 08:20  yfceshi  阅读(280)  评论(0编辑  收藏  举报