C语言俄罗斯方块时钟
这个小程序是因为在某宝上看见一款点阵屏俄罗斯方块时钟,感觉很有意思就想在自己的stm32f469开发板上抄袭一下,看看效果,大家想玩一玩的可以参考一下,核心代码在下面。
先看一下效果图
当时间改变,每个数字是由小的俄罗斯方块逐渐累加起来的,不是视频看不到效果。
下面是一些const数据用来定义俄罗斯方块基础形状及每个数字下落的方块序列
/* config grid */
#define GRID_SIZE 14
#define GRID_ROW_NUM (RK043FN48H_HEIGHT/GRID_SIZE-6)
#define GRID_COL_NUM (RK043FN48H_WIDTH/GRID_SIZE-1)
/* config number and colon position */
#define NUMBER_COUNT 4
#define FIRST_NUMBER_OFFSET 1
#define SECOND_NUMBER_OFFSET 8
#define THIRD_NUMBER_OFFSET 18
#define FOURTH_NUMBER_OFFSET 25
#define COLON_OFFSET_X 15
#define COLON_OFFSET_Y 5
uint16_t u16GridMap[GRID_ROW_NUM * GRID_COL_NUM];
uint8_t u8ClockNumber[NUMBER_COUNT+1] = {0, 0, 0, 0, 0};
stTetrisClockHandle stTetrisNumHandle[NUMBER_COUNT];
struct tm *p_tm;
const uint32_t u32ColorRgb565Table[] =
{
SCRLET ,
CAMEL ,
SUN_ORANGE ,
LAWN_GREEN ,
FOREST_GREEN ,
MARINE_BLUE ,
CHROME_YELLOW ,
DARK_SLATE_BLUE ,
CAYN_BLUE ,
RUBY ,
MIDIUM_TURQUOISE ,
VERIDIAN ,
LIGHT_LIME ,
INDIAN_RED ,
MAROON ,
MISTY_ROSE ,
LIGHT_CORAL ,
HELIOTROPE ,
};
const stNumSeqBase au8NumZeroTerisSeq[] =
{
{18, 3}, {4, 1}, {9, 3}, {9, -1}, {5, 0}, {5, 4}, {5, 4}, {11, 0}, {0, 0}, {9, 0},
{6, 2}, {19, 4}
};
const stNumSeqBase au8NumOneTerisSeq[] =
{
{3, 4}, {17, 3}, {19, 4}, {17, 3}, {19, 4}
};
const stNumSeqBase au8NumTwoTerisSeq[] =
{
{18, 3}, {14, 0}, {1, 1}, {2, 4}, {18, 3}, {14, 0}, {1, 1}, {2, 4}, {2, 2}, {0, 1}, {0, 0}
};
const stNumSeqBase au8NumThreeTerisSeq[] =
{
{18, 3}, {14, 0}, {1, 1}, {0, 5}, {19, 3}, {14, 0}, {1, 1}, {2, 4}, {1, 1}, {12, 3}, {16, 0}
};
const stNumSeqBase au8NumFourTerisSeq[] =
{
{2, 4}, {2, 4}, {2, 0}, {2, 0}, {18, 3}, {14, 0}, {1, 1}, {2, 4}, {2, 4}
};
const stNumSeqBase au8NumFiveTerisSeq[] =
{
{2, 4, 8}, {2, 2, 8}, {0, 1}, {0, 0}, {1, 1}, {16, 0, 4}, {12, 3}, {2, 4}, {1, 1}, {12, 3},
{16, 0}
};
const stNumSeqBase au8NumSixTerisSeq[] =
{
{2, 4, 8}, {2, 2, 8}, {0, 1}, {0, 0}, {1, 1}, {16, 0}, {12, 3}, {2, 4}, {2, 0}, {1, 1},
{12, 3},{16, 0}
};
const stNumSeqBase au8NumSevenTerisSeq[] =
{
{18, 3}, {14, 0}, {1, 1}, {17, 3}, {0, 5}, {13, 3}, {2, 4}
};
const stNumSeqBase au8NumEightTerisSeq[] =
{
{15, 4}, {1, 0}, {1, 1}, {7, 4}, {7, 0}, {16, 0}, {12, 1}, {9, 3}, {7, 0}, {12, 2}, {0, 5},
{9, -1},{1, 1}
};
const stNumSeqBase au8NumNineTerisSeq[] =
{
{10, 2}, {4, 0}, {5, 4}, {9, -1}, {12, 2}, {0, 5}, {5, 0}, {1, 1}, {2, 4}, {1, 2}, {1, 2}, {2, 0}
};
const stNumSeqBase *stNumSeqArrayTable[] =
{
au8NumZeroTerisSeq ,
au8NumOneTerisSeq ,
au8NumTwoTerisSeq ,
au8NumThreeTerisSeq ,
au8NumFourTerisSeq ,
au8NumFiveTerisSeq ,
au8NumSixTerisSeq ,
au8NumSevenTerisSeq ,
au8NumEightTerisSeq ,
au8NumNineTerisSeq ,
};
const uint16_t u16NumSeqArraySize[] =
{
sizeof(au8NumZeroTerisSeq )/sizeof(stNumSeqBase),
sizeof(au8NumOneTerisSeq )/sizeof(stNumSeqBase),
sizeof(au8NumTwoTerisSeq )/sizeof(stNumSeqBase),
sizeof(au8NumThreeTerisSeq)/sizeof(stNumSeqBase),
sizeof(au8NumFourTerisSeq )/sizeof(stNumSeqBase),
sizeof(au8NumFiveTerisSeq )/sizeof(stNumSeqBase),
sizeof(au8NumSixTerisSeq )/sizeof(stNumSeqBase),
sizeof(au8NumSevenTerisSeq)/sizeof(stNumSeqBase),
sizeof(au8NumEightTerisSeq)/sizeof(stNumSeqBase),
sizeof(au8NumNineTerisSeq )/sizeof(stNumSeqBase),
};
//这个是俄罗斯方块在一个9x9像素点中的坐标
//就是这样(x,x)(x,x)(x,x)(x,x),每个坐标2bit, 组成一个16bit数据
const uint16_t u16BasicModelTable[20] = {
/* #
* #
* #
* #
*/
0xC840,/* 0 */
/* # # # #
*/
0x3210,/* 1 */
/* # #
* # #
*/
0x5410,/* 2 */
/* # #
* # #
*/
0x5410,/* 3 */
/* # #
* # #
*/
0x6510,/* 4 */
/* #
* # #
* #
*/
0x8541,/* 5 */
/* # #
* # #
*/
0x5421,/* 6 */
/* #
* # #
* #
*/
0x9540,/* 7 */
/* #
* # # #
*/
0x6541,/* 8 */
/* #
* # #
* #
*/
0x9651,/* 9 */
/* # # #
* #
*/
0x9654,/* 10 */
/* #
* # #
* #
*/
0x9541,/* 11 */
/* #
* # # #
*/
0x6542,/* 12 */
/* #
* #
* # #
*/
0xA951,/* 13 */
/* # # #
* #
*/
0x8654,/* 14 */
/* # #
* #
* #
*/
0x9510,/* 15 */
/* #
* # # #
*/
0x6540,/* 16 */
/* # #
* #
* #
*/
0x9521,/* 17 */
/* # # #
* #
*/
0xA654,/* 18 */
/* #
* #
* # #
*/
0x9851, /* 19 */
};
下面是显示的main函数,10ms调用周期,可以自己改,把对应延时设置好就可以。
void tetris_main(void)
{
static uint8_t u8LastSec = 0xff;
static uint8_t u8LastDay = 0xff;
static uint32_t u8AnimDelay = 0;
stTetrisClockHandle *handle = NULL;
uint32_t u32RandomColor = 0;
int16_t xx, yy;
uint8_t u8RandomNum = 0;
uint8_t u8MinuteChange = TetrisClockUpdateTime(u8ClockNumber);
if (u8MinuteChange) {
/* clean screen and grid map */
BSP_LCD_SetTextColor(LCD_COLOR_BLACK);
BSP_LCD_FillRect(0, 0, RK043FN48H_WIDTH, 200);
rt_memset(u16GridMap, 0, sizeof(u16GridMap));
/* init handle */
for (uint16_t i = 0; i < NUMBER_COUNT; i++) {
handle = &stTetrisNumHandle[i];
u8RandomNum = HAL_RNG_GetRandomNumber(&hrng);
/* set clock number offset */
(i == 0) && (handle->u16TerisXOffset = FIRST_NUMBER_OFFSET);
(i == 1) && (handle->u16TerisXOffset = SECOND_NUMBER_OFFSET);
(i == 2) && (handle->u16TerisXOffset = THIRD_NUMBER_OFFSET);
(i == 3) && (handle->u16TerisXOffset = FOURTH_NUMBER_OFFSET);
/* init paramter */
handle->pNumSeq = stNumSeqArrayTable[u8ClockNumber[i]];
handle->u8SeqIdx = u16NumSeqArraySize[u8ClockNumber[i]] - 1;
handle->u32TerisColor = u32ColorRgb565Table[u8RandomNum % (sizeof(u32ColorRgb565Table)/sizeof(uint32_t))];
handle->u16TerisMode = u16BasicModelTable[handle->pNumSeq[handle->u8SeqIdx].u8ModeIdx];
handle->u16TerisX = handle->pNumSeq[handle->u8SeqIdx].u8DefX + handle->u16TerisXOffset;
handle->u16TerisY = 0;
handle->u8EndFlag = 0;
}
}
/* delay 80 ms */
if (++u8AnimDelay % 8 == 0) {
/* draw clock number loop */
for (uint16_t i = 0; i < NUMBER_COUNT; i++) {
handle = &stTetrisNumHandle[i];
/* update next tetris mode */
if (handle->u8EndFlag) {
if (handle->u8SeqIdx == 0)
continue;
u8RandomNum = HAL_RNG_GetRandomNumber(&hrng);
handle->u8SeqIdx--;
handle->u32TerisColor = u32ColorRgb565Table[u8RandomNum % (sizeof(u32ColorRgb565Table)/sizeof(uint32_t))];
handle->u16TerisMode = u16BasicModelTable[handle->pNumSeq[handle->u8SeqIdx].u8ModeIdx];
handle->u16TerisX = handle->pNumSeq[handle->u8SeqIdx].u8DefX + handle->u16TerisXOffset;
handle->u16TerisY = 0;
handle->u8EndFlag = 0;
}
/* clear last tetris base mode */
for (uint16_t i = handle->u16TerisMode; xx = handle->u16TerisX + (i&0x3), yy = handle->u16TerisY + ((i>>2)&0x3), i; i >>= 4)
{
u16GridMap[yy * GRID_COL_NUM + xx] = 0;
DrawTetrisRect(xx, yy, GRID_SIZE, GRID_SIZE, LCD_COLOR_BLACK, LCD_COLOR_BLACK);
}
/* down */
down(handle);
/* draw new tetris base mode */
for (uint16_t i = handle->u16TerisMode; xx = handle->u16TerisX + (i&0x3), yy = handle->u16TerisY + ((i>>2)&0x3), i; i >>= 4)
{
u16GridMap[yy * GRID_COL_NUM + xx] = 1;
DrawTetrisRect(xx, yy, GRID_SIZE, GRID_SIZE, handle->u32TerisColor, LCD_COLOR_BLACK);
}
}
}
/* flash colon */
if (u8LastSec != u8ClockNumber[NUMBER_COUNT]) {
/* get random color */
u8RandomNum = HAL_RNG_GetRandomNumber(&hrng);
u32RandomColor = u32ColorRgb565Table[u8RandomNum % (sizeof(u32ColorRgb565Table)/sizeof(uint32_t))];
for (uint16_t i = u16BasicModelTable[2]; xx = COLON_OFFSET_X + (i&0x3), yy = COLON_OFFSET_Y + ((i>>2)&0x3), i; i >>= 4) {
if (u8LastSec%2 == 0) {
DrawTetrisRect(xx, yy, GRID_SIZE, GRID_SIZE, u32RandomColor, LCD_COLOR_BLACK);
DrawTetrisRect(xx, yy+4, GRID_SIZE, GRID_SIZE, u32RandomColor, LCD_COLOR_BLACK);
} else {
DrawTetrisRect(xx, yy, GRID_SIZE, GRID_SIZE, LCD_COLOR_BLACK, LCD_COLOR_BLACK);
DrawTetrisRect(xx, yy+4, GRID_SIZE, GRID_SIZE, LCD_COLOR_BLACK, LCD_COLOR_BLACK);
}
}
u8LastSec = u8ClockNumber[NUMBER_COUNT];
}
/* update calendar */
if (u8LastDay != p_tm->tm_mday) {
/* show calendar */
const char* str;
str = solar_calendar(p_tm->tm_mon+1, p_tm->tm_mday, p_tm->tm_wday);
show_str(170, 205, 200, 16, (uint8_t*)str, CH_FONT16);
str = lunar_calendar(p_tm->tm_year+1900, p_tm->tm_mon+1, p_tm->tm_mday);
show_str(170, 224, 440, 16, (uint8_t*)str, CH_FONT16);
u8LastDay = p_tm->tm_mday;
}
}
其他几个函数用来更新俄罗斯方块下落后的坐标,及获取时间还有画每个小方块的函数,
画图和获取时间的函数用到了其他接口,可以根据自己的工程修改。
static void DrawTetrisRect(uint16_t TetrisX, uint16_t TetrisY, uint16_t Width, uint16_t Height,
uint32_t Fcolor, uint32_t Bcolor)
{
BSP_LCD_SetTextColor(Fcolor);
BSP_LCD_FillRect(TetrisX*(Width+1), TetrisY*(Height+1), Width, Height);
if (Fcolor != Bcolor) {
BSP_LCD_SetTextColor(LCD_COLOR_WHITE);
BSP_LCD_DrawRect(TetrisX*(Width+1), TetrisY*(Height+1), Width-1, Height-1);
}
}
static uint8_t TetrisClockUpdateTime(uint8_t *data)
{
static uint8_t u8LastMinute = 0xff;
uint8_t u8MinuteChange = 0;
rt_device_t rtc_dev = NULL;
time_t time_stamp = 0;
rtc_dev = rt_device_find("rtc");
if (rtc_dev != NULL) {
rt_device_control(rtc_dev, RT_DEVICE_CTRL_RTC_GET_TIME, &time_stamp);
}
p_tm = localtime(&time_stamp);
//rt_kprintf("year %d day %d h:%d m:%d\n", p_tm->tm_year, p_tm->tm_mday, p_tm->tm_hour, p_tm->tm_min);
data[0] = p_tm->tm_hour/10%10;
data[1] = p_tm->tm_hour%10;
data[2] = p_tm->tm_min/10%10;
data[3] = p_tm->tm_min%10;
data[4] = p_tm->tm_sec;
if (u8LastMinute != p_tm->tm_min) {
u8LastMinute = p_tm->tm_min;
u8MinuteChange = 1;
}
return u8MinuteChange;
}
void down(stTetrisClockHandle *handle)
{
int16_t xx, yy;
handle->u16TerisY += 1;
for (uint16_t i = handle->u16TerisMode; xx = handle->u16TerisX + (i&0x3), yy = handle->u16TerisY + ((i>>2)&0x3), i; i >>= 4)
{
if (yy >= GRID_ROW_NUM-handle->pNumSeq[handle->u8SeqIdx].u8DefY|| u16GridMap[yy * GRID_COL_NUM + xx]) {
/* crash bottom */
handle->u16TerisY -= 1;
handle->u8EndFlag = 1;
}
}
}
头文件
/*
* Copyright (c) 2006-2021, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2022-08-06 Administrator the first version
*/
#ifndef APPLICATIONS_TETRIS_TETRIS_CLOCK_H_
#define APPLICATIONS_TETRIS_TETRIS_CLOCK_H_
#include <stdint.h>
#include <stdlib.h>
#include <rtthread.h>
#include "main.h"
#define SCRLET 0xFF2400
#define CAMEL 0xA16B47
#define SUN_ORANGE 0xFF7300
#define LAWN_GREEN 0x7CFC00
#define FOREST_GREEN 0x228B22
#define MARINE_BLUE 0x00477D
#define CHROME_YELLOW 0xFFD700
#define DARK_SLATE_BLUE 0x483D8B
#define CAYN_BLUE 0x0DBF8C
#define RUBY 0xCC0080
#define MIDIUM_TURQUOISE 0x48D1CC
#define VERIDIAN 0x127436
#define LIGHT_LIME 0xCCFF00
#define INDIAN_RED 0xCD5C5C
#define MAROON 0x800000
#define MISTY_ROSE 0xFFE4E1
#define LIGHT_CORAL 0xF08080
#define HELIOTROPE 0x5000B8
typedef struct{
uint8_t u8ModeIdx;
int8_t u8DefX;
int8_t u8DefY;
int8_t u8BottomOffset;
}stNumSeqBase;
typedef struct{
const stNumSeqBase *pNumSeq;
uint16_t u16TerisMode;
uint16_t u16TerisX;
uint16_t u16TerisXOffset;
uint16_t u16TerisY;
uint32_t u32TerisColor;
uint8_t u8SeqIdx;
uint8_t u8EndFlag;
}stTetrisClockHandle;
extern RNG_HandleTypeDef hrng;
void tetris_main(void);
#endif /* APPLICATIONS_TETRIS_TETRIS_CLOCK_H_ */