一、项目介绍
使用小脚丫RP2350核心板以及配套综合扩展训练板,实现一个简易的数字温度与电压表,由按键控制切换功能,并在OLED屏幕上显示相关信息。
二、硬件介绍
RP2350系列是树莓派公司推出的第二代自研微控制器芯片,于2024年8月随RaspberryPiPico2发布,是继RP2040之后的重要升级。RP2350B是该系列的高引脚数版本,相比RP2350A提供更多GPIO与更强扩展能力。
小脚丫RP2350核心板基于RP2350B芯片,凭借GPIO数量优势,板载了一些硬件的同时,仍保留了可观数量的引出排针。
RP2350系列芯片可选择ARM Cortex‑M33或Hazard3 RISC‑V内核,主频最高150MHz。片内内置520KB SRAM,是RP2040的两倍,可以更好发挥两个核心带来的多任务处理能力。Flash除RP2354系列外均需外置,通常核心板会配备4MB、8MB或16MB的容量。如果有需求,也可以通过SPI或者PIO总线外接更多Flash或其他存储器。
树莓派是全球最受欢迎的单板计算机(SBC)系列之一,以低成本、小体积和强大的社区生态著称。它不仅是硬件产品,更是推动计算机教育普及的重要力量。树莓派的诞生源于一个简单但深刻的愿景:让每个人都能轻松获得可编程计算机,尤其是学生与创客。2006年左右,剑桥大学计算机系的几位成员注意到:新生的编程能力逐年下降,原因之一是缺乏可负担的实验硬件。于是他们开始设计一种廉价、可编程的小型计算机。树莓派基金会(Raspberry Pi Foundation)于2009年成立,后面依次推出了第一代Raspberry Pi Model B、第二代Pi2、第三代Pi3、第四代Pi4共计4代SBC。2021年,基金会开始探索更低成本的嵌入式应用场景,发布了Pi Zero2,以低成本高性能高扩展性打入更大市场。
RP2系列则是树莓派基金会针对MCU市场推出的产品。2024年推出的RP2350是RP2040的全面升级版本,在性能、安全性与扩展能力方面均有显著提升。其核心提供双核ARM Cortex‑M33或双核RISC‑V Hazard3(32IMAC)两种选择,主频提升至150MHz,并配备520KB片上SRAM。除继续采用QSPI外置Flash外,部分型号还集成堆叠式片上闪存。Cortex‑M33支持浮点运算与ARM TrustZone,适用于安全性要求较高的工业应用;RISC‑V版本则强化开源生态兼容性。外设方面,GPIO数量提升至30或48路,PWM通道扩展至24路,PIO状态机增至12个,使其能够处理更复杂的高速外设协议。凭借这些改进,RP2350不仅覆盖RP2040的传统应用场景,还能支持边缘AI推理、复杂机器人控制与工业物联网等高负载任务,同时保持与RP2040的引脚兼容性,降低了升级成本。
小脚丫RP2350核心板板载了8颗矩阵单色LED、2颗WS2812 RGB LED、1颗独立单色LED、2个7段数码管、4个拨码开关、4个轻触按键和一片KXTJ3-1057三轴加速度传感器,并引出了剩余GPIO引脚,充分利用RP2350B芯片GPIO数量的优势,实现了丰富的板载资源与优秀拓展性共存。利用核心板资源即可轻松实现加速度计、水平仪等应用,例如,购买后自带的例程即综合运用了加速度计、数码管、按键和WS2812彩色LED。
配套的综合扩展训练板是原StepFPGA核心板系列的配套拓展板,由于硬禾RP2350核心板兼容StepFPGA核心板系列的引脚排布,因此可以直接使用。其包含一块OLED、一个蜂鸣器、一个温度传感器、一个ADC和一个DAC网络,并板载了USB-TTL转换以方便进行串口下载调试。
本次实验主要使用OLED屏幕、温度传感器(DS18B20,需注意板载款非Maxim原厂产品,不支持ROM相关功能)、ADC(ADS7868)和蜂鸣器。
三、系统架构
在Pico SDK平台上使用C++语言编程,与扩展板上的ADC和温度传感器通信,从而获得电压和温度数据。然后通过SPI协议与OLED屏幕控制器通信,显示接收到的数据。此外还需要监听按键组的引脚电压,以判断按键是否按下并执行响应操作。

四、软件架构
在核心1上运行初始化代码、PWM控制代码以及OLED控制代码,而核心2负责持续进行ADC转换、按键消抖算法并将结果通过跨核IPC通知核心1。核心1接到IPC消息后切换显示页面,并更新对应的OLED屏幕显示信息,实现按键切换页面效果。另外核心1主循环中持续采集温度与电压数据,并将数据以图形和文字形式绘制到OLED显示屏和数码管上。

五、硬件连接
由于核心板与扩展板配套,仅需连接二者,再使用Type-C数据线将核心板与PC连接即可。

