Skip to content

Commit 80713e4

Browse files
authored
Merge pull request #59 from oresat/gpiod-migration
gpiod migration and misc refactors
2 parents f978768 + 5ef8250 commit 80713e4

8 files changed

Lines changed: 587 additions & 249 deletions

File tree

oresat_c3/__main__.py

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,6 @@
77
from threading import Thread
88

99
from olaf import (
10-
Gpio,
11-
GpioError,
1210
ServiceState,
1311
UpdaterState,
1412
app,
@@ -72,23 +70,6 @@ def keys_template():
7270
return render_olaf_template("keys.html", name="Keys")
7371

7472

75-
def get_hw_id(mock: bool) -> int:
76-
"""
77-
Get the hardware ID of the C3 card.
78-
79-
There are 5 gpio pins used to get the unique hardware of the card.
80-
"""
81-
82-
hw_id = 0
83-
try:
84-
for i in range(5):
85-
hw_id |= Gpio(f"HW_ID_BIT_{i}", mock).value << i
86-
except GpioError:
87-
pass
88-
logger.info(f"hardware id is 0x{hw_id:X}")
89-
return hw_id
90-
91-
9273
def watchdog():
9374
"""Pet the watchdog app (which pets the watchdog circuit)."""
9475

@@ -153,8 +134,6 @@ def main():
153134
app.node._fwrite_cache = CacheStore(app.node.fwrite_cache.dir) # pylint: disable=W0212
154135

155136
app.od["versions"]["sw_version"].value = __version__
156-
if app.od["versions"]["hw_version"].value == "6.0":
157-
app.od["hw_id"].value = get_hw_id(mock_hw)
158137

159138
state_service = StateService(config.fram_def, mock_hw)
160139
radios_service = RadiosService(mock_hw)

oresat_c3/drivers/si41xx.py

Lines changed: 65 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,14 @@
22
SI41xx RF Synthesizer driver.
33
"""
44

5+
from __future__ import annotations
6+
7+
import math
58
from 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

1115
class Si41xxRegister(IntEnum):
@@ -54,7 +58,13 @@ class Si41xxError(Exception):
5458

5559

5660
class 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

oresat_c3/services/node_manager.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ def _check_co_nodes_state(self, co: Node, last_hb: float) -> NodeState:
144144
return NodeState.ON
145145
return NodeState.DEAD
146146

147-
if co.info.processor == "stm32":
147+
if co.info.processor in ("stm32", "mcxn"):
148148
timeout = self._STM32_BOOT_TIMEOUT
149149
else:
150150
timeout = self._OCTAVO_BOOT_TIMEOUT

0 commit comments

Comments
 (0)