Funpack4-2 基于SAME51移植RT-Thread实现游戏机
该项目使用了SAME51开发板,实现了移植RT-Thread操作系统的设计,它的主要功能为:在SAME51与RT-Thread的软硬件基础上实现游戏机。
标签
Funpack活动
游戏机
rt-thread
SAME51
枫雪天
更新2025-07-02
38

任务介绍

    本项目实现了Funpack第4-2期活动SAME51J20A Curiosity Nano板卡的自命题任务,基于SAME51开发板实现了对RT-Thread操作系统的移植,并在此软硬件基础上控制OLED屏幕,实现了一个简易游戏机,支持通过扩展板按键进行游戏操作,同时与OLED屏幕联动显示游戏画面和状态信息。

硬件平台

    本次使用Microchip推出的SAME51J20A Curiosity Nano开发板,是一款面向嵌入式开发的高性能评估平台。该开发板搭载了基于ARM Cortex-M4内核的SAME51系列MCU,具备丰富的外设资源,包括多个I2C接口、SPI接口、ADC/DAC模块等,非常适合用于实时嵌入式系统的开发。开发板集成了调试器接口、USB接口、用户可编程LED与按键,便于快速搭建原型系统

在软件方面,本项目采用RT-Thread实时操作系统作为底层运行环境。RT-Thread以其轻量级、高可扩展性和良好的跨平台支持而著称,适用于资源受限的嵌入式设备。通过将RT-Thread移植至SAME51平台,实现了多线程调度、设备驱动管理以及图形界面显示等功能。

主控设备:Microchip SAME51J20A Curiosity Nano开发板

  • 搭载SAME51 MCU(ARM Cortex-M4F)
  • 集成USB-C接口与调试器
  • 板载2个用户可编程LED指示灯
  • 支持多线程实时操作系统(RT-Thread)

传感执行器扩展板

  • 128x64 SSD1306 OLED显示屏
  • 按键

任务分析与实现

本系统实现了基于RT-Thread实时操作系统的嵌入式游戏平台,主要功能包括:

三通道数据交互:

  • 游戏输入采集
    • 方向按键扫描频率:10Hz
    • 支持四向移动输入
  • OLED显示控制
    • 显示刷新率:15Hz
    • 图形化界面渲染
  • RTOS任务调度
    • 多线程架构保障响应实时性
    • 系统资源动态管理


方案框图:

RT-Thread 的移植过程

本项目基于 Microchip SAME51J20A Curiosity Nano 开发板完成了 RT-Thread 操作系统的成功移植,并在此基础上实现了对 OLED 屏幕的控制与简易游戏机功能。以下详细描述了 RT-Thread 移植的关键流程和核心要点。

一、移植前的准备

1. 硬件环境

  • 主控芯片:Microchip SAME51J20A(ARM Cortex-M4F 内核)
  • 开发板:SAME51J20A Curiosity Nano
  • 外设资源
    • I2C 接口用于驱动 SSD1306 OLED 显示屏
    • GPIO 控制方向按键输入
    • USART 串口调试输出

2. 软件环境

  • RT-Thread 版本:RT-Thread Master 分支(实时更新版)
  • 编译工具链:GCC ARM Embedded (arm-none-eabi-gcc)
  • 构建工具env 命令行工具 + RT-Thread Studio
  • 底层支持库:Atmel START 提供 HAL 支持

二、移植的核心步骤

1. BSP(Board Support Package)创建

BSP 是 RT-Thread 针对特定硬件平台的支持包,主要包括启动代码、CPU 初始化、中断向量表、时钟配置以及设备驱动适配。

a. 启动文件配置 (startup_same51.c)

  • 定义中断向量表(包括异常处理和外设中断)
  • 设置堆栈指针、初始化 .data  .bss 
  • 调用 rtthread_startup() 进入 RT-Thread 内核
