博主无竞赛专业知识,仅分享心得体会,内容不免有疏漏。欢迎评论指正。

前言

为什么集创赛要报信诺达杯呢?因为看错了。画着版图导着管,突然就被赛事联系人抓走了

前段时间终于把信诺达杯的初赛方案交上去了。这个赛题是我和队友一起从无到有、摸爬滚打,才逐渐摸清的,简单说一点心得吧。

赛题分析

MT8880是具有MPU和IO接口的可以进行编码和译码的DTMF收发机芯片,通过数据总线和使能信号,在时钟沿对内部寄存器进行读写,可以实现功能模式的转换和数据收发的时序控制。

在发送模式中,通过数据总线写入发送寄存器中的数据,在经过数模转换器后产生对应的DTMF信号,内部行列计数器可以精准定时传输脉冲串信号。

在接收模式中,经过输入端差分放大器对输入的DTMF进行采样,经过滤波器和检测装置的解码,在迟滞电路中产生迟滞信号,输出信号中断,并在接收寄存器存储译码后的数字信号。

同时,通过对控制寄存器进行改写,可以实现在接收模式,单音发送模式,双音多频发送模式,突发模式,暂停模式等模式间进行切换。

这是一片数模混合芯片,需要测试的参数繁多,且功能测试复杂。因此,秉持“由易入难”的思想,先分析数字方面会比较简单。

比赛用的DIP-20封装引脚图

数字部分

数字部分要测的参数为:VOLO、VOHO、IOZ、IIZ、Data Bus(IOH和IOL)、EST(IOH和IOL)、nIRQ/CP(IOL)。我们先来解读一下。

参数 定义 测试条件 最小值 典型值 最大值 单位
VOLO 振荡电路工作时输出信号低电平时的输出电压 空载 0.1 V
VOHO 振荡电路工作时输出信号高电平时的输出电压 空载 VDD=5V 4.9
IOZ 即IRQ脚在高阻态,外加一定电压下,
向芯片内输入的电流
VOH=2.4V 1 10 uA
IIZ 是数据总线、CS、RS0、RW和Phi2数字信号输入引脚
在输入不同信号时漏电流
VIN=
VSS to VDD
IOH 数据总线拉电流[1] VOH=2.4V -1.4 -6.6 mA
IOL 数据总线灌电流[2] VOL=0.4V 2.0 4.0

既然参数的含义明确了,那就一步步来测试吧。

VOHO/VOLO

这两个参数是晶振相关的。有个很奇怪的问题:既然这两个引脚接晶振,那它们应该都是输入端,为何会有电压的测量?

实际上,这两个引脚是作为IO引脚的,既可以输入也可以输出。在手册里并未讲,但通过外接高电平之后测量芯片引脚的方法可以测试出,OSC1和OSC2永远是反相的。也就是说,OSC1输入高电平,OSC2必定输出低电平。 为什么要这么设计?个人猜测是因为晶振两个引脚交替输出高低电平,这样子接有助于增加稳定性吧。

因此,要想测量VOHO/VOLO,只需外部加压然后测量另一个就行。需要注意的是负载要求。

IOZ/IIZ

所有带Z的参数都是高阻态时测量得到的电流值。如何让芯片高阻态?很简单!我把电断了不就行了。

实际测量当然不能这样。不过换一种思路:只要芯片不工作,那它就是高阻。因此只需让使能端失效就行了,随后进行加压测流。同样需要注意负载要求。因为测量参数有IOZ和IIZ,还需要注意电流方向。

Databus IOH/IOL

这是有关数据引脚的,因此需要在芯片功能测试时一并进行测试。

EST/GT IOH/IOL

查询芯片Datasheet可知,EST/GT在接收到有效音频信号时会被拉高,其余时间拉低,因此也需要在芯片功能测试时一并进行测试。

模拟部分

模拟部分要测的参数有:IDD、Pc、VREF、VHOUT、VLOUT、dBp。继续解读。

参数 定义 测试条件 最小值 典型值 最大值 单位
IDD 芯片不实现任何功能时流进VDD脚的电流 7 11 mA
Pc 芯片不实现任何功能时芯片的功耗 57.8 mW
VREF VREF脚的输出电压 空载 VDD=5V 2.4 2.5 2.6 V
VHOUT 双音多频信号的高频组分在输出时的功率,
用来表示输出的音频信号强度
RL=10kΩ -6.1 -4.1 dBm
VLOUT 双音多频信号的低频组分在输出时的功率,
用来表示输出的音频信号强度
-8.1 -2.1
dBp 高频输出电压与低频输出电压之比 2 3 dB

