Si5351A I2Cクロックジェネレーターを使った周波数波形出力プログラム例

Si5351Aクロックジェネレーター
40kHz波形

Silicon Laboratories社製Si5351Aを使用したクロックジェネレーターモジュール(ここではストロベリーリナックス社)を使い、Raspberry Piで電波時計用の40kHz/60kHzの周波数の長波を出すプログラム例を紹介する。

このモジュールは、I2C制御で個別設定できる3chの端子からそれぞれ2.5kHz~200MHzの任意の周波数を出力することができる。原発振は25MHzの水晶で、内部VCOとPLLをI2C制御することで逓倍かつ分周し任意の周波数を発生させる。

●使用したハードウェア

Si5351A I2Cクロックジェネレータモジュール
\990(ストロベリーリナックス社)※秋月電子通商製もあるが、在庫なしで入手できなかった

■主な仕様
 ・ジェネレータ: Si5351A(Silicon Laboratories)
 ・出力範囲:2.5kHz~200MHzまで
 ・出力波形:方形波のみ
 ・出力数:3ch
 ・原発振:25MHz水晶
 ・コア電圧:2.5V~3.6V
 ・ロジック電圧:1.8V~3.6V
 ・出力レベル:1.8V~3.6VのLVTTL

●Raspberry Pi とクロックジェネレーターモジュールの接続

●任意の周波数の発生方法

 Si5351には3タイプあり、このモジュールに使用されているのはSi5351Aで、原発振は水晶発振だけとなっている。このデバイスは2つのPLL(PLLA/PLLB)から構成されていて、それぞれのPLLは600~900MHz範囲の中間VCO周波数を発生させるフィードバックMultisynthから構成されている。2つのVCO周波数はMultisynth dividerによって、500kHz~200MHzのMultisynth周波数を発生させる。更にR dividersで周波数を2.5kHzまで下げることができる。

以降、簡単化するために、PLLAから中間周波数\(f_{vco}\)を出力、Multisynth0、R0を使い、CLK0から出力周波数\(f_{out}\)を出力するための計算式を示す。

(1)中間周波数\(f_{vco}\)を出力するレジスター・パラメーターの設定
  \(f_{vco} = f_{XTAL} \times Fr\)
    \(f_{XTAL} = 25MHz\) : このモジュールのクリスタル周波数
    \(Fr\) : Fractional Ratio

  \(Fr(Fractional Ratio)\)は次のように算出する。
  \(Fr = \displaystyle (a + \frac{b}{c})\displaystyle\)
    \(a : 15 ~ 90\)
    \(b : 0 ~ 1,048,575\)
    \(c : 1 ~ 1,048,575\)

  ここで得られた\(a,b,c\)を用いて、レジスター26 ~ 33のパラメータMSNA_P1, MSNA_P2, MSNA_P3を算出する
  MSNA_P1 [17:0]= \(\displaystyle 128 \times a + Floor(128 \times \frac{b}{c}) -512\displaystyle \)
  MSNA_P2 [19:0]= \(\displaystyle 128 \times b – c \times Floor(128 \times \frac{b}{c})\displaystyle \)
  MSNA_P3 [19:0]= \(c\)

(2) 出力周波数\(f_{out}\)を出力するレジスター・パラメーターの設定(\(f_{out} <= 150MHz\)の場合)
  \( f_{out} = \displaystyle (\frac{f_{vco}}{Multisynth \times R})\displaystyle\)
    \(f_{out}\) : 出力周波数、500kHz ~ 200MHz
      \(R\)(Divide Ratio)を使うと、500kHz以下の周波数を発生させることができる。
    \(f_{vco}\) : 中間のVCO周波数
    \(Multisynth\) : Multisynth Divider Ratio
    \(R\) : Divide Ratio = 1, 2, 4, 8, 16, 32, 64, 128(500kHz以下の時)

  \(Multisynth\) (Multisynth Divider Ratio)は、次のように算出する。
  \(Multisynth = A + \displaystyle\frac{B}{C}\displaystyle\)、この結果は 6 ~ 1800の範囲でなければならない。
    \(A : 15 ~ 90\)
    \(B : 0 ~ 1,048,575\)
    \(C : 1 ~ 1,048,575\)

  ここで得られた、\(A,B,C\)を用いて、レジスター42 ~ 49のパラメータMS0_P1, MS0_P2, MS0_P3と、レジスター44のR0_DIVを算出する。
  MS0_P1 [17:0]= \(\displaystyle 128 \times A + Floor(128 \times \frac{B}{C}) -512\displaystyle \)
  MS0_P2 [19:0]= \(\displaystyle 128 \times B – C \times Floor(128 \times \frac{B}{C})\displaystyle \)
  MS0_P3 [19:0]= \(C\)
  R0_DIV[2:0] = \(R\)

●具体的事例でのレジスター設定

