BBBのTSC_ADCモジュールの使い方

概要

block diagram

BeagleBone Blackに搭載されているAM335xのTSC_ADCを使用する方法について説明する。
BeagleboneBlackに搭載されているAM3358には8chのマルチプレクサを備えた12bit ADCが内蔵されている。
このADCはTSC_ADCと呼ばれており、ARMプロセッサーとは独立して動作させることができる。タッチスクリーンの読み込みを意識したつくりになっているが、汎用目的で使用することも可能。
詳細についてはTexas Instruments提供のAM335x and AMIC110 Sitara Processors Technical Reference Manualを参照の事。
kernel 4.19.94-ti-r42を前提とする。基本的にkernel依存は無いはずだ。

クロックはOCPクロックとADCクロックがあり、それぞれ100MHz, 24MHzとなっている。 データシートの5.10 Touch Screen Controller and Analog-to-Digital Subsystem Electrical Parametersによると、Acquisition time 2 clock cycle、Conversion time 13 clock cycleとなっているので、合計15 clock cycleで変換が可能という事になる。
ADCクロックが前述の通り24MHz入力となっているが、ADC clock frequencyは最大3MHzで、200kSPSと記載があるので、ADCクロックは1/8に分周しなければならない。(ADC_CLKDIV=7に設定する)
なんでオーバークロックが可能になっているかわからないが、TIのフォーラムのここら辺でも回答されている。
実際に動作テストをした限り、精度は落ちるがそれらしく動作はするようだ。

Clock SignalMax FreqReference / SourceComments
ocp_clk
OCP / Functional clock
100 MHz CORE_CLKOUTM4 / 2pd_pwkup_l4_wkup_gclk
From PRCM
adc_clk
ADC clock
24 MHz CLK_M_OSCpd_wkup_adc_fclk
From PRCM
functional block diagram

設定方法

ubootの設定

Am335xではアナログ入力は専用になっていてMUXでピン割り当てを設定する必要はない。
BBBの場合、ピンヘッダにAIN0-6(Channel 1-7)のアナログ入力ピンが出ている。(Ch8は内部で1/2Vccに41kΩの抵抗器で分圧されていて、ピンヘッダにはでていない。)
uboot_overlay_adcを有効にすると、sysfsからADCをコントロールする機能が有効となる(default)が、これを用いるとドライバがレジスタの更新を行ったり、IRQ割り込みのフックを行うため、問題が起きるのでDisableしておく必要がある。

disable_uboot_overlay_adc=1

TSC_ADC moduleのenable

TSC_ADC moduleはデフォルトでdisableになっているので、 CM_WKUP_ADC_TSC_CLKCTRL registerで有効に設定する必要がある。
この設定を行わないと、TSC_ADC関連レジスタへのアクセスもBus errorになる。
TIの実装例などを見ると、CM_WKUP_CLKSTCTRL = 0も設定しているようだが、私が試した限り必要なかった。

Offset
0x44E0_0400 +
Acronym
BChCM_WKUP_ADC_TSC_CLKCTRL
CM_WKUP_ADC_TSC_CLKCTRL Register
BitFieldTypeResetDescription
31-18ReservedR0h
17-16IDLESTR3h Module idle status.
0h (R) = Module is fully functional, including OCP
1h (R) = Module is performing transition: wakeup, or sleep, or sleep abortion
2h (R) = Module is in Idle mode (only OCP part). It is functional if using separate functional clock
3h (R) = Module is disabled and cannot be accessed
15-2ReservedR/W0h
1-0MODULEMODER/W0h Control the way mandatory clocks are managed.
0h (R/W) = Module is disable by SW. Any OCP access to module results in an error, except if resulting from a module wakeup (asynchronous wakeup).
1h (R/W) = Reserved
2h (R/W) = Module is explicitly enabled. Interface clock (if not used for functions) may be gated according to the clock domain state. Functional clocks are guarantied to stay present. As long as in this configuration, power domain sleep transition cannot happen.
3h (R) = Reserved

関連レジスタ