IDD

上电之后直接测量电源管脚即可。

在后续讨论中,我们认为IDD应当是正常工作时的电流。因此也需要在功能测试的中途进行测量。

Pc

同上。简单的乘法运算。

Vref

Vref是芯片内部产生的基准电压:它应当保持为供电电压VDD的一半。芯片上电后测量即可。

VHOUT/VLOUT

芯片发送DTMF信号时,高/低频信号的电平值。需要在功能测试的中途进行测量。

dBp

同上。简单的乘法运算。

测试方案

现在,我们已经分析了所有的参数。如何把它们一点一点测出来?

永远记得:由易到难!毕竟你考试也不会直接去做压轴题。能稳定拿分的一定是最前面的题目。

MT8880外围电路图

这是外围电路图,但是有不少参数是需要接额外负载的。因此,我们需要使用继电器来选通对应的分路。在参数测试时外加负载,在功能测试时正常接入外围电路即可。

总线和nIRQ/CP的测试负载电路

测试顺序

我们应当按照什么测试顺序来?本着“从易到难、从少到多”的顺序,应当先测量最简单和相关引脚最少的参数。

很显然,上电后的直流静态功耗应当在一开始就测试。还有Vref,值是固定的,因此可以在芯片接入其他引脚之前测试。

随后,是晶振部分,因为晶振与模拟管脚和芯片功能测试有关。

然后,应当测量漏电流参数,因为只需要将芯片的nCS引脚置1使芯片失去使能即可。

较为简单的参数测完了。下面要进行一部分的功能相关测试了。

数字引脚的IIH/IIL较为好测,只需要加压测流即可。对于Databus的IOH/IOL,则需要调整芯片到接收模式后进行测量。这就会带来一个问题: 如果芯片未正确调整好,那么测量结果将会出现错误。 实际上确实错了

代码

有关ST3020系统的函数在此不做赘述。请自行搜索相关资料并学习。

连接性测试

我们为什么要进行连接性测试?

连接性测试,又称接触测试,英文可称Continuity Test或Open & Short Test,主要用来检验测试过程中所有的电学连接是否良好(包括Tester本身,Tester与Loadboard,DUT本身等等之间)(可见图1),是否有不该出现的短路/开路出现。

通常来说,只有数字管脚需要测试。我们为了保险,对数字和模拟都进行了连接性测试。

连接性测试的原理是基于内部的ESD保护二极管。进行加流测压后,因为反偏二极管的存在,引脚应当输出一个负电压值。如果电压值在-0.1~-1.9之间,我们认为芯片连上了测试仪器。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
SET_DPS_POS(0, V, 10, MA);                                              // 加0V电压,电流箝位10mA
PMU_CONDITIONS(FIMV, -0.1, MA, 2, V); // 加流测压,-0.1mA电流,电压箝位2V
if (!PMU_MEASURE("1-12", 20, "_CON", V, -0.1, -1.9)) // 测量1-12脚之间的连通性
BIN(1);
Delay(10); // 延迟10ms

if (!SHOW_RESULT("_CON", DVM_MEASURE(VRefPin, 2, V, 10), V, -0.1, -1.9))// 显示连通性测试结果
BIN(1);
if (!SHOW_RESULT("_CON", DVM_MEASURE(Crystal, 2, V, 10), V, -0.1, -1.9))// 显示连通性测试结果
BIN(1);
if (!SHOW_RESULT("_CON", DVM_MEASURE(INP, 2, V, 10), V, -0.1, -1.9)) // 显示连通性测试结果
BIN(1);
if (!SHOW_RESULT("_CON", DVM_MEASURE(INN, 2, V, 10), V, -0.1, -1.9)) // 显示连通性测试结果
BIN(1);
if (!SHOW_RESULT("_CON", DVM_MEASURE(TONE, 2, V, 10), V, -0.1, -1.9)) // 显示连通性测试结果
BIN(1);
if (!SHOW_RESULT("_CON", DVM_MEASURE(GSPin, 2, V, 10), V, -0.1, -1.9)) // 显示连通性测试结果
BIN(1);

IDD、Pc、Vref

