Arduino Nano 33 BLE Sense

警告

该开发板已不再受支持。 针对 Arduino Nano 33 BLE Sense 的最后一个 OpenMV 固件版本是 4.7.0。该目标平台将不再发布任何后续的固件更新、缺陷修复或新功能。以下信息仅为仍在运行 4.7.0 或更早版本的用户保留。

Arduino Nano 33 BLE Sense 是一款 45 × 18 mm、采用 Arduino-Nano 外形规格的开发板,围绕 Nordic Semiconductor nRF52840 打造——这是一颗主频 64 MHz、带 FPU 的单核 ARM Cortex-M4,内置 256 KB 片内 SRAM 和 1 MB 片内闪存。BLE 由片上无线电提供,开发板还搭载了 9 轴 IMU、LPS22HB 气压计、HTS221 / HS3003 温湿度传感器、APDS9960 环境光 / 颜色 / 接近 / 手势传感器以及 MP34DT05 PDM 麦克风。OpenMV 固件通过 MicroPython 驱动以上全部器件。

Arduino Nano 33 BLE Sense

完整的数据手册、照片和尺寸请参阅 Arduino Nano 33 BLE Rev2 产品页面

亮点

  • Nordic nRF52840 主频 64 MHz、带 FPU 的 Cortex-M4,内置 256 KB 片内 SRAM1 MB 片内闪存

  • Bluetooth LE 5.0,通过片上无线电和 Nordic SoftDevice s140 实现。

  • 9 轴 IMU——Rev 1 上为 LSM9DS1,Rev 2 上为 BMI270 + BMM150。固化的 imu 驱动会在启动时探测两者。

  • LPS22HB 气压计、HTS221 / HS3003 温湿度传感器、APDS9960 环境光 / 颜色 / 接近 / 手势传感器,以及 MP34DT05 PDM 麦克风。

  • Micro USB 连接器,用于供电、编程和 CDC REPL。

  • 标准 Nano 排针上的 22 个用户 I/O 引脚——TX/RXD2D13(数字)、A0A7(模拟)。

引脚分布

Arduino Nano 33 BLE Sense 引脚分布

引脚参考

引脚名称

参考

功能

TX

3.3 V

UART1 TX

RX

3.3 V

UART1 RX

D2

3.3 V

PWM

D3

3.3 V

PWM

D4

3.3 V

PWM

D5

3.3 V

PWM

D6

3.3 V

PWM

D7

3.3 V

PWM

D8

3.3 V

PWM

D9

3.3 V

PWM

D10

3.3 V

PWM

D11

3.3 V

PWM / SPI0 MOSI

D12

3.3 V

PWM / SPI0 MISO

D13

3.3 V

PWM / SPI0 SCK

A0

3.3 V

ADC / PWM

A1

3.3 V

ADC / PWM

A2

3.3 V

ADC / PWM

A3

3.3 V

ADC / PWM

A4 / I2C_SDA

3.3 V

ADC / PWM / I2C0 SDA

A5 / I2C_SCL

3.3 V

ADC / PWM / I2C0 SCL

A6

3.3 V

ADC / PWM

A7

3.3 V

ADC / PWM

RESET

3.3 V

按下板载 RESET 按钮或拉至 GND 以复位

LED_BUILTIN

位于 D13 上的橙色用户 LED

LED_RED

RGB LED 红色通道(低电平有效)

LED_GREEN

RGB LED 绿色通道(低电平有效)

LED_BLUE

RGB LED 蓝色通道(低电平有效)

警告

Nano 33 BLE Sense 的 I/O 引脚 仅支持 3.3 V——它们 不能耐受 5 V。向其灌入 5 V 会损坏 nRF52840。

电源引脚

  • VIN——4.5 – 21 V 输入。通过板载稳压器为开发板供电。同时还经一个二极管由 USB 5 V 供电轨馈入,因此 USB 和 VIN 可以同时存在而不会相互倒灌。

  • +5V——默认不连接。

  • +3V3——3.3 V 稳压器输出。

  • AREF——模拟参考引脚。在本开发板上未连接到 nRF52840——ADC 始终以 3.3 V 为参考。

  • GND——公共地。

Nano 33 BLE Sense 可通过以下任一路径供电:

  • Micro USB——向板载稳压器提供 5 V。

  • VIN 引脚——接入 4.5 – 21 V 的稳压电源。

