2026寒假练 - 基于小脚丫RP2350核心板及扩展板实现OLED数字温度与电压表
该项目使用了小脚丫RP2350核心板及扩展板,实现了OLED数字温度与电压表的设计,它的主要功能为:由按键控制切换功能,并在OLED屏幕上显示电压和温度信息。
标签
ADC
RP2350
DS18030
贪吃蛇
更新2026-03-24
21

一、项目介绍

使用小脚丫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屏幕控制器通信,显示接收到的数据。此外还需要监听按键组的引脚电压,以判断按键是否按下并执行响应操作。

arch.png

四、软件架构

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

flow.png

五、硬件连接

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

pic.jpg

六、软件代码和结构

  • 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。

t1.jpg

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

t2.jpg

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

t3.jpg

其余功能(例如温度声光报警)图片无法展示,请参见演示视频

八、总结与展望

本次项目探索了实验平台的开发与调试流程,熟悉了ADC和1-Wire设备的操作,精进了多核心开发和跨核心同步,特别是修复了一些之前版本ADC按键的识别错误。未来可以依托RP2350B平台的扩展性进行更多实验。


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