這是在Hackster.io上發(fā)布的一個項目,基于STM32F4 Discovery板制作的游戲機。采用編寫游戲的方式來學習嵌入式系統(tǒng)是比較好的方式,寓教于樂,培養(yǎng)成就感,并不需要學習完模電、數(shù)電就可以編程。
下面是這個項目的硬件部分,主要的器件:

一個STM32F4 Discovery板
一個3.2寸、320*240分辨率、帶觸摸的TFT LCD顯示屏(ILI9341為控制器)
一個2軸游戲手柄
單通道2.5W D類音頻放大器
一只4ohm,3W的喇叭
兩個按鍵
其它電阻、二極管若干
原理圖:

關于游戲手柄及按鍵的功能定義參見原文 - 點擊左下角的“閱讀原文”即可到達,在這里不再贅述。
下面是板子的正面和背面器件布局示意圖:

下圖為軟件模塊的構成及調(diào)用關系

下圖為數(shù)據(jù)流:

游戲手柄在兩個方向的變化通過其內(nèi)部兩個獨立的可變電阻來實現(xiàn),電阻的改變以變化的電壓送到控制器的12位精度的ADC,變換后的數(shù)據(jù)以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;}}}}
第二個周期性的中斷產(chǎn)生DAC需要的數(shù)據(jù),以產(chǎn)生相應的音頻效果:
void TIM7_IRQHandler (void){TIM_intrpt_handler(TIM7);DAC_write(&DACxHandle,*(soundPtrGlobal++));if(soundPtrGlobal == soundEnd){speaker_stop_sound();}}
創(chuàng)建特殊的空間維度:
/***********************************************************************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;}}
下面的示意 - 右邊慢慢消失,出現(xiàn)在左側

下面的代碼就是在左側重畫出圖像中右側消失掉的部分:
/***********************************************************************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;}
要了解更多設計細節(jié)請認真閱讀原文。