这三个参数都可以在芯片上电后测试出。非常简单。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
SET_DPS_POS(5.25, V, 20, MA);  // 上电,加可接受的最大电压
SET_RELAY("xxx");
// IDD测试
if (!DPS_MEASURE(1, R20MA, 5, "IDD", MA, 11, No_LoLimit))
{ // 测量IDD是否小于11mA
BIN(2);
}
else
{ // 计算PC
Idd = DPS_MEASURE(1, R20MA, 5);
if (Idd * 5.25 > 0.0578)
{
BIN(2);
}
}

// Vref测试
double Vref;
SET_DPS_POS(5, V, 20, MA); // 5V电压
Vref = DVM_MEASURE(VRefPin, 3, V, 10); // Vref使用DVM1
if (Vref < 2.4 || Vref > 2.6)
BIN(2);

VOHO/VOLO

这两个管脚很有意思:给其中一个加电平,另外一个会输出反相的电平。也就是说这两个管脚也是IO的?可能这样子设计是为了提高稳定性吧。

1
2
3
4
5
6
7
SET_RELAY("xxx");
SET_VS1(5, V);
if (DVM_MEASURE(Crystal, 5, V, 10) > 0.1)
BIN(3);
SET_VS1(0, V); // 设为0V
if (DVM_MEASURE(Crystal, 5, V, 10) < 4.9)
BIN(3);

IIZ

为什么选择NRZ0的标准波形?因为这里不需要考虑严格时序,因此直接认为波形是理想的就行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
SET_PERIOD(1000);          // 时钟周期为1000ns
SET_TIMING(65, 125, 125); // 前沿65ns,后沿125ns,采250ns
SET_RELAY("xxx");

FORMAT(NRZ0, "xxx");
for (int i = 1; i < 10; i++)
{
PMU_CONDITIONS(FVMI, i * 0.5, V, 12, UA);
RUN_PATTERN(1, 1, 0, 0);
if (!PMU_MEASURE("xxx", 20, "_RWIIZ", UA, 10, No_LoLimit))
{
BIN(3);
break;
}
}

Databus IIZ

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// Databus IIZ
SET_PERIOD(1000); // 时钟周期为1000ns //FIXME: 后面改回
SET_TIMING(65, 125, 125); // 前沿65ns,后沿125ns,采250ns
FORMAT(NRZ0, "3-6,10,11"); // 非归零模式波形,即理想情况下波形


for (int i = 0; i < 11; i++)
{
PMU_CONDITIONS(FVMI, i * 0.5, V, 12, UA);
RUN_PATTERN(1, 1, 0, 0);
if (!PMU_MEASURE("3-6", 20, "_DIIZ", UA, 10, No_LoLimit))
{
BIN(3);
}
}

Databus IOH/IOL

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// DataBus IOH
f1 = 852, f2 = 1633;
for (int i = 0; i < num; i++)
{
wave1[i] = (a1 * sin(2 * 3.1415926 * i * 5e-7 * f1) + a2 * sin(2 * 3.1415926 * i * 5e-7 * f2) + dc) / 2 * 65535;
} // 此时相当于以500ns一次对双音多频信号进行采样
LOAD_AS_PATTERN(6, 1, wave1);
RUN_AS_PATTERN(6, 1, 50, 1); // 此时相当于以500ns改变一次输出电压的方式输出信号

if (!RUN_PATTERN(18, 1, 2, 200000))
BIN(5);
PMU_CONDITIONS(FVMI, 2.4, V, 20, MA);
if (!PMU_MEASURE("3-6", 20, "_DIOH", MA, No_UpLimit, -1.4))
{
BIN(5);
}

int num = 2048, a1 = 0.1, a2 = 0.126, dc = 0.226;
// Databus IOL
int f1 = 941, f2 = 1633;
WORD wave1[2048];
for (int i = 0; i < num; i++)
{
wave1[i] = (a1 * sin(2 * 3.1415926 * i * 5e-7 * f1) + a2 * sin(2 * 3.1415926 * i * 5e-7 * f2) + dc) / 2 * 65535;
} // 此时相当于以500ns一次对双音多频信号进行采样
LOAD_AS_PATTERN(6, 1, wave1);
RUN_AS_PATTERN(6, 1, 50, 1); // 此时相当于以500ns改变一次输出电压的方式输出信号

nIRQOL