void Reset_Handler(void)
{
        uint32_t *pSrc, *pDest;

        /* Initialize the relocate segment */
        pSrc  = &_etext;
        pDest = &_srelocate;

        if (pSrc != pDest) {
                for (; pDest < &_erelocate;) {
                        *pDest++ = *pSrc++;
                }
        }

        /* Clear the zero segment */
        for (pDest = &_szero; pDest < &_ezero;) {
                *pDest++ = 0;
        }

        /* Set the vector table base address */
        pSrc      = (uint32_t *)&_sfixed;
        SCB->VTOR = ((uint32_t)pSrc & SCB_VTOR_TBLOFF_Msk);


#if __FPU_USED
        /* Enable FPU */
        SCB->CPACR |= (0xFu << 20);
        __DSB();
        __ISB();
#endif

        /* Initialize the C library */
        __libc_init_array();

        /* Branch to rtthread_startup function */
        rtthread_startup();

        /* Infinite loop */
        while (1)
                ;
}

b. 内核入口函数 (rtthread_startup)

  • 初始化内核对象(线程、信号量、定时器等)
  • 创建空闲线程
  • 启动调度器并进入 main 函数
int rtthread_startup(void)
{
rt_hw_board_init();
rt_system_kernel_init();
rt_application_init();
rt_thread_idle_init();
rt_schedule();
return 0;
}

2. 板级初始化 (board.c)

完成 CPU、内存、时钟、串口等基础资源配置:

void SystemInit(void) {
// Keep the default device state after reset
    SystemCoreClock = __SYSTEM_CLOCK;
    return;
}

void rt_hw_board_init(void)
{
    /* Initializes MCU, drivers and middleware */
    atmel_start_init();

    /* enable USART stdout module */
    hw_board_init_usart();

    /* UART driver initialization is open by default */
#ifdef RT_USING_SERIAL
    rt_hw_uart_init();
#endif

    /* init systick */
    SysTick_Config(CONF_CPU_FREQUENCY / RT_TICK_PER_SECOND);

    /* set pend exception priority */
    NVIC_SetPriority(PendSV_IRQn, (1 << __NVIC_PRIO_BITS) - 1);

#ifdef RT_USING_HEAP
    #if defined(__ARMCC_VERSION)
        rt_system_heap_init((void*)&Image$$RW_IRAM1$$ZI$$Limit, (void*)HEAP_END);
    #elif __ICCARM__
        rt_system_heap_init((void*)HEAP_BEGIN, (void*)HEAP_END);
    #else
        /* init memory system */
        rt_system_heap_init((void*)&__bss_end, (void*)HEAP_END);
    #endif
#endif

    /* Set the shell console output device */
#if defined(RT_USING_CONSOLE) && defined(RT_USING_DEVICE)
    rt_console_set_device(RT_CONSOLE_DEVICE_NAME);
#endif


#ifdef RT_USING_COMPONENTS_INIT
    rt_components_board_init();
#endif
}

3. 设备驱动适配

a. 串口驱动 (serial.c)

  • 使用 USART 异步接口实现标准输入输出
  • 注册中断回调以支持接收数据通知机制
static int serial_putc(struct rt_serial_device *serial, char c)
{
struct usart_async_descriptor* desc = ...;
while (!usart_async_is_tx_empty(desc));
_usart_async_write_byte(&TARGET_IO.device, (uint8_t)c);
return 1;
}

b. I2C 总线驱动 (sam_i2c.c)

  • 封装 struct rt_i2c_bus_device_ops 操作集
  • 实现 master_xfer 函数用于主模式下的读写操作
  • 注册 I2C 总线设备并初始化硬件参数
const struct rt_i2c_bus_device_ops sam_i2c_ops = {
.master_xfer = sam_i2c_master_xfer,
.slave_xfer = sam_i2c_slave_xfer,
.i2c_bus_control = sam_i2c_bus_control,
};

int rt_hw_i2c_init(void)
{
sam_i2c0.parent.ops = &sam_i2c_ops;
rt_i2c_bus_device_register(&sam_i2c0.parent, "i2c0");
i2c_m_sync_enable(sam_i2c0.i2c_desc);
return 0;
}

4. 外设驱动注册与自动初始化

使用 RT-Thread 提供的组件自动初始化机制,确保各模块按顺序初始化:

