STM32之音频数据的Flash读取与DAC播放

2023-11-20

一. STM32103之内部Flash原理

1. Flash介绍

Flash 中文名字叫闪存,是一种长寿命的非易失性(断电数据不丢失)的存储器,可以对称为块的存储器单元块进行擦写和再编程,在进行写入操作之前必须先执行擦除。
一个Nand Flash由多个块(Block)组成,每个块里面又包含很多页(page)。每个页对应一个空闲区域/冗余区域(spare area),这个区域不是用来存储数据的,用于放置数据的校验值检测和纠错的。
块,是Nand Flash的擦除操作的基本/最小单位。
页,是Nand Flash的写入操作的基本/最小的单位。

2. Flash的组成

STM32F1 的闪存(Flash)模块由:主存储器信息块闪存存储器接口寄存器三部分组成

模块 作用
主存储器 该部分用来存放代码和数据常数(如 const 类型的数据)。对于大容量产品,其被划分为 256 页,每页 2K 字节。注意,小容量和中容量产品则每页只有 1K 字节。从上图可以看出主存储器的起始地址就是0X08000000, BOOT0、BOOT1 都接 GND 的时候,就是从 0X08000000 开始运行代码的。
信息块 该部分分为 2 个小部分,其中启动程序代码,是用来存储 ST 自带的启动程序,用于串口下载代码,当 BOOT0 接 V3.3, BOOT1 接 GND 的时候,运行的就是这部分代码。用户选择字节,则一般用于配置写保护、读保护等功能
闪存存储器接口寄存器 该部分用于控制闪存读写等,是整个闪存模块的控制机构。对主存储器和信息块的写入由内嵌的闪存编程/擦除控制器(FPEC)管理;编程与擦除的高电压由内部产生。

在执行闪存写操作时,任何对闪存的读操作都会锁住总线,在写操作完成后读操作才能正确地进行;
在进行写或擦除操作时,不能进行代码或数据的读取操作。
它们的地址分布及大小如下:
在这里插入图片描述
STM32的Flash地址起始于0x0800 0000,结束地址是0x0800 0000加上芯片实际的Flash大小,不同的芯片flash大小不同。

3. STM32内部框架图

STM32F103系列的FLASH 容量一般为 512K 字节,属于大容量芯片

  • STM32内部框架图
    在这里插入图片描述
    其中的SRAM为静态随机存取存储器,是随机存取存储器的一种,只要保持通电,里面储存的数据就可以恒常保持。但是当电力供应停止时,SRAM储存的数据是会消失的;而Flash不会断电丢失数据的特性就被无限放大。

二. SD卡的读写

在SD卡创建一个.txt文件,循环(不加延时)分批一次写入256字节,累计写入不少于64KB字节;然后读取此文件数据,通过串口显示出来,并分析写入和读取的速率。

具体操作参考博客:STM32F103之完成对SD卡的数据读取

1. 实验过程

  • 打开串口助手,观察输出结果
    在这里插入图片描述
    总共用了12分钟,写了64K字节。

2. 查看hello.txt

在这里插入图片描述

3. 从SD卡里读出数据

利用FATS从SD卡读出数据,并且串口输出
(1)代码修改

  • 因为要进行读取,所以将写入部分的函数改为读出函数
    在这里插入图片描述
  • 声明读出函数,定义双字节数据类型s,用来保存地址(或者存放指针),再定义字节类型格式的变量和一个存储读取的数组
    在这里插入图片描述
    ① 修改f_open函数的第三个参数设置为打开状态为FA_READ模式
    ②文件指针移至相应位置,否则无输出
    ③指针加地址
    ④使用FATS的read函数,第一个参数是指针,第二个是存储数组,第三个是数组大小,第四个是强制转换br为UINT格式
    ⑤输出
    在这里插入图片描述
    (2)结果显示
    在这里插入图片描述

三. Flash地址空间的数据读取

STM32F103C8T6只有20KB内存(RAM)供程序代码和数组变量存放,因此针对内部Flash的总计64KB存储空间(地址从0x08000000开始),运行一次写入8KB数据,总计复位运行代码4次,将32KB数据写入Flash。并验证写入数据的正确性和读写速率。

链接:https://pan.baidu.com/s/11Tn8TocHT8qithneDyKFIQ
提取码:pmvn

