Zephyr 开发笔记:RA6E2 SPI 屏 (SSD1306) + 双路 ADC
1. 硬件接线表 (根据代码分析)
根据你的 app.overlay 配置,硬件连接如下。请务必检查物理连线是否一致:
| 模块 | 功能 (Pin Name) | RA6E2 引脚 | 备注 |
|---|---|---|---|
| SSD1306 | D0 (SCK/CLK) | P111 | SPI0 RSPCK (硬件 SPI 时钟) |
| D1 (MOSI/DIN) | P109 | SPI0 MOSI (硬件 SPI 数据) | |
| RES (Reset) | P208 | GPIO 输出 | |
| DC (Data/Cmd) | P110 | GPIO 输出 | |
| CS (Chip Sel) | P301 | GPIO 输出 | |
| VCC | 3.3V | ||
| GND | GND | ||
| ADC | AN002 | P002 | 模拟输入通道 2 |
| AN004 | P004 | 模拟输入通道 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)
这是最核心的部分。我们做了两件事:
- ADC 配置:添加 P002 和 P004 到 ADC0。
- 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. 调试常见问题
-
屏幕全黑,没反应:
- 检查 RES (P208) 和 D/C (P110) 接线。SPI 屏如果不复位或 D/C 状态错误,完全不会显示。
- 检查 D1 (MOSI) 和 D0 (SCK) 是否接反。
- 尝试调低
spi-max-frequency,例如 100000 (100kHz),确保不是信号质量问题。
-
ADC 读数不准:
- RA6E2 的模拟地 (AVSS0) 和数字地 (VSS) 必须共地。
- 如果读数跳动大,可以在 ADC 通道配置中增加
zephyr,acquisition-time。
-
编译报错
pinctrl冲突:- 确保你的 overlay 中
&spi0已经覆写了pinctrl-0为<&spi0_new_custom>,否则默认的 pinctrl 配置可能会抢占 P110 引脚导致 GPIO 初始化失败。 

- 确保你的 overlay 中
1639

被折叠的 条评论
为什么被折叠?



