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);
}
}