目录
源码:
百度网盘链接: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矩阵的原理图和实物图
嘉立创链接: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;
}
}