Offset
0x44E0_D000 +
Acronym
0hREVISION
10hSYSCONFIG
24hIRQSTATUS_RAW
28hIRQSTATUS
2ChIRQENABLE_SET
30hIRQENABLE_CLR
34hIRQWAKEUP
38hDMAENABLE_SET
3ChDMAENABLE_CLR
40hCTRL
44hADCSTAT
48hADCRANGE
4ChADC_CLKDIV
50hADC_MISC
54hSTEPENABLE
58hIDLECONFIG
5ChTS_CHARGE_STEPCONFIG
60hTS_CHARGE_DELAY
64hSTEPCONFIG1
68hSTEPDELAY1
6ChSTEPCONFIG2
70hSTEPDELAY2
74hSTEPCONFIG3
78hSTEPDELAY3
7ChSTEPCONFIG4
80hSTEPDELAY4
84hSTEPCONFIG5
88hSTEPDELAY5
8ChSTEPCONFIG6
90hSTEPDELAY6
94hSTEPCONFIG7
98hSTEPDELAY7
9ChSTEPCONFIG8
A0hSTEPDELAY8
A4hSTEPCONFIG9
A8hSTEPDELAY9
AChSTEPCONFIG10
B0hSTEPDELAY10
B4hSTEPCONFIG11
B8hSTEPDELAY11
BChSTEPCONFIG12
C0hSTEPDELAY12
C4hSTEPCONFIG13
C8hSTEPDELAY13
CChSTEPCONFIG14
D0hSTEPDELAY14
D4hSTEPCONFIG15
D8hSTEPDELAY15
DChSTEPCONFIG16
E0hSTEPDELAY16
E4hFIFO0COUNT
E8hFIFO0THRESHOLD
EChDMA0REQ
F0hFIFO1COUNT
F4hFIFO1THRESHOLD
F8hDMA1REQ
100hFIFO0DATA
200hFIFO1DATA

以下、汎用ADCとして用いる際に関連するレジスタについて説明する。

以下のレジスタは基本的に触る必要は無い。

