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 func
tion 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] = { '�' };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); }}