1. Flash写入流程

  1. 解锁(固定的KEY值)
    由于内部 FLASH 空间主要存储的是应用程序,是非常关键的数据,为了防止误操作修改了这些内容,芯片复位后默认会给控制寄存器 FLASH_CR 上锁,这个时候不允许设置 FLASH 的控制寄存器,从而不能修改 FLASH 中的内容。所以对 FLASH 写入数据前,需要先给它解锁。
    解锁的操作步骤如下:
    (1)往 FPEC 键寄存器 FLASH_KEYR 中写入 KEY1 = 0x45670123
    (2)再往 FPEC 键寄存器 FLASH_KEYR 中写入 KEY2 = 0xCDEF89AB

  2. 页擦除
    在写入新的数据前,需要先擦除存储区域,STM32 提供了页(扇区)擦除指令和整个 FLASH 擦除(批量擦除)的指令,批量擦除指令仅针对主存储区。
    页擦除的过程如下:
    (1)检查 FLASH_SR 寄存器中的“忙碌寄存器位 BSY”,以确认当前未执行任何 Flash 操作;
    (2)在 FLASH_CR 寄存器中,将“激活页擦除寄存器位 PER ”置 1;
    (3)用 FLASH_AR 寄存器选择要擦除的页;
    (4)将 FLASH_CR 寄存器中的“开始擦除寄存器位 STRT ”置 1,开始擦除;
    (5)等待 BSY 位被清零时,表示擦除完成。
    在这里插入图片描述

  3. 写入数据
    擦除完毕后即可写入数据,写入数据的过程并不是仅仅使用指针向地址赋值,赋值前还需要配置一系列的寄存器,步骤如下:
    (1)检查 FLASH_SR 中的 BSY 位,以确认当前未执行任何其它的内部 Flash 操作;
    (2)将 FLASH_CR 寄存器中的 “激活编程寄存器位 PG” 置 1;
    (3)向指定的 FLASH 存储器地址执行数据写入操作,每次只能以 16 位的方式写入;
    (4)等待 BSY 位被清零时,表示写入完成。
    在这里插入图片描述

2. 配置STM32CubeMX

  • 时钟配置
    在这里插入图片描述
  • SYS 配置
    在这里插入图片描述
  • 设置PC13为GPIO_Output
    在这里插入图片描述
  • 使能GPIO引脚
    在这里插入图片描述
  • 设置堆栈大小为4K
    在这里插入图片描述
    之后导出即可

3. 修改代码

  1. 将准备好的flash.h加入到工程中
    在这里插入图片描述
  2. 在main.c中添加代码
  • 修改数组大小
    在这里插入图片描述
  • 添加头文件 #include "flash.h"
    在这里插入图片描述
  • 修改数据内容
    在这里插入图片描述
  • 读取数据
    在这里插入图片描述
    编译后没有错误

4. 设置ST-Link

电路连线:

ST-Link STM32F103
SWCLK/TCK SWCLK/TCK
SWDIO/TMS SWDIO/TMS
GND GND
VCC VCC

在这里插入图片描述
连接好后,安装好ST-Link驱动
链接:https://pan.baidu.com/s/1cmreV0
安装好驱动后,上电,查看设备管理器,可以看到STLink在电脑上显示出来
在这里插入图片描述

5. Keil配置

  • 进入keil 配置,设置Debug为ST-Link Debugger
    在这里插入图片描述
  • 点击Settings可以看到要检测出SWDIO有信息
    在这里插入图片描述

6. 调试程序

  • 点击LOAD将程序下载到STM32中
    在这里插入图片描述
    注意:download后,要将STM32调整为工作模式,并按下复位键
  • 进入Debug,全速运行,View->memory windows->memory 1打开内存观察窗口
    在这里插入图片描述
    并在地址栏中输入0x800c000后回车
    在这里插入图片描述
  • 在Memory1窗口可观察到成功写入内容
    在这里插入图片描述
  • View->Watch windows->Watch 1打开一个变量观察窗口
    在这里插入图片描述
    在main.c文件中,找到变量FlashWBuff 和FlashRBuff,右健单击 'Add FlashWBuff’ to watch1,将变量FlashWBuff 和 FlashRBuff加入到 Watch 1 观察窗口
    在这里插入图片描述
  • 全速运行,可以观察到FlashWBuffFlashRBuff内容相同
    在这里插入图片描述

