STM32嵌入式:如何使用keil 来获取flash块数据并转换成可视化的数据 来判断源头数据是否错误
比较实用的实战步骤,分三部分讲:
- 在 Keil 里怎么看到/导出 Flash 数据
- 怎么把原始 16 位补码数据“转成”我们能看懂的波形
- 怎么对比“Flash 源头数据”和“你解析出来的数据”,判断解析是否错误
中间用一点伪命令,你按自己芯片型号和工程改一下就行。
一、整体流程示意(先有个整体感)
简单来说,你可以按下面这个思路来:
二、在 Keil 中查看并导出 Flash 数据
1. 调试状态下打开 Memory 窗口
- 在 Keil 中:
- 点击 “Debug” → “Start/Stop Debug Session”(或 Ctrl+F5)进入调试
- 菜单 “View” → “Memory Windows” → “Memory 1/2/3/4”,打开一个内存窗口
- 在 Memory 窗口的 “Address” 输入栏里填你要看的那块 Flash 地址,例如:
0x08000000(STM32 的 Flash 起始地址,按你芯片的实际情况来)
- 回车后,下面就会显示一段原始字节内容,形如:
0x08000000 03 1F 03 3B 01 AD 01 15 ...
如果你的 Flash 数据已经是一些“采样数据”写死在里面(比如 const 数组),你会在 Memory 窗口里看到它们以字节形式排布。
2. 确认数组/变量的地址
如果数据是以某个常量数组存在代码里,比如:
- 代码中定义:
const int16_t flash_wave[128] = { ... };
那么在调试时:
- 打开 “View” → “Watch Windows” → “Watch 1”
- 在 Watch 窗口里输入
flash_wave,Keil 会显示:- 这个数组的起始地址(比如
0x08001234) - 数组里各个位置的值(如果显示格式对的话)
你可以记下这个起始地址和长度,然后直接用 Memory 窗口看同一块区域。
- 这个数组的起始地址(比如
3. 使用 SAVE 命令导出一段 Flash 到文件(重点)
Keil 的调试命令行(Debug → Command 窗口)支持 SAVE 命令,可以把一段内存内容导出为 Intel HEX 文件:
- 在 Command 窗口输入(示例):
参数含义:SAVE flash_block.hex 0x08000000, 0x08000400flash_block.hex:你要保存的文件名(默认在工程目录)0x08000000:起始地址0x08000400:结束地址(导出0x08000000~0x080003FF这一段)
注意:这里写的是“结束地址”,不是“长度”。
- SAVE 命令说明文档里讲的是:将内存区域以 HEX386 格式写入文件。
这样你就有了一份“从 Flash 里原封不动导出来”的数据文件。
三、把 HEX 转成数组/CSV,并做 16 位补码转换
现在你拿到了flash_block.hex,但它是 HEX 格式,不方便直接画图,一般做法是:
- 要么用在线工具 / 小工具把 HEX 转成二进制 / C 数组 / CSV
- 要么直接用 Python/C 脚本解析 HEX 文件并做补码转换
这里给你一个最通用、可自动化的做法:用 Python 脚本一次性做完“解析 HEX + 补码转换 + 生成 CSV”。
假设: - 你的原始采样数据是 16 位,按“小端模式(Little Endian)”存在 Flash 中(STM32 这类常见)
- 地址
0x08000000开始是:0x03 0x1F 0x03 0x3B 0x01 0xAD ...
- 对应的 16 位有符号值是:
0x1F03、0x3B03、0xAD01…
示例:Python 脚本解析 HEX + 转 16 位有符号 + 生成 CSV
你可以建一个parse_hex.py:
importintelheximportcsv# 输入 Keil 导出的 HEX 文件hex_file="flash_block.hex"csv_file="flash_wave.csv"start_addr=0x08000000end_addr=0x08000400# 和 SAVE 命令的结束地址一致ih=intelhex.IntelHex(hex_file)values=[]addr=start_addrwhileaddr<end_addr:# 小端模式:低字节在前,高字节在后low=ih[addr]high=ih[addr+1]u16=(high<<8)|low# 组合成 16 位无符号整数# 转为 16 位有符号(补码)ifu16&0x8000:i16=u16-0x10000else:i16=u16 values.append(i16)addr+=2# 每次跳 2 字节# 写入 CSV(只有一列,就是转换后的值)withopen(csv_file,"w",newline="")asf:writer=csv.writer(f)writer.writerow(["Index","Value"])# 表头fori,vinenumerate(values):writer.writerow([i,v])print(f"Done. Written{len(values)}samples to{csv_file}.")需要先安装intelhex库:
pipinstallintelhex运行脚本后,你会得到flash_wave.csv,里面形如:
Index,Value 0,192 1,315 2,429 3,533 ...这个Value就是你“Flash 源头数据 + 16 位补码转换”之后的真实波形值。
四、可视化成波形,和你的解析结果对比
有了 CSV 之后,你可以用三种常用方式可视化。
1)用 Excel 快速看趋势
- 直接用 Excel 打开
flash_wave.csv - 选中两列数据,插入“折线图”
- 看一下:
- 波形是否类似正弦/三角/你预期的信号
- 有没有奇怪的跳变(比如从几百突然跳到几千)
这一步主要用来判断“源头数据本身是不是就乱”。
2)用 Python + Matplotlib 画更漂亮的波形
importcsvimportmatplotlib.pyplotasplt xs=[]ys=[]withopen("flash_wave.csv")asf:reader=csv.DictReader(f)forrowinreader:xs.append(int(row["Index"]))ys.append(int(row["Value"]))plt.figure(figsize=(10,4))plt.plot(xs,ys,marker='o',markersize=3,linestyle='-')plt.title("Flash Waveform (signed 16-bit)")plt.xlabel("Sample Index")plt.ylabel("Amplitude")plt.grid(True)plt.tight_layout()plt.show()这样你可以很清楚地看到:
- 是否是连续平滑的波形
- 在 0 附近是否正确跨越正负(例如从几十到负几十,而不是从几十跳到 65471)
3)和“你的代码解析结果”对比(判断是否解析错误)
你前面有一段从data里解析通道数据并做twos_comp的代码。你可以:
- 在 Keil 调试时:
- 在 Watch 窗口添加
wavedata.channelValue[curchanneltmp][0]、lenOfChannel等,实时查看解析后的数组。 - 或在调试过程中执行类似的
SAVE命令,把解析后的数组所在内存也导出一份 HEX/CSV。
- 在 Watch 窗口添加
- 然后画在同一张图上对比:
- 曲线 A:直接从 Flash 导出 + 正确补码转换(“标准答案”)
- 曲线 B:你的代码解析出来的数据
对比时看几个点:
- 形状是否一致(正弦/方波/三角等)
- 幅值是否一致
- 有没有“多出一些奇怪的点”或“整体平移/翻转”
- 特别检查从正到负的过渡区域(例如你之前那组 60 → 65471)
如果形状明显不一样,大部分是你代码里: - 通道号/索引用错
- 数据长度算错
- 高低位字节搞反
- 没有做补码转换或转换方式不对
五、几个常见坑和检查点
1)大小端模式
- 很多 ARM MCU(STM32 等)是小端:
- 低字节在低地址,高字节在高地址
- 如果你误用大端解析,波形会变得完全不对(幅值、噪声都异常)
- 可以在 Memory 窗口先看几个典型点的字节分布:
- 如果 Flash 中是
01 FC,预期值大概是 0xFC01 - 如果你解析出 0x01FC,说明大小端反了
- 如果 Flash 中是
2)数据类型 & 步长
- 如果你的数据实际是:
- 16 位有符号:用 int16_t,步长 = 2 字节
- 32 位有符号/浮点:步长 = 4 字节
- 步长错了,会导致解析错位(比如把高/低字拼到相邻采样上)
3)数组边界 / 通道切换
- 你之前的代码里
curchanneltmp和maxchannel等逻辑,如果在 Flash 里有多个通道连续排布:- 需要确认每个通道的起始地址和长度
- 不然容易把两个通道的数据拼在一起,看起来像奇怪的折线
4)如果只是简单确认 Flash 数据是不是“写死”得对
- 直接在 Keil 里:
- 打开 Watch 窗口,添加数组变量
- 看它的值是不是你预期
- 再对比一下 Memory 窗口同一位置的字节
- 如果两者都对,那问题基本就出在“接收/解析”逻辑,而不是 Flash 源头。
六、小结:推荐的实战步骤
给出一个简单可照抄的流程:
- 在 Keil 里调试,Memory 窗口定位到你的 Flash 数组起始地址(比如
0x08001000) - 用 Command 窗口执行:
导出这块数据到 HEX 文件SAVE flash_block.hex 0x08001000 0x08001200 - 用 Python 解析这个 HEX 文件,按 16 位小端 + 补码转换,生成
flash_wave.csv - 用 Excel/Matlab/Python 画出波形,确认是否像你预期的正弦/三角等信号
- 同时,在 Keil 调试中导出你代码解析出来的数组(也可以用 SAVE 或 watch+复制),按相同方式画出来
- 把两张图叠在一起对比:
- 完全一致:说明解析没问题,源头数据也 OK
- 不一致:根据差异点反推:
- 波形整体平移/翻转 → 大小端或符号位有问题
- 多出跳变、毛刺 → 通道/长度/索引处理有误
- 某一段完全乱掉 → 可能是多通道混在一起或数组指针错