OffsetAcronym
0hREVISION Register
10hSYSCONFIG Register
24hIRQSTATUS_RAW Register
34hIRQWAKEUP Register
5ChTS_CHARGE_STEPCONFIG Register
60hTS_CHARGE_DELAY Register
50hADC_MISC Register
IRQSTATUS Register
BitFieldTypeResetDescription
31-11ReservedR0h
10PEN_IRQ_synchronizedR/W0h Write 0 = No action.
Read 0 = No (enabled) event pending.
Read 1 = Event pending.
Write 1 = Clear (raw) event.
9Pen_Up_eventR/W0h
8Out_of_RangeR/W0h
7FIFO1_UnderflowR/W0h
6FIFO1_OverrunR/W0h
5FIFO1_ThresholdR/W0h
4FIFO0_UnderflowR/W0h
3FIFO0_OverrunR/W0h
2FIFO0_ThresholdR/W0h
1End_of_SequenceR/W0h
0HW_Pen_Event_asynchronousR/W0h
IRQENABLE_SET/IRQENABLE_CLR
BitFieldTypeResetDescription
31-11RESERVEDR/W0h
10HW_Pen_Event_synchronousR/W0h Write 0 = No action.
Read 0 = Interrupt disabled (masked).
Read 1 = Interrupt enabled.
Write 1 = Enable interrupt.(IRQENABLE_SETの場合)
Write 1 = Disable interrupt.(IRQENABLE_CLRの場合)
9Pen_Up_eventR/W0h
8Out_of_RangeR/W0h
7FIFO1_UnderflowR/W0h
6FIFO1_OverrunR/W0h
5FIFO1_ThresholdR/W0h
4FIFO0_UnderflowR/W0h
3FIFO0_OverrunR/W0h
2FIFO0_ThresholdR/W0h
1End_of_SequenceR/W0h
0HW_Pen_Event_asynchronousR/W0h
DMAENABLE_SET/DMAENABLE_CLR
BitFieldTypeResetDescription
31-2RESERVEDR/W0h
1Enable_1R/W0h Enable DMA request FIFO 1.
Write 0 = No action.
Read 0 = DMA line disabled.
Read 1 = DMA line enabled.
Write 1 = Enable DMA line.(DMAENABLE_SETの場合)
Write 1 = Disable DMA line.(DMAENABLE_CLRの場合)
0Enable_0R/W0h Enable DMA request FIFO 0.
Write 0 = No action.
Read 0 = DMA line disabled.
Read 1 = DMA line enabled.
Write 1 = Enable DMA line.(DMAENABLE_SETの場合)
Write 1 = Disable DMA line.(DMAENABLE_CLRの場合)
CTRL
BitFieldTypeResetDescription
31-10RESERVEDR/W0h
9HW_preemptR/W0h 0 = SW steps are not pre-empted by HW events.
1 = SW steps are pre-empted by HW events.
8HW_event_mappingR/W0h 0 = Map HW event to Pen touch irq (from AFE).
1 = Map HW event to HW event input.
7Touch_Screen_EnableR/W0h 0 = Touchscreen transistors disabled.
1 = Touchscreen transistors enabled.
6-5AFE_Pen_CtrlR/W0h These two bits are sent directly to the AFE Pen Ctrl inputs.
Bit 6 controls the Wiper touch (5 wire modes)Bit 5 controls the X+ touch (4 wire modes)User also needs to make sure the ground path is connected properly for pen interrupt to occur (using the StepConfig registers)Refer to section 4 interrupts for more information.
4Power_DownR/W0h ADC Power Down control.
0 = AFE is powered up (default).
1 = Write 1 to power down AFE (the tsc_adc_ss enable (bit 0) should also be set to off)
3ADC_Blas_selectR/W0h ADC Power Down control.
0 = AFE is powered up (default).
1 = Write 1 to power down AFE (the tsc_adc_ss enable (bit 0) should also be set to off)
2StepConfig_WriteProtect_n_active_lowR/W0h 0 = Step configuration registers are protected (not writable).
1 = Step configuration registers are not protected (writable).
1Step_ID_tagR/W0h Writing 1 to this bit will store the Step ID number with the captured ADC data in the FIFO.
0 = Write zeroes.
1 = Store the channel ID tag.
0EnableR/W0h TSC_ADC_SS module enable bit.
After programming all the steps and configuration registers, write a 1to this bit to turn on TSC_ADC_SS.
Writing a 0 will disable the module (after the current conversion).
ADCSTAT
BitFieldTypeResetDescription
31-8RESERVEDR/W0h
7PEN_IRQ1R0h PEN_IRQ[1] status
6PEN_IRQ0R0h PEN_IRQ[0] status
5FSM_BUSYR0h Status of OCP FSM and ADC FSM.
0 = Idle.
1 = Busy.
4-0STEP_IDR10h Encoded values:.
10000 = Idle.
10001 = Charge.
00000~01111 = Step ID
ADCRANGE
BitFieldTypeResetDescription
31-28RESERVEDR0h
27-16High_Range_DataR/W0h Sampled ADC data is compared to this value.
If the sampled data is greater than the value, then an interrupt is generated.
15-12RESERVEDR0h
11-0Low_Range_DataR/W0h Sampled ADC data is compared to this value.
If the sampled data is less than the value, then an interrupt is generated.
ADC_CLKDIV
BitFieldTypeResetDescription
31-16RESERVEDR0h
15-0ADC_ClkDivR/W0h The input ADC clock will be divided by this value and sent to the AFE.
Program to the value minus 1.
STEPENABLE
BitFieldTypeResetDescription
32-17RESERVEDR0h
16-1STEPxR/W0h Enable step x
0TS_ChargeR/W0h Enable TS Charge step
IDLECONFIG
BitFieldTypeResetDescription
31-26RESERVEDR0h
25CMPEQR/W0h Differential Control Pin.
0 = Single Ended, SEL_INM_SWC_3_0 must be 1xxx.
1 = Differential Pair Enable.
24-23CMPEQR/W0h SEL_RFM pins SW configuration.
00 = VSSA_ADC.
01 = XNUR.
10 = YNLR.
11 = VREFN.
22-19CMPEQR/W0h SEL_INP pins SW configuration.
0000 = Channel 1.
0111 = Channel 8.
1xxx = VREFN.
18-15CMPEQR/W0h SEL_INM pins for neg differential.
0000 = Channel 1.
0111 = Channel 8.
1xxx = ADCREFM, anytime DIFF_CNTRL = 0.
14-12CMPEQR/W0h SEL_RFP pins SW configuration.
000 = VDDA_ADC.
001 = XPUL.
010 = YPLL.
011 = VREFP.
1xx = Reserved.
11CMPEQR/W0h WPNSW pin SW configuration
10CMPEQR/W0h YPNSW pin SW configuration
9CMPEQR/W0h XNPSW pin SW configuration
8CMPEQR/W0h YNNSW pin SW configuration
7CMPEQR/W0h YPPSW pin SW configuration
6CMPEQR/W0h XNNSW pin SW configuration
5XPPSW_SWCR/W0h XPPSW pin SW configuration
4-0RESERVEDR/W0h
STEPCONFIGx
BitFieldTypeResetDescription
31-28RESERVEDR/W0h
27Range_checkR/W0h 0 = Disable out-of-range check.
1 = Compare ADC data with range check register.
26FIFO_selectR/W0h Sampled data will be stored in FIFO.
0 = FIFO 0.
1 = FIFO 1.
25Diff_CNTRLR/W0h Differential Control Pin.
0 = Single Ended, SEL_INM_SWC_3_0 must be 1xxx.
1 = Differential Pair Enable.
24-23SEL_RFM_SWC_1_0R/W0h SEL_RFM pins SW configuration.
00 = VSSA.
01 = XNUR.
10 = YNLR.
11 = VREFN.
22-19SEL_INP_SWC_3_0R/W0h SEL_INP pins SW configuration.
0000 = Channel 1.
0111 = Channel 8.
1xxx = VREFN.
18-15SEL_INM_SWC_3_0R/W0h SEL_INM pins for negative differential.
0000 = Channel 1.
0111 = Channel 8.
1xxx = ADCREFM, anytime DIFF_CNTRL = 0.
14-12SEL_RFP_SWC_2_0R/W0h SEL_RFP pins SW configuration.
000 = VDDA_ADC.
001 = XPUL.
010 = YPLL.
011 = VREFP.
1xx = Reserved.
11WPNSW_SWCR/W0h WPNSW pin SW configuration
10YPNSW_SWCR/W0h YPNSW pin SW configuration
9XNPSW_SWCR/W0h XNPSW pin SW configuration
8YNNSW_SWCR/W0h YNNSW pin SW configuration
7YPPSW_SWCR/W0h XPPSW pin SW configuration
6XNNSW_SWCR/W0h XNNSW pin SW configuration
5XPPSW_SWCR/W0h XPPSW pin SW configuration
4-2AveragingR/W0h Number of samplings to average:
000 = No average.
001 = 2 samples average.
010 = 4 samples average.
011 = 8 samples average.
100 = 16 samples average.
1-0ModeR/W0h 00 = SW enabled, one-shot.
01 = SW enabled, continuous.
10 = HW synchronized, one-shot.
11 = HW synchronized, continuous.
STEPDELAYx
BitFieldTypeResetDescription
31-24SampleDelayR/W0h This register will control the number of ADC clock cycles to sample (hold SOC high).
Any value programmed here will be added to the minimum requirement of 1 clock cycle.
23-18RESERVEDR/W0h
17-0OpenDelayR/W0h Program the number of ADC clock cycles to wait after applying the step configuration registers and before sending the start of ADC conversion
FIFOxCOUNT
BitFieldTypeResetDescription
31-7RESERVEDR0h
6-0Words_in_FIFOxR/W0h Number of words currently in the FIFOx
FIFOxTHRESHOLD
BitFieldTypeResetDescription
31-6RESERVEDR0h
5-0FIFOx_threshold_LevelR/W0h Program the desired FIFOx data sample level to reach before generating interrupt to CPU (program to value minus 1)
DMAxREQ
BitFieldTypeResetDescription
31-6RESERVEDR0h
5-0DMA_Request_LevelR/W0h Number of words in FIFOx before generating a DMA request (program to value minus 1)
FIFOxDATA
BitFieldTypeResetDescription
31-20RESERVEDR0h
19-16ADCCHNLIDR0h Optional ID tag of channel that captured the data.
If tag option is disabled, these bits will be 0.
15-12RESERVEDR0h
11-0ADCDATAR0h 12 bit sampled ADC converted data value stored in FIFO x.