四. 实例——基于片内Flash的提示音播放程序

1. DAC介绍

  • DAC 是数字/模拟转换模块,它的作用就是把输入的数字编码,转换成对应的模拟电压输出,它的功能与 ADC相反。在常见的数字信号系统中,大部分传感器信号被化成电压信号,而 ADC把电压模拟信号转换成易于计算机存储、处理的数字编码,由计算机处理完成后,再由 DAC输出电压模拟信号,该电压模拟信号常常用来驱动某些执行器件,使人类易于感知,如音频信号的采集及还原就是这样一个过程。
  • STM32具有片上DAC外设,它的分辨率可配置为 8位或 12位的数字输入信号,具有两个 DAC 输出通道,这两个通道互不影响,每个通道都可以使用 DMA 功能,都具有出错检测能力,可外部触发。

STM32的DAC模块主要特点有:

  • 2个DAC转换器,每个转换器对应1个输出通道
  • 8位或12位单调输出
  • 12位模式下数据左对齐或右对齐
  • 同步更新功能
  • 噪声波形形成
  • 三角波形形成
  • 双DAC通道同时或者分别转换

DAC结构框图如下
在这里插入图片描述

2. 使用DAC输出周期2KHZ的正弦波

建议先用单音音频(比如2000Hz的正弦波)的wav数据进行实验,通过DAC或PCM音频模块能够基本还原出原始正弦波声音后,再用语言/音乐信号进行实验。

工程链接:https://pan.baidu.com/s/1gEATonRXK7km3yf-Mu0eEQ
提取码:1234

  • 打开Adobe Audition CS6,点击文件->新建->音频文件
    在这里插入图片描述
  • 效果->生成基本音色
    在这里插入图片描述
  • 文件->导出->设置导出为wav文件
    在这里插入图片描述
  • 用UltraEdit打开刚才保存的wav文件
    在这里插入图片描述
  • CTRL+A,接着鼠标右键,选择十六进制复制选定视图,将内容粘贴到一个新建文件中
    在新建文件中,CTRL+A,然后右键,选择范围输入起始的行号和列号,确定就选中了整个内容
    在这里插入图片描述
  • 然后将上面选中的内容复制到notepad++中,Edit->Column editor(列块编辑-)>输入,0x
    点击第一行的每个数字后面,列编辑插入,0x
    在这里插入图片描述
  • 将生成的十六进制写入FLASH
    打开keil工程的bsp_dac.c文件,将内容复制波形数据位置,在下图中进行替换
    在这里插入图片描述
    在这里插入图片描述
    然后编译下载,看能否观察到预期的正弦波

3. 数字音频还原播放

编程读取此段音频,通过stm32f103c8t6自带的DAC通道,转换为模拟音频进行播放,并用示波器观察波形,用耳机/喇叭收听,评判音乐还原效果。

  • 用Adobe Audition CS6打开一个音频文件
    在这里插入图片描述
  • 右键,点击存储选择为,然后更改参数
    在这里插入图片描述
  • 点击确定,完成
    在这里插入图片描述
    接下来用“使用DAC输出周期2KHZ的正弦波”一样的方法:
    ① 用UltraEdit打开刚才保存wav文件-> ② 选择十六进制复制选定视图,将内容粘贴到一个新建文件中-> ③ 将选中的内容复制到notepad++中-> ④ 采用列编辑,在每一列前面加上0x-> ⑤ 将生成的音频十六进制写入FLASH-> ⑥ 打开keil中的文件bsp_dac.c,将正弦波数据换成音频文件生成的数据(刚刚从FLASH写入并读出的数据)-> ⑦ 替换后,烧录程序,通过DAC模块可收听音乐。

五. 总结

本文介绍了STM32内部的Flash原理,以及认识了DAC。通过实例——使用DAC输出周期2KHZ的正弦波和使用STM32F103的DAC功能实现音频输出,进一步了解了Flash的写入过程,将音频文件写入Flash比较复杂,要通过采用audition、ultraedit、notepad++ 手工将wav音频数据提取、再将音频歌曲数据转换为模拟音频波形输出(循环)一系列过程才能成功做出来。

六. 参考资料