電波時計の受信/送信周波数は、40kHz(東)と60kHz(西)なので、その2つの周波数を出力するレジスターパラメータを計算する。

<40kHz>
  \(f_{vco} = 25,000kHz \times 16 = 400,000kHz\)
   \(a = 15, b = 1, c = 1 → \displaystyle a + \frac{b}{c}\displaystyle = 16\)
  MSNA_P1 [17:0]= \(\displaystyle128 \times 15 + Floor(128 \times \frac{1}{1}) – 512 \displaystyle\)= 1536(0x600)
  MSNA_P2 [19:0]= \(\displaystyle128 \times 1 – 1 \times Floor(128 \times \frac{1}{1})\displaystyle\) = 0
  MSNA_P3 [19:0]= 1
  \(f_{out} =\displaystyle\frac{400,000kHz}{1250 \times 8}\displaystyle = 40kHz\)
   \(A = 50, B = 1200, C = 1 →  A + \displaystyle\frac{B}{C} = 16\displaystyle\)
   \(R = 8\)
  MS0_P1 [17:0]= \(\displaystyle128 \times 50 + Floor(128 \times \frac{1200}{1}) -512\displaystyle\) = 159,488(0x26F00)
  MS0_P2 [19:0]= \(\displaystyle128 \times 1200 – 1 \times Floor(128 \times \frac{1200}{1})\displaystyle\) = 0
  MS0_P3 [19:0]= 1
  R0_DIV[2:0] = 011b(Divide by 8の場合、\(2^3\))

<60kHz>
  \(f_{vco} = 25,000kHz \times 24 = 600,000kHz\)
   \(a = 23, b = 1, c = 1 → \displaystyle a + \frac{b}{c}\displaystyle = 24\)
  MSNA_P1 [17:0]= \(\displaystyle128 \times 23 + Floor(128 \times \frac{1}{1}) -512\displaystyle\) = 2560(0xA00)
  MSNA_P2 [19:0]= \(\displaystyle128 \times 1 – 1 \times Floor(128 \times \frac{1}{1})\displaystyle\) = 0
  MSNA_P3 [19:0]= 1
  \(f_{out} =\displaystyle\frac{600,000kHz}{1250 \times 8}\displaystyle = 60kHz\)
   \(A = 50, B = 1200, C = 1 →  \displaystyle A + \frac{B}{C}\displaystyle = 16\)
   \(R = 8\)
  MS0_P1 [17:0]= \(\displaystyle128 \times 50 + Floor(128 \times \frac{1200}{1}) -512\displaystyle\) = 159,488(0x26F00)
  MS0_P2 [19:0]= \(\displaystyle128 \times 1200 – 1 \times Floor(128 \times \frac{1200}{1})\displaystyle\) = 0
  MS0_P3 [19:0]= 1
  R0_DIV[2:0] = 011b(Divide by 8の場合、\(2^3\))

●その他のレジスター設定

 Reg0 : デバイスのステータス。D7(SYS_INIT)=0の時、Device Ready
 Reg3 : CLK出力の可否。D0(CLK0_EN)=0(CLK0出力可) / 1(不可)
 Reg15 : PLL入力ソース。D4(PLLA_SRC)=0(Select XTAL)
 Reg16 : CLK0 コントロール。
     D7 : CLK0 Power Down/Up = 0(Powered Up) / 1(Powered Down)
     D6 : MultiSynth0 Integer Mode = 1 (Integer)
     D5 : MultiSynth Source Select = 0 (Select PLLA)
     D4 : Output Clock 0 Invert = 0 (Not inverted)
     D3-D2 : Output CLK0 Input Source = 11b(Select MultiSynth0)
     D1-D0 : Drive Strength = 11b(8mA)
 Reg 44 : Multisynth0 パラメータ
     D6-D4 : 上述R0_DIV[2:0]
     D3-D2 : MS0 Divide by 4 Enable = 00b(other than 4) 150MHz以下の時
 Reg177 : PLL Reset。 D5(PLLA_RST) = 1(PLLA Reset)
 Reg183 : Crystal Internal Load Capacitance。
     D7-D6 = 10b(8pF)
     D5-D0 = 010010b(Reserved)

●レジスター

●Si5351A周波数発生モジュールとプログラムサンプル

・Si5351A周波数発生モジュール(PLLgen5351.py)
パラメータ(a,b,c,A,B,C,R)を設定することで、その周波数を出力する。

# -*- coding: utf-8 -*-
import smbus
import time
import math

I2C = smbus.SMBus(1)
ADR5351 = 0x60		# Si5351A クロックジェネレーター I2C アドレス
DEV_STAT = 0			# Device status
OUT_ENABLE = 3		# Output Enable Cnotrol
PLL_SRC = 15			# PLL Input Source
CLK0_CTL = 16		# Clock0 Control
MULN_PARAM = 26	# Multisynth NA Parameters Start Reg
MUL0_PARAM = 42	# Multisynth0 Parameters Start Reg
XTAL_CL = 183		# Crystal Internal Load Capacitance (8pF) [D7:D6]
PLL_RESET = 177		# PLL Reset

