|
本帖最后由 good 于 2025-2-21 14:04 编辑
一.介绍
大家可能都去商场的游戏厅玩过哈,是否对下面的游戏机有一种熟悉感呢?
就是抓零食, 抓玩具等各种游戏机, 它们有的是用LCD12864单色屏做的,有的是用更简单的数码管做的,不管外观咋样,仪器内部的实现原理基本都是差不多的,大概可以分为如下几部分构成:
1.电机,机械臂,导轨等结构
2.摇杆, 按键, 显示系统, 投币器, 光耦定位等硬件
3.游戏状态机, 充值, 厂家后台等软件组件
哈哈,大家都知道现在商场里面的游戏机都是可以调节难易程度的, 一般小白可能不知情哈, 这里我给大家解密一下, 这些调节参数都是在我们的"厂家后台"软件组件中调节的, 比如调节爪子力度大小, 中奖概率, 电机移动速度, 送币概率, 下降绳长等参数,当然了
我们用户一般是无法进入到这个"厂家后台"调试界面的, 一般在游戏机柜内有一个按键, 可以通过长按来触发进入的,
好了现在进入主题, 本项目采用了迪文触摸彩屏来实现零食机, 也算是市面上的一个革新点吧, 然后还有许多硬件模块其实它内部是封装好了的, 它内部处理掉了复杂的处理事项, 只是暴露简单的接口来供我们控制, 比如其中的投币器
二.GUI设计
1.所有图片素材如下:
2.首先游戏机开机后会进入到开机界面,这里其实是在完成各组件模块的自检流程,如果出错了,就会跳到相应的报错界面
3.自检没有问题后,就会进入到正常的主界面,也就是所谓的游戏状态机
在这里会显示用户投币的累计总局数,每玩一次会减少1, 然后还会显示每一次游戏的倒计时,时间一到系统将会自动出爪,然后还会显示投几个币才能玩一局等,
当然了这里的时间,和几币一局都是在后台可以设置的喔
4.然后就是前面我们所说到的"厂家后台"界面,它是通过长按一个按键来进入的,后台系统的界面是比较复杂的,它不仅要给车间工人提供调试接口,还要给游戏厅老板提供参数设定接口
看上图就知道复杂程度了,里面的每一个一级菜单项又细分出来了很多二级菜单项的,比如"C基础设置"
三.代码设计
1.迪文屏和主控的通信代码
void uart2_master_isr() interrupt 4
{
u8 res;
if(RI0)
{
RI0 = 0;
res = SBUF0;
uart2_rx_timeout = UART2_RX_TIMEOUT;
if((uart2_rx_sta&UART2_PACKET_OK)==0)
{
if(step==0)
{
recv_len = 0;
if(res==0x15)
step = 1;
}else if(step==1)
{
date_len = res;
step = 2;
if(date_len>UART2_PACKET_MAX_LEN)
step = 0;
}else if(step==2)
{
if(recv_len==date_len)
{
step = 0;
if(res==0x16)
{
uart2_rx_sta = date_len;
uart2_rx_sta |= UART2_PACKET_OK;
}
}else
uart2_buf[recv_len++] = res;
}
}
}
}
2.电机的驱动代码
void motor_move(MOTOR motor,MOTOR_DIR dir)
{
if(motor==MOTOR_Z)//Z轴上的爪子电机
{
if(dir==MOTOR_DIR_NONE)//停止
{
MOTOR_Z_DISABLE();
}else
{
if((IS_MOTOR_Z_BACKWARD_POS()&&(dir==MOTOR_DIR_BACKWARD))
||(IS_MOTOR_Z_FORWARD_POS()&&(dir==MOTOR_DIR_FORWARD)))
{
MOTOR_Z_DISABLE();
dir = MOTOR_DIR_NONE;
}else
{
MOTOR_Z_ENABLE();
MOTOR_Z_DIR_PIN = (dir==MOTOR_DIR_BACKWARD);
}
}
}else if(motor==MOTOR_X)//X轴移动电机
{
if(dir==MOTOR_DIR_NONE)//停止
{
MOTOR_X_DISABLE();
}else
{
if(IS_MOTOR_X_BACKWARD_POS()&&(dir==MOTOR_DIR_BACKWARD))
{
MOTOR_X_DISABLE();
dir = MOTOR_DIR_NONE;
}else
{
MOTOR_X_ENABLE();
MOTOR_X_DIR_PIN = (dir==MOTOR_DIR_BACKWARD);
}
}
x_last_dir = dir;
}else if(motor==MOTOR_Y)//Y轴移动电机
{
if(dir==MOTOR_DIR_NONE)//停止
{
MOTOR_Y_DISABLE();
}else
{
if((IS_MOTOR_Y_BACKWARD_POS()&&(dir==MOTOR_DIR_BACKWARD))
||(IS_MOTOR_Y_FORWARD_POS()&&(dir==MOTOR_DIR_FORWARD)))
{
MOTOR_Y_DISABLE();
dir = MOTOR_DIR_NONE;
}else
{
MOTOR_Y_ENABLE();
MOTOR_Y_DIR_PIN = (dir==MOTOR_DIR_BACKWARD);
}
}
y_last_dir = dir;
}else if(motor==MOTOR_CLAW)
{
if(dir==MOTOR_DIR_NONE)
{
MOTOR_CLAW_RELEASE();
}else
{
MOTOR_CLAW_HOLD();
}
}
}
3.爪子调力度,速度的代码(采用的是PWM)
const u16 MOTOR_DUTY[MOTOR_TOTAL][MOTOR_SPEED_MAX+1] = {
{45+DUTY_OFFSET,60+DUTY_OFFSET,80+DUTY_OFFSET,110+DUTY_OFFSET,130+DUTY_OFFSET,160+DUTY_OFFSET,200+DUTY_OFFSET,250+DUTY_OFFSET,550+DUTY_OFFSET,TIM_ARR},//X
{45+DUTY_OFFSET,60+DUTY_OFFSET,80+DUTY_OFFSET,110+DUTY_OFFSET,130+DUTY_OFFSET,160+DUTY_OFFSET,200+DUTY_OFFSET,250+DUTY_OFFSET,550+DUTY_OFFSET,TIM_ARR},//Y
{45,60,80,110,130,160,200,250,550,TIM_ARR},//Z
{200,220,240,270,290,320,360,410,550,TIM_ARR}//爪子
};
void motor_set_speed(MOTOR motor,u8 speed)
{
if(speed>MOTOR_SPEED_MAX)
return;
pwm_set_duty((PWM_CH)motor,MOTOR_DUTY[motor][speed]);
}
void motor_set_claw_strength_by_vol(float vol)
{
#define CLAW_DUTY_MIN 200
#define CLAW_DUTY_MAX TIM_ARR
u16 duty;
vol = (float)(vol-QZLDY_MIN)/(QZLDY_MAX-QZLDY_MIN);
if(vol<0)
vol = 0;
else if(vol>1)
vol = 1;
duty = (u16)(vol*(CLAW_DUTY_MAX-CLAW_DUTY_MIN)+0.5f)+CLAW_DUTY_MIN;
pwm_set_duty(PWM_CH_MOTOR_CLAW, duty);
}
4.厂家后台按键长按检测
u8 key_check_long_press(KEY key,u32 time)
{
while(1)
{
if(!(KEY_Scan(1)&key))
return 1;//失败
if(time)
{
sys_delay_ms(1);
time--;
if(time==0)
return 0;//成功
}
}
}
四.视频演示
https://b23.tv/7rEnD5J
GUI参考:
vs代码参考:
|
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有账号?立即注册
x
|