
一、FRAM_wr(Uint16 xaddr,Uint16 wd)骨架一句话主线拉低片选 → 发 WRITE 命令 → 发地址 → 发数据 → 拉高片选。八行口诀一拉低二延时三发命令四发地址高五发地址低六发数据高七发数据低八拉高。分组表组行干什么一句话A 拉低1~2拉低片选 延时选中 FM25CL64B等它识别片选B 发命令3~4赋值 WRITE 发送发写数据命令 0x02C 发地址5~8发地址高字节 发地址低字节MSB first先高后低D 发数据9~12发数据高字节 发数据低字节MSB first先高后低E 拉高13拉高片选释放 FM25CL64B上升沿触发内部写周期并清 WEL重要前置条件FRAM_wr() 调用前必须先 wren_cmd()否则 WEL0WRITE 被拒绝。FRAM_wr 后的行为片选上升沿触发 FM25CL64B 内部写周期。片选上升沿清 WEL0下次写前要重发 WREN。自测题Q1FRAM_wr 分哪几步A拉低 → 发命令 → 发地址 → 发数据 → 拉高。Q2FRAM_wr 发了几次 spisend分别发什么A5 次WRITE 命令码、地址高字节、地址低字节、数据高字节、数据低字节。Q3WRITE_INST 为什么是 0x0200FM25CL64B 的 WRITE 命令码是多少A命令码放高 8 位左对齐低 8 位补 0SPI 取高 8 位发送所以 0x0200 实际发的是 0x02。FM25CL64B 的 WRITE 命令码是 0x02。Q4地址和数据为什么都要分两次发AFM25CL64B 的地址是 16 位实际 13 位高 3 位补 0数据是 16 位。C2000 SPI 是8 位字符每次只能发 8 位所以要发两次高字节 低字节。Q5FRAM_wr 结尾为什么没有 delay(10)AFM25CL64B 是铁电写周期极短ns 级结尾不加 delay 是可以接受的。但片选上升沿会清 WEL0下次写前要重发 WREN。Q6为什么地址高字节用 xaddr低字节用 xaddr8AC2000 SPI 取高 8 位发送。spisend(xaddr)发 xaddr 的高 8 位地址高字节。spisend(xaddr 8)发 xaddr 的低 8 位左移后到了高 8 位。Q7如果写spisend(xaddr 0xFF)会怎样Axaddr 0xFF是低 8 位但spisend取高 8 位发送实际发的是0x00发错了。必须用xaddr 8。Q8地址和数据的字节序是什么AMSB first高字节先发低字节后发。FM25CL64B 要求 MSB first。Q9片选拉高后 WRITE 才生效吗AWRITE 指令和数据在发送过程中就已经进入 FM25CL64B 的内部缓冲片选上升沿是锁定和启动内部写周期的边沿。Q10FRAM_wr 和 FRAM_rd 有什么区别AFRAM_wr()发 WRITE(0x02) 2 字节地址 2 字节数据写操作。FRAM_rd()发 READ(0x03) 2 字节地址 2 字节哑元读操作。读不需要 WREN写必须先 WREN。Q11FRAM_wr 为什么不带回读校验A回读校验在store_para_FRAM()里做。FRAM_wr()只负责写写完就返回。校验是写完立即读回比较属于上层逻辑调用FRAM_wr()的上一层代码store_para_FRAM()。它负责流程控制写使能 → 写 → 回读 → 比较 → 记录错误FRAM_wr()只是它调用的一个下层函数。。Q12如果忘了发 WREN 直接调 FRAM_wr 会怎样AWEL0WRITE 被 FM25CL64B 拒绝数据写不进去。比喻整体比喻FRAM_wr 去银行保险柜存包裹FRAM 芯片 银行的保险柜仓库仓库里有无数个格子存储单元每个格子有一个编号地址你要往某个格子里放东西写入数据必须告诉柜员放哪个格子、放什么东西函数参数xaddr 你要存的格子编号地址wd 你要存的东西16 位数据逐行比喻拆解第一步拉低片选 → 走到银行门口拉门把手GpioDataRegs.GPACLEAR.bit.GPIO11 1; // 拉低片选选中 FM25CL64B比喻你走到银行门口拉低门把手CS 拉低银行开门了。第二步延时 → 等柜员坐定tCSSdelay(10); // 延时保证片选建立时间比喻门开了等柜员从后台走到窗口坐定tCSS你才能开始说话。第三步准备并发送 WRITE 命令码 → 递存包裹申请单spixdat WRITE_INST; // spixdat0x0200WRITE 写数据命令 spisend(spixdat); // 发送 WRITE 命令码给 FM25CL64B比喻你对柜员说我要存东西递申请单命令码 0x02。第四步~第五步发送地址高字节 低字节 → 告诉柜员存哪个格子spixdat xaddr; // 地址高字节xaddr 的高 8 位 spisend(spixdat); // 发送地址高字节 spixdat (xaddr8); // 地址低字节左移后高 8 位 原低 8 位 spisend(spixdat); // 发送地址低字节比喻柜员问存哪个格子你说第 xxx 号格子。因为格子编号是 16 位的比如 0x1234你要分两次说先说高两位12再说低两位34。为什么先发高字节再发低字节FM25CL64B 规定MSB first高位先发这是器件规定照做就行。第六步~第七步发送数据高字节 低字节 → 把东西放进格子spixdat wd; // 数据高字节wd 的高 8 位 spisend(spixdat); // 发送数据高字节 spixdat wd8; // 数据低字节左移后高 8 位 原低 8 位 spisend(spixdat); // 发送数据低字节比喻柜员问存什么东西你把要存的东西递过去。因为东西是 16 位的比如 0xABCD也要分两次递先递高字节AB再递低字节CD。第八步拉高片选 → 关上门走人GpioDataRegs.GPASET.bit.GPIO11 1; // 片选拉高释放 FM25CL64B比喻东西存完了你拉高门把手CS 拉高关上门走人。柜员在关门瞬间把东西锁进保险柜触发内部写周期。注意事项的比喻解释注意①必须先 wren_cmd() 写使能比喻存东西之前必须先递写权限申请单wren_cmd否则柜员会拒绝你——你没授权我不能让你存。注意②必须先拉低片选再发命令最后拉高片选比喻必须开门→办事→关门的顺序不能反过来。没开门就递东西东西掉地上了办完事不关门柜员不知道你到底办完了没有。注意③地址和数据都分两次发先高字节再低字节比喻格子编号和东西都是 16 位的但窗口一次只能递 8 位所以要分两次。FRAM 规定高字节先发MSB first照做就行。注意④片选拉低后加 delay()满足建立时间比喻开门后等柜员坐定tCSS不然你刚开门就说话柜员没听到后面全乱了。注意⑤代码结尾没有 delay()比喻关上门之后代码里没有等柜员处理完的时间。但 FM25CL64B 是铁电写周期极短ns 级通常不会出错。注意片选上升沿会清 WEL0下次写前要重发 WREN。整段注释/******************************************************************************* * 函数名 : FRAM_wr() * 简述 : 向 FM25CL64B 写一个 16 位数据 * * 功能 : 拉低片选依次发送 WRITE 指令 (0x02)、2 字节地址、2 字节数据 * 拉高片选触发内部写周期。具体分 6 步 * 1. 拉低 GPIO 片选选中 FM25CL64B * 2. 发送 WRITE 指令 (0x02) * 3. 发送地址高字节 * 4. 发送地址低字节 * 5. 发送数据高字节 * 6. 发送数据低字节拉高片选触发内部写周期。 * * 参数 : xaddr —— 铁电字节地址16 位实际只用低 13 位 * wd —— 要写入的 16 位数据 * 返回值 : 无 * 调用关系 : 被 store_para_FRAM() 调用内部调用 spisend() * * 公式计算 : 无本函数为纯数据写入 * * 片选 : GPIO11低有效 * * 注意事项 : 1. 调用本函数前必须先发 WREN(0x06)否则 WEL0 被拒绝 * 2. 片选在整个操作期间必须保持低操作结束才拉高 * 3. 片选上升沿触发 FM25CL64B 内部写周期同时清 WEL * 4. 本函数不带回读校验校验在 store_para_FRAM() 中做。 ******************************************************************************/ void FRAM_wr(Uint16 xaddr, Uint16 wd) { // 第 1 步拉低片选选中 FM25CL64B // GPACLEAR 写 1 表示对应位清零GPIO11 输出低电平片选有效 GpioDataRegs.GPACLEAR.bit.GPIO11 1; // 片选拉低GPIO110低有效 // 片选建立时间等 GPIO 电平稳定后再发指令 delay(10); // 延时 10 个单位保证建立时间 // 第 2 步发送 WRITE 指令 (0x02) // spixdat 赋值为 WRITE_INST0x0200左对齐 spixdat WRITE_INST; // 准备 WRITE 指令值 spisend(spixdat); // 发送 WRITE 指令内部等待收发完成 // 第 3 步发送地址高字节 // xaddr 高 8 位左对齐后发送C2000 SPI 只取高 8 位 spixdat xaddr; // 准备地址高字节 spisend(spixdat); // 发送地址高字节 // 第 4 步发送地址低字节 // xaddr 左移 8 位后低 8 位移到高 8 位SPI 取高 8 位发送 spixdat (xaddr 8); // 准备地址低字节左移后高 8 位 原低 8 位 spisend(spixdat); // 发送地址低字节 // 第 5 步发送数据高字节 // wd 高 8 位左对齐后发送 spixdat wd; // 准备数据高字节 spisend(spixdat); // 发送数据高字节 // 第 6 步发送数据低字节 // wd 左移 8 位后低 8 位移到高 8 位SPI 取高 8 位发送 spixdat (wd 8); // 准备数据低字节左移后高 8 位 原低 8 位 spisend(spixdat); // 发送数据低字节 // 第 6 步续拉高片选触发 FM25CL64B 内部写周期 // GPASET 写 1 表示对应位置 1GPIO11 输出高电平操作结束 GpioDataRegs.GPASET.bit.GPIO11 1; // 片选拉高GPIO111写周期开始 }逐行逐位分析第 1 行GpioDataRegs.GPACLEAR.bit.GPIO11 1GpioDataRegs.GPACLEAR.bit.GPIO11 1; // 片选拉低GPIO110低有效位含义位名称值含义GPIO11GPACLEAR 对应位1GPIO 清零寄存器。写 1 将该位清零输出低电平写 0 不影响。GPIO11 作为 FM25CL64B 的片选低有效。为什么用 GPACLEAR原子操作写 1 只清零 GPIO11不影响其他位避免读-改-写竞态。QAQ片选拉低后要 delay 吗A要。delay(10) 保证 FM25CL64B 检测到片选有效tCSS。QGPACLEAR 和 GPADAT 有什么区别AGPACLEAR 是写 1 清零寄存器原子操作GPADAT 读写整个端口有竞态风险。第 2 行delay(10)delay(10); // 延时 10 个单位保证建立时间作用保证 FM25CL64B 在第一个时钟沿之前检测到片选有效tCSS。QAQdelay(10) 是多久A纯软件空循环delay(10)表示循环 10 次F28002x 100MHz 下约几百纳秒。对 FM25CL64B 的 ns 级 tCSS 足够。第 3 行spixdat WRITE_INSTspixdat WRITE_INST; // 准备 WRITE 指令值位含义位名称值含义WRITE_INST宏定义0x0200WRITE 指令。左对齐高 8 位 0x02低 8 位 0x00。FM25CL64B 的 WRITE 指令项目值指令名WRITE编码0000_0010b (0x02)作用写数据到指定地址前置条件必须先 WREN后续字节2 字节地址 N 字节数据QAQWRITE 指令需要先 WREN 吗A需要。WEL0 时 WRITE 被拒绝。store_para_FRAM()里先调wren_cmd()再调FRAM_wr()。QWRITE_INST 为什么是 0x0200 而不是 0x02AC2000 SPI 8 位模式下数据必须左对齐。0x0200 高 8 位是 0x02SPI 取高 8 位发送正确。第 4 行spisend(spixdat)spisend(spixdat); // 发送 WRITE 指令内部等待收发完成执行过程步骤操作1写 SPITXBUF 0x02002SPI 硬件取高 8 位 0x02移位输出3spisend 内部while(INT_FLAG0)等待4读 SPIRXBUF清 INT_FLAGQAQWRITE 指令发送后FM25CL64B 会返回数据吗A不会。WRITE 是写指令FM25CL64B 在 SOMI 上不返回有效数据。spiimg里是无意义值。第 5 行spixdat xaddrspixdat xaddr; // 准备地址高字节 spisend(spixdat); // 发送地址高字节位含义位名称值含义xaddr参数16 位地址铁电字节地址。高 8 位是地址的高字节低 8 位是地址的低字节。地址发送分析xaddr高 8 位 地址高字节。spisend(xaddr)左对齐后发送的是xaddr的高 8 位即地址高字节。FM25CL64B 地址要求项目值地址宽度13 位8KByte地址字节数2 字节字节序MSB first高字节先发高位补 0高 3 位补 0QAQ为什么先发地址高字节AFM25CL64B 要求 MSB first地址高字节先发低字节后发。Qxaddr 0x0010 时实际发的是什么Aspisend(0x0010)左对齐后发0x00高字节spisend(0x1000)左对齐后发0x10低字节。第 6 行spixdat (xaddr 8)spixdat (xaddr 8); // 准备地址低字节左移后高 8 位 原低 8 位 spisend(spixdat); // 发送地址低字节位含义位名称值含义xaddr 8移位操作16 位把 xaddr 的低 8 位移到高 8 位。SPI 取高 8 位发送即发送原 xaddr 的低 8 位。移位分析xaddr 0x0010xaddr 8 0x1000spisend(0x1000)左对齐后发高 8 位0x10即原 xaddr 的低 8 位。QAQ为什么用xaddr 8而不是xaddr 0xFFA因为 C2000 SPI 取高 8 位发送。要发低 8 位必须把它移到高 8 位。xaddr 8正好做到。Q如果写spisend(xaddr 0xFF)会怎样Axaddr 0xFF 0x10spisend(0x0010)左对齐后发高 8 位0x00发错了。第 7 行spixdat wdspixdat wd; // 准备数据高字节 spisend(spixdat); // 发送数据高字节位含义位名称值含义wd参数16 位数据要写入的数据。高 8 位是数据高字节低 8 位是数据低字节。数据发送分析spisend(wd)左对齐后发送wd的高 8 位即数据高字节。QAQ为什么先发数据高字节AFM25CL64B 要求 MSB first数据高字节先发。Qwd 0x1234 时实际发的是什么Aspisend(0x1234)左对齐后发0x12高字节spisend(0x3400)左对齐后发0x34低字节。第 8 行spixdat (wd 8)spixdat (wd 8); // 准备数据低字节左移后高 8 位 原低 8 位 spisend(spixdat); // 发送数据低字节位含义位名称值含义wd 8移位操作16 位把 wd 的低 8 位移到高 8 位。SPI 取高 8 位发送即发送原 wd 的低 8 位。移位分析wd 0x1234wd 8 0x3400spisend(0x3400)左对齐后发高 8 位0x34即原 wd 的低 8 位。QAQ数据和地址的发送方式一样吗A一样。都是高字节用原值低字节用左移 8 位因为 C2000 SPI 取高 8 位发送。第 9 行GpioDataRegs.GPASET.bit.GPIO11 1GpioDataRegs.GPASET.bit.GPIO11 1; // 片选拉高GPIO111写周期开始位含义位名称值含义GPIO11GPASET 对应位1GPIO 置位寄存器。写 1 将该位置 1输出高电平写 0 不影响。FM25CL64B 片选高有效表示操作结束。FM25CL64B 的 WRITE 时序阶段片选数据操作前高—发送 WRITE 指令低0x02发送地址高字节低xaddr 高 8 位发送地址低字节低xaddr 低 8 位发送数据高字节低wd 高 8 位发送数据低字节低wd 低 8 位操作后高—片选上升沿的作用触发 FM25CL64B 内部写周期数据被写入铁电存储单元。清 WEL 位WEL 自动清 0下次写前要重发 WREN。QAQ片选拉高后 WRITE 才生效吗AWRITE 指令和数据在发送过程中就已经进入 FM25CL64B 的内部缓冲片选上升沿是锁定和启动内部写周期的边沿。Q片选拉高后 WEL 会怎样AWEL 自动清 0。下次写前要重发 WREN。Q片选拉高后要 delay 吗AFM25CL64B 是铁电写周期极短ns 级通常不需要。但严格来说可以加一个短 delay。四、逐位分析总结表序代码寄存器/位值中文含义面试要点1GPACLEAR.GPIO11 1GPACLEAR.bit.GPIO111片选拉低原子操作写 1 清零低有效2delay(10)——片选建立时间保证 FM25CL64B 检测到片选3spixdat WRITE_INST全局变量0x0200WRITE 指令值左对齐高 8 位 0x024spisend(spixdat)SPITXBUF0x0200发送 WRITE 指令8 位模式只发高 8 位5spixdat xaddr全局变量xaddr地址高字节高 8 位 地址高字节6spisend(spixdat)SPITXBUFxaddr发送地址高字节MSB first7spixdat xaddr 8全局变量xaddr8地址低字节左移后高 8 位 原低 8 位8spisend(spixdat)SPITXBUFxaddr8发送地址低字节MSB first9spixdat wd全局变量wd数据高字节高 8 位 数据高字节10spisend(spixdat)SPITXBUFwd发送数据高字节MSB first11spixdat wd 8全局变量wd8数据低字节左移后高 8 位 原低 8 位12spisend(spixdat)SPITXBUFwd8发送数据低字节MSB first13GPASET.GPIO11 1GPASET.bit.GPIO111片选拉高触发内部写周期清 WEL五、提问与答案解析Q1FRAM_wr() 的作用是什么答向 FM25CL64B 写一个 16 位数据。流程为拉低片选 → 发 WRITE 指令 (0x02) → 发 2 字节地址 → 发 2 字节数据 → 拉高片选触发内部写周期。Q2FRAM_wr() 前为什么必须先 WREN答FM25CL64B 的 WEL 位必须为 1 才能写数据。WEL 只有 WREN 指令能置 1。所以store_para_FRAM()里先调wren_cmd()再调FRAM_wr()。Q3WRITE_INST 为什么是 0x0200 而不是 0x02答C2000 SPI 8 位模式下数据必须左对齐。0x0200 高 8 位是 0x02SPI 取高 8 位发送正确。Q4为什么地址和数据都要发两次答FM25CL64B 的地址是 16 位实际 13 位高 3 位补 0数据是 16 位。C2000 SPI 是 8 位字符每次只能发 8 位所以要发两次高字节 低字节。Q5为什么地址高字节用 xaddr低字节用 xaddr8答C2000 SPI 取高 8 位发送。spisend(xaddr)发 xaddr 的高 8 位地址高字节。spisend(xaddr 8)发 xaddr 的低 8 位左移后到了高 8 位。Q6如果写spisend(xaddr 0xFF)会怎样答xaddr 0xFF是低 8 位但spisend取高 8 位发送实际发的是0x00发错了。必须用xaddr 8。Q7地址和数据的字节序是什么答MSB first高字节先发低字节后发。FM25CL64B 要求 MSB first。Q8片选拉高后 WRITE 才生效吗答WRITE 指令和数据在发送过程中就已经进入 FM25CL64B 的内部缓冲片选上升沿是锁定和启动内部写周期的边沿。Q9片选拉高后 WEL 会怎样答WEL 自动清 0。下次写前要重发 WREN。Q10FRAM_wr() 为什么不带回读校验答回读校验在store_para_FRAM()里做。FRAM_wr()只负责写写完就返回。校验是写完立即读回比较属于上层逻辑。Q11如果忘了拉高片选会怎样答FM25CL64B 一直选中下一条指令被当作本次操作的后续字节命令错位。Q12FRAM_wr() 和 FRAM_rd() 有什么区别答FRAM_wr()发 WRITE(0x02) 2 字节地址 2 字节数据写操作。FRAM_rd()发 READ(0x03) 2 字节地址 2 字节哑元读操作。读不需要 WREN写必须先 WREN。六、一句话总结FRAM_wr()就干六件事拉低片选 → 发 WRITE(0x0200) → 发地址高字节 → 发地址低字节 → 发数据高字节 → 发数据低字节 → 拉高片选。核心点WRITE 前必须先 WRENWRITE 指令是 0x0200左对齐地址和数据都是 MSB first高字节先发低字节用 8左移到高 8 位片选上升沿触发内部写周期并清 WEL。面试重点WRITE 前必须 WREN、左对齐原因、地址/数据字节序、低字节左移、片选上升沿作用。