前回BBBのDMTimerを用いてTimer出力の試行を行った。
引き続き今度はDMtimerのtimer captureを試す。
多少実用的にするために任意の入力信号の周波数測定を行うサンプルデモとした。
AM335Xのレジスタ詳細に関しては以下のドキュメント参照。
AM335x Sitara(TM) Processors Technical Reference Manual
DMTimerのcapture機能はTIMx_OUTを入力に切り替え、基準クロックTimer TCRRを走らせておき、TIMx_OUTに入力された任意の信号のエッジ割り込みが発生した際のTCRRの値をキャプチャする。
言い換えるとTIMx_OUTに入力された任意の信号のエッジ割り込み二回分のキャプチャ結果の差により入力信号の周波数を求めることができる。
ただ、DMTimerのcapture modeは今一つ使いどころが難しい。
正直に言って使い難い。
同じtimer capture機能でもeCAP moduleのcapture modeの方がはるかに実用的である。
使い難い理由は主に以下。
・Prescalerが入力信号側ではなく基準クロック側にしかきかない。
・TCAR_IT_FLAGをクリアしなければcaptureを3回以上連続で行うことができない。
eCAP moduleでは入力信号側にPrescalerがあるので、入力信号を分周することにより精度を上げることができるので意味がある。
DMTimerでは計測する側の基準クロックの分周しかできないので、通常の使い方では高精度の周波数測定ができないと思われる。
TCAR_IT_FLAGをクリアしなければならないということは、レジスタ設定だけを行い必要なタイミングでそのタイミングでのキャプチャ結果を読みに行くような使い方ができないので何かしらのソフトウェア制御が必要となるだろう。
二回までの連続キャプチャは可能なので、devmem2でレジスタ設定を行って結果をポーリングするような使い方は可能である。
尚、私がDMTimerのcapture modeの設計意図を理解していないだけでもっと便利な使い方があるかもしれない。
とは言え、この手のペリフェラルロジックは工夫次第で応用範囲が広がるものである。
今回二つのDMTimerをPRUから制御を行い、比較的高精度の周波数測定を実現させる。
実装する構成の信号の流れは以下の図のようになる。
基本的なアイデアは以下。
・TIMER4を基準クロックCLK_M_OSCとし10HzのTimer出力とする。
・TIMER5を基準クロックtclkin、capture modeとしTIM5_OUTからの10HzのTimer出力を入力とする。
この構成で、10Hzの入力クロックの立ち上がりエッジ間に基準クロックtclkinが何回カウントされるかをcaptureする。
つまり主客を逆にしてTIM5_OUTの入力信号を基準に、tclkinの周波数を計測するわけだ。
TCAR_IT_FLAGのクリアと計測結果及びタイミングのホストへの通知はPRUにより行う。
ついでにTIMER6からtclkinの四分周の波形を、TIMER7からtclkinの四分周の反転波形の出力を行う。
TIMER4からの出力、すなわちTIMER5への入力周波数は単位計測周期を作るが既知であればなんでも良く、周波数が高ければ計測時間が短くなる代わりに計測精度が悪くなり、反対に周波数が低ければ計測精度が高くなる代わりに測定時間が長くなる。
10Hzであれば100msec毎の計測となるので周波数の変動への追随は比較的早く使いやすいのではないだろうか。
尚、今回は実装していないが、別解として以下もあるだろう。
・TIMER5を基準クロックtclkin、分周比nのタイマ出力としてTIM5_OUTから出力させる。
・TIMER4を基準クロックCLK_M_OSC、capture modeとしTIM5_OUTからのTimer出力を入力とする。
この構成ではtclkinに測定信号を入れ、TIMER5をPrescalerとして用いて周波数を下げ、TIMER4でキャプチャする。
多分この構成の方がDMTIMERのcapture modeとして素直であろう。
測定時間がtclkinへの入力信号周波数依存となることを嫌い採用しなかった。
今回も前回までの環境が構築されている前提とする。
Beagle Bone BlackのPRUを学ぶ(その4)
Beagle Bone BlackのPRUを学ぶ(その3)
Beagle Bone BlackのPRUを学ぶ(その2)
Beagle Bone BlackのPRUを学ぶ(その1)
先ずはdevice treeの設定。
今回は以下のように使う。
TIMER4_OUT: P8_7 TIMER5_IN: P8_9 TIMER6_OUT: P8_10 TIMER7_OUT: P8_8 tclkin: P9_41 24.576M_CLK_OUT: P9.25
前回も述べたが、TIMER4~TIMER7は少なくともKernel3.8ベースではドライバとしてサポートされていない。
よって、device treeのoverrayではpinmuxの設定だけを行い、DMTimerの設定はPRUプログラムで行うこととする。
tclkinはP9_41にアサインされているが、P9_41はAM335XのBot D14だけでなくBBB内部でBot D13:gpio3[20]にも接続されている。
従ってP9_41をtclkinとして使用する場合はgpio3[20]をhigh-Zに設定する必要がある。
また、既知の周波数である入力信号が欲しいのでBBBに搭載されているP9.25の24.576MHzオシレータのCLK出力を用いる。
24.576MHzのオシレータのOEはBBB内部でBot V17:gpio1[27]に結線されているので入力かつプルアップ設定とする。
BeagleBone Black Rev.Cのピンアサイン表参照。
以上を踏まえ、各GPIO pinmuxを設定するためのdtsファイルを書いてみた。
/dts-v1/;
/plugin/;
/ {
compatible = "ti,beaglebone", "ti,beaglebone-black";
/* identification */
part-number = "bone-JB-DMTIMER";
version = "00A0";
exclusive-use =
/* pin header uses */
"P8.7", /* timer4_P8_7: [Bot R7 ] timer4_mux3 */
"P8.8", /* timer7_P8_8: [Bot T7 ] timer7_mux3 */
"P8.10", /* timer6_P8_10: [Bot U6 ] timer6_mux3 */
"P8.9", /* timer5_P8_9: [Bot T6 ] timer5_mux3 */
"P9.41", /* timer_P9_41: [Bot D14] tclkin ! shared use ! */
/* gpio3_P9_41: [Bot D13] gpio3[20] ! shared use ! */
/* P9.41 is shared resource that is connected with both D14 and D13.
Since we wll use function on D14 as input pin, D13 has to be set
to input(high-Z). */
"P9.25", /* gpio3_P9_25: [Bot A14] gpio3[21] */
/* P9.25 is conected with 24.576MHz oscillator CLK.
This oscillator is enabled and disabled with gpio1[27] */
/* the hardware IP uses */
"timer4",
"timer5",
"timer6",
"timer7";
fragment@0 {
target = <&am33xx_pinmux>;
__overlay__ {
timer_gpio: pinmux_timer_pins {
pinctrl-single,pins = <
/* TIMx_OUT settings */
0x090 0x0a /* [Bot R7 ] (IDIS|OFF|MODE2)=(0 01 010) timer4_mux3 P8_7 */
0x094 0x0a /* [Bot T7 ] (IDIS|OFF|MODE2)=(0 01 010) timer7_mux3 P8_8 */
0x098 0x0a /* [Bot U6 ] (IDIS|OFF|MODE2)=(0 01 010) timer6_mux3 P8_10 */
0x09C 0x22 /* [Bot T6 ] (IEN |PD |MODE2)=(1 00 010) timer5_mux3 P8_9 */
/* tclkin settings */
0x1A8 0x2f /* [Bot D13] (IEN |OFF|MODE7)=(1 01 111) gpio3[20] P9_41# */
0x1B4 0x22 /* [Bot D14] (IEN |PD |MODE2)=(1 00 010) tclkin P9_41# */
/* 24.576MHz oscillator settings */
0x06C 0x37 /* [Bot V17] (IEN |PU |MODE7)=(1 10 111) gpio1[27] OSC_OE */
0x1AC 0x2f /* [Bot A14] (IEN |OFF|MODE7)=(1 01 111) gpio3[21] p9_25 */
>;
};
};
};
fragment@1 {
target = <&ocp>;
__overlay__ {
test_helper: helper {
compatible = "bone-pinmux-helper";
pinctrl-names = "default";
pinctrl-0 = <&timer_gpio>;
status = "okay";
};
};
};
fragment@2 {
target = <&pruss>;
__overlay__ {
status = "okay";
};
};
};
このdtsファイルをbone-JB-DMTIMER-00A0.dtsという名前で/lib/firmwareに保存して以下のようにdtcでコンパイルする。
debian@beaglebone:~$ sudo -s root@beaglebone:/home/debian# cd /lib/firmware/ root@beaglebone:/lib/firmware# dtc -@ -O dtb -o bone-JB-DMTIMER-00A0.dtbo bone-JB-DMTIMER-00A0.dts root@beaglebone:/lib/firmware# reboot
念の為にリブートを行う。
ちなみに使用するピンのデフォルトでのpinmuxは全てmode7の入力となっている。
debian@beaglebone:~$ sudo -s root@beaglebone:/home/debian# export PINS=/sys/kernel/debug/pinctrl/44e10800.pinmux/pins root@beaglebone:/home/debian# cat $PINS | grep 890 pin 36 (44e10890) 00000037 pinctrl-single root@beaglebone:/home/debian# cat $PINS | grep 894 pin 37 (44e10894) 00000037 pinctrl-single root@beaglebone:/home/debian# cat $PINS | grep 898 pin 38 (44e10898) 00000037 pinctrl-single root@beaglebone:/home/debian# cat $PINS | grep 89c pin 39 (44e1089c) 00000037 pinctrl-single root@beaglebone:/home/debian# cat $PINS | grep 9a8 pin 106 (44e109a8) 00000027 pinctrl-single root@beaglebone:/home/debian# cat $PINS | grep 9b4 pin 109 (44e109b4) 00000027 pinctrl-single root@beaglebone:/home/debian# cat $PINS | grep 86c pin 27 (44e1086c) 00000027 pinctrl-single root@beaglebone:/home/debian# cat $PINS | grep 9ac pin 107 (44e109ac) 00000027 pinctrl-single root@beaglebone:/home/debian#
再立ち上げ後root権限でcapemgrにbone-JB-DMTIMERを与える。
debian@beaglebone:~$ sudo -s root@beaglebone:/home/debian# export SLOTS=/sys/devices/bone_capemgr.*/slots root@beaglebone:/home/debian# echo bone-JB-DMTIMER > $SLOTS root@beaglebone:/home/debian# cat $SLOTS 0: 54:PF--- 1: 55:PF--- 2: 56:PF--- 3: 57:PF--- 4: ff:P-O-L Bone-LT-eMMC-2G,00A0,Texas Instrument,BB-BONE-EMMC-2G 5: ff:P-O-- Bone-Black-HDMI,00A0,Texas Instrument,BB-BONELT-HDMI 6: ff:P-O-- Bone-Black-HDMIN,00A0,Texas Instrument,BB-BONELT-HDMIN 7: ff:P-O-L Override Board Name,00A0,Override Manuf,bone-JB-DMTIMER root@beaglebone:/home/debian#
pinmuxが設定されているか確認。
root@beaglebone:/home/debian# export PINS=/sys/kernel/debug/pinctrl/44e10800.pinmux/pins root@beaglebone:/home/debian# cat $PINS | grep 890 pin 36 (44e10890) 0000000a pinctrl-single root@beaglebone:/home/debian# cat $PINS | grep 894 pin 37 (44e10894) 0000000a pinctrl-single root@beaglebone:/home/debian# cat $PINS | grep 898 pin 38 (44e10898) 0000000a pinctrl-single root@beaglebone:/home/debian# cat $PINS | grep 89c pin 39 (44e1089c) 00000022 pinctrl-single root@beaglebone:/home/debian# cat $PINS | grep 9a8 pin 106 (44e109a8) 0000002f pinctrl-single root@beaglebone:/home/debian# cat $PINS | grep 9b4 pin 109 (44e109b4) 00000022 pinctrl-single root@beaglebone:/home/debian# cat $PINS | grep 86c pin 27 (44e1086c) 00000037 pinctrl-single root@beaglebone:/home/debian# cat $PINS | grep 9ac pin 107 (44e109ac) 0000002f pinctrl-single root@beaglebone:/home/debian#
意図通りに設定できていることが確認できた。
PRUのソースコードを以下に晒す。
// dmtimer.p
.origin 0
.entrypoint DMTIMER_SAMPLE
#include "dmtimer.hp"
// Our plan to use Registers
// R0-R4: temporary use
// R10: Address of CM_PER_BASE
// R11: Address of CM_DPLL_BASE
// R12: Address of DMTIMER4_BASE
// R13: Address of DMTIMER5_BASE
// R14: Address of DMTIMER6_BASE
// R15: Address of DMTIMER7_BASE
// R17-R20: always all ZERO
// I/F with Host
// CONST_PRUSHAREDRAM + 0x00: size 4Byte: Status value to indicate to host.
// CONST_PRUSHAREDRAM + 0x04: size 4Byte: Frequency value to indicate to host.
// Our plan to use timers
// TIMER4: Make 1Hz wave
// TIMER5: Capture Frequency value of TCLKIN with feedback TIMER4
// TIMER6: Make positive phase wave which divided TCLKIN by 2
// TIMER7: Make negative phase wave which divided TCLKIN by 2
DMTIMER_SAMPLE:
// Make Constant values for registers
MOV r10, CM_PER_BASE // r10 is Address of CM_PER_BASE
MOV r11, CM_DPLL_BASE // r11 is Address of CM_DPLL_BASE
MOV r12, DMTIMER4_BASE // r12 is Address of DMTIMER4_BASE
MOV r13, DMTIMER5_BASE // r13 is Address of DMTIMER5_BASE
MOV r14, DMTIMER6_BASE // r14 is Address of DMTIMER6_BASE
MOV r15, DMTIMER7_BASE // r15 is Address of DMTIMER7_BASE
MOV r17, 0x00000000 // r17 is always all ZERO
MOV r18, r17 // also r18-r20 are always all ZERO
MOV r19, r17 //
MOV r20, r17 //
// Enable OCP Master port
LBCO r0, CONST_PRUCFG, OFFSET_CFG_SYSCFG_REG, 4 // r0 = *(CONST_PRUCFG + OFFSET_CFG_SYSCFG_REG)
CLR r0, r0, 4 // Clear bit4 of r0 (Clear SYSCFG:STANDBY_INIT
// to enable OCP master port)
SBCO r0, CONST_PRUCFG, OFFSET_CFG_SYSCFG_REG, 4 // *(CONST_PRUCFG + OFFSET_CFG_SYSCFG_REG) = r0
// Init Indexs and Pointers of Constant tables
MOV r0, PRU0_CTRL_REG_BASE //
SBBO r17, r0, OFFSET_PRU_CTBIR0_REG, 4 // *(PRU0_CTRL_REG_BASE + OFFSET_PRU_CTBIR0_REG) = r17:0x00000000
// Set Fields C25_BLK_INDEX and C24_BLK_INDEX to 0.
MOV r1, 0x00000100 //
SBBO r1, r0, OFFSET_PRU_CTPPR0_REG, 4 // *(PRU0_CTRL_REG_BASE + OFFSET_PRU_CTPPR0_REG) = r1:0x00000100
// Set Fields C29_POINTER to 0x0000 and C28_POINTER to 0x0100.
// Unexpectedly, C28 is NOT dedicated for Shared PRU RAM,
// we can use C28 for PRU0/1 Local Data RAM if C28_POINTER
// in CTPPRx register is set to 0x0000/0x0020. So to use
// for Shared PRU RAM C28_POINTER must be set to 0x0100.
INIT_DMTIMER:
// Init Indication params for/from Host
SBCO r17, CONST_PRUSHAREDRAM, 0x00, 4 // *(CONST_PRUSHAREDRAM + 0) = r17:0x00000000
SBCO r17, CONST_PRUSHAREDRAM, 0x04, 4 // *(CONST_PRUSHAREDRAM + 4) = r17:0x00000000
// Enable DMTIMER modules
MOV r0, 0x00000002 //
SBBO r0, r10, OFFSET_CM_PER_TIMER4_CLKCTRL, 4 // *(r10:CM_PER_BASE + OFFSET_CM_PER_TIMER4_CLKCTRL) = r0:00000002
// 0x44E00088 : 0x00000002
SBBO r0, r10, OFFSET_CM_PER_TIMER5_CLKCTRL, 4 // *(r10:CM_PER_BASE + OFFSET_CM_PER_TIMER5_CLKCTRL) = r0:00000002
// 0x44E000ec : 0x00000002
SBBO r0, r10, OFFSET_CM_PER_TIMER6_CLKCTRL, 4 // *(r10:CM_PER_BASE + OFFSET_CM_PER_TIMER6_CLKCTRL) = r0:00000002
// 0x44E000f0 : 0x00000002
SBBO r0, r10, OFFSET_CM_PER_TIMER7_CLKCTRL, 4 // *(r10:CM_PER_BASE + OFFSET_CM_PER_TIMER7_CLKCTRL) = r0:00000002
// 0x44E0007c : 0x00000002
// TIMERx_CLKCTRL are manage the TIMERx clocks.
// 31-18 Reserved
// 17-16 IDLEST : Module idle status.
// 0x0 = Func : Module is fully functional, including OCP
// 0x1 = Trans : Module is performing transition: wakeup,
// or sleep, or sleep abortion
// 0x2 = Idle : Module is in Idle mode (only OCP part).
// It is functional if using separate functional clock
// 0x3 = Disable : Module is disabled and cannot be accessed
// 15-2 Reserved
// 1-0 MODULEMODE : Control the way mandatory clocks are managed.
// 0x0 = DISABLED : Module is disable by SW. Any OCP access to module
// results in an error, except if resulting
// from a module wakeup (asynchronous wakeup).
// 0x1 = RESERVED_1 : Reserved
// * 0x2 = ENABLE : 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
// just in case we check TIDR registers
LBBO r0, r12, OFFSET_TIDR, 4 // r0 = *(r12:DMTIMER4_BASE + OFFSET_TIDR)
// 0x48044000 : 0x40000100
LBBO r0, r13, OFFSET_TIDR, 4 // r0 = *(r13:DMTIMER5_BASE + OFFSET_TIDR)
// 0x48046000 : 0x40000100
LBBO r0, r14, OFFSET_TIDR, 4 // r0 = *(r14:DMTIMER6_BASE + OFFSET_TIDR)
// 0x48048000 : 0x40000100
LBBO r0, r15, OFFSET_TIDR, 4 // r0 = *(r15:DMTIMER7_BASE + OFFSET_TIDR)
// 0x4804A000 : 0x40000100
// TIDR Register
// TIDR register is fixed value as 0x40000100.
// we stop and configure DMTIMERs
MOV r0, 0x00001402 // TIMER4 is used to make 1Hz clock.
SBBO r0, r12, OFFSET_TCLR, 4 // *(r12:DMTIMER4_BASE + OFFSET_TCLR) = r0:0x00001402
// 0x48044038: 0x00001402
// 0000 0000 0000 0000 0001 0100 0000 0010
// TCLR Register
// 31-15 RESERVED
// 14 GPO_CFG : General purpose output this register
// drives directly the PORGPOCFG output pin
// * 0h = PORGPOCFG drives 0 and configures the timer pin
// as an output.
// 1h = PORGPOCFG drives 1 and configures the timer pin
// as an input.
// 13 CAPT_MODE : Capture mode.
// * 0h = Single capture
// 1h = Capture on second event
// 12 PT : Pulse or toggle mode on PORTIMERPWM output pin
// 0h = Pulse
// * 1h = Toggle
// 11-10 TRG : Trigger output mode on PORTIMERPWM output pin
// 0h = No trigger
// * 1h = Trigger on overflow
// 2h = Trigger on overflow and match
// 3h = Reserved
// 9-8 TCM : Transition Capture Mode on PIEVENTCAPT input pin
// * 0h = No capture
// 1h = Capture on low to high transition
// 2h = Capture on high to low transition
// 3h = Capture on both edge transition
// 7 SCPWM : This bit should be set or clear while
// the timer is stopped or the trigger is off
// * 0h = Clear the PORTIMERPWM output pin and select positive
// pulse for pulse mode
// 1h = Set the PORTIMERPWM output pin and select negative
// pulse for pulse mode
// 6 CE :
// * 0h = Compare mode is disabled
// 1h = Compare mode is enabled
// 5 PRE : Prescaler enable
// * 0h = The TIMER clock input pin clocks the counter
// 1h = The divided input pin clocks the counter
// 4-2 PTV : Pre-scale clock Timer value
// * 0h = x2
// 1 AR :
// 0h = One shot timer
// * 1h = Auto-reload timer
// 0 ST : In the case of one-shot mode selected (AR = 0),
// this bit is automatically reset by internal logic
// when the counter is overflowed.
// * 0h (R) = Stop timeOnly the counter is frozen
// 1h = Start timer
MOV r0, 0x00004102 // TIMER5 is used to capture frequensy of TCLKIN with TIMER4 feedback signal.
SBBO r0, r13, OFFSET_TCLR, 4 // *(r13:DMTIMER5_BASE + OFFSET_TCLR) = r0:0x4102
// 0x48046038 : 0x00004102
// 0000 0000 0000 0000 0100 0001 0000 0010
// TCLR Register
// 31-15 RESERVED
// 14 GPO_CFG : General purpose output this register
// drives directly the PORGPOCFG output pin
// 0h = PORGPOCFG drives 0 and configures the timer pin
// as an output.
// * 1h = PORGPOCFG drives 1 and configures the timer pin
// as an input.
// 13 CAPT_MODE : Capture mode.
// * 0h = Single capture
// 1h = Capture on second event
// 12 PT : Pulse or toggle mode on PORTIMERPWM output pin
// * 0h = Pulse
// 1h = Toggle
// 11-10 TRG : Trigger output mode on PORTIMERPWM output pin
// * 0h = No trigger
// 1h = Trigger on overflow
// 2h = Trigger on overflow and match
// 3h = Reserved
// 9-8 TCM : Transition Capture Mode on PIEVENTCAPT input pin
// 0h = No capture
// * 1h = Capture on low to high transition
// 2h = Capture on high to low transition
// 3h = Capture on both edge transition
// 7 SCPWM : This bit should be set or clear while
// the timer is stopped or the trigger is off
// * 0h = Clear the PORTIMERPWM output pin and select positive
// pulse for pulse mode
// 1h = Set the PORTIMERPWM output pin and select negative
// pulse for pulse mode
// 6 CE :
// * 0h = Compare mode is disabled
// 1h = Compare mode is enabled
// 5 PRE : Prescaler enable
// * 0h = The TIMER clock input pin clocks the counter
// 1h = The divided input pin clocks the counter
// 4-2 PTV : Pre-scale clock Timer value
// * 0h = x2
// 1 AR :
// 0h = One shot timer
// * 1h = Auto-reload timer
// 0 ST : In the case of one-shot mode selected (AR = 0),
// this bit is automatically reset by internal logic
// when the counter is overflowed.
// 0h (R) = Stop timeOnly the counter is frozen
// 1h = Start timer
MOV r0, 0x00001400 // TIMER6 is used to make positive wave which divided TCLKIN by 2.
SBBO r0, r14, OFFSET_TCLR, 4 // *(r14:DMTIMER6_BASE + OFFSET_TCLR) = r0:0x00001400
// 0x48048038 : 0x00001400
// 0000 0000 0000 0000 0001 0100 0000 0000
// TCLR Register
// 31-15 RESERVED
// 14 GPO_CFG : General purpose output this register
// drives directly the PORGPOCFG output pin
// * 0h = PORGPOCFG drives 0 and configures the timer pin
// as an output.
// 1h = PORGPOCFG drives 1 and configures the timer pin
// as an input.
// 13 CAPT_MODE : Capture mode.
// * 0h = Single capture
// 1h = Capture on second event
// 12 PT : Pulse or toggle mode on PORTIMERPWM output pin
// 0h = Pulse
// * 1h = Toggle
// 11-10 TRG : Trigger output mode on PORTIMERPWM output pin
// 0h = No trigger
// * 1h = Trigger on overflow
// 2h = Trigger on overflow and match
// 3h = Reserved
// 9-8 TCM : Transition Capture Mode on PIEVENTCAPT input pin
// * 0h = No capture
// 1h = Capture on low to high transition
// 2h = Capture on high to low transition
// 3h = Capture on both edge transition
// 7 SCPWM : This bit should be set or clear while
// the timer is stopped or the trigger is off
// * 0h = Clear the PORTIMERPWM output pin and select positive
// pulse for pulse mode
// 1h = Set the PORTIMERPWM output pin and select negative
// pulse for pulse mode
// 6 CE :
// * 0h = Compare mode is disabled
// 1h = Compare mode is enabled
// 5 PRE : Prescaler enable
// * 0h = The TIMER clock input pin clocks the counter
// 1h = The divided input pin clocks the counter
// 4-2 PTV : Pre-scale clock Timer value
// * 0h = x2
// 1 AR :
// * 0h = One shot timer
// 1h = Auto-reload timer
// 0 ST : In the case of one-shot mode selected (AR = 0),
// this bit is automatically reset by internal logic
// when the counter is overflowed.
// * 0h (R) = Stop timeOnly the counter is frozen
// 1h = Start timer
MOV r0, 0x00001482 // TIMER7 is used to make negative wave which divided TCLKIN by 2.
SBBO r0, r15, OFFSET_TCLR, 4 // *(r15:DMTIMER7_BASE + OFFSET_TCLR) = r0:0x00001482
// 0x4804a038 : 0x00001482
// 0000 0000 0000 0000 0001 0100 1000 0010
// TCLR Register
// 31-15 RESERVED R 0h
// 14 GPO_CFG : General purpose output this register
// drives directly the PORGPOCFG output pin
// * 0h = PORGPOCFG drives 0 and configures the timer pin
// as an output.
// 1h = PORGPOCFG drives 1 and configures the timer pin
// as an input.
// 13 CAPT_MODE : Capture mode.
// * 0h = Single capture
// 1h = Capture on second event
// 12 PT : Pulse or toggle mode on PORTIMERPWM output pin
// 0h = Pulse
// * 1h = Toggle
// 11-10 TRG : Trigger output mode on PORTIMERPWM output pin
// 0h = No trigger
// * 1h = Trigger on overflow
// 2h = Trigger on overflow and match
// 3h = Reserved
// 9-8 TCM : Transition Capture Mode on PIEVENTCAPT input pin
// * 0h = No capture
// 1h = Capture on low to high transition
// 2h = Capture on high to low transition
// 3h = Capture on both edge transition
// 7 SCPWM : This bit should be set or clear while
// the timer is stopped or the trigger is off
// 0h = Clear the PORTIMERPWM output pin and select positive
// pulse for pulse mode
// * 1h = Set the PORTIMERPWM output pin and select negative
// pulse for pulse mode
// 6 CE :
// * 0h = Compare mode is disabled
// 1h = Compare mode is enabled
// 5 PRE : Prescaler enable
// * 0h = The TIMER clock input pin clocks the counter
// 1h = The divided input pin clocks the counter
// 4-2 PTV : Pre-scale clock Timer value
// * 0h = x2
// 1 AR :
// 0h = One shot timer
// * 1h = Auto-reload timer
// 0 ST : In the case of one-shot mode selected (AR = 0),
// this bit is automatically reset by internal logic
// when the counter is overflowed.
// 0h (R) = Stop timeOnly the counter is frozen
// * 1h = Start timer
// then we change clock sources for each TCRRs.
// CLKSEL_TIMERx are in DPLL sub-system
MOV r0, 0x00000001 //
SBBO r0, r11, OFFSET_CLKSEL_TIMER4_CLK, 4 // *(r11:CM_DPLL_BASE + OFFSET_CLKSEL_TIMER4_CLK) = r0:0x01
// 0x44E00510 : 0x00000001
// CLKSEL Register
// 31-2 Reserved
// 1-0 CLKSEL : Selects the Mux select line for TIMERx
// clock [warm reset insensitive]
// 0x0 = SEL1 : Select TCLKIN clock
// * 0x1 = SEL2 : Select CLK_M_OSC clock
// 0x2 = SEL3 : Select CLK_32KHZ clock
// 0x3 = SEL4 : Reserved
SBBO r17, r11, OFFSET_CLKSEL_TIMER5_CLK, 4 // *(r11:CM_DPLL_BASE + OFFSET_CLKSEL_TIMER5_CLK) = r17:0x00
// 0x44E00518 : 0x00000001
SBBO r17, r11, OFFSET_CLKSEL_TIMER6_CLK, 4 // *(r11:CM_DPLL_BASE + OFFSET_CLKSEL_TIMER6_CLK) = r17:0x00
// 0x44E0051c : 0x00000000
SBBO r17, r11, OFFSET_CLKSEL_TIMER7_CLK, 4 // *(r11:CM_DPLL_BASE + OFFSET_CLKSEL_TIMER7_CLK) = r17:0x00
// 0x44E00504 : 0x00000000
// CLKSEL Register
// 31-2 Reserved
// 1-0 CLKSEL : Selects the Mux select line for TIMERx
// clock [warm reset insensitive]
// * 0x0 = SEL1 : Select TCLKIN clock
// 0x1 = SEL2 : Select CLK_M_OSC clock
// 0x2 = SEL3 : Select CLK_32KHZ clock
// 0x3 = SEL4 : Reserved
// we set TLDRs
MOV r0, 0xffffffff - 1200000 + 1 // target frequency is 10Hz
SBBO r0, r12, OFFSET_TLDR, 4 // *(r12:DMTIMER4_BASE + OFFSET_TLDR) = r0:
// 0x48044040 :
SBBO r17, r13, OFFSET_TLDR, 4 // *(r13:DMTIMER5_BASE + OFFSET_TLDR) = r17:0x0
// 0x48046040 : 0x00000000
MOV r0, 0xfffffffe //
SBBO r0, r14, OFFSET_TLDR, 4 // *(r14:DMTIMER6_BASE + OFFSET_TLDR) = r0:0xfffffffe
// 0x48047040 : 0xfffffffe
SBBO r0, r15, OFFSET_TLDR, 4 // *(r15:DMTIMER7_BASE + OFFSET_TLDR) = r0:0xfffffffe
// 0x4804a040 : 0xfffffffe
// TLDR Register
// 31-0 LOAD_VALUE : Timer counter value loaded
// on overflow in auto-reload mode or on TTGR write access
// we set TCRRs to initial values as same value with TLDR
MOV r0, 0xffffffff - 1200000 + 1 // target frequency is 10Hz
SBBO r0, r12, OFFSET_TCRR, 4 // *(r12:DMTIMER4_BASE + OFFSET_TCRR) = r0:
// 0x4804403c :
SBBO r17, r13, OFFSET_TCRR, 4 // *(r13:DMTIMER5_BASE + OFFSET_TCRR) = r17:0x0
// 0x4804603c : 0x00000000
MOV r0, 0xfffffffe //
SBBO r0, r14, OFFSET_TCRR, 4 // *(r14:DMTIMER6_BASE + OFFSET_TCRR) = r0:0xfffffffe
// 0x4804703c : 0xfffffffe
SBBO r0, r15, OFFSET_TCRR, 4 // *(r15:DMTIMER7_BASE + OFFSET_TCRR) = r0:0xfffffffe
// 0x4804a03c : 0xfffffffe
// TCRR Register
// 31-0 TIMER_COUNTER R/W 0h Value of TIMER counter
// we clear TCAR1s and TCAR2s
SBBO r17, r12, OFFSET_TCAR1, 4 // *(r12:DMTIMER4_BASE + OFFSET_TCAR1) = r17:0x00000000
// 0x48044050 : 0x00000000
SBBO r17, r13, OFFSET_TCAR1, 4 // *(r13:DMTIMER5_BASE + OFFSET_TCAR1) = r17:0x00000000
// 0x48046050 : 0x00000000
SBBO r17, r14, OFFSET_TCAR1, 4 // *(r14:DMTIMER6_BASE + OFFSET_TCAR1) = r17:0x00000000
// 0x48047050 : 0x00000000
SBBO r17, r15, OFFSET_TCAR1, 4 // *(r15:DMTIMER7_BASE + OFFSET_TCAR1) = r17:0x00000000
// 0x4804a050 : 0x00000000
// TCAR1 Register
// 31-0 CAPTURED_VALUE : Timer counter value
// captured on an external event trigger
SBBO r17, r12, OFFSET_TCAR2, 4 // *(r12:DMTIMER4_BASE + OFFSET_TCAR2) = r17:0x00000000
// 0x48044058 : 0x00000000
SBBO r17, r13, OFFSET_TCAR2, 4 // *(r13:DMTIMER5_BASE + OFFSET_TCAR2) = r17:0x00000000
// 0x48046058 : 0x00000000
SBBO r17, r14, OFFSET_TCAR2, 4 // *(r14:DMTIMER6_BASE + OFFSET_TCAR2) = r17:0x00000000
// 0x48047058 : 0x00000000
SBBO r17, r15, OFFSET_TCAR2, 4 // *(r15:DMTIMER7_BASE + OFFSET_TCAR2) = r17:0x00000000
// 0x4804a058 : 0x00000000
// TCAR2 Register
// 31-0 CAPTURED_VALUE : Timer counter value
// captured on an external event trigger
MOV r0, 0x0000007 //
SBBO r0, r12, OFFSET_IRQENABLE_CLR, 4 // *(r12:DMTIMER4_BASE + OFFSET_IRQENABLE_CLR) = r0:0x07
// 0x48044030 : 0x07
SBBO r0, r13, OFFSET_IRQENABLE_CLR, 4 // *(r13:DMTIMER5_BASE + OFFSET_IRQENABLE_CLR) = r0:0x07
// 0x48046030 : 0x07
SBBO r0, r14, OFFSET_IRQENABLE_CLR, 4 // *(r14:DMTIMER6_BASE + OFFSET_IRQENABLE_CLR) = r0:0x07
// 0x48047030 : 0x07
SBBO r0, r15, OFFSET_IRQENABLE_CLR, 4 // *(r15:DMTIMER7_BASE + OFFSET_IRQENABLE_CLR) = r0:0x07
// 0x4804a030 : 0x07
// IRQENABLE_CLR Register
// 31-3 RESERVED R 0h
// 2 TCAR_EN_FLAG : IRQ enable for Capture
// 0h = IRQ event is disabled
// 1h = Clear IRQ enable
// 1 OVF_EN_FLAG : IRQ enable for Overflow
// 0h = IRQ event is disabled
// 1h = Clear IRQ enable
// 0 MAT_EN_FLAG : IRQ enable for Match
// 0h = IRQ event is disabled
// 1h = Clear IRQ enable
MOV r0, 0x00000004 //
SBBO r0, r13, OFFSET_IRQENABLE_SET, 4 // *(r13:DMTIMER5_BASE + OFFSET_IRQENABLE_SET) = r0:0x04
// 0x4804602c : 0x00000004
MOV r0, 0x00000002 //
SBBO r0, r14, OFFSET_IRQENABLE_SET, 4 // *(r14:DMTIMER6_BASE + OFFSET_IRQENABLE_SET) = r0:0x02
// 0x4804702c : 0x00000002
// IRQENABLE_SET Register
// 31-3 RESERVED
// 2 TCAR_EN_FLAG : IRQ enable for Capture
// 0h = IRQ event is disabled
// 1h = IRQ event is enabled
// 1 OVF_EN_FLAG : IRQ enable for Overflow
// 0h = IRQ event is disabled
// 1h = IRQ event is enabled
// 0 MAT_EN_FLAG : IRQ enable for Match
// 0h = IRQ event is disabled
// 1h = IRQ event is enabled
MOV r0, 0x00000007 //
SBBO r0, r12, OFFSET_IRQSTATUS, 4 // *(r12:DMTIMER4_BASE + OFFSET_IRQSTATUS) = r0:0x00000007
// 0x48044028 : 0x00000007
SBBO r0, r13, OFFSET_IRQSTATUS, 4 // *(r13:DMTIMER5_BASE + OFFSET_IRQSTATUS) = r0:0x00000007
// 0x48046028 : 0x00000007
SBBO r0, r14, OFFSET_IRQSTATUS, 4 // *(r14:DMTIMER6_BASE + OFFSET_IRQSTATUS) = r0:0x00000007
// 0x48047028 : 0x00000007
SBBO r0, r15, OFFSET_IRQSTATUS, 4 // *(r15:DMTIMER7_BASE + OFFSET_IRQSTATUS) = r0:0x00000007
// 0x4804a028 : 0x00000007
// IRQSTATUS Register
// 31-3 RESERVED
// 2 TCAR_IT_FLAG : IRQ status for Capture
// 0h = No event pending
// 1h = IRQ event pending
// 1 OVF_IT_FLAG : IRQ status for Overflow
// 0h = No event pending
// 1h = IRQ event pending
// 0 MAT_IT_FLAG : IRQ status for Match
// 0h = No event pending
// 1h = IRQ event pending
// we start TIMER4 as 1Hz signal wave output
MOV r0, 0x00001403 // TIMER4 is used to make 10Hz clock out.
SBBO r0, r12, OFFSET_TCLR, 4 // *(r12:DMTIMER4_BASE + OFFSET_TCLR) = r0:0x00001403
// 0x48044038 : 0x00001403
// 0000 0000 0000 0000 0001 0100 0000 0011
// TCLR Register
// 0 ST : In the case of one-shot mode selected (AR = 0),
// this bit is automatically reset by internal logic
// when the counter is overflowed.
// 0h (R) = Stop timeOnly the counter is frozen
// * 1h = Start timer
MOV r0, 1 // it means notification "WAITING 1st INTERRUPT"
SBCO r0, CONST_PRUSHAREDRAM, 0x00, 4 // *(CONST_PRUSHAREDRAM + 0) = r0:0x00000001
MOV r31.b0, PRU0_ARM_INTERRUPT+16 // we send notification to Host
// we start TIMER6 as one-shot timer for phase syncronization between TIMER6 and TIMER7.
MOV r3, 0x00001403 // TCLR in TIMER6 value to re-start TIMER6
MOV r4, 0x00001483 // TCLR in TIMER7 value to start TIMER7
MOV r0, 0x00001401 // TIMER6 is used to divide TCLKIN frequency by 4 as one-shot timer
SBBO r0, r14, OFFSET_TCLR, 4 // *(r14:DMTIMER6_BASE + OFFSET_TCLR) = r0:0x00001401
// 0x48048038 : 0x00001401
// 0000 0000 0000 0000 0001 0100 0000 0001
// TCLR Register
// 0 ST : In the case of one-shot mode selected (AR = 0),
// this bit is automatically reset by internal logic
// when the counter is overflowed.
// 0h (R) = Stop timeOnly the counter is frozen
// * 1h = Start timer
// start timing of timer may compete with level transition of TCLKIN as race condition.
// in order to synchronize phase between TIMER6 and TIMER7, we re-start TIMER6 and start TIMER7 with TIMER6 OVF timing.
// since TIMER6 has started as one-shot, TIMER6 will stop automatically when OVF is occurred.
WAIT_TIMER6_1ST_OVF:
// we wait for OVF_IT_FLAG of TIMER6 to be set
LBBO r0, r14, OFFSET_IRQSTATUS, 4 // r0 = *(r14:DMTIMER6_BASE + OFFSET_IRQSTATUS)
// 0x48047028 :
AND r0, r0, 0x02 //
QBEQ WAIT_TIMER6_1ST_OVF, r0, 0x00 // if (r0 &amp;amp;amp;amp;amp; 0x02) == 0x00 then goto WAIT_TIMER6_1ST_OVF
// re-start both TIMER6 and TIMER7
// TIMER6 is used to make positive phase wave which divided TCLKIN by 4.
SBBO r3, r14, OFFSET_TCLR, 4 // *(r14:DMTIMER6_BASE + OFFSET_TCLR) = r3:0x00001403
// TIMER7 is used to make negative phase wave which divided TCLKIN by 4.
SBBO r4, r15, OFFSET_TCLR, 4 // *(r15:DMTIMER7_BASE + OFFSET_TCLR) = r4:0x00001483
// since interrupt of TIMER6 has served, we disable TIMER6 OVF interrupt.
MOV r0, 0x00000007 //
SBBO r0, r14, OFFSET_IRQENABLE_CLR, 4 // *(r14:DMTIMER6_BASE + OFFSET_IRQENABLE_CLR) = r0:0x07
// 0x48047030 : 0x07
SBBO r0, r14, OFFSET_IRQSTATUS, 4 // *(r14:DMTIMER6_BASE + OFFSET_IRQSTATUS) = r0:0x00000007
// 0x48047028 : 0x00000007
MOV r0, 2 // it means notification "START CAPTURE"
SBCO r0, CONST_PRUSHAREDRAM, 0x00, 4 // *(CONST_PRUSHAREDRAM + 0) = r0:0x00000002
MOV r31.b0, PRU0_ARM_INTERRUPT+16 // we send notification to Host
// we start TIMER5
MOV r4, r17 // we initialize r4:previus_CAP_value with r17:0x000000000.
MOV r0, 0x00004103 // TIMER5 is used to capture frequensy of TCLKIN using feedback TIMER4 signal.
SBBO r0, r13, OFFSET_TCLR, 4 // *(r13:DMTIMER5_BASE + OFFSET_TCLR) = r0:0x4103
// 0x44E00518 : 0x00004103
// 0000 0000 0000 0000 0100 0001 0000 0011
// TCLR Register
// 0 ST : In the case of one-shot mode selected (AR = 0),
// this bit is automatically reset by internal logic
// when the counter is overflowed.
// 0h (R) = Stop timeOnly the counter is frozen
// * 1h = Start timer
CAPTURE_LOOP:
// we wait for TCAR_IT_FLAG of TIMER5 to be set
LBBO r0, r13, OFFSET_IRQSTATUS, 4 // r0 = *(r13:DMTIMER6_BASE + OFFSET_IRQSTATUS)
QBEQ CAPTURE_LOOP, r0, 0x00 // if (r0 &amp;amp;amp;amp;amp; 0x04) == 0x00 then goto CAPTURE_LOOP
// we clear all interrupt flsgs
MOV r1, 0x07 //
SBBO r1, r13, OFFSET_IRQSTATUS, 4 // *(r13:DMTIMER5_BASE + OFFSET_IRQSTATUS) = r1:0x00000007
// 0x48046028 :
AND r0, r0, 0x04 //
QBEQ CAPTURE_LOOP, r0, 0x00 // if (r0 &amp;amp;amp;amp;amp; 0x04) == 0x00 then goto CAPTURE_LOOP
WAIT_CAPTUER:
LBBO r0, r13, OFFSET_TCAR1, 4 // r0 = *(r13:DMTIMER5_BASE + OFFSET_TCAR1)
// 0x48046050 : 0x00000000
QBEQ WAIT_CAPTUER, r0, r4 // if r0:current_capture == r4:previous_capture
// then goto CAPTURE_LOOP
// in my investigation, in order to capture the TCRR into TCAR1,
// more tclkin clocks are required after TCAR_IT_FLAG is set.
SUB r1, r0, r4 //
SBCO r1, CONST_PRUSHAREDRAM, 0x04, 4 // *(CONST_PRUSHAREDRAM + 4) = r1
MOV r4, r0 // update r4:previus_CAP_value with r0:new_CAPTURE value
MOV r0, 3 // it means notification "CAPTUERED" to host
SBCO r0, CONST_PRUSHAREDRAM, 0x00, 4 // *(CONST_PRUSHAREDRAM + 0) = r0:0x00000003
MOV r31.b0, PRU0_ARM_INTERRUPT+16 // we send notification to Host
JMP CAPTURE_LOOP
同じくPRUのincludeファイル。
// dtimer.hp
#ifndef _PRU_PWM_
#define _PRU_PWM_
// ***************************************
// * Global Macro definitions *
// ***************************************
#define PRU0_PRU1_INTERRUPT 17
#define PRU1_PRU0_INTERRUPT 18
#define PRU0_ARM_INTERRUPT 19
#define PRU1_ARM_INTERRUPT 20
#define ARM_PRU0_INTERRUPT 21
#define ARM_PRU1_INTERRUPT 22
#define CONST_PRUCFG C4
#define CONST_PRUD0RAM C24
#define CONST_PRUD1RAM C25
#define CONST_PRUSHAREDRAM C28
#define CONST_DDR C31
#define PRU0_CTRL_REG_BASE 0x00022000
#define OFFSET_PRU_CONTROL_REG 0x00
#define OFFSET_PRU_STATUS_REG 0x04
// 5 SUB_MWAIT
// Status bit for wait state.
// 0 = Ready for Transaction
// 1 = Wait until 0
// 4 STANDBY_INIT
// 1 = Initiate standby sequence.
// 0 = Ready for Transaction
// 3-2 STANDBY_MODE
// 0h = Force standby mode: Initiator unconditionally
// in standby (standby = 1).
// 1h = No standby mode: Initiator unconditionally out
// of standby (standby = 0).
// 2h = Smart standby mode: Standby requested by initiator
// depending on internal conditions.
// 3h = Reserved.
// 1-0 IDLE_MODE
// 0h = Force-idle mode.
// 1h = No-idle mode.
// 2h = Smart-idle mode.
// 3h = Reserved.
#define OFFSET_PRU_WAKEUP_EN_REG 0x08
#define OFFSET_PRU_CYCLE_REG 0x0C
#define OFFSET_PRU_STALL_REG 0x10
#define OFFSET_PRU_CTBIR0_REG 0x20
// 23-16 C25_BLK_INDEX (PRU1/0 Local Data)
// 7-0 C24_BLK_INDEX (PRU0/1 Local Data)
#define OFFSET_PRU_CTBIR1_REG 0x24
// 23-16 C27_BLK_INDEX (MII_RT (local))
// 7-0 C26_BLK_INDEX (IEP (local))
#define OFFSET_PRU_CTPPR0_REG 0x28
// 31-16 C29_POINTER (TPCC)
// 15-0 C28_POINTER (Shared PRU RAM (local))
#define OFFSET_PRU_CTPPR1_REG 0x2C
// 31-16 C31_POINTER (EMIF0 DDR Base)
// 15-0 C30_POINTER (L3 OCMC0)
#define PRU_ICSS_CFG_REG_BASE 0x00026000
#define OFFSET_CFG_REVID_REG 0x00
#define OFFSET_CFG_SYSCFG_REG 0x04
// 5 SUB_MWAIT
// Status bit for wait state.(Read only)
// 0 = Ready for Transaction
// 1 = Wait until 0
// 4 STANDBY_INIT
// 1 = Initiate standby sequence.
// 0 = Enable OCP master ports.
// 3-2 STANDBY_MODE
// 0h = Force standby mode: Initiator unconditionally
// in standby (standby = 1).
// 1h = No standby mode: Initiator unconditionally out
// of standby (standby = 0).
// 2h = Smart standby mode: Standby requested by initiator
// depending on internal conditions.
// 3h = Reserved.
// 1-0 IDLE_MODE
// 0h = Force-idle mode.
// 1h = No-idle mode.
// 2h = Smart-idle mode.
// 3h = Reserved.
#define OFFSET_CFG_GPCFG0_REG 0x08
// 25 PRU0_GPO_SH_SEL
// Defines which shadow register is currently getting used
// for GPO shifting.(Read only)
// 0 = gpo_sh0 is selected
// 1 = gpo_sh1 is selected
// 24-20 PRU0_GPO_DIV1
// Divisor value (divide by PRU0_GPO_DIV1 + 1).
// 0h = div 1.0.
// 1h = div 1.5.
// 2h = div 2.0.
// ..
// 1eh = div 16.0.
// 1fh = reserved.
// 19-15 PRU0_GPO_DIV0
// Same as PRU0_GPO_DIV1
// 14 PRU0_GPO_MODE
// 0 = Direct output mode
// 1 = Serial output mode
// 13 PRU0_GPI_SB
// Start Bit event for 28-bit shift mode.
// PRU0_GPI_SB (pru0_r31_status[29]) is set when first capture
// of a 1 on pru0_r31_status[0].
// Read 1: Start Bit event occurred.
// Read 0: Start Bit event has not occurred.
// Write 1: Will clear PRU0_GPI_SB and clear the whole
// shift register.
// Write 0: No Effect.
// 12-8 PRU0_GPI_DIV1
// Divisor value (divide by PRU0_GPI_DIV1 + 1).
// 0h = div 1.0.
// 1h = div 1.5.
// 2h = div 2.0.
// ..
// 1eh = div 16.0.
// 1fh = reserved.
// 7-3 PRU0_GPI_DIV0
// Same as PRU0_GPI_DIV1
// 2 PRU0_GPI_CLK_MODE
// Parallel 16-bit capture mode clock edge.
// 0 = Use the positive edge of pru0_r31_status[16]
// 1 = Use the negative edge of pru0_r31_status[16]
// 1-0 PRU0_GPI_MODE
// 0h = Direct connection mode.
// 1h = 16-bit parallel capture mode.
// 2h = 28-bit shift mode.
// 3h = Mii_rt mode.
#define OFFSET_CFG_GPCFG1_REG 0x0C
// Fields are Same as GPCFG0_REG, buf GPCFG1_REG is for PRU1.
#define OFFSET_CFG_CGR_REG 0x10
#define OFFSET_CFG_ISRP_REG 0x14
#define OFFSET_CFG_ISP_REG 0x18
#define OFFSET_CFG_IESP_REG 0x1C
#define OFFSET_CFG_IECP_REG 0x20
#define OFFSET_CFG_PMAO_REG 0x28
#define OFFSET_CFG_MII_RT_REG 0x2C
#define OFFSET_CFG_IEPCLK_REG 0x30
#define OFFSET_CFG_SPP_REG 0x34
#define OFFSET_CFG_PIN_MX_REG 0x40
// Address for the Constant table Block Index Register (CTBIR)
#define CTBIR 0x22020
// Address for the Constant table Programmable Pointer Register 0(CTPPR_0)
#define CTPPR_0 0x22028
// Address for the Constant table Programmable Pointer Register 1(CTPPR_1)
#define CTPPR_1 0x2202C
// CM_PER related registers define
#define CM_PER_BASE 0x44E00000 // Clock Module Peripheral Registers
#define OFFSET_CM_PER_L4LS_CLKSTCTRL 0x000
#define OFFSET_CM_PER_L3S_CLKSTCTRL 0x004
#define OFFSET_CM_PER_L3_CLKSTCTRL 0x00C
#define OFFSET_CM_PER_CPGMAC0_CLKCTRL 0x014
#define OFFSET_CM_PER_LCDC_CLKCTRL 0x018
#define OFFSET_CM_PER_USB0_CLKCTRL 0x01C
#define OFFSET_CM_PER_TPTC0_CLKCTRL 0x024
#define OFFSET_CM_PER_EMIF_CLKCTRL 0x028
#define OFFSET_CM_PER_OCMCRAM_CLKCTRL 0x02C
#define OFFSET_CM_PER_GPMC_CLKCTRL 0x030
#define OFFSET_CM_PER_MCASP0_CLKCTRL 0x034
#define OFFSET_CM_PER_UART5_CLKCTRL 0x038
#define OFFSET_CM_PER_MMC0_CLKCTRL 0x03C
#define OFFSET_CM_PER_ELM_CLKCTRL 0x040
#define OFFSET_CM_PER_I2C2_CLKCTRL 0x044
#define OFFSET_CM_PER_I2C1_CLKCTRL 0x048
#define OFFSET_CM_PER_SPI0_CLKCTRL 0x04C
#define OFFSET_CM_PER_SPI1_CLKCTRL 0x050
#define OFFSET_CM_PER_L4LS_CLKCTRL 0x060
#define OFFSET_CM_PER_MCASP1_CLKCTRL 0x068
#define OFFSET_CM_PER_UART1_CLKCTRL 0x06C
#define OFFSET_CM_PER_UART2_CLKCTRL 0x070
#define OFFSET_CM_PER_UART3_CLKCTRL 0x074
#define OFFSET_CM_PER_UART4_CLKCTRL 0x078
#define OFFSET_CM_PER_TIMER7_CLKCTRL 0x07C // timer7
#define OFFSET_CM_PER_TIMER2_CLKCTRL 0x080
#define OFFSET_CM_PER_TIMER3_CLKCTRL 0x084
#define OFFSET_CM_PER_TIMER4_CLKCTRL 0x088 // timer4
#define OFFSET_CM_PER_GPIO1_CLKCTRL 0x0AC
#define OFFSET_CM_PER_GPIO2_CLKCTRL 0x0B0
#define OFFSET_CM_PER_GPIO3_CLKCTRL 0x0B4
#define OFFSET_CM_PER_TPCC_CLKCTRL 0x0BC
#define OFFSET_CM_PER_DCAN0_CLKCTRL 0x0C0
#define OFFSET_CM_PER_DCAN1_CLKCTRL 0x0C4
#define OFFSET_CM_PER_EPWMSS1_CLKCTRL 0x0CC // epwmss1
#define OFFSET_CM_PER_EPWMSS0_CLKCTRL 0x0D4 // epwmss0
#define OFFSET_CM_PER_EPWMSS2_CLKCTRL 0x0D8 // epwmss2
#define OFFSET_CM_PER_L3_INSTR_CLKCTRL 0x0DC
#define OFFSET_CM_PER_L3_CLKCTRL 0x0E0
#define OFFSET_CM_PER_IEEE5000_CLKCTRL 0x0E4
#define OFFSET_CM_PER_PRU_ICSS_CLKCTRL 0x0E8 // pru_icss
#define OFFSET_CM_PER_TIMER5_CLKCTRL 0x0EC // timer5
#define OFFSET_CM_PER_TIMER6_CLKCTRL 0x0F0 // timer6
#define OFFSET_CM_PER_MMC1_CLKCTRL 0x0F4
#define OFFSET_CM_PER_MMC2_CLKCTRL 0x0F8
#define OFFSET_CM_PER_TPTC1_CLKCTRL 0x0FC
#define OFFSET_CM_PER_TPTC2_CLKCTRL 0x100
#define OFFSET_CM_PER_SPINLOCK_CLKCTRL 0x10C
#define OFFSET_CM_PER_MAILBOX0_CLKCTRL 0x110
#define OFFSET_CM_PER_L4HS_CLKSTCTRL 0x11C
#define OFFSET_CM_PER_L4HS_CLKCTRL 0x120
#define OFFSET_CM_PER_OCPWP_L3_CLKSTCTRL 0x12C
#define OFFSET_CM_PER_OCPWP_CLKCTRL 0x130
#define OFFSET_CM_PER_PRU_ICSS_CLKSTCTRL 0x140 // pru_icss_clk
#define OFFSET_CM_PER_CPSW_CLKSTCTRL 0x144
#define OFFSET_CM_PER_LCDC_CLKSTCTRL 0x148
#define OFFSET_CM_PER_CLKDIV32K_CLKCTRL 0x14C
#define OFFSET_CM_PER_CLK_24MHZ_CLKSTCTRL 0x150
// CM_DPLL related registers define
#define CM_DPLL_BASE 0x44E00500 // Clock Module PLL Registers
#define OFFSET_CLKSEL_TIMER7_CLK 0x04 // Selects the Mux select line for TIMER7 clock
#define OFFSET_CLKSEL_TIMER2_CLK 0x08 // Selects the Mux select line for TIMER2 clock
#define OFFSET_CLKSEL_TIMER3_CLK 0x0C // Selects the Mux select line for TIMER3 clock
#define OFFSET_CLKSEL_TIMER4_CLK 0x10 // Selects the Mux select line for TIMER4 clock
#define OFFSET_CM_MAC_CLKSEL 0x14 // Selects the clock divide ration for MII clock
#define OFFSET_CLKSEL_TIMER5_CLK 0x18 // Selects the Mux select line for TIMER5 clock
#define OFFSET_CLKSEL_TIMER6_CLK 0x1C // Selects the Mux select line for TIMER6 clock
#define OFFSET_ CM_CPTS_RFT_CLKSEL 0x20 // Selects the Mux select line for CPTS RFT clock
#define OFFSET_CLKSEL_TIMER1MS_CLK 0x28 // Selects the Mux select line for TIMER1 clock
#define OFFSET_CLKSEL_GFX_FCLK 0x2C // Selects the divider value for GFX clock
#define OFFSET_CLKSEL_PRU_ICSS_OCP_CLK 0x30 // Controls the Mux select line for PRU-ICSS OCP clock
#define OFFSET_CLKSEL_LCDC_PIXEL_CLK 0x34 // Controls the Mux select line for LCDC PIXEL clock
#define OFFSET_CLKSEL_WDT1_CLK 0x38 // Selects the Mux select line for Watchdog1 clock
#define OFFSET_CLKSEL_GPIO0_DBCLK 0x3C // Selects the Mux select line for GPIO0 debounce clock
// PWMSS related registers define
#define PWM_Subsystem_0_BASE 0x48300000 // PWMSS0 Configuration Registers
#define PWM_Subsystem_1_BASE 0x48302000 // PWMSS1 Configuration Registers
#define PWM_Subsystem_2_BASE 0x48304000 // PWMSS2 Configuration Registers
#define OFFSET_IDVER 0x00 // IDVER IP Revision Register
#define OFFSET_SYSCONFIG 0x04 // SYSCONFIG System Configuration Register Section 15.1.2.2
#define OFFSET_CLKCONFIG 0x08 // CLKCONFIG Clock Configuration Register Section 15.1.2.3
#define OFFSET_CLKSTATUS 0x0C // CLKSTATUS Clock Status Register Section 15.1.2.4
// ePWM related registers define
#define ePWM0_BASE 0x48300200 // PWMSS ePWM0 Registers
#define ePWM1_BASE 0x48302200 // PWMSS ePWM1 Registers
#define ePWM2_BASE 0x48304200 // PWMSS ePWM2 Registers
#define OFFSET_TBCTL 0x00 // TBCTL Time-Base Control Register Section 15.2.4.1
#define OFFSET_TBSTS 0x02 // TBSTS Time-Base Status Register Section 15.2.4.2
#define OFFSET_TBPHSHR 0x04 // TBPHSHR Extension for HRPWM Phase Register Section 15.2.4.3
#define OFFSET_TBPHS 0x06 // TBPHS Time-Base Phase Register Section 15.2.4.4
#define OFFSET_TBCNT 0x08 // TBCNT Time-Base Counter Register Section 15.2.4.5
#define OFFSET_TBPRD 0x0A // TBPRD Time-Base Period Register Section 15.2.4.6
#define OFFSET_CMPCTL 0x0E // CMPCTL Counter-Compare Control Register Section 15.2.4.7
#define OFFSET_CMPAHR 0x10 // CMPAHR Extension for HRPWM Counter-Compare A Register Section 15.2.4.8
#define OFFSET_CMPA 0x12 // CMPA Counter-Compare A Register Section 15.2.4.9
#define OFFSET_CMPB 0x14 // CMPB Counter-Compare B Register Section 15.2.4.10
#define OFFSET_AQCTLA 0x16 // AQCTLA Action-Qualifier Control Register for Output A (EPWMxA) Section 15.2.4.11
#define OFFSET_AQCTLB 0x18 // AQCTLB Action-Qualifier Control Register for Output B (EPWMxB) Section 15.2.4.12
#define OFFSET_AQSFRC 0x1A // AQSFRC Action-Qualifier Software Force Register Section 15.2.4.13
#define OFFSET_AQCSFRC 0x1C // AQCSFRC Action-Qualifier Continuous S/W Force Register Set Section 15.2.4.14
#define OFFSET_DBCTL 0x1E // DBCTL Dead-Band Generator Control Register Section 15.2.4.15
#define OFFSET_DBRED 0x20 // DBRED Dead-Band Generator Rising Edge Delay Count Register Section 15.2.4.16
#define OFFSET_DBFED 0x22 // DBFED Dead-Band Generator Falling Edge Delay Count Register Section 15.2.4.17
#define OFFSET_TZSEL 0x24 // TZSEL Trip-Zone Select Register Section 15.2.4.18
#define OFFSET_TZCTL 0x28 // TZCTL Trip-Zone Control Register Section 15.2.4.19
#define OFFSET_TZEINT 0x2A // TZEINT Trip-Zone Enable Interrupt Register Section 15.2.4.20
#define OFFSET_TZFLG 0x2C // TZFLG Trip-Zone Flag Register Section 15.2.4.21
#define OFFSET_TZCLR 0x2E // TZCLR Trip-Zone Clear Register Section 15.2.4.22
#define OFFSET_TZFRC 0x30 // TZFRC Trip-Zone Force Register Section 15.2.4.23
#define OFFSET_ETSEL 0x32 // ETSEL Event-Trigger Selection Register Section 15.2.4.24
#define OFFSET_ETPS 0x34 // ETPS Event-Trigger Pre-Scale Register Section 15.2.4.25
#define OFFSET_ETFLG 0x36 // ETFLG Event-Trigger Flag Register Section 15.2.4.26
#define OFFSET_ETCLR 0x38 // ETCLR Event-Trigger Clear Register Section 15.2.4.27
#define OFFSET_ETFRC 0x3A // ETFRC Event-Trigger Force Register Section 15.2.4.28
#define OFFSET_PCCTL 0x3C // PCCTL PWM-Chopper Control Register Section 15.2.4.29
#define OFFSET_HRCNFG 0xC0 // HRCNFG HRPWM configuration register (HRCNFG) Section 15.2.4.30
// eCAP related registers define
#define eCAP0_BASE 0x48300100 // PWMSS eCAP0 Registers
#define eCAP1_BASE 0x48302100 // PWMSS eCAP1 Registers
#define eCAP2_BASE 0x48304100 // PWMSS eCAP2 Registers
#define OFFSET_TSCTR 0x00 // TSCTR Time-Stamp Counter Register Section 15.3.4.1.1
#define OFFSET_CTRPHS 0x04 // CTRPHS Counter Phase Offset Value Register Section 15.3.4.1.2
#define OFFSET_CAP1 0x08 // CAP1 Capture 1 Register Section 15.3.4.1.3
#define OFFSET_CAP2 0x0C // CAP2 Capture 2 Register Section 15.3.4.1.4
#define OFFSET_CAP3 0x10 // CAP3 Capture 3 Register Section 15.3.4.1.5
#define OFFSET_CAP4 0x14 // CAP4 Capture 4 Register Section 15.3.4.1.6
#define OFFSET_ECCTL1 0x28 // ECCTL1 Capture Control Register 1 Section 15.3.4.1.7
#define OFFSET_ECCTL2 0x2A // ECCTL2 Capture Control Register 2 Section 15.3.4.1.8
#define OFFSET_ECEINT 0x2C // ECEINT Capture Interrupt Enable Register Section 15.3.4.1.9
#define OFFSET_ECFLG 0x2E // ECFLG Capture Interrupt Flag Register Section 15.3.4.1.10
#define OFFSET_ECCLR 0x30 // ECCLR Capture Interrupt Clear Register Section 15.3.4.1.11
#define OFFSET_ECFRC 0x32 // ECFRC Capture Interrupt Force Register Section 15.3.4.1.12
#define OFFSET_REVID 0x5C // REVID Revision ID Register Section 15.4.3.25
// eQEP related registers defines
#define eQEP0_BASE 0x48300180 // PWMSS eQEP0 Registers
#define eQEP1_BASE 0x48302180 // PWMSS eQEP1 Registers
#define eQEP2_BASE 0x48304180 // PWMSS eQEP2 Registers
// Timer related registers
#define DMTIMER0_BASE 0x44E05000 // DMTimer0 Registers
#define DMTIMER1_1MS_BASE 0x44E31000 // DMTimer1 1ms Registers
#define DMTIMER4_BASE 0x48044000 // DMTimer4 Registers
#define DMTIMER5_BASE 0x48046000 // DMTimer5 Registers
#define DMTIMER6_BASE 0x48048000 // DMTimer6 Registers
#define DMTIMER7_BASE 0x4804A000 // DMTimer7 Registers
#define OFFSET_TIDR 0x00 // Identification Register
#define OFFSET_TIOCP_CFG 0x10 // Timer OCP Configuration Register
#define OFFSET_IRQ_EOI 0x20 // Timer IRQ End-of-Interrupt Register
#define OFFSET_IRQSTATUS_RAW 0x24 // Timer Status Raw Register
#define OFFSET_IRQSTATUS 0x28 // Timer Status Register
#define OFFSET_IRQENABLE_SET 0x2C // Timer Interrupt Enable Set Register
#define OFFSET_IRQENABLE_CLR 0x30 // Timer Interrupt Enable Clear Register
#define OFFSET_IRQWAKEEN 0x34 // Timer IRQ Wakeup Enable Register Section
#define OFFSET_TCLR 0x38 // Timer Control Register Section
#define OFFSET_TCRR 0x3C // Timer Counter Register Section
#define OFFSET_TLDR 0x40 // Timer Load Register
#define OFFSET_TTGR 0x44 // Timer Trigger Register
#define OFFSET_TWPS 0x48 // Timer Write Posting Bits Register
#define OFFSET_TMAR 0x4C // Timer Match Register
#define OFFSET_TCAR1 0x50 // Timer Capture Register
#define OFFSET_TSICR 0x54 // Timer Synchronous Interface Control Register
#define OFFSET_TCAR2 0x58 // Timer Capture Register
#endif //_PRU_PWM_
PRUを実行管理するためのC言語プログラムは以下。
このプログラムはPRUプログラムをロードし実行し、100msec毎にPRUから割り込みをうけSHARED Memoryを介してキャプチャデータを取得してterminalに表示する。
/*
* dmtimer.c
*/
#include &amp;amp;amp;amp;lt;stdio.h&amp;amp;amp;amp;gt;
#include &amp;amp;amp;amp;lt;sys/mman.h&amp;amp;amp;amp;gt;
#include &amp;amp;amp;amp;lt;fcntl.h&amp;amp;amp;amp;gt;
#include &amp;amp;amp;amp;lt;errno.h&amp;amp;amp;amp;gt;
#include &amp;amp;amp;amp;lt;unistd.h&amp;amp;amp;amp;gt;
#include &amp;amp;amp;amp;lt;string.h&amp;amp;amp;amp;gt;
#include &amp;amp;amp;amp;lt;unistd.h&amp;amp;amp;amp;gt;
#include &amp;amp;amp;amp;lt;sched.h&amp;amp;amp;amp;gt;
#include "prussdrv.h"
#include &amp;amp;amp;amp;lt;pruss_intc_mapping.h&amp;amp;amp;amp;gt;
#define PRU_NUM 0
#define OFFSET_MEM0 0x00000000
#define OFFSET_MEM1 0x00002000
#define OFFSET_SHAREDRAM 0x00010000
/******************************************************************************
* Global Variable Definitions *
******************************************************************************/
static void *pruDataMem;
//static unsigned int *pruDataMem_int0; /*AM33XX_DATA 8KB RAM0*/
//static unsigned int *pruDataMem_int1; /*AM33XX_DATA 8KB RAM1*/
static unsigned int *sharedMem_int; /*AM33XX_SHARED 12KB RAM*/
//static unsigned int *currentBuffer_int;
/******************************************************************************
* Global Function Definitions *
******************************************************************************/
int main(int argc, char *argv[]) {
unsigned int ret;
struct sched_param sp;
int policy;
int result;
tpruss_intc_initdata pruss_intc_initdata = PRUSS_INTC_INITDATA;
/* change schduling policy to real-time */
policy = SCHED_FIFO; /* we can select either SCHED_RR or SCHED_FIFO as policy. */
sp.sched_priority = 1; /* we can select priority value within 1 to 99. */
result = sched_setscheduler(0, policy, &amp;amp;amp;amp;amp;sp);
if (result) {
printf("Unable to change schduling policy: sched_setscheduler = %d\n", result);
return (0);
}
/* Initialize the PRU */
prussdrv_init ();
/* Open PRU Interrupt */
ret = prussdrv_open(PRU_EVTOUT_0);
if (ret) {
printf("prussdrv_open open failed\n");
return (0);
}
/* Get the interrupt initialized */
prussdrv_pruintc_init(&amp;amp;amp;amp;amp;pruss_intc_initdata);
/* Execute example on PRU */
printf("\tINFO: Executing example.\r\n");
prussdrv_exec_program (PRU_NUM, "./dmtimer.bin");
prussdrv_map_prumem(PRUSS0_PRU0_DATARAM, &amp;amp;amp;amp;amp;pruDataMem);
sharedMem_int = (unsigned int*)(pruDataMem + OFFSET_SHAREDRAM );
while (1) {
/* Wait PRU0 interrupt event */
prussdrv_pru_wait_event (PRU_EVTOUT_0);
/* clear interrupt event */
prussdrv_pru_clear_event (PRU_EVTOUT_0, PRU0_ARM_INTERRUPT);
printf("[0] = %d, [1] = %d\n", sharedMem_int[0],sharedMem_int[1]);
if (sharedMem_int[0] &amp;amp;amp;amp;gt;= 2) break;
}
printf("pru is starting to capture.\n");
/* main loop*/
while (1) {
/* Wait PRU0 interrupt event */
prussdrv_pru_wait_event(PRU_EVTOUT_0);
/* clear interrupt event */
prussdrv_pru_clear_event(PRU_EVTOUT_0, PRU0_ARM_INTERRUPT);
printf("i=%dHz,o=%dHz\n"
, sharedMem_int[1] * 10
, sharedMem_int[1] * 10 / 4
);
}
/* Wait PRU0 interrupt event */
prussdrv_pru_wait_event(PRU_EVTOUT_0);
/* clear interrupt event */
prussdrv_pru_clear_event(PRU_EVTOUT_0, PRU0_ARM_INTERRUPT);
/* Disable PRU and close memory mapping. */
prussdrv_pru_disable(PRU_NUM);
prussdrv_exit();
return(0);
}
一応コンパイル済のソース一式を以下に格納しておく。
dmtimer_demo.tar.gz
ビルドは以下の手順で行う。
debian@beaglebone:~/pru/am335x_pru_package/pru_sw/dmtimer_demo$ export CROSS_COMPILE= debian@beaglebone:~/pru/am335x_pru_package/pru_sw/dmtimer_demo$ make clean debian@beaglebone:~/pru/am335x_pru_package/pru_sw/dmtimer_demo$ make debian@beaglebone:~/pru/am335x_pru_package/pru_sw/dmtimer_demo$ cd bin/ debian@beaglebone:~/pru/am335x_pru_package/pru_sw/dmtimer_demo/bin$ cp ./dmtimer.bin ../dmtimer/obj/ debian@beaglebone:~/pru/am335x_pru_package/pru_sw/dmtimer_demo/bin$ cd ../dmtimer/obj/ debian@beaglebone:~/pru/am335x_pru_package/pru_sw/dmtimer_demo/dmtimer/obj$ gcc dmtimer.o -L../../../app_loader/lib -lprussdrv -lpthread -o dmtimer.out debian@beaglebone:~/pru/am335x_pru_package/pru_sw/dmtimer_demo/dmtimer/obj$
このサンプルを実行するためにはTIMER4_OUT:P8_7とTIMER5_IN:P8_9を短絡する必要がある。
tclkinに適当な波形を与えるとその周波数を計測する。
今回は前述の24.576MHzオシレータ出力と、秋月で入手できる”1KHz~30MHzオシレータ LTC1799モジュール”を使う。
これらの測定信号をtclkinに入力し、TIMER5での周波数計測及びTIMER6/TIMER7のTimer出力に用いる。
回路図を以下に示す。
LTC1799モジュールはSETピンとVDDの間に設置したポテンショメータVR1の抵抗値により出力周波数を制御できるオシレータキット。
ジッタが大きいため精度を求める用途には向かないようだが、周波数レンジの広さと手軽さからとりあえずの用途に重宝する。
今回はVR1に多回転型半固定ボリューム50KΩを使う。
これも秋月で入手できる。
VR1を50KΩとした場合、V+が3.3Vの条件で約2MHz~20MHzの範囲の周波数を出力することができる。
もう少し低い周波数も欲しいのでVR1は100KΩとした方が良いが手持ちがなかった。
R1~R4はダンパ抵抗のつもりであるがちょっとテストするだけであるならば省略しても良いだろう。
今回この回路をブレッドボード上に組んだ。
SW1は実際にはジャンパを差し替えているだけで、tclkinへの入力信号を手動で切り替える。。
サンプルデモの実行は以下のように行う。
root権限が必要。
以下は24.576MHzオシレータをtclkinに入力した場合の結果。
debian@beaglebone:~/pru/am335x_pru_package/pru_sw/dmtimer_demo/dmtimer/obj$ sudo ./dmtimer.out
INFO: Executing example.
[0] = 2, [1] = 0
pru is starting to capture.
i=12287560Hz,o=3071890Hz
i=24575380Hz,o=6143845Hz
i=24575410Hz,o=6143852Hz
i=24575390Hz,o=6143847Hz
i=24575420Hz,o=6143855Hz
i=24575410Hz,o=6143852Hz
i=24575410Hz,o=6143852Hz
i=24575390Hz,o=6143847Hz
i=24575380Hz,o=6143845Hz
i=24575390Hz,o=6143847Hz
i=24575390Hz,o=6143847Hz
i=24575400Hz,o=6143850Hz
i=24575390Hz,o=6143847Hz
i=24575390Hz,o=6143847Hz
i=24575390Hz,o=6143847Hz
i=24575390Hz,o=6143847Hz
i=24575380Hz,o=6143845Hz
i=24575380Hz,o=6143845Hz
i=24575380Hz,o=6143845Hz
i=24575390Hz,o=6143847Hz
i=24575390Hz,o=6143847Hz
i=24575390Hz,o=6143847Hz
i=24575390Hz,o=6143847Hz
i=24575400Hz,o=6143850Hz
i=24575380Hz,o=6143845Hz
i=24575390Hz,o=6143847Hz
i=24575380Hz,o=6143845Hz
i=24575380Hz,o=6143845Hz
i=24575390Hz,o=6143847Hz
i=24575390Hz,o=6143847Hz
i=24575390Hz,o=6143847Hz
i=24575390Hz,o=6143847Hz
i=24575390Hz,o=6143847Hz
i=24575380Hz,o=6143845Hz
i=24575390Hz,o=6143847Hz
i=24575390Hz,o=6143847Hz
i=24575400Hz,o=6143850Hz
i=24575390Hz,o=6143847Hz
i=24575400Hz,o=6143850Hz
i=24575390Hz,o=6143847Hz
i=24575380Hz,o=6143845Hz
i=24575400Hz,o=6143850Hz
i=24575390Hz,o=6143847Hz
i=24575400Hz,o=6143850Hz
i=24575390Hz,o=6143847Hz
i=24575400Hz,o=6143850Hz
i=24575390Hz,o=6143847Hz
i=24575400Hz,o=6143850Hz
i=24575390Hz,o=6143847Hz
i=24575400Hz,o=6143850Hz
i=24575390Hz,o=6143847Hz
i=24575390Hz,o=6143847Hz
i=24575390Hz,o=6143847Hz
i=24575390Hz,o=6143847Hz
i=24575390Hz,o=6143847Hz
i=24575380Hz,o=6143845Hz
i=24575390Hz,o=6143847Hz
i=24575390Hz,o=6143847Hz
i=24575370Hz,o=6143842Hz
i=24575360Hz,o=6143840Hz
i=24575380Hz,o=6143845Hz
i=24575390Hz,o=6143847Hz
i=24575390Hz,o=6143847Hz
i=24575400Hz,o=6143850Hz
i=24575390Hz,o=6143847Hz
i=24575400Hz,o=6143850Hz
i=24575400Hz,o=6143850Hz
i=24575390Hz,o=6143847Hz
i=24575400Hz,o=6143850Hz
ユーザーランドのCプログラムはPRUからのキャプチャ結果を受け表示し続ける。
止める場合はCtrl-Cで止める。
i はtclkinへの入力周波数を示し、o はTIMER6/TIMER7への出力周波数を示す。
i の1の位は桁合わせのためで常に0が表示される。
o は単に計算で出しているだけであるので出力周波数を保証するものではない。
測定誤差は測定に用いている基準クロックの誤差と1クロック分のジッタ、測定する信号の1クロック分のジッタが含まれる。
今回の方式では測定する信号の1クロック分のジッタ誤差は周波数が低くなってくると支配的になってくるので、tclkinに入力する信号の周波数が低い程測定精度は悪くなる。
初回はエラーが発生しているが概ねそこそこの精度で測定できている。
同様にLTC1799モジュールの出力信号をtclkinに入力してやるとVR1を調整することにより出力結果が変わることが確認できる。
LTC1799モジュールはジッタが大きいというネット上の情報があるが、自分の環境では言うほど悪いものではなく、AMの局発になら使えそうだ。
ただ、電源を入り切りするたびに大きく周波数がズレる。
これは恐らく温度特性のためであると思われるので、実際に使うためには通電後時間を置き温度を安定させる必要があるだろう。
今回、DMTimerを用いて周波数カウンタを実装できることを示せた。
周波数の測定を行いたいケースは多々あるので応用範囲が広いのではないだろうか。

