RISC-V MCU中文社区

【分享】 RV-STAR开发板移植Chrome的离线游戏小恐龙

发表于 GD32VF103 MCU 2021-04-08 15:40:12
3
10778
17

RV-STAR开发板上正好有一个按钮,不来做小游戏可惜了,就买了OLED屏幕,改了网上大佬的游戏代码,移植了Chrome的离线小游戏Dino小恐龙。



我买的OLED是7脚的,支持I2C和SPI协议,通过焊接电阻来修改协议,我没修改用的是默认的SPI协议。接线如下图:


本来GD32VF103支持硬件SPI的,但是我一直成功不了,就用软件SPI(用普通GPIO模拟SPI协议传输信号)了。

OLED的厂商会提供相应的驱动,网上随便就找得到,不只包括OLED初始化代码(但不同厂商的初始化代码好像不一样),还有 软件SPI通信的实现、OLED图形GUI库等等。开发起来还是很方便的。

游戏代码是修改自网上大佬 cr4fun 的 代码,我把代码从MicroPython修改为了C语言,基本游戏逻辑没太大改动。


感谢芯来科技公司提供的RISC-V课程和体验机会,RV-STAR开发板有板载调试器,调试起来很方便,现在免驱动并且有不错的IDE,建立工程下载代码很方便, GD32VF103的库函数看起来也很丰富了,只是网上的资料、例程还不算多,学习起来有点迷茫。

希望 芯来科技和国产芯片发展得越来越好!


代码如下。

入口:

#include "gd32vf103.h"
#include "gd32vf103v_rvstar.h"
#include "game.h"
#include "oled.h"
#include "gui.h"

/*!
    \brief      main function
    \param[in]  none
    \param[out] none
    \retval     none
*/

int main(void)
{
	gd_rvstar_key_init(KEY_WAKEUP,KEY_MODE_EXTI);
	__enable_irq();
 	OLED_Init();				//初始化OLED
	OLED_Clear(1);             //清屏(全黑)
	while(1)
	{
		game_loop();
	}
}

OLED初始化:

//-----------------Pin Def----------------
#define OLED_PORT GPIOA
#define OLED_CS //empty
#define OLED_DC GPIO_PIN_8
#define OLED_RST GPIO_PIN_6
#define OLED_SCK GPIO_PIN_5
#define OLED_MOSI GPIO_PIN_7
//-----------------OLED Reg Clr/Set function----------------
#define OLED_CS_Clr() //empty
#define OLED_CS_Set() //empty
#ifdef SPI_SW
#define OLED_MOSI_Clr() gpio_bit_reset(OLED_PORT, OLED_MOSI)
#define OLED_MOSI_Set() gpio_bit_set(OLED_PORT, OLED_MOSI)
#define OLED_CLK_Clr() gpio_bit_reset(OLED_PORT, OLED_SCK)
#define OLED_CLK_Set() gpio_bit_set(OLED_PORT, OLED_SCK)
#define OLED_DC_Clr() gpio_bit_reset(OLED_PORT, OLED_DC)
#define OLED_DC_Set() gpio_bit_set(OLED_PORT, OLED_DC)

#define OLED_RST_Clr() gpio_bit_reset(OLED_PORT, OLED_RST)
#define OLED_RST_Set() gpio_bit_set(OLED_PORT, OLED_RST)
void OLED_Init_GPIO(void)
{

    // 	RCU config
    rcu_periph_clock_enable(RCU_GPIOA);

    //  GPIO config
    /* SPI_SW GPIO config:SCK/PA5->D0, MOSI/PA7->D1 */
    gpio_init(GPIOA, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, OLED_SCK | OLED_MOSI);
    /* GPIO config:PA6->RES, PA8->DC */
    gpio_init(GPIOA, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, OLED_DC | OLED_RST);
    /* GND->CS */
}
void SPI_SW_WriteByte(uint8_t Data)
{
    uint8_t i = 0;
    for (i = 8; i > 0; i--) {
        if (Data & 0x80) {
            OLED_MOSI_Set(); //写数据1
        } else {
            OLED_MOSI_Clr(); //写数据0
        }
        OLED_CLK_Clr(); //将时钟拉低拉高
        OLED_CLK_Set(); //发送1bit数据
        Data <<= 1;
    }
}

