枚举当前目录下的所有mp4,然后创建同名的目录并将对应文件移动进去。
@echo off
for %%i in (*.mp4) do (
mkdir "%%~ni" 2>nul
move /y "%%i" "%%~ni\"
)
枚举当前目录下的所有mp4,然后创建同名的目录并将对应文件移动进去。
@echo off
for %%i in (*.mp4) do (
mkdir "%%~ni" 2>nul
move /y "%%i" "%%~ni\"
)
要点:
1.淘宝购买的RDA5807FP芯片有可能是假的/有问题的。具体现象是:只能听到沙沙声,无法收到电台。搜索到有人遇到同样的问题,请教B站大佬“我是电视电视迷”,他推荐淘宝“深圳市义胜电子网店”,我重新购买的芯片,换上去就OK了;
2.这个芯片有两种模式:全自动和单片机模式,上电之前要跳线选择一下:
3.使用一个引线作为天线,理论上这个电线越长效果越好,我用了20cm就可以收到台了;他人的设计有直接使用耳机的地,就是耳机左声道右声道,然后再用地做天线;
4.打板2次,第一次设计用了 0603 电容电阻,后来发现这个封装 22pf以上比较少见,就修改为 1206电容电阻了,这样也方便焊接。
工作的视频在
项目主页在:
https://oshwhub.com/zoologist/rda5807fp-shou-yin-ji
电路图和PCB 还可以在这里直接下载:
堆叠方法通常用于经济地增加 PC 内存的密度,即通过添加额外的层(例如芯片、IC 和/或 PCB)来实现。堆叠通常分为两种布置方式:卡叠卡和封装叠封装。
为了提高单芯片密度,BGA 芯片采用了多芯片封装 (MCP) 堆叠技术。与沿 DIMM 侧面排列多个芯片不同,每个芯片在单个芯片封装内彼此堆叠。DRAM 制造商可能拥有自己专有的晶圆减薄和微型化技术,以实现更小的封装和更高的芯片数量。
从 2007 年起,三星半导体预计将使用其专有的“硅通孔”(TSV)和全 DRAM 堆叠配置——这种设计更简洁、更小、更快、功耗更低。然而,三星并不是唯一使用这项技术的公司,英特尔在去年春季的 IDF 上也曾演示过Terascale 技术,将内存堆叠在 80 核处理器上。
当今 MCP 技术的问题在于,不同层的 DRAM 通过脆弱的引线键合连接。这种配置要求层与层之间有几十微米深的垂直间隙,而且这种连接还需要封装板上几百微米宽的水平间距来连接芯片之间的引线。这种技术精度成本高且很难控制质量,因为数百条引线中一条短路都很难找到,更难纠正。
三星晶圆级堆叠封装 资料
来源:Samsung Electronics
据三星称,其晶圆级工艺堆叠封装 (WSP) 技术“通过激光切割形成微米级孔洞,这些孔洞垂直穿透硅片,并将内存电路与铜 (Cu) 填充物直接连接,无需额外的间隙和超出芯片边缘的导线。这些优势使三星的 WSP 能够显著缩小尺寸并实现更薄的封装。”
最新成果是三星改进的 4GB DDR2 内存模块,其性能更加出色。每个芯片将包含四层 512 兆位 DRAM,使每个芯片的密度达到 2 千兆位。升级后的 TSV 技术预计将用于 DDR3。
全球最薄的 1.4 毫米 MCP,配备 20 个堆叠芯片
来源: Elpida Memory
秋田尔必达存储器公司(Akita Elpida Memory)于2007年上半年成功开发出一款厚度仅为1.4毫米的MCP,其内部堆叠了多达20个芯片。该芯片采用独特的研磨技术,厚度仅为30微米,并在处理这些微型芯片、引线键合以及将树脂注入狭窄间隙以实现各层之间电气绝缘等方面也做出了创新。最重要的是,该芯片预计产量高且成本低——坦白说,这确实是一项了不起的成就。
以下是一些用于增加每个模块密度的堆叠方法:
TSOP DRAM 堆叠
来源: STEC
模块堆叠
来源:Kingston Memory
专有 BGA 堆叠
来源: Tessera
TSOP载带封装堆叠技术:芯片放置在载带上,背面暴露。
来源:Elpida Memory
SMART Modular Technologies 在其 CoolFlex DDR2 2GB 薄型模块中采用了一种名为“折叠印刷电路板”的有趣技术。该技术利用折叠 PCB 技术将内存容量翻倍,这样避免了DRAM本身的队列。与更复杂的多芯片堆叠技术相比,这是一种相对经济有效的方法。
超薄型 2GB DDR2 带折叠印刷电路板
资料来源: SMART Modular Technologies
DRAM制造
每个内存芯片封装都包含一个或多个晶圆,每个晶圆都是从一块更大的圆形硅片上切割下来的。制造过程中会进行严格的检查,以确保晶圆完美无瑕。晶圆切割完成后,会被放入封装中,并进行连接——这构成了DRAM芯片的基础。
DRAM 制造的一个重要方面是晶圆制造分辨率,以纳米为单位。与 CPU 的演进一样,DRAM 技术也遵循着稳步的微型化路径,以实现更高的密度和更强大的性能。DRAM的性能与光刻制造工艺中的关键技术进步息息相关,这些进步就像 DRAM 演进时间线上的感叹号。下表列出了国际半导体技术路线图 (ITRS) 设定的 DRAM 制造工艺目标。
ITRS 的 DRAM 目标
来源:半导体行业协会
ITRS 解释说:“DRAM 半节距与连接 DRAM 位单元的金属线之间的宽度和间距相关——半节距越小,在给定面积内可容纳的 DRAM 位单元就越多……栅极长度越短,切换时间越短。”
更小的电路可以使信号传输速度更快,同时功耗更低。技术发展的每个阶段都会带来效率的提升、容量的增大以及性能特征的全面提升:这与 CPU 技术的演进非常相似。
随着行业向 45 纳米制造工艺、450 毫米晶圆、45 微米晶圆厚度和 3D 芯片封装技术迈进,预计 2010 年和 2011 年将发生重大变革。当技术经历范式转变时,企业会提出许多担忧,主要涉及设备更换或改造的成本,以及某些材料在 45 纳米或更小节点的电气性能下降。
随着行业向 45 纳米及以下节点迈进,电阻和电子散射效应将会增加。随着电路尺寸的减小,电流会因为较小铜线中的较大电阻而变得更加困难,从而增加了缺陷的风险或损害了信号完整性。
ITRS 的 DRAM 目标
来源: ITPC 小组成员 Tsujimura 博士的讲座
除了提高“互连”线路的导电性外,另一种提高电流的有趣方法是降低晶体管的电阻特性。制造商利用先进的材料科学来改进基本的芯片电路。2007年,东芝通过使用不同的材料配置并在晶体管电极和硅基板之间的边界表面分散掺杂剂,使晶体管的电流提高了35%。
目前也有一些关于使用碳纳米管 (CNT) 作为基本电路和互连线的基础研究。它被认为是在2020年之前维持摩尔定律的有希望的元素之一。据Philip G. Collins和Phaedon Avouris在《科学美国人》(2000年12月刊)上报道,金属纳米管的电流密度可以比银和铜等金属高1000多倍。
首先,使用碳纳米管在一些方面具有挑战性,其制造和电路布局工艺极其复杂。人们考虑过各种制造技术,包括印刷法和生长法,但这两种技术都还非常年轻,尚未直接应用于半导体的大规模生产。2007年 5 月,镁光科技宣布推出采用 78 纳米工艺的 DDR2 和 DDR3 DRAM 芯片。凭借这种新的制造工艺,镁光公司能够将数据频率提高到 1,066MHz,同时将工作电压保持在常见的 1.8V。这是一项重大成就,因为它使 DDR2 1,066MHz 内存能够在 JEDEC 认可的 DDR2 电压标准下工作,与所有主板高度兼容,这意味着它也完全符合 AMD 新款 AM2+ CPU 的要求,这些 CPU 以 1,066MHz DDR2 内存速度运行。
使用这些 IC 构建的模块也将具有更大的超频余地,正如镁光目前的“6F2” DDR3 芯片产品所见; “镁光公司采用 78 纳米工艺和 6F2 技术,实现了迄今为止分析过的所有 DRAM 器件中最小的单元尺寸,仅为 0.0365 微米……金属-绝缘体-金属 (MIM) 电容单元设计和凹槽通道存取晶体管,使字线和位线间距均减小至 156 纳米。” 目前,所有低延迟 1,333MHz 和 1,600MHz 及以上频率的高性能 DDR3 均采用基于镁光D9 的内存芯片。
这正是核心爱好者和超频玩家寻找特定 DRAM 芯片并追踪特定内存品牌和型号的关键原因——此前,一些 DDR1 模块上使用的华邦 BH-5 和三星 TCCD 芯片也曾出现过这种情况。值得注意的是,一些制造商(例如 Corsair 和 OCZ Technology)倾向于对这些额定频率较低的高质量 DRAM 进行预超频,以便通过提高工作电压来获得更快的时钟速度。
原文在 https://www.bit-tech.net/reviews/tech/memory/the_secrets_of_pc_memory_part_2/4/
这篇介绍了 DRAM 的封装,对于BIOS工程师来说,稍微了解一下即可
当行业从 SDR 转向 DDR 后,内存芯片的封装设计发生了显著变化。一些常见的 DRAM 封装包括薄型小外形封装 (TSOP)、芯片级封装 (CSP)、薄型四方扁平封装 (LQFP) 和球栅阵列 (BGA)。DDR中最常用的封装包括 BGA 或 TSOP 的变体,其中 TSOP 在 DDR1 中更常用,而当前的 DDR2 和 DDR3 标准则采用 FBGA,不同 DRAM 制造商之间的封装差异很小。
不同尺寸的 FBGA 样品
来源: Spansion Memory
FBGA 的主要优势包括高密度、更好的散热性能以及更短的连接器,从而减少信号失真。需要注意,这里提到的 FBGA 与Fortified BGA (强化BGA)不是同一种东西,后者是一种加强 BGA强度的技术。
LAB-Z 注释:先说一下FBGA。来自【参考1】
BGA(球栅阵列)是一种将球形焊料(焊球)以网格图案排列在封装底面上的封装。
间距有1.27mm、1.0mm、0.8mm、0.75mm、0.65mm、0.5mm、0.4mm等。
BGA前添加英文字母,会改变“封装安装高度”和“引脚间距”等。例如“ L ”表示封装安装高度L为“ 1.20 mm < 高度L ≦ 1.70 mm ”。因此,BGA前面带“L”的“LBGA”是封装安装高度为“1.20mm<高度L≦1.70mm”的SOP。
与QFP(四方扁平封装)相比,BGA具有以下优点和缺点。
优点
缺点
FBGA
BGA前面的“ F ”代表“细间距”。
通过添加“ F ”,引脚间距缩短如下。
关于引脚间距
因此,“FBGA(Fine-pitch BGA)”是指引脚间距为0.8mm或更小的BGA 。
总结:FBGA是 BGA 封装的一种,比普通的 BGA 底部引脚间距更小。
这里再介绍一下强化BGA(Fortified BGA)
BGA-PCB焊点应力
如果芯片底部的连接器较短,且呈网格状排列,则在热胀冷缩循环过程中,会导致每个引脚或焊球之间的应力不均匀。当 DRAM 相对于所连接的印刷电路板 (PCB) 升温且膨胀速率不同时,就会发生这种情况。PCB和 DRAM 芯片之间的不同膨胀速率会对焊球施加应力,并可能导致连接断裂 – 这就是为什么用户不应将 DRAM 电压升至高于规格的原因之一,除非采取了额外的措施来保持内存冷却。更高的电压会产生更多的热量,并以更快的速度降低内部 DRAM 电路的性能。OCZ(FlexXLC、Reaper、ReaperX)和 Corsair(DHX、Dominator)的某些内存模块采用先进的散热技术,可减少热循环过程中 DRAM 和 PCB 之间的差异。
OCZ 和 Corsair 验证其 PCB 散热技术有效性的方法是,让模块长时间承受极端温度循环,然后用 X 射线检查 DRAM 球连接处是否有断裂。这是一个极其耗时的过程,也是超频玩家需要牢记的要点之一。
OCZ 和 Corsair 为 FBGA DRAM 提供专用 PCB 冷却 资料
来源: OCZ Technology 和 Corsair Memory
如前所述,BGA技术的另一种变体被称为强化BGA。顾名思义,它通过强化连接点来提高DRAM在热循环(从热到冷,反之亦然)中的可靠性。DRAM芯片底部的焊球直径与焊点在温度循环过程中的可靠性成正比。直径越大,可靠性越高——这也是某些内存模块能够耐受更高温度的原因之一。
内存模块制造商经常使用 X 射线技术检查从生产批次中抽取的样品,以查找异常焊点。BGA连接不易从各个角度接触,因此需要使用 X 射线、在线测试和专用显微镜等先进的成像技术。X射线图像可以从各个角度拍摄,以验证 DRAM 芯片与底层 PCB 之间的连接是否正确。成像解决方案的最新进展可以与符合 RoHS(《限制在电气和电子设备中使用某些有害物质指令》)规定的无铅焊膏配合使用,同时提供计算机生成的3D细节。
BGA X射线检测
来源: STL Electronics
常见的BGA缺陷包括焊点桥接、焊点缺失、错位和焊点开路。质量控制流程通常通过光学识别软件实现自动化。机器会尝试检测诸如焊点桥接、过多空隙、焊点直径不规则或球形焊点等缺陷。所有制造商都使用上述X射线技术的变体。
BGA焊球 – 边缘颜色较深的圆环表示润湿性良好,亮点表示空洞。
数据来源: Phoenix/X-ray GmbH
焊点中的空隙或气泡被认为是有害的,但在制造过程中极难避免,通常将其控制在总连接量的一定百分比以下。空隙过大会导致焊点缺焊,并损害连接几何形状。BGA 焊点中过多的空隙会在热循环过程中削弱接触力,从而缩短内存模块的使用寿命。
其他手动检查流程包括使用功能验证、内部电路测试仪 (ICT) 和专用探针。有时,会使用“最高放大倍数斜视图”(OVHM) X 射线技术代替典型的倾斜法,从一定角度检查焊球,而不会降低放大倍数。
LAB-Z注释: SMT 厂商是否能够对产品进行X光检测可以作为考察SMT能力的标准。我接触过的一搏科技和一些厂商都有这样的能力。SMT 完成之后,直接用X光检查,能够确保SOC焊接的可靠性。
参考:
1. https://detail-infomation.com/package-types-bga/
关于 PCB/应力/芯片不良的问题,感受最深的恐怕就是微软了。
微软公司在2005年11月推出游戏主机Xbox360后,众多用户曾向微软方面投诉游戏主机经常出现不同程度的故障,而且几率偏高。有调查显示,早期版本Xbox360返修率高达68%,而2007年7月的报告指出故障几率还是有33%。
三个红色灯光形成一个环形,绰号为“三红”(Red Ring of Death)。
微软随后将游戏主机三红故障部分保修期由一年延长至三年,但其他故障问题仍维持在一年保修。微软声称将于新推出的版本主机改进制造工艺。【参考2】
在花费了十几亿美元之后,微软终于搞清楚产生的原因是GPU 芯片本身。简单的说是基片、封胶几个部件的膨胀系数都不一样,封胶在高温下还有软化的趋向。这样导致出厂之后不同材料之间存在着应力,相互拉扯。用户在使用过程中热胀冷缩加剧了这种现象,最终导致内部封装的焊料失效。更详细的解释可以在【参考3】看到。
2.https://zh.wikipedia.org/wiki/Xbox_360%E6%95%85%E9%9A%9C%E5%95%8F%E9%A1%8C
3.https://www.zhihu.com/question/340045804/answer/2638540514
CH554内置了128字节的 DataFlash ,掉电不会丢失,方便写入
这里展示了如何在 Ch55xDuino 环境下使用 DataFlash。
#ifndef USER_USB_RAM
#error "This example needs to be compiled with a USER USB setting"
#endif
#include "src/CdcHidCombo/USBCDC.h"
#include "DataFlash.H"
uint8_t lastValue;
void setup() {
USBInit();
// 读取
Flash_Op_Check_Byte1 = 0x00;
Flash_Op_Check_Byte2 = 0x00;
ReadDataFlash(0,1,&lastValue);
lastValue++;
// 写入
Flash_Op_Check_Byte1 = DEF_FLASH_OP_CHECK1;
Flash_Op_Check_Byte2 = DEF_FLASH_OP_CHECK2;
WriteDataFlash(0,&lastValue,1);
}
void loop() {
USBSerial_println(lastValue-1);
delay(3000);
}
实现的效果是:插入Ch554 ,打开 USB串口可以看到输出的数字。拔掉之后再次插入,输出的是前一次加一的数字。
做测试借了一个崭新的,没开封PCIE5 2T 的NVME, 品牌是 Fanxiang。发现无法在 PCIE 5 上写入数据(读取可以)。但是在 PCIE 4 Port上面可以读写。猜测这是一个“假的PCIE 5 NVME”。
很多年前还被 Foresee 这个牌子的 M.2 SSD 坑过,它不支持 DEVSLP ,然后我用它测试 ModernStandby 一直搞不定。
一个简单的 Web Serial 的例子,页面上有两个按钮,一个是选择串口号,另外一个是连接串口之后按下会发送”Hello World!” 到这个串口。
<!DOCTYPE html>
<html>
<body>
<button id="connectBtn">连接串口</button>
<button id="sendBtn" disabled>发送 hello world</button>
</body>
<script>
let port; // 存储串口实例
// 连接串口按钮事件
document.getElementById('connectBtn').addEventListener('click', async () => {
try {
port = await navigator.serial.requestPort();
await port.open({ baudRate: 9600 }); // 设置波特率
document.getElementById('sendBtn').disabled = false;
} catch (error) {
console.error("端口打开失败:", error);
}
});
// 发送按钮事件
document.getElementById('sendBtn').addEventListener('click', async () => {
if (!port?.writable) return;
const writer = port.writable.getWriter();
const encoder = new TextEncoder();
try {
await writer.write(encoder.encode('hello world!\r\n')); // 发送数据
} finally {
writer.releaseLock(); // 释放写入器
}
});
</script>
</html>
之前的文章【参考1】,介绍了 ASCII 的字体,这次用这个实现在 Console 下显示数值。使用 VS2019 VC 编译通过。
运行结果如下:
测试代码如下:
// AsciiFontTest.cpp : This file contains the 'main' function. Program execution begins and ends there.
//
#include <windows.h> // 必须包含头文件
#include <iostream>
// 字体宽度
#define DOHWIDTH 20
#define DOHHEIGHT 16
// 定义二维数组,每行预留足够空间
char DOH[10][DOHHEIGHT*10][DOHWIDTH+1] = {
// char "0"
{
" 000000000 ",
" 00:::::::::00 ",
" 00:::::::::::::00 ",
"0:::::::000:::::::0 ",
"0::::::0 0::::::0 ",
"0:::::0 0:::::0 ",
"0:::::0 0:::::0 ",
"0:::::0 000 0:::::0 ",
"0:::::0 000 0:::::0 ",
"0:::::0 0:::::0 ",
"0:::::0 0:::::0 ",
"0::::::0 0::::::0 ",
"0:::::::000:::::::0 ",
" 00:::::::::::::00 ",
" 00:::::::::00 ",
" 000000000 "
},
// char "1"
{
" 1111111 ",
" 1::::::1 ",
" 1:::::::1 ",
" 111:::::1 ",
" 1::::1 ",
" 1::::1 ",
" 1::::1 ",
" 1::::l ",
" 1::::l ",
" 1::::l ",
" 1::::l ",
" 1::::l ",
" 111::::::111 ",
" 1::::::::::1 ",
" 1::::::::::1 ",
" 111111111111 "
},
// char "2"
{
" 222222222222222 ",
"2:::::::::::::::22 ",
"2::::::222222:::::2 ",
"2222222 2:::::2 ",
" 2:::::2 ",
" 2:::::2 ",
" 2222::::2 ",
" 22222::::::22 ",
" 22::::::::222 ",
" 2:::::22222 ",
"2:::::2 ",
"2:::::2 ",
"2:::::2 222222",
"2::::::2222222:::::2",
"2::::::::::::::::::2",
"22222222222222222222",
},
// char "3"
{
" 333333333333333 ",
"3:::::::::::::::33 ",
"3::::::33333::::::3 ",
"3333333 3:::::3 ",
" 3:::::3 ",
" 3:::::3 ",
" 33333333:::::3 ",
" 3:::::::::::3 ",
" 33333333:::::3 ",
" 3:::::3 ",
" 3:::::3 ",
" 3:::::3 ",
"3333333 3:::::3 ",
"3::::::33333::::::3 ",
"3:::::::::::::::33 ",
" 333333333333333 ",
},
// char "4"
{
" 444444444 ",
" 4::::::::4 ",
" 4:::::::::4 ",
" 4::::44::::4 ",
" 4::::4 4::::4 ",
" 4::::4 4::::4 ",
" 4::::4 4::::4 ",
" 4::::444444::::444 ",
" 4::::::::::::::::4 ",
" 4444444444:::::444 ",
" 4::::4 ",
" 4::::4 ",
" 4::::4 ",
" 44::::::44 ",
" 4::::::::4 ",
" 4444444444 ",
},
// char "5"
{
" 555555555555555555 ",
" 5::::::::::::::::5 ",
" 5::::::::::::::::5 ",
" 5:::::555555555555 ",
" 5:::::5 ",
" 5:::::5 ",
" 5:::::5555555555 ",
" 5:::::::::::::::5 ",
" 555555555555:::::5 ",
" 5:::::5",
" 5:::::5",
" 5555555 5:::::5",
" 5::::::55555::::::5",
" 55:::::::::::::55 ",
" 55:::::::::55 ",
" 555555555 ",
},
// char "6"
{
" 66666666 ",
" 6::::::6 ",
" 6::::::6 ",
" 6::::::6 ",
" 6::::::6 ",
" 6::::::6 ",
" 6::::::6 ",
" 6::::::::66666 ",
" 6::::::::::::::66 ",
" 6::::::66666:::::6 ",
" 6:::::6 6:::::6",
" 6:::::6 6:::::6",
" 6::::::66666::::::6",
" 66:::::::::::::66 ",
" 66:::::::::66 ",
" 666666666 ",
},
// char "7"
{
"77777777777777777777",
"7::::::::::::::::::7",
"7::::::::::::::::::7",
"777777777777:::::::7",
" 7::::::7 ",
" 7::::::7 ",
" 7::::::7 ",
" 7::::::7 ",
" 7::::::7 ",
" 7::::::7 ",
" 7::::::7 ",
" 7::::::7 ",
" 7::::::7 ",
" 7::::::7 ",
" 7::::::7 ",
"77777777 ",
},
// char "8"
{
" 888888888 ",
" 88:::::::::88 ",
" 88:::::::::::::88 ",
" 8::::::88888::::::8",
" 8:::::8 8:::::8",
" 8:::::8 8:::::8",
" 8:::::88888:::::8 ",
" 8:::::::::::::8 ",
" 8:::::88888:::::8 ",
" 8:::::8 8:::::8",
" 8:::::8 8:::::8",
" 8:::::8 8:::::8",
" 8::::::88888::::::8",
" 88:::::::::::::88 ",
" 88:::::::::88 ",
" 888888888 "
},
// char "9"
{
" 999999999 ",
" 99:::::::::99 ",
" 99:::::::::::::99 ",
" 9::::::99999::::::9",
" 9:::::9 9:::::9",
" 9:::::9 9:::::9",
" 9:::::99999::::::9",
" 99::::::::::::::9",
" 99999::::::::9 ",
" 9::::::9 ",
" 9::::::9 ",
" 9::::::9 ",
" 9::::::9 ",
" 9::::::9 ",
" 9::::::9 ",
" 99999999 "
}
};
// 计算 n 的位数
// 比如: 123 返回2, 1 返回0
int count_digits(int n) {
// 处理特殊情况:0的位数为1
if (n == 0) return 0; // [^1]
int count = 0;
n = abs(n); // 处理负数
while (n != 0) {
n /= 10; // 每次循环移除最后一位
count++;
}
return count-1;
}
/**
* @brief 获取整数指定位的数字
* @param v 目标整数(支持负数)
* @param index 位索引(0表示个位,1表示十位,依此类推)
* @return int 对应位的数字(0-9)
*
* @example
* GetValueOf(1234, 0) -> 4
* GetValueOf(-987, 2) -> 9
*/
int GetValueOf(int v, int index) {
if (index < 0) return 0; // 非法索引处理
v = abs(v); // 处理负数情况[^1]
long long divisor = 1; // 使用long long防止溢出
for (int i = 0; i < index; ++i) {
divisor *= 10;
if (divisor > INT_MAX) return 0; // 大索引保护机制[^2]
}
return (v / divisor) % 10;
}
char* Int2Ascii(int value) {
// 计算最终的宽度
int StrWitdh = (count_digits(value)+1) * DOHWIDTH + 1; // 最后加入回车换行字符
char *str =(char*) malloc(StrWitdh * DOHHEIGHT + 1); //末尾使用 \0 作为标记
if (str == NULL) {
return NULL;
} else memset(str, '*', StrWitdh * DOHHEIGHT + 1);
str[StrWitdh * DOHHEIGHT + 1 - 1] = NULL;
// 赋值换行
for (int i = 1; i < DOHHEIGHT+1; i++) {
str[i * StrWitdh - 1] = '\n';
}
// 从 Value 的最高位开始搬移
for (int i = 0; i<count_digits(value)+1; i++)
{
printf("GetValueOf[%d,%d]=%d\n", value, count_digits(value) - i, GetValueOf(value,count_digits(value)-i));
for (int j = 0; j < DOHHEIGHT; j++) {
memcpy(&str[j* StrWitdh+ DOHWIDTH*i], &DOH[GetValueOf(value, count_digits(value)-i)][j][0], DOHWIDTH);
//printf("copy DOH[%d][%d][0] > str[%d]\n", GetValueOf(value, count_digits(value) - i), j, j * StrWitdh + DOHWIDTH * i);
}
}
return str;
}
int main()
{
for (int i = 0; i < 100; i++) {
char* p = Int2Ascii(i);
printf("%s\r\n", p);
free(p);
Sleep(500);
}
}
参考:
继续之前的话题,这里来到了《内存的秘密 第二部分》
https://www.bit-tech.net/reviews/tech/memory/the_secrets_of_pc_memory_part_2/1
第一章 紧凑型 DDR
SO-DIMM(小外形双列直插式内存模块,small outline dual in-line memory module)是最常见的移动内存类型:其尺寸几乎是标准 DIMM 的一半,并且不同代之间的连接引脚数量也有所不同。它最常用于笔记本电脑、嵌入式系统、打印机和高端网络设备。除此之外紧凑型和移动型 DDR 还有其他外形尺寸,包括 Mini DIMM 和 Micro DIMM,其区别如下表所示。
引脚数量
尺寸
移动 DDR(例如采用 SO-DIMM 外形尺寸的 DDR)具有一些动态功率调节功能,可以降低自身功耗降尽可能的延长待机时间。这些功能包括部分阵列自刷新 (Partial Array Self-Refresh ,PASR)、温度补偿自刷新 (Temperature Compensated Self-Refresh,TCSR) 和深度省电 (Deep Power Down,DPD)。移动内存设计人员需要努力平衡功耗与性能关系。
个别 DRAM 制造商可能也拥有专有的节能技术,例如,Elpida Memory 就拥有一项名为超级自刷新 (Super Self-Refresh,SSR) 的技术,可将 DDR1 自刷新电流降低 95%。【注释:SSR 是一种新型电路技术,它与嵌入式纠错电路 (ECC) 协同工作,显著延长了内部刷新间隔。ECC 会在退出自刷新周期时检查并纠正数据。SSR 功能取代了传统 SDRAM 中常见的自刷新功能。SSR 利用片上温度传感器(通常称为自动温度补偿自刷新 (ATCSR)),自动调整自刷新时间,以补偿内部温度变化。这是 2005年的技术。】
为了理解移动内存功耗的本质,我们只需使用如下基本公式:
总功率=核心功率+IO功率
DDR GDDR和 移动DDR 内存功耗比较
需要注意的是:DIMM和SO-DIMM使用的DDR芯片通常都是相同的。此外,还有一种 LPDDR (Low Power Double Data Rate SDRAM)的内存芯片,这种和 DDR 芯片差异较大需要特别注意。
DRAM自刷新技术
即使在没有读取数据时,内存(RAM) 也需要持续进行刷新以保持电容器中的电荷。因为DRAM使用电容器来记录数据,其中的电荷会随着时间的推移逐渐泄漏。为了确保数据的可靠必须进行刷新操作,这个操作简单的说就是对于电容再次充电。
这是 RAM (易失性存储器)与非易失性存储器(例如,只读存储器 ROM 或闪存NAND 或 NOR)的主要区别。在笔记本电脑和手持设备等移动设备中,这种自刷新特性不断改进,努力降低功耗,从而延长设备的待机时间。
以下章节介绍了移动 DDR 使用的四种不同的节能方法。
当计算机处于休眠或睡眠模式时,在关闭自刷新功能之前,内存内容会被复制到硬盘驱动器,这样内存不消耗电力同时数据也得到了保存。
温度补偿自刷新 (TCSR)
温度补偿自刷新技术在各种内存上都有使用。当DRAM 进行刷新操作时,功耗会增加。此外执行刷新操作时无法进行读写操作。因此,从技术角度厂商都在想办法降低内存刷新的频率。
DDR1 和 DDR2只有一种自刷新模式,而 DDR3 则增强了这项技术,新增了两种温度敏感型自刷新模式。当 DDR3 温度低于 85˚C (185˚F) 时,刷新间隔设置为 7.8µs。如果工作温度介于 85˚C (185˚F) 和 95˚C (203˚F) 之间,则刷新间隔需要降至 3.9µs(速度提高一倍)。
该技术基于以下原理:DRAM 保留数据的时间与工作温度直接相关。较高的温度会导致 DRAM 因电荷泄漏而更快地丢失数据。因此,温度升高后需要增加刷新频率才能保证数据不丢失。
当温度较低时,自刷新周期可以更长,根据这个特性,当 DDR3 在 85˚C (185˚F) 以下运行时,降低刷新频率可以在功耗方面节省近 50%。对于台式机内存模块,因为通常有足够的的空间进行散热, DIMM 的温度很少会超过 60˚C (140˚F)。不同DRAM 制造商的刷新间隔可能略有不同 , 这取决于各个公司使用的电路和芯片制造技术,但这种差异也可能导致移动内存和设备 BIOS 之间的一些兼容性问题。一些制造商也可能根据他们放置或使用温度传感器的位置而略微不同地实现此功能;有些温度传感器位于片上或 DIMM 上,前者专门检测内存核心温度,而位于DIMM 上的传感器更多用于检测环境温度。
部分阵列自刷新 (Partial Array Self-Refresh ,PASR)
DRAM 芯片内部包含由列和行构成的内存条,类似于 Microsoft Excel 等电子表格程序中的工作表。PASR 技术预先编程了 DRAM 的某些自刷新行为,用于寻址这些列和行,以降低功耗。
例如:
为了降低自刷新期间的功耗,移动 DDR 可以选择性刷新某些bank中的数据而不刷新其他bank。
部分阵列自刷新示意图 来源:Elpida Memory
不同 的PASR 配置对应的不同电流消耗 来源:Micron Technology
当需要刷新的阵列数量较少时,自刷新电流会下降;但当温度升高时,电流消耗会自动增加,因为阵列需要更频繁地刷新。为此,PASR功能通常使用芯片内部嵌入的热传感器来确定其精确温度。
深度断电 (Deep Power Down , DPD)
在深度掉电模式下,内部电源关闭,所有刷新操作暂停。因此,进入深度掉电模式后数据将无法保留。
在正常运行情况下,单个活动存储体的功耗通常低于 80mA。进行刷新操作时,功耗约为正常运行时的三倍,但在深度掉电模式下,电流消耗降至约 10µA。
自刷新和深度断电模式概述 来源: Elpida Memory
时钟停止模式 (Clock Stop Mode)
此功能通过减少时钟路径上的转换来降低功耗。镁光科技称,主要有两种方法可以实现此目的:
所采用的方法取决于具体产品的要求。移动 DDR 内存可以在运行过程中更改时钟频率,但只有在满足所有时序和内存刷新要求的情况下才可以这样做。
根据 JEDEC 的规定,在以下条件下甚至可以完全停止时钟:
时钟停止模式的进入和退出 来源: JEDEC
最近在研究 KML文件(也是一种XML格式),忽然发现很多工具都是在线的,使用起来并不顺手,因此编写一个本地的 XML 格式化工具。能够帮助将KML变成容易阅读的格式。
使用方法:
XMLLocalFormatter 输入文件名
之后会生成一个“输入文件名_for.XML”的新文件。
代码如下:
#include <iostream>
#include <fstream>
#include <string>
#include "tinyxml2.h"
using namespace std;
using namespace tinyxml2;
int main(int argc, char* argv[]) {
if (argc != 2) {
cerr << "Usage: xml_formatter input_file" << endl;
return EXIT_FAILURE;
}
string inputFile = argv[1];
// Load the XML file using TinyXML2.
XMLDocument doc;
XMLError error = doc.LoadFile(inputFile.c_str());
if (error != XML_SUCCESS) {
cerr << "Error loading file: " << inputFile << ". Error code: " << error << "." << endl;
return EXIT_FAILURE;
}
// 查找最后一个 '.' 的位置以分离扩展名
size_t dotPosition = inputFile.find_last_of('.');
if (dotPosition == std::string::npos || dotPosition == 0) {
std::cerr << "Invalid file name format.\n";
return 1;
}
// 构造新的文件名
std::string baseName = inputFile.substr(0, dotPosition);
std::string extension = inputFile.substr(dotPosition);
std::string newFileName = baseName + "_for" + extension;
const char* output = newFileName.c_str();
// Use PrettyPrint option for formatting.
doc.SaveFile(output,false);
cout << "Formatted XML saved successfully to: " << newFileName << endl;
return EXIT_SUCCESS;
}
运行:
左边是格式化之前的,右边是格式化之后的
有需要的朋友可以试试。
可执行程序(建议自行编译):
源代码工程: