al_mn 2024-09-11 12:33 采纳率: 50%
浏览 18

STM32实现MAX7219驱动4个8*8led点阵级联显示推箱子游戏设计

使用STM32F103VCT6最小系统板,通过max7219实现四个8*8led共阴点阵屏的级联,四个按键上下左右移动实现推箱子游戏设计
其中点阵屏的CLK接PA8,CS接PA10,DIN接PA12,控制左上右下移动的按键分别接引脚PD1,3,6,7,以下是部分代码

#include "stm32f10x.h"  // 包含STM32F10x系列微控制器的头文件
#include "bsp_SysTick.h"  // 包含系统滴答定时器的实现,用于延时
#include "Dot_Matrix.h"  // 包含点阵屏的相关配置

#define MAP_WIDTH 16  // 定义地图宽度为16个格子
#define MAP_HEIGHT 16 //地图高度为16个格子
#define DEBOUNCE_DELAY 1000  // 设置消抖时间为1000ms

// 定义地图、玩家、箱子和目标位置的全局变量
char map[MAP_HEIGHT][MAP_WIDTH] = {
    {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
    {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
    {1, 0, 4, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1},
    {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 1, 1, 1},
    {1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1},
    {0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1},
    {1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1},
    {1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1},
    {1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1},
    {1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1},
    {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1},
    {1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1},
    {0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1},
    {0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
};
int player_x = 2;  // 玩家初始x坐标
int player_y = 3;  // 玩家初始y坐标
int box_x = 2;  // 箱子初始x坐标
int box_y = 6;  // 箱子初始y坐标
int target_x = 7;  // 目标x坐标
int target_y = 8;  // 目标y坐标

// 函数声明
void Display_Update(void);
void Display_Win(void);
void Clear_Display(void);
void Display_Box(int x, int y);
void Display_Target(int x, int y);
void Display_Player(int x, int y);

// 按键初始化函数
void Key_Init(void) {
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD, ENABLE);  // 使能GPIOD端口时钟
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_3 | GPIO_Pin_6 | GPIO_Pin_7;  // 配置PD1, PD3, PD6, PD7引脚
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;  // 设置为下拉输入模式
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;  // 设置引脚速度
    GPIO_Init(GPIOD, &GPIO_InitStructure);  // 初始化GPIOD
}

// 按键扫描函数,带有消抖处理
int Key_Scan(void) {
    static int key_state = 0;
    static int key_pressed = 0;
    static uint32_t key_time = 0;
    int current_state;

    // 检测按键状态
    current_state = GPIO_ReadInputData(GPIOD) & (GPIO_Pin_1 | GPIO_Pin_3 | GPIO_Pin_6 | GPIO_Pin_7);  // 只检测PD1, PD3, PD6, PD7

    if (current_state != key_state) {
        key_time = 0;
        key_state = current_state;
    }

    if (key_time < DEBOUNCE_DELAY) {
        key_time++;
    } else {
        if (current_state != key_pressed) {
            key_pressed = current_state;
            if (key_pressed == GPIO_Pin_1) return 1;  // PD1按下
            if (key_pressed == GPIO_Pin_3) return 2;  // PD3按下
            if (key_pressed == GPIO_Pin_6) return 3;  // PD6按下
            if (key_pressed == GPIO_Pin_7) return 4;  // PD7按下
        }
    }

    return 0;
}

// 主函数
int main(void) {
    Key_Init();  // 初始化按键
    SysTick_Init();  // 初始化系统滴答定时器

    while (1) {
        int key = Key_Scan();  // 扫描按键
        if (key) {
            SysTick_Delay_Ms(10);  // 延时消抖

            switch (key) {
                case 1: // 向左移动
                    if (player_x > 0 && map[player_y][player_x - 1] != 1) {  // 确保不是墙壁且不超出左边界
                        if (map[player_y][player_x - 1] == 2) {  // 如果左边是箱子
                            if (player_y > 0 && map[player_y - 1][player_x - 1] == 0) {  // 确保箱子可以被推动
                                map[player_y][player_x] = 0;  // 清除玩家当前位置
                                map[player_y - 1][player_x - 1] = 2;  // 移动箱子
                                map[player_y][player_x - 1] = 4;  // 更新玩家位置
                                player_x--;  // 更新玩家的x坐标
                                box_x--;  // 更新箱子的x坐标
                            }
                        } else {
                            map[player_y][player_x] = 0;  // 清除玩家当前位置
                            map[player_y][player_x - 1] = 4;  // 更新玩家位置
                            player_x--;  // 更新玩家的x坐标
                        }
                    }
                    break;
                case 2: // 向上移动
                    if (player_y > 0 && map[player_y - 1][player_x] != 1) {  // 确保不是墙壁且不超出上边界
                        if (map[player_y - 1][player_x] == 2) {  // 如果上面是箱子
                            if (player_x > 0 && map[player_y - 1][player_x - 1] == 0) {  // 确保箱子可以被推动
                                map[player_y][player_x] = 0;  // 清除玩家当前位置
                                map[player_y - 1][player_x - 1] = 2;  // 移动箱子
                                map[player_y - 1][player_x] = 4;  // 更新玩家位置
                                player_y--;  // 更新玩家的y坐标
                                box_y--;  // 更新箱子的y坐标
                            }
                        } else {
                            map[player_y][player_x] = 0;  // 清除玩家当前位置
                            map[player_y - 1][player_x] = 4;  // 更新玩家位置
                            player_y--;  // 更新玩家的y坐标
                        }
                    }
                    break;
                case 3: // 向右移动
                    if (player_x < MAP_WIDTH - 1 && map[player_y][player_x + 1] != 1) {  // 确保不是墙壁且不超出右边界
                        if (map[player_y][player_x + 1] == 2) {  // 如果右边是箱子
                            if (player_y > 0 && map[player_y - 1][player_x + 1] == 0) {  // 确保箱子可以被推动
                                map[player_y][player_x] = 0;  // 清除玩家当前位置
                                map[player_y - 1][player_x + 1] = 2;  // 移动箱子
                                map[player_y][player_x + 1] = 4;  // 更新玩家位置
                                player_x++;  // 更新玩家的x坐标
                                box_x++;  // 更新箱子的x坐标
                            }
                        } else {
                            map[player_y][player_x] = 0;  // 清除玩家当前位置
                            map[player_y][player_x + 1] = 4;  // 更新玩家位置
                            player_x++;  // 更新玩家的x坐标
                        }
                    }
                    break;
                case 4: // 向下移动
                    if (player_y < MAP_HEIGHT - 1 && map[player_y + 1][player_x] != 1) {  // 确保不是墙壁且不超出下边界
                        if (map[player_y + 1][player_x] == 2) {  // 如果下边是箱子
                            if (player_x > 0 && map[player_y + 1][player_x - 1] == 0) {  // 确保箱子可以被推动
                                map[player_y][player_x] = 0;  // 清除玩家当前位置
                                map[player_y + 1][player_x - 1] = 2;  // 移动箱子
                                map[player_y + 1][player_x] = 4;  // 更新玩家位置
                                player_y++;  // 更新玩家的y坐标
                                box_y++;  // 更新箱子的y坐标
                            }
                        } else {
                            map[player_y][player_x] = 0;  // 清除玩家当前位置
                            map[player_y + 1][player_x] = 4;  // 更新玩家位置
                            player_y++;  // 更新玩家的y坐标
                        }
                    }
                    break;
            }

            // 检查胜利条件
            if (box_x == target_x && box_y == target_y) {
                Display_Win();  // 显示胜利信息
            }

            // 更新显示
            Display_Update();  // 更新游戏显示
        }
    }
}

// 显示更新函数
void Display_Update(void) {
    Clear_Display();  // 清屏函数,根据你的显示设备实现

    // 显示地图
    for (int y = 0; y < MAP_HEIGHT; y++) {
        for (int x = 0; x < MAP_WIDTH; x++) {
            switch (map[y][x]) {
                case 0: break;  // 空白,不显示
                case 1: Display_Box(x, y); break;  // 箱子
                case 2: Display_Target(x, y); break;  // 目标位置
                case 3: break;  // 墙壁,不显示
                case 4: Display_Player(x, y); break;  // 玩家
            }
        }
    }
}

// 清屏函数
void Clear_Display(void) {
    // 清屏函数,根据你的显示设备实现
}

// 显示箱子函数
void Display_Box(int x, int y) {
    // 绘制3x3全亮的LED表示箱子
    for (int dy = -1; dy <= 1; dy++) {
        for (int dx = -1; dx <= 1; dx++) {
            Display_Char(x + dx, y + dy, 'O');  // 假设Display_Char函数可以显示单个字符
        }
    }
}

// 显示目标位置函数
void Display_Target(int x, int y) {
    // 绘制3x3的"X"表示目标位置
    for (int dy = -1; dy <= 1; dy++) {
        for (int dx = -1; dx <= 1; dx++) {
            if (abs(dx) + abs(dy) == 1) {  // 绘制"X"形状
                Display_Char(x + dx, y + dy, 'X');
            }
        }
    }
}

// 显示玩家位置函数
void Display_Player(int x, int y) {
    // 绘制3x3的空心框表示玩家位置
    for (int dy = -1; dy <= 1; dy++) {
        for (int dx = -1; dx <= 1; dx++) {
            if (dx == -1 || dx == 1 || dy == -1 || dy == 1) {  // 绘制边框
                Display_Char(x + dx, y + dy, 'P');
            }
        }
    }
}

// 显示字符函数
void Display_Char(int x, int y, char ch) {
    // 显示字符函数,根据你的显示设备实现
}

// 显示胜利信息函数
void Display_Win(void) {
    Clear_Display();
    // 显示"WIN"字样
    Display_Char(1, 1, 'W');
    Display_Char(2, 1, 'I');
    Display_Char(3, 1, 'N');
}

有几个问题是#include "Dot_Matrix.h" // 包含点阵屏的相关配置,这个头文件是在网上找的显示数字的代码,并不能实现其他功能

  if (current_state != key_pressed) {
            key_pressed = current_state;
            if (key_pressed == GPIO_Pin_1) return 1;  // PD1按下
            if (key_pressed == GPIO_Pin_3) return 2;  // PD3按下
            if (key_pressed == GPIO_Pin_6) return 3;  // PD6按下
            if (key_pressed == GPIO_Pin_7) return 4;  // PD7按下
        }
这段代码不太懂return数字实现的什么

以下代码是待实现功能还未编写的函数

 // 检查胜利条件
            if (box_x == target_x && box_y == target_y) {
                Display_Win();  // 显示胜利信息
            }

            // 更新显示
            Display_Update();  // 更新游戏显示
        }
// 显示更新函数
void Display_Update(void) {
    Clear_Display();  // 清屏函数,根据你的显示设备实现
// 清屏函数
void Clear_Display(void) {
    // 清屏函数,根据你的显示设备实现
  Display_Char(x + dx, y + dy, 'O');  // 假设Display_Char函数可以显示单个字符,然而假设不成立
// 显示字符函数
void Display_Char(int x, int y, char ch) {
    // 显示字符函数,根据你的显示设备实现
}

// 显示胜利信息函数
void Display_Win(void) {
    Clear_Display();
    // 显示"WIN"字样
    Display_Char(1, 1, 'W');
    Display_Char(2, 1, 'I');
    Display_Char(3, 1, 'N');
}
  • 写回答

1条回答 默认 最新

  • 码踏云端 Java领域新星创作者 2024-09-15 21:45
    关注

    问题分析与解决方案

    你提到的问题主要集中在以下几个方面:

    1. Dot_Matrix.h头文件不支持所需功能
    2. 按键扫描函数中return数字的实现逻辑不清晰
    3. 未实现的显示函数

    下面我将逐一解决这些问题,并提供相应的代码实现。

    1. Dot_Matrix.h头文件不支持所需功能

    由于Dot_Matrix.h头文件不支持你所需要的显示功能,你需要自己实现这些功能。具体来说,你需要实现以下几个函数:

    • Clear_Display():清屏函数。
    • Display_Char(int x, int y, char ch):在指定位置显示字符。
    • Display_Update():更新显示内容。
    • Display_Win():显示胜利信息。

    实现Clear_Display()函数

    void Clear_Display(void) {
        // 清屏函数,根据你的显示设备实现
        // 假设你使用的是MAX7219控制点阵屏,可以通过发送全0的数据来清屏
        for (int i = 1; i <= 8; i++) {
            MAX7219_Write(i, 0x00);  // 假设MAX7219_Write是写入数据的函数
        }
    }
    

    实现Display_Char(int x, int y, char ch)函数

    void Display_Char(int x, int y, char ch) {
        // 显示字符函数,根据你的显示设备实现
        // 假设你使用的是MAX7219控制点阵屏,可以通过查表的方式显示字符
        const uint8_t font[128][8] = {
            // 字符'W'的点阵数据
            ['W'] = {
                0b00010001,
                0b00101010,
                0b01000100,
                0b01000100,
                0b01000100,
                0b01000100,
                0b00101010,
                0b00010001
            },
            // 字符'I'的点阵数据
            ['I'] = {
                0b00000000,
                0b01111110,
                0b00011000,
                0b00011000,
                0b00011000,
                0b00011000,
                0b01111110,
                0b00000000
            },
            // 字符'N'的点阵数据
            ['N'] = {
                0b01110001,
                0b01110001,
                0b01110001,
                0b01110001,
                0b01110001,
                0b01110001,
                0b01110001,
                0b01110001
            },
            // 其他字符的点阵数据
        };
    
        // 计算字符在点阵屏上的位置
        int char_index = ch;
        if (char_index < 0 || char_index >= 128) return;  // 超出范围的字符不显示
    
        // 显示字符
        for (int i = 0; i < 8; i++) {
            MAX7219_Write(y + 1, font[char_index][i]);  // 假设MAX7219_Write是写入数据的函数
        }
    }
    

    实现Display_Update()函数

    void Display_Update(void) {
        Clear_Display();  // 清屏函数,根据你的显示设备实现
    
        // 显示地图
        for (int y = 0; y < MAP_HEIGHT; y++) {
            for (int x = 0; x < MAP_WIDTH; x++) {
                switch (map[y][x]) {
                    case 0: break;  // 空白,不显示
                    case 1: Display_Box(x, y); break;  // 箱子
                    case 2: Display_Target(x, y); break;  // 目标位置
                    case 3: break;  // 墙壁,不显示
                    case 4: Display_Player(x, y); break;  // 玩家
                }
            }
        }
    }
    

    实现Display_Win()函数

    void Display_Win(void) {
        Clear_Display();
        // 显示"WIN"字样
        Display_Char(1, 1, 'W');
        Display_Char(2, 1, 'I');
        Display_Char(3, 1, 'N');
    }
    

    2. 按键扫描函数中return数字的实现逻辑

    在按键扫描函数中,return数字的目的是为了标识哪个按键被按下。具体的实现逻辑如下:

    int Key_Scan(void) {
        static int key_state = 0;
        static int key_pressed = 0;
        static uint32_t key_time = 0;
        int current_state;
    
        // 检测按键状态
        current_state = GPIO_ReadInputData(GPIOD) & (GPIO_Pin_1 | GPIO_Pin_3 | GPIO_Pin_6 | GPIO_Pin_7);  // 只检测PD1, PD3, PD6, PD7
    
        if (current_state != key_state) {
            key_time = 0;
            key_state = current_state;
        }
    
        if (key_time < DEBOUNCE_DELAY) {
            key_time++;
        } else {
            if (current_state != key_pressed) {
                key_pressed = current_state;
                if (key_pressed == GPIO_Pin_1) return 1;  // PD1按下
                if (key_pressed == GPIO_Pin_3) return 2;  // PD3按下
                if (key_pressed == GPIO_Pin_6) return 3;  // PD6按下
                if (key_pressed == GPIO_Pin_7) return 4;  // PD7按下
            }
        }
    
        return 0;
    }
    

    在这个函数中,return的数字表示按键的编号:

    • return 1:表示PD1按键被按下。
    • return 2:表示PD3按键被按下。
    • return 3:表示PD6按键被按下。
    • return 4:表示PD7按键被按下。

    3. 未实现的显示函数

    你已经提供了大部分未实现的显示函数,我已经在前面补充了这些函数的实现。

    总结

    通过以上修改和补充,你的代码应该能够正常运行并实现推箱子游戏的功能。主要的工作集中在显示函数的实现上,确保这些函数能够正确地在点阵屏上显示游戏状态和胜利信息。

    评论

报告相同问题?

问题事件

  • 创建了问题 9月11日

悬赏问题

  • ¥15 如何在vue.config.js中读取到public文件夹下window.APP_CONFIG.API_BASE_URL的值
  • ¥50 浦育平台scratch图形化编程
  • ¥20 求这个的原理图 只要原理图
  • ¥15 vue2项目中,如何配置环境,可以在打完包之后修改请求的服务器地址
  • ¥20 微信的店铺小程序如何修改背景图
  • ¥15 UE5.1局部变量对蓝图不可见
  • ¥15 一共有五道问题关于整数幂的运算还有房间号码 还有网络密码的解答?(语言-python)
  • ¥20 sentry如何捕获上传Android ndk 崩溃
  • ¥15 在做logistic回归模型限制性立方条图时候,不能出完整图的困难
  • ¥15 G0系列单片机HAL库中景园gc9307液晶驱动芯片无法使用硬件SPI+DMA驱动,如何解决?