XG-50 の AD Converter には、外部電源(バッテリー[ch1]、ワイド電源[ch2]) を接続しています。
加えて、SoC 内部の Vref、温度センサーも有効化しています。
Channel | 入力 | Note |
---|---|---|
0 | VREFINT | SoC 内部基準電圧源 |
1 | バッテリー電圧 | アッテネータ経由1) |
2 | ワイド電源電圧2) | アッテネータ経由3) |
17 | 温度センサー | SoC 内蔵温度センサー |
AD Converter を有効化するため、make menuconfig で NuttX の構成を変更します。
config を保存し、build します。
ファームウェアの書き込みと動作 を参考に、ファームウェアを XG-50 に書き込みます。
書き込んだファームウェアを実行すると、下のように adc というコマンドが使用できるようになります。
NuttShell (NSH) nsh> help help usage: help [-v] [<cmd>] [ dirname false mkfatfs pwd time ? date free mkfifo reboot true basename dd help mkrd rm uname break df hexdump mh rmdir umount cat dmesg kill mount set unset cd echo ls mv sh usleep cp exec mb mw sleep xd cmp exit mkdir ps test Builtin Apps: adc <------- cu i2c sudoku nsh>
さっそく実行してみます。
nsh> adc adc_main: g_adcstate.count: 1 adc_main: Hardware initialized. Opening the ADC device: /dev/adc0 Sample: 1: channel: 0 value: 1498 2: channel: 1 value: 3483 3: channel: 2 value: 0 4: channel: 17 value: 936 nsh>
Channel 0, 1, 2, 17 の 4 つの値を取ることができました。
上記 AD 変換で取得した値は、アナログ電源(VDDA) を基準にした相対値4)となっています。
STM32L4 の Reference Manual にあるとおり、実際の電圧を求めるには計算を行う必要があります。
計算式は下記のとおりです。
$$ V_{CHANNEL_X} = \frac{V_{DDA}}{FULL\_SCALE} \times ADC_X\_DATA $$
(Reference Manual より)
ここで、VDDA は 3.3[V]、FULL_SCALE は 4095 なのでそれを当てはめて Channel 1 (バッテリー入力電圧) を計算すると、
$$ V_{CHANNEL1} = \frac{3.3}{4095} \times 3483 \times 1.1 = 3.087 [V] $$
となります。
STM32L4 の VREFINT を利用し、VDDA の電圧に依存しない計算方法です。
$$ V_{CHANNEL_X} = \frac{3.0 V \times VREFINT\_CAL \times ADC_X\_DATA}{VREFINT\_DATA \times FULL\_SCALE} $$
ここで、各変数は下記の通りです。
変数 | 内容 |
---|---|
VREFINT_CAL | VREFINT calibration value5) (0x1FFF75AA - 0x1FFF75AB) |
ADC_DATA | AD 変換結果 |
VREFINT_DATA | Channel 0 AD変換結果 |
FULL_SCALE | 4095 |
手元のチップでは、VREFINT_CAL の値は 0x0678 でした。
nsh> mh 0x1fff75aa 1fff75aa = 0x0678 nsh>
この値を上記式に当てはめて計算すると、
In [8]: ((3 * 0x0678 * 3483) / (1498 * 4095)) * 1.1 Out[8]: 3.1028589034463536
となります。
試しに電圧計で電源ピンのところで計測してみると 3.094 [V] でした。
どちらの方法を使用しても 0.3% 程度の誤差6)で計測できるようです。
CH17 の温度センサーの値から温度への変換式は下記のとおりです。
$$ Temperature\ (in \ ℃) = \frac{110\ ℃ - 30\ ℃}{TS\_CAL2 - TS\_CAL1} \times (TS\_DATA - TS\_CAL1) + 30\ ℃ $$
ここで、各変数は下記になります。
変数 | 内容 |
---|---|
TS_CAL1 | 温度センサーキャリブレーション値7) @30℃ (0x1FFF75A8 - 0x1FFF75A9) |
TS_CAL2 | 温度センサーキャリブレーション値8) @110℃ (0x1FFF75CA - 0x1FFF75CB) |
TS_DATA | 温度センサー ADC 出力値 |
TS_CAL1, TS_CAL2 ともに VDDA が 3.0V のときの値となっていますが、XG-50 の VDDA は 3.3V のため、
『VREFINT を利用した計算』 を用いて換算する必要があります。
\begin{align} Temperature\ (in\ ℃) = \frac{110\ ℃ - 30\ ℃}{TS\_CAL2 - TS\_CAL1} \times \left(\frac{VREFINT\_CAL \times TS\_DATA}{VREFINT\_DATA} - TS\_CAL1 \right) + 30\ ℃ \end{align}
手元のチップで TS_CAL1, TS_CAL2 を確認してみると、
nsh> mh 0x1fff75a8 1fff75a8 = 0x0410 nsh> mh 0x1fff75ca 1fff75ca = 0x051b
それぞれ 0x0410, 0x051b となっているため、それを用いて計算すると、
In [18]: VREFINT_CAL=0x0678 In [19]: TS_DATA=936 In [20]: TS_CAL1=0x410 In [21]: TS_CAL2=0x51b In [22]: VREFINT_DATA=1498 In [23]: ((110 - 30) / (TS_CAL2 - TS_CAL1)) * ((VREFINT_CAL * TS_DATA) / VREFINT_DATA - TS_CAL1) + 30 Out[23]: 28.419065620577733
28.4℃ となりました。
apps/examples/adc/adc_main.c
を参照してください。