一年前(2021年1月21日),树莓派基金会推出了 4 美元的树莓派 Pico 开发板。这是第一款 RP2040 微控制器产品,也是树莓派基金会开发的全新芯片。
一年后,Pico 开发板已经售出了近 150 万个,成千上万的人在自己的电子项目和产品中使用了 RP2040。
随着树莓派官方渠道 Raspberry Pi Direct 的推出(RP2040芯片零售价是 1 美元,批量价是 0.7 美元),很快就会看到更多应用。虽然围绕 RP2040 的硬件生态系统还没有成熟,但它已经开始快速发展,在发布一年后,我们看到了各种移植语言的激增。
当开始在硬件编写软件时,在新的编程环境中运行的第一个程序通常是闪烁 LED。下面,简单介绍一下在各种不同的语言中控制 Pico 闪灯(LED 连接到 RP2040 芯片的引脚 25)。
Raspberry Pi Pico C/C++ SDK 是 Pico 和其他基于 RP2040 硬件的基础。
#include "pico/stdlib.h"
const uint LED_PIN = 25;
int main() {
gpio_init(LED_PIN);
gpio_set_dir(LED_PIN, GPIO_OUT);
while (1) {
gpio_put(LED_PIN, 0);
sleep_ms(250);
gpio_put(LED_PIN, 1);
sleep_ms(1000);
}
}
在去年 1 月,MicroPython 的官方移植是与 Raspberry Pi Pico 、C SDK 同时发布的。
from machine import Pin, Timer
led = Pin(25, Pin.OUT)
timer = Timer()
def blink(timer):
led.toggle()
timer.init(freq=2.5, mode=Timer.PERIODIC, callback=blink)
另一种在 Raspberry Pi Pico 和其他基于RP2040板上运行的 Python 是CircuitPython。CircuitPython 在去年与 Pico 一起发布,虽然它与MicroPython 有些相似,但也有一些区别。
如果需要使用中断和线程等高级功能,或者需要在 Python 中完全访问 RP2040 的可编程 I/O(PIO),一般需要使用 MicroPython。而对于传感器和 USB,则会推荐 CircuitPython。它得到了 Adafruit 的大力支持。
import time
import board
import digitalio
led = digitalio.DigitalInOut(board.LED)
led.direction = digitalio.Direction.OUTPUT
while True:
led.value = True
time.sleep(0.5)
led.value = False
time.sleep(0.5)
Kaluma 是一个微型 JavaScript 运行时,适用于由 JerryScript 提供支持的微控制器,最近刚发布。如果您是熟悉 Node.js 的 JavaScript 开发人员,现在无需学习新语言即可使用 Raspberry Pi Pico。
有一些关于如何开始使用 Kaluma 及其 Web 开发环境的优秀文档,或者如果您更喜欢在本地开发而不是在浏览器中开发,可以使用命令行界面。
var led = 25;
pinMode(led, OUTPUT);
setInterval(() => {
digitalToggle(led);
}, 1000);
有趣的是,Arduino 内核实际上有两个完全独立的版本,分别针对 RP2040 芯片和 Raspberry Pi Pico。
首先出现的是基于纯 C SDK 的社区移植版,以及一个基于 GCC 10.3 和 Newlib 4.0 的自定义工具链。有相关大量文档介绍如何开始使用社区版本以及如何将其与正在进行的 PlatformIO 集成 。
Arduino 官方版本是在社区版本移植几周后发布的,与许多最近的 Arduino 平台移植一样,核心基于 Arm Mbed 而不是直接 C SDK。如果已经安装了 Arduino 环境,则可以直接通过 Boards Manager 菜单安装官方 RP2040 版本。
无论哪种方式,使板载 LED 闪烁的代码完全相同。
#define LED 25
void setup() {
pinMode(LED, OUTPUT);
}
void loop() {
digitalWrite(LED, HIGH);
delay(1000);
digitalWrite(LED, LOW);
delay(1000);
}
作为 Raspberry Pi Pico 发布后最早出现的产品之一,Rust 现在对 RP2040 的支持相当成熟,带有用于 RP2040 芯片的硬件抽象层 (HAL),以及用于许多基于 RP2040 开发板的板级支持包。当然也包括 Raspberry Pi Pico 。除此之外,还有一个项目模板,可以作为基于 RP2040 HAL 开发的起点。
对于那些还不熟悉 Rust 的人来说,没有大量的入门文档,但有一些很好的教程。
#![no_std]
#![no_main]
use cortex_m_rt::entry;
use defmt::*;
use defmt_rtt as _;
use embedded_hal::digital::v2::OutputPin;
use embedded_time::fixed_point::FixedPoint;
use panic_probe as _;
use rp2040_hal as hal;
use hal::{
clocks::{init_clocks_and_plls, Clock},
pac,
io::Sio,
watchdog::Watchdog,
};
#[link_section = ".boot2"]
#[used]
pub static BOOT2: [u8; 256] = rp2040_boot2::BOOT_LOADER_W25Q080;
#[entry]
fn main() -> ! {
let mut pac = pac::Peripherals::take().unwrap();
let core = pac::CorePeripherals::take().unwrap();
let mut watchdog = Watchdog::new(pac.WATCHDOG);
let sio = Sio::new(pac.SIO);
let external_xtal_freq_hz = 12_000_000u32;
let clocks = init_clocks_and_plls(
external_xtal_freq_hz,
pac.XOSC,
pac.CLOCKS,
pac.PLL_SYS,
pac.PLL_USB,
&mut pac.RESETS,
&mut watchdog,
)
.ok()
.unwrap();
let mut delay = cortex_m::delay::Delay::new(core.SYST, clocks.system_clock.freq().integer()
);
let pins = hal::gpio::Pins::new(
pac.IO_BANK0,
pac.PADS_BANK0,
sio.gpio_bank0,
&mut pac.RESETS,
);
let mut led_pin = pins.gpio25.into_push_pull_output();
loop {
led_pin.set_high().unwrap();
delay.delay_ms(500);
led_pin.set_low().unwrap();
delay.delay_ms(500);
}
}
作为 Espressif ESP8266 芯片的一些早期社区构建的固件,嵌入式硬件上的 Lua 越来越受欢迎。随着 NodeMCU 的到来,ESP8266 不仅仅是为其他微控制器提供廉价 WiFi 连接的一种方式,它本身就成为了一个微控制器。
目前 Lua 环境仍处于概念验证阶段,但可以从 Github 项目存储库的源代码进行构建和查看文档。除了 Lua 运行时,该项目还包括一个基本的shell,可以接受类似 Linux 的命令、一个全屏编辑器和基本的文件管理工具。它支持数字 I/O、模拟输入、I2C 读写和硬件 PWM 的通用、轮询、GPIO 操作。它在一个核心上运行,让另一个核心空闲。目前不支持 DMA、中断、线程或多核操作。
只需几行代码,即可闪烁 LED。
gpio_pin = 25
pico.gpio_set_function (gpio_pin, GPIO_FUNC_SIO)
pico.gpio_set_dir (gpio_pin, GPIO_OUT)
while true do
pico.gpio_put (gpio_pin, HIGH)
pico.sleep_ms (300)
pico.gpio_put (gpio_pin, LOW)
pico.sleep_ms (300)
end
TinyGo 通过创建基于 LLVM 的新编译器,将 Go 编程语言引入嵌入式系统。有入门文档,YouTube 上还有一个很棒的演示,可帮助您开始闪烁 LED。
package main
import (
"machine"
"time"
)
func main() {
led := machine.LED
led.Configure(machine.PinConfig{Mode: machine.PinOutput})
for {
led.Low()
time.Sleep(time.Millisecond * 500)
led.High()
time.Sleep(time.Millisecond * 500)
}
}
FreeRTOS 不是一种语言,它是一个完整的微控制器实时操作系统(RTOS),支持RP2040的两个内核的SMP。
#include
#include
#include
#include "pico/stdlib.h"
void led_task() {
const uint LED_PIN = PICO_DEFAULT_LED_PIN;
gpio_init(LED_PIN);
gpio_set_dir(LED_PIN, GPIO_OUT);
while (true) {
gpio_put(LED_PIN, 1);
vTaskDelay(100);
gpio_put(LED_PIN, 0);
vTaskDelay(100);
}
}
int main() {
stdio_init_all();
xTaskCreate(led_task, "LED_Task", 256, NULL, 1, NULL);
vTaskStartScheduler();
while(1){};
}
此外,NET 、Forth、Ada 等语言也在进行移植了。
除了官方列举的内容之外,爱好者们还补充了下面的内容:
▶ free pascal
开源跨平台的 free pascal 继承了 Delphi 的精髓,现在也支持 Pico 了。
program blinky;
{$MODE OBJFPC}
{$H+}
{$MEMORY 10000,10000}
uses
pico_gpio_c,
pico_timer_c;
begin
gpio_init(TPicoPin.LED);
gpio_set_dir(TPicoPin.LED,TGPIODirection.GPIO_OUT);
repeat
gpio_put(TPicoPin.LED,true);
busy_wait_us_32(500000);
gpio_put(TPicoPin.LED,false);
busy_wait_us_32(500000);
until 1=0;
end.
▶ Lisp
uLisp 的 ARM 版本包括一个 ARM 汇编器,它允许您生成与 Lisp 集成的机器代码函数,并用 ARM 拇指代码编写。汇编器本身是用 Lisp 编写的,以便于扩展它或添加新指令。
需要通过 Arduino IDE 安装 uLisp。
(defun blink (&optional x)
(pinmode 25 t)
(digitalwrite 25 x)
(delay 1000)
(blink (not x)))