【瑞萨RA × Zephyr评测】iic测试(AHT20 (SCI I2C))

Zephyr 4.3 开发笔记:Renesas RA 驱动 AHT20 (SCI I2C)

日期 : 2025年
平台 : Renesas RA 系列 (使用 SCI 模拟 I2C)
硬件环境:

  • 传感器: AHT20 (I2C 地址 0x38) 连接于 P410/P411
  • 显示屏: SSD1306 (SPI)
  • 关键硬件特性 : I2C 总线外部上拉电阻为 10kΩ

1. 核心问题分析

1.1 资源冲突:SCI0 (UART vs I2C)

现象

默认情况下,Zephyr 会将系统的控制台 (Console/Shell) 映射到 UART0 。在瑞萨 RA 芯片的硬件层面上,UART0 和我们使用的 I2C 接口通常都指向同一个物理模块------SCI0 (Serial Communications Interface 0)

冲突原因

SCI 模块是多功能的,但在同一时刻只能工作在一种模式下(UART、I2C 或 SPI)。

  • 默认配置:Zephyr 尝试初始化 SCI0 为 UART 模式用于打印日志。
  • 应用需求:传感器 AHT20 接在 P410/P411 引脚,这两个引脚对应 SCI0 的 I2C 功能。
  • 结果:如果不同时禁用 UART 控制台,I2C 驱动将无法获取 SCI0 硬件锁,导致初始化失败或引脚配置冲突。

解决方案

必须在设备树中删除控制台属性禁用 UART0 节点,完全释放 SCI0 给 I2C 驱动使用。

1.2 信号质量:10kΩ 大电阻导致的波形畸变

现象

通常 I2C 推荐 4.7kΩ 或 2.2kΩ 上拉电阻。当前硬件使用了 10kΩ ,导致在 100kHz 频率下,信号(尤其是时钟信号)上升沿过缓(呈鲨鱼鳍状)。SCI 硬件对边沿检测严格,从而引发 I2C_MASTER_EVENT_ABORTED 错误。

解决方案 (软件配置)

利用设备树 pinctrl 对 SDA 和 SCL 采用差异化配置,无需修改硬件即可解决:

  • SDA (数据) : Open-Drain + 内部上拉 (内部上拉与外部 10k 并联,降低阻值,改善波形)。
  • SCL (时钟) : Push-Pull (推挽) (利用 MCU 主动驱动高电平,彻底消除大电阻带来的上升沿延迟)。

2. 最终代码实现

2.1 设备树配置 (app.overlay)

请注意 chosen 节点中的删除操作,以及 pinctrl 中的差异化配置。

dts 复制代码
/*
 * 硬件连接配置:
 * SSD1306 (SPI): SCK=P111, MOSI=P109, D/C=P110, CS=P301, RES=P208
 * AHT20   (I2C): SDA=P411, SCL=P410 (通过 SCI0)
 */

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

/ {
	chosen {
		zephyr,display = &ssd1306_spi;
		
		/* 
		 * [冲突解决] 禁用控制台
		 * 因为 UART0 和 I2C 共享 SCI0 硬件资源,必须禁用控制台以释放 SCI0。
		 */
		/delete-property/ zephyr,console;
		/delete-property/ zephyr,shell-uart;
	};
};

/* 强制开启 IO Port 控制器 */
&ioport1 { status = "okay"; };
&ioport2 { status = "okay"; };
&ioport3 { status = "okay"; };
&ioport4 { status = "okay"; };

/* 
 * [冲突解决] 禁用 UART0 节点 
 * 确保 UART 驱动不会尝试初始化 SCI0
 */
&uart0 {
	status = "disabled";
};

