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 Signal | Max Freq | Reference / Source | Comments |
---|---|---|---|
ocp_clk OCP / Functional clock | 100 MHz | CORE_CLKOUTM4 / 2 | pd_pwkup_l4_wkup_gclk From PRCM |
adc_clk ADC clock | 24 MHz | CLK_M_OSC | pd_wkup_adc_fclk From PRCM |
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はデフォルトでdisableになっているので、 CM_WKUP_ADC_TSC_CLKCTRL registerで有効に設定する必要がある。
この設定を行わないと、TSC_ADC関連レジスタへのアクセスもBus errorになる。
TIの実装例などを見ると、CM_WKUP_CLKSTCTRL = 0も設定しているようだが、私が試した限り必要なかった。
Offset 0x44E0_0400 + | Acronym |
---|---|
BCh | CM_WKUP_ADC_TSC_CLKCTRL |
CM_WKUP_ADC_TSC_CLKCTRL Register | ||||
---|---|---|---|---|
Bit | Field | Type | Reset | Description |
31-18 | Reserved | R | 0h | |
17-16 | IDLEST | R | 3h |
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-2 | Reserved | R/W | 0h | |
1-0 | MODULEMODE | R/W | 0h |
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 |
---|---|
0h | REVISION |
10h | SYSCONFIG |
24h | IRQSTATUS_RAW |
28h | IRQSTATUS |
2Ch | IRQENABLE_SET |
30h | IRQENABLE_CLR |
34h | IRQWAKEUP |
38h | DMAENABLE_SET |
3Ch | DMAENABLE_CLR |
40h | CTRL |
44h | ADCSTAT |
48h | ADCRANGE |
4Ch | ADC_CLKDIV |
50h | ADC_MISC |
54h | STEPENABLE |
58h | IDLECONFIG |
5Ch | TS_CHARGE_STEPCONFIG |
60h | TS_CHARGE_DELAY |
64h | STEPCONFIG1 |
68h | STEPDELAY1 |
6Ch | STEPCONFIG2 |
70h | STEPDELAY2 |
74h | STEPCONFIG3 |
78h | STEPDELAY3 |
7Ch | STEPCONFIG4 |
80h | STEPDELAY4 |
84h | STEPCONFIG5 |
88h | STEPDELAY5 |
8Ch | STEPCONFIG6 |
90h | STEPDELAY6 |
94h | STEPCONFIG7 |
98h | STEPDELAY7 |
9Ch | STEPCONFIG8 |
A0h | STEPDELAY8 |
A4h | STEPCONFIG9 |
A8h | STEPDELAY9 |
ACh | STEPCONFIG10 |
B0h | STEPDELAY10 |
B4h | STEPCONFIG11 |
B8h | STEPDELAY11 |
BCh | STEPCONFIG12 |
C0h | STEPDELAY12 |
C4h | STEPCONFIG13 |
C8h | STEPDELAY13 |
CCh | STEPCONFIG14 |
D0h | STEPDELAY14 |
D4h | STEPCONFIG15 |
D8h | STEPDELAY15 |
DCh | STEPCONFIG16 |
E0h | STEPDELAY16 |
E4h | FIFO0COUNT |
E8h | FIFO0THRESHOLD |
ECh | DMA0REQ |
F0h | FIFO1COUNT |
F4h | FIFO1THRESHOLD |
F8h | DMA1REQ |
100h | FIFO0DATA |
200h | FIFO1DATA |
以下、汎用ADCとして用いる際に関連するレジスタについて説明する。
以下のレジスタは基本的に触る必要は無い。
Offset | Acronym |
---|---|
0h | REVISION Register |
10h | SYSCONFIG Register |
24h | IRQSTATUS_RAW Register |
34h | IRQWAKEUP Register |
5Ch | TS_CHARGE_STEPCONFIG Register |
60h | TS_CHARGE_DELAY Register |
50h | ADC_MISC Register |
IRQSTATUS Register | ||||
---|---|---|---|---|
Bit | Field | Type | Reset | Description |
31-11 | Reserved | R | 0h | |
10 | PEN_IRQ_synchronized | R/W | 0h |
Write 0 = No action. Read 0 = No (enabled) event pending. Read 1 = Event pending. Write 1 = Clear (raw) event. |
9 | Pen_Up_event | R/W | 0h | |
8 | Out_of_Range | R/W | 0h | |
7 | FIFO1_Underflow | R/W | 0h | |
6 | FIFO1_Overrun | R/W | 0h | |
5 | FIFO1_Threshold | R/W | 0h | |
4 | FIFO0_Underflow | R/W | 0h | |
3 | FIFO0_Overrun | R/W | 0h | |
2 | FIFO0_Threshold | R/W | 0h | |
1 | End_of_Sequence | R/W | 0h | |
0 | HW_Pen_Event_asynchronous | R/W | 0h |
IRQENABLE_SET/IRQENABLE_CLR | ||||
---|---|---|---|---|
Bit | Field | Type | Reset | Description |
31-11 | RESERVED | R/W | 0h | |
10 | HW_Pen_Event_synchronous | R/W | 0h |
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の場合) |
9 | Pen_Up_event | R/W | 0h | |
8 | Out_of_Range | R/W | 0h | |
7 | FIFO1_Underflow | R/W | 0h | |
6 | FIFO1_Overrun | R/W | 0h | |
5 | FIFO1_Threshold | R/W | 0h | |
4 | FIFO0_Underflow | R/W | 0h | |
3 | FIFO0_Overrun | R/W | 0h | |
2 | FIFO0_Threshold | R/W | 0h | |
1 | End_of_Sequence | R/W | 0h | |
0 | HW_Pen_Event_asynchronous | R/W | 0h |
DMAENABLE_SET/DMAENABLE_CLR | ||||
---|---|---|---|---|
Bit | Field | Type | Reset | Description |
31-2 | RESERVED | R/W | 0h | |
1 | Enable_1 | R/W | 0h |
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の場合) |
0 | Enable_0 | R/W | 0h |
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 | ||||
---|---|---|---|---|
Bit | Field | Type | Reset | Description |
31-10 | RESERVED | R/W | 0h | |
9 | HW_preempt | R/W | 0h |
0 = SW steps are not pre-empted by HW events. 1 = SW steps are pre-empted by HW events. |
8 | HW_event_mapping | R/W | 0h |
0 = Map HW event to Pen touch irq (from AFE). 1 = Map HW event to HW event input. |
7 | Touch_Screen_Enable | R/W | 0h |
0 = Touchscreen transistors disabled. 1 = Touchscreen transistors enabled. |
6-5 | AFE_Pen_Ctrl | R/W | 0h |
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. |
4 | Power_Down | R/W | 0h |
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) |
3 | ADC_Blas_select | R/W | 0h |
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) |
2 | StepConfig_WriteProtect_n_active_low | R/W | 0h |
0 = Step configuration registers are protected (not writable). 1 = Step configuration registers are not protected (writable). |
1 | Step_ID_tag | R/W | 0h |
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. |
0 | Enable | R/W | 0h |
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 | ||||
---|---|---|---|---|
Bit | Field | Type | Reset | Description |
31-8 | RESERVED | R/W | 0h | |
7 | PEN_IRQ1 | R | 0h | PEN_IRQ[1] status |
6 | PEN_IRQ0 | R | 0h | PEN_IRQ[0] status |
5 | FSM_BUSY | R | 0h |
Status of OCP FSM and ADC FSM. 0 = Idle. 1 = Busy. |
4-0 | STEP_ID | R | 10h |
Encoded values:. 10000 = Idle. 10001 = Charge. 00000~01111 = Step ID |
ADCRANGE | ||||
---|---|---|---|---|
Bit | Field | Type | Reset | Description |
31-28 | RESERVED | R | 0h | |
27-16 | High_Range_Data | R/W | 0h |
Sampled ADC data is compared to this value. If the sampled data is greater than the value, then an interrupt is generated. |
15-12 | RESERVED | R | 0h | |
11-0 | Low_Range_Data | R/W | 0h |
Sampled ADC data is compared to this value. If the sampled data is less than the value, then an interrupt is generated. |
ADC_CLKDIV | ||||
---|---|---|---|---|
Bit | Field | Type | Reset | Description |
31-16 | RESERVED | R | 0h | |
15-0 | ADC_ClkDiv | R/W | 0h |
The input ADC clock will be divided by this value and sent to the AFE. Program to the value minus 1. |
STEPENABLE | ||||
---|---|---|---|---|
Bit | Field | Type | Reset | Description |
32-17 | RESERVED | R | 0h | |
16-1 | STEPx | R/W | 0h | Enable step x |
0 | TS_Charge | R/W | 0h | Enable TS Charge step |
IDLECONFIG | ||||
---|---|---|---|---|
Bit | Field | Type | Reset | Description |
31-26 | RESERVED | R | 0h | |
25 | CMPEQ | R/W | 0h |
Differential Control Pin. 0 = Single Ended, SEL_INM_SWC_3_0 must be 1xxx. 1 = Differential Pair Enable. |
24-23 | CMPEQ | R/W | 0h |
SEL_RFM pins SW configuration. 00 = VSSA_ADC. 01 = XNUR. 10 = YNLR. 11 = VREFN. |
22-19 | CMPEQ | R/W | 0h |
SEL_INP pins SW configuration. 0000 = Channel 1. 0111 = Channel 8. 1xxx = VREFN. |
18-15 | CMPEQ | R/W | 0h |
SEL_INM pins for neg differential. 0000 = Channel 1. 0111 = Channel 8. 1xxx = ADCREFM, anytime DIFF_CNTRL = 0. |
14-12 | CMPEQ | R/W | 0h |
SEL_RFP pins SW configuration. 000 = VDDA_ADC. 001 = XPUL. 010 = YPLL. 011 = VREFP. 1xx = Reserved. |
11 | CMPEQ | R/W | 0h | WPNSW pin SW configuration |
10 | CMPEQ | R/W | 0h | YPNSW pin SW configuration |
9 | CMPEQ | R/W | 0h | XNPSW pin SW configuration |
8 | CMPEQ | R/W | 0h | YNNSW pin SW configuration |
7 | CMPEQ | R/W | 0h | YPPSW pin SW configuration |
6 | CMPEQ | R/W | 0h | XNNSW pin SW configuration |
5 | XPPSW_SWC | R/W | 0h | XPPSW pin SW configuration |
4-0 | RESERVED | R/W | 0h |
STEPCONFIGx | ||||
---|---|---|---|---|
Bit | Field | Type | Reset | Description |
31-28 | RESERVED | R/W | 0h | |
27 | Range_check | R/W | 0h |
0 = Disable out-of-range check. 1 = Compare ADC data with range check register. |
26 | FIFO_select | R/W | 0h |
Sampled data will be stored in FIFO. 0 = FIFO 0. 1 = FIFO 1. |
25 | Diff_CNTRL | R/W | 0h |
Differential Control Pin. 0 = Single Ended, SEL_INM_SWC_3_0 must be 1xxx. 1 = Differential Pair Enable. |
24-23 | SEL_RFM_SWC_1_0 | R/W | 0h |
SEL_RFM pins SW configuration. 00 = VSSA. 01 = XNUR. 10 = YNLR. 11 = VREFN. |
22-19 | SEL_INP_SWC_3_0 | R/W | 0h |
SEL_INP pins SW configuration. 0000 = Channel 1. 0111 = Channel 8. 1xxx = VREFN. |
18-15 | SEL_INM_SWC_3_0 | R/W | 0h |
SEL_INM pins for negative differential. 0000 = Channel 1. 0111 = Channel 8. 1xxx = ADCREFM, anytime DIFF_CNTRL = 0. |
14-12 | SEL_RFP_SWC_2_0 | R/W | 0h |
SEL_RFP pins SW configuration. 000 = VDDA_ADC. 001 = XPUL. 010 = YPLL. 011 = VREFP. 1xx = Reserved. |
11 | WPNSW_SWC | R/W | 0h | WPNSW pin SW configuration |
10 | YPNSW_SWC | R/W | 0h | YPNSW pin SW configuration |
9 | XNPSW_SWC | R/W | 0h | XNPSW pin SW configuration |
8 | YNNSW_SWC | R/W | 0h | YNNSW pin SW configuration |
7 | YPPSW_SWC | R/W | 0h | XPPSW pin SW configuration |
6 | XNNSW_SWC | R/W | 0h | XNNSW pin SW configuration |
5 | XPPSW_SWC | R/W | 0h | XPPSW pin SW configuration |
4-2 | Averaging | R/W | 0h |
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-0 | Mode | R/W | 0h |
00 = SW enabled, one-shot. 01 = SW enabled, continuous. 10 = HW synchronized, one-shot. 11 = HW synchronized, continuous. |
STEPDELAYx | ||||
---|---|---|---|---|
Bit | Field | Type | Reset | Description |
31-24 | SampleDelay | R/W | 0h |
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-18 | RESERVED | R/W | 0h | |
17-0 | OpenDelay | R/W | 0h | Program the number of ADC clock cycles to wait after applying the step configuration registers and before sending the start of ADC conversion |
FIFOxCOUNT | ||||
---|---|---|---|---|
Bit | Field | Type | Reset | Description |
31-7 | RESERVED | R | 0h | |
6-0 | Words_in_FIFOx | R/W | 0h | Number of words currently in the FIFOx |
FIFOxTHRESHOLD | ||||
---|---|---|---|---|
Bit | Field | Type | Reset | Description |
31-6 | RESERVED | R | 0h | |
5-0 | FIFOx_threshold_Level | R/W | 0h | Program the desired FIFOx data sample level to reach before generating interrupt to CPU (program to value minus 1) |
DMAxREQ | ||||
---|---|---|---|---|
Bit | Field | Type | Reset | Description |
31-6 | RESERVED | R | 0h | |
5-0 | DMA_Request_Level | R/W | 0h | Number of words in FIFOx before generating a DMA request (program to value minus 1) |
FIFOxDATA | ||||
---|---|---|---|---|
Bit | Field | Type | Reset | Description |
31-20 | RESERVED | R | 0h | |
19-16 | ADCCHNLID | R | 0h |
Optional ID tag of channel that captured the data. If tag option is disabled, these bits will be 0. |
15-12 | RESERVED | R | 0h | |
11-0 | ADCDATA | R | 0h | 12 bit sampled ADC converted data value stored in FIFO x. |
続いて代表的な使用例について見ていく。
簡単なライブラリを作ったので、レジスタのmmapとオフセット計算はライブラリに任せることにする。main関数のみ解説するので、詳細はソースを見てほしい。
それぞれ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]
それぞれ、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のジッタの影響を回避できると考えられる。
ADCの分解能は12bitということだが、実際には若干劣ると思われるので、実測値を確認してみた。BBBではCh8がVccを抵抗器で1/2に分圧した電圧が入力されているので、これを計測してみることにする。私の環境では、平均すると1646mV程度になるようだ。
ちなみに、検証時、PCからのUSB電源を使用している。モバイルバッテリーは使用してみたが、傾向性は変わらず。
左図がヒストグラムにまとめたものだが、1610mV付近に多くの出力があり、ちょっとこのままでは使えない感じがする。
さすがにこれはないだろうという事で、外部から十分精度の高い直流を入力して確認した。確認はContinuous modeで行っている。
それぞれ、0, 0.9, 1.8Vのヒストグラムが次の3枚だ。何れもサンプル数は10000件だ。
それほどおかしな出力にはなっていない。どうやら、内部のCh8の入力にはノイズが載っているようだ。
変換にチャージが追いついていない可能性があると考え、open delay, sample delayを100 clock程度まであげてみたが改善する様子は見られなかった。
4mV程度低く表示されるようだが、9~10Bit程度の分解能は期待できそうである。
左図が矩形波(75kHz, 15kHz)と正弦波(50kHz)を取り込んでみた結果だ。最大である625usec/1600SPSでサンプリングしている。