基于RP2040游戏机的电子沙漏
基于RP2040在keil开发环境下设计的可以定时调节速度的电子沙漏
标签
嵌入式系统
RP2040
沙漏
灯板
2022暑假在家练
冷月烟
更新2022-09-02
597

1.项目描述

项目目的

   1.自行设计一个电子沙漏的物理结构,将提供的两个LED灯板和RP2040 Game Kit固定

   2.通过RP2040 Game Kit上的按键和LCD屏幕设定沙漏一个周期的时间,实现如上图中LED的效果

   3.通过RP2040 Game Kit上的姿态传感器来感知沙漏的方向变化,并开始新的沙漏操作

设计思路

沙漏显示部分使用LED点阵,使用串转并芯片节省大量IO。

设计使用4个按键,功能分别是:增加时间,减少时间,开始,停止。

最大定时时间240秒,最小定时时间5秒,单次调整步进5秒。

整体分为两个模式,1.定时设置模式,按增加跟减少可以调整定时时间,按开始后转到沙漏控制模式。2.沙漏控制模式,进行模拟沙漏部分的运算与显示,运行时可以按停止切换到定时模式。或者当计时结束后,也会自动切换到定时设置模式。

沙粒的重力模拟分两部分设计:上半部分:采用查表的方式确定那一颗沙粒落下。下半部分:采用碰撞检测的方式,当发现下面沙粒阻碍移动时,选择一个可以移动的方向继续移动,直到无法移动,当碰到两个方向都能走需要决策的时候,将会采用一次左一次右的方式来确保沙粒能够均匀落到底部。

重力感应部分,定时检测,当发现出现翻转的时候,交换两个沙粒显示的缓冲区,实现沙漏翻转功能。

沙粒控制部分,将整体分为两类动沙粒与静沙粒,其中静沙粒存储在缓存区里,而动沙粒以坐标记录,每次移动只需要判断动沙粒的碰撞即可,当动沙粒无法移动时,其所在的位置会产生一个静沙粒,原本的动沙粒清除,等待下一次流程。

总结:该设计方式主要好处是简单,避免了模拟重力是出现的符合重力规律但是不符合实际常识的问题。但因为每一次都会只控制一颗沙粒,无法实现倾斜沙漏,沙粒自由移动的情况。

 

2.硬件介绍

 

  • 采用树莓派Pico核心芯片RP2040:

    • 双核Arm Cortex M0+内核,可以运行到133MHz

    • 264KB内存
    • 性能强大、高度灵活的可编程IO可用于高速数字接口

    • 片内温度传感器、并支持外部4路模拟信号输入,内部ADC采样率高达500Ksps、12位精度

    • 支持MicroPython、C、C++编程

  • 板上功能:

    • 240*240分辨率的彩色IPS LCD,SPI接口,控制器为ST7789

    • 四向摇杆 + 2个轻触按键 + 一个三轴姿态传感器MMA7660用做输入控制

    • 板上外扩2MB Flash,预刷MicroPython的UF2固件

    • 一个红外接收管 + 一个红外发射管

    • 一个三轴姿态传感器MMA7660
    • 一个蜂鸣器

    • 双排16Pin连接器,有SPI、I2C以及2路模拟信号输入

    • 可以使用MicroPython、C、C++编程

    • USB Type C连接器用于供电、程序下载

3.开发环境

RP2040支持多种环境进行开发,它作为一个M0的双核芯片,第一时间想到的就是用ARM的亲儿子keil开发设计,可是树莓派官方并没有提供这一种方式,而是把类似linux的一套搬到RP2040上面了,GCC固然强大,可是使用起来还是比较折腾的,MicroPython固然简单,可是与主流的嵌入式开发相差不小,封装起来的代码使用起来很畅快,但是自己要写一个驱动就麻烦的很。不过早就有大佬实现了这个想法,在keil上搭建了RP2040的开发环境。

