这是在Hackster.io上发布的一个项目,基于STM32F4 Discovery板制作的游戏机。采用编写游戏的方式来学习嵌入式系统是比较好的方式,寓教于乐,培养成就感,并不需要学习完模电、数电就可以编程。
下面是这个项目的硬件部分,主要的器件:
一个STM32F4 Discovery板
一个3.2寸、320*240分辨率、带触摸的TFT LCD显示屏(ILI9341为控制器)
一个2轴游戏手柄
单通道2.5W D类音频放大器
一只4ohm,3W的喇叭
两个按键
其它电阻、二极管若干
原理图:
关于游戏手柄及按键的功能定义参见原文 - 点击左下角的“阅读原文”即可到达,在这里不再赘述。
下面是板子的正面和背面器件布局示意图:
下图为软件模块的构成及调用关系
下图为数据流:
游戏手柄在两个方向的变化通过其内部两个独立的可变电阻来实现,电阻的改变以变化的电压送到控制器的12位精度的ADC,变换后的数据以40Hz的频度被控制器的游戏引擎读取。
下面是游戏的流程图:
ISR的程序:
void TIM6_DAC_IRQHandler (void)
{
TIM_intrpt_handler(TIM6);
frameUpdate = SET;
}
前景:
int main (void)
{
RTE_init();
RTE_display_start_screen();
while(SHOOT_BUTTON_READ);
while(1){
RTE_display_black_background();
RTE_create_player_spaceship(&PlayerSpaceship);
RTE_draw_player_spaceship(&PlayerSpaceship);
RTE_create_asteroid(&AsteroidVect,Asteroid,numOfAsteroidInWave[currentWa ve],&PlayerSpaceship);
RTE_draw_asteroid(&AsteroidVect);
RNG_deinit();
RTE_start_update_frame();
while(1){
if(frameUpdate == SET){
RTE_display_score();
RTE_update_player_spaceship(&PlayerSpaceship);
RTE_draw_player_spaceship(&PlayerSpaceship);
RTE_create_rocket(&RocketVect,Rocket,&PlayerSpaceship);
RTE_update_rocket(&RocketVect,&AsteroidVect);
RTE_draw_rocket(&RocketVect);
RTE_update_asteroid(&AsteroidVect,&PlayerSpaceship);
RTE_draw_asteroid(&AsteroidVect);
if(PlayerSpaceship.Object_Property.aliveFlag == RTE_ALIVE_FALSE){
PROTOBOARD_GREEN_LED_ON;
RTE_display_game_over_screen();
while(SHOOT_BUTTON_READ);
RTE_reset_game();
PROTOBOARD_GREEN_LED_OFF;
break;
}
if(AsteroidVect.total == 0){
TIM_ctr(TIM6,STOP);
currentWave++;
RNG_init();
RTE_create_asteroid(&AsteroidVect,Asteroid,numOfAsteroidInWave[currentWave],&PlayerSpaceship);
TIM_ctr(TIM6,START);
}
frameUpdate = CLEAR;
}
}
}
}
第二个周期性的中断产生DAC需要的数据,以产生相应的音频效果:
void TIM7_IRQHandler (void)
{
TIM_intrpt_handler(TIM7);
DAC_write(&DACxHandle,*(soundPtrGlobal++));
if(soundPtrGlobal == soundEnd){
speaker_stop_sound();
}
}
创建特殊的空间维度:
/***********************************************************************
Private function: Wrap coordinate
***********************************************************************/
void RTE_wrap_cordinate (int16_t *xPtr, int16_t *yPtr)
{
if (*xPtr < 0){
*xPtr += ILI9341_config.width;
}
if (*xPtr >= ILI9341_config.width){
*xPtr -= ILI9341_config.width;
}
if (*yPtr < 0){
*yPtr += ILI9341_config.height;
}
if (*yPtr >= ILI9341_config.height){
*yPtr -= ILI9341_config.height;
}
}
下面的示意 - 右边慢慢消失,出现在左侧
下面的代码就是在左侧重画出图像中右侧消失掉的部分:
/***********************************************************************
External function: Overwrite draw pixel function in ILI9341 driver library (in order to draw pixels going off screen)
***********************************************************************/
void ILI9341_draw_pixel (int16_t x, int16_t y, uint16_t color)
{
RTE_wrap_cordinate(&x,&y);
ILI9341_set_active_area(x,x,y,y);
ILI9341_send_command(ILI9341_MEM_WRITE);
ILI9341_send_parameter_16_bits(color);
}
碰撞检测:
/***********************************************************************
Private function: Detect collision between 2 object using AABB algorithm
***********************************************************************/
uint8_t RTE_collision_detect (Space_Object_t *Object1Ptr, Space_Object_t *Object2Ptr)
{
int16_t Obj1BottomRight_X = Object1Ptr->Object_Property.x + Object1Ptr->Object_Image.imageWidth;
int16_t Obj1BottomRight_Y = Object1Ptr->Object_Property.y + Object1Ptr->Object_Image.imageHeight;
int16_t Obj2BottomRight_X = Object2Ptr->Object_Property.x + Object2Ptr->Obje ct_Image.imageWidth;
int16_t Obj2BottomRight_Y = Object2Ptr->Object_Property.y + Object2Ptr->Object_Image.imageHeight;
if (Object1Ptr->Object_Property.x < Obj2BottomRight_X
&& Object2Ptr->Object_Property.x < Obj1BottomRight_X
&& Object1Ptr->Object_Property.y < Obj2BottomRight_Y
&& Object2Ptr->Object_Property.y < Obj1BottomRight_Y){
return RTE_COLLISION_TRUE;
}
return RTE_COLLISION_FALSE;
}
要了解更多设计细节请认真阅读原文。