【瑞萨RA × Zephyr评测】SPI 屏 (SSD1306) + 双路 ADC

Zephyr 开发笔记:RA6E2 SPI 屏 (SSD1306) + 双路 ADC

1. 硬件接线表 (根据代码分析)

根据你的 app.overlay 配置,硬件连接如下。请务必检查物理连线是否一致:

模块功能 (Pin Name)RA6E2 引脚备注
SSD1306D0 (SCK/CLK)P111SPI0 RSPCK (硬件 SPI 时钟)
D1 (MOSI/DIN)P109SPI0 MOSI (硬件 SPI 数据)
RES (Reset)P208GPIO 输出
DC (Data/Cmd)P110GPIO 输出
CS (Chip Sel)P301GPIO 输出
VCC3.3V
GNDGND
ADCAN002P002模拟输入通道 2
AN004P004模拟输入通道 4

注意:你的代码中 D/C 使用的是 P110 (<&ioport1 10 ...>),而 MOSI 使用的是 P109 (RA_PSEL(..., 1, 9))。这与常见的 P109/P110 功能互换不同,请确保接线严格对应代码。


在这里插入图片描述
在这里插入图片描述

2. 软件配置

2.1 项目配置 (prj.conf)

这里启用了 SPI 驱动,并配置了 CFB 字体。

# --- 系统基础 ---
CONFIG_STDOUT_CONSOLE=y
CONFIG_LOG=y
CONFIG_HEAP_MEM_POOL_SIZE=4096

# --- 外设驱动 ---
CONFIG_GPIO=y
CONFIG_SPI=y
CONFIG_ADC=y

# --- 显示系统 (SSD1306 SPI) ---
CONFIG_DISPLAY=y
CONFIG_SSD1306=y
CONFIG_SSD1306_DEFAULT_CONTRAST=128

# --- 图形库 (CFB) ---
CONFIG_CHARACTER_FRAMEBUFFER=y
# 保持默认字体设置,避免字体宏冲突
CONFIG_CHARACTER_FRAMEBUFFER_USE_DEFAULT_FONTS=y

# --- 日志级别 ---
CONFIG_ADC_LOG_LEVEL_INF=y

2.2 设备树覆盖 (app.overlay)

这是最核心的部分。我们做了两件事:

  1. ADC 配置:添加 P002 和 P004 到 ADC0。
  2. SPI 自定义配置:这是难点。系统默认的 spi0 可能包含 P109/P110/P111 全套引脚。我们需要定义一个新的引脚组 spi0_new_custom只包含 SCK 和 MOSI,把其他引脚释放出来给 GPIO 使用。
/*
 * SSD1306 SPI Overlay for Renesas FPB-RA6E2
 */

#include <zephyr/dt-bindings/gpio/gpio.h>
#include <zephyr/dt-bindings/pinctrl/renesas/pinctrl-ra.h>

/ {
	chosen {
		zephyr,display = &ssd1306_spi; /* 指定使用 SPI 屏作为主显示 */
	};
};

/* 强制开启相关 GPIO 端口控制 */
&ioport1 { status = "okay"; };
&ioport2 { status = "okay"; };
&ioport3 { status = "okay"; };

&pinctrl {
	/* 
	 * 自定义 SPI0 引脚组 
	 * 仅配置 MOSI (P109) 和 SCK (P111) 给 SPI 控制器
	 * P110 未在此处出现,因此可以被用作普通 GPIO (D/C)
	 */
	spi0_new_custom: spi0_new_custom {
		group1 {
			psels = <RA_PSEL(RA_PSEL_SPI, 1, 9)>,  /* MOSI: P109 */
			        <RA_PSEL(RA_PSEL_SPI, 1, 11)>; /* SCK:  P111 */
			drive-strength = "high"; /* 增强驱动能力,提升 SPI 稳定性 */
		};
	};

	/* ADC 引脚配置 */
	adc0_default: adc0_default {
		group1 {
			psels = <RA_PSEL(RA_PSEL_ADC, 0, 2)>, /* AN002: P002 */
			        <RA_PSEL(RA_PSEL_ADC, 0, 4)>; /* AN004: P004 */
			renesas,analog-enable;
		};
	};
};

&spi0 {
	status = "okay";
	/* 应用我们自定义的引脚配置 */
	pinctrl-0 = <&spi0_new_custom>;
	pinctrl-names = "default";
	
	/* CS 片选引脚 (P301) - 由 SPI 驱动软件控制 */
	cs-gpios = <&ioport3 1 GPIO_ACTIVE_LOW>;

	ssd1306_spi: ssd1306@0 {
		compatible = "solomon,ssd1306fb";
		reg = <0>;
		
		/* 
		 * SPI 频率: 100kHz 用于调试。
		 * 如果屏幕刷新太慢,后续可改为 <4000000> (4MHz) 或更高 
		 */
		spi-max-frequency = <100000>; 
		
		width = <128>;
		height = <64>;
		segment-offset = <0>;
		page-offset = <0>;
		display-offset = <0>;
		multiplex-ratio = <63>;
		segment-remap;
		com-invdir;
		prechargep = <0x22>;
		
		/* D/C 引脚: P110 */
		data-cmd-gpios = <&ioport1 10 GPIO_ACTIVE_HIGH>;
		/* RES 复位引脚: P208 */
		reset-gpios = <&ioport2 8 GPIO_ACTIVE_LOW>;
	};
};

