这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » 嵌入式开发 » 国产MCU » 【ZephyrGD32F503】I2C之EEPROM图形化测试

共1条 1/1 1 跳转至

【ZephyrGD32F503】I2C之EEPROM图形化测试

高工
2026-07-05 22:40:52     打赏

【前言】

昨天移植好了zephyr adc 的驱动,开发板上板载了一个AT24C02C,我设计了一个lvgl界面,可以进行读取与写入的测试。本篇将分享如何进行读取与展示。

【原理图】

开发板的使用手册有AT24C02C的提供,如下所示:

image.png

从原理图得知他的使用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);
}

【效果展示】

0277bc9ecdeb5987bb859597640a2cff.jpg





关键词: Zephyr     GD32F503V-EVAL     LVG    

共1条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]