六、软件代码和结构
- main.cpp为入口文件,负责初始化和拉起第二核心,以及主循环中的oled上层逻辑控制。
- oled.cpp中包含了OLED初始化和通信的一些底层代码。
- key.cpp中包含了在核心2上运行的ADC按键及其消抖逻辑。
- ext_adc.cpp与ext_temp.cpp中包含了与ADC和温度传感器通信的代码。
- 其余文件包括了一些辅助函数和数据数组。
主要代码main.cpp内容如下:
#include "common.h"
#include "hardware/clocks.h"
#include "hardware/pwm.h"
#include "hardware/uart.h"
#include "pico/multicore.h"
#include <stdio.h>
#define IO4_PWM_TEST 0
extern void oled_init();
extern void oled_clear();
extern void oled_disp_cover();
extern void oled_print(uint8_t x, uint8_t y, const char *s);
extern void oled_show16(bool vol_or_temp, float value, bool is_alert);
extern void seg_init();
extern void seg_set(uint8_t num);
extern void ext_adc_init();
extern uint16_t ext_adc_read();
extern void ext_temp_init();
extern void ext_temp_start();
extern float ext_temp_read();
extern void thread1();
#define dbg(x) \
if (logo_cnt == 0) printf("%s\n", x)
int main() {
stdio_init_all();
if (false) {
volatile int dummy;
const int max_wait_sec = 15;
// timeout after XXXs if no usb-uart connected
for (int i = 0; i < max_wait_sec; i++) {
printf("Waiting for keypress %d/%d ...\n", i, max_wait_sec);
dummy = getchar_timeout_us(1000000);
if (dummy >= 0) break;
}
}
// Avoid wierd bug on pico USB stdio: (sleep >=8ms)
// Immediately call `printf` would makes later call to `multicore_fifo_pop` stall the whole chip (including PWM)
sleep_ms(100);
printf("System Clock Frequency is %d Hz\n", clock_get_hz(clk_sys));
printf("USB Clock Frequency is %d Hz\n", clock_get_hz(clk_usb));
gpio_init_out(BUZZER_PIN, 0);
gpio_init_out(LED_PIN, 0);
gpio_init_out(LED_IO1_PIN, 1);
gpio_init_out(LED_IO2_PIN, 1);
gpio_init_out(LED_IO3_PIN, 1);
gpio_init_out(LED_IO4_PIN, 1);
oled_init();
seg_init();
ext_adc_init();
ext_temp_init();
multicore_launch_core1(thread1);
sleep_ms(10);
// PWM
gpio_set_function(LED_IO1_PIN, GPIO_FUNC_PWM);
pwm_set_output_polarity(LED_IO1_PWM_SLICE, true, true);
pwm_set_wrap(LED_IO1_PWM_SLICE, 65535);
pwm_set_clkdiv(LED_IO1_PWM_SLICE, 255.9375);
pwm_set_chan_level(LED_IO1_PWM_SLICE, LED_IO1_PIN & 1 ? PWM_CHAN_B : PWM_CHAN_A, 3000);
pwm_set_enabled(LED_IO1_PWM_SLICE, false);
gpio_set_function(BUZZER_PIN, GPIO_FUNC_PWM);
pwm_set_output_polarity(LED_IO1_PWM_SLICE, true, true);
pwm_set_wrap(BUZZER_PWM_SLICE, 2240);
pwm_set_clkdiv(BUZZER_PWM_SLICE, 255.9375); // 252=595238Hz, 255.9375=586080.586080586080586080Hz
pwm_set_chan_level(BUZZER_PWM_SLICE, BUZZER_PIN & 1 ? PWM_CHAN_B : PWM_CHAN_A, 150);
pwm_set_enabled(BUZZER_PWM_SLICE, false);
// oled_disp_cover();
// sleep_ms(1000);
oled_clear();
absolute_time_t last_time = 0;
absolute_time_t last_temp_time = 0;
int page = 0;
float voltage = 1.5;
float temperature = 10.0;
float temp_alert = 30.0;
while (true) {
absolute_time_t now = get_absolute_time();
if (now - last_time < 1000000 / 120) {
// Lock to 120Hz
continue;
}
last_time = now;
seg_set(page);
voltage = ext_adc_read() / (float)(1 << 12) * 3.3;
if (now - last_temp_time > 1000000) {
temperature = ext_temp_read();
ext_temp_start();
last_temp_time = get_absolute_time();
}
if (page == 0) { // 电压
oled_show16(true, voltage, false);
} else if (page == 1) { // 温度
oled_show16(false, temperature, false);
} else if (page == 2) { // 温度警报
oled_show16(false, temp_alert, true);
}
uint32_t key = 0xffff;
if (multicore_fifo_pop_timeout_us(1000, &key)) {
// printf("Keypress %d got\n", key);
if (key == 4) { // 切页面
if (++page >= 3) {
page = 0;
}
} else if (page == 2 && key == 1) {
temp_alert += 1.0;
} else if (page == 2 && key == 2) {
temp_alert -= 1.0;
}
}
if (temperature > temp_alert) {
pwm_set_enabled(BUZZER_PWM_SLICE, true);
pwm_set_enabled(LED_IO1_PWM_SLICE, true);
} else {
pwm_set_enabled(BUZZER_PWM_SLICE, false);
pwm_set_enabled(LED_IO1_PWM_SLICE, false);
}
}
while (true)
;
}
七、测试运行
上电初始化完成后,进入电压测量状态。同步显示状态条和数字。数码管显示00。

按键切换显示温度页面。数码管显示11。

再次按下按键切换显示温度报警设置页面,同时可以使用最左侧两个按键调节设置的报警值。数码管显示22。

其余功能(例如温度声光报警)图片无法展示,请参见演示视频
八、总结与展望
本次项目探索了实验平台的开发与调试流程,熟悉了ADC和1-Wire设备的操作,精进了多核心开发和跨核心同步,特别是修复了一些之前版本ADC按键的识别错误。未来可以依托RP2350B平台的扩展性进行更多实验。