class PLL:
	def __init__(self, freq):
		# freq[no] = [a, b, c, A, B, C, R]
		self.freqNo = len(freq)
		self.MSNA = []
		self.MS0 = []
		for no in range(self.freqNo):
			Fv = freq[no][0:3]		# [a, b, c]
			Fo = freq[no][3:]		# [A, B, C, R]
			self.MSNA.append(self.calcParam(0, Fv))
			self.MS0.append(self.calcParam(1, Fo))
		regData = I2C.read_byte_data(ADR5351, DEV_STAT)		# Device Status
		if regData & 0x80:		# D7 = 0 : Ready
			print("Device Status Error")
			exit(0)
		I2C.write_byte_data(ADR5351, PLL_RESET, 0b10100000)    # PLL Reset
		time.sleep(0.1)
		I2C.write_byte_data(ADR5351, PLL_SRC, 0x00)    # Input Sorce -> XTAL
		I2C.write_byte_data(ADR5351, OUT_ENABLE, 0xFF)    # Disable all clock
		I2C.write_byte_data(ADR5351, XTAL_CL, 0x92)		# Capacitance= 8pF
	
	def calcParam(self, s, p):
		ms = []
		P1 = 128 * p[0] + math.floor(128 * p[1] / p[2]) - 512
		P2 = 128 * p[1] - p[2] * math.floor(128 * p[1] / p[2])
		P3 = p[2]
		ms.append((P3 >> 8) & 0xFF)	# adr=26, 42
		ms.append(P3 & 0xFF)				# adr=27, 43
		r28 = (P1 >> 16) & 0x03			# adr=28
		if s:										# MS0
			for div in range(8):
				if (p[3] >> div) & 1:		# R
					bit = div						# 2^bit
			ms.append((bit << 4) | r28) 	# adr=44
		else:										# MSNA
			ms.append(r28)					# adr=28
		ms.append((P1 >> 8) & 0xFF)	# adr=29, 45
		ms.append(P1 & 0xFF)				# adr=30, 46
		pa, pb = (P3 >> 16) & 0x0F, (P2 >> 16) & 0x0F
		ms.append((pa << 4) | pb) 		# adr=31, 47
		ms.append((P2 >> 8) & 0xFF)	# adr=32, 48
		ms.append(P2 & 0xFF)				# adr=33, 49
		return ms
		
	def freqGen(self, no):
		I2C.write_byte_data(ADR5351, 3, 0xFF)    # Disable All Clock
		I2C.write_byte_data(ADR5351, CLK0_CTL, 0b01001111)    # CLK0 Power Up
		for reg in range(CLK0_CTL+1, CLK0_CTL+8):
			I2C.write_byte_data(ADR5351, reg, 0x80)    # Other Clock Power Down
		I2C.write_i2c_block_data (ADR5351, MULN_PARAM, self.MSNA[no])		# MSNA parameter
		I2C.write_i2c_block_data (ADR5351, MUL0_PARAM, self.MS0[no])		# MS0 parameter
		readData = I2C.read_i2c_block_data (ADR5351, 42, 8)
		I2C.write_byte_data(ADR5351, 3, 0xFE)    # CLK0 Enable
	
	def stopGen(self):
		I2C.write_byte_data(ADR5351, 3, 0xFF)    # Disable clk0

if __name__ == "__main__":
	# 40kHzと60kHzの周波数発生の事例
	freq40 = [15, 1, 1, 50, 1200, 1, 8]		# [a, b, c, A, B, C, R]
	freq60 = [23, 1, 1, 50, 1200, 1, 8]		# [a, b, c, A, B, C, R]
			# F = [a, b, c, A, B, C, R], Fxtal = 25MHz
			# Fvco = Fxtal*(a+b/c) = 25,000kHz*(15+1/1) = 400,000kHz
			# Fvco = Fxtal*(a+b/c) = 25,000kHz*(23+1/1) = 600,000kHz
			# Fout = Fvco/(A+B/C)/R = 400,000/(50+1200/1)/8 = 40kHz
			# Fout = Fvco/(A+B/C)/R = 600,000/(50+1200/1)/8 = 60kHz
			# MSNA40kHz = [0x00, 0x01, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00]		# 40kHz
			# MSNA60kHz = [0x00, 0x01, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x00]		# 60kHz
			# MS040kHz, MS060kHz = [0x00, 0x01, 0x32, 0x6F, 0x00, 0x00, 0x00, 0x00]
	pll = PLL([freq40, freq60])
	pll.freqGen(0)	# 40kHz
	time.sleep(10)
	pll.freqGen(1)	# 60kHz
	time.sleep(10)
	pll.stopGen()

●出力波形(40kHz/60kHz)

40kHz波形
40kHz
60kHz波形
60kHz