备注

开发板底部有一个标注为 VUSB 的焊接跳线,用于将 +5V 桥接到 USB 5 V 供电轨。闭合它即可让 +5V 排针引脚实际输出 5 V。

备注

板载 4.5–21 V 开关稳压器输出端有一个常闭焊接跳线,可将其割断以禁用稳压器,从而可在 +3V3 上通过外部 3.3 V 电源直接为开发板供电。

恢复与调试引脚

  • RESET——开发板顶部既有一个裸露焊盘,也有一个瞬时 RESET 按钮,均连接到 nRF52840 的复位线。拉至 GND 或按下按钮即可复位。

Nano 33 BLE Sense 使用 Arduino 标准的 双击复位 进入 Arduino 引导加载程序。快速按两次 RESET 按钮——开发板即进入引导加载程序模式,OpenMV IDE 便可烧录新的固件镜像。

nRF52840 的 SWD 信号引出至开发板背面的镀层焊盘。所有调试信号均以 3.3 V 为参考

板载外设

LED

Nano 33 BLE Sense 拥有一个用户 RGB LED——通过丝印的 LED_REDLED_GREENLED_BLUE 通道驱动——外加位于 D13 上的一个独立橙色 LED_BUILTIN。这四者均可通过 machine.LED 由软件控制:

from machine import LED

LED("LED_RED").on()
LED("LED_GREEN").on()
LED("LED_BLUE").on()
LED("LED_BUILTIN").on()

开发板上还有一个独立的绿色 电源 LED,只要 +3.3 V 供电轨上电它便会点亮,且不可由用户控制。

摄像头传感器

Nano 33 BLE Sense 上的 OpenMV 固件支持 OmniVision OV7670 并行 CMOS 传感器。开发板没有板载图像传感器——请将 OV7670 模块接到下列丝印排针引脚上,并通过 csi --- 摄像头传感器 模块驱动它:

import csi

cam = csi.CSI()
cam.reset()
cam.pixformat(csi.RGB565)
cam.framesize(csi.QVGA)
cam.snapshot(time=2000)       # let auto‑exposure settle

while True:
    img = cam.snapshot()

备注

OV7670 需要 14 个引脚。固件按如下方式连线:

传感器信号

Nano 33 BLE Sense 引脚

D0

D10

D1

TX

D2

RX

D3

D2

D4

D3

D5

D5

D6

D6

D7

D4

HSYNC

A1

VSYNC

D8

PXCLK

A0

MXCLK

D9

POWER

A3

RESET

A2

SCL

A5(I²C 0)

SDA

A4(I²C 0)

OV7670 的 I²C 控制总线与引出至 A5/A4 的外部 I²C 0 是同一条。该传感器的 7 位地址为 0x21——接入摄像头后,该总线上的用户设备必须避开此地址。

IMU

9 轴 IMU 通过固化的 imu 模块引出,该模块会自动检测开发板搭载的是 LSM9DS1(Rev 1)还是 BMI270 + BMM150(Rev 2),并提供统一的 imu.IMU 类。这些传感器位于内部 I²C 1 总线(P14 / P15)上:

import time
from machine import I2C, Pin
from imu import IMU

bus = I2C(1, scl=Pin("P15"), sda=Pin("P14"))
sensor = IMU(bus)

while True:
    print(sensor.accel())     # (x, y, z) in g
    print(sensor.gyro())      # (x, y, z) in deg/s
    print(sensor.magnet())    # (x, y, z) magnetometer
    time.sleep_ms(100)

若需直接访问诸如敲击检测或 FIFO 等功能,请导入对应的固化驱动(lsm9ds1bmi270bmm150),并在同一总线上将其实例化。

环境传感器

气压计(LPS22HB)和温湿度传感器(Rev 1 上为 HTS221,Rev 2 上为 HS3003)与 IMU 共用同一条内部 I²C 1 总线:

import time
from machine import I2C, Pin
from lps22h import LPS22H
from hts221 import HTS221

bus = I2C(1, scl=Pin("P15"), sda=Pin("P14"))
lps = LPS22H(bus)
try:
    hts = HTS221(bus)
except OSError:
    from hs3003 import HS3003
    hts = HS3003(bus)

