diff --git a/.readthedocs.yaml b/.readthedocs.yaml new file mode 100644 index 0000000..9fb58cd --- /dev/null +++ b/.readthedocs.yaml @@ -0,0 +1,20 @@ +# .readthedocs.yaml +# Read the Docs configuration file +# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details + +# Required +version: 2 + +# Set the version of Python and other tools you might need +build: + os: ubuntu-24.04 + tools: + python: "3.12" + +mkdocs: + configuration: docs/mkdocs/mkdocs.yml + +# Optionally declare the Python requirements required to build your docs +python: + install: + - requirements: docs/requirements.txt diff --git a/docs/mkdocs/.gitignore b/docs/mkdocs/.gitignore new file mode 100644 index 0000000..ccbfadb --- /dev/null +++ b/docs/mkdocs/.gitignore @@ -0,0 +1 @@ +site/ \ No newline at end of file diff --git a/docs/mkdocs/mkdocs.yml b/docs/mkdocs/mkdocs.yml new file mode 100644 index 0000000..5ebe806 --- /dev/null +++ b/docs/mkdocs/mkdocs.yml @@ -0,0 +1,53 @@ +site_name: Embedded Tester Protocol Library + +docs_dir: src +site_url: https://etplib.readthedocs.io/ +repo_url: https://github.com/jabezwinston/etplib-python +repo_name: etplib-python +copyright: Copyright © 2024 Jabez Winston + +theme: + name: readthedocs + highlightjs: true + +nav: + - Introduction: index.md + - API reference guide: + - General: api/general.md + - GPIO: api/gpio.md + - ADC: api/adc.md + - PWM: api/pwm.md + - I2C: api/i2c.md + - SPI: api/spi.md + +plugins: + - mkdocstrings: + handlers: + python: + import: + - https://docs.python.org/3/objects.inv + paths: [../..] + options: + docstring_options: + ignore_init_summary: true + # docstring_section_style: numpy + filters: ["!^_"] + heading_level: 1 + inherited_members: true + merge_init_into_class: true + parameter_headings: true + preload_modules: [mkdocstrings] + relative_crossrefs: true + scoped_crossrefs: true + separate_signature: true + show_bases: false + show_inheritance_diagram: true + show_root_heading: true + show_root_full_path: false + show_signature_annotations: true + show_source: false + show_symbol_type_heading: true + show_symbol_type_toc: true + signature_crossrefs: true + summary: true + unwrap_annotated: true diff --git a/docs/mkdocs/src/api/adc.md b/docs/mkdocs/src/api/adc.md new file mode 100644 index 0000000..f0a2319 --- /dev/null +++ b/docs/mkdocs/src/api/adc.md @@ -0,0 +1 @@ +::: etplib.adc.ADC \ No newline at end of file diff --git a/docs/mkdocs/src/api/general.md b/docs/mkdocs/src/api/general.md new file mode 100644 index 0000000..e8aa93d --- /dev/null +++ b/docs/mkdocs/src/api/general.md @@ -0,0 +1 @@ +::: etplib.lib.ETP diff --git a/docs/mkdocs/src/api/gpio.md b/docs/mkdocs/src/api/gpio.md new file mode 100644 index 0000000..e95f21d --- /dev/null +++ b/docs/mkdocs/src/api/gpio.md @@ -0,0 +1 @@ +::: etplib.gpio.GPIO diff --git a/docs/mkdocs/src/api/i2c.md b/docs/mkdocs/src/api/i2c.md new file mode 100644 index 0000000..0fa33bd --- /dev/null +++ b/docs/mkdocs/src/api/i2c.md @@ -0,0 +1 @@ +::: etplib.i2c.I2C diff --git a/docs/mkdocs/src/api/pwm.md b/docs/mkdocs/src/api/pwm.md new file mode 100644 index 0000000..407dd68 --- /dev/null +++ b/docs/mkdocs/src/api/pwm.md @@ -0,0 +1 @@ +::: etplib.pwm.PWM diff --git a/docs/mkdocs/src/api/spi.md b/docs/mkdocs/src/api/spi.md new file mode 100644 index 0000000..2f6c755 --- /dev/null +++ b/docs/mkdocs/src/api/spi.md @@ -0,0 +1 @@ +::: etplib.spi.SPI diff --git a/docs/mkdocs/src/index.md b/docs/mkdocs/src/index.md new file mode 120000 index 0000000..8a33348 --- /dev/null +++ b/docs/mkdocs/src/index.md @@ -0,0 +1 @@ +../../../README.md \ No newline at end of file diff --git a/docs/requirements.in b/docs/requirements.in new file mode 100644 index 0000000..1137b73 --- /dev/null +++ b/docs/requirements.in @@ -0,0 +1,4 @@ +mkdocs +mkdocstrings[python] +markdown-include +black diff --git a/docs/requirements.txt b/docs/requirements.txt new file mode 100644 index 0000000..3fc948b --- /dev/null +++ b/docs/requirements.txt @@ -0,0 +1,90 @@ +# +# This file is autogenerated by pip-compile with Python 3.12 +# by the following command: +# +# pip-compile requirements.in +# +black==24.10.0 + # via -r requirements.in +click==8.1.7 + # via + # black + # mkdocs + # mkdocstrings +colorama==0.4.6 + # via griffe +ghp-import==2.1.0 + # via mkdocs +griffe==1.5.1 + # via mkdocstrings-python +jinja2==3.1.4 + # via + # mkdocs + # mkdocstrings +markdown==3.7 + # via + # markdown-include + # mkdocs + # mkdocs-autorefs + # mkdocstrings + # pymdown-extensions +markdown-include==0.8.1 + # via -r requirements.in +markupsafe==3.0.2 + # via + # jinja2 + # mkdocs + # mkdocs-autorefs + # mkdocstrings +mergedeep==1.3.4 + # via + # mkdocs + # mkdocs-get-deps +mkdocs==1.6.1 + # via + # -r requirements.in + # mkdocs-autorefs + # mkdocstrings +mkdocs-autorefs==1.2.0 + # via + # mkdocstrings + # mkdocstrings-python +mkdocs-get-deps==0.2.0 + # via mkdocs +mkdocstrings[python]==0.27.0 + # via + # -r requirements.in + # mkdocstrings-python +mkdocstrings-python==1.12.2 + # via mkdocstrings +mypy-extensions==1.0.0 + # via black +packaging==24.2 + # via + # black + # mkdocs +pathspec==0.12.1 + # via + # black + # mkdocs +platformdirs==4.3.6 + # via + # black + # mkdocs-get-deps + # mkdocstrings +pymdown-extensions==10.12 + # via mkdocstrings +python-dateutil==2.9.0.post0 + # via ghp-import +pyyaml==6.0.2 + # via + # mkdocs + # mkdocs-get-deps + # pymdown-extensions + # pyyaml-env-tag +pyyaml-env-tag==0.1 + # via mkdocs +six==1.17.0 + # via python-dateutil +watchdog==6.0.0 + # via mkdocs diff --git a/etplib/adc.py b/etplib/adc.py index 77ff1ef..fb99cb8 100644 --- a/etplib/adc.py +++ b/etplib/adc.py @@ -25,11 +25,23 @@ def __init__(self, etp): self.reference_mv = 3300 self.resolution = 10 - """ - Query ADC information - """ - - def get_info(self): + def get_info(self) -> dict: + """Query ADC information. + + Returns: + dict: Dictionary containing ADC information of the following format: + + | Key | Value Type | Description | + |---------------|------------|----------------------------------------------| + | num_adc | int | Number of ADCs | + | num_channels | int | Number of channels | + | resolution | int | Resolution of ADC | + | reference | int | Reference voltage of ADC | + | max_rate | int | Maximum sampling rate of ADC | + | port_count | int | Number of ADC ports | + | ports | list | List of ADC ports and their corresponding pins | + + """ adc_info_cmd = self.code << 8 | self.ops['info'] cmd = self.etp.frame_packet(adc_info_cmd) self.etp.cmd_queue.put(cmd) @@ -53,31 +65,45 @@ def get_info(self): 'ports': adc_info } - """ - Enable/Disable ADC pins - - """ - def init(self, pin_list): + def init(self, pin_list: dict) -> int: + """Enable/Disable ADC pins. + + Args: + pin_list: Dictionary of pins to enable/disable. + + """ adc_init_cmd = self.code << 8 | self.ops['init'] adc_pin_mask = 0 adc_enable_mask = 0 for pin in pin_list.keys(): port, pin_num = self.etp.gpio.decode_gpio_pin(pin) - if (pin_list[pin] == True): + if pin_list[pin] is True: adc_enable_mask |= (1 << pin_num) adc_pin_mask |= (1 << pin_num) cmd = self.etp.frame_packet(adc_init_cmd, struct.pack(' int: + """ + Control ADC pin. + + Args: + pin: ADC pin to control. + **kwargs (dict): Keyword arguments. + + Keyword Args: **kwargs: + resolution (int, optional): Resolution of ADC pin. Defaults to self.resolution. + reference_mv (int, optional): Reference voltage of ADC pin. Defaults to self.reference_mv. + rate (int, optional): Sampling rate of ADC pin. Defaults to 1000. + start (int, optional): Start value of ADC pin. Defaults to 0. + + Returns: + Status code. + """ adc_ctrl_cmd = self.code << 8 | self.ops['ctrl'] port, pin = self.etp.gpio.decode_gpio_pin(pin) @@ -109,17 +135,20 @@ def ctrl(self, pin, **kwargs): cmd = self.etp.frame_packet(adc_ctrl_cmd, struct.pack(' int: + """Read data from ADC pin. + + Args: + pin: ADC pin to read from. + + Returns: + ADC value read from the pin. + """ adc_read_cmd = self.code << 8 | self.ops['read'] - port, pin = self.etp.gpio.decode_gpio_pin(pin_str) + port, pin = self.etp.gpio.decode_gpio_pin(pin) cmd = self.etp.frame_packet(adc_read_cmd, struct.pack(' dict: + """Get GPIO port and pin information. + + Returns: + Dictionary containing GPIO port count and pin information. + + Dictionary format: + - `port_count` (int): Number of GPIO ports. + - `info` (list): List of dictionaries containing GPIO port and pin information. + - `port` (str): GPIO port identifier. + - `pins` (int): GPIO pins available in the port. + + """ gpio_info_cmd = self.code << 8 | self.ops['info'] # Get the number of GPIO ports cmd = self.etp.frame_packet(gpio_info_cmd, struct.pack('B', self.info_cmds['port_count'])) @@ -58,7 +68,7 @@ def get_info(self): return {"port_count": port_count, "info": pin_info} - def decode_gpio_pin(self, pin): + def decode_gpio_pin(self, pin: str) -> tuple: port = '' pin_num = 0 @@ -77,20 +87,35 @@ def decode_gpio_pin(self, pin): return port, pin_num - def encode_gpio_pin(self, port, pin_num): + def encode_gpio_pin(self, port, pin_num) -> str: if port >= ord('A') and port <= ord('Z'): return f"P{chr(port)}{pin_num}" elif port >= ord('a') and port <= ord('z'): return f"{chr(port)}{pin_num}" elif port == ord('_'): return f"_{pin_num}" - - """ - Initialize GPIO pins - """ + def init(self, pin_list: dict) -> int: + """ + Initialize GPIO pins with the given configuration. + + Args: + pin_list (dict): A dictionary where keys are GPIO pin identifiers and values are + dictionaries containing configuration options for each pin. + Configuration options include: + + Dictionary format: + + - `mode`: Specifies the direction of the pin ('input' or 'output'). - def init(self, pin_list): + - `type`: Specifies the pull type of the pin ('pull_up' or 'pull_down'). + + - `interrupt`: Specifies the interrupt type for the pin + ('rising_edge', 'falling_edge', or 'both_edges'). + + Returns: + Status of the operation. + """ gpio_init_cmd = self.code << 8 | self.ops['init'] dir_mask = 0 dir_val = 0 @@ -123,13 +148,20 @@ def init(self, pin_list): cmd = self.etp.frame_packet(gpio_init_cmd, struct.pack(' dict: + """Read GPIO pins provided as list. - """ - Read GPIO pins provided as list - """ + Args: + pin_list: List of GPIO pins to read. - def read(self, pin_list): + Returns: + Dictionary of GPIO pins and their states. + + """ pin_read = dict() gpio_read_cmd = self.code << 8 | self.ops['read'] pin_mask = 0 @@ -148,12 +180,14 @@ def read(self, pin_list): return pin_read - """ - Write GPIO pins provided as dictionary - - """ - def write(self, pin_state): + def write(self, pin_state: dict): + """Write GPIO pins provided as dictionary. + + Args: + pin_state: Dictionary of GPIO pins and their states. + + """ gpio_write_cmd = self.code << 8 | self.ops['write'] port = '' port_mask = 0 diff --git a/etplib/i2c.py b/etplib/i2c.py index e780c0c..bb04fec 100644 --- a/etplib/i2c.py +++ b/etplib/i2c.py @@ -32,12 +32,12 @@ class I2C: def __init__(self, etp): self.etp = etp - """ - Query I2C pin information, bus counts and supported speeds. - - """ - - def get_info(self): + def get_info(self) -> dict: + """Query I²C pin information, bus counts and supported speeds. + + Returns: + Dictionary containing I²C bus count, supported speeds and pin information. + """ i2c_info_cmd = self.code << 8 | self.ops['info'] # Get the number of I2C buses cmd = self.etp.frame_packet(i2c_info_cmd, struct.pack(' int: + """ Initialize I²C bus with speed. + + Args: + bus: I²C bus number. + speed: I²C bus speed in kHz. + + Returns: + Initialization status. + + """ i2c_init_cmd = self.code << 8 | self.ops['init'] cmd = self.etp.frame_packet(i2c_init_cmd, struct.pack(' list: + """ Scan I²C bus for devices. + + Args: + bus: I²C bus number. + start_addr: Start address to scan. + end_addr: End address to scan. + + Returns: + List of I²C device addresses. + """ i2c_scan_cmd = self.code << 8 | self.ops['scan'] cmd = self.etp.frame_packet(i2c_scan_cmd, struct.pack(' list[int]: + """ Read data from I²C device. + + Args: + bus (int): I²C bus number. + addr (int): I²C device address. + num_bytes (int): Number of bytes to read. + + Returns: + list[int]: List of bytes read from the device. + """ i2c_read_cmd = self.code << 8 | self.ops['read'] cmd = self.etp.frame_packet(i2c_read_cmd, struct.pack(' int: + """ Write data to I²C device. + + Args: + bus (int): I²C bus number. + addr (int): I²C device address. + data (list): List of bytes to write. + + Returns: + Write status. + """ i2c_write_cmd = self.code << 8 | self.ops['write'] length = len(data) cmd = self.etp.frame_packet(i2c_write_cmd, struct.pack(' list[int]: + """ Read data from I²C device register. + + Args: + bus (int): I²C bus number. + addr (int): I²C device address. + reg (int): Register address. + num_bytes (int): Number of bytes to read. + + Returns: + List of bytes read from the register. + """ i2c_read_reg_cmd = self.code << 8 | self.ops['read_reg'] cmd = self.etp.frame_packet(i2c_read_reg_cmd, struct.pack(' int: + """ Write data to I²C device register. + + Args: + bus: I²C bus number. + addr: I²C device address. + reg: Register address. + data: List of bytes to write. + + Returns: + Write status. + + """ i2c_write_reg_cmd = self.code << 8 | self.ops['write_reg'] length = len(data) cmd = self.etp.frame_packet(i2c_write_reg_cmd, struct.pack(' dict: + """ Get Firmware Information + + Returns: + dict: Firmware information. + + | Key | Value Type | Description | + |---------------|------------|----------------------------------------------| + | version | str | Firmware version | + | fw_version | str | Firmware version | + | build_date | str | Firmware build date | + | hw_type | str | Hardware type | + """ self.cmd_queue.put(self.frame_packet(self.general_ops['fw_info'], [self.fw_info_cmds['version']])) rsp, _ = self.read_rsp() version = struct.unpack(' list[int]: + """ Get Supported Operations + + + Args: + start_op: Start operation code + end_op: End operation code + + Returns: + List of supported operation codes. + + """ supported_ops = [] sub_cmd = [start_op & 0xFF, start_op >> 8, end_op & 0xFF, end_op >> 8] p = self.frame_packet(self.general_ops['get_supported_ops'], sub_cmd) @@ -284,15 +299,11 @@ def get_supported_ops(self, start_op = 0, end_op = 0xFFFF): Debug Print control """ - def fw_dbg_print_ctrl(self, log_level): + def fw_dbg_print_ctrl(self, log_level: int): self.cmd_queue.put(self.frame_packet(self.general_ops['debug_print'], [log_level])) - """ - Configure Transport - - """ - def configure_transport(self, transport, **kwargs): + """Configure Transport.""" if transport == 'serial': self.port = kwargs['port'] self.baudrate = kwargs['baudrate'] @@ -322,6 +333,7 @@ def configure_transport(self, transport, **kwargs): def close(self): + """Close ETP Device.""" self.transport_open = False self.reader_thread_handle.join() self.writer_thread_handle.join() diff --git a/etplib/pwm.py b/etplib/pwm.py index 03512bd..1c58152 100644 --- a/etplib/pwm.py +++ b/etplib/pwm.py @@ -22,12 +22,20 @@ class PWM: def __init__(self, etp): self.etp = etp - """ - Query PWM information - - """ - - def get_info(self): + def get_info(self) -> dict: + """Query PWM information. + + Returns: + Dictionary containing PWM information of the following format: + + | Key | Value Type | Description | + |---------------|------------|----------------------------------------------| + | num_pwm | int | Number of PWMs | + | max_freq | int | Maximum frequency of PWM | + | port_count | int | Number of PWM ports | + | ports | list | List of PWM ports and their corresponding pins | + + """ pwm_info_cmd = self.code << 8 | self.ops['info'] cmd = self.etp.frame_packet(pwm_info_cmd) self.etp.cmd_queue.put(cmd) @@ -49,34 +57,45 @@ def get_info(self): 'port_count': port_count, 'ports': pwm_info } - - """ - Enable/Disable PWM pins - """ + def init(self, pin_list: dict) -> int: + """Enable/Disable PWM pins. + + Args: + pin_list: Dictionary containing PWM pins and their enable status. + + Returns: + Initialization status. - def init(self, pin_list): + """ pwm_init_cmd = self.code << 8 | self.ops['init'] pwm_pin_mask = 0 pwm_enable_mask = 0 for pin in pin_list.keys(): port, pin_num = self.etp.gpio.decode_gpio_pin(pin) - if (pin_list[pin] == True): + if pin_list[pin] is True: pwm_enable_mask |= (1 << pin_num) pwm_pin_mask |= (1 << pin_num) cmd = self.etp.frame_packet(pwm_init_cmd, struct.pack(' int: + """Control PWM pin. + + Args: + pin: PWM pin identifier. + duty_cycle (float): Duty cycle of the PWM signal. + freq (int): Frequency of the PWM signal in Hz. - """ - Control PWM pins - - """ + Returns: + Control status. - def ctrl(self, pin_str, duty_cycle, freq = 1000): + """ pwm_ctrl_cmd = self.code << 8 | self.ops['ctrl'] - port, pin_num = self.etp.gpio.decode_gpio_pin(pin_str) + port, pin_num = self.etp.gpio.decode_gpio_pin(pin) freq_unit = 0 if freq < 65535: freq_unit = 0 @@ -86,4 +105,5 @@ def ctrl(self, pin_str, duty_cycle, freq = 1000): duty_cycle = int(duty_cycle * 100) cmd = self.etp.frame_packet(pwm_ctrl_cmd, struct.pack(' dict: + """Query SPI information, bus counts and supported speeds. + + Returns: + dict: Dictionary containing SPI bus count, supported speeds and pin information - def get_info(self): + | Key | Value Type | Description | + |---------------|------------|----------------------------------------------| + | bus_count | int | Number of SPI buses | + | bus_speeds | list | List of supported SPI bus speeds | + | info | list | List of SPI pins and their corresponding pins | + + """ spi_info_cmd = self.code << 8 | self.ops['info'] # Get the number of SPI buses cmd = self.etp.frame_packet(spi_info_cmd, struct.pack(' int: + """Initialize SPI bus. + + Args: + bus (int): SPI bus number. + mode (int): SPI mode (0, 1, 2, 3). + speed (int): SPI bus speed in KHz. + bit_order (int): Bit order (MSB_FIRST, LSB_FIRST). + cs (int): Chip select pin number. + + Returns: + int: Status of the operation. + + """ spi_init_cmd = self.code << 8 | self.ops['init'] cmd = self.etp.frame_packet(spi_init_cmd, struct.pack(' list[int]: + """Transfer data over SPI bus. + + Args: + bus: SPI bus number. + tx_data: List of bytes to be transmitted. + cs: Chip select pin number. + + Returns: + List of bytes received over SPI bus. + + """ spi_transfer_cmd = self.code << 8 | self.ops['transfer'] cmd = self.etp.frame_packet(spi_transfer_cmd, struct.pack(' License : MIT -Date : 25-Nov-2024 +Date : 14-Dec-2024 """ -VERSION="0.2.0" +VERSION="0.2.1"