基于51单片机和8X8点阵屏、独立按键的单人弹球小游戏
目录
- 系列文章目录
- 前言
- 一、效果展示
- 二、原理分析
- 三、各模块代码
- 1、8X8点阵屏
- 2、独立按键
- 3、定时器0
- 4、定时器1
- 四、主函数
- 总结
系列文章目录
前言
用的是普中A2开发板,用到板上的独立按键、8X8点阵屏。
【单片机】STC89C52RC
【频率】12T@11.0592MHz
效果查看/操作演示:B站搜索“甘腾胜”或“gantengsheng”查看。
源代码下载:B站对应视频的简介有工程文件下载链接。
一、效果展示
1、上电后向左循环滚动显示游戏名称的英文
2、改变球移动的速度时数字上下滚动显示
3、游戏开始前调整初始位置
4、游戏中
5、游戏结束后向左循环滚动显示得分
二、原理分析
1、向左或向上滚动显示字母或数字
每一个B对应一个灯。缓存数组DisplayBuffer的8个字节分别对应这8列,高位在下。
B0 B0 B0 B0 B0 B0 B0 B0
B1 B1 B1 B1 B1 B1 B1 B1
B2 B2 B2 B2 B2 B2 B2 B2
B3 B3 B3 B3 B3 B3 B3 B3
B4 B4 B4 B4 B4 B4 B4 B4
B5 B5 B5 B5 B5 B5 B5 B5
B6 B6 B6 B6 B6 B6 B6 B6
B7 B7 B7 B7 B7 B7 B7 B7
想要改变显示内容,改变数组DisplayBuffer的数据就行了,由定时器自动扫描
unsigned char DisplayBuffer[8];
将所需滚动显示的内容进行取模,保存到一个数组中(用code修饰,即把数据保存到Flash中,否则RAM空间不够用),滚动显示函数的参数中需要有一个偏移量Offset,定时器进行计时,每隔一段时间让Offset自增(或自减),就可以实现滚动显示的效果,滚动显示的实现利用与、或、移位等运算进行操作。向左滚动和向上滚动,对应的操作不同。向左滚动很简单,因为字符字模是纵向取模的,而上下滚动则需要用到移位等操作。
ASCII字符字模数据提取自B站江协科技STM32视频教程(OLED模块的使用)所分享的源码。所有ASCII的字符尺寸都是宽6高8。
2、独立按键的检测
用定时器扫描检测,这样可以防止阻塞主程序的运行。每隔20ms检查一次按键按下的状态。
检测出哪个按键被按下后,再跟上一次的检测结果对比,判断是按下瞬间,长按,还是松手瞬间,不同情况返回不同的值。长按后,设置成每隔100ms返回一次长按的键码,这样方便挡板连续移动,而且速度刚刚好,不快不慢。
除了游戏时移动挡板用到按下瞬间和长按,其他模式的操作用的是松手瞬间,因为检测出松手(下一次按下按键前)且在键码被获取前,键码不会改变。
3、挡板、球的显示、移动,球的碰撞
用8个字节作为显示缓存,对应64Bit(64位),每个Bit对应一个LED,1为亮,0为灭。
最基本的是其中的两个函数。通过与、或、移位运算可以只改变其中的一个Bit,这样就可以随意控制屏幕的显示了。
挡板用三个LED显示,挡板的移动只需要改变显示缓存的两个Bit,例如,向右移动,则将挡板最左边的LED熄灭,将挡板最右边LED的右侧的LED点亮,就可以完成移动了。建立一个平面直角坐标系,左上方为原点(0,0),向右为X轴正方向,向下为Y轴正方向,则右下角的坐标为(7,7)。然后用一个变量保存挡板中间的LED的位置就行了,要注意限制移动的范围,不要超出屏幕区域。
用一个LED表示球,用两个变量BallX、BallY保存球的X、Y坐标。球移动的时候,先将移动前所在位置的LED熄灭,再将移动的下一个位置的LED点亮,最后还要更新球的X、Y坐标对应的两个变量。
用两个变量记录球移动的方向,Direction1记录X方向上的移动,Direction2记录Y方向上的移动。值为0时无意义,值为1时向坐标轴的正方向移动,即向右或向下移动,值为2时向坐标轴的负方向移动,即向左或向上移动。当来到边界时,改变这两个变量的值就可以改变移动方向了。例如,球来到右边界,即来到第八列,Direction2不变,Direction1由1变成2。又例如,如果球向右下方运动,来到第七行与挡板碰撞,则Direction1保持为1不变,Direction2由1变成2。Direction1、Direction2这两个记录方向的变量是为了确定球的下一个位置,并更新BallX、BallY的值。
球的碰撞跟光的反射的原理类似。
需要注意的是,挡板的移动需要在定时器中断函数中进行更新显示,我自己测试的时候发现如果在主函数中更新显示的话,移动的时候会有卡顿的感觉。
通过变量MoveSpeed来保存球移动的速度,实际是移动的时间间隔,值越小,移动越快,结合定时器控制球的移动速度。
4、游戏结束的判定
当球来到第七行(挡板在第八行),如果球的正下方不是挡板,则视为游戏结束。
5、游戏结束的全屏闪烁
因开发板的LED点阵是通过定时器扫描显示的,所以需要在定时器中断函数中操作。如果游戏没结束,则正常扫描显示,如果游戏结束,则结合闪烁变量FlashFlag(FlashFlag在定时器中每隔500ms置反一次)让屏幕以1s的周期进行闪烁。如果FlashFlag为1,让屏幕停止显示(8列都不导通,即P0赋值为0xFF),如果FlashFlag为0,则让定时器正常扫描,让屏幕正常显示。
6、游戏分数
每接住一次球,分数增加1,游戏结束后循环滚动显示三位数得分。用16位的变量(无符号整型)记录分数,游戏结束后取出其中的百位、十位、个位,再根据0~9这10个数字的字模进行滚动显示。
三、各模块代码
1、8X8点阵屏
h文件
#ifndef __MATRIXLED__
#define __MATRIXLED__extern unsigned char DisplayBuffer[];
void MatrixLED_Clear(void);
void MatrixLED_Init(void);
void MatrixLED_MoveLeft(unsigned char *Array,unsigned int Offset);
void MatrixLED_MoveUp(unsigned char *Array,unsigned int Offset);
void MatrixLED_Tick(void);
void MatrixLED_DrawPoint(unsigned char X,unsigned char Y);
void MatrixLED_ClearPoint(unsigned char X,unsigned char Y);#endif
c文件
#include <REGX52.H>/*引脚定义*/sbit _74HC595_DS=P3^4; //串行数据输入
sbit _74HC595_STCP=P3^5; //储存寄存器时钟输入,上升沿有效
sbit _74HC595_SHCP=P3^6; //移位寄存器时钟输入,上升沿有效/*
每一个B对应一个灯。缓存数组DisplayBuffer的8个字节分别对应这8列,高位在下
B0 B0 B0 B0 B0 B0 B0 B0
B1 B1 B1 B1 B1 B1 B1 B1
B2 B2 B2 B2 B2 B2 B2 B2
B3 B3 B3 B3 B3 B3 B3 B3
B4 B4 B4 B4 B4 B4 B4 B4
B5 B5 B5 B5 B5 B5 B5 B5
B6 B6 B6 B6 B6 B6 B6 B6
B7 B7 B7 B7 B7 B7 B7 B7
*///想要改变显示内容,改变数组DisplayBuffer的数据就行了,由定时器自动扫描
unsigned char DisplayBuffer[8];/*函数定义*//*** 函 数:LED点阵屏清空显示* 参 数:无* 返 回 值:无* 说 明:直接更改缓存数组的数据就行了,由定时器自动扫描显示*/
void MatrixLED_Clear(void)
{unsigned char i;for(i=0;i<8;i++){DisplayBuffer[i]=0;}
}/*** 函 数:MatrixLED初始化(即74HC595初始化)* 参 数:无* 返 回 值:无*/
void MatrixLED_Init(void)
{_74HC595_SHCP=0; //移位寄存器时钟信号初始化_74HC595_STCP=0; //储存寄存器时钟信号初始化MatrixLED_Clear(); //点阵屏清屏
}/*** 函 数:74HC595写入字节* 参 数:Byte 要写入的字节* 返 回 值:无*/
void _74HC595_WriteByte(unsigned char Byte)
{unsigned char i;for(i=0;i<8;i++) //循环8次{_74HC595_DS=Byte&(0x01<<i); //低位先发_74HC595_SHCP=1; //SHCP上升沿时,DS的数据写入移位寄存器_74HC595_SHCP=0;}_74HC595_STCP=1; //STCP上升沿时,数据从移位寄存器转存到储存寄存器_74HC595_STCP=0;
}/*** 函 数:8X8LED点阵屏显示数组内容* 参 数:Array 传递过来的数组的首地址(即指针),数组名就是数组的首地址* 返 回 值:Offset 偏移量,向左偏移Offset个像素* 说 明:要求逐列式取模,高位在下*/
void MatrixLED_MoveLeft(unsigned char *Array,unsigned int Offset)
{unsigned char i;Array+=Offset;for(i=0;i<8;i++){DisplayBuffer[i]=*Array;Array++; //地址自增}
}/*** 函 数:8X8LED点阵屏显示数组内容* 参 数:Array 传递过来的数组的首地址(即指针),数组名就是数组的首地址* 返 回 值:Offset 显示数组数据的偏移量,向上偏移Offset个像素* 说 明:要求逐列式取模,高位在下*/
void MatrixLED_MoveUp(unsigned char *Array,unsigned int Offset)
{unsigned char i,m,n;m=Offset/8;n=Offset%8;Array+=m*8;for(i=0;i<8;i++){DisplayBuffer[i]=(*Array>>n)|(*(Array+8)<<(8-n));Array++;}
}/*** 函 数:LED点阵屏驱动函数,中断中调用* 参 数:无* 返 回 值:无*/
void MatrixLED_Tick(void)
{static unsigned char i=0; //定义静态变量P0=0xFF; //消影_74HC595_WriteByte(DisplayBuffer[i]); //将数据写入到74HC595中锁存P0=~(0x80>>i); //位选,低电平选中i++; //下次进中断后扫描下一列i%=8; //显示完第八列后,又从第一列开始显示
}/*** 函 数:MatrixLED在指定位置画一个点* 参 数:X 指定点的横坐标,范围:0~7* 参 数:Y 指定点的纵坐标,范围:0~7* 返 回 值:无* 说 明:左上角的LED为原点(0,0),向右为X轴正方向,向下为Y轴正方向*/
void MatrixLED_DrawPoint(unsigned char X,unsigned char Y)
{if(X>=0 && X<=7 && Y>=0 && Y<=7){DisplayBuffer[X]|=0x01<<Y;}
}/*** 函 数:MatrixLED在指定位置清除一个点* 参 数:X 指定点的横坐标,范围:0~7* 参 数:Y 指定点的纵坐标,范围:0~7* 返 回 值:无* 说 明:左上角的LED为原点(0,0),向右为X轴正方向,向下为Y轴正方向*/
void MatrixLED_ClearPoint(unsigned char X,unsigned char Y)
{if(X>=0 && X<=7 && Y>=0 && Y<=7){DisplayBuffer[X]&=~(0x01<<Y);}
}
2、独立按键
h文件
#ifndef __KEYSCAN_H__
#define __KEYSCAN_H__extern unsigned char KeyNumber1;
unsigned char Key(void);
void Key_Tick(void);#endif
c文件
#include <REGX52.H>sbit Key1=P3^1;
sbit Key2=P3^0;
sbit Key3=P3^2;
sbit Key4=P3^3;unsigned char KeyNumber;
unsigned char KeyNumber1;/*** 函 数:获取独立按键键码* 参 数:无* 返 回 值:按下按键的键码,范围:0~12,0表示无按键按下* 说 明:在下一次检测按键之前,第二次获取键码一定会返回0*/
unsigned char Key(void)
{unsigned char KeyTemp=0;KeyTemp=KeyNumber;KeyNumber=0;return KeyTemp;
}/*** 函 数:按键驱动函数,在中断中调用* 参 数:无* 返 回 值:无*/
void Key_Tick(void)
{static unsigned char NowState,LastState;static unsigned int KeyCount;LastState=NowState; //保存上一次的按键状态NowState=0; //如果没有按键按下,则NowState为0//获取当前按键状态if(Key1==0){NowState=1;}if(Key2==0){NowState=2;}if(Key3==0){NowState=3;}if(Key4==0){NowState=4;}//如果上个时间点按键未按下,这个时间点按键按下,则是按下瞬间if(LastState==0){switch(NowState){case 1:KeyNumber=1;break;case 2:KeyNumber=2;break;case 3:KeyNumber=3;break;case 4:KeyNumber=4;break;default:break;}}//如果上个时间点按键按下,这个时间点按键按下,则是一直按住按键if(LastState && NowState){KeyCount++;if(KeyCount%5==0) //定时器中断函数中每隔20ms检测一次按键{ //长按后每隔100ms返回一次长按的键码if (LastState==1 && NowState==1){KeyNumber=5;}else if(LastState==2 && NowState==2){KeyNumber=6;}else if(LastState==3 && NowState==3){KeyNumber=7;}else if(LastState==4 && NowState==4){KeyNumber=8;}}}else{KeyCount=0;}//如果上个时间点按键按下,这个时间点按键未按下,则是松手瞬间if(NowState==0){switch(LastState){case 1:KeyNumber=9;break;case 2:KeyNumber=10;break;case 3:KeyNumber=11;break;case 4:KeyNumber=12;break;default:break;}}KeyNumber1=KeyNumber;
}
3、定时器0
h文件
#ifndef __TIMER0_H__
#define __TIMER0_H__void Timer0_Init(void);#endif
c文件
#include <REGX52.H>/*** 函 数:定时器0初始化* 参 数:无* 返 回 值:无*/
void Timer0_Init(void)
{
// AUXR&=0x7F; //定时器时钟12T模式(STC89C52RC是12T单片机,无需设置)TMOD&=0xF0; //设置定时器模式(高四位不变,低四位清零)TMOD|=0x01; //设置定时器模式(通过低四位设为16位不自动重装)TL0=0xF0; //设置定时初值,定时10ms,12T@12.0000MHzTH0=0xD8; //设置定时初值,定时10ms,12T@12.0000MHzTF0=0; //清除TF0标志TR0=1; //定时器0开始计时ET0=1; //打开定时器0中断允许EA=1; //打开总中断PT0=0; //当PT0=0时,定时器0为低优先级,当PT0=1时,定时器0为高优先级
}/*定时器中断函数模板
void Timer0_Routine() interrupt 1 //定时器0中断函数
{static unsigned int T0Count; //定义静态变量TL0=0xF0; //设置定时初值,定时10ms,12T@12.0000MHzTH0=0xD8; //设置定时初值,定时10ms,12T@12.0000MHzT0Count++;if(T0Count>=1000){T0Count=0;}
}
*/
4、定时器1
h文件
#ifndef __TIMER1_H__
#define __TIMER1_H__void Timer1_Init(void);#endif
c文件
#include <REGX52.H>/*** 函 数:定时器1初始化* 参 数:无* 返 回 值:无*/
void Timer1_Init(void)
{
// AUXR&=0xBF; //定时器时钟12T模式(STC89C52RC是12T单片机,无需设置)TMOD&=0x0F; //设置定时器模式(低四位不变,高四位清零)TMOD|=0x10; //设置定时器模式(通过高四位设为16位不自动重装的模式)TL1=0x66; //设置定时初值,定时1ms,12T@11.0592MHzTH1=0xFC; //设置定时初值,定时1ms,12T@11.0592MHzTF1=0; //清除TF1标志TR1=1; //定时器1开始计时ET1=1; //打开定时器1中断允许EA=1; //打开总中断PT1=1; //当PT1=0时,定时器1为低优先级,当PT1=1时,定时器1为高优先级
}/*定时器中断函数模板
void Timer1_Routine() interrupt 3 //定时器1中断函数
{static unsigned int T1Count; //定义静态变量TL1=0x66; //设置定时初值,定时1ms,12T@11.0592MHzTH1=0xFC; //设置定时初值,定时1ms,12T@11.0592MHzT1Count++;if(T1Count>=1000){T1Count=0;}
}
*/
四、主函数
main.c
/*by甘腾胜@20250327
【效果查看/操作演示】B站搜索“甘腾胜”或“gantengsheng”查看
【单片机】STC89C52RC
【频率】12T@11.0592MHz
【外设】8X8LED点阵屏、独立按键
【简单的原理分析】https://blog.csdn.net/gantengsheng/article/details/143581157
【注意】
【操作说明】
(1)显示游戏名称界面和显示速度的英文的界面按任意按键跳过
(2)速度选择界面通过K1和K2选择速度
(3)K4为确定键,K3为返回键
(4)玩家通过独立按键K1和K2控制挡板左右移动
(5)显示得分的英文的界面可按K4跳过
(6)循环显示得分界面可按K3返回速度选择界面
*/#include <REGX52.H>
#include <STDLIB.H> //包含随机函数的声明
#include "MatrixLED.h"
#include "KeyScan.h"
#include "Timer0.h"
#include "Timer1.h"unsigned char KeyNum; //存储获取的独立按键的键码
unsigned char Mode; //游戏模式,0:显示游戏名称,1:显示速度的英文,2:速度选择界面,3:游戏开始前的调整,
//4:游戏进行中,5:游戏结束全屏闪烁,6:显示得分的英文,7:循环显示三位数得分
unsigned char AllowChangeModeFlag=1; //允许改变模式的标志,1:允许改变,0:不允许改变
unsigned char Player=4; //玩家挡板(下挡板)的中心位置,范围:1~6
unsigned char BallX=4; //球的X坐标,左上角的LED为原点(0,0),向右为X轴正方向,向下为Y轴正方向
unsigned char BallY=6; //球的Y坐标,左上角的LED为原点(0,0),向右为X轴正方向,向下为Y轴正方向
unsigned char Direction1=2; //球左右移动的方向,0:左右方向不移动,1:向左移动,2:向右移动
unsigned char Direction2=1; //球上下移动的方向,1:向上移动,2:向下移动
unsigned char MoveFlag; //球移动的标志,1:移动,0:不移动
unsigned char OnceFlag; //各模式中切换为其他模式前只执行一次的标志(类似于主函数死循环前的那部分),1:执行,0:不执行
unsigned char Offset1; //偏移量,用来控制英文字母向左滚动显示(切换模式后清零)
unsigned char Offset2; //偏移量,用来控制速度对应的数字上下滚动显示(切换模式后不清零)
unsigned char UpFlag; //速度选择界面,数字向上滚动显示的标志,1:滚动,0:不滚动
unsigned char DownFlag; //速度选择界面,数字向下滚动显示的标志,1:滚动,0:不滚动
unsigned char RollFlag; //字母或数字滚动显示的标志,1:滚动,0:不滚动
unsigned char RollCount; //上下滚动的计次
unsigned char MoveSpeed=100; //球移动的速度,值越小,速度越快,单位是10ms(定时器0定时10ms),默认0.5s移动一次
unsigned char GameOverFlag; //游戏结束的标志,1:游戏结束,0:游戏未结束
unsigned char FlashFlag; //闪烁的标志,1:不显示,0:显示
unsigned char T0Count1,T0Count2,T0Count3,T0Count4; //定时器计数变量
unsigned int Score; //游戏得分unsigned char idata ScoreShow[]={ //三位数游戏得分(用于滚动显示)
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // 无显示
0x00,0x00,0x00,0x00,0x00,0x00, // 得分的百位
0x00,0x00,0x00,0x00,0x00,0x00, // 得分的十位
0x00,0x00,0x00,0x00,0x00,0x00, // 得分的个位
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // 无显示
};//取模要求:阴码(亮点为1),纵向取模,高位在下
unsigned char code Table1[]={ //游戏名称“弹球游戏”的英文:<<PINBALL>>
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // 无显示
0x00,0x08,0x14,0x22,0x49,0x14,0x22,0x41, // << 宽8高8(自定义书名号:两个小于号)
0x00,0x7F,0x09,0x09,0x09,0x06, // P 宽6高8
0x00,0x00,0x41,0x7F,0x41,0x00, // I
0x00,0x7F,0x04,0x08,0x10,0x7F, // N
0x00,0x7F,0x49,0x49,0x49,0x36, // B
0x00,0x7C,0x12,0x11,0x12,0x7C, // A
0x00,0x7F,0x40,0x40,0x40,0x40, // L
0x00,0x7F,0x40,0x40,0x40,0x40, // L
0x00,0x41,0x22,0x14,0x49,0x22,0x14,0x08, // >>
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // 无显示
};
unsigned char code Table2[]={ //“速度”的英文:SPEED,宽6高8
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // 无显示
0x00,0x46,0x49,0x49,0x49,0x31, // S
0x00,0x7F,0x09,0x09,0x09,0x06, // P
0x00,0x7F,0x49,0x49,0x49,0x41, // E
0x00,0x7F,0x49,0x49,0x49,0x41, // E
0x00,0x7F,0x41,0x41,0x22,0x1C, // D
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // 无显示
0x00,0x00,0x00,0x42,0x7F,0x40,0x00,0x00, // 1 如果不按按键跳过,则在显示“1”后自动切换到下一个模式
};
unsigned char code Table3[]={ //速度选择界面速度对应的数字:“123451”,宽8高8,数值越大,速度越快
0x00,0x00,0x00,0x42,0x7F,0x40,0x00,0x00, // 1
0x00,0x00,0x42,0x61,0x51,0x49,0x46,0x00, // 2
0x00,0x00,0x21,0x41,0x45,0x4B,0x31,0x00, // 3
0x00,0x00,0x18,0x14,0x12,0x7F,0x10,0x00, // 4
0x00,0x00,0x27,0x45,0x45,0x45,0x39,0x00, // 5
0x00,0x00,0x00,0x42,0x7F,0x40,0x00,0x00, // 1
};
unsigned char code Table4[]={ //得分的英文:“SCORE”,宽6高8
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, //无显示
0x00,0x46,0x49,0x49,0x49,0x31, // S
0x00,0x3E,0x41,0x41,0x41,0x22, // C
0x00,0x3E,0x41,0x41,0x41,0x3E, // O
0x00,0x7F,0x09,0x19,0x29,0x46, // R
0x00,0x7F,0x49,0x49,0x49,0x41, // E
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, //无显示
};
unsigned char code Table5[]={ //游戏得分的字模数据,宽6高8
0x00,0x3E,0x51,0x49,0x45,0x3E, // 0
0x00,0x00,0x42,0x7F,0x40,0x00, // 1
0x00,0x42,0x61,0x51,0x49,0x46, // 2
0x00,0x21,0x41,0x45,0x4B,0x31, // 3
0x00,0x18,0x14,0x12,0x7F,0x10, // 4
0x00,0x27,0x45,0x45,0x45,0x39, // 5
0x00,0x3C,0x4A,0x49,0x49,0x30, // 6
0x00,0x01,0x71,0x09,0x05,0x03, // 7
0x00,0x36,0x49,0x49,0x49,0x36, // 8
0x00,0x06,0x49,0x49,0x29,0x1E, // 9
};/*** 函 数:主函数(有且仅有一个)* 参 数:无* 返 回 值:无* 说 明:主函数是程序执行的起点,负责执行整个程序的主要逻辑*/
void main()
{unsigned char i;P2_5=0; //防止开发板上的蜂鸣器发出声音Timer0_Init(); //定时器0初始化Timer1_Init(); //定时器1初始化MatrixLED_Init(); //点阵屏初始化while(1){KeyNum=Key(); //获取独立按键的键码/*如果有按键按下*/if(KeyNum){if(Mode==0) //如果是循环滚动显示游戏英文名“<<PINBALL>>”的界面{if(KeyNum>=9 && KeyNum<=12 && AllowChangeModeFlag) //如果按下任意按键(松手瞬间),且允许改变模式{Mode=1; //切换到滚动显示速度的英文“SPEED”的界面OnceFlag=1; //切换模式前只执行一次的标志置1AllowChangeModeFlag=0; //允许切换模式的标志置零}}else if(Mode==1) //如果是滚动显示速度的英文“SPEED”的界面{if(KeyNum>=9 && KeyNum<=12 && AllowChangeModeFlag) //如果按下任意按键(松手瞬间),且允许改变模式{Mode=2; //切换到难度选择界面OnceFlag=1;AllowChangeModeFlag=0;}}else if(Mode==2) //如果是速度选择界面{if(KeyNum==9) //如果按了“上”键K1(松手瞬间){UpFlag=1; //数字向上滚动的标志置1}if(KeyNum==10) //如果按了“下”键K2(松手瞬间){DownFlag=1; //数字向下滚动的标志置1}if(KeyNum==12 && AllowChangeModeFlag) //如果按了确认键K4(松手瞬间),且允许改变模式{Mode=3; //切换到游戏准备界面OnceFlag=1;AllowChangeModeFlag=0;MatrixLED_Clear();}}else if(Mode==3) //如果是游戏准备界面{if(KeyNum==12 && AllowChangeModeFlag) //如果按了开始键K4(松手瞬间),且允许改变模式{Mode=4; //切换到游戏模式OnceFlag=1;AllowChangeModeFlag=0;}}else if(Mode==5) //如果是游戏结束全屏闪烁的界面{if(KeyNum==12 && AllowChangeModeFlag) //如果按了确认键K4(松手瞬间),且允许改变模式{Mode=6; //切换到显示英文“SCORE”的界面OnceFlag=1;AllowChangeModeFlag=0;}}else if(Mode==6) //如果是显示英文“SCORE”的界面{if(KeyNum==12 && AllowChangeModeFlag) //如果按了确认键K4(松手瞬间),且允许改变模式{Mode=7; //切换到循环显示三位数得分的界面OnceFlag=1;AllowChangeModeFlag=0;}}else if(Mode==7) //如果是循环显示三位数得分的界面{if(KeyNum==11 && AllowChangeModeFlag) //如果按了返回键K3(松手瞬间),且允许改变模式{Mode=2; //返回到速度选择界面OnceFlag=1;AllowChangeModeFlag=0;}}AllowChangeModeFlag=1; //允许改变模式的标志置1}/*循环滚动显示游戏英文名“<<PINBALL>>”*/if(Mode==0){if(OnceFlag) //切换到其他模式前,此if中的代码只执行1次{OnceFlag=0; //只执行一次的标志清零Offset1=0; //偏移量清零}if(RollFlag) //如果滚动的标志RollFlag为真(非零即真){RollFlag=0; //滚动的标志RollFlag清零MatrixLED_MoveLeft(Table1,Offset1); //向左滚动Offset1++; //每次向左移动一个像素Offset1%=66; //越界清零,循环滚动显示}}/*滚动显示速度的英文“SPEED”*/else if(Mode==1){if(OnceFlag){OnceFlag=0;Offset1=0;}if(RollFlag && Offset1<=46) //只向左滚动显示一次,不循环滚动显示{RollFlag=0;MatrixLED_MoveLeft(Table2,Offset1);Offset1++;}else if(Offset1>46) //显示数字“1”之后,自动切换到速度选择界面{Mode=2;Offset1=0;}}/*速度选择界面*/else if(Mode==2){if(OnceFlag){OnceFlag=0;MatrixLED_MoveUp(Table3,Offset2);}if(RollFlag && UpFlag) //如果滚动标志为1,且向上滚动的标志也为1{RollFlag=0;Offset2++; //向上移动一个像素Offset2%=40; //越界清零,总共5个数字,每个数字的高度是8,所以是5*8=40MatrixLED_MoveUp(Table3,Offset2); //更新显示RollCount++;if(RollCount==8) //移动了8个像素后停止移动{RollCount=0;UpFlag=0;Offset2=(Offset2/8)*8; //防止移动到一半的时候按下“上”或“下”按键导致数字没有在点阵屏中间,Offset2的值必须是8的整数倍switch(Offset2/8){case 0:MoveSpeed=50;break; //速度1,0.5s移动1次case 1:MoveSpeed=40;break; //速度2,0.4s移动1次case 2:MoveSpeed=30;break; //速度3,0.3s移动1次case 3:MoveSpeed=20;break; //速度4,0.2s移动1次case 4:MoveSpeed=10;break; //速度5,0.1s移动1次default:break;}}}if(RollFlag && DownFlag) //如果滚动标志为1,且向下滚动的标志也为1{RollFlag=0;if(Offset2==0){Offset2=40;}Offset2--;MatrixLED_MoveUp(Table3,Offset2);RollCount++;if(RollCount==8){RollCount=0;DownFlag=0;Offset2=(Offset2/8)*8;switch(Offset2/8){case 0:MoveSpeed=50;break;case 1:MoveSpeed=40;break;case 2:MoveSpeed=30;break;case 3:MoveSpeed=20;break;case 4:MoveSpeed=10;break;default:break;}}}}/*游戏准备阶段*/else if(Mode==3){if(OnceFlag){OnceFlag=0;//显示挡板MatrixLED_DrawPoint(Player,7);MatrixLED_DrawPoint(Player+1,7);MatrixLED_DrawPoint(Player-1,7);//显示球MatrixLED_ClearPoint(BallX,BallY);MatrixLED_DrawPoint(Player,6);BallX=Player;BallY=6;}}/*游戏进行中*/else if(Mode==4){if(OnceFlag){OnceFlag=0;//游戏初始化GameOverFlag=0;MoveFlag=1;T0Count2=0;Score=0;Direction1=2;Direction2=1;}if(MoveFlag) //如果球移动的标志为真{MoveFlag=0; //球移动的标志清零if(BallY==6) //如果球在从上往下数的第7行{if(BallX!=Player && BallX!=Player-1 && BallX!=Player+1) //且正下方不是挡板{GameOverFlag=1; //则游戏结束}else{Score++; //每接住一次球,分数增加1}}if(GameOverFlag==0) //如果游戏没结束{MatrixLED_ClearPoint(BallX,BallY); //清除球的显示if(BallX==7){Direction1=1;} //如果球向右到了第8列,则球(反弹)改成向左移动if(BallX==0){Direction1=2;} //如果球向左到了第1列,则球(反弹)改成向右移动if(BallY==6){Direction2=1;} //如果球向下到了第7行,则球(反弹)改成向上移动if(BallY==0){Direction2=2;} //如果球向上到了第1行,则球(反弹)改成向下移动/*更新球的位置*/if(Direction1==2){BallX+=1;}else if(Direction1==1){BallX-=1;}if(Direction2==2){BallY+=1;}else if(Direction2==1){BallY-=1;}/*显示球的下一个位置*/MatrixLED_DrawPoint(BallX,BallY);}else //如果游戏结束{Mode=5; //切换到模式5}}}/*游戏结束全屏闪烁*/else if(Mode==5){//在定时器中断中实现全屏闪烁}/*显示得分的英文“SCORE”*/else if(Mode==6){if(OnceFlag){OnceFlag=0;Offset1=0;}if(RollFlag && Offset1<=38) //只滚动显示一次英文“SCORE”{RollFlag=0;MatrixLED_MoveLeft(Table4,Offset1);Offset1++;}else if(Offset1>38) //滚动结束后,自动切换到循环显示得分的模式{Mode=7;OnceFlag=1;} }/*循环显示得分*/else if(Mode==7){if(OnceFlag){OnceFlag=0;Offset1=0;Score--; //计算得分的时候加多了一分,要减去一for(i=0;i<6;i++) //将得分的百位、十位、个位的字模写入数组ScoreShow中{ScoreShow[8+i]=Table5[(Score/100)*6+i]; //百位}for(i=0;i<6;i++){ScoreShow[14+i]=Table5[(Score/10%10)*6+i]; //十位}for(i=0;i<6;i++){ScoreShow[20+i]=Table5[(Score%10)*6+i]; //个位}}if(RollFlag){RollFlag=0;MatrixLED_MoveLeft(ScoreShow,Offset1);Offset1++;Offset1%=26; //循环滚动显示}}}
}/*** 函 数:定时器0中断函数* 参 数:无* 返 回 值:无*/
void Timer0_Routine() interrupt 1
{TL0=0x00; //设置定时初值,定时10ms,12T@11.0592MHzTH0=0xDC; //设置定时初值,定时10ms,12T@11.0592MHzT0Count1++;T0Count2++;T0Count3++;T0Count4++;if(T0Count1>=2) //每隔20ms检测一次按键,且更新挡板的显示{T0Count1=0;/*在中断函数中更新挡板的位置*/ //在主循环中更新显示会有卡顿的现象if(KeyNumber1 && (Mode==3 || Mode==4)) //如果有独立按键按下且处于游戏准备阶段或正在游戏{if(KeyNumber1==1 || KeyNumber1==5) //如果短按K1或长按K1{Player--; //下挡板向左移动一格if(Player<1){Player=1;} //限制范围MatrixLED_ClearPoint(Player+2,7); //更新显示MatrixLED_DrawPoint(Player-1,7); //更新显示if(Mode==3){OnceFlag=1;} //限制范围}if(KeyNumber1==2 || KeyNumber1==6) //如果短按K2或长按K2{Player++; //下挡板向右移动一格if(Player>6){Player=6;}MatrixLED_ClearPoint(Player-2,7);MatrixLED_DrawPoint(Player+1,7);if(Mode==3){OnceFlag=1;}}KeyNumber1=0; //独立按键的键码清零}}if(T0Count2>=MoveSpeed) //控制球移动的速度,MoveSpeed越小,球移动的速度越快{T0Count2=0;MoveFlag=1;}if(T0Count3>=10) //每隔100ms滚动显示一次字母或数字{T0Count3=0;RollFlag=1;}if(T0Count4>=50) //每隔500ms置反FlashFlag{T0Count4=0;FlashFlag=!FlashFlag;}
}/*** 函 数:定时器1中断函数* 参 数:无* 返 回 值:无*/
void Timer1_Routine() interrupt 3
{static unsigned char T1Count;TL1=0x66; //设置定时初值,定时1ms,12T@11.0592MHzTH1=0xFC; //设置定时初值,定时1ms,12T@11.0592MHzif(Mode==5 && FlashFlag){P0=0xFF;} //控制游戏结束后的全屏闪烁else{MatrixLED_Tick();}T1Count++;if(T1Count>=20) //每隔20ms检测一次按键{T1Count=0;Key_Tick();}
}
总结
很简单的一个小游戏,难度不大,可以用来练练手。
相关文章:
基于51单片机和8X8点阵屏、独立按键的单人弹球小游戏
目录 系列文章目录前言一、效果展示二、原理分析三、各模块代码1、8X8点阵屏2、独立按键3、定时器04、定时器1 四、主函数总结 系列文章目录 前言 用的是普中A2开发板,用到板上的独立按键、8X8点阵屏。 【单片机】STC89C52RC 【频率】12T11.0592MHz 效果查看/操作…...
群体智能避障革命:RVO算法在Unity中的深度实践与优化
引言:游戏群体移动的挑战与进化 在《全面战争》中万人战场恢弘列阵,在《刺客信条》闹市里人群自然涌动,这些令人惊叹的场景背后,都离不开一个关键技术——群体动态避障。传统路径规划算法(如A*)虽能解决单…...
Java 实现选择排序:[通俗易懂的排序算法系列之一]
引言 大家好!从今天开始,我计划写一个关于常见排序算法的系列文章,旨在用通俗易懂的方式,结合 Java 代码实现,帮助大家理解和掌握这些基础但非常重要的数据结构与算法知识。 排序是计算机科学中最基本的操作之一&…...
动画过渡设置
使用Animator的Trigger参数 步骤 1:打开 Animator 窗口 确保你的 Sprite 对象已添加 Animator 组件。 在 Unity 编辑器顶部菜单栏,选择 Window > Animation > Animator,打开 Animator 窗口。 步骤 2:创建 Trigger 参数 在…...
【项目管理-高项】学习方法 整体概览
相关文档,希望互相学习,共同进步 风123456789~-CSDN博客 1.背景 📝 软考高项,全称 信息系统项目管理师 ,是软考高级资格项目之一。 本考试考三门科目:综合知识(上午)、案例分析(下午…...
HarmonyOS应用开发者高级-编程题-001
题目一:跨设备分布式数据同步 需求描述 开发一个分布式待办事项应用,要求: 手机与平板登录同一华为账号时,自动同步任务列表任一设备修改任务状态(完成/删除),另一设备实时更新任务数据在设备…...
HarmonyOS-ArkUI Ability进阶系列-UIAbility与各类Context
UIAbility及相关类关系 一个模块编译的时候会出一个HAP包, 每一个HAP包在运行时都对应一个AbilityStage。 AbilityStage持有一个AbilityStageContext一个APP, 有时候会有很多个HAP包, 至少一个。 一个APP运行时,对应的是我们的App…...
接口并行执行且流式顺序输出的解决方案
接口并行执行且流式顺序输出的解决方案: import asyncio from aiotas_agi2all_llms_utils.output_answer_from_ask_question_results import (reasoning_model_ask_question, ) import os from aiotas_agi2all_llms_utils.logging_utils import create_logger import uuid fr…...
浅谈AI - DeepSpeed - 单卡慎用!
前言 曾在游戏世界挥洒创意,也曾在前端和后端的浪潮间穿梭,如今,而立的我仰望AI的璀璨星空,心潮澎湃,步履不停!愿你我皆乘风破浪,逐梦星辰! 简介 Deepspeed 的 ZeRO(Ze…...
Java Web从入门到精通:全面探索与实战(一)
目录 引言:开启 Java Web 之旅 一、Java Web 基础概念大揭秘 1.1 什么是 Java Web 1.2 Java Web 的优势剖析 1.3 Java Web 相关核心概念详解 二、搭建 Java Web 开发环境:步步为营 2.1 所需软件大盘点 2.2 软件安装与配置全流程 三…...
5G从专家到小白
文章目录 第五代移动通信技术(5G)简介应用场景 数据传输率带宽频段频段 VS 带宽中低频(6 GHz以下):覆盖范围广、穿透力强高频(24 GHz以上):满足在热点区域提升容量的需求毫米波热点区…...
leetcode111 二叉树的最小深度
相对于 104.二叉树的最大深度 ,本题还也可以使用层序遍历的方式来解决,思路是一样的。 最小深度的定义:从根节点到最近叶子节点的最短路径上的节点数量。 特别注意: 如果一个子树不存在,就不能用它来计算深度&#x…...
算法设计学习10
实验目的及要求: 本查找实验旨在使学生深入了解不同查找算法的原理、性能特征和适用场景,培养其在实际问题中选择和应用查找算法的能力。通过实验,学生将具体实现多种查找算法,并通过性能测试验证其在不同数据集上的表现ÿ…...
数字统计题解
题目理解 题目要求计算所有不大于 N 的非负整数中数字 D 出现的总次数。例如,当 D1 且 N12 时,数字1出现在1、10、11(两次)、12中,共5次。 输入输出分析 输入格式: 两个正整数 D 和 N,其中1≤…...
eclipse导入工程提示Project has no explicit encoding set
eclipse导入工程提示Project has no explicit encoding set-CSDN博客...
【网络安全论文】筑牢局域网安全防线:策略、技术与实战分析
【网络安全论文】筑牢局域网安全防线:策略、技术与实战分析 简述一、引言1.1 研究背景1.2 研究目的与意义1.3 国内外研究现状1.4 研究方法与创新点二、局域网网络安全基础理论2.1 局域网概述2.1.1 局域网的定义与特点2.1.2 局域网的常见拓扑结构2.2 网络安全基本概念2.2.1 网络…...
JVM虚拟机篇(五):深入理解Java类加载器与类加载机制
深入理解Java类加载器与类加载机制 深入理解Java类加载器与类加载机制一、引言二、类加载器2.1 类加载器的定义2.2 类加载器的分类2.2.1 启动类加载器(Bootstrap ClassLoader)2.2.2 扩展类加载器(Extension ClassLoader)2.2.3 应用…...
纯个人整理,蓝桥杯使用的算法模板day4(图论 最小生成树问题),手打个人理解注释,超全面,且均已验证成功(附带详细手写“模拟流程图”,全网首个
目录 最小生成树Prim代码模拟流程图 kruskal代码 代码对应实现案例 最小生成树 最小生成树:在无向图中求一棵树(n-1条边,无环,连通所有点),而且这棵树的边权和最小 (ps:可能结果不止…...
学习笔记,DbContext context 对象是保存了所有用户对象吗
DbContext 并不会将所有用户对象保存在内存中: DbContext 是 Entity Framework Core (EF Core) 的数据库上下文,它是一个数据库访问的抽象层它实际上是与数据库的一个连接会话,而不是数据的内存缓存当您通过 _context.Users 查询数据时&…...
Kafka 和 Flink的讲解
一、Kafka:分布式消息队列 1. 核心概念 角色:Kafka 是一个分布式、高吞吐量的消息队列(Pub-Sub 模型),用于实时传输数据流。关键术语: Producer(生产者&…...
Kafka 高吞吐量的原因是什么?
Kafka 的高吞吐量是它成为“数据中枢”的关键特性之一,这背后是多个技术设计的巧妙配合。下面我给你整理一下 Kafka 高吞吐量的主要原因,通俗又系统。 ✅ 1. 顺序写磁盘(磁盘也能飞) Kafka 的消息写入是追加到日志末尾ÿ…...
基于Python+Flask的服装零售商城APP方案,用到了DeepSeek AI、个性化推荐和AR虚拟试衣功能
首先创建项目结构: fashion_store/ ├── backend/ │ ├── app/ │ │ ├── __init__.py │ │ ├── models/ │ │ ├── routes/ │ │ ├── services/ │ │ └── utils/ │ ├── config.py │ ├── requirements.t…...
26.[MRCTF2020]Transform 1
打开文件是可执行程序.exe,打开看一下,顺便拖入ExeinfoPE 查询一下基本信息。如图。 无壳,且是64-bit,打开执行文件也没有什么特别的信息。那就 IDA-x64 分析吧。 🆗,简单的一个加密,我们直接逆…...
LeetCode-98. 验证二叉搜索树
一、题目 给定一个二叉树,判断其是否是一个有效的二叉搜索树。假设一个二叉搜索树具有如下特征: 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值; 若它的右子树不空,则右子树上所有结点的值均大于它的…...
【LeetCode Solutions】LeetCode 146 ~ 150 题解
CONTENTS LeetCode 146. LRU 缓存(中等)LeetCode 147. 对链表进行插入排序(中等)LeetCode 148. 排序链表(中等)LeetCode 149. 直线上最多的点数(困难)LeetCode 150. 逆波兰表达式求值…...
leetcode二叉树刷题调试不方便的解决办法
1. 二叉树不易构建 在leetcode中刷题时,如果没有会员就需要将代码拷贝到本地的编译器进行调试。但是leetcode中有一类题可谓是毒瘤,那就是二叉树的题。 要调试二叉树有关的题需要根据测试用例给出的前序遍历,自己构建一个二叉树,…...
【家政平台开发(16)】消息通知系统设计:搭建高效沟通桥梁
本【家政平台开发】专栏聚焦家政平台从 0 到 1 的全流程打造。从前期需求分析,剖析家政行业现状、挖掘用户需求与梳理功能要点,到系统设计阶段的架构选型、数据库构建,再到开发阶段各模块逐一实现。涵盖移动与 PC 端设计、接口开发及性能优化…...
AI比人脑更强,因为被植入思维模型【44】成长破圈思维
giszz的理解:芒格说,不懂不投。我们一生都在追求破圈,突破本我,突破舒适圈、恐惧圈、学习圈、成长圈、自在圈,可是我们真正能懂的知识有实际真的太少了。这个思维模型给我的启迪,一是要破圈,二是…...
【JavaWeb-Spring boot】学习笔记
目录 <<回到导览Spring boot1. http协议1.1.请求协议1.2.响应协议 2.Tomcat2.1.请求2.1.1.apifox2.1.2.简单参数2.1.3.实体参数2.1.4.数组集合参数2.1.5.日期参数2.1.6.(重点)JSON参数2.1.7.路径参数 2.2.响应2.3.综合练习 3.三层架构3.1.三层拆分3.2.分层解耦3.3.补充 &…...
基于GitLab+Jenkins的持续集成实践指南
架构设计原则 分层自动化策略 基础层: 代码提交触发自动构建(100%自动化)中间层: 制品生成与验证(自动化+人工审核)发布层: 环境部署(受控手动触发)工具定位矩阵 工具核心职责关键优势GitLab源码管理+MR流程精细的权限控制Jenkins流水线编排+任务调度插件生态丰富Nexus制…...
用HTML.CSS.JavaScript实现一个贪吃蛇小游戏
目录 一、引言二、实现思路1. HTML 结构2. CSS 样式3. JavaScript 逻辑 三、代码实现四、效果展示 一、引言 贪吃蛇是一款经典的小游戏,曾经风靡一时。今天,我们将使用 HTML、CSS 和 JavaScript 来实现一个简单的贪吃蛇小游戏。通过这个项目,…...
医疗思维图与数智云融合:从私有云到思维图的AI架构迭代(代码版)
医疗思维图作为AI架构演进的重要方向,其发展路径从传统云计算向融合时空智能、大模型及生态开放的“思维图”架构迭代,体现了技术与场景深度融合的趋势。 以下是其架构迭代的核心路径与关键特征分析: 一、从“智慧云”到“思维图”的架构演进逻辑 以下是针对医疗信息化领域…...
Kafka 中,为什么同一个分区只能由消费者组中的一个消费者消费?
在 Kafka 中,同一个分区只能由消费者组中的一个消费者消费,这是 Kafka 的设计决策之一,目的是保证消息的顺序性和避免重复消费。这背后有几个关键的原因: 1. 保证消息顺序性 Kafka 中的每个 分区(Partitionÿ…...
Kafka 中的批次
在 Kafka 中,批次(Batch) 是生产者发送消息的一个重要概念。它对 Kafka 的性能、吞吐量、延迟等有很大影响。批量处理可以使消息发送更高效,减少网络往返和磁盘写入的开销。 下面我将详细解释 Kafka 中的批次机制,包括…...
《UNIX网络编程卷1:套接字联网API》第7章:套接字选项深度解析
《UNIX网络编程卷1:套接字联网API》第7章:套接字选项深度解析 一、套接字选项核心原理 1.1 选项层级体系 套接字选项按协议层级划分(图1): SOL_SOCKET:通用套接字层选项IPPROTO_IP:IPv4协议层…...
使用 pytest-xdist 进行高效并行自化测试
pytest-xdist 是 pytest 的一个扩展插件,主要用于实现测试用例的并行执行和分布式测试。通过利用多进程或者多机分布式的方式,pytest-xdist 能够显著缩短测试执行时间,提升持续集成(CI)流程的效率。 在自动化测试中&a…...
谈谈策略模式,策略模式的适用场景是什么?
一、什么是策略模式? 策略模式(Strategy Pattern)属于行为型设计模式。核心思路是将一组可替换的算法封装在独立的类中,使它们可以在运行时动态切换,同时使客户端代码与具体算法解耦。它包含三个…...
网络安全防御核心原则与实践指南
一、四大核心防御原则 A. 纵深防御原则(Defense in Depth) 定义:通过在多个层次(如网络、系统、应用、数据)设置互补的安全措施,形成多层次防护体系。 目的:防止单一漏洞导致整体安全失效&…...
动态规划2——斐波那契数列模型——三步问题
1.题目 三步问题。有个小孩正在上楼梯,楼梯有 n 阶台阶,小孩一次可以上 1 阶、2 阶或 3 阶。实现一种方法,计算小孩有多少种上楼梯的方式。结果可能很大,你需要对结果模 1000000007。 示例 1: 输入:n 3 …...
周末总结(2024/04/05)
工作 人际关系核心实践: 要学会随时回应别人的善意,执行时间控制在5分钟以内 坚持每天早会打招呼 遇到接不住的话题时拉低自己,抬高别人(无阴阳气息) 朋友圈点赞控制在5min以内,职场社交不要放在5min以外 职场的人际关系在面对利…...
常见的图像生成算法
综合技术原理、优化方向和应用场景,结合经典模型与前沿进展进行分述: 一、经典生成模型 1. 生成对抗网络(GAN) 原理:由生成器(Generator)和判别器(Discriminator)通过…...
PE结构(十五)系统调用与函数地址动态寻找
双机调试 当需要分析一个程序时,这个程序一定是可以调试的,操作系统也不例外。在调试过程中下断点是很重要的 当我们对一个应用程序下断点时,应用程序是挂起的。但当我们对操作系统的内核程序下断点时,被挂起的不是内核程序而是…...
verilog状态机思想编程流水灯
目录 一、状态机1. 状态机基本概念2. 状态机类型3. Verilog 状态机设计要点 二、状态机实现一个1s流水灯三、DE2-115实物演示 一、状态机 1. 状态机基本概念 状态机(Finite State Machine, FSM)是数字电路设计中用于描述系统状态转换的核心组件&#x…...
Java实现N皇后问题的双路径探索:递归回溯与迭代回溯算法详解
N皇后问题要求在NN的棋盘上放置N个皇后,使得她们无法互相攻击。本文提供递归和循环迭代两种解法,并通过图示解释核心逻辑。 一、算法核心思想 使用回溯法逐行放置皇后,通过冲突检测保证每行、每列、对角线上只有一个皇后。发现无效路径时回退…...
#SVA语法滴水穿石# (000)断言基本概念和背景
一、前言 随着数字电路规模越来越大、设计越来越复杂,使得对设计的功能验证越来越重要。首先,我们要明白为什么要对设计进行验证?验证有什么作用?例如,在用FPGA进行设计时,我们并不能确保设计出来的东西没有功能上的漏洞,因此在设计后我们都会对其进行验证仿真。换句话说…...
【Android Studio 下载 Gradle 失败】
路虽远行则将至,事虽难做则必成 一、事故现场 下载Gradle下载不下来,没有Gradle就无法把项目编译为Android应用。 二、问题分析 观察发现下载时长三分钟,进度条半天没动,说明这个是国外的东西,被墙住了,需…...
贪心算法之Huffman编码
1. 算法推理 Huffman 编码的目标是为给定字符构造一种前缀码,使得整体编码长度最短。基本思想是: 贪心选择:每次选择频率最小的两个节点合并。合并后的节点的权值为两个子节点权值之和,代表这部分子树出现的总频率。 局部最优导…...
Flask学习笔记 - 表单
表单处理 基本表单处理:使用 request.form 获取表单数据。使用 Flask-WTF:结合 WTForms 进行表单处理和验证,简化表单操作。表单验证:使用验证器确保表单数据的有效性。文件上传:处理文件上传和保存文件。CSRF 保护&a…...
指针的补充(用于学习笔记的记录)
1.指针基础知识 1.1 指针变量的定义和使用 指针也是一种数据类型,指针变量也是一种变量 指针变量指向谁,就把谁的地址赋值给指针变量 #include<stdio.h>int main() {int a 0;char b 100;printf("%p,%p \n", &a,&b); // …...
【mongodb】mongodb的字段类型
目录 1. 基本数据类型1.1 String1.2 Number1.3 Boolean1.4 Date1.5 Null1.6 ObjectId1.7 Array1.8 Binary Data1.9 Object 2. 特殊数据类型2.1 Regular Expression2.2 JavaScript2.3 Symbol2.4 Decimal1282.5 Timestamp2.6 MinKey/MaxKey2.7 DBPointer 3. 常用字段类型示例4. 注…...