void OLED_WR_Byte(uint8_t dat, uint8_t cmd)
{
    if (cmd) {
        OLED_DC_Set();
    } else {
        OLED_DC_Clr();
    }
    OLED_CS_Clr();
    SPI_SW_WriteByte(dat);
    OLED_CS_Set();
}

void OLED_Init(void)
{

    OLED_Init_GPIO(); //初始化GPIO
    delay_1ms(200);
    OLED_Reset(); //复位OLED
    /**************初始化SSD1306*****************/
    OLED_WR_Byte(0xAE, OLED_CMD); //--turn off oled panel
    OLED_WR_Byte(0x00, OLED_CMD); //---set low column address
    OLED_WR_Byte(0x10, OLED_CMD); //---set high column address
    OLED_WR_Byte(0x40, OLED_CMD); //--set start line address  Set Mapping RAM Display Start Line (0x00~0x3F)
    OLED_WR_Byte(0x81, OLED_CMD); //--set contrast control register
    OLED_WR_Byte(0xCF, OLED_CMD); // Set SEG Output Current Brightness
    OLED_WR_Byte(0xA1, OLED_CMD); //--Set SEG/Column Mapping     0xa0左右反置 0xa1正常
    OLED_WR_Byte(0xC8, OLED_CMD); //Set COM/Row Scan Direction   0xc0上下反置 0xc8正常
    OLED_WR_Byte(0xA6, OLED_CMD); //--set normal display
    OLED_WR_Byte(0xA8, OLED_CMD); //--set multiplex ratio(1 to 64)
    OLED_WR_Byte(0x3f, OLED_CMD); //--1/64 duty
    OLED_WR_Byte(0xD3, OLED_CMD); //-set display offset	Shift Mapping RAM Counter (0x00~0x3F)
    OLED_WR_Byte(0x00, OLED_CMD); //-not offset
    OLED_WR_Byte(0xd5, OLED_CMD); //--set display clock divide ratio/oscillator frequency
    OLED_WR_Byte(0x80, OLED_CMD); //--set divide ratio, Set Clock as 100 Frames/Sec
    OLED_WR_Byte(0xD9, OLED_CMD); //--set pre-charge period
    OLED_WR_Byte(0xF1, OLED_CMD); //Set Pre-Charge as 15 Clocks & Discharge as 1 Clock
    OLED_WR_Byte(0xDA, OLED_CMD); //--set com pins hardware configuration
    OLED_WR_Byte(0x12, OLED_CMD);

    OLED_WR_Byte(0xDB, OLED_CMD); //--set vcomh
    OLED_WR_Byte(0x40, OLED_CMD); //Set VCOM Deselect Level
    OLED_WR_Byte(0x20, OLED_CMD); //-Set Page Addressing Mode (0x00/0x01/0x02)
    OLED_WR_Byte(0x02, OLED_CMD); //
    OLED_WR_Byte(0x8D, OLED_CMD); //--set Charge Pump enable/disable
    OLED_WR_Byte(0x14, OLED_CMD); //--set(0x10) disable
    OLED_WR_Byte(0xA4, OLED_CMD); // Disable Entire Display On (0xa4/0xa5)
    OLED_WR_Byte(0xA6, OLED_CMD); // Disable Inverse Display On (0xa6/a7)
    OLED_WR_Byte(0xAF, OLED_CMD);
    OLED_Clear(0);
}


