Zephyr学习之spi flash驱动记录(w25q128)

前言

1.环境搭建 Zephyr开发环境搭建记录(Clion)

  1. 使用的开发板为正点原子的探索者:mcu使用的是STM32F407ZGT6

  2. 本篇参考示例spi_flash

  3. 开发环境使用Clion

参考说明

W25Q128部分原理图

W25Q128资料信息

DMA通道映射

参考示例

打开测试项目

项目配置编写(启用FLASH)

c 复制代码
CONFIG_LOG=y
CONFIG_FLASH=y
CONFIG_FLASH_LOG_LEVEL_INF=y

设备树覆盖文件编写

  1. 完整overlay文件配置
c 复制代码
/*
	探索者开发板LED灯驱动

	LED0 PF9
	LED1 PF10

	===================w25q128(SPI1)===================
	SPI1_MISO == PB4
	SPI1_SCK  == PB3
	SPI1_MOSI == PB5
	SPI1_CS   == PB14
 */

// 定义根节点,包含整个设备树的主要配置
/ {
	// 配置系统选择项,指定控制台和shell使用的UART接口
	chosen {
		// 指定系统控制台使用USART1
		zephyr,console = &usart1;
		// 指定shell命令行使用USART1
		zephyr,shell-uart = &usart1;
	};

	// 定义LED设备节点,配置GPIO控制的LED
	leds {
		// 设置兼容性字符串,表示使用GPIO LED驱动
		compatible = "gpio-leds";

		// 定义第一个LED设备,别名为led_0
		led_0: led_0 {
			// 配置GPIO引脚为PF9(GPIOF端口第9号引脚),高电平有效
			gpios = <&gpiof 9 GPIO_ACTIVE_HIGH>;  // PF9
			// 设置LED的标签名称
			label = "User LED0";
		};

		// 定义第二个LED设备,别名为led_1
		led_1: led_1 {
			// 配置GPIO引脚为PF10(GPIOF端口第10号引脚),高电平有效
			gpios = <&gpiof 10 GPIO_ACTIVE_HIGH>; // PF10
			// 设置LED的标签名称
			label = "User LED1";
		};
	};
	// 定义别名节点,为设备树中的节点提供简短的别名引用
	aliases {
		// 将"led0"别名指向led_0节点
		led0 = &led_0;
		// 将"led1"别名指向led_1节点
		led1 = &led_1;
		usart1 = &usart1;
		// 将"w25q128"别名指向w25q128节点
		w25q128 = &w25q128;
	};
};


&spi1 {
	dmas = <&dma2 3 3 STM32_DMA_PERIPH_TX STM32_DMA_FIFO_FULL>,
		   <&dma2 3 2 STM32_DMA_PERIPH_RX STM32_DMA_FIFO_FULL>;
	dma-names = "tx", "rx";
	status = "okay";
	// clk、mosi、miso
	pinctrl-0 = <&spi1_sck_pb3 &spi1_miso_pb4 &spi1_mosi_pb5>;
	pinctrl-names = "default";
	cs-gpios = <&gpiob 14 GPIO_ACTIVE_LOW>;

	w25q128: w25q128@0 {
		compatible = "jedec,spi-nor";
		size = <0x8000000>;
		reg = <0>;
		spi-max-frequency = <4000000>;
		status = "okay";
		jedec-id = [ef 40 18];
	};
};

&dma2 {
	status = "okay";
};
// 引用并修改预定义的usart1节点
&usart1 {
	// 配置USART1的引脚控制,TX连接到PA9,RX连接到PA10
	pinctrl-0 = <&usart1_tx_pa9>, <&usart1_rx_pa10>;
	// 定义引脚控制配置的名称
	pinctrl-names = "default";
	// 设置串口通信波特率为115200
	current-speed = <115200>;
	// 启用此UART外设
	status = "okay";
};

测试主函数编写

