====== AD Converter の利用 ====== XG-50 の AD Converter には、外部電源(バッテリー[ch1]、ワイド電源[ch2]) を接続しています。\\ 加えて、SoC 内部の Vref、温度センサーも有効化しています。 ^ Channel ^ 入力 ^ Note | | 0| VREFINT |SoC 内部基準電圧源| | 1| バッテリー電圧 |アッテネータ経由((10/11 にしているため、真の値を求めるには 1.1 倍する必要があります))| | 2| ワイド電源電圧((5〜36V)) |アッテネータ経由((1/12 にしているため、真の値を求めるには 12倍 する必要があります))| | 17| 温度センサー |SoC 内蔵温度センサー| \\ ===== 設定 ===== ==== NuttX Configuration ==== AD Converter を有効化するため、**make menuconfig** で NuttX の構成を変更します。 * Device Drivers -> Analog Device(ADC/DAC) Support 有効化 * Device Drivers -> Analog Device(ADC/DAC) Support -> Analog-to-Digital Conversion 有効化 {{:xg_series_devel:use_adc:adc_menuconfig_01.png?600|}} * Application Configuration -> Examples -> ADC example 有効化 * ADC device path -> **"/dev/adc0"** * Number of Samples per Group -> 4 * Use software trigger 有効化 {{:xg_series_devel:use_adc:adc_menuconfig_02.png?600|}} config を保存し、build します。 \\ ==== ファームウェアの書き込み ==== [[xg_series_devel:boot_firmware:start]] を参考に、ファームウェアを XG-50 に書き込みます。 \\ ===== 動作テスト ===== 書き込んだファームウェアを実行すると、下のように **adc** というコマンドが使用できるようになります。 NuttShell (NSH) nsh> help help usage: help [-v] [] [ 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) を基準にした相対値((12bit ADC なので、0〜4095))となっています。\\ 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] $$ となります。 \\ === VREFINT を利用した計算 === 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 value((メーカーにて出荷時にキャリブレーションした結果を不揮発領域に書き込んであります)) (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% 程度の誤差((電圧計が正しいかどうかは置いておいて))で計測できるようです。 \\ ==== 値の(温度への)変換 ==== CH17 の温度センサーの値から温度への変換式は下記のとおりです。 $$ Temperature\ (in \ ℃) = \frac{110\ ℃ - 30\ ℃}{TS\_CAL2 - TS\_CAL1} \times (TS\_DATA - TS\_CAL1) + 30\ ℃ $$ ここで、各変数は下記になります。 ^ 変数 ^ 内容 ^ | TS_CAL1 |温度センサーキャリブレーション値((VDDA 3.0V)) @30℃ (0x1FFF75A8 - 0x1FFF75A9) | | TS_CAL2 |温度センサーキャリブレーション値((VDDA 3.0V)) @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'' を参照してください。 \\