while True:
    print("pressure:    %.2f hPa" % lps.pressure())
    print("temperature: %.2f C"   % lps.temperature())
    print("humidity:    %.2f %%"  % hts.humidity())
    time.sleep_ms(500)

光线 / 颜色 / 接近 / 手势

Broadcom APDS9960 位于同一条内部 I²C 1 总线上,可提供环境光、RGB 颜色、接近和手势检测:

import time
from machine import I2C, Pin
from apds9960 import uAPDS9960 as APDS9960

bus = I2C(1, scl=Pin("P15"), sda=Pin("P14"))
apds = APDS9960(bus)
apds.enableLightSensor()

while True:
    print("ambient light:", apds.readAmbientLight())
    time.sleep_ms(250)

麦克风

板载 MP34DT05 PDM 麦克风通过 audio --- 音频模块 采集。每个缓冲区以有符号 16 位 PCM bytearray 的形式到达,可直接送入 ulab/numpy 进行 DSP 处理:

import audio
from ulab import numpy as np

def loudness(pcmbuf):
    samples = np.array(np.frombuffer(pcmbuf, dtype=np.int16), dtype=np.float)
    rms = np.sqrt(np.mean(samples ** 2))
    if rms > 10000:
        print("Loud!", int(rms))

audio.init(channels=1, frequency=16000, gain_db=24)
audio.start_streaming(loudness)

while True:
    pass

Bluetooth

nRF52840 的 Bluetooth LE 5.0 无线电运行在 Nordic SoftDevice s140 上,并通过旧版 ubluepy 模块引出——本固件中未启用现代的 bluetooth / aioble --- 异步 BLE API。外设(GATT 服务器、广播)和 中心(GAP 观察者 / 扫描器 + 连接)两种角色均可使用。

以外设角色进行广播,提供单个 Environmental Sensing 服务和一个可通知的温度特性——event_handler 回调会在连接、断开和 CCCD 写入时触发:

from ubluepy import Service, Characteristic, UUID, Peripheral, constants
from machine import LED

def event_handler(event_id, handle, data):
    if event_id == constants.EVT_GAP_CONNECTED:
        LED("LED_GREEN").on()
    elif event_id == constants.EVT_GAP_DISCONNECTED:
        LED("LED_GREEN").off()
        periph.advertise(device_name="Nano 33", services=[svc])

svc = Service(UUID("181A"))                          # Environmental Sensing
char = Characteristic(UUID("2A6E"),                  # Temperature
                      props=Characteristic.PROP_NOTIFY | Characteristic.PROP_READ,
                      attrs=Characteristic.ATTR_CCCD)
svc.addCharacteristic(char)

periph = Peripheral()
periph.addService(svc)
periph.setConnectionHandler(event_handler)
periph.advertise(device_name="Nano 33", services=[svc])

以中心角色扫描附近正在广播的设备:

from ubluepy import Scanner

for entry in Scanner().scan(1_000):                  # 1 second window
    print(entry.addr(), entry.rssi(), "dBm")

完整 API 请参阅 ubluepy 参考文档——UUIDServiceCharacteristicPeripheralScannerScanEntry 以及 constants 命名空间。

总线参考

GPIO

使用 machine.Pin 读取或驱动任意丝印引脚。输出为 3.3 V CMOS——每个引脚 15 mA,所有 GPIO 合计 25 mA。

from machine import Pin

out = Pin("D2", Pin.OUT)
out.on()
out.off()
out.value(1)

inp = Pin("D3", Pin.IN, Pin.PULL_UP)
print(inp.value())

任意输入引脚还可在边沿跳变时触发中断:

def handler(pin):
    print("triggered:", pin)

Pin("D3", Pin.IN, Pin.PULL_UP).irq(
    handler, Pin.IRQ_FALLING | Pin.IRQ_RISING,
)

UART

总线

TX

RX

UART1

TX

RX

结合 machine.UART 使用丝印名称 TX/RX:

from machine import UART

uart = UART(1, baudrate=115200)
uart.write("hello")
uart.read(5)

I²C

总线

SDA

SCL

I2C0

I2C_SDA / A4

I2C_SCL / A5

I2C1

P14

P15

两条总线都需要将引脚显式传给 machine.I2C:

from machine import I2C, Pin

bus0 = I2C(0, scl=Pin("I2C_SCL"), sda=Pin("I2C_SDA"), freq=400_000)
bus0.scan()

