📄 cfk.c
字号:
/*-----------------------------------------------------------------------
函 数 名: read_key_n
功 能: 读取按键,具有连发功能
输入参数:
注:
要每隔10ms调用一次,这样做是为了消抖
-----------------------------------------------------------------------*/
char read_key_n(void)
{
static char key_state = 0, key_time = 0, key_buffer=0;
char key_press, key_return = 0;
key_press = key_mask & key_input; // 读按键I/O 电平
switch (key_state)
{
case key_state_0: // 按键初始态
if (key_press != key_mask) key_state = key_state_1; // 键被按下,状态转换到键确认态
break;
case key_state_1: // 按键确认态
if (key_press != key_mask)
{
key_state = key_state_2; // 按键仍按下,状态转换到计时1
key_buffer = key_press;
key_time = 0; // 清另按键时间计数器
}
else key_state = key_state_0; // 按键已抬起,转换到按键初始态
break;
case key_state_2:
if (key_press != key_buffer)
{
key_state = key_state_0; // 按键已释放,转换到按键初始态
switch (key_buffer)
{
case 0b11100000: key_return = 1; break; // 输出up
case 0b11010000: key_return = 2; break; // 输出down
case 0b10110000: key_return = 3; break; // 输出left
case 0b01110000: key_return = 4; break; // 输出right
}
}
else if (++key_time >= 30) // 按键时间计数
{
key_state = key_state_3; // 按下时间>0.3s,状态转换到计时2
key_time = 0; // 清按键计数器
switch (key_buffer)
{
case 0b11100000: key_return = 1; break; // 长按up
case 0b11010000: key_return = 2; break; // 长按down
case 0b10110000: key_return = 3; break; // 长按left
case 0b01110000: key_return = 4; break; // 长按right
}
}
break;
case key_state_3:
if (key_press != key_buffer) key_state = key_state_0; //按键已释放,转换到按键初始态
else
{
if (++key_time >= 20) // 按键时间计数
{
key_time = 0; // 按下时间>0.2s,清按键计数器
switch (key_buffer)
{
case 0b11100000: key_return = 1; break; // 长按up
case 0b11010000: key_return = 2; break; // 长按down
case 0b10110000: key_return = 3; break; // 长按left
case 0b01110000: key_return = 4; break; // 长按right
}
}
}
break;
}
return key_return;
}
//--------------------------------方块主程序--------------------------------//
#define ulong unsigned long
#define uint unsigned int
#define uchar unsigned char
//----------------俄罗斯方块函数定义----------------------------------------//
void fk_dot(uchar x,uchar y); //显示1个方块点
void fk_cldot(uchar x,uchar y); //清除1个方块点
void fk_show(void); //显示分数
void fk_reffk(void); //刷新方块
void fk_refnew(void); //刷新预览方块
void fk_refline(uchar yy); //刷新1行背景
uchar fk_chk(void); //冲突检查
void fk_new(void); //产生新方块
void fk_add(void); //方块合并
void fk_init(void); //方块初始化
void fk_move(unsigned char mode); //移动方块
//--------------------------方块所需变量等定义--------------------------//
#define FULLMAP 0x03ff /*方块Y向的掩码,一行10个方块*/
#define LINEMAX 21 /*屏幕最高21行*/
uint fk_map[LINEMAX+4]; //背景映象
uchar fk_x,fk_y,fk_r; //方块X坐标、Y坐标、方向
uchar fk_type; //方块形状
uchar fk_oldx,fk_oldy,fk_oldr; //方块上次X坐标、Y坐标、方向
uchar fk_newtype,fk_newr; //新方块形状、方向
static ulong int score; //总分
bit fk_run; //方块运行状态 1-运行,0-停止
uint fk_mod[7][4][4]={ //方块模型号,4个方向,4行
7,2,0,0,1,3,1,0,2,7,0,0,2,3,2,0, //_|_
2,3,1,0,3,6,0,0,2,3,1,0,3,6,0,0, //_|~
1,3,2,0,6,3,0,0,1,3,2,0,6,3,0,0, //~|_
1,1,3,0,4,7,0,0,3,2,2,0,7,1,0,0, //|__
3,1,1,0,1,7,0,0,2,2,3,0,7,4,0,0, //__|
1,1,1,1,15,0,0,0,1,1,1,1,15,0,0,0, //____
3,3,0,0,3,3,0,0,3,3,0,0,3,3,0,0 //田字
};
/*-----------------------------------------------------------------------
函 数 名: fk_show
功 能: 显示分数
输入参数:
注:
-----------------------------------------------------------------------*/
void fk_show(void)
{
unsigned char i,tmp,num;
unsigned int j;
LCD_set_XY(42,0); //设置坐标
tmp=score;
for (j=10000;j>=1;j/=10) //取各位数字显示
{
num=tmp/j;
tmp%=j;
for (i=0;i<8;i++)
LCD_write_byte(number[num][i],1);
}
}
/*-----------------------------------------------------------------------
函 数 名: fk_reffk
功 能: 刷新方块
输入参数:
注:
-----------------------------------------------------------------------*/
void fk_reffk(void)
{
uchar i,j;
uchar temp;
//----------------------------------------------//清除原来的方块
for (i=0;i<4;i++)
{
temp=(fk_mod[fk_type][fk_oldr][i]);
for (j=fk_oldx;j<fk_oldx+4;j++)
{
if(temp&0x01)
{
fk_cldot(j,fk_oldy+i);
}
temp>>=1;
}
}
//----------------------------------------------//显示新的方块
for (i=0;i<4;i++)
{
temp=(fk_mod[fk_type][fk_r][i]);
for (j=fk_x;j<fk_x+4;j++)
{
if(temp&0x01)
{
fk_dot(j,fk_y+i);
}
temp>>=1;
}
}
fk_oldx=fk_x;fk_oldy=fk_y;fk_oldr=fk_r; //保存新方块位置
}
/*-----------------------------------------------------------------------
函 数 名: fk_refline
功 能: 刷新1行背景
输入参数: yy 要刷新的行
注:
-----------------------------------------------------------------------*/
void fk_refline(uchar yy)
{
uchar i;
uint temp;
if (yy>=1)
{
temp=fk_map[yy];
for (i=0;i<10;i++)
{
if ((temp&0x01) !=0)
fk_dot(i,yy);
else
fk_cldot(i,yy);
temp >>= 1;
}
}
}
/*-----------------------------------------------------------------------
函 数 名: fk_refnew
功 能: 刷新预览方块
输入参数:
注:
-----------------------------------------------------------------------*/
void fk_refnew(void)
{
uchar i;
LCD_set_XY(35,0);
for (i=0;i<4;i++)
{
LCD_write_byte(smfk[fk_newtype][fk_newr][i], 1);
}
}
/*-----------------------------------------------------------------------
函 数 名: fk_chk
功 能: 冲突检查
输入参数:
注:
-----------------------------------------------------------------------*/
char fk_chk(void)
{
uchar i;
char neq=0;
for (i=0;i<4;i++)
{
if ( (((fk_mod[fk_type][fk_r][i])<<fk_x)+(fk_map[fk_y+i])) != (((fk_mod[fk_type][fk_r][i])<<fk_x)|(fk_map[fk_y+i])) )
neq=1;
}
return neq ;
}
/*-----------------------------------------------------------------------
函 数 名: fk_new
功 能: 产生新方块
输入参数:
注:
-----------------------------------------------------------------------*/
void fk_new(void)
{
srand(rand()+fk_x+fk_y+fk_r);
fk_oldx=fk_x=4;
fk_oldy=fk_y=LINEMAX;
fk_type = fk_newtype;
fk_oldr=fk_newr;
fk_newtype = rand()%7;
fk_newr=rand()%4;
fk_refnew(); //刷新预览方块
if (fk_run)
fk_reffk(); //刷新显示
}
/*-----------------------------------------------------------------------
函 数 名: fk_add
功 能: 方块合并
输入参数:
注:
-----------------------------------------------------------------------*/
void fk_add(void)
{
uchar i,j;
uchar full=0x00;
uchar fulltemp;
uchar fullline=0x00;
for (i=0;i<4;i++) //方块合并
{
fk_map[fk_y+i] |= (fk_mod[fk_type][fk_r][i])<<fk_x;
full <<= 1;
if ((fk_y+i >= 1) && (fk_map[fk_y+i] == 0xffff)) //满行
{
full |= 0x01;
}
}
if (full != 0) //有满行
{
fulltemp=full;
for (i=fk_y;i<LINEMAX+4+fullline;i++) //上面行下移
{
if ((i < LINEMAX+4)&&(fk_map[i-fullline]!=fk_map[i]))
{
fk_map[i-fullline]=fk_map[i];
fk_refline(i-fullline);
}
if ((i >= LINEMAX+4)&&(fk_map[i-fullline]!=~FULLMAP))
{
fk_map[i-fullline]=~FULLMAP; //背景映象
fk_refline(i-fullline);
}
if ((fulltemp&0x08) != 0)
{
fullline++;
}
fulltemp <<= 1;
}
score+=fullline;
fk_show(); //显示分数
fk_new(); //产生新方块
}
else
{
if (fk_y==LINEMAX) //在最高位置碰撞且不能消行则游戏结束
{
#asm("cli") //关中断
LCD_clear();
LCD_draw_bmp_pixel(15,0,32,44);
for (j=0;j<3;j++)
{
delay_ms(700);
LCD_write_byte(0x0d, 0);
delay_ms(700);
LCD_write_byte(0x0c, 0);
}
fk_run=0;
#asm("sei")
}
else
fk_new(); //产生新方块
}
}
/*-----------------------------------------------------------------------
函 数 名: fk_init
功 能: 方块初始化
输入参数:
注:
-----------------------------------------------------------------------*/
void fk_init(void)
{
uchar i;
LCD_clear(); //LCD清屏
fk_map[0]=0xffff; //背景映象
for (i=1;i<LINEMAX+4;i++)
{
fk_map[i]=~FULLMAP; //背景映象
fk_refline(i); //刷新1行背景
}
LCD_draw_top(); //画右边的信息栏
fk_run = 1;
fk_show(); //显示分数
fk_new(); //产生新方块
}
/*-----------------------------------------------------------------------
函 数 名: fk_move
功 能: 方块移动
输入参数: key 移动方向
注:
-----------------------------------------------------------------------*/
void fk_move(uchar key)
{
switch(key)
{
case 2: //下移到底
while((fk_y>0)&&!(fk_chk())) //一直下移直到冲突
{
fk_y--;
}
fk_y++; //恢复到未冲突位置
break;
case 3: //右移
if (fk_run) //游戏中右移
{
if(fk_x<10)
{
fk_x++;
if(fk_chk())
fk_x--;//有冲突取消操作
}
}
break;
case 4: //左移
if (fk_run) //游戏中左移
{
if(fk_x>0)
{
fk_x--;
if(fk_chk())
fk_x++;//有冲突取消操作
}
}
break;
case 1: //变形
if (fk_run) //游戏中变形
{
fk_r++;
if(fk_chk())
fk_r--; //有冲突取消操作
fk_r &= 0x03;
}
else //初始化时
{
fk_init();
}
break;
}
if((fk_run)&& ((fk_x!=fk_oldx)||(fk_y!=fk_oldy)||(fk_r!=fk_oldr)) ) //方块的属性有变化就刷新
{
fk_reffk(); //刷新显示
}
rand();
}
void main(void)
{
char temp;
OSCCAL=0x9d; // 8M系统内部时钟校准
PORTA=0x00;
DDRA=0x00;
//设置MCU的I/O口
DDRB |= LCD_RST | LCD_DC | LCD_CE | SPI_MOSI | SPI_CLK;
SPSR |= BIT(SPI2X); // 设置SPI时钟倍速
SPCR |= BIT(SPE)|BIT(MSTR); // 使能SPI接口,主机模式,4M时钟
// T/C0 初始化
TCCR0 = 0x0B; // 内部时钟,64 分频(4M/64=62.5KHz),CTC 模式
TCNT0 = 0x00;
OCR0 = 0x7C; // OCR0 = 0x7C(124),(124+1)/62.5=2ms
TIMSK = 0x02; // 允许T/C0 比较匹配中断
LCD_init(); //初始化液晶
LCD_write_chinese_string(2,2,16,5,0,0); //显示LOGE
delay_ms(3000);
LCD_clear(); //LCD清屏
LCD_draw_top(); //画右边的信息栏
#asm("sei") // 开放全局中断
while (1)
{
if (key_10ms_ok) // 10ms 到
{
key_10ms_ok = 0;
temp = read_key_n();
if (temp!=0)
fk_move(temp); //有键按下
}
if (key_500ms_ok) //0.5s 到
{
key_500ms_ok=0;
if (fk_run) //自然下移
{
if (fk_y>0)
{
fk_y--;
if (fk_chk())
{
fk_y++; //有冲突取消操作,执行碰撞组合
fk_add(); //方块合并
}
}
else //方块到底也执行碰撞组合
{
fk_add(); //方块合并
}
if (fk_run) fk_reffk(); //刷新显示
}
}
};
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -