【前言】
昨天移植好了zephyr adc 的驱动,开发板上板载了一个AT24C02C,我设计了一个lvgl界面,可以进行读取与写入的测试。本篇将分享如何进行读取与展示。
【原理图】
开发板的使用手册有AT24C02C的提供,如下所示:

从原理图得知他的使用PB6\PB7.
同时开发板的电路也使用上拉电阻进行上拉处理。
【工程配置】
1、prj.con
CONFIG_I2C=y CONFIG_I2C_GD32_V2=y
【代码实现】
1、添加eeprom_at24c.c到工程中,主要实现初始化、读取等代码实现。
/*
* Copyright (c) 2026, GD32F503 Zephyr Port
* SPDX-License-Identifier: Apache-2.0
*
* AT24C02C (2 Kbit / 256 byte I2C EEPROM) operations.
*
* Spec highlights:
* - 256 bytes organised in 8-byte pages (yes, 8 — not 16 like the 04/08).
* - 7-bit slave address 0x50 with A0/A1/A2 strapped to GND on the EVAL.
* - Page write tWR (write cycle time, max) = 5 ms.
* - Sequential read supports the whole 256-byte address space.
*
* Backend: Zephyr I2C subsystem via drivers/i2c/i2c_gd32_v2.c.
* Convenience helpers:
* i2c_burst_read() — write mem_addr, restart, read n bytes
* i2c_burst_write() — write (mem_addr, data, data, ...).n+1 bytes
* i2c_write() — bare write (used for Probe)
*/
#include "eeprom_at24c.h"
#include <zephyr/device.h>
#include <zephyr/drivers/i2c.h>
#include <zephyr/kernel.h>
/* F50x dtsi labels the node `i2c0` (and `i2c1`), so DT_NODELABEL works. */
#define I2C_DEV DEVICE_DT_GET(DT_NODELABEL(i2c0))
#define AT24C_I2C_ADDR 0x50U /* 7-bit, A0/A1/A2 = GND */
#define AT24C_TWR_MS 5U /* write cycle time, max */
int eeprom_init(void)
{
if (!device_is_ready(I2C_DEV)) {
return -ENODEV;
}
return 0;
}
int eeprom_probe(void)
{
/* Bare 1-byte write: slave ACK on the address phase proves the
* device is on the bus. The byte value is ignored by AT24C02C
* unless we also send a memory address byte afterwards, which
* we don't — so no side effect. */
uint8_t dummy = 0U;
return i2c_write(I2C_DEV, &dummy, 1, AT24C_I2C_ADDR);
}
int eeprom_read(uint8_t mem_addr, uint8_t *out, size_t n)
{
if (out == NULL || n == 0U || n > 255U) {
return -EINVAL;
}
/* i2c_burst_read does write-then-restart-then-read in one call.
* The driver handles STOP at the end. */
return i2c_burst_read(I2C_DEV, AT24C_I2C_ADDR,
mem_addr, out, n);
}
int eeprom_write_byte(uint8_t mem_addr, uint8_t byte)
{
int rc;
rc = i2c_burst_write(I2C_DEV, AT24C_I2C_ADDR,
mem_addr, &byte, 1);
if (rc == 0) {
/* Wait for the chip's internal write cycle. */
k_msleep(AT24C_TWR_MS);
}
return rc;
}2、把这个文件添加进CmakeListis.txt,参与编译。
3、设备树中添加i2c0的描述
&i2c0 {
status = "okay";
pinctrl-0 = <&i2c0_default>;
pinctrl-names = "default";
clock-frequency = <I2C_BITRATE_STANDARD>;
};4、在ui中添加代码
/* AT24C02C 7-bit addr,跟 src/i2c_gd32_v2.h 一致 */
#define EEPROM_TAB_ADDR_STR "AT24C02C @ I2C0 (PB6/PB7)"
#define EEPROM_PAGE_BYTES 8U /* AT24C02C page size = 8 */
static lv_obj_t *eeprom_status_lbl;
static lv_obj_t *eeprom_addr_lbl; /* Addr 当前值 (大字) */
static lv_obj_t *eeprom_val_lbl; /* Val 当前值 (大字) */
static lv_obj_t *eeprom_r_lbl; /* Read 结果 hex (大字) */
static lv_obj_t *eeprom_d_lbl; /* 8 字节 dump hex (大字) */
static void eeprom_work_handler(struct k_work *work);
static K_WORK_DELAYABLE_DEFINE(eeprom_work, eeprom_work_handler);
static atomic_t eeprom_busy;
static uint8_t eeprom_addr = 0U; /* 当前 addr 值(0..255) */
static uint8_t eeprom_val = 0U; /* 当前 val 值(0..255) */
static uint8_t eeprom_op; /* 0=probe, 1=read byte, 2=write byte, 3=read 8 bytes */
static uint8_t eeprom_pending_addr;
static uint8_t eeprom_pending_val;
static uint8_t eeprom_page_buf[EEPROM_PAGE_BYTES]; /* dump buffer */
/* Worker scratch buffers — keep them off the stack. */
static char eeprom_msg_buf[48];
static char eeprom_dump_buf[EEPROM_PAGE_BYTES * 4 + 8];
/* Refresh the Addr label to show current value. Must hold lvgl_lock. */
static void eeprom_refresh_addr_label(void)
{
lv_label_set_text_fmt(eeprom_addr_lbl, "0x%02X", eeprom_addr);
}
static void eeprom_refresh_val_label(void)
{
lv_label_set_text_fmt(eeprom_val_lbl, "0x%02X", eeprom_val);
}
static void eeprom_work_handler(struct k_work *work)
{
ARG_UNUSED(work);
uint8_t op = eeprom_op;
uint8_t addr = eeprom_pending_addr;
uint8_t val = eeprom_pending_val;
int rc;
uint8_t rx = 0U;
switch (op) {
case 0: /* probe */
rc = eeprom_probe();
if (rc != 0) {
snprintf(eeprom_msg_buf, sizeof(eeprom_msg_buf),
"Probe: NACK (rc=%d)", rc);
} else {
snprintf(eeprom_msg_buf, sizeof(eeprom_msg_buf),
"Probe: ACK @ 0x%02X", AT24C_I2C_ADDR);
}
break;
case 1: /* read single byte */
rc = eeprom_read(addr, &rx, 1U);
if (rc != 0) {
snprintf(eeprom_msg_buf, sizeof(eeprom_msg_buf),
"Read 0x%02X: err rc=%d", addr, rc);
} else {
snprintf(eeprom_msg_buf, sizeof(eeprom_msg_buf),
"Read 0x%02X = 0x%02X", addr, rx);
}
break;
case 2: /* write single byte */
rc = eeprom_write_byte(addr, val);
if (rc != 0) {
snprintf(eeprom_msg_buf, sizeof(eeprom_msg_buf),
"Write 0x%02X: err rc=%d", addr, rc);
} else {
snprintf(eeprom_msg_buf, sizeof(eeprom_msg_buf),
"Write 0x%02X := 0x%02X", addr, val);
}
break;
case 3: /* read 8 bytes */
rc = eeprom_read(addr, eeprom_page_buf, EEPROM_PAGE_BYTES);
if (rc != 0) {
snprintf(eeprom_msg_buf, sizeof(eeprom_msg_buf),
"Read8 @0x%02X: err rc=%d", addr, rc);
} else {
snprintf(eeprom_msg_buf, sizeof(eeprom_msg_buf),
"Read8 @0x%02X ok", addr);
}
break;
default:
rc = -EINVAL;
break;
}
lvgl_lock();
lv_label_set_text(eeprom_status_lbl, eeprom_msg_buf);
if (op == 1U && rc == 0) {
char r_buf[16];
snprintf(r_buf, sizeof(r_buf), "0x%02X", rx);
lv_label_set_text(eeprom_r_lbl, r_buf);
} else if (op == 3U && rc == 0) {
char *p = eeprom_dump_buf;
for (size_t i = 0; i < EEPROM_PAGE_BYTES; i++) {
int n = snprintf(p, 4, "%02X ", eeprom_page_buf[i]);
p += n;
}
lv_label_set_text(eeprom_d_lbl, eeprom_dump_buf);
}
lvgl_unlock();
atomic_set(&eeprom_busy, 0);
}
/* ---- Button callbacks ---------------------------------------------------- */
static void btn_eeprom_probe_cb(lv_event_t *e)
{
ARG_UNUSED(e);
if (!atomic_cas(&eeprom_busy, 0, 1)) {
return;
}
eeprom_op = 0U;
eeprom_pending_addr = eeprom_addr;
eeprom_pending_val = eeprom_val;
k_work_schedule(&eeprom_work, K_NO_WAIT);
}
static void btn_eeprom_read_cb(lv_event_t *e)
{
ARG_UNUSED(e);
if (!atomic_cas(&eeprom_busy, 0, 1)) {
return;
}
eeprom_op = 1U;
eeprom_pending_addr = eeprom_addr;
eeprom_pending_val = eeprom_val;
k_work_schedule(&eeprom_work, K_NO_WAIT);
}
static void btn_eeprom_write_cb(lv_event_t *e)
{
ARG_UNUSED(e);
if (!atomic_cas(&eeprom_busy, 0, 1)) {
return;
}
eeprom_op = 2U;
eeprom_pending_addr = eeprom_addr;
eeprom_pending_val = eeprom_val;
k_work_schedule(&eeprom_work, K_NO_WAIT);
}
static void btn_eeprom_read8_cb(lv_event_t *e)
{
ARG_UNUSED(e);
if (!atomic_cas(&eeprom_busy, 0, 1)) {
return;
}
eeprom_op = 3U;
eeprom_pending_addr = eeprom_addr;
eeprom_pending_val = eeprom_val;
k_work_schedule(&eeprom_work, K_NO_WAIT);
}
/* +/- step buttons for Addr (wrap 0..255) */
static void btn_eeprom_addr_inc_cb(lv_event_t *e)
{
ARG_UNUSED(e);
eeprom_addr = (uint8_t)(eeprom_addr + 1U); /* 自然溢出 wrap */
lvgl_lock();
eeprom_refresh_addr_label();
lvgl_unlock();
}
static void btn_eeprom_addr_dec_cb(lv_event_t *e)
{
ARG_UNUSED(e);
eeprom_addr = (uint8_t)(eeprom_addr - 1U); /* 自然下溢 wrap */
lvgl_lock();
eeprom_refresh_addr_label();
lvgl_unlock();
}
/* +/- step buttons for Val */
static void btn_eeprom_val_inc_cb(lv_event_t *e)
{
ARG_UNUSED(e);
eeprom_val = (uint8_t)(eeprom_val + 1U);
lvgl_lock();
eeprom_refresh_val_label();
lvgl_unlock();
}
static void btn_eeprom_val_dec_cb(lv_event_t *e)
{
ARG_UNUSED(e);
eeprom_val = (uint8_t)(eeprom_val - 1U);
lvgl_lock();
eeprom_refresh_val_label();
lvgl_unlock();
}
/* ---- Build the screen ---------------------------------------------------- */
static void ui_screen_eeprom(lv_obj_t *parent)
{
lv_obj_set_style_bg_color(parent, lv_color_hex(0xFAFAFA), 0);
/* 标题 */
lv_obj_t *title = lv_label_create(parent);
lv_label_set_text(title, EEPROM_TAB_ADDR_STR);
lv_obj_set_style_text_font(title, &lv_font_montserrat_12, 0);
lv_obj_set_style_text_color(title, lv_color_hex(0x616161), 0);
lv_obj_align(title, LV_ALIGN_TOP_MID, 0, 4);
/* Status 行 */
eeprom_status_lbl = lv_label_create(parent);
lv_label_set_text(eeprom_status_lbl, "Status: idle");
lv_obj_set_style_text_font(eeprom_status_lbl, &lv_font_montserrat_12, 0);
lv_obj_set_style_text_color(eeprom_status_lbl, lv_color_hex(0x424242), 0);
lv_obj_align(eeprom_status_lbl, LV_ALIGN_TOP_MID, 0, 22);
/* Probe 按钮 (居中, y=42) */
lv_obj_t *btn_probe = lv_btn_create(parent);
lv_obj_set_size(btn_probe, 100, 26);
lv_obj_align(btn_probe, LV_ALIGN_TOP_MID, 0, 42);
lv_obj_t *lbl = lv_label_create(btn_probe);
lv_label_set_text(lbl, "Probe");
lv_obj_center(lbl);
lv_obj_add_event_cb(btn_probe, btn_eeprom_probe_cb,
LV_EVENT_CLICKED, NULL);
/* ---- Addr row: 标签 [-] 0xNN [+] ---- (垂直堆叠避免横向溢出) */
lv_obj_t *addr_lbl_txt = lv_label_create(parent);
lv_label_set_text(addr_lbl_txt, "Addr");
lv_obj_set_style_text_font(addr_lbl_txt, &lv_font_montserrat_12, 0);
lv_obj_set_style_text_color(addr_lbl_txt, lv_color_hex(0x9E9E9E), 0);
lv_obj_align(addr_lbl_txt, LV_ALIGN_TOP_LEFT, 18, 80);
lv_obj_t *btn_addr_dec = lv_btn_create(parent);
lv_obj_set_size(btn_addr_dec, 30, 24);
lv_obj_align(btn_addr_dec, LV_ALIGN_TOP_LEFT, 56, 76);
lbl = lv_label_create(btn_addr_dec);
lv_label_set_text(lbl, "-");
lv_obj_center(lbl);
lv_obj_add_event_cb(btn_addr_dec, btn_eeprom_addr_dec_cb,
LV_EVENT_CLICKED, NULL);
eeprom_addr_lbl = lv_label_create(parent);
lv_label_set_text_fmt(eeprom_addr_lbl, "0x%02X", eeprom_addr);
lv_obj_set_style_text_font(eeprom_addr_lbl, &lv_font_montserrat_14, 0);
lv_obj_set_style_text_color(eeprom_addr_lbl, lv_color_hex(0x1976D2), 0);
lv_obj_align(eeprom_addr_lbl, LV_ALIGN_TOP_LEFT, 94, 78);
lv_obj_t *btn_addr_inc = lv_btn_create(parent);
lv_obj_set_size(btn_addr_inc, 30, 24);
lv_obj_align(btn_addr_inc, LV_ALIGN_TOP_LEFT, 150, 76);
lbl = lv_label_create(btn_addr_inc);
lv_label_set_text(lbl, "+");
lv_obj_center(lbl);
lv_obj_add_event_cb(btn_addr_inc, btn_eeprom_addr_inc_cb,
LV_EVENT_CLICKED, NULL);
/* ---- Val row: 标签 [-] 0xNN [+] ---- */
lv_obj_t *val_lbl_txt = lv_label_create(parent);
lv_label_set_text(val_lbl_txt, "Val");
lv_obj_set_style_text_font(val_lbl_txt, &lv_font_montserrat_12, 0);
lv_obj_set_style_text_color(val_lbl_txt, lv_color_hex(0x9E9E9E), 0);
lv_obj_align(val_lbl_txt, LV_ALIGN_TOP_LEFT, 18, 110);
lv_obj_t *btn_val_dec = lv_btn_create(parent);
lv_obj_set_size(btn_val_dec, 30, 24);
lv_obj_align(btn_val_dec, LV_ALIGN_TOP_LEFT, 56, 106);
lbl = lv_label_create(btn_val_dec);
lv_label_set_text(lbl, "-");
lv_obj_center(lbl);
lv_obj_add_event_cb(btn_val_dec, btn_eeprom_val_dec_cb,
LV_EVENT_CLICKED, NULL);
eeprom_val_lbl = lv_label_create(parent);
lv_label_set_text_fmt(eeprom_val_lbl, "0x%02X", eeprom_val);
lv_obj_set_style_text_font(eeprom_val_lbl, &lv_font_montserrat_14, 0);
lv_obj_set_style_text_color(eeprom_val_lbl, lv_color_hex(0x1976D2), 0);
lv_obj_align(eeprom_val_lbl, LV_ALIGN_TOP_LEFT, 94, 108);
lv_obj_t *btn_val_inc = lv_btn_create(parent);
lv_obj_set_size(btn_val_inc, 30, 24);
lv_obj_align(btn_val_inc, LV_ALIGN_TOP_LEFT, 150, 106);
lbl = lv_label_create(btn_val_inc);
lv_label_set_text(lbl, "+");
lv_obj_center(lbl);
lv_obj_add_event_cb(btn_val_inc, btn_eeprom_val_inc_cb,
LV_EVENT_CLICKED, NULL);
/* 三个操作按钮横排 (y=140) — 间距 16 px,各按钮变小 */
lv_obj_t *btn_r = lv_btn_create(parent);
lv_obj_set_size(btn_r, 48, 22);
lv_obj_align(btn_r, LV_ALIGN_TOP_LEFT, 24, 140);
lbl = lv_label_create(btn_r);
lv_label_set_text(lbl, "Read");
lv_obj_center(lbl);
lv_obj_add_event_cb(btn_r, btn_eeprom_read_cb,
LV_EVENT_CLICKED, NULL);
lv_obj_t *btn_w = lv_btn_create(parent);
lv_obj_set_size(btn_w, 48, 22);
lv_obj_align(btn_w, LV_ALIGN_TOP_LEFT, 88, 140);
lbl = lv_label_create(btn_w);
lv_label_set_text(lbl, "Write");
lv_obj_center(lbl);
lv_obj_add_event_cb(btn_w, btn_eeprom_write_cb,
LV_EVENT_CLICKED, NULL);
lv_obj_t *btn_r8 = lv_btn_create(parent);
lv_obj_set_size(btn_r8, 64, 22);
lv_obj_align(btn_r8, LV_ALIGN_TOP_LEFT, 152, 140);
lbl = lv_label_create(btn_r8);
lv_label_set_text(lbl, "Read 8B");
lv_obj_center(lbl);
lv_obj_add_event_cb(btn_r8, btn_eeprom_read8_cb,
LV_EVENT_CLICKED, NULL);
/* R — 单字节 Read 结果 (蓝色, 14pt) */
lv_obj_t *r_lbl_txt = lv_label_create(parent);
lv_label_set_text(r_lbl_txt, "R");
lv_obj_set_style_text_font(r_lbl_txt, &lv_font_montserrat_12, 0);
lv_obj_set_style_text_color(r_lbl_txt, lv_color_hex(0x9E9E9E), 0);
lv_obj_align(r_lbl_txt, LV_ALIGN_TOP_LEFT, 18, 176);
eeprom_r_lbl = lv_label_create(parent);
lv_label_set_text(eeprom_r_lbl, "0x??");
lv_obj_set_style_text_font(eeprom_r_lbl, &lv_font_montserrat_14, 0);
lv_obj_set_style_text_color(eeprom_r_lbl, lv_color_hex(0x1976D2), 0);
lv_obj_align(eeprom_r_lbl, LV_ALIGN_TOP_LEFT, 36, 172);
/* D dump — 8 字节 hex (12pt) */
lv_obj_t *d_lbl_txt = lv_label_create(parent);
lv_label_set_text(d_lbl_txt, "D");
lv_obj_set_style_text_font(d_lbl_txt, &lv_font_montserrat_12, 0);
lv_obj_set_style_text_color(d_lbl_txt, lv_color_hex(0x9E9E9E), 0);
lv_obj_align(d_lbl_txt, LV_ALIGN_TOP_LEFT, 18, 204);
eeprom_d_lbl = lv_label_create(parent);
lv_label_set_text(eeprom_d_lbl, "-- -- -- -- -- -- -- --");
lv_obj_set_style_text_font(eeprom_d_lbl, &lv_font_montserrat_12, 0);
lv_obj_set_style_text_color(eeprom_d_lbl, lv_color_hex(0x212121), 0);
lv_obj_align(eeprom_d_lbl, LV_ALIGN_TOP_LEFT, 36, 204);
lv_obj_set_width(eeprom_d_lbl, 210);
}【效果展示】

我要赚赏金