使用例

続いて代表的な使用例について見ていく。
簡単なライブラリを作ったので、レジスタのmmapとオフセット計算はライブラリに任せることにする。main関数のみ解説するので、詳細はソースを見てほしい。

Channel1(AIN0), Channel8(1/2Vcc)の2chをOne shot modeでA/D変換

flow chart

それぞれSTEPCONFIG1, STEPCONFIG2を用いてFIFO0に格納するものとし、FIFOが2個使用されたらホスト側から読み出す。
IRQSTATUS_RAWのうち、IRQENABLE_SETでしていたフラグをIRQSTATUSに反映し、IRQ割り込みを発生させる機能があるが、IRQ割り込みを利用するにはドライバを作らないといけないし、Linux kernelがIRQをポーリングしているだけなので、真のリアルタイム性はいずれにしても期待できないので、IRQSTATUS_RAWを直接確認してエラーの有無を確認している。

おそらくこのプログラムではエラーが発生することはないのでエラー処理が使われることはないが、後述のcontinuousモードや、もっと複雑な制御を行う際はOverrun, Underflowの発生を検知するために役立つので、例として実装してある。

実行例

root@beaglebone:~/TSC_ADC# ./oneshot
ID0=2.20[mV] ID1=1690.14[mV]

Channel1(AIN0), Channel8(1/2Vcc)の2chをContinuous modeでA/D変換