bus1 = I2C(1, scl=Pin("P15"), sda=Pin("P14"), freq=400_000)
bus1.scan()

备注

总线 1 是位于 P14/P15 上的内部传感器总线(不在用户排针上)——它服务于 IMU、气压计、环境传感器和 APDS9960。固化的传感器驱动直接使用它;用户代码也可以扫描它,但这些地址已被板载传感器占用。

SPI

总线

MOSI

MISO

SCK

CS

SPI0

D11

D12

D13

D10

CS 线不由 SPI 外设驱动——请将 D10 配置为输出,并在传输前后手动翻转它:

from machine import SPI, Pin

spi = SPI(0, baudrate=10_000_000)
cs = Pin("D10", Pin.OUT, value=1)   # CS is not driven by the SPI peripheral

cs.value(0)
spi.write(b"hello")
cs.value(1)

备注

D13 同时兼作橙色 LED_BUILTIN——在该总线上驱动 SPI 会使该 LED 随总线时钟同步闪烁。

ADC

nRF52840 拥有八个 12 位 ADC 通道(SAADC),引出至 A0–A7,全部 以 3.3 V 为参考——read_u16 在引脚 0–3.3 V 范围内返回 0–65535。开发板的 AREF 引脚未连接,因此参考电压始终为 3.3 V:

from machine import ADC
import time

adc = ADC("A0")
while True:
    voltage = adc.read_u16() * 3.3 / 65535
    print(voltage)
    time.sleep_ms(100)

PWM

nRF52840 提供 四个 PWM 外设PWM0PWM3),每个驱动 四个通道,共计 16 个硬件 PWM 槽位。与固定功能的端口不同,这些外设通过 GPIOTE 矩阵进行路由——任意 GPIO 都可作为 PWM 输出,因此不存在引脚到切片的固定映射。这种灵活性的代价是硅片中固化的两条约束:

  • 一个模块内的全部四个通道共享 单一的周期/频率

  • 每个通道有其 各自的占空比 和极性。

从概念上看,这 16 个槽位如下所示:

模块

通道 0

通道 1

通道 2

通道 3

PWM0

占空比

占空比

占空比

占空比

PWM1

占空比

占空比

占空比

占空比

PWM2

占空比

占空比

占空比

占空比

PWM3

占空比

占空比

占空比

占空比

每一行以一个频率运行;行中的四个单元各自驱动一个独立选定的引脚,并带有各自的占空比。不同的行可以以完全不同的频率运行。

通过 machine.PWM 驱动任意丝印引脚(或板载 LED):

from machine import Pin, PWM

pwm = PWM(Pin("D3"), freq=1_000, duty_u16=32768)

警告

自动分配每次调用会占用一整个模块。 当你创建一个不带 device=/channel= 关键字参数的 PWM 时,驱动会抓取第一个空闲模块,并将你的引脚只绑定到它的 通道 0。该模块剩下的三个通道处于闲置状态,只能通过显式的 device=/channel= 访问。这使得无辅助的 PWM(Pin(...)) 调用上限为 四次,超过后驱动会抛出 ValueError: all PWM devices in use——尽管从技术上讲仍有十二个槽位空闲。

若要使用超过四个 PWM,或要刻意在多个引脚间共享频率,请传入 device(0–3)和 channel(0–3):

# Two PWMs on the same module → forced to share frequency,
# but each gets its own duty cycle.
pwm_a = PWM(Pin("D3"), device=0, channel=0,
            freq=1_000, duty_u16=32768)
pwm_b = PWM(Pin("D5"), device=0, channel=1,
            freq=1_000, duty_u16=16384)

# A third PWM on a separate module, free to pick any frequency.
pwm_c = PWM(Pin("D6"), device=1, channel=0,
            freq=20_000, duty_u16=49152)

占空比接受 duty(0–100%)、duty_u16(0–65535)或 duty_ns。添加 invert=1 可翻转输出极性(对低电平有效的 RGB LED 很方便)。

备注

由于频率是每个模块的属性,对某个模块的 任意 通道调用 pwm.freq(new_freq) 都会对整个模块重新运行 nrfx_pwm_init,并改变共享该模块的所有其他通道所看到的频率。

备注

允许的频率范围大致为 4 Hz 到 5.3 MHz,由 16 MHz 基准时钟经 1/2/4/8/16/32/64/128 分频器和一个 15 位周期计数器推导而来。驱动会自动选取最接近的分频值——freq() 报告的是请求值,而非实际可达到的精确值。

