Funpack 4-2: 基于 SAM E51 开发板的 Arduino 框架迁移适配
该项目使用了SAM E51 开发板,实现了Arduino 框架适配的设计,它的主要功能为:呼吸灯速率切换,通过 IDE 的串口波形图显示传感器数据。
标签
Arduino
Funpack活动
传感器
呼吸灯
SAME51J20A
clr
更新2025-07-01
15

一、 项目介绍

SAME51J20A 采用高性能 32 Cortex-M4 核心架构,运行频率为 120 MHz,配备 1  MB ECC Flash256 KB ECC SRAM。支持2 CAN-FD 接口;支持全速 USB 接口;还有特色的触摸检测功能,最多支持 32 路自容检测  256 (16 x 16) 路互容检测,适用于按键、滑条及转轮等多种交互场景。


项目目标

  1. 构建 bootloader 固件,支持 sam-ba 协议写入固件。
  2. 迁移适配 Arduino 框架。
  3. 适配引脚映射。
  4. 通过 IDE 内置的串口波形图可视化显示传感器数据。

 

二、设计思路

任务二:移植Arduino框架

Arduino IDE 因其简易上手,拥有丰富的开源生态而备受初学者的喜爱。内置的示例库和简洁的代码结构,降低了嵌入式开发门槛,许多第三方设备、模块厂商都为 Arduino 提供了开源库,初学者可以非常简单地进行功能实现和验证。本项目通过移植 Arduino 框架,适配 bootloader,实现电脑直接与芯片交互,并通过 Arduino IDE 直接更新固件。

 

软件流程图:

 

image.png

 

三、实现过程

1.修改迁移 Bootloader

Bootloader 基于 Adafruit 的 uf2-samdx1 中使用相似芯片的 feather m4 can(SAME51J19A)进行修改,使其适配 EV76S68A。首先 clone 仓库,在 boards 文件夹下新建文件夹添加板卡定义,配置参考 feather_m4_can 文件夹,修改 LED 引脚定义;修改 inc/uf2.h,根据需要开启功能,比如 CDC,由于有 16 KB 空间可以分配给Bootloader,所以能同时开启大部分功能并将 Makefile 中的编译目标修改为刚创建的板卡名称(详细修改见附件)。 Linux 环境下安装编译器后,make 即可编译Bootloader 固件。

 image.png

这里我使用 Beaglebone Black 搭配 openocd 烧录编译好的 Bootloader 固件;将 P9.22 连接 DBG1SWCLK),将P9.18 连接 DBG0 SWDIO),通过SWD 方式烧写:

sudo config-pin P9_22 gpio

sudo config-pin P9_18 gpio

/usr/local/bin/openocd -f /usr/local/share/openocd/scripts/interface/beaglebone-swd-native.cfg -f /usr/local/share/openocd/scripts/target/atsame5x.cfg -c "init" -c "reset halt" -c "program bootloader-EV76S68A-v3.16.0-19-g4365018-dirty.bin 0x00000000 verify" -c "reset" -c "exit"

 

2. Arduino 框架移植

Arduino 官方没有对 SAM E51系列提供适配,所以依然选择 Adafruit Feather m4 CAN 作为基础进行适配。

Arduino IDE 内添加 Adafruit 自定义板卡配置在此基础上修改 boards.txt,并添加对应的 variant 文件夹,同时参考 niladridmgit/curiosityniladri 针对 EV76S68A 开发板的引脚映射,就可以让 Arduino IDE 使用 bossac 工具通过 sam-ba 协议写入编译的固件。(详细修改见附件);平台框架细节

 

image.png image.png


相关示例(C:\Users\用户名\AppData\Local\Arduino15\packages\adafruit\hardware\samd\1.7.16 文件夹下):
./boards.txt,包含编译方法、上传方法、运行速率、编译优化选项等