&adc0 {
	status = "okay";
	pinctrl-0 = <&adc0_default>;
	pinctrl-names = "default";
	#address-cells = <1>;
	#size-cells = <0>;

	channel@2 {
		reg = <2>;
		zephyr,gain = "ADC_GAIN_1";
		zephyr,reference = "ADC_REF_INTERNAL";
		zephyr,acquisition-time = <ADC_ACQ_TIME_DEFAULT>;
		zephyr,resolution = <12>;
	};

	channel@4 {
		reg = <4>;
		zephyr,gain = "ADC_GAIN_1";
		zephyr,reference = "ADC_REF_INTERNAL";
		zephyr,acquisition-time = <ADC_ACQ_TIME_DEFAULT>;
		zephyr,resolution = <12>;
	};
};

3. 代码实现 (main.c)

逻辑与 I2C 版本基本通用,因为 Zephyr 屏蔽了底层 SPI/I2C 的差异。只需注意 display_dev 是通过 zephyr,display 自动获取的。

#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/drivers/display.h>
#include <zephyr/display/cfb.h>
#include <zephyr/drivers/adc.h>
#include <stdio.h>
#include <zephyr/logging/log.h>

LOG_MODULE_REGISTER(main, LOG_LEVEL_INF);

/* ADC 定义 */
const struct device *adc_dev = DEVICE_DT_GET(DT_NODELABEL(adc0));
#define ADC_CH2_ID 2
#define ADC_CH4_ID 4
static int16_t m_sample_buffer;

/* ADC 初始化辅助函数 */
static int setup_adc_channel(int id) {
    struct adc_channel_cfg cfg = {
        .gain = ADC_GAIN_1,
        .reference = ADC_REF_INTERNAL,
        .acquisition_time = ADC_ACQ_TIME_DEFAULT,
        .channel_id = id,
    };
    return adc_channel_setup(adc_dev, &cfg);
}

/* ADC 读取辅助函数 */
static int read_adc_val(int id, int16_t *val) {
    struct adc_sequence seq = {
        .channels = BIT(id),
        .buffer = &m_sample_buffer,
        .buffer_size = sizeof(m_sample_buffer),
        .resolution = 12,
    };
    int ret = adc_read(adc_dev, &seq);
    *val = m_sample_buffer;
    return ret;
}

int main(void)
{
    /* 1. 获取显示设备 (自动根据 chosen 节点找到 ssd1306_spi) */
    const struct device *display = DEVICE_DT_GET(DT_CHOSEN(zephyr_display));

    if (!device_is_ready(display)) {
        LOG_ERR("Display not ready! Check SPI wiring.");
        return 0;
    }

    if (!device_is_ready(adc_dev)) {
        LOG_ERR("ADC not ready!");
        return 0;
    }

    /* 2. 初始化显示 */
    display_blanking_off(display);
    cfb_framebuffer_init(display);
    cfb_framebuffer_set_font(display, 0); // 使用默认字体

    /* 3. 初始化 ADC */
    setup_adc_channel(ADC_CH2_ID);
    setup_adc_channel(ADC_CH4_ID);

    LOG_INF("System Started: SPI SSD1306 + ADC");

    char buf[32];
    int16_t val2, val4;

    while (1) {
        /* 读取 ADC */
        read_adc_val(ADC_CH2_ID, &val2);
        read_adc_val(ADC_CH4_ID, &val4);

        /* 串口打印 */
        LOG_INF("ADC: CH2=%d, CH4=%d", val2, val4);

        /* 屏幕显示 */
        cfb_framebuffer_clear(display, false);

        cfb_print(display, "RA6E2 Monitor", 0, 0);
        
        snprintf(buf, sizeof(buf), "AN002: %d", val2);
        cfb_print(display, buf, 0, 16);

        snprintf(buf, sizeof(buf), "AN004: %d", val4);
        cfb_print(display, buf, 0, 32);

        /* 画个动态条 (简单的可视化) */
        int bar_w = (val2 * 100) / 4095;
        struct cfb_position start = {0, 50};
        struct cfb_position end = {bar_w, 54};
        cfb_draw_rect(display, &start, &end);

        cfb_framebuffer_finalize(display);

        k_sleep(K_MSEC(200));
    }
    return 0;
}

4. 调试常见问题

  1. 屏幕全黑,没反应

    • 检查 RES (P208)D/C (P110) 接线。SPI 屏如果不复位或 D/C 状态错误,完全不会显示。
    • 检查 D1 (MOSI)D0 (SCK) 是否接反。
    • 尝试调低 spi-max-frequency,例如 100000 (100kHz),确保不是信号质量问题。
  2. ADC 读数不准

    • RA6E2 的模拟地 (AVSS0) 和数字地 (VSS) 必须共地。
    • 如果读数跳动大,可以在 ADC 通道配置中增加 zephyr,acquisition-time
  3. 编译报错 pinctrl 冲突

    • 确保你的 overlay 中 &spi0 已经覆写了 pinctrl-0<&spi0_new_custom>,否则默认的 pinctrl 配置可能会抢占 P110 引脚导致 GPIO 初始化失败。
    • 在这里插入图片描述
      在这里插入图片描述
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值