emdbg.io.digilent
1# Copyright (c) 2023, Auterion AG 2# SPDX-License-Identifier: BSD-3-Clause 3 4from __future__ import annotations 5from pydwf import DwfLibrary 6from pydwf.utilities import openDwfDevice 7from contextlib import contextmanager 8from functools import lru_cache 9import enum 10import logging 11 12LOGGER = logging.getLogger("io:dwf") 13_DWF = None 14 15@contextmanager 16def analog_discovery(identifier: str): 17 """ 18 Starts the DWF library, opens and resets the device and yields it. 19 20 :param identifier: serial number of the device to connect to. 21 """ 22 global _DWF 23 if _DWF is None: 24 _DWF = DwfLibrary() 25 with openDwfDevice(_DWF, serial_number_filter=identifier) as device: 26 try: 27 LOGGER.info("Starting...") 28 device.reset() 29 yield Digilent(device) 30 finally: 31 device.reset() 32 LOGGER.debug("Stopping.") 33 34# ----------------------------------------------------------------------------- 35class DigilentPin: 36 """ 37 Describes a single pin of the Digilent device. Useful only for low-speed 38 sampling or driving. 39 """ 40 class Config(enum.Enum): 41 """ 42 Specifies the hardware configuration of the pin. 43 """ 44 Input = enum.auto() 45 """Input state""" 46 PushPull = enum.auto() 47 """Output state with strong high and low driver""" 48 OpenDrain = enum.auto() 49 """Output state with only a strong low driver""" 50 OpenSource = enum.auto() 51 """Output state with only a strong high driver""" 52 53 class Level(enum.IntEnum): 54 """ 55 Logic level of the pin, for input and output. 56 """ 57 Low = 0, 58 High = 1 59 60 def __init__(self, dio, mask: int): 61 """ 62 :param dio: digitalIO driver of the dwf module. 63 :param mask: the single bit mask of the pin. 64 """ 65 self.dio = dio 66 self.mask = mask 67 self.config = self.Config.Input 68 69 def _set_input(self): 70 self.dio.outputEnableSet(self.dio.outputEnableGet() & ~self.mask) 71 72 def _set_output(self): 73 self.dio.outputEnableSet(self.dio.outputEnableGet() | self.mask) 74 75 def _set(self): 76 self.dio.outputSet(self.dio.outputGet() | self.mask) 77 78 def _reset(self): 79 self.dio.outputSet(self.dio.outputGet() & ~self.mask) 80 81 def is_high(self) -> bool: 82 """ 83 .. warning:: 84 This function does not return the actual pin state, only the set 85 output state. Use `read()` for that instead. 86 :return: `True` if the pin output state is set high. 87 """ 88 return self.dio.outputGet() & self.mask 89 90 def is_output(self) -> bool: 91 """ 92 :return: `True` if pin is configured as output. 93 """ 94 return self.dio.outputEnableGet() & self.mask 95 96 def set_output(self, config: Config = None, level: Level = None): 97 """ 98 Configures the pin as output with an initial level. 99 :param config: pin hardware configuration. Default: `Config.PushPull` 100 :param level: initial output level. Default: `Level.High`. 101 """ 102 self.config = self.Config.PushPull if config is None else config 103 self.set(level) 104 105 def set(self, level: Level = None): 106 """ 107 Sets an output level. Note that depending on pin configuration, the 108 output level may not be achieved. 109 110 :param level: output level. Default: `Level.High`. 111 """ 112 if level is None: level = self.Level.High 113 if self.config == self.Config.PushPull: 114 if level: self._set() 115 else: self._reset() 116 self._set_output() 117 elif self.config == self.Config.OpenDrain: 118 if level: 119 self._set_input() 120 else: 121 self._reset() 122 self._set_output() 123 elif self.config == self.Config.OpenSource: 124 if level: 125 self._set() 126 self._set_output() 127 else: 128 self._set_input() 129 130 def high(self): 131 """ 132 Sets the output level to `Level.High`. 133 """ 134 self.set(self.Level.High) 135 136 def low(self): 137 """ 138 Sets the output level to `Level.Low`. 139 """ 140 self.set(self.Level.Low) 141 142 def set_input(self): 143 """ 144 Configures the pin as `Config.Input`. 145 """ 146 self.config = self.Config.Input 147 self._set_input() 148 149 def read(self) -> bool: 150 """ 151 :return: the actual input state of the pin. 152 """ 153 return self.dio.inputStatus() & self.mask 154 155# ----------------------------------------------------------------------------- 156class Digilent: 157 """ 158 Wrapper class of the Digilent device. 159 The [DWF Python API][dwf] is already very good and should be used directly 160 to configure more advanced use-cases, so this class is very light-weight. 161 """ 162 def __init__(self, device): 163 """ 164 :param device: DWF device 165 """ 166 self.dwf = device 167 168 @lru_cache(100) 169 def gpio(self, position: int) -> DigilentPin: 170 """ 171 Creates a pin abstraction. 172 :param position: Pin number 173 """ 174 return DigilentPin(self.dwf.digitalIO, 1 << position) 175 176 def _info_digital_io(self): 177 info = ["", 178 f"Enable Support = {self.dwf.digitalIO.outputEnableInfo():032b}", 179 f"Enabled = {self.dwf.digitalIO.outputEnableGet():032b}", 180 f"Output Support = {self.dwf.digitalIO.outputInfo():032b}", 181 f"Output High/Low = {self.dwf.digitalIO.outputGet():032b}", 182 f"Input Support = {self.dwf.digitalIO.inputInfo():032b}", 183 f"Input Read = {self.dwf.digitalIO.inputStatus():032b}", 184 ] 185 LOGGER.info("\n".join(info))
16@contextmanager 17def analog_discovery(identifier: str): 18 """ 19 Starts the DWF library, opens and resets the device and yields it. 20 21 :param identifier: serial number of the device to connect to. 22 """ 23 global _DWF 24 if _DWF is None: 25 _DWF = DwfLibrary() 26 with openDwfDevice(_DWF, serial_number_filter=identifier) as device: 27 try: 28 LOGGER.info("Starting...") 29 device.reset() 30 yield Digilent(device) 31 finally: 32 device.reset() 33 LOGGER.debug("Stopping.")
Starts the DWF library, opens and resets the device and yields it.
Parameters
- identifier: serial number of the device to connect to.
36class DigilentPin: 37 """ 38 Describes a single pin of the Digilent device. Useful only for low-speed 39 sampling or driving. 40 """ 41 class Config(enum.Enum): 42 """ 43 Specifies the hardware configuration of the pin. 44 """ 45 Input = enum.auto() 46 """Input state""" 47 PushPull = enum.auto() 48 """Output state with strong high and low driver""" 49 OpenDrain = enum.auto() 50 """Output state with only a strong low driver""" 51 OpenSource = enum.auto() 52 """Output state with only a strong high driver""" 53 54 class Level(enum.IntEnum): 55 """ 56 Logic level of the pin, for input and output. 57 """ 58 Low = 0, 59 High = 1 60 61 def __init__(self, dio, mask: int): 62 """ 63 :param dio: digitalIO driver of the dwf module. 64 :param mask: the single bit mask of the pin. 65 """ 66 self.dio = dio 67 self.mask = mask 68 self.config = self.Config.Input 69 70 def _set_input(self): 71 self.dio.outputEnableSet(self.dio.outputEnableGet() & ~self.mask) 72 73 def _set_output(self): 74 self.dio.outputEnableSet(self.dio.outputEnableGet() | self.mask) 75 76 def _set(self): 77 self.dio.outputSet(self.dio.outputGet() | self.mask) 78 79 def _reset(self): 80 self.dio.outputSet(self.dio.outputGet() & ~self.mask) 81 82 def is_high(self) -> bool: 83 """ 84 .. warning:: 85 This function does not return the actual pin state, only the set 86 output state. Use `read()` for that instead. 87 :return: `True` if the pin output state is set high. 88 """ 89 return self.dio.outputGet() & self.mask 90 91 def is_output(self) -> bool: 92 """ 93 :return: `True` if pin is configured as output. 94 """ 95 return self.dio.outputEnableGet() & self.mask 96 97 def set_output(self, config: Config = None, level: Level = None): 98 """ 99 Configures the pin as output with an initial level. 100 :param config: pin hardware configuration. Default: `Config.PushPull` 101 :param level: initial output level. Default: `Level.High`. 102 """ 103 self.config = self.Config.PushPull if config is None else config 104 self.set(level) 105 106 def set(self, level: Level = None): 107 """ 108 Sets an output level. Note that depending on pin configuration, the 109 output level may not be achieved. 110 111 :param level: output level. Default: `Level.High`. 112 """ 113 if level is None: level = self.Level.High 114 if self.config == self.Config.PushPull: 115 if level: self._set() 116 else: self._reset() 117 self._set_output() 118 elif self.config == self.Config.OpenDrain: 119 if level: 120 self._set_input() 121 else: 122 self._reset() 123 self._set_output() 124 elif self.config == self.Config.OpenSource: 125 if level: 126 self._set() 127 self._set_output() 128 else: 129 self._set_input() 130 131 def high(self): 132 """ 133 Sets the output level to `Level.High`. 134 """ 135 self.set(self.Level.High) 136 137 def low(self): 138 """ 139 Sets the output level to `Level.Low`. 140 """ 141 self.set(self.Level.Low) 142 143 def set_input(self): 144 """ 145 Configures the pin as `Config.Input`. 146 """ 147 self.config = self.Config.Input 148 self._set_input() 149 150 def read(self) -> bool: 151 """ 152 :return: the actual input state of the pin. 153 """ 154 return self.dio.inputStatus() & self.mask
Describes a single pin of the Digilent device. Useful only for low-speed sampling or driving.
61 def __init__(self, dio, mask: int): 62 """ 63 :param dio: digitalIO driver of the dwf module. 64 :param mask: the single bit mask of the pin. 65 """ 66 self.dio = dio 67 self.mask = mask 68 self.config = self.Config.Input
Parameters
- dio: digitalIO driver of the dwf module.
- mask: the single bit mask of the pin.
82 def is_high(self) -> bool: 83 """ 84 .. warning:: 85 This function does not return the actual pin state, only the set 86 output state. Use `read()` for that instead. 87 :return: `True` if the pin output state is set high. 88 """ 89 return self.dio.outputGet() & self.mask
This function does not return the actual pin state, only the set
output state. Use read()
for that instead.
Returns
True
if the pin output state is set high.
91 def is_output(self) -> bool: 92 """ 93 :return: `True` if pin is configured as output. 94 """ 95 return self.dio.outputEnableGet() & self.mask
Returns
True
if pin is configured as output.
97 def set_output(self, config: Config = None, level: Level = None): 98 """ 99 Configures the pin as output with an initial level. 100 :param config: pin hardware configuration. Default: `Config.PushPull` 101 :param level: initial output level. Default: `Level.High`. 102 """ 103 self.config = self.Config.PushPull if config is None else config 104 self.set(level)
Configures the pin as output with an initial level.
Parameters
- config: pin hardware configuration. Default:
Config.PushPull
- level: initial output level. Default:
Level.High
.
106 def set(self, level: Level = None): 107 """ 108 Sets an output level. Note that depending on pin configuration, the 109 output level may not be achieved. 110 111 :param level: output level. Default: `Level.High`. 112 """ 113 if level is None: level = self.Level.High 114 if self.config == self.Config.PushPull: 115 if level: self._set() 116 else: self._reset() 117 self._set_output() 118 elif self.config == self.Config.OpenDrain: 119 if level: 120 self._set_input() 121 else: 122 self._reset() 123 self._set_output() 124 elif self.config == self.Config.OpenSource: 125 if level: 126 self._set() 127 self._set_output() 128 else: 129 self._set_input()
Sets an output level. Note that depending on pin configuration, the output level may not be achieved.
Parameters
- level: output level. Default:
Level.High
.
131 def high(self): 132 """ 133 Sets the output level to `Level.High`. 134 """ 135 self.set(self.Level.High)
Sets the output level to Level.High
.
137 def low(self): 138 """ 139 Sets the output level to `Level.Low`. 140 """ 141 self.set(self.Level.Low)
Sets the output level to Level.Low
.
41 class Config(enum.Enum): 42 """ 43 Specifies the hardware configuration of the pin. 44 """ 45 Input = enum.auto() 46 """Input state""" 47 PushPull = enum.auto() 48 """Output state with strong high and low driver""" 49 OpenDrain = enum.auto() 50 """Output state with only a strong low driver""" 51 OpenSource = enum.auto() 52 """Output state with only a strong high driver"""
Specifies the hardware configuration of the pin.
54 class Level(enum.IntEnum): 55 """ 56 Logic level of the pin, for input and output. 57 """ 58 Low = 0, 59 High = 1
Logic level of the pin, for input and output.
157class Digilent: 158 """ 159 Wrapper class of the Digilent device. 160 The [DWF Python API][dwf] is already very good and should be used directly 161 to configure more advanced use-cases, so this class is very light-weight. 162 """ 163 def __init__(self, device): 164 """ 165 :param device: DWF device 166 """ 167 self.dwf = device 168 169 @lru_cache(100) 170 def gpio(self, position: int) -> DigilentPin: 171 """ 172 Creates a pin abstraction. 173 :param position: Pin number 174 """ 175 return DigilentPin(self.dwf.digitalIO, 1 << position) 176 177 def _info_digital_io(self): 178 info = ["", 179 f"Enable Support = {self.dwf.digitalIO.outputEnableInfo():032b}", 180 f"Enabled = {self.dwf.digitalIO.outputEnableGet():032b}", 181 f"Output Support = {self.dwf.digitalIO.outputInfo():032b}", 182 f"Output High/Low = {self.dwf.digitalIO.outputGet():032b}", 183 f"Input Support = {self.dwf.digitalIO.inputInfo():032b}", 184 f"Input Read = {self.dwf.digitalIO.inputStatus():032b}", 185 ] 186 LOGGER.info("\n".join(info))
Wrapper class of the Digilent device. The [DWF Python API][dwf] is already very good and should be used directly to configure more advanced use-cases, so this class is very light-weight.