(1) RC回路の伝達関数を導出し、周波数特性を描画せよ
RC回路の出力電圧 $V_{out}(\omega)$ は入力 $V_{in}(\omega)$ に対して次式で表されます:
$$
H(j\omega) = \frac{V_{out}(\omega)}{V_{in}(\omega)} = \frac{1}{1 + j\omega RC}
$$
Pythonを使って次を行いなさい:
- $R = 1\text{k}\Omega, C = 100\text{pF}$ とする。
- 周波数 $f = 10^2$ Hz ~ $10^8$ Hz の範囲で、
ゲイン $|H(j\omega)|$ と位相 $\angle H(j\omega)$ を計算せよ。 - Matplotlibを用いてボード線図(ゲイン[dB], 位相[deg])を描画せよ。
- -3 dBとなるカットオフ周波数を求め、グラフ上にマーカーを描け。
(2) ステップ応答をシミュレーションせよ
入力電圧が単位ステップ(0から1に立ち上がる)としたときの微分方程式は次式です:
$$
RC \frac{dV_{out}(t)}{dt} + V_{out}(t) = V_{in}(t)
$$
- Pythonでこの微分方程式をシミュレーションし、$V_{out}(t)$ を数値的に解け。
(Scipyのsignal.ltiやsignal.stepを使ってもよい) - 応答波形を時間領域で描画せよ。
- $V_{out}(t)$ が 0.9 に到達する時間を数値的に求めよ。
(NumPyで条件を満たすインデックスを探索)
(3) 発展課題
- $R$ と $C$ を変数にし、複数の組み合わせで周波数特性とステップ応答を比較せよ。
- 実際のADC入力回路のように「有限インパルス応答」を持つことを確認せよ。
import numpy as np
import matplotlib.pyplot as plt
from scipy import signal
# ================== Parameters / パラメータ ==================
R = 1e3 # Resistance [ohm]
C = 100e-12 # Capacitance [F]
RC = R * C
fmin, fmax = 1e2, 1e8 # Frequency range [Hz]
f = np.logspace(np.log10(fmin), np.log10(fmax), 1000)
omega = 2 * np.pi * f
# ================== (1) Frequency Response / 周波数特性 ==================
H = 1 / (1 + 1j * omega * RC) # Transfer function
gain = 20 * np.log10(np.abs(H)) # Gain [dB]
phase = np.angle(H, deg=True) # Phase [deg]
# Cutoff frequency (-3 dB point)
fc = 1 / (2 * np.pi * RC)
wc = 2 * np.pi * fc
Hc = 1 / (1 + 1j * wc * RC)
gain_c = 20 * np.log10(np.abs(Hc))
phase_c = np.angle(Hc, deg=True)
plt.figure(figsize=(10,6))
plt.subplot(2,1,1)
plt.semilogx(f, gain, 'b', label="Gain [dB]")
plt.axvline(fc, color='r', linestyle='--')
plt.plot(fc, gain_c, 'ro', label="-3 dB point")
plt.title("RC Low-Pass Filter Frequency Response")
plt.ylabel("Gain [dB]")
plt.grid(True, which="both")
plt.legend()
plt.subplot(2,1,2)
plt.semilogx(f, phase, 'g', label="Phase [deg]")
plt.axvline(fc, color='r', linestyle='--')
plt.plot(fc, phase_c, 'ro')
plt.xlabel("Frequency [Hz]")
plt.ylabel("Phase [deg]")
plt.grid(True, which="both")
plt.legend()
plt.tight_layout()
plt.show()
print(f"Cutoff frequency fc = {fc:.2e} Hz")
# ================== (2) Step Response / ステップ応答 ==================
# Transfer function H(s) = 1 / (RC s + 1)
num = [1]
den = [RC, 1]
system = signal.lti(num, den)
t = np.linspace(0, 5*RC, 1000)
t, y = signal.step(system, T=t)
plt.figure(figsize=(8,4))
plt.plot(t, y, 'b', label="Step Response Vout(t)")
plt.axhline(0.9, color='r', linestyle='--', label="0.9 level")
plt.title("RC Circuit Step Response")
plt.xlabel("Time [s]")
plt.ylabel("Vout(t)")
plt.grid(True)
plt.legend()
plt.show()
# Find time when output reaches 0.9
idx = np.where(y >= 0.9)[0][0]
t_90 = t[idx]
print(f"Vout(t) reaches 0.9 at t = {t_90:.4e} s")
# ================== (3) Advanced: Compare different R, C ==================
R_list = [1e3, 5e3] # [ohm]
C_list = [100e-12, 1e-9] # [F]
plt.figure(figsize=(10,6))
for R in R_list:
for C in C_list:
RC = R*C
H = 1 / (1 + 1j*omega*RC)
gain = 20*np.log10(np.abs(H))
plt.semilogx(f, gain, label=f"R={R:.0e}Ω, C={C:.0e}F")
plt.title("Frequency Response with Different R,C")
plt.xlabel("Frequency [Hz]")
plt.ylabel("Gain [dB]")
plt.grid(True, which="both")
plt.legend()
plt.show()
plt.figure(figsize=(10,6))
for R in R_list:
for C in C_list:
RC = R*C
num = [1]
den = [RC, 1]
system = signal.lti(num, den)
t = np.linspace(0, 5*RC, 1000)
t, y = signal.step(system, T=t)
plt.plot(t, y, label=f"R={R:.0e}Ω, C={C:.0e}F")
plt.title("Step Response with Different R,C")
plt.xlabel("Time [s]")
plt.ylabel("Vout(t)")
plt.grid(True)
plt.legend()
plt.show()
1. フーリエ級数による矩形波の生成
フーリエ級数の積和を用いて、振幅が [0V, 1V] となる矩形波を作成してください。
ヒント:奇数次高調波を足し合わせて矩形波を近似します。
2. for文を使わずに実装
課題1で作成した矩形波を、for文を使わずにベクトル演算(NumPy配列のブロードキャストやsum関数) で実装してください。
3. 一部が欠けた円の描画
半径1の円をプロットしてください。ただし、90°~180°の部分は欠けるようにしてください。
ヒント:極座標で角度 θ を制御して、該当区間をマスクして描画します。
4. PythonでADCを模擬的に作成
簡単なADCをPythonで作成してください。
- 入力レンジは
[-1V, 1V] - 分解能は 4bit (16レベル)
- (ライブラリに依存せず、自作で量子化処理を行う)
課題内容:
- 振幅が1Vの正弦波をADCに入力した場合、入力波形と量子化後の出力波形を同じグラフに表示してください。
- -1.5V~+1.5V のランプ波をADCに入力した場合も同様に、入力波形と出力を表示してください。
import numpy as np
import matplotlib.pyplot as plt
# ================== Parameters / パラメータ ==================
fs = 1000 # Sampling frequency [Hz]
T = 1 # Duration [s]
N = fs * T # Number of points
t = np.linspace(0, T, N, endpoint=False) # Time axis
harmonics = 25 # Number of harmonics for Fourier series
A_square = 0.5 # Square wave amplitude offset
Vpp = 1.0 # Peak-to-peak = 1V, range [0,1]
circle_radius = 1.0 # Circle radius
theta = np.linspace(0, 2*np.pi, 1000)
adc_bits = 4 # ADC resolution
adc_min = -1.0 # ADC input range minimum
adc_max = 1.0 # ADC input range maximum
# ================== 1. Fourier Series Square Wave ==================
n = np.arange(1, harmonics*2, 2)[:, None] # odd harmonics as column vector
terms = (1/n) * np.sin(2*np.pi*n*t) # broadcast to (harmonics, N)
square_wave = A_square + (2/np.pi) * np.sum(terms, axis=0) * (Vpp/2)
plt.figure(figsize=(10,4))
plt.plot(t, square_wave, label="Fourier Square Wave [0-1V]")
plt.title("Square Wave Approximation by Fourier Series")
plt.xlabel("Time [s]")
plt.ylabel("Amplitude [V]")
plt.grid(True)
plt.legend()
plt.show()
# ================== 2. Circle with missing sector (through origin) ==================
x = circle_radius * np.cos(theta)
y = circle_radius * np.sin(theta)
mask = ~((theta >= np.pi/2) & (theta <= np.pi)) # exclude 90°–180°
plt.figure(figsize=(5,5))
plt.plot(x[mask], y[mask], 'b') # 円弧
# 欠け部分を原点で結ぶ線を追加 / Add lines from origin
plt.plot([0, -circle_radius], [0, 0], 'r--') # negative x-axis
plt.plot([0, 0], [0, circle_radius], 'r--') # positive y-axis
plt.gca().set_aspect("equal", adjustable="box")
plt.title("Circle with Missing 90°–180° Sector (through origin)")
plt.xlabel("x")
plt.ylabel("y")
plt.grid(True)
plt.show()
# ================== 3. Simple ADC Function ==================
def adc_quantize(x, bits, vmin, vmax):
"""
自作ADC量子化処理 / Manual ADC quantization
"""
levels = 2**bits
step = (vmax - vmin) / levels
q = np.round((x - vmin)/step) * step + vmin
q = np.clip(q, vmin, vmax - step)
return q
# --- (a) Sine input ---
f_in = 5 # [Hz]
x_sin = np.sin(2*np.pi*f_in*t)
y_sin_q = adc_quantize(x_sin, adc_bits, adc_min, adc_max)
plt.figure(figsize=(10,4))
plt.plot(t, x_sin, label="Input Sine [V]")
plt.step(t, y_sin_q, where="mid", label="Quantized Output [V]")
plt.title("4-bit ADC with Sine Input")
plt.xlabel("Time [s]")
plt.ylabel("Amplitude [V]")
plt.grid(True)
plt.legend()
plt.show()
# --- (b) Ramp input ---
x_ramp = np.linspace(-1.5, 1.5, N)
y_ramp_q = adc_quantize(x_ramp, adc_bits, adc_min, adc_max)
plt.figure(figsize=(10,4))
plt.plot(t, x_ramp, label="Input Ramp [V]")
plt.step(t, y_ramp_q, where="mid", label="Quantized Output [V]")
plt.title("4-bit ADC with Ramp Input")
plt.xlabel("Time [s]")
plt.ylabel("Amplitude [V]")
plt.grid(True)
plt.legend()
plt.show()
% ================== Parameters / パラメータ ==================
fs = 1000; % Sampling frequency [Hz]
T = 1; % Duration [s]
N = fs * T; % Number of points
t = linspace(0, T, N); % Time axis
harmonics = 25; % Number of harmonics for Fourier series
A_square = 0.5; % Square wave amplitude offset
Vpp = 1.0; % Peak-to-peak = 1V, range [0,1]
circle_radius = 1.0; % Circle radius
theta = linspace(0, 2*pi, 1000);
adc_bits = 4; % ADC resolution
adc_min = -1.0; % ADC input range minimum
adc_max = 1.0; % ADC input range maximum
% ================== 1. Fourier Series Square Wave ==================
n = (1:2:(harmonics*2-1))'; % odd harmonics as column vector
terms = (1./n) * sin(2*pi*n*t); % broadcasting
square_wave = A_square + (2/pi) * sum(terms, 1) * (Vpp/2);
figure;
plot(t, square_wave);
title("Square Wave Approximation by Fourier Series");
xlabel("Time [s]"); ylabel("Amplitude [V]");
grid on;
% ================== 2. Circle with missing sector ==================
x = circle_radius * cos(theta);
y = circle_radius * sin(theta);
mask = (theta < pi/2) | (theta > pi); % exclude 90°–180°
figure;
plot(x(mask), y(mask), 'b'); hold on;
% 欠け部分を原点で結ぶ線
plot([0, -circle_radius], [0, 0], 'r--'); % negative x-axis
plot([0, 0], [0, circle_radius], 'r--'); % positive y-axis
axis equal;
title("Circle with Missing 90°–180° Sector (through origin)");
xlabel("x"); ylabel("y");
grid on;
% ================== 3. Simple ADC Function ==================
function q = adc_quantize(x, bits, vmin, vmax)
levels = 2^bits;
step = (vmax - vmin) / levels;
q = round((x - vmin)/step) * step + vmin;
q(q > vmax - step) = vmax - step; % clipping
q(q < vmin) = vmin;
end
% --- (a) Sine input ---
f_in = 5; % [Hz]
x_sin = sin(2*pi*f_in*t);
y_sin_q = adc_quantize(x_sin, adc_bits, adc_min, adc_max);
figure;
plot(t, x_sin, 'b'); hold on;
stairs(t, y_sin_q, 'r');
title("4-bit ADC with Sine Input");
xlabel("Time [s]"); ylabel("Amplitude [V]");
grid on;
% --- (b) Ramp input ---
x_ramp = linspace(-1.5, 1.5, N);
y_ramp_q = adc_quantize(x_ramp, adc_bits, adc_min, adc_max);
figure;
plot(t, x_ramp, 'b'); hold on;
stairs(t, y_ramp_q, 'r');
title("4-bit ADC with Ramp Input");
xlabel("Time [s]"); ylabel("Amplitude [V]");
grid on;
(1) ΔΣ変調器のシミュレーション
-
Python(NumPy + Matplotlib)を用いて、1次の ΔΣ変調器を実装せよ。
- 入力信号: 正弦波
sin(2π f_in t) - サンプリング周波数
Fs = 2^14 - 入力振幅
1V - 入力周波数
f_in = 17 Hz
- 入力信号: 正弦波
-
変調器は以下の構造を持つ:
- 加算器 (入力 - 1ビットDACの出力)
- 積分器(離散時間積分)
- 量子化器(sign関数で ±1 に変換)
- 1ビットDAC(量子化結果をアナログ側に戻す)
(2) 出力波形の確認
- 入力正弦波と ΔΣ出力を時間波形として同じグラフに描け。
- ΔΣ出力は ±1 の矩形パルス系列になることを確認せよ。
(3) スペクトル解析
- ΔΣ出力を高速フーリエ変換(FFT)してパワースペクトルを描け。
- 信号帯域(低周波成分)に入力信号が存在することを確認し、
高周波に量子化雑音が押し出される(ノイズシェーピング)ことを示せ。
import numpy as np
import matplotlib.pyplot as plt
# ======================== Parameters / パラメータ ========================
Fs = 2**14 # Sampling frequency [Hz] / サンプリング周波数
f_in = 17 # Input sine frequency [Hz] / 入力信号周波数
A = 1.0 # Input amplitude [V] / 入力振幅
N = 8192 # Number of samples / サンプル数
# ======================== Input Signal / 入力信号 ========================
n = np.arange(N) # time index / 離散時間インデックス
x = A * np.sin(2*np.pi*f_in*n/Fs) # input sinewave / 入力サイン波
# ======================== Delta-Sigma Modulator / ΔΣ変調器 ========================
v_int = 0.0 # integrator state / 積分器の状態
y_out = np.zeros(N) # output sequence / 出力信号系列
dac_out = 0.0 # 1-bit DAC output / 1ビットDAC出力
for i in range(N):
e = x[i] - dac_out # adder: input - DAC / 加算器
v_int += e # integrator / 積分器
y_q = 1 if v_int >= 0 else -1 # quantizer: sign / 量子化器
y_out[i] = y_q # output assignment / 出力保存
dac_out = y_q # 1-bit DAC feedback / 1ビットDAC出力
# ======================== (2) Time-domain Waveform / 時間領域波形 ========================
plt.figure(figsize=(10,5))
plt.plot(n[:500]/Fs, x[:500], label="Input Sinewave")
plt.step(n[:500]/Fs, y_out[:500], where='post', label="ΔΣ Output (±1)")
plt.xlabel("Time [s]")
plt.ylabel("Amplitude")
plt.title("Time-Domain Waveform of Delta-Sigma Modulator")
plt.legend()
plt.grid(True)
plt.show()
# ======================== (3) Spectrum Analysis / スペクトル解析 ========================
Xf = np.fft.rfft(y_out * np.hanning(N)) # apply window / 窓関数を適用
f_axis = np.fft.rfftfreq(N, d=1/Fs) # frequency axis / 周波数軸
Pxx = 20*np.log10(np.abs(Xf)/np.max(np.abs(Xf))) # normalized dB / 正規化dB
plt.figure(figsize=(10,5))
plt.plot(f_axis, Pxx)
plt.xlim(0, 2000) # show baseband region / 基本帯域を表示
plt.ylim(-120, 5)
plt.xlabel("Frequency [Hz]")
plt.ylabel("Magnitude [dB]")
plt.title("Spectrum of ΔΣ Output (Noise Shaping Demonstration)")
plt.grid(True)
plt.show()
Pythonプログラミング問題
問題:SAR-ADCの二分探索アルゴリズムを実装せよ
背景
逐次比較型ADC(SAR-ADC)は、入力電圧を 二分探索(Binary Search) によって量子化する方式のADCである。
NビットのSAR-ADCは、上位ビットから順に比較を行い、入力値がDACの出力より大きいか小さいかを判定しながら、最終的にNビットのデジタルコードを決定する。
問題文
次の条件に従って、SAR-ADCのアルゴリズムをPythonで実装せよ。
- 入力アナログ電圧
Vinが与えられる。範囲はVmin = 0.0 [V]~Vmax = 1.0 [V]とする。 - 分解能は
N = 4ビットとし、量子化レベル数は2^N = 16。 - SAR-ADCの逐次比較アルゴリズムを模擬し、入力電圧を 4ビットのデジタルコード に変換せよ。
アルゴリズムは以下の手順に従うこと。
-
SARレジスタのMSBを1に設定する(例:
1000)。 -
内部DACの出力値と入力Vinを比較する。
- Vin >= Vdac ならビットを保持。
- Vin < Vdac ならビットを0に戻す。
-
次のビットを1に設定して比較する。
-
全ビットについて繰り返し、最終的なデジタルコードを求める。
import numpy as np
import matplotlib.pyplot as plt
# ======================
# SAR-ADC Function
# ======================
def sar_adc(Vin, N=4, Vmin=0.0, Vmax=1.0):
"""
SAR-ADC 二分探索アルゴリズム
Vin : 入力アナログ電圧
N : 分解能(ビット数)
Vmin : 入力範囲最小値
Vmax : 入力範囲最大値
"""
code = 0
steps = []
for i in range(N):
# 試しにこのビットを立てる
trial_code = code | (1 << (N - 1 - i))
Vdac = Vmin + (Vmax - Vmin) * trial_code / (2**N)
if Vin >= Vdac:
code = trial_code
decision = "keep"
else:
decision = "clear"
steps.append((format(trial_code, f"0{N}b"), Vdac, decision))
return code, steps
# ======================
# Example Run
# ======================
Vin = 0.7
N = 4
Vmin, Vmax = 0.0, 1.0
final_code, steps = sar_adc(Vin, N, Vmin, Vmax)
# 結果表示
print(f"Vin = {Vin:.2f} V")
print("SAR steps:")
for bincode, Vdac, decision in steps:
print(f"Trial {bincode} -> Vdac={Vdac:.4f} V → {decision}")
print(f"\nFinal code = {format(final_code, f'0{N}b')} (binary) = {final_code} (decimal)")
# ======================
# Visualization
# ======================
plt.figure(figsize=(8,5))
plt.axhline(Vin, color="g", linestyle="--", label=f"Vin = {Vin:.2f} V")
trial_indices = np.arange(1, N+1)
Vdacs = [V for _, V, _ in steps]
plt.step(trial_indices, Vdacs, where="mid", label="DAC trial value", color="b")
plt.plot(trial_indices, Vdacs, "o", color="b")
plt.xticks(trial_indices, [f"Step {i}" for i in trial_indices])
plt.title("SAR-ADC Binary Search Process")
plt.xlabel("Comparison Step")
plt.ylabel("Voltage [V]")
plt.legend()
plt.grid(True)
plt.show()
(1) RC回路の周波数特性とステップ応答
理論式
-
伝達関数:
$$
H(j\omega) = \frac{1}{1 + j\omega RC}
$$ -
ゲイン:
$$
|H(j\omega)| = \frac{1}{\sqrt{1 + (\omega RC)^2}}
$$ -
位相:
$$
\angle H(j\omega) = -\tan^{-1}(\omega RC)
$$ -
カットオフ周波数(−3 dB点):
$$
f_c = \frac{1}{2\pi R C}
$$ -
ステップ応答:
微分方程式$$
RC \frac{dV_{out}(t)}{dt} + V_{out}(t) = 1
$$初期条件 $V_{out}(0) = 0$ の解:
$$
V_{out}(t) = 1 - e^{-t/(RC)}
$$
課題
- $R = 1\text{k}\Omega, C = 100\text{pF}$ のとき、周波数範囲 $10^2$ Hz ~ $10^8$ Hz で $ |H(j\omega)|$ と $\angle H(j\omega)$ を計算し、ボード線図を描け。
- 上の回路のカットオフ周波数を理論式で求め、それをプロットにマークせよ。
- 単位ステップ入力 $u(t)$ を入力として、応答 $V_{out}(t)$ を数値的にシミュレーションし、理論的時定数 $\tau = R C$ と比較せよ。
(2) MOSFET の Id-Vds 特性(NMOS / PMOS)
素子レベルの理論式
-
デバイス定数:
$$
k = \mu C_{ox} \frac{W}{L}
$$NMOS 用 $k_n$、PMOS 用 $k_p$。
-
NMOS のドレイン電流式(チャネル長変調 無し/有り):
$$
I_{d,n} =
\begin{cases}
0, & V_{gs} < V_{thn} \
k_n\left[(V_{gs}-V_{thn})V_{ds} - \frac{V_{ds}^2}{2}\right], & 0 \le V_{ds} < V_{gs}-V_{thn} \quad (\text{線形領域}) \
\frac{k_n}{2}(V_{gs}-V_{thn})^2, & V_{ds} \ge V_{gs}-V_{thn} \quad (\text{飽和領域})
\end{cases}
$$チャネル長変調を含めると飽和領域で:
$$
I_{d,n} = \frac{k_n}{2}(V_{gs}-V_{thn})^2 (1 + \lambda_n V_{ds})
$$ -
PMOS の式は符号を反転し、ゲート‐対‐ソース電圧 $V_{sg}$、ソース‐ドレイン電圧 $V_{sd}$ を使う類似形式。
-
トランスコンダクタンス:
$$
g_{m,n} = \frac{\partial I_{d,n}}{\partial V_{gs}} = k_n (V_{gs} - V_{thn}) \quad (\text{飽和領域})
$$
課題
- NMOS および PMOS の $I_d - V_{ds}$ 特性を、複数の $V_{gs}$(または $V_{sg}$)でプロットせよ。
- $g_m$ を飽和領域で計算し、$V_{gs} - V_{thn}$ の関数としてプロットせよ。
(3) NMOSソース接地アンプ
理論式
-
入力信号:
$$
v_{in}(t) = 0.025 \sin(2\pi \cdot 1000 t)
$$ -
増幅率(小信号近似):
$$
A_v = - g_m R_D
$$ただし $g_m = k_n (V_{gs} - V_{thn})$, $R_D$ はドレイン抵抗。
-
出力電圧:
$$
v_{out}(t) = A_v \cdot v_{in}(t)
$$ただし電源電圧 $V_{DD}$ による制限(クリッピング)がある場合を考慮する。
課題
- パラメータ例として $k_n, V_{thn}, R_D$ を仮定せよ(例:$k_n = 200\ \mu A/V^2, V_{thn} = 0.7V, R_D = 1\text{ k}\Omega$)。
- Python で入力波形と出力波形を生成し、クリップを入れた結果をプロットせよ。
(4) NMOS差動アンプ
理論式
-
差動入力:
$$
v_{id}(t) = 0.01 \sin(2\pi \cdot 10^6 t)
$$ -
出力(理想差動増幅):
$$
v_{out}(t) = A_v \cdot v_{id}(t), \quad A_v = g_m R_D
$$ -
テール電流制約:
差動ペアに流せる電流 $I_{tail}$ が2 mA とすると、各トランジスタの $I_d$ は合計でこれを超えないようにする。
課題
- NMOS 差動対のパラメータ(例:$k_n, V_{thn}, R_D, I_{tail}$)を仮定せよ。
- Python で入力‐差動信号と出力信号をシミュレーションし、テール電流制約による飽和やクリッピングが起きる領域も観察せよ。
(5) CMOS NAND回路
理論式
-
真理値表: $Y = \overline{A \cdot B}$
-
プルアップ(PMOS 並列)、プルダウン(NMOS 直列)による構成。
-
遅延時間近似:
$$
t_{pd} = 0.69 R_{eq} C_L
$$ここで $R_{eq}$ は入力が変化したときの等価抵抗、$C_L$ は負荷容量。
課題
- 真理値表を Python で生成せよ。
- 複数の $C_L$(例:1 pF、10 pF、100 pF)を仮定し、遅延時間を計算・プロットせよ。
また、NOT回路(CMOSインバータ)をシグモイド関数で近似し,電源電圧 (V_{DD}),トランジスタ寸法 (W/L) を含めて表すと以下になる。
(1) 基本式(電圧入力 → 電圧出力)
Vout = VDD * (1 - 1 / (1 + exp(-k * (Vin - VM))))
(2) パラメータ定義
VDD : 電源電圧 例) 1.8 V, 3.3 V
VM : 反転点(スイッチング閾値) NMOS/PMOS比で決定
k : 遷移の鋭さを決める係数 k ∝ sqrt( μp * (W/L)p / (μn * (W/L)n) )
(3) MOS寸法との関係(スイッチング点近似)
μn * ((W/L)n / (W/L)p) * ((VM - Vtn) / (VDD - VM - |Vtp|))^2 = 1
μn, μp : 電子・正孔移動度
Vtn, Vtp : NMOS/PMOSのしきい値電圧
(4) 解釈
(W/L)n ↑ → VM ↓ (NMOS強 → 出力0側に寄る)
(W/L)p ↑ → VM ↑ (PMOS強 → 出力1側に寄る)
VDD ↑ → 出力スイング幅が拡大し、シグモイドの上下限が広がる
(6) CMOSインバータ(NOT回路)
理論式
-
NMOS と PMOS が補完的動作する回路。静的電流は通常ゼロ(入力が定常状態のとき)。
-
電流平衡条件で VTC を導出:NMOS の漏れ電流/飽和電流式と、PMOS の式を使って $I_{d,n}(V_{in}, V_{out}) = I_{d,p}(V_{in}, V_{out})$ と設定する。
-
簡略な転移特性例:
$$
V_{out} \approx \frac{k_p (V_{DD}-V_{in}-|V_{thp}|)^2}{k_n (V_{in}-V_{thn})^2 + k_p (V_{DD}-V_{in}-|V_{thp}|)^2} ; V_{DD}
$$ -
理想スイッチモデル(しきい値による出力の変化):
$$
V_{out} =
\begin{cases}
V_{DD}, & V_{in} < V_{th} \
0, & V_{in} > V_{th}
\end{cases}
$$
課題
- NMOS と PMOS のデバイス定数 $k_n, k_p$、しきい値 $V_{thn}, |V_{thp}|$ を仮定せよ(例:$k_n = 200\ \mu A/V^2, k_p = 100\ \mu A/V^2, V_{thn} = 0.7V, |V_{thp}| = 0.7V$)。
- 矩形波入力を与えたときの過渡応答も Python でシミュレーションせよ。
import numpy as np
import matplotlib.pyplot as plt
# ================== (2) MOSFET Id-Vds ==================
# Device parameters
kn = 200e-6 # NMOS [A/V^2]
kp = 100e-6 # PMOS [A/V^2]
Vthn = 0.7 # NMOS threshold [V]
Vthp = -0.7 # PMOS threshold [V]
lambda_n = 0.02 # Channel-length modulation (example)
lambda_p = 0.02
Vds = np.linspace(0, 5, 200)
Vgs_list = [1.0, 2.0, 3.0, 4.0]
def Id_nmos(Vgs, Vds):
Id = np.zeros_like(Vds)
for i, vds in enumerate(Vds):
if Vgs < Vthn:
Id[i] = 0
elif vds < (Vgs - Vthn):
Id[i] = kn*((Vgs-Vthn)*vds - 0.5*vds**2) # linear
else:
Id[i] = 0.5*kn*(Vgs-Vthn)**2*(1+lambda_n*vds) # saturation
return Id
plt.figure(figsize=(8,5))
for Vgs in Vgs_list:
plt.plot(Vds, Id_nmos(Vgs,Vds), label=f"Vgs={Vgs} V")
plt.xlabel("Vds [V]")
plt.ylabel("Id [A]")
plt.title("NMOS Id-Vds Characteristics")
plt.legend()
plt.grid(True)
plt.show()
# Transconductance in saturation
Vgs = np.linspace(Vthn, 3, 100)
gm = kn*(Vgs-Vthn)
plt.figure()
plt.plot(Vgs-Vthn, gm)
plt.xlabel("Vgs - Vthn [V]")
plt.ylabel("gm [A/V]")
plt.title("NMOS Transconductance in Saturation")
plt.grid(True)
plt.show()
# ================== (3) NMOS Common-Source Amplifier ==================
Vin_t = np.linspace(0, 2e-3, 1000) # time ~2 ms
vin = 0.025*np.sin(2*np.pi*1e3*Vin_t) # 1 kHz input
RD = 1e3
Vgs_bias = 2.0
gm_bias = kn*(Vgs_bias-Vthn)
Av = -gm_bias*RD
vout = Av*vin
VDD = 5.0
vout_clipped = np.clip(vout, -VDD, VDD)
plt.figure(figsize=(8,4))
plt.plot(Vin_t*1000, vin, label="Input Vin [V]")
plt.plot(Vin_t*1000, vout_clipped, label="Output Vout [V]")
plt.xlabel("Time [ms]")
plt.ylabel("Voltage [V]")
plt.title("NMOS Common-Source Amplifier (with clipping)")
plt.legend()
plt.grid(True)
plt.show()
# ================== (4) NMOS Differential Amplifier ==================
t = np.linspace(0, 2e-6, 2000)
vid = 0.01*np.sin(2*np.pi*1e6*t)
Itail = 2e-3
Id_single = Itail/2 + kn*(vid/2)*(1.0) # crude approx
Id_single = np.clip(Id_single, 0, Itail) # tail current constraint
vout_diff = (Id_single - Itail/2)*RD
plt.figure(figsize=(8,4))
plt.plot(t*1e6, vid, label="Differential Input [V]")
plt.plot(t*1e6, vout_diff, label="Output Vout [V]")
plt.xlabel("Time [us]")
plt.ylabel("Voltage [V]")
plt.title("NMOS Differential Amplifier with Tail Current Constraint")
plt.legend()
plt.grid(True)
plt.show()
# ================== (5) CMOS NAND Gate ==================
import itertools
def nand_gate(A,B):
return int(not(A and B))
print("CMOS NAND Truth Table")
print("A B | Y")
for A,B in itertools.product([0,1],[0,1]):
print(f"{A} {B} | {nand_gate(A,B)}")
CL_values = [1e-12, 10e-12, 100e-12]
Req = 1e3
tpd = [0.69*Req*CL for CL in CL_values]
plt.figure()
plt.loglog(CL_values, tpd, 'o-')
plt.xlabel("Load Capacitance CL [F]")
plt.ylabel("Propagation Delay [s]")
plt.title("CMOS NAND Propagation Delay vs Load Capacitance")
plt.grid(True, which="both")
plt.show()
# ================== (6) CMOS Inverter ==================
Vin = np.linspace(0,5,500)
VDD = 5.0
Vout = (kp*(VDD-Vin-Vthp)**2) / (kn*(Vin-Vthn)**2 + kp*(VDD-Vin-Vthp)**2) * VDD
Vout[Vin<Vthn] = VDD
Vout[Vin>VDD+Vthp] = 0
plt.figure()
plt.plot(Vin, Vout)
plt.xlabel("Vin [V]")
plt.ylabel("Vout [V]")
plt.title("CMOS Inverter Voltage Transfer Characteristic")
plt.grid(True)
plt.show()
# Transient response with square wave input
t = np.linspace(0, 2e-6, 2000)
vin_square = (np.sign(np.sin(2*np.pi*1e6*t))+1)/2*VDD
vout_square = VDD - vin_square # ideal inverter
plt.figure()
plt.plot(t*1e6, vin_square, label="Vin")
plt.plot(t*1e6, vout_square, label="Vout")
plt.xlabel("Time [us]")
plt.ylabel("Voltage [V]")
plt.title("CMOS Inverter Transient Response")
plt.legend()
plt.grid(True)
plt.show()
演習問題(定電流負荷差動アンプの伝達関数解析)
【目的】
定電流負荷差動増幅器の小信号動作を理解し、周波数応答(伝達関数)を理論式とシミュレーションの両面から検証する。
【問題文】
図の定電流負荷差動アンプについて、次の設問に答えよ。
(1) 小信号等価回路を描け。
入力:vid = v1 - v2
出力:vo = vout
(2) 小信号解析により、出力電圧 vo と入力差電圧 vid の関係式(伝達関数)を導け。
H(s) = Vout(s) / Vid(s)
(3) 次の式をもとに、低周波ゲイン Av0 と極(ポール)周波数 ωp を求めよ。
H(s) = -gmN * (roN || roP) / (1 + s * (roN || roP) * (Cgs + Cgd))
Av0 = -gmN * (roN || roP)
ωp = 1 / ((roN || roP) * (Cgs + Cgd))
(4) パラメータ gmN, roN, roP を以下の式で定義し、設計値から数値計算せよ。
gmN = μn * Cox * (W/L)n * (VGSN - VthN)
roN = 1 / (λn * ID)
roP = 1 / (λp * ID)
(5) 伝達関数を近似式で表し、シミュレーションのボード線図と比較せよ。
H(s) ≈ Av0 / (1 + s / ωp)
≈ (-gmN * (roN || roP)) / (1 + s * (roN || roP) * Ceff)
(Ceff ≈ Cgs + Cgd)
(6) 図の周波数特性(ゲイン特性)を読み取り、低周波ゲイン [dB] と
-3 dB 周波数 fp [Hz] を求め、理論値と比較せよ。
!pip install numpy matplotlib scipy
import numpy as np
import matplotlib.pyplot as plt
from scipy import signal
# ==============================
# パラメータ設定 / Parameters
# ==============================
mu_n = 300e-4 # 電子移動度 [m^2/Vs]
Cox = 3.45e-3 # 酸化膜容量 [F/m^2]
W_Ln = 100e-6 / 1e-6 # (W/L)n
VGSN = 1.2 # ゲート・ソース電圧 [V]
VthN = 0.6 # しきい値電圧 [V]
lam_n = 0.02 # λn [1/V]
lam_p = 0.03 # λp [1/V]
ID = 50e-6 # ドレイン電流 [A]
Cgs = 0.2e-12 # ゲート・ソース容量 [F]
Cgd = 0.1e-12 # ゲート・ドレイン容量 [F]
# ==============================
# 小信号パラメータ計算 / Parameter calculation
# ==============================
gmN = mu_n * Cox * W_Ln * (VGSN - VthN)
roN = 1 / (lam_n * ID)
roP = 1 / (lam_p * ID)
ro_eq = (roN * roP) / (roN + roP)
Ceff = Cgs + Cgd
Av0 = -gmN * ro_eq
wp = 1 / (ro_eq * Ceff)
fp = wp / (2 * np.pi)
print(f"gmN = {gmN:.3e} [S]")
print(f"ro_eq = {ro_eq:.3e} [Ω]")
print(f"Av0 = {Av0:.2f} (linear), {20*np.log10(abs(Av0)):.2f} [dB]")
print(f"fp = {fp/1e6:.2f} [MHz]")
# ==============================
# 伝達関数生成 / Transfer function
# ==============================
num = [Av0]
den = [1/(wp), 1]
sys = signal.TransferFunction(num, den)
# ==============================
# 周波数特性プロット / Frequency response
# ==============================
w = np.logspace(4, 9, 500) # 10 kHz〜1 GHz
w, mag, phase = signal.bode(sys, w)
plt.figure(figsize=(6,4))
plt.semilogx(w/(2*np.pi), mag)
plt.title("Differential Amplifier Frequency Response")
plt.xlabel("Frequency [Hz]")
plt.ylabel("Gain [dB]")
plt.grid(True, which="both", ls="--")
plt.show()
【出力項目】
- ( g_m ):トランスコンダクタンス [S]
- ( r_o ):等価出力抵抗 [Ω]
- ( A_{v0} ):低周波利得(線形・dB)
- ( f_p ):極(ポール)周波数 [Hz]
【結果の考察】
- 低周波域でゲイン ≈ ( 20\log_{10}|A_{v0}| )
- 約 ( f_p ) 以降、-20 dB/dec の傾きで減衰
- 理論式
[
H(s) = \frac{-g_m (r_{on} || r_{op})}{1 + s (r_{on} || r_{op})(C_{gs}+C_{gd})}
]
と一致する一次ローパス特性を確認できる。
自由演習:オペアンプの開ループ周波数特性とステップ応答解析
【目的】
CMOSオペアンプの開ループ周波数特性およびステップ応答(過渡応答)を理論とシミュレーションにより解析し,
設計パラメータ(トランジスタ寸法,バイアス電流,容量)との関係を理解する。
【課題】
(1) 以下のオペアンプ回路について,AC解析により開ループ利得特性 |Av(f)| [dB] と位相特性を求めよ。
- 低周波利得 Av0 [dB]
- 利得交点周波数 (unity gain frequency) fu [Hz]
- 位相余裕 (phase margin) [deg]
(2) 小信号等価回路を描き,伝達関数 H(s) = Vout(s) / Vin(s) を導出せよ。
H(s) = Av0 / (1 + s/ωp1)(1 + s/ωp2)
ここで ωp1, ωp2 は1段目および2段目の極(ポール)とする。
(3) 1段目差動増幅段と2段目増幅段のゲインをそれぞれ gm1·ro1,gm2·ro2 として,
直流利得 Av0 を次式で表せ。
Av0 ≈ (gm1·ro1) · (gm2·ro2)
(4) 周波数応答を近似的に次式で表せ。
|Av(f)| ≈ Av0 / √{ [1 + (f/fp1)^2] [1 + (f/fp2)^2] }
fp1 = 1 / (2π·R1·C1)
fp2 = 1 / (2π·R2·C2)
(5) 図(a)の検証回路を用い,開ループゲインおよび帯域を理論計算せよ。
f1 = 1 / (2π·R1·C1)
f2 = Av0 / (2π·R2·C2)
(条件:f1 < f2 < fu)
例:
Av0 = 60 dB (≈ 1000)
f1 = 1 / (2π·16 kΩ·10 μF) ≈ 1 Hz
f2 = Av0 / (2π·160 kΩ·1 mF) ≈ 10 Hz
(6) 過渡解析によりステップ応答(立上り時間・立下り時間・オーバーシュート)を求め,
開ループゲインおよびポール位置との関係を考察せよ。
(7) ステップ応答の理論式(一次系近似)を示せ。
vo(t) = Vfinal·(1 - e^{-t/τ})
τ = 1 / ωp1
(8) PMOS/NMOSのSPICEモデルパラメータ(Level=2)を用いてシミュレーションを実施し,
モデル式に基づく gm, ro の概算を示せ。
gm = 2·ID / (VGS - Vth)
ro = 1 / (λ·ID)
【補足】
.model pmos-X PMOS (Level=2 ... )
.model nmos-X NMOS (Level=2 ... )
上記パラメータを回路シミュレータ(例:LTspice, NGspice)に入力し,解析を行うこと。
# Program Name: opamp_openloop_response.py
# Creation Date: 20251021
# Overview: Open-loop frequency response and step response analysis of CMOS operational amplifier
# Usage: Run in Google Colab or Jupyter → observe Bode plot and transient response
!pip install numpy matplotlib scipy
import numpy as np
import matplotlib.pyplot as plt
from scipy import signal
# ======================================
# Parameters / パラメータ設定
# ======================================
# トランジスタ・回路パラメータ(例)
gm1 = 1e-3 # [S] 1段目トランスコンダクタンス
gm2 = 2e-3 # [S] 2段目トランスコンダクタンス
ro1 = 100e3 # [Ω] 1段目出力抵抗
ro2 = 200e3 # [Ω] 2段目出力抵抗
R1 = 16e3 # [Ω]
R2 = 160e3 # [Ω]
C1 = 10e-6 # [F]
C2 = 1e-3 # [F]
Vstep = 1.0 # ステップ入力 [V]
# ======================================
# Derived values / 派生値計算
# ======================================
Av0 = (gm1 * ro1) * (gm2 * ro2)
fp1 = 1 / (2 * np.pi * R1 * C1)
fp2 = Av0 / (2 * np.pi * R2 * C2)
wp1, wp2 = 2*np.pi*fp1, 2*np.pi*fp2
print("=== Open-Loop Parameters ===")
print(f"Av0 = {Av0:.2e} (linear) = {20*np.log10(Av0):.2f} dB")
print(f"fp1 = {fp1:.2f} Hz")
print(f"fp2 = {fp2:.2f} Hz")
# ======================================
# Transfer Function / 伝達関数
# ======================================
num = [Av0]
den = [1/(wp1*wp2), (1/wp1 + 1/wp2), 1]
sys = signal.TransferFunction(num, den)
# ======================================
# Frequency Response / 周波数応答解析
# ======================================
f = np.logspace(0, 8, 500) # 1 Hz ~ 100 MHz
w = 2 * np.pi * f
w, mag, phase = signal.bode(sys, w)
plt.figure(figsize=(7,4))
plt.semilogx(f, mag)
plt.title("Open-Loop Gain (Bode Magnitude)")
plt.xlabel("Frequency [Hz]")
plt.ylabel("Gain [dB]")
plt.grid(which='both')
plt.show()
plt.figure(figsize=(7,4))
plt.semilogx(f, phase)
plt.title("Open-Loop Phase Response")
plt.xlabel("Frequency [Hz]")
plt.ylabel("Phase [deg]")
plt.grid(which='both')
plt.show()
# ======================================
# Step Response / ステップ応答解析
# ======================================
t, y = signal.step(sys)
y = y * Vstep / np.max(y) * Vstep # 正規化(ステップ入力振幅を考慮)
plt.figure(figsize=(6,4))
plt.plot(t, y)
plt.title("Step Response (Transient)")
plt.xlabel("Time [s]")
plt.ylabel("Vout [V]")
plt.grid(True)
plt.show()
# ======================================
# Time constant and comparison / 時定数比較
# ======================================
tau = 1 / wp1
print("\n=== Step Response (First-Order Approximation) ===")
print(f"Time constant τ = {tau:.3e} [s]")
print("Expected vo(t) ≈ Vfinal * (1 - exp(-t/τ))")
【Python課題】AD/DA変換器の特性評価(ランプ波入力)
次の仕様に基づき、Pythonでプログラムを作成せよ。
⸻
(1) A/D変換器の評価プログラム
• A/D変換器は 8 ビット、基準電圧 Vref = 2.0 V とする。
• 1LSB の理論値を計算すること。
• 入力信号はランプ波(0 V → Vref まで直線的に増加)とする。
• 例えば、100 サンプルに分割して入力を生成すること。
• 各入力値に対する理論的デジタル出力を計算し、グラフにプロットせよ。
• 測定値が与えられた場合は理論値との差(誤差)を計算せよ。
⸻
(2) D/A変換器の評価プログラム
• D/A変換器は 8 ビット、基準電圧 Vref = 2.0 V とする。
• 1LSB の理論値を計算すること。
• 入力信号はランプ波に対応するデジタルコード列とする。
• 例: CODE = 0, 1, 2, …, 255
• 各コードに対する出力電圧(理論値)を計算し、グラフにプロットせよ。
• 測定値が与えられた場合は理論値との差(誤差)を計算せよ。
⸻
(3) 発展課題
• A/D → D/A を直列接続したシステムをシミュレーションする。
• 入力信号をランプ波(0 V → Vref)とした場合、最終的な出力波形を描画せよ。
• 理論値と比較して、システム全体の誤差を評価せよ。
import numpy as np
import matplotlib.pyplot as plt
# ===================== Parameters / パラメータ =====================
Vref = 2.0 # Reference voltage [V] / 基準電圧
Nbit = 8 # Number of bits / ビット数
Nsamples = 100 # Number of samples for ramp input / ランプ波の分割数
# ===================== 1 LSB Calculation / 1LSB計算 =====================
LSB = Vref / (2**Nbit) # 1LSB voltage value / 1LSBの電圧
print("1 LSB =", LSB, "V")
# ===================== (1) ADC Evaluation / A/D変換器評価 =====================
# Generate ramp input signal / ランプ波入力信号生成
Vin = np.linspace(0, Vref, Nsamples) # 0V → Vrefまで直線的に増加
# Ideal ADC output code / 理論的デジタル出力
ADC_code = np.floor(Vin / Vref * (2**Nbit - 1)).astype(int)
# Plot ADC transfer characteristic / ADC伝達特性の描画
plt.figure(figsize=(8,4))
plt.plot(Vin, ADC_code, "bo-", label="ADC Output Code")
plt.title("ADC Ramp Response")
plt.xlabel("Input Voltage [V]")
plt.ylabel("Digital Output Code")
plt.grid(True)
plt.legend()
plt.show()
# ===================== (2) DAC Evaluation / D/A変換器評価 =====================
# Generate digital codes / デジタルコード生成
DAC_code = np.arange(0, 2**Nbit)
# Ideal DAC output / 理論的出力電圧
Vout_DAC = DAC_code * LSB
# Plot DAC transfer characteristic / DAC伝達特性の描画
plt.figure(figsize=(8,4))
plt.plot(DAC_code, Vout_DAC, "r-", label="DAC Output Voltage")
plt.title("DAC Ramp Response")
plt.xlabel("Digital Input Code")
plt.ylabel("Output Voltage [V]")
plt.grid(True)
plt.legend()
plt.show()
# ===================== (3) Cascade ADC→DAC / ADC→DAC直列システム =====================
# ADC output codes fed into DAC / ADC出力コードをDACに入力
Vout_cascade = ADC_code * LSB
# Error evaluation / 誤差評価
error = Vin - Vout_cascade
# Plot comparison / 比較プロット
plt.figure(figsize=(10,5))
plt.plot(Vin, Vin, "k--", label="Ideal Input (Vin)")
plt.plot(Vin, Vout_cascade, "g.-", label="ADC→DAC Output")
plt.title("ADC→DAC Cascade Response")
plt.xlabel("Input Voltage [V]")
plt.ylabel("Voltage [V]")
plt.grid(True)
plt.legend()
plt.show()
# Plot error / 誤差プロット
plt.figure(figsize=(8,4))
plt.plot(Vin, error, "m.-", label="Error (Vin - Vout)")
plt.title("System Error (ADC→DAC)")
plt.xlabel("Input Voltage [V]")
plt.ylabel("Error [V]")
plt.grid(True)
plt.legend()
plt.show()
【技術解説:A/D・D/A変換器の1LSB理論式と評価】
- はじめに
アナログ・デジタル変換器(ADC)およびデジタル・アナログ変換器(DAC)は、連続信号と離散信号を相互変換する装置である。その性能指標の一つに1LSB(Least Significant Bit:最小有効ビット)電圧がある。本稿では、AD/DA変換器の1LSB理論式を導出し、実験値との関係を整理する。
- A/D変換器(ADC)の1LSB理論式
(1) 定義式
V_LSB = (2 × V_ref) / (2^n - 1)
ここで、
V_ref:基準電圧(Reference Voltage)
n:分解能(ビット数)
「2×Vref」は入力範囲が±Vref(差動入力)であることを意味する。範囲は −Vref ~ +Vref、全振幅2Vref。
(2) 数値例
V_ref = 1.23 [V], n = 8
V_LSB = (2 × 1.23) / 255 = 9.647 [mV]
1カウントあたりの入力電圧変化量は約9.65mV。
(3) 意味
・V_LSBはデジタル出力1カウントに対応する最小の入力電圧変化量。
・(2^n - 1)は255段階の間隔を表す(8ビットの場合)。
・ADCは入力振幅2Vrefを255分割する。
- D/A変換器(DAC)の1LSB理論式
(1) 定義式
V_LSB = V_ref / (2^n - 1)
(2) 出力電圧の一般式
V_out = V_ref × (1/2^1 × b1 + 1/2^2 × b2 + … + 1/2^n × bn)
ここで bi ∈ {0,1}
各ビットは2進加重抵抗分圧回路(R-2Rラダー等)により実現される。
(3) 数値例
V_ref = 2.0 [V], n = 8
V_LSB = 2.0 / 255 = 7.843 [mV]
出力電圧は0〜2Vの範囲を255等分。
- ADCとDACの比較表
| 項目 | A/D変換器 | D/A変換器 |
|---|---|---|
| 入力信号 | アナログ電圧 | デジタル値 |
| 出力信号 | デジタル値 | アナログ電圧 |
| 分解能 | 2^n 段階 | 2^n 段階 |
| 1LSB電圧 | (2V_ref)/(2^n - 1) | (V_ref)/(2^n - 1) |
| 入力・出力範囲 | ±Vref | 0〜Vref |
| 数値例 (n=8, Vref=1.23V) | 9.647 mV | 7.843 mV (Vref=2V時) |
- 物理的意味と誤差要因
(1) 物理的意味
・ADCは±Vrefの差動入力を2Vrefで換算。
・DACは0〜Vrefの単極変換で基準電圧1倍。
(2) 主な誤差要因
・抵抗精度:R-2Rラダーの抵抗誤差
・オペアンプ誤差:オフセット電圧、バイアス電流
・基準電圧誤差:Vrefの温度ドリフトや電源ノイズ
・スイッチング遅延:サンプリング保持遅延
・ノイズ:外部電源や量子化ノイズの影響
- まとめ
理論的な1LSB電圧式:
V_LSB(ADC) = (2V_ref)/(2^n - 1)
V_LSB(DAC) = (V_ref)/(2^n - 1)
ADCは正負両振幅(±Vref)の差動入力を扱い、
DACは0〜Vrefの単極出力を扱うため、係数が異なる。
実験では、上式の理論値を基準に、実測値との差から
非直線性誤差、オフセット誤差などを評価する。