トリオ TS-120/TS-130シリーズで電波を出そうの会
Day31 - TS-120S専用PLL VFOを作る(2)
最終更新:
ts-120s
-
view
Micropythonでコーディング。
Raspberry Pi Pico + 秋月Si5351A基板でVFO製作、続きです。
まずはそれらしいコードを書いちゃいましょう。
基本、si5351.set_freq()で、適当にCL0に周波数を吐けるはずです。
まずはそれらしいコードを書いちゃいましょう。
基本、si5351.set_freq()で、適当にCL0に周波数を吐けるはずです。
下記コードは、コピるなり試すなりいじるなり、個人でお好きに使っていただいて構いません。転載・引用も無断でどうぞ。ただし、①当方に質問やケチ付けてきても関知しません。②無断の商用利用禁止。③何が起ころうと自己責任!!
from machine import Pin, I2C, Timer
from time import sleep, ticks_ms, ticks_diff
# --------------------------
# SI5351 クラス定義
# --------------------------
class SI5351:
SI5351_ADDRESS = 0x60
def __init__(self, i2c):
self.i2c = i2c
def write_register(self, register, data):
self.i2c.writeto_mem(self.SI5351_ADDRESS, register, bytes([data]))
def write_bulk(self, register, data_list):
self.i2c.writeto_mem(self.SI5351_ADDRESS, register, bytes(data_list))
def init(self):
self.write_register(3, 0xFF)
self.write_register(16, 0x80)
self.write_register(177, 0xAC)
def gcd(self, a, b):
while b:
a, b = b, a % b
return a
def approximate_fraction(self, num, denom, max_denominator=1_048_575):
if num == 0:
return 0, 1
g = self.gcd(num, denom)
num //= g
denom //= g
if denom > max_denominator:
scale = denom // max_denominator + 1
denom //= scale
num //= scale
return num, denom
def set_freq(self, freq_hz, clk=0):
XTAL_FREQ = 24_999_497
PLL_FREQ = 900_000_000
MULT = PLL_FREQ // XTAL_FREQ
num = PLL_FREQ % XTAL_FREQ
denom = XTAL_FREQ
num, denom = self.approximate_fraction(num, denom)
pll_params = self.calc_params(MULT, num, denom)
self.set_pll(pll='A', params=pll_params)
div = PLL_FREQ // freq_hz
num = PLL_FREQ % freq_hz
denom = freq_hz
num, denom = self.approximate_fraction(num, denom)
ms_params = self.calc_params(div, num, denom)
self.set_ms(clk, pll='A', params=ms_params)
self.write_register(3, 0xFE & ~(1 << clk))
ms_div = div + num / denom
actual_freq = PLL_FREQ / ms_div
return actual_freq
def calc_params(self, mult, num, denom):
P1 = 128 * mult + int(128 * num / denom) - 512
P2 = 128 * num - denom * int(128 * num / denom)
P3 = denom
return (P1, P2, P3)
def set_pll(self, pll='A', params=(0, 0, 1)):
base = 26 if pll == 'A' else 34
P1, P2, P3 = params
data = [
(P3 >> 8) & 0xFF, P3 & 0xFF,
(P1 >> 16) & 0x03,
(P1 >> 8) & 0xFF, P1 & 0xFF,
((P3 >> 12) & 0xF0) | ((P2 >> 16) & 0x0F),
(P2 >> 8) & 0xFF, P2 & 0xFF
]
self.write_bulk(base, data)
def set_ms(self, clk, pll='A', params=(0, 0, 1)):
base = 42 + clk * 8
P1, P2, P3 = params
data = [
(P3 >> 8) & 0xFF, P3 & 0xFF,
(P1 >> 16) & 0x03 | (0b00 << 6),
(P1 >> 8) & 0xFF, P1 & 0xFF,
((P3 >> 12) & 0xF0) | ((P2 >> 16) & 0x0F),
(P2 >> 8) & 0xFF, P2 & 0xFF
]
self.write_bulk(base, data)
self.write_register(16 + clk, 0x0F | (0 << 6))
# --------------------------
# GPIO設定
# --------------------------
i2c = I2C(0, scl=Pin(17), sda=Pin(16), freq=100_000)
enc_a = Pin(14, Pin.IN, Pin.PULL_UP)
enc_b = Pin(15, Pin.IN, Pin.PULL_UP)
button_step = Pin(13, Pin.IN, Pin.PULL_UP)
# --------------------------
# 状態初期化
# --------------------------
step_sizes = [100, 500, 1000, 10000]
step_index = 2
base_freq = 5_500_000
# --------------------------
# Si5351 初期化
# --------------------------
si5351 = SI5351(i2c)
si5351.init()
si5351.set_freq(base_freq)
# --------------------------
# ステップ切替ボタン
# --------------------------
last_button_time = 0
def handle_button(pin):
global step_index, last_button_time
now = ticks_ms()
if ticks_diff(now, last_button_time) > 300:
step_index = (step_index + 1) % len(step_sizes)
last_button_time = now
button_step.irq(trigger=Pin.IRQ_FALLING, handler=handle_button)
# --------------------------
# ロータリーエンコーダ
# --------------------------
enc_table = {
(0b00, 0b01): 1,
(0b01, 0b11): 1,
(0b11, 0b10): 1,
(0b10, 0b00): 1,
(0b00, 0b10): -1,
(0b10, 0b11): -1,
(0b11, 0b01): -1,
(0b01, 0b00): -1,
}
last_enc = (enc_a.value() << 1) | enc_b.value()
rotate_dir = 0
update_flag = False
new_freq = 0
def check_encoder(timer):
global last_enc, rotate_dir, update_flag, new_freq, base_freq
current = (enc_a.value() << 1) | enc_b.value()
if current != last_enc:
dir = enc_table.get((last_enc, current), 0)
if dir != 0:
step = step_sizes[step_index]
temp_freq = base_freq + dir * step
new_freq = temp_freq
rotate_dir = dir
update_flag = True
last_enc = current
timer = Timer()
timer.init(freq=2000, mode=Timer.PERIODIC, callback=check_encoder)
# --------------------------
# メインループ
# --------------------------
while True:
if update_flag:
update_flag = False
base_freq = new_freq
freq = base_freq
si5351.set_freq(freq)
sleep(0.05)
まあとりあえずこんなかんじでしょうか。
- Si5351Aのライブラリを参照してもよかったのですが、たいそうなものじゃないのでクラスとしてコード内に定義しちまいました。
- ロータリーエンコーダのチャタリング対策は、ポーリングと状態遷移定義程度で、超甘いです。
- 周波数やステップを表示させたければ、コンソールなり液晶なりに吐くよう機能を足してください。
(※家主が実際組んだものは機能ゴテゴテです。このコードは一通りテストしたけど、使ってません)
つづく。