基于STM32单片机的三子棋(井字棋)思路分享

目录

主程序

定义游玩状态

游玩状态机

判断胜负

矩阵键盘

矩阵LED

原理图

原理

LED源码


a739f4d5be594d5d809a753128560c17.jpg

源码:

百度网盘链接:https://pan.baidu.com/s/1ay95lFveRtQ_KOjjboXspw?pwd=6666 

运行视频:【基于STM32的三子棋(井字棋)-哔哩哔哩】https://b23.tv/PplML8L

前言:最近在学STM32的中断和矩阵键盘时,有学长提出可以实现一下井字棋,于是这段时间我就自己琢磨着,画了一个双色LED并配合矩阵键盘最后实现了双人三子棋。这个project不仅用到了矩阵键盘和矩阵LED、中断,在主程序中还有一些状态机的思想。下面是一些设计思路。

主程序

#include "stm32f10x.h"                  // Device header
#include "LED.h"
#include "KEY.h"
#include "Delay.h"
#include "paly.h"
int flag=-1;
int Key_Num=0;
int main()
{
	LED_Init();
	Key_Init();
	while(1)
	{		
		game();
	}
}

上面代码是main.c文件中的,主程序game()函数在paly.c文件中,以下是对paly.c的介绍。

定义游玩状态

使用枚举来定义一些游玩时的状态,并把初状态置为运行状态。

typedef enum//定义游玩状态
{
	RUN=0,//运行状态
	AGAIN,//重来
	WIN_1,//1玩家获胜
	WIN_2	//2玩家获胜
}GAME_STATE;

GAME_STATE state=RUN;//初始化状态为运行状态

游玩状态机

在游戏进行中,根据条件变化切换到相应状态。

void game(void)//主游戏程序——状态机
{
	switch (state)
	{
		case RUN ://运行状态
		{
			LED_Polling[flag]=Key_Num;
			if(Key_Num ==16)//判断重启键
			{
				state=AGAIN;//状态转换至重启
			}
			else 
			{
				LEDRun();//LED轮询
				judge();//判断胜负				
			}
		}break ;
		
		case AGAIN ://重启状态
		{
			flag=-1;//初始化按键次数
			Key_Num=0;//初始化键码
			a=0,b=0,c=0,d=0,e=0,f=0,g=0,h=0,j=0,x=0;//初始化亮灯判断变量
			LED_OFF();//关闭所有灯
			for(int i = 0;i<9;i++)//初始化轮询数组
			{ LED_Polling[i] = 0; }	
			state = RUN ;//状态转换至运行
		}break ;
			
		case WIN_1 ://玩家1胜利结算动画
		{
			Delay_ms (400);
			for (int i=1;i<5;i++)//1号所有灯闪烁四次
			{
				LED_ALL_1();
				Delay_ms (200);
				LED_OFF();
				Delay_ms (200);
			state=AGAIN;	//状态转换至重启	
			}
		}break ;
		
		case WIN_2 ://玩家2胜利结算动画
		{
			for (int i=1;i<5;i++)//2号所有灯闪烁四次
			{
				LED_ALL_2();
				Delay_ms (200);
				LED_OFF();
				Delay_ms (200);
			}
			state=AGAIN;	//状态转换至重启		
		}break ;
	}
	
}

判断胜负

通过计次flag判断,给位置变量赋值。然后就是最笨的方法判断胜负:穷举所有获胜条件。

void judge(void)//判断胜负
{
	if(flag%2==0)
	{x=1;}
	else 
	{x=2;}
		switch (Key_Num )
	{
		case 1:a=x;break;
		case 2:b=x;break;
		case 3:c=x;break;
		case 5:d=x;break;
		case 6:e=x;break;
		case 7:f=x;break;
		case 9:g=x;break;
		case 10:h=x;break;
		case 11:j=x;break;
		default :break;
	}
	if((a==1&&b==1&&c==1)||(d==1&&e==1&&f==1)||(g==1&&h==1&&j==1)||(a==1&&d==1&&g==1)||(e==1&&b==1&&h==1)||(f==1&&j==1&&c==1)||(a==1&&e==1&&j==1)||(c==1&&e==1&&g==1))
	{state=WIN_1 ;}
	else if((a==2&&b==2&&c==2)||(d==2&&e==2&&f==2)||(g==2&&h==2&&j==2)||(a==2&&d==2&&g==2)||(e==2&&b==2&&h==2)||(f==2&&j==2&&c==2)||(a==2&&e==2&&j==2)||(c==2&&e==2&&g==2))
	{state=WIN_2 ;}
}