c 复制代码
/*
 * Copyright (c) 2025 Embeint Inc
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/net_buf.h>
#include <zephyr/logging/log.h>
#include <zephyr/drivers/flash.h>
#include <stdio.h>
#include <string.h>
// SPI测试
LOG_MODULE_REGISTER(test, LOG_LEVEL_DBG);
#define SPI_FLASH_TEST_REGION_OFFSET 0xff000
#define SPI_FLASH_SECTOR_SIZE        4096

const uint8_t erased[] = { 0xff, 0xff, 0xff, 0xff };

void single_sector_test(const struct device *flash_dev)
{

	const uint8_t expected[] = { 0x55, 0xaa, 0x66, 0x99 };
	const size_t len = sizeof(expected);
	uint8_t buf[sizeof(expected)];
	int rc;

	LOG_DBG("\nPerform test on single sector");
	/* Write protection needs to be disabled before each write or
	 * erase, since the flash component turns on write protection
	 * automatically after completion of write and erase
	 * operations.
	 */
	LOG_DBG("\nTest 1: Flash erase\n");

	/* Full flash erase if SPI_FLASH_TEST_REGION_OFFSET = 0 and
	 * SPI_FLASH_SECTOR_SIZE = flash size
	 */
	rc = flash_erase(flash_dev, SPI_FLASH_TEST_REGION_OFFSET,
			 SPI_FLASH_SECTOR_SIZE);
	if (rc != 0) {
		LOG_DBG("Flash erase failed! %d\n", rc);
	} else {
		/* Check erased pattern */
		memset(buf, 0, len);
		rc = flash_read(flash_dev, SPI_FLASH_TEST_REGION_OFFSET, buf, len);
		if (rc != 0) {
			LOG_DBG("Flash read failed! %d\n", rc);
			return;
		}
		if (memcmp(erased, buf, len) != 0) {
			LOG_DBG("Flash erase failed at offset 0x%x got 0x%x\n",
				SPI_FLASH_TEST_REGION_OFFSET, *(uint32_t *)buf);
			return;
		}
		LOG_DBG("Flash erase succeeded!\n");
	}
	LOG_DBG("\nTest 2: Flash write\n");

	LOG_DBG("Attempting to write %zu bytes\n", len);
	rc = flash_write(flash_dev, SPI_FLASH_TEST_REGION_OFFSET, expected, len);
	if (rc != 0) {
		LOG_DBG("Flash write failed! %d\n", rc);
		return;
	}

	memset(buf, 0, len);
	rc = flash_read(flash_dev, SPI_FLASH_TEST_REGION_OFFSET, buf, len);
	if (rc != 0) {
		LOG_DBG("Flash read failed! %d\n", rc);
		return;
	}

	if (memcmp(expected, buf, len) == 0) {
		LOG_DBG("Data read matches data written. Good!!\n");
	} else {
		const uint8_t *wp = expected;
		const uint8_t *rp = buf;
		const uint8_t *rpe = rp + len;

		LOG_DBG("Data read does not match data written!!\n");
		while (rp < rpe) {
			LOG_DBG("%08x wrote %02x read %02x %s\n",
			       (uint32_t)(SPI_FLASH_TEST_REGION_OFFSET + (rp - buf)),
			       *wp, *rp, (*rp == *wp) ? "match" : "MISMATCH");
			++rp;
			++wp;
		}
	}
}

int main(void)
{
	const struct device *flash_dev = DEVICE_DT_GET(DT_ALIAS(w25q128));

	if (!device_is_ready(flash_dev)) {
		LOG_ERR("%s: device not ready.\n", flash_dev->name);
		return 0;
	}

	LOG_DBG("\n%s SPI flash testing\n", flash_dev->name);
	LOG_DBG("==========================\n");

	single_sector_test(flash_dev);

	return 0;
}

测试结果

相关推荐
西岸行者3 天前
学习笔记:SKILLS 能帮助更好的vibe coding
笔记·学习
悠哉悠哉愿意3 天前
【单片机学习笔记】串口、超声波、NE555的同时使用
笔记·单片机·学习
别催小唐敲代码3 天前
嵌入式学习路线
学习
毛小茛3 天前
计算机系统概论——校验码
学习
babe小鑫3 天前
大专经济信息管理专业学习数据分析的必要性
学习·数据挖掘·数据分析
winfreedoms3 天前
ROS2知识大白话
笔记·学习·ros2
在这habit之下3 天前
Linux Virtual Server(LVS)学习总结
linux·学习·lvs
我想我不够好。3 天前
2026.2.25监控学习
学习
im_AMBER3 天前
Leetcode 127 删除有序数组中的重复项 | 删除有序数组中的重复项 II
数据结构·学习·算法·leetcode
CodeJourney_J3 天前
从“Hello World“ 开始 C++
c语言·c++·学习