flow chart

それぞれ、FIFO0、FIFO1に格納するものとし、FIFO1の使用量を監視して、データがある場合はアプリ側で用意した配列にコピーする。FIFO0とFIFO1の使用量は常に同じになるはずで、FIFO1の方が後から処理されるので、FIFO1の使用量のみ見ておけば問題無いはずだ。FIFO1の使用量は、FIFO1COUNTレジスタで確認できる。
前述の理由で、割り込みは使用しておらず、エラーの確認にはIRQSTATUS_RAWを直接参照している。

実行例

root@beaglebone:~/TSC_ADC# ./continuous
000, ID0=1.76[mV] ID1=1674.76[mV]
001, ID0=165.23[mV] ID1=1673.00[mV]
002, ID0=164.36[mV] ID1=1673.00[mV]
003, ID0=164.79[mV] ID1=1673.88[mV]
004, ID0=163.92[mV] ID1=1672.56[mV]
005, ID0=164.79[mV] ID1=1673.00[mV]
006, ID0=165.23[mV] ID1=1673.44[mV]
    :
20475, ID0=165.23[mV] ID1=1672.56[mV]
20476, ID0=165.23[mV] ID1=1674.32[mV]
20477, ID0=164.36[mV] ID1=1673.88[mV]
20478, ID0=164.79[mV] ID1=1673.00[mV]
20479, ID0=165.67[mV] ID1=1673.88[mV]

使用上の注意

実際に動作させてみると、少々問題があることが分かった。FIFOのOverrun/Underflowが発生した場合は、一旦TSC_ADC_SSを停止させて、割り込みフラグをクリアした後に再度起動させないと、以降の割り込みがかからなくなる。FIFOx_Thresholdに関しては、モジュールを停止せずにクリアすることができる。
< また、TSC_ADC_SSをEnableにした直後に、割り込みフラグがセットされることがあり、この挙動を知らないと嵌まるので注意が必要だ。

前述のプログラムではAverageをとっているので問題は起きないのだが、continuousモードでAverageをとらずに動作させるとFIFOバッファのOverrunが発生する。アプリケーション側の処理や、OS上で動作するほかのプログラムの兼ね合いもあると思うが、テストプログラムでは平均を2回以下とした場合、数千回程度サンプリングするとOverrunした。
高速に動作させたい場合は、PRUでFIFOバッファの刈り取りを行うことで、OSのジッタの影響を回避できると考えられる。

電圧分解能の評価

histgram

ADCの分解能は12bitということだが、実際には若干劣ると思われるので、実測値を確認してみた。BBBではCh8がVccを抵抗器で1/2に分圧した電圧が入力されているので、これを計測してみることにする。私の環境では、平均すると1646mV程度になるようだ。
ちなみに、検証時、PCからのUSB電源を使用している。モバイルバッテリーは使用してみたが、傾向性は変わらず。
左図がヒストグラムにまとめたものだが、1610mV付近に多くの出力があり、ちょっとこのままでは使えない感じがする。

histgram 0V histgram 0.9V histgram 1.8V

さすがにこれはないだろうという事で、外部から十分精度の高い直流を入力して確認した。確認はContinuous modeで行っている。
それぞれ、0, 0.9, 1.8Vのヒストグラムが次の3枚だ。何れもサンプル数は10000件だ。
それほどおかしな出力にはなっていない。どうやら、内部のCh8の入力にはノイズが載っているようだ。
変換にチャージが追いついていない可能性があると考え、open delay, sample delayを100 clock程度まであげてみたが改善する様子は見られなかった。
4mV程度低く表示されるようだが、9~10Bit程度の分解能は期待できそうである。

Square wave Sin wave

左図が矩形波(75kHz, 15kHz)と正弦波(50kHz)を取り込んでみた結果だ。最大である625usec/1600SPSでサンプリングしている。

ソースファイル

上記のテストプログラムとライブラリが含まれる。
TSC_ADC.tar.xz
プログラムはgccとmake一般的なライブラリが入っていればmakeするだけでビルドできるはずだ。