游戏主体(原仓库:https://github.com/OpensourceBooks/chrome_offline_game_on_esp8266

#include "game.h"
#include "gui.h"
#include "oled.h"
#include 
#include 
#define TRUE 1
#define FALSE 0
#define FONTSIZE 8
#define jump_button_click_handler EXTI0_IRQHandler
//===========================BMP===================================
uint8_t OBJ_BMP[] = {

    0x1C, 0x00, 0x1C, 0x00, 0x1D, 0x80, 0x1D, 0x80, 0x1D, 0x80, 0x1D, 0x80, 0x1D, 0x80, 0xDD, 0x80,
    0xDD, 0x80, 0xDF, 0x00, 0xDE, 0x00, 0xDC, 0x00, 0xDC, 0x00, 0xDC, 0x00, 0x7C, 0x00, 0x3C, 0x00,

    0x1C, 0x00, 0x1C, 0x00, 0x1C, 0x00, 0x1C, 0x00
};
uint8_t PLAYER_BMP[] = {
    0x00, 0x1F, 0xE0, 0x00, 0x3F, 0xF0, 0x00, 0x37, 0xF0, 0x00, 0x3F, 0xF0, 0x00, 0x3F, 0xF0, 0x00,

    0x3E, 0x00, 0x80, 0x3F, 0xC0, 0x80, 0x78, 0x00, 0xC1, 0xF8, 0x00, 0xE7, 0xFE, 0x00, 0xFF, 0xFA,
    0x00, 0xFF, 0xF8, 0x00, 0x7F, 0xF8, 0x00, 0x3F, 0xF8, 0x00, 0x1F, 0xF0, 0x00, 0x0F, 0xE0, 0x00,

    0x06, 0x60, 0x00, 0x04, 0x20, 0x00, 0x04, 0x20, 0x00, 0x06, 0x30, 0x00
};
uint8_t PLAYER1_BMP[] = {
    0x00, 0x1F, 0xE0, 0x00, 0x3F, 0xF0, 0x00, 0x37, 0xF0, 0x00, 0x3F, 0xF0, 0x00, 0x3F, 0xF0, 0x00,
    0x3E, 0x00, 0x80, 0x3F, 0xC0, 0x80, 0x78, 0x00, 0xC1, 0xF8, 0x00, 0xE7, 0xFE, 0x00, 0xFF, 0xFA,
    0x00, 0xFF, 0xF8, 0x00, 0x7F, 0xF8, 0x00, 0x3F, 0xF8, 0x00, 0x1F, 0xF0, 0x00, 0x0F, 0xA0, 0x00,

    0x06, 0x20, 0x00, 0x04, 0x30, 0x00, 0x04, 0x00, 0x00, 0x06, 0x00, 0x00
};
uint8_t PLAYER2_BMP[] = {
    0x00, 0x1F, 0xE0, 0x00, 0x3F, 0xF0, 0x00, 0x37, 0xF0, 0x00, 0x3F, 0xF0, 0x00, 0x3F, 0xF0, 0x00,

    0x3E, 0x00, 0x80, 0x3F, 0xC0, 0x80, 0x78, 0x00, 0xC1, 0xF8, 0x00, 0xE7, 0xFE, 0x00, 0xFF, 0xFA,
    0x00, 0xFF, 0xF8, 0x00, 0x7F, 0xF8, 0x00, 0x3F, 0xF8, 0x00, 0x1F, 0xF0, 0x00, 0x0C, 0xE0, 0x00,

    0x04, 0x60, 0x00, 0x06, 0x20, 0x00, 0x00, 0x20, 0x00, 0x00, 0x30, 0x00
};
//===========================ENUM===================================
typedef enum BMPn_Enum {
    PLAYER_BMPN,
    PLAYER_LEG1_BMPN,
    PLAYER_LEG2_BMPN,
    OBJ_BMPN,
} BMPn_Enum;
typedef enum Game_State_Enum {
    READY,
    PLAYING,
    GAMEOVER
} Game_State_Enum;
//===========================Global variables===================================
Game_State_Enum Game_State = PLAYING;
int Status_is_jumpfinish = 0, Status_is_jumping = 0;


uint16_t Gametime = 0, Distance = 0;
char Distance_str[20] = { '\0' };
int X_obj = 130, Y_obj = 44;

int X_player = 10, Y_player = 44, Status_leg_player = 1;
const int X_bg = 0, Y_bg = 53;

//===========================Functions===================================
#define blit_text(str, x, y) (GUI_ShowString((x), (y), (str), (FONTSIZE), 1))
void blit_bmp(BMPn_Enum BMPn, uint8_t x, uint8_t y)
{
    switch (BMPn) {
    case PLAYER_BMPN:
        GUI_DrawBMP_TransparentBg(x, y, 20, 20, PLAYER_BMP, 1);
        break;
    case PLAYER_LEG1_BMPN:
        GUI_DrawBMP_TransparentBg(x, y, 20, 20, PLAYER1_BMP, 1);
        break;
    case PLAYER_LEG2_BMPN:
        GUI_DrawBMP_TransparentBg(x, y, 20, 20, PLAYER2_BMP, 1);
        break;
    case OBJ_BMPN:
        GUI_DrawBMP_TransparentBg(x, y, 10, 20, OBJ_BMP, 1);
        break;
    default:
        break;

    }
}

void game_init(void)
{
    Game_State = READY;
    Gametime = 0;
    Distance = 0;
    Status_is_jumping = FALSE;
    Status_is_jumpfinish = TRUE;
    X_obj = 130;
    Y_obj = 44;

    Y_player = 44;
}

void jump_button_click_handler(void)
{
    if (RESET != exti_interrupt_flag_get(WAKEUP_KEY_PIN)) {

        if (RESET == gd_rvstar_key_state_get(KEY_WAKEUP)) {
            if (Game_State == PLAYING) {
                if (Status_is_jumpfinish) {
                    Status_is_jumping = TRUE;
                    Status_is_jumpfinish = FALSE;
                }
            }
            if (Game_State == GAMEOVER) {
                Game_State = READY;
                game_init();
            } else {
                Game_State = PLAYING;

            }
        }
    }
    /* clear EXTI lines pending flag */
    exti_interrupt_flag_clear(WAKEUP_KEY_PIN);
}
void draw_player(void)
{
    static uint8_t player_pic = PLAYER_BMPN;
    if (Status_is_jumping) {
        Y_player -= 3;
        blit_bmp(player_pic, X_player, Y_player);
        if (Y_player < 15)
            Status_is_jumping = FALSE;
    } else {
        player_pic = PLAYER_LEG1_BMPN;
        Y_player += 3;
    }
    if (Y_player >= 43) {
        Y_player = 43;
        Status_is_jumpfinish = TRUE;
    }
    if (Status_leg_player == 1) {
        blit_bmp(PLAYER_LEG1_BMPN, X_player, Y_player);
        Status_leg_player = 2;
    }
    else if (Status_leg_player == 2) {
        blit_bmp(PLAYER_LEG2_BMPN, X_player, Y_player);

        Status_leg_player = 1;
    }
}

void draw_obj(void)
{
    X_obj -= 4;
    blit_bmp(OBJ_BMPN, X_obj, Y_obj);
    if (X_obj <= -10)
        X_obj = 130;
}
void draw_score(void)
{
    itoa(Distance, Distance_str, 10);
    strcat(Distance_str, " km");
    blit_text(Distance_str, 2, 0);
}
void check(void)
{
    if (X_obj - X_player < 15 && Y_obj - Y_player < 15)
        Game_State = GAMEOVER;
}

int game_loop(void)
{
    while (TRUE) {
        GUI_Fill(0, 0, 127, 63, 0); //Black
        if (Game_State == READY) {
            blit_text("> Click to play!", 10, 10);
            blit_text("by cr4fun", 10, 30);
            blit_text("   & Endless", 10, 40);
        } else if (Game_State == PLAYING) {
            Distance += 1;
            Gametime += 1;
            GUI_DrawLine(0, 63, 127, 63, 1); //draw the ground
            draw_score();
            draw_player();
            draw_obj();
            check();
        } else if (Game_State == GAMEOVER) {
            draw_score();
            GUI_ShowString(23, 20, "GAME OVER!", 16, 1);
        }
        OLED_Display();
        delay_1ms(100);
    }
}



喜欢17
用户评论 (3)
  • wmei

    2021-05-23 00:37:25 wmei 1#

    请问大佬用的OLED显示屏型号是什么?我也想做

    随便,有手册知道协议就行,最好上tb上专门卖OLED的厂家那里买,手册齐而且还有现成的图像库

  • 2021-05-23 00:33:32 2#

    请问大佬用的OLED显示屏型号是什么?我也想做

  • 霍宏鹏

    2021-04-11 00:18:31 霍宏鹏 3#

    好家伙 可以的 我也去试试

wmei

wmei 实名认证

懒的都不写签名

积分
问答
粉丝
关注
  • RV-STAR 开发板
  • RISC-V处理器设计系列课程
  • 培养RISC-V大学土壤 共建RISC-V教育生态
RV-STAR 开发板