矩阵键盘

关于矩阵键盘的使用,请先参照我的这个博客  基于STM32的矩阵键盘  ,不过我在这个程序中用到了中断,所以直接在中断中判断键码并返回键码即可,以下是源代码

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "LED.h"
uint16_t keyz=0;
extern  int flag;
extern int Key_Num;
	uint8_t Hang,Lie,k,i;
void Key_Init(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE );
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
	
	GPIO_InitTypeDef GPIO_KEY0_3;
	GPIO_KEY0_3.GPIO_Mode=GPIO_Mode_Out_PP;
	GPIO_KEY0_3.GPIO_Pin=GPIO_Pin_0 |GPIO_Pin_1 |GPIO_Pin_2 |GPIO_Pin_3 ;
	GPIO_KEY0_3.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init (GPIOA ,&GPIO_KEY0_3);
	
	GPIO_InitTypeDef GPIO_KEY4_7;
	GPIO_KEY4_7.GPIO_Mode=GPIO_Mode_IPU;
	GPIO_KEY4_7.GPIO_Pin=GPIO_Pin_4 |GPIO_Pin_5 |GPIO_Pin_6 |GPIO_Pin_7 ;
	GPIO_KEY4_7.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init (GPIOA ,&GPIO_KEY4_7);
	//配置中断
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource4);
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource5);
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource6);
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource7);
	
	EXTI_InitTypeDef KEY_EXTI;
	KEY_EXTI.EXTI_Line=EXTI_Line4|EXTI_Line5|EXTI_Line6|EXTI_Line7;
	KEY_EXTI.EXTI_LineCmd=ENABLE;
	KEY_EXTI.EXTI_Mode=EXTI_Mode_Interrupt;
	KEY_EXTI.EXTI_Trigger=EXTI_Trigger_Falling;
	
	EXTI_Init(&KEY_EXTI);
	
	NVIC_PriorityGroupConfig (NVIC_PriorityGroup_2);
	
	NVIC_InitTypeDef KEY_NVIC;
	KEY_NVIC.NVIC_IRQChannel=EXTI9_5_IRQn;
	KEY_NVIC.NVIC_IRQChannelCmd=ENABLE;
	KEY_NVIC.NVIC_IRQChannelPreemptionPriority=1;
	KEY_NVIC.NVIC_IRQChannelSubPriority=1;
	NVIC_Init (&KEY_NVIC);
	

	KEY_NVIC.NVIC_IRQChannel=EXTI4_IRQn;
	KEY_NVIC.NVIC_IRQChannelCmd=ENABLE;
	KEY_NVIC.NVIC_IRQChannelPreemptionPriority=1;
	KEY_NVIC.NVIC_IRQChannelSubPriority=1;
	NVIC_Init (&KEY_NVIC);


		GPIO_Write(GPIOA ,0xF0);

}	


void EXTI9_5_IRQHandler(void)
{
	if(EXTI_GetITStatus (EXTI_Line5 | EXTI_Line6|EXTI_Line7)==SET)
	{
	Hang=GPIO_ReadInputData (GPIOA );					
			Lie=~Hang;											 					
			Lie=Lie&0xF0;
for(i=0;i<4 && ((Hang&0xF0)!=0xF0);i++)   
			{																					
				GPIO_Write (GPIOA ,0xF0|(0x01<<i));			
				Hang=GPIO_ReadInputData (GPIOA );		    
			}
			Hang&=0x0F;
			k=Hang|Lie;                              
			
		switch(k)
		{
			case 0x81:Key_Num=1;break;
			case 0x82:Key_Num=2;break;
			case 0x84:Key_Num=3;break;
			case 0x88:Key_Num=4;break;
			case 0x41:Key_Num=5;break;
			case 0x42:Key_Num=6;break;
			case 0x44:Key_Num=7;break;
			case 0x48:Key_Num=8;break;
			case 0x21:Key_Num=9;break;
			case 0x22:Key_Num=10;break;
			case 0x24:Key_Num=11;break;
			case 0x28:Key_Num=12;break;
			default:break;
		}
			Key_Init ();					    
			while((GPIO_ReadInputData(GPIOA)&0xF0)!=0xF0)
			{
				Delay_ms (40);													
			}

		flag++;
	}
		EXTI_ClearITPendingBit (EXTI_Line5 | EXTI_Line6|EXTI_Line7);
}