软件位拨总线

若你需要额外的总线,machine.SoftI2Cmachine.SoftSPI 可在任意 GPIO 上工作。

热成像传感器(板外)

固件包含 fir --- 热成像传感器驱动(fir == 远红外) 驱动,用于外接连线的热成像仪:

  • MLX90621——16 × 4 红外阵列

  • MLX90640——32 × 24 红外阵列

  • MLX90641——16 × 12 红外阵列

  • AMG8833——8 × 8 红外阵列

将模块接到开发板的 I²C 总线上,并用 fir.init() + fir.snapshot() 读取帧:

import time
import image
import fir

fir.init()                          # auto‑detects the sensor
clock = time.clock()

while True:
    clock.tick()
    try:
        img = fir.snapshot(x_scale=5, y_scale=5,
                           color_palette=image.PALETTE_IRONBOW,
                           hint=image.BICUBIC,
                           copy_to_fb=True)
    except OSError:
        continue
    print(clock.fps())

fir 驱动只通过 I²C 0 与传感器通信——请将模块接到 I2C_SCL / I2C_SDA 焊盘(A5 / A4)。

计时

time

time 模块涵盖阻塞延时、单调计时和耗时测量:

import time

time.sleep(1)              # seconds
time.sleep_ms(500)
time.sleep_us(10)

start = time.ticks_ms()
# ...do work...
elapsed = time.ticks_diff(time.ticks_ms(), start)

虚拟定时器

machine.Timer 可调度周期性或单次回调,且不占用硬件定时器槽位。将 -1 作为 id 传入即可使用虚拟(软件)定时器:

from machine import Timer

one_shot = Timer(-1)
one_shot.init(period=5_000, mode=Timer.ONE_SHOT,
              callback=lambda t: print("once"))

periodic = Timer(-1)
periodic.init(period=2_000, mode=Timer.PERIODIC,
              callback=lambda t: print("tick"))

周期值以毫秒为单位。调用 deinit() 可停止并释放该槽位。

实时时钟

machine.RTC 可在复位间保持挂钟时间。nRF52840 的 RTC 连接到片上振荡器,无法在完全断电后保持——若这对你的应用很重要,请在每次冷启动时设置时间:

from machine import RTC

rtc = RTC()
rtc.datetime((2026, 4, 30, 4, 12, 0, 0, 0))   # Y, M, D, weekday, h, m, s, subsec
print(rtc.datetime())

看门狗

若应用挂起,machine.WDT 会复位开发板。一旦启动便无法停止或重新配置——请在主循环中定期喂狗:

from machine import WDT

wdt = WDT(timeout=5_000)   # 5 second window
while True:
    # ...do work...
    wdt.feed()

启动与运行时信息

固件更新

Nano 33 BLE Sense 使用 Arduino 标准的 双击复位 进入 Arduino 引导加载程序。快速按两次 RESET 按钮——开发板即进入引导加载程序模式,OpenMV IDE 便可烧录新的固件镜像。

正在运行的脚本可通过调用 machine.bootloader() 按需重新进入引导加载程序:

import machine

machine.bootloader()

文件系统与启动顺序

Nano 33 BLE Sense 固件在启动时挂载单个文件系统:

  • 片内闪存——始终挂载在 /flash 并用作工作目录。默认存放 main.pyREADME.txt;在首次启动时创建。

挂载之后,解释器随即从 /flash 运行脚本:

  • boot.py每次 软复位时执行。

  • main.py 仅在冷启动时 执行,紧接在 boot.py 之后。

刚烧录的开发板上随附的默认 main.py 只是将用户 RGB LED 的 蓝色 通道作为心跳闪烁(两次短脉冲,短暂间隔),这样你无需连接任何主机即可判断固件是否干净地启动了。

在本开发板上,/flash 不会 作为 USB 大容量存储驱动器暴露。

存储容量

Nano 33 BLE Sense 随附:

  • /flash——64 KB FAT 文件系统,可读写。

Nano 33 BLE Sense 构建不包含 ROMFS;请直接将 Python 模块放在 /flash 上。

软件库

完整的模块列表请参阅 库索引——其中包括哪些模块是 Nano 33 BLE Sense 构建所独有的。