# Build
EV76S68A.build.mcu=cortex-m4
EV76S68A.build.f_cpu=120000000L
EV76S68A.build.usb_product="Feather M4 CAN"
EV76S68A.build.usb_manufacturer="Adafruit"
EV76S68A.build.board=EV76S68A
EV76S68A.build.core=arduino
EV76S68A.build.extra_flags=-D__SAME51J20A__ -DADAFRUIT_FEATHER_M4_EXPRESS -DADAFRUIT_FEATHER_M4_CAN -D__SAMD51__ -D__FPU_PRESENT -DARM_MATH_CM4 -mfloat-abi=hard -mfpu=fpv4-sp-d16 {build.usb_flags}
EV76S68A.build.ldscript=linker_scripts/gcc/flash_with_bootloader.ld
EV76S68A.build.openocdscript=scripts/openocd/daplink_samd51.cfg
EV76S68A.build.variant=EV76S68A
EV76S68A.build.variant_system_lib=
EV76S68A.build.vid=0x239A
EV76S68A.build.pid=0x80CD
EV76S68A.bootloader.tool=openocd
EV76S68A.bootloader.file=featherM4/bootloader-feather_m4_express-v2.0.0-adafruit.5.bin
EV76S68A.compiler.arm.cmsis.ldflags="-L{runtime.tools.CMSIS-5.4.0.path}/CMSIS/Lib/GCC/" "-L{build.variant.path}" -larm_cortexM4lf_math -mfloat-abi=hard -mfpu=fpv4-sp-d16


./variants/EV76S68A/variant.cpp,将引脚定义成数组形式:

const PinDescription g_APinDescription[]=
{
  // 0..13 - Digital pins
  // ----------------------
  // 0/1 - SERCOM/UART (Serial1)
  { PORTB, 17, PIO_SERCOM, PIN_ATTR_PWM_G, No_ADC_Channel, TCC0_CH4, NOT_ON_TIMER, EXTERNAL_INT_1 }, // RX: SERCOM5/PAD[1]
  { PORTB, 16, PIO_SERCOM, PIN_ATTR_PWM_G, No_ADC_Channel, TCC0_CH5, NOT_ON_TIMER, EXTERNAL_INT_0 }, // TX: SERCOM5/PAD[0]
 
  // 2..12
  // Digital Low
  { PORTA,   7, PIO_ANALOG, (PIN_ATTR_ANALOG|PIN_ATTR_PWM_E), ADC_Channel7, TC1_CH1, TC1_CH1, EXTERNAL_INT_7 },  // D2
  { PORTA,  22, PIO_DIGITAL, PIN_ATTR_PWM_G, No_ADC_Channel, TCC0_CH2, NOT_ON_TIMER, EXTERNAL_INT_6 }, // D13
  { PORTA,  14, PIO_DIGITAL, PIN_ATTR_PWM_F, No_ADC_Channel, TCC2_CH0, TC3_CH0, EXTERNAL_INT_14 }, // D4
  { PORTA,  15, PIO_DIGITAL, PIN_ATTR_PWM_F, No_ADC_Channel, TCC2_CH1, TC3_CH1, EXTERNAL_INT_15 }, // D5
  { PORTB,   2, PIO_ANALOG, PIN_ATTR_ANALOG, ADC_Channel14, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_2 }, // Dotstar Clock (D6)
  { PORTA,  18, PIO_DIGITAL, PIN_ATTR_PWM_F, No_ADC_Channel, TCC1_CH2, TC3_CH0, EXTERNAL_INT_2 }, // D7
...
...
};


./variants/EV76S68A/variant.h,将 Arduino 抽象引脚,如 LED_BUILTIN、PIN_WIRE_SCL,对应 variant.cpp 中数组的编号:


/*
 * Wire Interfaces
 */
#define WIRE_INTERFACES_COUNT 1


#define PIN_WIRE_SDA         (21u)
#define PIN_WIRE_SCL         (22u)
#define PERIPH_WIRE          sercom2
#define WIRE_IT_HANDLER      SERCOM2_Handler
#define WIRE_IT_HANDLER_0    SERCOM2_0_Handler
#define WIRE_IT_HANDLER_1    SERCOM2_1_Handler
#define WIRE_IT_HANDLER_2    SERCOM2_2_Handler
#define WIRE_IT_HANDLER_3    SERCOM2_3_Handler