&pinctrl {
	/* SPI0 屏幕引脚 */
	spi0_new_custom: spi0_new_custom {
		group1 {
			psels = <RA_PSEL(RA_PSEL_SPI, 1, 9)>,
				    <RA_PSEL(RA_PSEL_SPI, 1, 11)>;
			drive-strength = "high";
		};
	};

	/* 
	 * [信号优化] SCI0 I2C 引脚配置
	 * 针对 10kΩ 外部上拉电阻的特殊优化
	 */
	sci0_i2c_custom: sci0_i2c_custom {
		/* 
		 * SDA (P411): 配置为开漏 + 内部上拉 
		 * 必须是开漏 (I2C标准)。开启内部上拉以辅助外部的 10k 电阻。
		 */
		group_sda {
			psels = <RA_PSEL(RA_PSEL_SCI_0, 4, 11)>;
			drive-strength = "medium";
			bias-pull-up;      /* 关键:内部电阻并联外部电阻 */
			drive-open-drain;  /* 关键:双向通信必须开漏 */
		};

		/* 
		 * SCL (P410): 配置为推挽输出 
		 * 这里的技巧是不设置 drive-open-drain。
		 * 推挽模式下 MCU 主动拉高 SCL,无视 10k 电阻的影响,保证时钟边缘陡峭。
		 */
		group_scl {
			psels = <RA_PSEL(RA_PSEL_SCI_0, 4, 10)>;
			drive-strength = "medium";
		};
	};
};

/* SCI0 模块配置为 I2C 模式 */
&sci0 {
	status = "okay";
	pinctrl-0 = <&sci0_i2c_custom>;
	pinctrl-names = "default";

	sci0_i2c: i2c {
		compatible = "renesas,ra-i2c-sci";
		status = "okay";
		
		channel = <0>;
		clock-frequency = <100000>; /* 100kHz 标准速率 */
		#address-cells = <1>;
		#size-cells = <0>;

		/* AHT20 传感器配置 (复用 DHT20 驱动) */
		aht20: dht20@38 {
			compatible = "aosong,dht20";
			reg = <0x38>; 
		};
	};
};

/* SPI0 屏幕配置 */
&spi0 {
	status = "okay";
	pinctrl-0 = <&spi0_new_custom>;
	pinctrl-names = "default";
	cs-gpios = <&ioport3 1 GPIO_ACTIVE_LOW>;

	ssd1306_spi: ssd1306@0 {
		compatible = "solomon,ssd1306fb";
		reg = <0>;
		spi-max-frequency = <4000000>;
		width = <128>;
		height = <64>;
		segment-offset = <0>;
		page-offset = <0>;
		display-offset = <0>;
		multiplex-ratio = <63>;
		segment-remap;
		com-invdir;
		prechargep = <0x22>;
		data-cmd-gpios = <&ioport1 10 GPIO_ACTIVE_HIGH>;
		reset-gpios = <&ioport2 8 GPIO_ACTIVE_LOW>;
	};
};

2.2 项目配置 (prj.conf)

必须在 Kconfig 层面彻底关闭串口功能,防止内核尝试调用串口驱动。

properties 复制代码
CONFIG_HEAP_MEM_POOL_SIZE=4096
CONFIG_MAIN_STACK_SIZE=2048

# [冲突解决] 禁用串口控制台
# 这一步至关重要,否则 UART 驱动会抢占 SCI0 寄存器
CONFIG_SERIAL=n
CONFIG_CONSOLE=n
CONFIG_UART_CONSOLE=n

# 硬件驱动支持
CONFIG_GPIO=y
CONFIG_I2C=y
CONFIG_SPI=y

# 传感器驱动: AHT20 (使用 DHT20 兼容驱动)
CONFIG_SENSOR=y
CONFIG_DHT20=y

# 显示驱动
CONFIG_DISPLAY=y
CONFIG_SSD1306=y
CONFIG_CHARACTER_FRAMEBUFFER=y

# 允许 printf 处理浮点数
CONFIG_CBPRINTF_FP_SUPPORT=y

2.3 主程序 (main.c)

c 复制代码
/*
 * Zephyr AHT20 + SSD1306 演示程序
 */
#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/drivers/display.h>
#include <zephyr/drivers/sensor.h>
#include <zephyr/display/cfb.h>
#include <stdio.h>

