位置: IT常识 - 正文

使用STM32F103ZE开发贪吃蛇游戏(stm32f103教程)

编辑:rootadmin
使用STM32F103ZE开发贪吃蛇游戏

目录

前言

一、设置FreeROTS用户任务

        (1)事件event任务

        (2)按键输入方向控制任务

        (3)果实食物任务

        (4)显示任务函数

        (3)开始任务

二、主函数

三、ADC采样

四、效果展示


前言

推荐整理分享使用STM32F103ZE开发贪吃蛇游戏(stm32f103教程),希望有所帮助,仅作参考,欢迎阅读内容。

文章相关热门搜索词:stm32开发方法,stm32开发步骤,stm32f103zet,stm32f103zet,stm32f103教程,stm32f103开发步骤,stm32f103开发步骤,stm32f1开发指南,内容如对您有帮助,希望把文章链接给更多的朋友!

        网络上贪吃蛇游戏的开源资料已经很丰富了,但是详细讲解代码的很少,所以我打算取之开源,回馈于开源,帮助大家能够更好的完成这款很经典的游戏项目。

        为了能够更好的实时处理贪吃蛇的各项任务,如:贪吃蛇任务,果实任务,显示任务等;所以对原始代码上了FreeROTS操作系统。

        这里我就不详细介绍FreeROTS操作系统了,以后我会单独出一期FreeROTS的文章,大家想了解的话,有一份《FreeROTS内核使用指南》可以详读。 

         如果大家英语好的话,推荐读英文版,会少一些翻译上的错误。

        实验平台:STM32F103ZE开发板,5个独立按键

独立按键与开发板连接:

KEYUP→F0

KEYDOWN→F1

KEYLEFT→F2

KEYRIGHT→F3

STOP→F4

        贪吃蛇项目概述:

        贪吃蛇也叫“移动的链表”,先将不同任务所需要的参数组成结构体,在用指针不断调用,还得用TFTLCD进行显示,可以参考我以前写过的博客:

学习记录:调用TFTLCD液晶屏_lcd_shownum_Bitter tea seeds的博客-CSDN博客

使用STM32F103ZE开发贪吃蛇游戏(stm32f103教程)

        废话不多说,代码(分析)来一波。

一、设置FreeROTS用户任务

        一个任务就是一个线程,由于操作系统管理不同的任务,不同的任务分配在不同的内存块中,所以一开始要给不同的任务设置优先级并为他们分配堆栈空间。被挂起的任务被送回堆栈,就绪任务和运行任务从栈中恢复被送入寄存器。

#ifndef __MY_TASK_H#define __MY_TASK_H #include "FreeRTOS.h" #include "task.h"//用户任务//任务优先级#define EVENT_TASK_PRIO7//任务堆栈大小#define EVENT_STK_SIZE 128//任务句柄TaskHandle_t EVENTTask_Handler;//任务函数void event_task(void *pvParameters);//任务优先级#define KEY_TASK_PRIO6//任务堆栈大小#define KEY_STK_SIZE 128//任务句柄TaskHandle_t KEYTask_Handler;//任务函数void key_task(void *pvParameters);//任务优先级#define APPLE_TASK_PRIO5//任务堆栈大小#define APPLE_STK_SIZE 128 //任务句柄TaskHandle_t APPLETask_Handler;//任务函数void apple_task(void *pvParameters);//任务优先级#define SNAKE_TASK_PRIO4//任务堆栈大小#define SNAKE_STK_SIZE 128 //任务句柄TaskHandle_t SNAKETask_Handler;//任务函数void snake_task(void *pvParameters);//任务优先级#define DISPLAY_TASK_PRIO3//任务堆栈大小#define DISPLAY_STK_SIZE 128//任务句柄TaskHandle_t DISPLAYTask_Handler;//任务函数void display_task(void *pvParameters);//任务优先级#define LED_TASK_PRIO2//任务堆栈大小#define LED_STK_SIZE 128//任务句柄TaskHandle_t LEDTask_Handler;//任务函数void led_task(void *pvParameters);//任务优先级#define START_TASK_PRIO1//任务堆栈大小#define START_STK_SIZE 128 //任务句柄TaskHandle_t StartTask_Handler;//任务函数void start_task(void *pvParameters);#endif         (1)事件event任务

        这是最重要的任务,它负责数据处理,所以得等其他任务完成之后,才轮到它来执行,它的优先级最小,首先设置一个死循环,判断游戏是否正常运行,如果正常运行在判断游戏是否暂停,都没有我们则对按键进行检测,根据按键按下的情况对蛇头坐标进行更改,坐标根据TFTLCD分辨率进行设置,更改完蛇头坐标,对蛇尾坐标进行保存,在进行判断,如果坐标和果实坐标相同的话,蛇的长度加1,果实消失,使能食物函数生成食物,使能LCD进行显示,如果游戏结束,则返回游戏结束函数。

        怎么让蛇的移速随着时间的变化越来越快?