#ifdef RT_USING_COMPONENTS_INIT
INIT_BOARD_EXPORT(rt_hw_i2c_init); // 自动调用注册函数
#endif

代码详解

整体软件流程图:


一、硬件初始化与传感器配置

系统上电后首先完成外设初始化序列,关键流程如下:


  • 配置GPIO引脚为输入/输出模式,确保按键与LED正常工作
  • 初始化I2C接口,设置标准速率(100kHz),用于驱动OLED显示屏
  • 启动RTOS内核并创建基础任务
void hardware_init(void)
{
/* 初始化LED GPIO */
gpio_set_pin_direction(LED0_PIN, GPIO_DIRECTION_OUT);
gpio_set_pin_level(LED0_PIN, true); // 初始点亮LED表示系统运行

/* 初始化方向按键 */
gpio_set_pin_direction(JOY_UP_PIN, GPIO_DIRECTION_IN);
gpio_set_pin_pull_mode(JOY_UP_PIN, GPIO_PULL_UP);

/* 初始化I2C接口 */
i2c_m_sync_enable(&I2C_0);
i2c_m_sync_set_baudrate(&I2C_0, 0, 100000UL); // 设置I2C速率为100kHz

/* 初始化OLED显示屏 */
oled_init(&I2C_0);
}

二、游戏任务调度

#define THREAD_PRIORITY         25
#define THREAD_STACK_SIZE       1024
#define THREAD_TIMESLICE        5

static rt_thread_t tid1 = RT_NULL;

static void u8g2_game_space_trash_entry(void *parameter)
{
  u8g2.begin();
  u8g2.setFont(u8g2_font_6x10_tr);
  u8g2.setFontDirection(0);
  u8g2.setFontRefHeightAll();

  st_Setup(u8g2.getU8g2());
  for(;;)
  {
    st_Step(y, /* is_auto_fire */ 1, /* is_fire */ rt_pin_read(pin_fire));
    u8g2.firstPage();
    do
    {
      st_Draw(0);
    } while( u8g2.nextPage() );

    if ( rt_pin_read(pin_down) ) {
      y++;
    }

    if ( rt_pin_read(pin_up) ) {
      y--;
    }
  }
}

static void u8g2_game_space_trash(int argc,char *argv[])
{
  tid1 = rt_thread_create("tu8g22",
                          u8g2_game_space_trash_entry, RT_NULL,
                          THREAD_STACK_SIZE,
                          THREAD_PRIORITY, THREAD_TIMESLICE);

  /* 如果获得线程控制块,启动这个线程 */
  if (tid1 != RT_NULL)
    rt_thread_startup(tid1);
}
MSH_CMD_EXPORT(u8g2_game_space_trash, u8g2 game space trash sample);

效果展示

遇到的难题与解决办法

问题:新平台支持RT-Thread,遇到系统启动异常的

解法

整个移植过程中,需重点关注:

  • 中断向量表与异常处理机制
  • 系统时钟、滴答定时器的准确配置
  • 核心驱动(如串口、I2C)的适配过程中,注意外设时钟开启与配置。

活动感想

通过本项目实践,深入掌握了RT-Thread操作系统在嵌入式平台上的移植方法、,深入掌握了 RT-Thread 在 Cortex-M4 架构上的启动流程、中断管理机制及设备驱动编写规范。Microchip SAME51J20A 开发板良好的硬件兼容性与 Atmel START 工具链的强大支持极大提升了开发效率。整个移植过程中,需重点关注:

  • 中断向量表与异常处理机制
  • 系统滴答定时器的准确配置
  • 核心驱动(如串口、I2C)的适配与优化

I2C总线通信机制以及图形界面渲染技巧。SAME51J20A开发板出色的性能表现和Microchip官方完善的SDK文档极大提升了开发效率。整个项目过程中,体会到嵌入式系统需要硬件、固件、应用层的紧密配合,每一个细节的优化都能显著提升整体体验。

    感谢硬禾学堂和得捷电子联合举办的Funpack活动,祝硬禾的活动越办越好!

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