void EXTI4_IRQHandler(void)
{
		if(EXTI_GetITStatus (EXTI_Line4)==SET)
	{
	Hang=GPIO_ReadInputData (GPIOA );					
			Lie=~Hang;											 					
			Lie=Lie&0xF0;
for(i=0;i<4 && ((Hang&0xF0)!=0xF0);i++)   
			{																					
				GPIO_Write (GPIOA ,0xF0|(0x01<<i));			
				Hang=GPIO_ReadInputData (GPIOA );		    
			}
			Hang&=0x0F;
			k=Hang|Lie;                              
			
					    
			
					switch(k)
		{
			case 0x11:Key_Num=13;break;
			case 0x12:Key_Num=14;break;
			case 0x14:Key_Num=15;break;
			case 0x18:Key_Num=16;break;
			default:break;
		}
		Key_Init ();	
		while((GPIO_ReadInputData(GPIOA)&0xF0)!=0xF0)
			{
				Delay_ms (40);													
			}
		flag++;
		
	}
		EXTI_ClearITPendingBit (EXTI_Line4);

}

矩阵LED

原理图

这是我自己画的双色led矩阵的原理图和实物图

8d4b97067ba445749abd23755387e054.png

ed74a53c11044a90839fa381843c5150.jpeg

 嘉立创链接:https://oshwhub.com/yxxcf/tic-tac-toe

原理

矩阵LED和矩阵键盘的原理类似,通过操纵行和列的电平实现LED亮灭。值得注意的是,只要数组循环控制多个灯的亮灭,由于单片机的快速运行,在人眼的视觉暂留下,可以实现多个LED同时亮而互不干扰。

LED源码

void LEDRun(void)//LED轮询函数
{
				for(int i=0;i<9;i++)
		{
			if(i%2==0)//判断玩家
			{
					LED_value_1(LED_Polling[i]);
			}
			else
			{
					LED_value_2(LED_Polling[i]);
			}
		}
}

以上是paly里的LED轮询函数,主要通过它实现多个LED同时亮。

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
void LED_Init(void)//PA1,PA2引脚连接LED负极引脚,此函数对其初始化
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
	GPIO_InitTypeDef LED_structure;
	LED_structure.GPIO_Mode=GPIO_Mode_Out_PP;
	LED_structure.GPIO_Pin=GPIO_Pin_All;
	LED_structure.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init (GPIOB,&LED_structure);
	GPIO_ResetBits (GPIOB ,GPIO_Pin_12 |GPIO_Pin_13 | GPIO_Pin_14);
	GPIO_SetBits (GPIOB ,GPIO_Pin_0 |GPIO_Pin_1 | GPIO_Pin_5|GPIO_Pin_7 |GPIO_Pin_8 | GPIO_Pin_9);
}

void LED_OFF(void)
{
	GPIO_ResetBits (GPIOB ,GPIO_Pin_12 |GPIO_Pin_13 | GPIO_Pin_14);
	GPIO_SetBits (GPIOB ,GPIO_Pin_0 |GPIO_Pin_1 | GPIO_Pin_5|GPIO_Pin_7 |GPIO_Pin_8 | GPIO_Pin_9);
}

void LED_ALL_1(void)
{
	GPIO_SetBits (GPIOB ,GPIO_Pin_12 |GPIO_Pin_13 | GPIO_Pin_14);
	GPIO_ResetBits (GPIOB ,GPIO_Pin_0 |GPIO_Pin_1 | GPIO_Pin_5 );
}
void LED_ALL_2(void)
{
	GPIO_SetBits (GPIOB ,GPIO_Pin_12 |GPIO_Pin_13 | GPIO_Pin_14);
	GPIO_ResetBits (GPIOB ,GPIO_Pin_7 |GPIO_Pin_8 | GPIO_Pin_9);
}