这里我是使用了傻孩子大佬的一个开源项目:https://github.com/GorgonMeducer/Pico_Template 。使用这个项目就能轻松的使用keil进行RP2040的开发了。部署非常简单,keil只要额外安装GorgonMeducer.perf_counter.1.9.4.pack这个包(这个包在项目里面也有提供,是一个测量运行时间的组件),就能直接打开工程使用了。

上面的项目再搭配上另一个开源项目:https://github.com/majbthrd/pico-debug/releases (将RP2040的一个核实现为DAP功能),打开后下载 pico-debug-gimmecache.uf2文件,将其烧录到RP2040里,就能利用上RP2040的一个核作为DAP去调试另一个核,让RP2040的开发方式更加接近于传统的单片机的开发方式,而且实现只需要一根USB线就完成下载与调试的功能,再也不用折腾来折腾去了。

 

4.软件运行流程图

FnbVX0Vgv_6TiZF-UsJPOezy6qxr

 

5.主要代码片段及说明

初始化部分

system_init();
stdio_init_all();

LCD_Init();//LCD初始化
LCD_Fill(0,0,LCD_W,LCD_H,WHITE);
MMA7660_Init();

gpio_init(LED_DIN_PIN);
gpio_set_dir(LED_DIN_PIN, GPIO_OUT);
gpio_init(LED_CLK_PIN);
gpio_set_dir(LED_CLK_PIN, GPIO_OUT);
gpio_init(LED_RCLK_PIN);
gpio_set_dir(LED_RCLK_PIN, GPIO_OUT);

gpio_put(LED_DIN_PIN, 0);
gpio_put(LED_CLK_PIN, 0);
gpio_put(LED_RCLK_PIN, 0);

add_repeating_timer_ms(2, repeating_timer_callback, NULL, &timer);

刷新点阵部分

一次显示一行,通过视觉暂存实现显示

uint8_t led_scan_i = 0;
bool repeating_timer_callback(repeating_timer_t *rt)
{
    for(uint8_t j=0;j<8;j++)
    {
        gpio_put(LED_DIN_PIN, led_data_scan[led_scan_i][j]);
        gpio_put(LED_CLK_PIN, 0);
        gpio_put(LED_CLK_PIN, 1);
    }
    for(uint8_t j=0;j<8;j++)
    {
        gpio_put(LED_DIN_PIN, led_data1[led_scan_i][j]);
        gpio_put(LED_CLK_PIN, 0);
        gpio_put(LED_CLK_PIN, 1);
    }
    
    for(uint8_t j=0;j<8;j++)
    {
        gpio_put(LED_DIN_PIN, led_data_scan[led_scan_i][j]);
        gpio_put(LED_CLK_PIN, 0);
        gpio_put(LED_CLK_PIN, 1);
    }
    for(uint8_t j=0;j<8;j++)
    {
        gpio_put(LED_DIN_PIN, led_data2[led_scan_i][j]);
        gpio_put(LED_CLK_PIN, 0);
        gpio_put(LED_CLK_PIN, 1);
    }
    gpio_put(LED_RCLK_PIN, 0);
    gpio_put(LED_RCLK_PIN, 1);
    
    led_scan_i++;
    if(led_scan_i>=8)led_scan_i = 0;
    return true;
}

数组与标志复位

for(uint8_t i=0;i<8;i++)
{
    for(uint8_t j=0;j<8;j++)
    {
        grains_data1[i][j] = 0;
        grains_data2[i][j] = 1;
    }
}
grains_coordinate[0] = -1;
grains_coordinate[1] = -1;
reduce_num = 0;
temp_time = 0;

配置时间部分

while(1)
{
    char str_log[20];
    LCD_ShowChinese(0,104,"定时时间",RED,WHITE,32,0);
    sprintf(str_log,":%dS   ",grains_time);
    LCD_ShowString(32*4,104,str_log,RED,WHITE,32,0);
    if(gpio_get(KEY_A_PIN) == 0)
    {
        sleep_ms(10);
        while(gpio_get(KEY_A_PIN) == 0);
        if(grains_time < 240)
            grains_time += 5;
    }
    if(gpio_get(KEY_B_PIN) == 0)
    {
        sleep_ms(10);
        while(gpio_get(KEY_B_PIN) == 0);
        if(grains_time > 5)
            grains_time-=5;
    }
    if(gpio_get(KEY_START_PIN) == 0)
    {
        sleep_ms(10);
        while(gpio_get(KEY_START_PIN) == 0);
        break;
    }
}