测试异常。初步推断是因为未正确调整芯片到接收模式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// IRQOL
SET_RELAY("1-7,9,10");
PMU_CONDITIONS(FVMI, 0.4, V, 20, MA);
for (int k = 0; k < num; k++)
{
wave[k] = (a1 * sin(2 * 3.1415926 * k * 5e-7 * 697) + a2 * sin(2 * 3.1415926 * k * 5e-7 * 1209) + dc) / 2 * 65535;
} // 此时相当于以500ns一次对双音多频信号进行采样
LOAD_AS_PATTERN(6, 1, wave);
RUN_AS_PATTERN(6, 1, 50, 1); // 此时相当于以500ns改变一次输出电压的方式输出信号
RUN_PATTERN(3, 1, 2, 200000);

if (!PMU_MEASURE("7", 40, "_IRQOL", MA, No_UpLimit, 4))
{
BIN(4);
}

功能测试

功能测试全部失败。原因尚不明确。

2024.08.12 初步推测可能是时钟周期设置问题。无法验证。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
// Initial
SET_RELAY("3-7,9-10");
SET_PERIOD(250); // 时钟周期为250ns
// FIXME: 应当为125ns,因为两行图形文件代表一个完整时钟周期
SET_TIMING(65, 125, 125); // 前沿65ns,后沿125ns,采样点125ns
FORMAT(NRZ, "3,4,5,6,10,11"); // 设置Databus的前沿与后沿
FORMAT(NRZ0, "8"); // 时钟波形为理想波形


// TODO: 8192/4
// 接收信号
int num = 2048, a1 = 0.1, a2 = 0.126, dc = 0.226;
// Databus IOL
int f1 = 941, f2 = 1633;
WORD wave1[2048];
for (int i = 0; i < num; i++)
{
wave1[i] = (a1 * sin(2 * 3.1415926 * i * 5e-7 * f1) + a2 * sin(2 * 3.1415926 * i * 5e-7 * f2) + dc) / 2 * 65535;
} // 此时相当于以500ns一次对双音多频信号进行采样
LOAD_AS_PATTERN(6, 1, wave1);
RUN_AS_PATTERN(6, 1, 50, 1); // 此时相当于以500ns改变一次输出电压的方式输出信号

if (!RUN_PATTERN(17, 1, 2, 200000))
BIN(5);
PMU_CONDITIONS(FVMI, 0.4, V, 20, MA);
if (!PMU_MEASURE("3-6", 20, "_DIOL", MA, No_UpLimit, 2))
{
BIN(5);
}

// DataBus IOH
f1 = 852, f2 = 1633;
for (int i = 0; i < num; i++)
{
wave1[i] = (a1 * sin(2 * 3.1415926 * i * 5e-7 * f1) + a2 * sin(2 * 3.1415926 * i * 5e-7 * f2) + dc) / 2 * 65535;
} // 此时相当于以500ns一次对双音多频信号进行采样
LOAD_AS_PATTERN(6, 1, wave1);
RUN_AS_PATTERN(6, 1, 50, 1); // 此时相当于以500ns改变一次输出电压的方式输出信号

if (!RUN_PATTERN(18, 1, 2, 200000))
BIN(5);
PMU_CONDITIONS(FVMI, 2.4, V, 20, MA);
if (!PMU_MEASURE("3-6", 20, "_DIOH", MA, No_UpLimit, -1.4))
{
BIN(5);
}


// 接收模式下测试16种DTMF信号
SET_RELAY("1,2,7,9,10,12");
int low_frequncy[4] = {697, 770, 852, 941}, high_frequency[3] = {1209, 1336, 1477}; // 低频和高频
int d_num = 3;

for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 3; j++)
{
WORD wave[2048];
for (int k = 0; k < num; k++)
{
wave[k] = (a1 * sin(2 * 3.1415926 * k * 5e-7 * low_frequncy[i]) + a2 * sin(2 * 3.1415926 * k * 5e-7 * high_frequency[j]) + dc) / 2 * 65535;
} // 此时相当于以500ns一次对双音多频信号进行采样
LOAD_AS_PATTERN(6, 1, wave);
RUN_AS_PATTERN(6, 1, 50, 1); // 此时相当于以500ns改变一次输出电压的方式输出信号
if (!RUN_PATTERN(d_num, 1, 2, 200000))
BIN(5);
d_num += 1;
Delay(50); // 延迟50ms

// EST/GT IOH
PMU_CONDITIONS(FVMI, 4.6, V, 20, MA);
if (!PMU_MEASURE("2", 20, "_EST", MA, No_UpLimit, -0.5))
{
BIN(5);
}
}
}