https://blog.csdn.net/lushoumin/article/details/87694389
https://www.cnblogs.com/pertor/p/9484663.html
https://blog.csdn.net/zhanglifu3601881/article/details/96632971
https://www.jianshu.com/p/a0c6ec6fcff4
https://blog.csdn.net/qq_43279579/article/details/111990896
https://blog.csdn.net/qq_45748462/article/details/112392144

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

STM32之音频数据的Flash读取与DAC播放 的相关文章

  • GCC ARM 汇编预处理器宏

    我正在尝试使用汇编 ARM 宏进行定点乘法 define MULT a b asm volatile SMULL r2 r3 0 1 n t ADD r2 r2 0x8000 n t ADC r3 r3 0 n t MOV 0 r2 ASR
  • C 嵌入式应用程序中 time() 函数的问题

    我在用time 在 ARM 微控制器上 处理器一到达此函数就会重新启动 奇怪的是 当我处于调试模式时 代码运行得很好 但一旦我想将其应用到独立模式 我就会遇到重置 我是否忽略了什么 这个功能有替代品吗 代码部分是这样的 include
  • 读取和打印手臂组件中的字符串

    我正在使用 ARMSim 刚刚开始学习汇编 所以如果我看起来一无所知 请原谅我 但我正在尝试从输入文件中读取字符串 然后将其打印到输出屏幕 到目前为止我有 equ SWI Open 0x66 open a file equ SWI Clos
  • 可以使用Visual Studio 2012构建ARM桌面程序吗?

    我正在使用 Visual Studio 2012 beta 我的桌面 win32 程序在 ARM 架构中编译得很好 升级到 Visual Studio 2012 RC 后 编译器无法工作并出现以下错误 不支持为 ARM 平台编译桌面应用程序
  • 分析 Cortex-M7 (stm32f7) 上的 memcpy 性能

    简洁版本 从 GNU ARM 工具链中提取的 memcpy 的性能指标在 ARM Cortex M7 上对于不同的副本大小似乎差异很大 即使复制数据的代码始终保持不变 这可能是什么原因造成的 长版 我是使用 GNU Arm 工具链 11 2
  • 移动数组中的元素

    我需要一点帮助 我想将数组中的元素向上移动一个元素 以便新位置 1 包含位置 1 中的旧值 new 2 包含 old 1 依此类推 旧的最后一个值被丢弃 第一个位置的新值是我每秒给出的新值 我使用大小为 10 的数组 uint32 t TE
  • STM32 传输结束时,循环 DMA 外设到存储器的行为如何?

    我想问一下 在以下情况下 STM32 中的 DMA SPI rx 会如何表现 我有一个指定的 例如 96 字节数组 名为 A 用于存储从 SPI 接收到的数据 我打开循环 SPI DMA 它对每个字节进行操作 配置为 96 字节 是否有可能
  • 了解 ctags 文件格式

    我使用 Exhuberant ctags 来索引我的 c 项目中的所有标签 c project 是 Cortex M7 微控制器的嵌入式软件 结果是一个标签文件 我正在尝试阅读该文件并理解所写的内容 根据我找到的 ctags 和 Exhub
  • 使用 NEON 优化 Cortex-A8 颜色转换

    我目前正在执行颜色转换例程 以便从 YUY2 转换为 NV12 我有一个相当快的函数 但没有我预期的那么快 主要是由于缓存未命中 void convert hd uint8 t orig uint8 t result uint32 t wi
  • 了解 U-Boot 内存占用

    我不明白加载 U Boot 时 RAM 中发生了什么 我正在开发 Xilinx Zynq ZC702 评估套件 并尝试使用 U Boot 在其上加载 Linux 内核 于是我使用Xilinx工具Vivado和SDK生成了一个BOOT bin
  • 如何获取结构体中任意成员的位位置

    如何获取结构体中任意成员的位位置 在示例中 gt typedef struct BitExamStruct unsigned int v1 3 unsigned int v2 4 unsigned int v3 5 unsigned int
  • 交叉编译armv5,但它创建v7二进制文件

    我设法为arm926ej s创建了一个目标文件我在 qemu 上使用 Debian Arm arm linux gnueabi gcc 4 4 static O c mcpu arm926ej s hello c o hello root
  • 英特尔的最后分支记录功能是英特尔处理器独有的吗?

    最后分支记录是指存储与最近执行的分支相关的源地址和目标地址的寄存器对 MSR 的集合 它们受英特尔酷睿 2 英特尔至强和英特尔凌动处理器系列的支持 http css csail mit edu 6 858 2012 readings ia3
  • 为什么前向引用 ADR 指令在 Thumb 代码中以偶数偏移进行汇编?

    To bx对于 Thumb 函数 需要设置地址的最低有效位 GNU 作为文档states https sourceware org binutils docs as ARM Opcodes html当地址是从一个生成时这是如何工作的adr伪
  • .ko 文件是如何构建的

    我正在尝试将我自己的驱动程序移植到Beagle 板 xm arm cortex A8 在移植时我试图弄清楚如何 ko文件实际构建 在我们的Makefile我们只有一个命令来构建 o file 怎样是一个 ko文件已建立 使用Linux 2
  • 哪些 GCC 优化标志对二进制大小影响最大?

    我正在使用 GCC 为 ARM 开发 C 我遇到了一个问题 我没有启用优化 我无法创建二进制文件 ELF https en wikipedia org wiki Executable and Linkable Format 对于我的代码 因
  • A64 Neon SIMD - 256 位比较

    我想将两个小端 256 位值与 A64 Neon 指令 asm 进行有效比较 平等 为了平等 我已经有了解决方案 bool eq256 const UInt256 lhs const UInt256 rhs bool result 首先 将
  • 你好世界,裸机 Beagleboard

    我正在尝试在我的 Beagleboard xm rev 上运行 hello world 类型的程序 C 通过调用 Cputs功能来自装配 到目前为止 我一直使用这个作为参考 http wiki osdev org ARM Beagleboa
  • 了解带有 pc 偏移的 Cortex-M 组件 LDR

    我正在查看这段 C 代码的反汇编代码 define GPIO PORTF DATA R volatile unsigned long 0x400253FC int main void Initialization code while 1
  • 支持 ARM 上的 Windows 10 桌面应用程序 - MFC 和 COM 以及 OPOS 可以工作吗?

    我试图了解将在 x86 Windows 10 上运行的 C MFC 应用程序移植到具有 Qualcomm Snapdragon 处理器的 ARM Windows 10 设备的障碍 32位应用程序具有以下特点 MFC 与 C 用于用户界面 C

