22SI41xx RF Synthesizer driver.
33"""
44
5+ from __future__ import annotations
6+
7+ import math
58from enum import IntEnum
6- from typing import Union
79
8- from olaf import Gpio
10+ from gpiod .line import Value
11+
12+ from ..subsystems ._gpio import request_gpio_input , request_gpio_output
913
1014
1115class Si41xxRegister (IntEnum ):
@@ -54,7 +58,13 @@ class Si41xxError(Exception):
5458
5559
5660class Si41xx :
57- """SI41xx RF Synthesizer driver."""
61+ """
62+ Si41xx RF Synthesizer driver.
63+
64+ Part of the radio subsystem, the Si41xx acts as a local oscillator that
65+ down-converts the incoming L-band Earth uplink to a UHF intermediate
66+ frequency, shifting the signal down to a manageable level for the radio.
67+ """
5868
5969 MAX_PHASEDET = 1_000_000
6070 MIN_PHASEDET = 10_000
@@ -99,11 +109,10 @@ def __init__(
99109 """
100110
101111 self ._state = Si41xxState .UNINIT
102-
103- self ._sen_gpio = Gpio (sen_pin , mock )
104- self ._sclk_gpio = Gpio (sclk_pin , mock )
105- self ._sdata_gpio = Gpio (sdata_pin , mock )
106- self ._auxout_gpio = Gpio (auxout_pin , mock )
112+ self ._sen_gpio = request_gpio_output ("/dev/gpiochip2" , 9 , sen_pin )
113+ self ._sclk_gpio = request_gpio_output ("/dev/gpiochip3" , 28 , sclk_pin )
114+ self ._sdata_gpio = request_gpio_output ("/dev/gpiochip3" , 21 , sdata_pin )
115+ self ._auxout_gpio = request_gpio_input ("/dev/gpiochip3" , 29 , auxout_pin )
107116 self ._ref_freq = ref_freq
108117 self ._if_div = if_div
109118 self ._if_n = if_n
@@ -115,38 +124,73 @@ def __init__(
115124
116125 def _write_reg (self , reg : Si41xxRegister , data : int ):
117126 """
118- Bit bang the register over serial
127+ Write a value to a register using a bit banged version of SPI.
119128
120129 Parameters
121130 ----------
122131 reg: Si41xxRegister
123132 The register to write to.
124133 data: int
125134 the data to write. Should
135+
136+ Notes
137+ -----
138+ While there is no official standard for SPI, Motorola (now NXP) AN991 serves as a de facto
139+ standard [1]_. The Si41xx serial interface differs slightly from the informal standard [2]_.
140+
141+ SEN <=> Slave Select or Chip Select (SS, CS)
142+ SDATA <=> Master Out Slave In (MOSI)
143+ SCLK <=> Serial Clock (SCLK)
144+
145+ A typical transation, SEN is asserted low to begin the transaction.
146+ Each bit of data is shifted in on the raising edge of SCLK,
147+ so the SDATA line needs to be asserted slightly before the transition.
148+
149+ SEN ‾‾‾‾‾‾\\ ________________________________________________________________/‾‾‾‾‾‾‾‾‾‾‾‾
150+
151+ SCLK ‾‾‾‾‾‾‾\\ _____/‾‾‾‾‾\\ _____/‾‾‾‾‾\\ _____/‾‾‾‾‾\\ _____/‾‾‾‾‾\\ _____/‾‾‾‾‾\\ _____/‾‾‾‾‾
152+
153+ SDATA -----------< bit0 >< bit1 >< bit2 >< bit3 >< bit4 >---------
154+
155+ References
156+ ----------
157+ .. [1] NXP, Appl. Note 991, pp.2.
158+ .. [2] Skyworks,
159+ "Dual-Band RF Synthesizer With Integrated VCOs For Wireless Communication,"
160+ Si41xx datasheet, Revision 1.61.
126161 """
127162
128163 if data > self ._DATA_MASK :
129164 raise Si41xxError (f"data must be less than 0x{ self ._DATA_MASK :X} , was 0x{ data :X} " )
130165
166+ """
167+ Pack the bits
168+ MSB LSB
169+ 21 4,3 0
170+ [ data ][ register address ]
171+ """
131172 word = reg .value | ((data & self ._DATA_MASK ) << 4 )
132173
133- self ._sen_gpio .low ()
174+ # Pull serial enable line low to start transaction
175+ self ._sen_gpio .set_value (self ._sen_gpio .offsets [0 ], Value .INACTIVE )
134176
135- # bit bang from MSB down
177+ # Bit bang from MSB down to LSB
136178 for _ in range (self ._MSG_SIZE , 0 , - 1 ):
137- self ._sclk_gpio .low ( )
179+ self ._sclk_gpio .set_value ( self . _sclk_gpio . offsets [ 0 ], Value . INACTIVE )
138180
181+ # Write a single bit to the serial data line
139182 if word & self ._MSG_MSB :
140- self ._sdata_gpio .high ( )
183+ self ._sdata_gpio .set_value ( self . _sdata_gpio . offsets [ 0 ], Value . ACTIVE )
141184 else :
142- self ._sdata_gpio .low ( )
185+ self ._sdata_gpio .set_value ( self . _sdata_gpio . offsets [ 0 ], Value . INACTIVE )
143186
144- self ._sclk_gpio .high ( )
187+ self ._sclk_gpio .set_value ( self . _sclk_gpio . offsets [ 0 ], Value . ACTIVE )
145188 word <<= 1
146189
147- self ._sclk_gpio .low ()
148- self ._sen_gpio .high ()
149- self ._sclk_gpio .high ()
190+ # Release the interface
191+ self ._sclk_gpio .set_value (self ._sclk_gpio .offsets [0 ], Value .INACTIVE )
192+ self ._sen_gpio .set_value (self ._sen_gpio .offsets [0 ], Value .ACTIVE )
193+ self ._sclk_gpio .set_value (self ._sclk_gpio .offsets [0 ], Value .ACTIVE )
150194
151195 def calc_div (self , freq : int ) -> tuple [int , int ]:
152196 """
@@ -167,15 +211,7 @@ def calc_div(self, freq: int) -> tuple[int, int]:
167211 if freq == 0 :
168212 raise Si41xxError ("freq must be a non-zero value" )
169213
170- phasedet = self ._ref_freq
171- gcd = freq
172-
173- # Find GCD of both frequencies
174- while phasedet != gcd :
175- if phasedet > gcd :
176- phasedet -= gcd
177- else :
178- gcd -= phasedet
214+ phasedet = math .gcd (self ._ref_freq , freq )
179215
180216 # Divide until frequency is less than the maximum
181217 while phasedet >= self .MAX_PHASEDET :
@@ -231,8 +267,8 @@ def _set_config_reg(
231267 autokp : bool ,
232268 autopdb : bool ,
233269 lprw : bool ,
234- if_div : Union [ Si41xxIfdiv , int ] ,
235- aux_sel : Union [ Si41xxAuxSel , int ] ,
270+ if_div : Si41xxIfdiv | int ,
271+ aux_sel : Si41xxAuxSel | int ,
236272 ):
237273 """Set the Config register"""
238274
0 commit comments