int main(void)
{
	const struct device *dev_disp;
	const struct device *dev_sensor;
	
	uint8_t font_height;
	uint8_t font_width;
	char buf[32];

	/* 1. 初始化屏幕 */
	dev_disp = DEVICE_DT_GET(DT_CHOSEN(zephyr_display));
	if (!device_is_ready(dev_disp)) {
		return 0; 
	}

	display_blanking_off(dev_disp);
	if (cfb_framebuffer_init(dev_disp)) {
		return 0;
	}
	
	cfb_framebuffer_set_font(dev_disp, 0);
	cfb_get_font_size(dev_disp, 0, &font_width, &font_height);

	/* 2. 获取传感器 (AHT20) */
	dev_sensor = DEVICE_DT_GET(DT_NODELABEL(aht20));

	while (1) {
		struct sensor_value temp, humidity;
		bool sensor_ok = false;

		/* 读取传感器 */
		if (device_is_ready(dev_sensor)) {
			/* AHT20 必须先 fetch 更新数据,再 get 获取数值 */
			if (sensor_sample_fetch(dev_sensor) == 0) {
				sensor_channel_get(dev_sensor, SENSOR_CHAN_AMBIENT_TEMP, &temp);
				sensor_channel_get(dev_sensor, SENSOR_CHAN_HUMIDITY, &humidity);
				sensor_ok = true;
			}
		}

		/* --- 屏幕显示 --- */
		cfb_framebuffer_clear(dev_disp, false);
		cfb_print(dev_disp, "AHT20 Monitor", 0, 0);
		
		if (sensor_ok) {
			double t_val = sensor_value_to_double(&temp);
			double h_val = sensor_value_to_double(&humidity);

			snprintf(buf, sizeof(buf), "Temp: %.1f C", t_val);
			cfb_print(dev_disp, buf, 0, font_height + 5);

			snprintf(buf, sizeof(buf), "Humi: %.1f %%", h_val);
			cfb_print(dev_disp, buf, 0, (font_height * 2) + 5);
		} else {
			cfb_print(dev_disp, "Sensor Error!", 0, font_height + 5);
			if (!device_is_ready(dev_sensor)) {
				cfb_print(dev_disp, "Init Fail (SCI?)", 0, (font_height * 2) + 5);
			} else {
				cfb_print(dev_disp, "Read Fail", 0, (font_height * 2) + 5);
			}
		}

		/* 运行状态指示条 */
		static int count = 0;
		struct cfb_position start = {0, 62};
		struct cfb_position end = {(count % 128), 63};
		cfb_draw_rect(dev_disp, &start, &end);
		count += 5;

		cfb_framebuffer_finalize(dev_disp);
		k_sleep(K_MSEC(1000));
	}
	return 0;
}
相关推荐
怀民民民2 小时前
双通道点光源追踪系统
单片机·嵌入式硬件·开源·操作系统·串口·硬件·frtos
贪玩成性2 小时前
TM1652驱动代码
单片机·mcu
GUET_一路向前2 小时前
STM32 MCU OTA升级办法1
stm32·单片机·嵌入式硬件
YouEmbedded2 小时前
解码GPIO到核心元件的原理与应用
stm32·gpio·二极管·电流·电阻器
微风欲寻竹影2 小时前
STC89C52电子日历:12864 LCD+按键调时【附源码+Proteus仿真,免费】
单片机·嵌入式硬件·51单片机·proteus
恒锐丰小吕2 小时前
屹晶微 EG2113D 高压 600V 半桥 MOS 管驱动芯片技术解析
嵌入式硬件·硬件工程
一路往蓝-Anbo12 小时前
【第13期】中断机制详解 :从向量表到ISR
c语言·开发语言·stm32·单片机·嵌入式硬件
ArrebolJiuZhou12 小时前
00 arm开发环境的搭建
linux·arm开发·单片机·嵌入式硬件
易水寒陈12 小时前
使用J-Link RTT Viewer
stm32·单片机