static const uint8_t SDA = PIN_WIRE_SDA;
static const uint8_t SCL = PIN_WIRE_SCL;

/*
 * USB
 */
#define PIN_USB_HOST_ENABLE (26ul)
#define PIN_USB_DM          (27ul)
#define PIN_USB_DP          (28ul)

4. 设备连接:

SAM E51 固定使用 PA 24 PA 25 引脚分别连接到 USB D+ D-,这里我通过一个 USB Type-C 母座测试板引出 D+ D-,并通过杜邦线接触到开发板上的测试点;通过 I2C 与传感器进行通讯。

 

1750508993310.jpg

5. 具体代码实现

呼吸灯速率切换实现:检测到按键状态变化后进行消抖,再进行速率切换,一共有三种不同的速率,并通过硬件 PWM 实现呼吸灯。

 

int mode = 0; // 0: 慢,1: 中,2: 快
unsigned int breathPeriods[] = {3000, 1500, 600}; // 每种模式完整呼吸周期时间(ms)

void updateBreathingLED() {
  static unsigned long lastUpdate = 0;
  static int brightness = 0;
  static int fadeAmount = 1;

  unsigned long period = breathPeriods[mode];
  unsigned long interval = period / 512; // 一个周期内上升+下降共512步

  if (millis() - lastUpdate >= interval) {
    analogWrite(LED_BUILTIN, brightness);
    brightness += fadeAmount;
    if (brightness <= 0 || brightness >= 255) {
      fadeAmount = -fadeAmount;
    }
    lastUpdate = millis();
  }
}


通过丰富的第三方开源传感器库,快速获取传感器数据,串口波形图能同时展示的变量数量好像有限,这里使用到了STTS22H 温度传感器、ISM330DHCX imu传感器LPS22HH 气压传感器

 

// 传感器库
#include <STTS22HSensor.h>
#include <ISM330DHCXSensor.h>
#include <LPS22HHSensor.h>

#define USE_I2C_INTERFACE
STTS22HSensor Temp(&Wire);
ISM330DHCXSensor AccGyr(&Wire);
LPS22HHSensor PressTemp(&Wire);

...

void readSensors() {
  // 读取STTS22H温度
  if (Temp.GetTemperature(&temperature) != 0) {
    Serial.println("Failed to read STTS22H temperature");
    temperature = -999.0; // 错误标记
  }
 
  // 读取ISM330DHCX加速度计和陀螺仪
  if (AccGyr.ACC_GetAxes(accelerometer) != 0) {
    Serial.println("Failed to read accelerometer");
    accelerometer[0] = accelerometer[1] = accelerometer[2] = -999;
  }
 
  if (AccGyr.GYRO_GetAxes(gyroscope) != 0) {
    Serial.println("Failed to read gyroscope");
    gyroscope[0] = gyroscope[1] = gyroscope[2] = -999;
  }
 
  // 读取LPS22HH气压和温度
  if (PressTemp.GetPressure(&pressure) != 0) {
    Serial.println("Failed to read pressure");
    pressure = -999.0;
  }
 
  if (PressTemp.GetTemperature(&lps22_temperature) != 0) {
    Serial.println("Failed to read LPS22HH temperature");
    lps22_temperature = -999.0;
  }
}

6.编译并运行

点击上传按键并完成编译后,Arduino 会通过 1200 bps 的特殊串口速率使芯片进入 Bootloader使用 bossac 工具通过 sam-ba 协议上传固件。


image.png

串口波形图可视化显示传感器数据:

image.png



四、未来的计划建议

探索触摸检测应用。




附件下载
uf2-samdx1.zip
bootloader 修改
1.7.16.zip
arduino 框架修改
bootloader-EV76S68A-v3.16.0-19-g4365018-dirty.bin
bootloader 固件
Program.ino
团队介绍
评论
0 / 100
查看更多
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2024 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号