void LED_value_1(int v)//参数中输入键盘键码,令所对应的1号颜色灯亮
{
	switch(v)
	{
		case 1:
		{	
			LED_OFF();
			GPIO_SetBits (GPIOB,GPIO_Pin_12);
			GPIO_ResetBits (GPIOB,GPIO_Pin_0);

		}break;
		case 2:
		{	
			LED_OFF();
			GPIO_SetBits (GPIOB,GPIO_Pin_13);
			GPIO_ResetBits (GPIOB,GPIO_Pin_0);

		}break;
		case 3:
		{	
			LED_OFF();
			GPIO_SetBits (GPIOB,GPIO_Pin_14);
			GPIO_ResetBits (GPIOB,GPIO_Pin_0);

		}break;
		case 5:
		{	
			LED_OFF();
			GPIO_SetBits (GPIOB,GPIO_Pin_12);
			GPIO_ResetBits (GPIOB,GPIO_Pin_1);
	
		}break;
		case 6:
		{	
			LED_OFF();
			GPIO_SetBits (GPIOB,GPIO_Pin_13);
			GPIO_ResetBits (GPIOB,GPIO_Pin_1);
	
		}break;
		case 7:
		{	
			LED_OFF();
			GPIO_SetBits (GPIOB,GPIO_Pin_14);
			GPIO_ResetBits (GPIOB,GPIO_Pin_1);

		}break;
		case 9:
		{	
			LED_OFF();
			GPIO_SetBits (GPIOB,GPIO_Pin_12);
			GPIO_ResetBits (GPIOB,GPIO_Pin_5);

		}break;
		case 10:
		{	
			LED_OFF();
			GPIO_SetBits (GPIOB,GPIO_Pin_13);
			GPIO_ResetBits (GPIOB,GPIO_Pin_5);
	
		}break;
		case 11:
		{	
			LED_OFF();
			GPIO_SetBits (GPIOB,GPIO_Pin_14);
			GPIO_ResetBits (GPIOB,GPIO_Pin_5);
	
		}break;
		case 0:break;
	}
	
}
void LED_value_2(int v)//参数中输入键盘键码,令所对应的1号颜色灯亮
{
	switch(v)
	{
		case 1:
		{
			LED_OFF();
			GPIO_SetBits (GPIOB,GPIO_Pin_12);
			GPIO_ResetBits (GPIOB,GPIO_Pin_7);
		}break;
		case 2:
		{
			LED_OFF();
			GPIO_SetBits (GPIOB,GPIO_Pin_13);
			GPIO_ResetBits (GPIOB,GPIO_Pin_7);
		}break;
		case 3:
		{
			LED_OFF();
			GPIO_SetBits (GPIOB,GPIO_Pin_14);
			GPIO_ResetBits (GPIOB,GPIO_Pin_7);
		}break;
		case 5:
		{
			LED_OFF();
			GPIO_SetBits (GPIOB,GPIO_Pin_12);
			GPIO_ResetBits (GPIOB,GPIO_Pin_8);
		}break;
		case 6:
		{
			LED_OFF();
			GPIO_SetBits (GPIOB,GPIO_Pin_13);
			GPIO_ResetBits (GPIOB,GPIO_Pin_8);
		}break;
		case 7:
		{
			LED_OFF();
			GPIO_SetBits (GPIOB,GPIO_Pin_14);
			GPIO_ResetBits (GPIOB,GPIO_Pin_8);
		}break;
		case 9:
		{
			LED_OFF();
			GPIO_SetBits (GPIOB,GPIO_Pin_12);
			GPIO_ResetBits (GPIOB,GPIO_Pin_9);
		}break;
		case 10:
		{
			LED_OFF();
			GPIO_SetBits (GPIOB,GPIO_Pin_13);
			GPIO_ResetBits (GPIOB,GPIO_Pin_9);
		}break;
		case 11:
		{
			LED_OFF();
			GPIO_SetBits (GPIOB,GPIO_Pin_14);
			GPIO_ResetBits (GPIOB,GPIO_Pin_9);
		}break;
		case 0:break;
	}
	
}




  • 37
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值