IPC-2581 예제와 읽는 코드
아래는 실제 문서의 발췌다. 이름이 뜻을 말하므로 형식을 몰라도 절반은 읽힌다.
1. 문서의 머리
섹션 제목: “1. 문서의 머리”<?xml version="1.0" encoding="UTF-8"?><IPC-2581 revision="C" xmlns="http://webstds.ipc.org/2581"> <Content roleRef="Owner"> <FunctionMode mode="ASSEMBLY"/> <StepRef name="test-3_r2.-17_4"/> <LayerRef name="TOP"/> <LayerRef name="BOTTOM"/> <LayerRef name="DRILL_1-6"/> <BomRef name="BOM_test-3_r2.-17_4"/> <DictionaryLineDesc units="MILLIMETER"/> </Content>| 요소 | 뜻 |
|---|---|
revision="C" | 표준의 판 |
FunctionMode | 이 문서의 용도 — 조립용인가 제조용인가 |
StepRef · LayerRef | 문서가 담은 스텝(step)과 층의 목록 |
BomRef | 어느 BOM 을 가리키는가 |
DictionaryLineDesc units | 사전과 그 단위 |
용도 표시를 먼저 본다. 조립용 문서에는 층 형상이 없을 수 있고, 제조용 문서에는 BOM 이 얇을 수 있다. 받은 문서가 무엇을 위한 것인지가 첫 줄에서 정해진다.
2. 설계 자료와 층 선언
섹션 제목: “2. 설계 자료와 층 선언” <Ecad name="Design"> <CadHeader units="INCH"/> <CadData> <Layer name="TOP" layerFunction="CONDUCTOR" side="TOP" polarity="POSITIVE"/> <Layer name="BOTTOM" layerFunction="CONDUCTOR" side="BOTTOM" polarity="POSITIVE"/> <Layer name="DRILL_1-6" layerFunction="DRILL" side="ALL" polarity="POSITIVE"/>| 속성 | 뜻 |
|---|---|
units | 이 문서의 좌표 단위 |
layerFunction | 그 층이 도체인가 · 드릴인가 · 마스크인가 |
side | 앞면 · 뒷면 · 전체 |
polarity | 양화 · 음화 |
층이 바뀌어 제조되는 사고가 여기서 사라진다. 파일 이름이 아니라 이 선언이 층을 정한다.
3. 스텝 — 외곽과 패키지
섹션 제목: “3. 스텝 — 외곽과 패키지” <Step name="board"> <Profile> <Polygon> <PolyBegin x="0" y="0"/> <PolyStepSegment x="100" y="0"/> <PolyStepSegment x="100" y="80"/> <PolyStepSegment x="0" y="80"/> <PolyStepSegment x="0" y="0"/> </Polygon> </Profile> <Package name="R_0402" type="OTHER"> <Outline> <Polygon> <PolyBegin x="-0.5" y="-0.3"/> <PolyStepSegment x="0.5" y="-0.3"/> <PolyStepSegment x="0.5" y="0.3"/> <PolyStepSegment x="-0.5" y="0.3"/> <PolyStepSegment x="-0.5" y="-0.3"/> </Polygon> </Outline> </Package> <Package name="HDR_1x02" type="OTHER"> <Pin number="1"> <Hole diameter="1.0"/> </Pin> </Package>| 요소 | 뜻 |
|---|---|
Profile | 보드 외곽(board outline) |
Polygon · PolyBegin · PolyStepSegment | 다각형 — 시작점과 이어지는 점 |
Package | 패키지 정의 — 부품마다 되풀이하지 않는다 |
Pin · Hole | 패키지의 단자와 홀 |
다각형은 시작점으로 돌아와 닫는다. 마지막 점이 시작점과 같은 것이 그것이다.
4. 부품
섹션 제목: “4. 부품” <Component refDes="R1" packageRef="R_0402" layerRef="TOP" part="10k" mountType="SMT"> <Location x="20" y="20"/> <Xform rotation="0"/> </Component> <Component refDes="C2" packageRef="C_0402" layerRef="BOTTOM" part="1nF" mountType="SMT"> <Location x="45" y="30"/> <Xform rotation="0" mirror="true"/> </Component>| 속성 | 뜻 |
|---|---|
refDes | 참조 지정자(reference designator) |
packageRef | 어느 패키지 정의를 쓰는가 |
layerRef | 어느 면에 있는가 |
mountType | 표면 실장인가 삽입인가 |
Location · Xform | 위치와 회전 · 뒷면은 뒤집힌다 |
5. BOM
섹션 제목: “5. BOM” <Bom name="bom"> <BomItem OEMDesignNumberRef="10k"> <RefDes name="R1"/> <RefDes name="R2"/> </BomItem> <BomItem OEMDesignNumberRef="10k"> <RefDes name="R3" populate="false"/> </BomItem> </Bom>populate="false" 가 실장 제외다. 별도 문서로 알리던 사실이 자료로 들어와 있다 — 조립 쪽이
사람의 메모를 읽지 않아도 된다.
6. 필요한 것만 골라 읽는 코드
섹션 제목: “6. 필요한 것만 골라 읽는 코드”문서 전체를 자료 구조로 올리면 큰 보드에서 메모리가 늘어난다. 필요한 요소만 골라 훑는 방식이 실무에서 쓸모 있다.
#include <string>#include <string_view>#include <unordered_map>#include <vector>
namespace codename7{ /// IPC-2581 문서에서 필요한 요소만 골라 받는 처리기. /// XML 파서는 SAX 계열(요소를 만나는 대로 알려 주는 방식)을 쓴다. class c_ipc2581_reader { public: struct s_layer { std::string name; std::string function; ///< CONDUCTOR · DRILL · SOLDERMASK … std::string side; ///< TOP · BOTTOM · ALL };
struct s_component { std::string refDes; std::string packageRef; std::string layerRef; double x = 0.0, y = 0.0, rotation = 0.0; bool mirror = false; };
/// 요소를 만날 때마다 호출된다. 관심 없는 요소는 그냥 지나간다. void OnElement(std::string_view aName, const std::unordered_map<std::string, std::string> &aAttr) { if (aName == "CadHeader") { m_units = attr(aAttr, "units"); } else if (aName == "Layer") { m_layers.push_back(makeLayer(aAttr)); } else if (aName == "Component") { m_components.push_back(makeComponent(aAttr)); } else if (aName == "Location") { setLocation(aAttr); } else if (aName == "Xform") { setXform(aAttr); } }
const std::string &Units() const { return m_units; } const std::vector<s_layer> &Layers() const { return m_layers; } const std::vector<s_component> &Components() const { return m_components; }
private: using t_attr = std::unordered_map<std::string, std::string>;
static std::string attr(const t_attr &a, const char *k) { const auto it = a.find(k); return it == a.end() ? std::string() : it->second; }
static double num(const t_attr &a, const char *k) { const std::string v = attr(a, k); return v.empty() ? 0.0 : std::stod(v); }
static s_layer makeLayer(const t_attr &a) { return {attr(a, "name"), attr(a, "layerFunction"), attr(a, "side")}; }
static s_component makeComponent(const t_attr &a) { s_component c; c.refDes = attr(a, "refDes"); c.packageRef = attr(a, "packageRef"); c.layerRef = attr(a, "layerRef"); return c; }
/// Location 과 Xform 은 바로 앞의 Component 에 붙는다. void setLocation(const t_attr &a) { if (m_components.empty()) return; m_components.back().x = num(a, "x"); m_components.back().y = num(a, "y"); }
void setXform(const t_attr &a) { if (m_components.empty()) return; m_components.back().rotation = num(a, "rotation"); m_components.back().mirror = (attr(a, "mirror") == "true"); }
std::string m_units; std::vector<s_layer> m_layers; std::vector<s_component> m_components; };}7. 낸 문서를 확인하는 순서
섹션 제목: “7. 낸 문서를 확인하는 순서”| 순서 | 하는 일 |
|---|---|
| 1 | 용도 표시와 단위를 확인한다 |
| 2 | 층 선언 수와 기능이 스택업(stackup)과 맞는지 본다 |
| 3 | 부품 수와 면 배분을 설계와 대조한다 |
| 4 | 넷 수를 대조한다 |
| 5 | 실장 제외 부품이 BOM 에 바르게 표시되었는지 본다 |
| 6 | 다시 읽어 보드로 복원해 화면에서 확인한다 |
6 번이 요령이다. 낸 문서를 그대로 읽어 원래 설계와 같은 그림이 나오면, 그 문서는 최소한 자기 자신과 일관된다.