我们可以初始化蛇的速度变量为一个定值,然后通过除以蛇的移速设置延时函数,来控制事件任务执行时间的间隔,随着不断调用蛇的移速,定值不断变大,延时函数时间的间隔边长,任务处理的时间间隔变长,显示出来蛇的移速变快。

void event_task(void *pvParameters){while(1){if(event.GameSta==ON)//如果游戏正常则继续{if(event.Process==ON)//如果没有暂停则继续{switch(event.Direction)//检测按键情况,根据方向调整蛇头坐标{case UP:{snake.firsty-=1;if(snake.firsty>GAME_YPART-1){snake.firsty=GAME_YPART-1;}}break;case DOWN:{snake.firsty+=1;if(snake.firsty>GAME_YPART-1){snake.firsty=0;}}break;case LEFT:{snake.firstx-=1;if(snake.firstx>GAME_XPART-1){snake.firstx=GAME_XPART-1;}}break;case RIGHT:{snake.firstx+=1;if(snake.firstx>GAME_XPART-1){snake.firstx=0;}}break;}snake.lastx=snake_axis[0].x;//保存下蛇尾坐标snake.lasty=snake_axis[0].y;if(snake.firstx==apple.x&&snake.firsty==apple.y)//如果此时的坐标与食物坐标相同{event.AppleSta=OFF;//食物被吃掉snake.energybuf+=apple.energy;//蛇的能量加一vTaskResume(APPLETask_Handler);//使能生成食物函数}vTaskResume(DISPLAYTask_Handler);//使能显示函数}}else GameOver();//如果游戏为结束状态则游戏结束delay_ms(1000/snake.speed);//按照蛇的速度调整此核心数据处理函数的时间间隔}}        (2)按键输入方向控制任务

        按键任务通过switch判断语句实现,需要注意的是,我们按的方向如果是蛇移动的方向的反方向,是不能响应的,因为蛇不能有两个脑袋吧?🐶然后就是从结构体中用指针调用参数使用。

void key_task(void *pvParameters){u8 key; while(1) {key=KEY_PLAY_Scan(0);switch(key){case KEY_UP_PRES:{if(event.Direction!=DOWN)event.Direction=UP;}break;case KEY_DOWN_PRES:{if(event.Direction!=UP)event.Direction=DOWN;}break;case KEY_LEFT_PRES:{if(event.Direction!=RIGHT)event.Direction=LEFT;}break;case KEY_RIGHT_PRES:{if(event.Direction!=LEFT)event.Direction=RIGHT;}break;case KEY_PASS_PRES://按下切换暂停/继续状态{event.Process=!event.Process;}break;} delay_ms(20);//每20ms响应一次 }}        (3)果实食物任务

        首先,果实的分布是随机的,所以,通过STM32F1自带的一个ADC采样随机获得ADC的值作为果实,将模拟量转换为数字量,如果在上位机上编写的话,可以使用时间戳来作为随机值。

        得到随机果实的坐标之后,我们还要保证食物的坐标不能出现在蛇的身上。蛇的坐标也是通过LCD分辨率来进行设置的。

void apple_task(void *pvParameters){u16 flag,i; while(1) {flag=1;while(flag){flag=0;apple.x=Get_Rand()%(u16)(GAME_XPART);apple.y=Get_Rand()%(u16)(GAME_YPART);for(i=0;i<snake.length;i++) {if(snake_axis[i].x==apple.x&&snake_axis[i].y==apple.y){flag++;}}}Display(apple.x,apple.y,RED);vTaskSuspend(APPLETask_Handler); }}        (4)显示任务函数

        对此任务,我们首先得知道自己的LCD型号id然后根据自己LCD的型号进行驱动程序的编写。它的任务是显示出来蛇的身子。

        Display显示出蛇头,如果果实坐标与蛇头坐标相同,蛇身长度+1,速度+1,然后更新蛇头坐标,保存蛇尾坐标,期间检查蛇头有没有碰到自己,遍历蛇身坐标是否与蛇头坐标相同,如果碰到了,游戏结束。

void display_task(void *pvParameters){u16 i; while(1) {Display(snake.firstx,snake.firsty,RED);//显示蛇头 if(snake.energybuf==0) {Display(snake.lastx,snake.lasty,WHITE);for(i=0;i<snake.length-1;i++){snake_axis[i].x=snake_axis[i+1].x;snake_axis[i].y=snake_axis[i+1].y;}}else //如果吃到了食物{snake.energybuf--;snake.length++; //if(snake.length%2==0)snake.speed++;snake.speed++; }snake_axis[snake.length-1].x=snake.firstx;snake_axis[snake.length-1].y=snake.firsty;for(i=0;i<snake.length-1;i++){if(snake_axis[i].x==snake.firstx&&snake_axis[i].y==snake.firsty){event.GameSta=OFF; }}vTaskSuspend(DISPLAYTask_Handler); }}        (3)开始任务

        使用操作系统,线程进入临界区,为了处理临界区的代码,需要关闭线程中断,处理完毕后在开启中断,这是为了避免同时有其他任务或中断服务ISR进入临界区代码。

void start_task(void *pvParameters){ taskENTER_CRITICAL(); xTaskCreate((TaskFunction_t )snake_task, (const char* )"snake_task", (uint16_t )SNAKE_STK_SIZE, (void* )NULL, (UBaseType_t )SNAKE_TASK_PRIO, (TaskHandle_t* )&SNAKETask_Handler); //创建食物任务 xTaskCreate((TaskFunction_t )apple_task, (const char* )"apple_task", (uint16_t )APPLE_STK_SIZE, (void* )NULL, (UBaseType_t )APPLE_TASK_PRIO, (TaskHandle_t* )&APPLETask_Handler); //创建事件任务 xTaskCreate((TaskFunction_t )event_task, (const char* )"event_task", (uint16_t )EVENT_STK_SIZE, (void* )NULL, (UBaseType_t )EVENT_TASK_PRIO, (TaskHandle_t* )&EVENTTask_Handler); //创建显示任务 xTaskCreate((TaskFunction_t )display_task, (const char* )"display_task", (uint16_t )DISPLAY_STK_SIZE, (void* )NULL, (UBaseType_t )DISPLAY_TASK_PRIO, (TaskHandle_t* )&DISPLAYTask_Handler); //创建闪烁任务 xTaskCreate((TaskFunction_t )led_task, (const char* )"led_task", (uint16_t )LED_STK_SIZE, (void* )NULL, (UBaseType_t )LED_TASK_PRIO, (TaskHandle_t* )&LEDTask_Handler); //创建输入任务 xTaskCreate((TaskFunction_t )key_task, (const char* )"key_task", (uint16_t )KEY_STK_SIZE, (void* )NULL, (UBaseType_t )KEY_TASK_PRIO, (TaskHandle_t* )&KEYTask_Handler); vTaskDelete(StartTask_Handler); taskEXIT_CRITICAL(); }二、主函数

        操作系统与裸机开发的一个区别就是,少了那个while(1)死循环,改成了任务调度

int main(void) { delay_init(); NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); Rand_Adc_Init();uart_init(115200); LED_Init(); KEY_PLAY_Init();LCD_Init();DisplayInit();Snake_Init(&snake);Apple_Init(&apple);Event_Init(&event);xTaskCreate((TaskFunction_t )start_task, (const char* )"start_task", (uint16_t )START_STK_SIZE, (void* )NULL, (UBaseType_t )START_TASK_PRIO, (TaskHandle_t* )&StartTask_Handler); vTaskStartScheduler(); }三、ADC采样

ADC几个比较重要的参数:

(1)测量范围:测量范围对于 ADC 来说就好比尺子的量程,ADC 测量范围决定了你外接的设备其信号输出电压范围,不能超过 ADC 的测量范围(比如,STM32系列的 ADC 正常就不能超过3.3V)。

(2)分辨率:假如 ADC 的测量范围为 0-5V,分辨率设置为12位,那么我们能测出来的最小电压就是 5V除以 2 的 12 次方,也就是 5/4096=0.00122V。很明显,分辨率越高,采集到的信号越精确,所以分辨率是衡量 ADC 的一个重要指标。

(3)采样时间:当 ADC 在某时刻采集外部电压信号的时候,此时外部的信号应该保持不变,但实际上外部的信号是不停变化的。所以在 ADC 内部有一个保持电路,保持某一时刻的外部信号,这样 ADC 就可以稳定采集了,保持这个信号的时间就是采样时间。

(4)采样率:也就是在一秒的时间内采集多少次。很明显,采样率越高越好,当采样率不够的时候可能会丢失部分信息,所以 ADC 采样率是衡量 ADC 性能的另一个重要指标

#include "rand.h"//使用ADC产生16位随机数void Rand_Adc_Init(void){ ADC_InitTypeDef ADC_InitStructure; GPIO_InitTypeDef GPIO_InitStructure;RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA |RCC_APB2Periph_ADC1, ENABLE ); //使能ADC1通道时钟RCC_ADCCLKConfig(RCC_PCLK2_Div6); //设置ADC分频因子6 72M/6=12,ADC最大时间不能超过14M//PA1 作为模拟通道输入引脚 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;//模拟输入引脚GPIO_Init(GPIOA, &GPIO_InitStructure);ADC_DeInit(ADC1); //复位ADC1,将外设 ADC1 的全部寄存器重设为缺省值ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;//ADC工作模式:ADC1和ADC2工作在独立模式ADC_InitStructure.ADC_ScanConvMode = DISABLE;//模数转换工作在单通道模式ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;//模数转换工作在单次转换模式ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;//转换由软件而不是外部触发启动ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;//ADC数据右对齐ADC_InitStructure.ADC_NbrOfChannel = 1;//顺序进行规则转换的ADC通道的数目ADC_Init(ADC1, &ADC_InitStructure);//根据ADC_InitStruct中指定的参数初始化外设ADCx的寄存器 ADC_Cmd(ADC1, ENABLE);//使能指定的ADC1ADC_ResetCalibration(ADC1);//使能复位校准 while(ADC_GetResetCalibrationStatus(ADC1));//等待复位校准结束ADC_StartCalibration(ADC1); //开启AD校准while(ADC_GetCalibrationStatus(ADC1)); //等待校准结束} //获得ADC值//ch:通道值 0~3u16 Get_Adc(u8 ch) { //设置指定ADC的规则组通道,一个序列,采样时间ADC_RegularChannelConfig(ADC1, ch, 1, ADC_SampleTime_1Cycles5 );//ADC1,ADC通道,采样时间为239.5周期 ADC_SoftwareStartConvCmd(ADC1, ENABLE);//使能指定的ADC1的软件转换启动功能while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC ));//等待转换结束return ADC_GetConversionValue(ADC1);//返回最近一次ADC1规则组的转换结果}u16 Get_Rand(void){u16 randnum=Get_Adc(ADC_Channel_1)&0x0001,i;for(i=0;i<15;i++){randnum<<=1;randnum+=Get_Adc(ADC_Channel_1)&0x0001;}return randnum;} 四、效果展示

STM32精英开发板制作贪吃蛇游戏

本文链接地址:https://www.jiuchutong.com/zhishi/298889.html 转载请保留说明!

上一篇:chat gpt 国内版免费网站(chat gpt 国内版免费手机)

下一篇:终于体验了一下ChatGPT(体验了一回)

  • vivo NEX 3s跑分成绩是多少呢

    vivo NEX 3s跑分成绩是多少呢

  • 手机剪卡剪坏了怎么办(手机卡用剪刀剪坏了)

    手机剪卡剪坏了怎么办(手机卡用剪刀剪坏了)

  • 苹果通话黑名单在哪查看(苹果通话黑名单设置在哪里)

    苹果通话黑名单在哪查看(苹果通话黑名单设置在哪里)

  • 常说的2g3g4g手机的g指的是什么(手机2g3g4g是什么意思)

    常说的2g3g4g手机的g指的是什么(手机2g3g4g是什么意思)

  • mate30开启5g没显示(mate30打开5g开关为啥没5g)

    mate30开启5g没显示(mate30打开5g开关为啥没5g)

  • 候补以后还能抢票吗(候补购票后还能抢当天其他车的票吗)

    候补以后还能抢票吗(候补购票后还能抢当天其他车的票吗)

  • 电脑双引号只能打一边(电脑上的双引号只能打一半是怎么回事)

    电脑双引号只能打一边(电脑上的双引号只能打一半是怎么回事)

  • 为什么腾讯游戏人脸识别要搞很多次(为什么腾讯游戏这么火)

    为什么腾讯游戏人脸识别要搞很多次(为什么腾讯游戏这么火)

  • am3和am3+ cpu能通用吗(am3和am2+ cpu能通用吗)

    am3和am3+ cpu能通用吗(am3和am2+ cpu能通用吗)

  • 华为mate30怎么打开悬浮球(华为mate30怎么打开双系统)

    华为mate30怎么打开悬浮球(华为mate30怎么打开双系统)

  • wan端口是什么(wan端口是哪个)

    wan端口是什么(wan端口是哪个)

  • 华为mate30德国发布会时间(华为mate304g发售价)

    华为mate30德国发布会时间(华为mate304g发售价)

  • 苹果xsmax面容识别不了怎么办(苹果xsmax面容识别不了)

    苹果xsmax面容识别不了怎么办(苹果xsmax面容识别不了)

  • iphonex变黑白如何还原(iphonex变黑白如何还原为彩色)

    iphonex变黑白如何还原(iphonex变黑白如何还原为彩色)

  • 手机盒子有用吗(手机盒子妙用)

    手机盒子有用吗(手机盒子妙用)

  • 手机邮箱格式怎么写(手机邮箱格式怎么写QQ)

    手机邮箱格式怎么写(手机邮箱格式怎么写QQ)

  • 换5g要换手机卡吗(换5g卡需要换手机号码吗)

    换5g要换手机卡吗(换5g卡需要换手机号码吗)

  • 空间互动4次是什么意思(空间互动4次是啥意思)

    空间互动4次是什么意思(空间互动4次是啥意思)

  • qq黄钻怎么看全部访客(从哪里看qq黄钻剩余天数)

    qq黄钻怎么看全部访客(从哪里看qq黄钻剩余天数)

  • 金税盘锁死如何解锁(金税盘锁住了怎么办)

    金税盘锁死如何解锁(金税盘锁住了怎么办)

  • 手机号能查出身份证吗(手机号能查出身份证)

    手机号能查出身份证吗(手机号能查出身份证)

  • 美易自动扣费怎么退回(美易自动续费)

    美易自动扣费怎么退回(美易自动续费)

  • Win10无法登录微软账号错误代码0x80190001的解决方法(win10无法登录微信)

    Win10无法登录微软账号错误代码0x80190001的解决方法(win10无法登录微信)

  • Windows10设备如何使用wifi direct(windows10设备如何打开蓝牙)

    Windows10设备如何使用wifi direct(windows10设备如何打开蓝牙)

  • linux怎么修改账户头像? linux修改登录用户头像的方法(Linux怎么修改账户名)

    linux怎么修改账户头像? linux修改登录用户头像的方法(Linux怎么修改账户名)

  • 拔罐的好处和坏处(拔罐的好处和坏处除湿)

    拔罐的好处和坏处(拔罐的好处和坏处除湿)

  • MSN中国男人频道采集规则For DedeCMS v5.5(中国男人百度百科)

    MSN中国男人频道采集规则For DedeCMS v5.5(中国男人百度百科)

  • 建筑公司分包项目违法吗
  • 发票冲红重开,重开时是按新税率还是旧税率
  • 税金及附加减半征收政策2022最新
  • 个人房产租金收入如何纳税申报
  • 门店活动经费计入什么
  • 预收账款开票怎么做账
  • 复利现值和复利终值的计算
  • 不动产进项税额抵扣最新通知2023
  • 销售货物并收取运费税率
  • 投标保证金利息计入什么科目
  • 材料存货的期末计量有何特点
  • 固定资产暂估入账条件
  • 税控盘怎么打票
  • 印花税是不是不用计提
  • 超出发票使用范围
  • 利润表中所得税费用为负数是什么意思
  • 增值税还原税负率一般控制在多少?
  • 延期申报的程序
  • 个人所得税申报晚了一天会干什么
  • 净利润怎么计入所有者权益
  • 按照税收一般规定计算的折旧摊销金额
  • 购买方现金折扣计入什么科目
  • 挂错科目怎么调整
  • mac安装软件提示需要更高版本
  • 信息化投入包括什么投入和什么投入
  • 应收账款记账凭证如何填写
  • mac big sur 菜单栏
  • Windows 11 Build 22000.176 (KB5006050) 更新推送(附更新内容+安装)
  • 赡养老人支出如果有四个子女都要填吗
  • PHP:stream_context_get_options()的用法_Stream函数
  • 怎么做合同
  • 逾期利息算法
  • 拉姆绍在哪里
  • element-ui table使用type=‘selection‘复选框全禁用-全选禁用
  • ajax 返回
  • 股东以固定资产入股会计处理
  • 微信账单可以打清单吗
  • 安装运行所需环境怎么填
  • 有关商品房质量的投诉,这些年一直
  • 存货盘盈盘亏会涉及到其他应收款吗
  • 减免增值税优惠政策
  • 物品登记制度
  • 用于维修安装服务的工具
  • 预付房租如何做分录
  • 车辆保险费印花税计税金额含进项税吗
  • 其他应付款转出的会计分录
  • 关联方往来款如何处理
  • 公司注销后退资需要交税吗
  • 水利工程施工税率是多少
  • 企业的其他应付款增加了说明什么
  • 交存超库存现金
  • 怎么辨别定额发票是什么项目啊
  • 间接费用的分配公式
  • 会计从业资格证取消了吗
  • 绿色安装包怎么卸载
  • mybatis怎么搭建
  • ubuntu14.04安装vim
  • mac os 应用
  • centos 拷贝
  • 硬盘安装64位win8.1/win8或win7操作系统图文教程
  • win10磁盘占用100%卡死
  • ubuntu flash player
  • windows8 enterprise
  • debian 单网卡设置双ip
  • windows8怎么搜索文件
  • window10系统连接wifi
  • linux内核文件夹
  • 铁嘴银牙剧情简介
  • react-native-navigation
  • android5种布局
  • Android游戏开发读后感
  • Androidannotation使用之@Rest获取资源及用户登录验证(一)
  • js对象用法
  • 安卓仿平板
  • 国家税务局级别排名
  • 国税局云南省税务局
  • 怎么查询个人所得税申报成功
  • 手机微信怎么预约
  • 慰问金能直接转账吗
  • 放管服中的服的深层含义
  • 免责声明:网站部分图片文字素材来源于网络,如有侵权,请及时告知,我们会第一时间删除,谢谢! 邮箱:opceo@qq.com

    鄂ICP备2023003026号

    网站地图: 企业信息 工商信息 财税知识 网络常识 编程技术

    友情链接: 武汉网站建设