随机推荐

  • case when 作为条件_sqlserver条件分支case when使用教程

    在sqlserver的条件分支case when有两种写法 1 case 字段 when 值 then 返回值 when 值2 then 返回值2 end 2 case when 条件1 then 返回值1 when 条件2 then 返回
  • 【翻译】 zswap 压缩交换缓存

    您知道吗 LWN net 是一份由订阅者支持的出版物 我们依靠订阅者来维持整个运作 请通过订阅来帮助我们 让 LWN 继续在网络上运行 2013年2月12日 本文由 Seth Jennings 投稿 交换是性能的最大威胁之一 即使在快速固态
  • mysql中的default_详解MySQL中default的使用

    详解MySQL中default的使用 发布时间 2020 10 03 17 50 29 来源 脚本之家 阅读 60 作者 子不语 wj NULL 和 NOT NULL 修饰符 DEFAULT 修饰符 AUTO INCREMENT 修饰符 N
  • unity的LOD组件

    本文转载自http blog csdn net huutu article details 52106468 LOD是Level Of Detais 的简称 多细节层次 在游戏场景中 根据摄像机与模型的距离 来决定显示哪一个模型 一般距离近
  • 电脑开机时按F几重装系统

    常使用电脑的朋友都清楚 电脑系统有时候会出现故障 如果是出现不能开机的情况就需重我们装系统 那么如果 重装系统f几进入呢 下面小编现在来为大家详细的介绍一下电脑重装系统开机按F几 一起往下看 工具 原料 系统版本 win7系统 品牌型号 联
  • linux内核学习(7)粗略走走kbuild Makefile编译流程

    今天看Makefile文件 我头大了 此Makefile非彼Makefile 里面多了很多内置命令 比如origin patsubst等等啦 这些都没听说过 更可恶的是 连网上都没有 可见 这是一件多么伤人的事情 分析这样的 真是让人折寿啊
  • Toad常用快捷键和缩写替换

    Toad常用快捷键 F8 调出以前执行的sql命令 F9 执行全部sql Ctrl T 补全table name 或者显示字段 alt 箭头上下 看sql history Ctrl Enter 直接执行当前选中的sql Ctrl Shift
  • 文件服务器恢复测试,基于文件传输中文件损坏检测和恢复办法.doc

    基于文件传输中文件损坏检测和恢复办法 基于文件传输中文件损坏检测和恢复办法 摘 要 在网络上文件传输是一种常见的应用 讨论在文件传输完成后检测错误和恢复数据的办法 关键词 文件传输 文件校验 恢复 中图分类号 TP3 文献标识码 A 文章编
  • Python3学习笔记之-第三方模块(Pillow)

    目录 前言 一 安装Pillow 二 操作图像 前言 在 pillow之前处理图形的库莫过于PIL 但是它支持到python2 7 年久失修 于是一群志愿者在PIL的基础上常见了pillow 支持python3 又丰富和功能特性 一 安装P
  • 百度会员租赁地址

    http xinjipin com 转载于 https www cnblogs com byfboke p 11602017 html
  • 10:00面试,10:04就出来了 ,问的实在是太...

    从外包出来 没想到竟然死在了另一家厂子 自从加入这家公司 每天都在加班 钱倒是给的不少 所以我也就忍了 没想到12月一纸通知 所有人都不许加班 薪资直降30 顿时有吃不起饭的赶脚 好在有个兄弟内推我去了一家互联网公司 兴冲冲见面试官 没想到
  • Maven搭建私有仓库(私服)

    Nexus简介 作为一个非常优秀且我找不到合适的替代品的二进制包储存库 功能也是非常强大 不单纯只能设置Maven私有仓库 包括我们常见的Yum Docker npm NuGel等等 专业版需要付费 个人用免费版就可以 专业版更加强大 专业
  • Communications link failure官网解释

    官网给的解释 https dev mysql com doc connector j 8 0 en connector j usagenotes troubleshooting html qandaitem 15 1 8 15 8 What
  • Ubuntu下QT程序打包(直接拷贝动态库和可执行文件)

    系统为Ubuntu16 04LTS QT版本为5 12 9 QT默认为动态编译 生成的可执行文件需要有依赖库才能运行 因此在打包的时候需要把程序的依赖库一块打包 这样子做会导致打包的程序非常大 因为依赖库也都一块拷贝了 这种方法好处是快捷
  • TS的类型转换

    TS的类型转换 元组类型转联合类型 元组类型转联合类型2 联合类型转交叉类型 将这个方法拆成两部分来看 U extends any k U gt void never 是第一部分 extends k infer I gt void I ne
  • 数据库——数据库备份与恢复

    目录 原因 数据库的备份与恢复 1 使用MySQLdump命令备份 2 恢复数据库 表的导入和导出 1 表的导出 2 表的导入 原因 尽管采取了一些管理措施来保证数据库的安全 但是不确定的意外情况总是有可能造成数据的损失 例 如意外的停电
  • GooglePlay提审警告(com.google.android.gms:play-services-safetynet:17.0.0)

    1 Goole在今年6月份出的新政策 不在使用safetynet 而使用Play Integrity API 2 项目本身没有使用过safetynet 3 使用了firebase 查阅资料 解决方案如下 implementation pla
  • [python]bokeh学习总结——QuickStart

    bokeh是python中一款基于网页的画图工具库 画出的图像以html格式保存 一个简单的例子 from bokeh plotting import figure output file show output file patch ht
  • 电子信息工程电子信息毕设分享100例(一)

    单片机毕业设计项目分享系列 这里是DD学长 单片机毕业设计及享100例系列的第一篇 目的是分享高质量的毕设作品给大家 包含全面内容 源码 原理图 PCB 实物演示 论文 这两年开始毕业设计和毕业答辩的要求和难度不断提升 传统的单片机项目缺少
  • STM32之音频数据的Flash读取与DAC播放

    文章目录 一 STM32103之内部Flash原理 1 Flash介绍 2 Flash的组成 3 STM32内部框架图 二 SD卡的读写 1 实验过程 2 查看hello txt 3 从SD卡里读出数据 三 Flash地址空间的数据读取 1