d_num = 15;
for (int i = 0; i < 4; i++) // 单独接收ABCD
{
WORD wave[2048];
for (int k = 0; k < num; k++)
{
wave[k] = (a1 * sin(2 * 3.1415926 * k * 5e-7 * low_frequncy[i]) + a2 * sin(2 * 3.1415926 * k * 5e-7 * 1633) + dc) / 2 * 65535;
} // 此时相当于以500ns一次对双音多频信号进行采样
LOAD_AS_PATTERN(6, 1, wave);
RUN_AS_PATTERN(6, 1, 50, 1); // 此时相当于以500ns改变一次输出电压的方式输出信号

if (!RUN_PATTERN(d_num, 1, 2, 200000))
BIN(5);

Delay(50);
d_num += 1;
}

// 发送DTMF
// FIXME: 低频和高频的范围
// L: 0.277-0.442
// H :0.351-0.554

SET_RELAY("1-7,9");
int dd_num = 22;
int low_frecuency_lt[4] = {143, 158, 175, 193}, high_frecuency_lt[3] = {248, 274, 303};
double temp_tone[2048], temp_dft[2048];
RUN_PATTERN(19, 1, 2, 200000);
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 3; j++)
{
RUN_PATTERN(dd_num, 1, 2, 200000);
dd_num += 1;
MAT_DVM_MEASURE(TONE, 5, V, 20, 2048, 5000, temp_tone);
DFT(temp_tone, 2048, temp_dft);
if (temp_dft[low_frecuency_lt[i]] < 1.245 || temp_dft[low_frecuency_lt[i]] > 1.972)
{
BIN(6);
}
if (temp_dft[high_frecuency_lt[j]] < 1.567 || temp_dft[high_frecuency_lt[j]] > 2.483)
{
BIN(6);
}
}
}

for (int j = 0; j < 4; j++) // 单独发送ABCD
{
RUN_PATTERN(dd_num, 1, 2, 200000);
dd_num += 1;
MAT_DVM_MEASURE(TONE, 5, V, 20, 2048, 5000, temp_tone);
DFT(temp_tone, 2048, temp_dft);
if (temp_dft[low_frecuency_lt[j]] < 1.245 || temp_dft[low_frecuency_lt[j]] > 1.972)
{
BIN(6);
}
if (temp_dft[335] < 1.567 || temp_dft[335] > 2.483)
{
BIN(6);
}
}


// VHOUT/VLOUT/dBp
int vhout_num[4] = {22, 26, 30, 37};
double dbp[4] = {0};
// 1 0001 697 1209
RUN_PATTERN(vhout_num[0], 1, 2, 200000);
MAT_DVM_MEASURE(TONE, 5, V, 50, 2048, 5000, temp_tone);
DFT(temp_tone, 2048, temp_dft);
dbp[0] = 20 * log10(temp_dft[248] / temp_dft[143]);
Delay(50);

// 5 0101 770 1336
RUN_PATTERN(vhout_num[1], 1, 2, 200000);
MAT_DVM_MEASURE(TONE, 5, V, 50, 2048, 5000, temp_tone);
DFT(temp_tone, 2048, temp_dft);
dbp[1] = 20 * log10(temp_dft[274] / temp_dft[158]);
Delay(50);

// 9 1001 852 1477
RUN_PATTERN(vhout_num[2], 1, 2, 200000);
MAT_DVM_MEASURE(TONE, 5, V, 50, 2048, 5000, temp_tone);
DFT(temp_tone, 2048, temp_dft);
dbp[2] = 20 * log10(temp_dft[303] / temp_dft[175]);
Delay(50);

// D 1010 941 1633
RUN_PATTERN(vhout_num[3], 1, 2, 200000);
MAT_DVM_MEASURE(TONE, 5, V, 50, 2048, 5000, temp_tone);
DFT(temp_tone, 2048, temp_dft);
dbp[3] = 20 * log10(temp_dft[335] / temp_dft[193]);
Delay(50);

for (int i = 0; i < 4; i++)
{
if (dbp[i] > 3)
{
BIN(7);
}
}

  1. 拉电流是在输出高电平时,输出脚电压降到一定值时能够对外输出的电流

  2. 灌电流是在输出低电平时,输出脚电压升高到一定值时能够接受外部电路的电流。用来表征输出引脚对外部负载的驱动能力。