主要显示控制部分

while(1)
{
    if(temp_time >= grains_time * 2)
    {
        temp_time = 0;
        if(grains_coordinate[0] == -1 || grains_coordinate[1] == -1)
        {
            grains_data2[gravity_reduce[reduce_num][0]][gravity_reduce[reduce_num][1]] = 0;
            reduce_num++;
            grains_coordinate[0] = 7;
            grains_coordinate[1] = 7;
        }
        else if((grains_coordinate[0] > 0 && grains_coordinate[1] > 0) && grains_data1[grains_coordinate[0]-1][grains_coordinate[1]-1] != 1)
        {
            grains_coordinate[0]--;
            grains_coordinate[1]--;
        }
        else if((grains_coordinate[0] > 0 && grains_coordinate[1] > 0) && grains_data1[grains_coordinate[0]-1][grains_coordinate[1]] != 1 && grains_data1[grains_coordinate[0]][grains_coordinate[1]-1] != 1)
        {
            static uint8_t grains_lr = 0;
            grains_coordinate[grains_lr]--;
            grains_lr++;
            if(grains_lr > 1)
                grains_lr = 0;
        }
        else if(grains_coordinate[0] > 0 && grains_data1[grains_coordinate[0]-1][grains_coordinate[1]] != 1)
        {
            grains_coordinate[0]--;
        }
        else if(grains_coordinate[1] > 0 && grains_data1[grains_coordinate[0]][grains_coordinate[1]-1] != 1)
        {
            grains_coordinate[1]--;
        }
        else
        {
            grains_data1[grains_coordinate[0]][grains_coordinate[1]] = 1;
            grains_coordinate[0] = -1;
            grains_coordinate[1] = -1;
        }
        
        if(MMA7660_GetResult_Y() >= 0)
        {
            for(uint8_t i=0;i<8;i++)
                for(uint8_t j=0;j<8;j++)
                {
                    led_data1[i][j] = grains_data1[i][j];
                    led_data2[i][j] = grains_data2[i][j];
                }
            
            if(grains_coordinate[0] != -1 && grains_coordinate[1] != -1)
                led_data1[grains_coordinate[0]][grains_coordinate[1]] = 1;
        }
        else
        {
            for(uint8_t i=0;i<8;i++)
                for(uint8_t j=0;j<8;j++)
                {
                    led_data1[i][j] = grains_data2[i][j];
                    led_data2[i][j] = grains_data1[i][j];
                }
            
            if(grains_coordinate[0] != -1 && grains_coordinate[1] != -1)
                led_data2[grains_coordinate[0]][grains_coordinate[1]] = 1;
        }
    }
    
    if(gpio_get(KEY_SELECT_PIN) == 0)
    {
        sleep_ms(10);
        while(gpio_get(KEY_SELECT_PIN) == 0);
        
        break;
    }
    
    temp_time++;
    sleep_us(1150);
}

6.功能演示

定时时间配置

FqSHq4o9NGN7o0rqY7rciftNP2eg

沙子落下

FgEv3ecZufWgUC5hywIztDggt9M3

重力感应翻转

FjQStg8XxO-BTljDDwfJHi3sF47r

 

7.遇到的主要难题及解决方法

点阵驱动不起来

忘记处理RCLK信号,加上就好了。

没在电子森林找到使用的TFT屏的资料

拿网上不同的几家初始化代码都试了试。

 

8.未来的计划或建议

可以探索更多的对芯片的开发方式,实验更多有意思的环境,希望电子森林里面能够将各种文档资料完善一下,或者是开放给部分有编辑意向的开发者,尽快完善起来。

 

附件下载
代码.zip
团队介绍
团队成员
冷月烟
评论
0 / 100
查看更多
目录
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2023 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号