├── pyproject.toml ├── tests ├── __init__.py ├── test_i2c.py ├── test_sd_nand.py ├── test_spi_write.py ├── test_device.py ├── test_gpio_intr.py ├── test_ina226.py ├── test_uart.py └── test_mpu6050.py ├── i2c_devices ├── __init__.py ├── ina226 │ ├── __init__.py │ └── ina226.py └── mpu6050 │ ├── __init__.py │ └── mpu6050.py ├── spi_devices ├── __init__.py └── sd_nand │ ├── __init__.py │ └── sd_nand.py ├── ch347 ├── lib │ ├── CH347DLL.DLL │ └── CH347DLLA64.DLL ├── include │ ├── CH347DLL.H │ └── CH347DLL_EN.H ├── __init__.py └── ch347.py ├── LICENSE ├── .vscode └── settings.json ├── README.md ├── README_en.md └── .gitignore /pyproject.toml: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /i2c_devices/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /spi_devices/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /spi_devices/sd_nand/__init__.py: -------------------------------------------------------------------------------- 1 | from .sd_nand import * -------------------------------------------------------------------------------- /i2c_devices/ina226/__init__.py: -------------------------------------------------------------------------------- 1 | from .ina226 import INA226 2 | -------------------------------------------------------------------------------- /i2c_devices/mpu6050/__init__.py: -------------------------------------------------------------------------------- 1 | from .mpu6050 import MPU6050 2 | -------------------------------------------------------------------------------- /ch347/lib/CH347DLL.DLL: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pengwon/ch347-py/HEAD/ch347/lib/CH347DLL.DLL -------------------------------------------------------------------------------- /ch347/include/CH347DLL.H: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pengwon/ch347-py/HEAD/ch347/include/CH347DLL.H -------------------------------------------------------------------------------- /ch347/lib/CH347DLLA64.DLL: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pengwon/ch347-py/HEAD/ch347/lib/CH347DLLA64.DLL -------------------------------------------------------------------------------- /tests/test_i2c.py: -------------------------------------------------------------------------------- 1 | from ch347.ch347 import CH347 2 | 3 | 4 | def test_i2c(): 5 | ch347 = CH347() 6 | ch347.open_device() 7 | print(ch347.stream_i2c([i for i in range(100)], 1)) 8 | 9 | 10 | if __name__ == "__main__": 11 | test_i2c() 12 | -------------------------------------------------------------------------------- /tests/test_sd_nand.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import numpy as np 4 | from time import sleep 5 | 6 | # Get the parent directory's path 7 | parent_directory = os.path.abspath(os.path.join(os.path.dirname(__file__), '..')) 8 | 9 | # Add the parent directory to the system path if not already present 10 | if parent_directory not in sys.path: 11 | sys.path.insert(0, parent_directory) 12 | 13 | from spi_devices.sd_nand import SD_NAND 14 | 15 | sd_nand = SD_NAND() 16 | sd_nand.initialize() -------------------------------------------------------------------------------- /tests/test_spi_write.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | 4 | # Get the parent directory's path 5 | parent_directory = os.path.abspath(os.path.join(os.path.dirname(__file__), '..')) 6 | 7 | # Add the parent directory to the system path if not already present 8 | if parent_directory not in sys.path: 9 | sys.path.insert(0, parent_directory) 10 | 11 | from ch347 import SPIConfig, CH347 12 | 13 | spi_config = SPIConfig( 14 | Mode=0, 15 | Clock=2, 16 | ByteOrder=1, 17 | SpiWriteReadInterval=0, 18 | SpiOutDefaultData=0xFF, 19 | ChipSelect=0x80, 20 | CS1Polarity=0, 21 | CS2Polarity=0, 22 | IsAutoDeative=1, 23 | ActiveDelay=0, 24 | DelayDeactive=0, 25 | ) 26 | 27 | ch347 = CH347() 28 | ch347.open_device() 29 | print(ch347.get_version()) 30 | ch347.spi_init(spi_config) 31 | ch347.spi_write(0x80, [0x01]) 32 | ch347.close_device() 33 | -------------------------------------------------------------------------------- /tests/test_device.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | import sys 3 | import os 4 | 5 | # Add the parent directory to the path to import the package 6 | sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))) 7 | 8 | from ch347 import CH347 9 | 10 | 11 | def test_device_information(): 12 | """Test to connect to a device and print its information.""" 13 | # Try to open a device 14 | try: 15 | device = CH347() 16 | device.list_devices() 17 | assert device is not None, "Failed to create device instance" 18 | 19 | # Open the first available device 20 | result = device.open_device() 21 | assert result, "Failed to open device" 22 | 23 | # Get device information 24 | device_info = device.get_device_info() 25 | 26 | # Close the device 27 | device.close_device() 28 | 29 | except Exception as e: 30 | pytest.fail(f"Test failed with exception: {str(e)}") 31 | 32 | 33 | if __name__ == "__main__": 34 | # Allow running the test directly 35 | test_device_information() 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Peter 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.suggest.snippetsPreventQuickSuggestions": false, 3 | "terminal.explorerKind": "integrated", 4 | "terminal.integrated.profiles.windows": { 5 | "Conda": { 6 | "name": "Conda", 7 | "path": "pwsh", 8 | "args": [ 9 | "-ExecutionPolicy", 10 | "ByPass", 11 | "-NoExit", 12 | "-Command", 13 | "& '${env:userprofile}\\miniconda3\\shell\\condabin\\conda-hook.ps1' " 14 | ] 15 | }, 16 | "Anaconda": { 17 | "name": "Anaconda", 18 | "path": "pwsh", 19 | "args": [ 20 | "-ExecutionPolicy", 21 | "ByPass", 22 | "-NoExit", 23 | "-Command", 24 | "& '${env:userprofile}\\anaconda3\\shell\\condabin\\conda-hook.ps1' " 25 | ] 26 | } 27 | }, 28 | "files.associations": { 29 | "MMC_SD.C": "cpp" 30 | }, 31 | "spellright.language": [], 32 | "spellright.documentTypes": [ 33 | "latex", 34 | "plaintext" 35 | ] 36 | } -------------------------------------------------------------------------------- /ch347/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | CH347 Module 3 | ------------ 4 | 5 | The `ch347` module provides functions for interacting with the CH347 USB-UART/SPI/I2C/JTAG bridge. 6 | 7 | Initialization: 8 | ---------------- 9 | 10 | - Use `ch347.init()` to initialize the CH347 device. 11 | 12 | Configuration: 13 | -------------- 14 | 15 | - Use `ch347.set_spi_config(clock: int, mode: int)` to configure the SPI parameters. 16 | 17 | SPI Communication: 18 | ------------------ 19 | 20 | - Use `ch347.spi_write(device_index: int, chip_select: int, write_data: bytes, write_step: int)` 21 | to write data to the SPI bus. 22 | 23 | - Use `ch347.spi_read(device_index: int, chip_select: int, write_data: bytes, read_length: int)` 24 | to read data from the SPI bus. 25 | 26 | Closing: 27 | -------- 28 | 29 | - Use `ch347.close()` to close the CH347 device. 30 | 31 | Usage Example: 32 | -------------- 33 | 34 | import ch347 35 | 36 | # Initialize the CH347 device 37 | ch347.init() 38 | 39 | # Configure the SPI parameters 40 | ch347.set_spi_config(clock=1000000, mode=0) 41 | 42 | # Write data to the SPI bus 43 | ch347.spi_write(device_index=0, chip_select=1, write_data=b'\x01\x02\x03', write_step=3) 44 | 45 | # Read data from the SPI bus 46 | data = ch347.spi_read(device_index=0, chip_select=1, write_data=b'\x00', read_length=3) 47 | 48 | # Close the CH347 device 49 | ch347.close() 50 | """ 51 | 52 | from .ch347 import * -------------------------------------------------------------------------------- /tests/test_gpio_intr.py: -------------------------------------------------------------------------------- 1 | #connect GPIO PIN 4 and PIN 7 for testing interrupt callback 2 | 3 | import os 4 | import sys 5 | import ctypes 6 | import time 7 | 8 | # Get the parent directory's path 9 | parent_directory = os.path.abspath(os.path.join(os.path.dirname(__file__), '..')) 10 | 11 | # Add the parent directory to the system path if not already present 12 | if parent_directory not in sys.path: 13 | sys.path.insert(0, parent_directory) 14 | 15 | from ch347 import CH347 16 | 17 | 18 | # Callback must match: void callback(unsigned char* pdata) 19 | CALLBACK_TYPE = ctypes.CFUNCTYPE(None, ctypes.POINTER(ctypes.c_ubyte)) 20 | @CALLBACK_TYPE 21 | def my_interrupt_callback(pdata): 22 | # Cast to pointer to array of 8 unsigned bytes 23 | byte_array = ctypes.cast(pdata, ctypes.POINTER(ctypes.c_ubyte * 8)).contents 24 | print("Interrupt received:", ' '.join(f'0x{b:02X}' for b in byte_array)) 25 | 26 | 27 | ch347 = CH347() 28 | ch347.open_device() 29 | 30 | print("open") 31 | print("version:", ch347.get_version()) 32 | io_dir = [0] 33 | io_data = [0] 34 | 35 | ch347.gpio_get(io_dir, io_data) 36 | print("get_io:", hex(io_dir[0]),hex(io_data[0])) 37 | 38 | ch347.gpio_set(0x80, 0x80, 0x00) 39 | ch347.gpio_get(io_dir, io_data) 40 | 41 | ch347.set_interrupt(4, 2, 8, 3, my_interrupt_callback) 42 | 43 | ch347.gpio_set(0x80, 0x80, 0x80) 44 | ch347.gpio_get(io_dir, io_data) 45 | print("get_io:", hex(io_dir[0]),hex(io_data[0])) 46 | 47 | ch347.gpio_set(0x80, 0x80, 0x00) 48 | ch347.gpio_get(io_dir, io_data) 49 | print("get_io:", hex(io_dir[0]),hex(io_data[0])) 50 | 51 | ch347.abort_interrupt() 52 | 53 | ch347.close_device() 54 | print("close") 55 | 56 | 57 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ch347-py 2 | 3 | [English](README_en.md) 4 | 5 | ## 简介 6 | 7 | `ch347-py`是一个用于驱动CH347芯片的Python绑定库。它提供了一种简单的方式来与CH347芯片进行交互,可以基于此开发I2C、SPI等通信接口应用。 8 | 9 | ## 安装 10 | 11 | ### 从源码安装 12 | 13 | ```bash 14 | git clone https://github.com/pengwon/ch347-py.git 15 | cd ch347-py 16 | ``` 17 | 18 | ### 使用pip安装 19 | 20 | [TODO] 21 | 22 | 你可以通过pip来安装这个库: 23 | 24 | ```bash 25 | pip install ch347-py 26 | ``` 27 | 28 | ## 使用方法 29 | 30 | 首先,你需要从`ch347`模块中导入`CH347`类: 31 | 32 | ```python 33 | from ch347 import CH347 34 | ``` 35 | 36 | 然后,你可以创建一个`CH347`类的实例,并使用它的方法来与CH347芯片进行交互。例如: 37 | 38 | ```python 39 | driver = CH347() 40 | driver.spi_write(0x00, [0xFF] * 10) 41 | ``` 42 | 43 | ## 测试 44 | 45 | 本项目包含了一些测试脚本,你可以运行这些脚本来测试和演示如何使用这个库。 46 | 47 | ## 参考文档 48 | 49 | 1. [读取MPU6050传感器数据和显示](https://mp.weixin.qq.com/s?__biz=MzA3NzMyNTIyOA==&mid=2651481825&idx=1&sn=bbe2833173dcf0420eaac9faff14ca8b&chksm=84ad7022b3daf9349f8e57e87abacef833bd742f655a60379e07e8ee2529cb012d807a132a92#rd) 50 | 2. [读写SPI Flash](https://mp.weixin.qq.com/s?__biz=MzA3NzMyNTIyOA==&mid=2651481820&idx=1&sn=b28a3b59fa7104383ced33ddf9cee7c5&chksm=84ad701fb3daf9099a0245f143c6fc3f5398af3657cf028e052fa3ad944aa510730fee319fa8#rd) 51 | 3. [连接MPU6050](https://mp.weixin.qq.com/s?__biz=MzA3NzMyNTIyOA==&mid=2651481772&idx=1&sn=509448890675dbcf03ed1ee877d0bff2&chksm=84ad706fb3daf979f6c5e74e8ec46f09307ffdd358026a33ac98bd13196a79fbabc2db6d746f#rd) 52 | 4. [INA226电流计的使用](https://mp.weixin.qq.com/s?__biz=MzA3NzMyNTIyOA==&mid=2651482424&idx=1&sn=5676fb2123294c17a611775965b1f1b1&chksm=84ad7ffbb3daf6eda6cc972bc08e5b0d0781651bada60ca906423fd59090a7f3794fed190d9a#rd) 53 | 5. [详解I2C](https://mp.weixin.qq.com/s?__biz=MzA3NzMyNTIyOA==&mid=2651481759&idx=1&sn=562c0748c593e1de21487a538ace637a&chksm=84ad705cb3daf94a2d3eb64d94acc374ea5ff3808b8f5800b4730d9ff02c9a0e1feea61e1566#rd) 54 | 6. [python封装动态库](https://mp.weixin.qq.com/s?__biz=MzA3NzMyNTIyOA==&mid=2651481701&idx=1&sn=2ddf1ce70703550bbcaeb7bed4aa0211&chksm=84ad70a6b3daf9b036b859b8b4c621c7a8db6a32ca1e04bd9369b7dc125e17ed16f3ebddc608#rd) 55 | 7. [详解SPI](https://mp.weixin.qq.com/s?__biz=MzA3NzMyNTIyOA==&mid=2651481820&idx=2&sn=4d5c44f98a4b174b867b9c05408207d9&chksm=84ad701fb3daf90966a02142c32b14a628b1d8b4564ac448925ccb77796800d6622a071421ee#rd) 56 | 57 | ## 许可证 58 | 59 | 本项目使用的许可证在 [`LICENSE`](LICENSE) 文件中给出。 60 | 61 | ## 贡献 62 | 63 | 欢迎对本项目做出贡献。在提交[Pull Request](https://github.com/pengwon/ch347-py/pulls)之前,请确保您的代码通过了所有的测试。 64 | 65 | ## 联系 66 | 67 | 如果您在使用这个库的过程中遇到任何问题,或者有任何建议和反馈,欢迎通过[GitHub Issues](https://github.com/pengwon/ch347-py/issues)与我们联系。 -------------------------------------------------------------------------------- /tests/test_ina226.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import matplotlib.pyplot as plt 4 | import matplotlib.animation as animation 5 | import numpy as np 6 | from time import sleep 7 | 8 | # Get the parent directory's path 9 | parent_directory = os.path.abspath(os.path.join(os.path.dirname(__file__), '..')) 10 | 11 | # Add the parent directory to the system path if not already present 12 | if parent_directory not in sys.path: 13 | sys.path.insert(0, parent_directory) 14 | 15 | from i2c_devices.ina226 import INA226 16 | 17 | # Initialize the INA226 sensor 18 | sensor = INA226() 19 | 20 | def read_sensor_data(): 21 | return [sensor.get_bus_voltage(), sensor.get_current(), sensor.get_power()] 22 | 23 | # Generator function to produce data from the sensor sensor 24 | def generate_sensor_data(): 25 | data_buffer = [] 26 | while True: 27 | data = read_sensor_data() 28 | data_buffer.append(data) 29 | yield data_buffer 30 | sleep(0.1) 31 | 32 | # Create a figure with 6 subplots for accelerometer and gyroscope data 33 | fig, axs = plt.subplots(3, 1, figsize=(8, 12)) 34 | 35 | # Initialize empty lines for the accelerometer and gyroscope data plots 36 | lines = [axs[i].plot([], [], lw=2)[0] for i in range(3)] 37 | 38 | # Set the number of data points to be displayed on the plot 39 | num_display_points = 50 40 | 41 | def init(): 42 | for line in lines: 43 | line.set_data([], []) 44 | return lines 45 | 46 | def update(frame): 47 | data_buffer = next(data_generator) 48 | 49 | # Generate the x-axis values (time steps) based on the number of data points 50 | time_steps = np.arange(len(data_buffer)) 51 | 52 | # Get the starting index to display a specific number of data points 53 | start_index = max(0, len(data_buffer) - num_display_points) 54 | 55 | # Update the plot data for accelerometer and gyroscope 56 | for i in range(3): 57 | lines[i].set_data(time_steps[start_index:], [data[i] for data in data_buffer[start_index:]]) 58 | axs[i].set_xlim(start_index, start_index + num_display_points - 1) 59 | 60 | # Update the x-axis limits for scrolling effect 61 | axs[0].set_ylim(0, 10000) 62 | axs[1].set_ylim(0, 100000) 63 | axs[2].set_ylim(0, 200) 64 | 65 | return lines 66 | 67 | # Create the generator for sensor sensor data 68 | data_generator = generate_sensor_data() 69 | 70 | # Create an animation for real-time plotting, update every 100 milliseconds (0.1 seconds) 71 | ani = animation.FuncAnimation(fig, update, frames=range(100), init_func=init, blit=True, interval=100) 72 | 73 | # Add labels and title to each subplot 74 | axis_labels = ['Voltage in mV', 'Current in uA', 'Power in mW'] 75 | for i in range(3): 76 | axs[i].set_title(f'{axis_labels[i]}') 77 | axs[i].set_xlabel('Time Steps') 78 | axs[i].set_ylabel('INA226 Data Value') 79 | 80 | plt.tight_layout() 81 | plt.show() 82 | sensor.close() 83 | -------------------------------------------------------------------------------- /tests/test_uart.py: -------------------------------------------------------------------------------- 1 | #connect GPIO PIN 4 and PIN 7 for testing interrupt callback 2 | 3 | import os 4 | import sys 5 | import random 6 | import string 7 | import ctypes 8 | import time 9 | import threading 10 | import msvcrt # Windows only, for keyboard input 11 | 12 | 13 | def send_random_uart(ch347, chip_select=0, length=16): 14 | rand_str = ''.join(random.choice(string.ascii_letters + string.digits) for _ in range(length)) 15 | data = rand_str.encode('utf-8') 16 | length_list = [len(data)] 17 | result = ch347.uart_write(data, length_list) 18 | print(f"[Sender] Sent ({length_list[0]} bytes): {rand_str} ") 19 | return result 20 | 21 | def uart_reader(ch347, stop_event, read_size=64): 22 | while not stop_event.is_set(): 23 | read_len = [read_size] 24 | buffer = (ctypes.c_char * read_size)() 25 | result = ch347.uart_read(buffer, read_len) 26 | if result and read_len[0] > 0: 27 | data = bytes(buffer[:read_len[0]]).decode('utf-8', errors='ignore') 28 | print(f"[Reader] Recv ({read_len[0]}) bytes: {data}") 29 | time.sleep(0.1) 30 | 31 | def uart_writer(ch347, stop_event, length=16): 32 | while not stop_event.is_set(): 33 | send_random_uart(ch347, length) 34 | time.sleep(1) 35 | 36 | 37 | def uart_loop_multithread(ch347): 38 | stop_event = threading.Event() 39 | 40 | reader_thread = threading.Thread(target=uart_reader, args=(ch347, stop_event)) 41 | writer_thread = threading.Thread(target=uart_writer, args=(ch347, stop_event)) 42 | 43 | reader_thread.start() 44 | writer_thread.start() 45 | 46 | print("Sending every 1s, reading every 0.1s. Press 'x' to stop.") 47 | while True: 48 | if msvcrt.kbhit(): 49 | if msvcrt.getwch().lower() == 'x': 50 | print("Exit requested.") 51 | stop_event.set() 52 | break 53 | time.sleep(0.05) 54 | 55 | reader_thread.join() 56 | writer_thread.join() 57 | print("Program terminated.") 58 | 59 | 60 | # Get the parent directory's path 61 | parent_directory = os.path.abspath(os.path.join(os.path.dirname(__file__), '..')) 62 | 63 | # Add the parent directory to the system path if not already present 64 | if parent_directory not in sys.path: 65 | sys.path.insert(0, parent_directory) 66 | 67 | from ch347 import CH347 68 | 69 | 70 | ch347 = CH347() 71 | 72 | print("version:", ch347.get_version()) 73 | 74 | status = ch347.uart_open() 75 | print("uart_open", status) 76 | 77 | status = ch347.uart_init(115200, 8, 0, 1, 5000) 78 | print("uart_init", status) 79 | 80 | # Get device information 81 | device_info = ch347.uart_get_device_info() 82 | print("uart_get_device_info", device_info) 83 | 84 | 85 | status = ch347.uart_set_timeout(500,500) 86 | print("uart_set_timeout", status) 87 | 88 | uart_loop_multithread(ch347) 89 | 90 | status = ch347.uart_close() 91 | print("uart_close", status) 92 | 93 | 94 | 95 | 96 | -------------------------------------------------------------------------------- /README_en.md: -------------------------------------------------------------------------------- 1 | # ch347-py 2 | 3 | [中文](README.md) 4 | 5 | ## Introduction 6 | 7 | `ch347-py` is a Python binding library for driving the CH347 chip. It provides a simple way to interact with the CH347 chip, and can be used to develop applications for I2C, SPI, and other communication interfaces. 8 | 9 | ## Installation 10 | 11 | ### From Source 12 | 13 | ```bash 14 | git clone https://github.com/pengwon/ch347-py.git 15 | cd ch347-py 16 | ``` 17 | 18 | ### Using pip 19 | 20 | [TODO] 21 | 22 | You can install this library using pip: 23 | 24 | ```bash 25 | pip install ch347-py 26 | ``` 27 | 28 | ## Usage 29 | 30 | First, you need to import the `CH347` class from the `ch347` module: 31 | 32 | ```python 33 | from ch347 import CH347 34 | ``` 35 | 36 | Then, you can create an instance of the `CH347` class and use its methods to interact with the CH347 chip. For example: 37 | 38 | ```python 39 | driver = CH347() 40 | driver.spi_write(0x00, [0xFF] * 10) 41 | ``` 42 | 43 | ## Testing 44 | 45 | This project includes some test scripts. You can run these scripts to test and demonstrate how to use this library. 46 | 47 | ## References 48 | 49 | 1. [Reading and displaying data from the MPU6050 sensor](https://mp.weixin.qq.com/s?__biz=MzA3NzMyNTIyOA==&mid=2651481825&idx=1&sn=bbe2833173dcf0420eaac9faff14ca8b&chksm=84ad7022b3daf9349f8e57e87abacef833bd742f655a60379e07e8ee2529cb012d807a132a92#rd) 50 | 2. [Reading and writing SPI Flash](https://mp.weixin.qq.com/s?__biz=MzA3NzMyNTIyOA==&mid=2651481820&idx=1&sn=b28a3b59fa7104383ced33ddf9cee7c5&chksm=84ad701fb3daf9099a0245f143c6fc3f5398af3657cf028e052fa3ad944aa510730fee319fa8#rd) 51 | 3. [Connecting MPU6050](https://mp.weixin.qq.com/s?__biz=MzA3NzMyNTIyOA==&mid=2651481772&idx=1&sn=509448890675dbcf03ed1ee877d0bff2&chksm=84ad706fb3daf979f6c5e74e8ec46f09307ffdd358026a33ac98bd13196a79fbabc2db6d746f#rd) 52 | 4. [Using the INA226 ammeter](https://mp.weixin.qq.com/s?__biz=MzA3NzMyNTIyOA==&mid=2651482424&idx=1&sn=5676fb2123294c17a611775965b1f1b1&chksm=84ad7ffbb3daf6eda6cc972bc08e5b0d0781651bada60ca906423fd59090a7f3794fed190d9a#rd) 53 | 5. [Detailed explanation of I2C](https://mp.weixin.qq.com/s?__biz=MzA3NzMyNTIyOA==&mid=2651481759&idx=1&sn=562c0748c593e1de21487a538ace637a&chksm=84ad705cb3daf94a2d3eb64d94acc374ea5ff3808b8f5800b4730d9ff02c9a0e1feea61e1566#rd) 54 | 6. [Python encapsulation of dynamic libraries](https://mp.weixin.qq.com/s?__biz=MzA3NzMyNTIyOA==&mid=2651481701&idx=1&sn=2ddf1ce70703550bbcaeb7bed4aa0211&chksm=84ad70a6b3daf9b036b859b8b4c621c7a8db6a32ca1e04bd9369b7dc125e17ed16f3ebddc608#rd) 55 | 7. [Detailed explanation of SPI](https://mp.weixin.qq.com/s?__biz=MzA3NzMyNTIyOA==&mid=2651481820&idx=2&sn=4d5c44f98a4b174b867b9c05408207d9&chksm=84ad701fb3daf90966a02142c32b14a628b1d8b4564ac448925ccb77796800d6622a071421ee#rd) 56 | 57 | ## License 58 | 59 | The license used by this project is given in the [`LICENSE`](LICENSE) file. 60 | 61 | ## Contributions 62 | 63 | Contributions to this project are welcome. Before submitting a [Pull Request](https://github.com/pengwon/ch347-py/pulls), please make sure your code passes all tests. 64 | 65 | ## Contact 66 | 67 | If you encounter any problems while using this library, or have any suggestions and feedback, please contact us through [GitHub Issues](https://github.com/pengwon/ch347-py/issues). -------------------------------------------------------------------------------- /tests/test_mpu6050.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import matplotlib.pyplot as plt 4 | import matplotlib.animation as animation 5 | import numpy as np 6 | from time import sleep 7 | 8 | # Get the parent directory's path 9 | parent_directory = os.path.abspath(os.path.join(os.path.dirname(__file__), '..')) 10 | 11 | # Add the parent directory to the system path if not already present 12 | if parent_directory not in sys.path: 13 | sys.path.insert(0, parent_directory) 14 | 15 | from i2c_devices.mpu6050 import MPU6050 16 | 17 | # Initialize the MPU6050 sensor 18 | mpu6050 = MPU6050() 19 | 20 | # Replace these lines with the actual import and initialization of the MPU6050 sensor 21 | # For demonstration purposes, we use dummy functions to generate random data 22 | def read_mpu6050_accel_data(): 23 | accel_data = mpu6050.get_accel_data(True) 24 | return [accel_data['x'], accel_data["y"], accel_data["z"]] 25 | 26 | def read_mpu6050_gyro_data(): 27 | gyro_data = mpu6050.get_gyro_data() 28 | return [gyro_data['x'], gyro_data["y"], gyro_data["z"]] 29 | 30 | # Generator function to produce data from the MPU6050 sensor 31 | def generate_mpu6050_data(): 32 | data_buffer = [] 33 | while True: 34 | accel_data = read_mpu6050_accel_data() 35 | gyro_data = read_mpu6050_gyro_data() 36 | data_buffer.append(accel_data + gyro_data) 37 | yield data_buffer 38 | sleep(0.1) 39 | 40 | # Create a figure with 6 subplots for accelerometer and gyroscope data 41 | fig, axs = plt.subplots(6, 1, figsize=(8, 12)) 42 | 43 | # Initialize empty lines for the accelerometer and gyroscope data plots 44 | lines = [axs[i].plot([], [], lw=2)[0] for i in range(6)] 45 | 46 | # Set the number of data points to be displayed on the plot 47 | num_display_points = 50 48 | 49 | def init(): 50 | for line in lines: 51 | line.set_data([], []) 52 | return lines 53 | 54 | def update(frame): 55 | data_buffer = next(data_generator) 56 | 57 | # Generate the x-axis values (time steps) based on the number of data points 58 | time_steps = np.arange(len(data_buffer)) 59 | 60 | # Get the starting index to display a specific number of data points 61 | start_index = max(0, len(data_buffer) - num_display_points) 62 | 63 | # Update the plot data for accelerometer and gyroscope 64 | for i in range(6): 65 | lines[i].set_data(time_steps[start_index:], [data[i] for data in data_buffer[start_index:]]) 66 | 67 | # Adjust the plot limits for better visualization 68 | if i < 3: 69 | axs[i].set_ylim(-2, 2) # Accelerometer data range: -2 to +2 70 | else: 71 | axs[i].set_ylim(-200, 200) # Gyroscope data range: -200 to +200 72 | axs[i].set_xlim(start_index, start_index + num_display_points - 1) 73 | 74 | return lines 75 | 76 | # Create the generator for MPU6050 sensor data 77 | data_generator = generate_mpu6050_data() 78 | 79 | # Create an animation for real-time plotting, update every 100 milliseconds (0.1 seconds) 80 | ani = animation.FuncAnimation(fig, update, frames=range(100), init_func=init, blit=True, interval=100) 81 | 82 | # Add labels and title to each subplot 83 | axis_labels = ['AX', 'AY', 'AZ', 'GX', 'GY', 'GZ'] 84 | for i in range(6): 85 | axs[i].set_title(f'{axis_labels[i]} Data') 86 | axs[i].set_xlabel('Time Steps') 87 | axs[i].set_ylabel('MPU6050 Data Value') 88 | 89 | plt.tight_layout() 90 | plt.show() 91 | mpu6050.close() 92 | -------------------------------------------------------------------------------- /spi_devices/sd_nand/sd_nand.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import os 3 | 4 | # Get the parent directory's path 5 | parent_directory = os.path.abspath(os.path.join(os.path.dirname(__file__), "../..")) 6 | 7 | # Add the parent directory to the system path if not already present 8 | if parent_directory not in sys.path: 9 | sys.path.insert(0, parent_directory) 10 | 11 | import ch347 12 | 13 | 14 | class Response: 15 | def __init__(self, data): 16 | self.data = data 17 | 18 | def get_raw_data(self): 19 | return self.data 20 | 21 | def parse(self): 22 | raise NotImplementedError("Subclasses should implement this!") 23 | 24 | 25 | class R1Response(Response): 26 | def parse(self): 27 | # Parse the data for an R1 response 28 | pass 29 | 30 | 31 | class R2Response(Response): 32 | def parse(self): 33 | # Parse the data for an R2 response 34 | pass 35 | 36 | 37 | class R3Response(Response): 38 | def parse(self): 39 | # Parse the data for an R3 response 40 | pass 41 | 42 | 43 | class R6Response(Response): 44 | def parse(self): 45 | # Parse the data for an R4 response 46 | pass 47 | 48 | 49 | class R7Response(Response): 50 | def parse(self): 51 | # Parse the data for an R7 response 52 | pass 53 | 54 | 55 | class SD_NAND: 56 | def __init__(self, cs=0, driver=ch347.CH347()): 57 | spi_config = ch347.SPIConfig( 58 | Mode=0, 59 | Clock=2, 60 | ByteOrder=1, 61 | SpiWriteReadInterval=0, 62 | SpiOutDefaultData=0xFF, 63 | ChipSelect=0x80 if cs == 0 else 0x81, 64 | CS1Polarity=0, 65 | CS2Polarity=0, 66 | IsAutoDeative=1, 67 | ActiveDelay=0, 68 | DelayDeactive=0, 69 | ) 70 | self.driver = driver 71 | self.driver.open_device() 72 | self.driver.spi_init(spi_config) 73 | 74 | def _crc7(data): 75 | crc = 0 76 | for byte in data: 77 | crc ^= byte 78 | for _ in range(8): 79 | if crc & 0x80: 80 | crc ^= 0x09 81 | crc <<= 1 82 | else: 83 | crc <<= 1 84 | return (crc >> 1) & 0x7F 85 | 86 | def _send_cmd(self, cmd, arg, is_acmd=False) -> Response: 87 | if is_acmd: 88 | self.send_cmd(55, 0) 89 | chip_select = 0x80 # Replace with the correct value for your application 90 | data = [cmd] + [(arg >> (24 - (8 * i))) & 0xFF for i in range(4)] 91 | # SPI模式下仅CMD0命令需要校验CRC,其余命令均忽略, CMD0命令CRC固定为0x95 92 | # crc = self._crc7(data) 93 | # data.append((crc << 1) | 0x01) 94 | data.append(0x95) 95 | if cmd == 8: 96 | return R7Response(self.driver.spi_read(chip_select, data, 9)) 97 | elif cmd == 58: 98 | return R3Response(self.driver.spi_read(chip_select, data, 9)) 99 | elif cmd == 13: 100 | return R2Response(self.driver.spi_read(chip_select, data, 10)) 101 | else: 102 | return R1Response(self.driver.spi_read(chip_select, data, 9)) 103 | 104 | def initialize(self): 105 | # self.driver.spi_change_cs(1) 106 | self.driver.spi_write(0x00, [0xFF] * 10) 107 | response = self._send_cmd(0, 0) # CMD0: GO_IDLE_STATE 108 | print(response.get_raw_data()) 109 | # self.send_cmd(8, 0x1AA) # CMD8: SEND_IF_COND 110 | # self.send_cmd(55, 0) # CMD55: APP_CMD 111 | # self.send_cmd(41, 0x40000000) # ACMD41: SD_SEND_OP_COND 112 | # while True: 113 | # response = self.read_register(41, 1) 114 | # if response[0] & 0x80: 115 | # break 116 | # self.send_cmd(2, 0) # CMD2: ALL_SEND_CID 117 | # self.send_cmd(3, 0) # CMD3: SEND_RELATIVE_ADDR 118 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | share/python-wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | MANIFEST 28 | 29 | # PyInstaller 30 | # Usually these files are written by a python script from a template 31 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 32 | *.manifest 33 | *.spec 34 | 35 | # Installer logs 36 | pip-log.txt 37 | pip-delete-this-directory.txt 38 | 39 | # Unit test / coverage reports 40 | htmlcov/ 41 | .tox/ 42 | .nox/ 43 | .coverage 44 | .coverage.* 45 | .cache 46 | nosetests.xml 47 | coverage.xml 48 | *.cover 49 | *.py,cover 50 | .hypothesis/ 51 | .pytest_cache/ 52 | cover/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | .pybuilder/ 76 | target/ 77 | 78 | # Jupyter Notebook 79 | .ipynb_checkpoints 80 | 81 | # IPython 82 | profile_default/ 83 | ipython_config.py 84 | 85 | # pyenv 86 | # For a library or package, you might want to ignore these files since the code is 87 | # intended to run in multiple environments; otherwise, check them in: 88 | # .python-version 89 | 90 | # pipenv 91 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 92 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 93 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 94 | # install all needed dependencies. 95 | #Pipfile.lock 96 | 97 | # poetry 98 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. 99 | # This is especially recommended for binary packages to ensure reproducibility, and is more 100 | # commonly ignored for libraries. 101 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control 102 | #poetry.lock 103 | 104 | # pdm 105 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. 106 | #pdm.lock 107 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it 108 | # in version control. 109 | # https://pdm.fming.dev/#use-with-ide 110 | .pdm.toml 111 | 112 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm 113 | __pypackages__/ 114 | 115 | # Celery stuff 116 | celerybeat-schedule 117 | celerybeat.pid 118 | 119 | # SageMath parsed files 120 | *.sage.py 121 | 122 | # Environments 123 | .env 124 | .venv 125 | env/ 126 | venv/ 127 | ENV/ 128 | env.bak/ 129 | venv.bak/ 130 | 131 | # Spyder project settings 132 | .spyderproject 133 | .spyproject 134 | 135 | # Rope project settings 136 | .ropeproject 137 | 138 | # mkdocs documentation 139 | /site 140 | 141 | # mypy 142 | .mypy_cache/ 143 | .dmypy.json 144 | dmypy.json 145 | 146 | # Pyre type checker 147 | .pyre/ 148 | 149 | # pytype static type analyzer 150 | .pytype/ 151 | 152 | # Cython debug symbols 153 | cython_debug/ 154 | 155 | # PyCharm 156 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can 157 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 158 | # and can be added to the global gitignore or merged into this file. For a more nuclear 159 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 160 | #.idea/ 161 | 162 | # EXCEPT CH347 DLL 163 | !ch347/lib/ 164 | !ch347/lib/*.DLL 165 | -------------------------------------------------------------------------------- /i2c_devices/mpu6050/mpu6050.py: -------------------------------------------------------------------------------- 1 | """ 2 | This program handles the communication over I2C 3 | between a CH347 and a MPU-6050 Gyroscope / Accelerometer combo. 4 | 5 | Released under the MIT License 6 | 7 | Thanks for Martijn (martijn@mrtijn.nl) and contributers 8 | https://github.com/m-rtijn/mpu6050 9 | 10 | """ 11 | 12 | import sys 13 | import os 14 | 15 | # Get the parent directory's path 16 | parent_directory = os.path.abspath(os.path.join(os.path.dirname(__file__), '../..')) 17 | 18 | # Add the parent directory to the system path if not already present 19 | if parent_directory not in sys.path: 20 | sys.path.insert(0, parent_directory) 21 | 22 | import ch347 23 | 24 | class MPU6050: 25 | 26 | # Global Variables 27 | GRAVITIY_MS2 = 9.80665 28 | address = None 29 | driver = None 30 | 31 | # Scale Modifiers 32 | ACCEL_SCALE_MODIFIER_2G = 16384.0 33 | ACCEL_SCALE_MODIFIER_4G = 8192.0 34 | ACCEL_SCALE_MODIFIER_8G = 4096.0 35 | ACCEL_SCALE_MODIFIER_16G = 2048.0 36 | 37 | GYRO_SCALE_MODIFIER_250DEG = 131.0 38 | GYRO_SCALE_MODIFIER_500DEG = 65.5 39 | GYRO_SCALE_MODIFIER_1000DEG = 32.8 40 | GYRO_SCALE_MODIFIER_2000DEG = 16.4 41 | 42 | # Pre-defined ranges 43 | ACCEL_RANGE_2G = 0x00 44 | ACCEL_RANGE_4G = 0x08 45 | ACCEL_RANGE_8G = 0x10 46 | ACCEL_RANGE_16G = 0x18 47 | 48 | GYRO_RANGE_250DEG = 0x00 49 | GYRO_RANGE_500DEG = 0x08 50 | GYRO_RANGE_1000DEG = 0x10 51 | GYRO_RANGE_2000DEG = 0x18 52 | 53 | FILTER_BW_256=0x00 54 | FILTER_BW_188=0x01 55 | FILTER_BW_98=0x02 56 | FILTER_BW_42=0x03 57 | FILTER_BW_20=0x04 58 | FILTER_BW_10=0x05 59 | FILTER_BW_5=0x06 60 | 61 | # MPU-6050 Registers 62 | PWR_MGMT_1 = 0x6B 63 | PWR_MGMT_2 = 0x6C 64 | 65 | ACCEL_XOUT0 = 0x3B 66 | ACCEL_YOUT0 = 0x3D 67 | ACCEL_ZOUT0 = 0x3F 68 | 69 | TEMP_OUT0 = 0x41 70 | 71 | GYRO_XOUT0 = 0x43 72 | GYRO_YOUT0 = 0x45 73 | GYRO_ZOUT0 = 0x47 74 | 75 | ACCEL_CONFIG = 0x1C 76 | GYRO_CONFIG = 0x1B 77 | MPU_CONFIG = 0x1A 78 | 79 | def __init__(self, address=0x68, driver=ch347.CH347()): 80 | self.address = address << 1 81 | self.driver = driver 82 | 83 | self.driver.open_device() 84 | # Wake up the MPU-6050 since it starts in sleep mode 85 | self.driver.stream_i2c([self.address, self.PWR_MGMT_1, 0x00], 0) 86 | 87 | # I2C communication methods 88 | 89 | def read_byte_data(self, register): 90 | raw_data = self.driver.stream_i2c([self.address, register], 1) 91 | return raw_data[0] 92 | 93 | def write_byte_data(self, register, value): 94 | return self.driver.stream_i2c([self.address, register, value], 0) 95 | 96 | def read_i2c_word(self, register): 97 | """ 98 | Read two i2c registers and combine them. 99 | 100 | register -- the first register to read from. 101 | Returns the combined read results. 102 | """ 103 | 104 | # Read the data from the registers 105 | # high = self.read_byte_data(self.address, register) 106 | # low = self.read_byte_data(self.address, register + 1) 107 | raw_data = self.driver.stream_i2c([self.address, register], 2) 108 | 109 | # value = (high << 8) + low 110 | value = raw_data[0] << 8 | raw_data[1] 111 | 112 | if (value >= 0x8000): 113 | return -((65535 - value) + 1) 114 | else: 115 | return value 116 | 117 | # MPU-6050 Methods 118 | 119 | def get_temp(self): 120 | """ 121 | Reads the temperature from the onboard temperature sensor of the MPU-6050. 122 | 123 | Returns the temperature in degrees Celcius. 124 | """ 125 | 126 | raw_temp = self.read_i2c_word(self.TEMP_OUT0) 127 | # raw_temp = self.driver.stream_i2c([self.address, self.TEMP_OUT0], 2) 128 | # temp = raw_temp[0] << 8 | raw_temp[1] 129 | 130 | # Get the actual temperature using the formule given in the 131 | # MPU-6050 Register Map and Descriptions revision 4.2, page 30 132 | actual_temp = (raw_temp / 340.0) + 36.53 133 | 134 | return actual_temp 135 | 136 | def set_accel_range(self, accel_range): 137 | """ 138 | Sets the range of the accelerometer to range. 139 | 140 | accel_range -- the range to set the accelerometer to. Using a 141 | pre-defined range is advised. 142 | """ 143 | 144 | # First change it to 0x00 to make sure we write the correct value later 145 | self.write_byte_data(self.ACCEL_CONFIG, 0x00) 146 | 147 | # Write the new range to the ACCEL_CONFIG register 148 | self.write_byte_data(self.ACCEL_CONFIG, accel_range) 149 | 150 | def read_accel_range(self, raw = False): 151 | """ 152 | Reads the range the accelerometer is set to. 153 | 154 | If raw is True, it will return the raw value from the ACCEL_CONFIG 155 | register 156 | If raw is False, it will return an integer: -1, 2, 4, 8 or 16. When it 157 | returns -1 something went wrong. 158 | """ 159 | 160 | raw_data = self.read_byte_data(self.ACCEL_CONFIG) 161 | 162 | if raw is True: 163 | return raw_data 164 | elif raw is False: 165 | if raw_data == self.ACCEL_RANGE_2G: 166 | return 2 167 | elif raw_data == self.ACCEL_RANGE_4G: 168 | return 4 169 | elif raw_data == self.ACCEL_RANGE_8G: 170 | return 8 171 | elif raw_data == self.ACCEL_RANGE_16G: 172 | return 16 173 | else: 174 | return -1 175 | 176 | def get_accel_data(self, g = False): 177 | """ 178 | Gets and returns the X, Y and Z values from the accelerometer. 179 | 180 | If g is True, it will return the data in g 181 | If g is False, it will return the data in m/s^2 182 | Returns a dictionary with the measurement results. 183 | """ 184 | 185 | x = self.read_i2c_word(self.ACCEL_XOUT0) 186 | y = self.read_i2c_word(self.ACCEL_YOUT0) 187 | z = self.read_i2c_word(self.ACCEL_ZOUT0) 188 | 189 | accel_scale_modifier = None 190 | accel_range = self.read_accel_range(True) 191 | 192 | if accel_range == self.ACCEL_RANGE_2G: 193 | accel_scale_modifier = self.ACCEL_SCALE_MODIFIER_2G 194 | elif accel_range == self.ACCEL_RANGE_4G: 195 | accel_scale_modifier = self.ACCEL_SCALE_MODIFIER_4G 196 | elif accel_range == self.ACCEL_RANGE_8G: 197 | accel_scale_modifier = self.ACCEL_SCALE_MODIFIER_8G 198 | elif accel_range == self.ACCEL_RANGE_16G: 199 | accel_scale_modifier = self.ACCEL_SCALE_MODIFIER_16G 200 | else: 201 | print("Unkown range - accel_scale_modifier set to self.ACCEL_SCALE_MODIFIER_2G") 202 | accel_scale_modifier = self.ACCEL_SCALE_MODIFIER_2G 203 | 204 | x = x / accel_scale_modifier 205 | y = y / accel_scale_modifier 206 | z = z / accel_scale_modifier 207 | 208 | if g is True: 209 | return {'x': x, 'y': y, 'z': z} 210 | elif g is False: 211 | x = x * self.GRAVITIY_MS2 212 | y = y * self.GRAVITIY_MS2 213 | z = z * self.GRAVITIY_MS2 214 | return {'x': x, 'y': y, 'z': z} 215 | 216 | def set_gyro_range(self, gyro_range): 217 | """ 218 | Sets the range of the gyroscope to range. 219 | 220 | gyro_range -- the range to set the gyroscope to. Using a pre-defined 221 | range is advised. 222 | """ 223 | 224 | # First change it to 0x00 to make sure we write the correct value later 225 | self.write_byte_data(self.GYRO_CONFIG, 0x00) 226 | 227 | # Write the new range to the ACCEL_CONFIG register 228 | self.write_byte_data(self.GYRO_CONFIG, gyro_range) 229 | 230 | def set_filter_range(self, filter_range=FILTER_BW_256): 231 | """ 232 | Sets the low-pass bandpass filter frequency""" 233 | 234 | # Keep the current EXT_SYNC_SET configuration in bits 3, 4, 5 in the MPU_CONFIG register 235 | EXT_SYNC_SET = self.read_byte_data(self.MPU_CONFIG) & 0b00111000 236 | return self.write_byte_data(self.MPU_CONFIG, EXT_SYNC_SET | filter_range) 237 | 238 | 239 | def read_gyro_range(self, raw = False): 240 | """ 241 | Reads the range the gyroscope is set to. 242 | 243 | If raw is True, it will return the raw value from the GYRO_CONFIG 244 | register. 245 | If raw is False, it will return 250, 500, 1000, 2000 or -1. If the 246 | returned value is equal to -1 something went wrong. 247 | """ 248 | 249 | raw_data = self.read_byte_data(self.GYRO_CONFIG) 250 | 251 | if raw is True: 252 | return raw_data 253 | elif raw is False: 254 | if raw_data == self.GYRO_RANGE_250DEG: 255 | return 250 256 | elif raw_data == self.GYRO_RANGE_500DEG: 257 | return 500 258 | elif raw_data == self.GYRO_RANGE_1000DEG: 259 | return 1000 260 | elif raw_data == self.GYRO_RANGE_2000DEG: 261 | return 2000 262 | else: 263 | return -1 264 | 265 | def get_gyro_data(self): 266 | """ 267 | Gets and returns the X, Y and Z values from the gyroscope. 268 | 269 | Returns the read values in a dictionary. 270 | """ 271 | 272 | x = self.read_i2c_word(self.GYRO_XOUT0) 273 | y = self.read_i2c_word(self.GYRO_YOUT0) 274 | z = self.read_i2c_word(self.GYRO_ZOUT0) 275 | 276 | gyro_scale_modifier = None 277 | gyro_range = self.read_gyro_range(True) 278 | 279 | if gyro_range == self.GYRO_RANGE_250DEG: 280 | gyro_scale_modifier = self.GYRO_SCALE_MODIFIER_250DEG 281 | elif gyro_range == self.GYRO_RANGE_500DEG: 282 | gyro_scale_modifier = self.GYRO_SCALE_MODIFIER_500DEG 283 | elif gyro_range == self.GYRO_RANGE_1000DEG: 284 | gyro_scale_modifier = self.GYRO_SCALE_MODIFIER_1000DEG 285 | elif gyro_range == self.GYRO_RANGE_2000DEG: 286 | gyro_scale_modifier = self.GYRO_SCALE_MODIFIER_2000DEG 287 | else: 288 | print("Unkown range - gyro_scale_modifier set to self.GYRO_SCALE_MODIFIER_250DEG") 289 | gyro_scale_modifier = self.GYRO_SCALE_MODIFIER_250DEG 290 | 291 | x = x / gyro_scale_modifier 292 | y = y / gyro_scale_modifier 293 | z = z / gyro_scale_modifier 294 | 295 | return {'x': x, 'y': y, 'z': z} 296 | 297 | def get_all_data(self): 298 | """ 299 | Reads and returns all the available data. 300 | """ 301 | 302 | temp = self.get_temp() 303 | accel = self.get_accel_data() 304 | gyro = self.get_gyro_data() 305 | 306 | return [accel, gyro, temp] 307 | 308 | def close(self): 309 | self.driver.close_device() 310 | 311 | if __name__ == "__main__": 312 | mpu = MPU6050() 313 | print(mpu.get_temp()) 314 | accel_data = mpu.get_accel_data(True) 315 | print(accel_data['x']) 316 | print(accel_data['y']) 317 | print(accel_data['z']) 318 | gyro_data = mpu.get_gyro_data() 319 | print(gyro_data['x']) 320 | print(gyro_data['y']) 321 | print(gyro_data['z']) 322 | mpu.close() 323 | -------------------------------------------------------------------------------- /i2c_devices/ina226/ina226.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import os 3 | 4 | # Get the parent directory's path 5 | parent_directory = os.path.abspath(os.path.join(os.path.dirname(__file__), '../..')) 6 | 7 | # Add the parent directory to the system path if not already present 8 | if parent_directory not in sys.path: 9 | sys.path.insert(0, parent_directory) 10 | 11 | import ch347 12 | 13 | class INA226: 14 | """ 15 | INA226 Sensor Driver. 16 | 17 | This class provides methods for interacting with the INA226 sensor 18 | using the CH347 I2C driver. 19 | 20 | Attributes: 21 | CONFIG_REG (int): Address of the configuration register. 22 | SHUNT_VOLTAGE_REG (int): Address of the shunt voltage register. 23 | BUS_VOLTAGE_REG (int): Address of the bus voltage register. 24 | POWER_REG (int): Address of the power register. 25 | CURRENT_REG (int): Address of the current register. 26 | CALIBRATION_REG (int): Address of the calibration register. 27 | MASK_ENABLE_REG (int): Address of the mask/enable register. 28 | ALERT_LIMIT_REG (int): Address of the alert limit register. 29 | MANUFACTURER_ID_REG (int): Address of the manufacturer ID register. 30 | DIE_ID_REG (int): Address of the die ID register. 31 | """ 32 | CONFIG_REG = 0x00 33 | SHUNT_VOLTAGE_REG = 0x01 34 | BUS_VOLTAGE_REG = 0x02 35 | POWER_REG = 0x03 36 | CURRENT_REG = 0x04 37 | CALIBRATION_REG = 0x05 38 | MASK_ENABLE_REG = 0x06 39 | ALERT_LIMIT_REG = 0x07 40 | MANUFACTURER_ID_REG = 0xfe 41 | DIE_ID_REG = 0xff 42 | 43 | def __init__(self, address=0x40, r_shunt=20, driver=ch347.CH347()): 44 | """ 45 | Initialize the INA226 driver. 46 | 47 | Args: 48 | address (int): I2C address of the INA226 sensor (default is 0x40). 49 | r_shunt (int): Value of the shunt resistor in milliohm (mΩ) (default is 100). 50 | driver: An instance of the CH347 driver (default is a new instance). 51 | """ 52 | self.address = address << 1 53 | self.r_shunt = r_shunt 54 | self.driver = driver 55 | self.driver.open_device() 56 | self.set_calibration(2048) 57 | 58 | def i2c_read_word(self, register): 59 | """ 60 | Read a 16-bit word from the specified register via I2C. 61 | 62 | Args: 63 | register (int): Register address to read from. 64 | 65 | Returns: 66 | int: The read 16-bit word value. 67 | """ 68 | raw_data = self.driver.stream_i2c([self.address, register], 2) 69 | value = (raw_data[0] << 8) | raw_data[1] 70 | return value 71 | 72 | def i2c_write_word(self, register, value): 73 | """ 74 | Write a 16-bit word to the specified register via I2C. 75 | 76 | Args: 77 | register (int): Register address to write to. 78 | value (int): 16-bit value to write. 79 | 80 | Returns: 81 | int: Result code (0 for success, -1 for failure). 82 | """ 83 | if not (0 <= value <= 65535): 84 | raise ValueError("Value out of range") 85 | 86 | byte1 = value >> 8 87 | byte2 = value & 0xff 88 | return self.driver.stream_i2c([self.address, register, byte1, byte2], 0) 89 | 90 | def reset(self): 91 | return self.i2c_write_word(self.CONFIG_REG, 0x8000) 92 | 93 | 94 | def get_config(self): 95 | """ 96 | Get the current configuration of the INA226 sensor. 97 | 98 | The Configuration Register settings control the operating modes for the device. This register controls the 99 | conversion time settings for both the shunt and bus voltage measurements as well as the averaging mode used. 100 | The operating mode that controls what signals are selected to be measured is also programmed in the 101 | Configuration Register . 102 | 103 | The Configuration Register can be read from at any time without impacting or affecting the device settings or a 104 | conversion in progress. Writing to the Configuration Register halts any conversion in progress until the write 105 | sequence is completed resulting in a new conversion starting based on the new contents of the Configuration 106 | Register (00h). This halt prevents any uncertainty in the conditions used for the next completed conversion. 107 | 108 | Returns: 109 | dict: A dictionary with the following keys: 110 | 111 | - "reset" (bool): Reset Bit. True if the reset bit (Bit 15) is set to '1'. Generates a system reset that is 112 | the same as power-on reset. Resets all registers to default values; this bit self-clears. 113 | 114 | - "avg" (int): Averaging Mode. Bits 9-11 determine the number of samples that are collected and averaged. 115 | Combinations: 116 | 0: 1 (default) 117 | 1: 4 118 | 2: 16 119 | 3: 64 120 | 4: 128 121 | 5: 256 122 | 6: 512 123 | 7: 1024 124 | 125 | - "vbus_ct" (int): Bus Voltage Conversion Time. Bits 6-8 set the conversion time for the bus voltage measurement. 126 | Combinations: 127 | 0: 140 µs 128 | 1: 204 µs 129 | 2: 332 µs 130 | 3: 588 µs 131 | 4: 1.1 ms (default) 132 | 5: 2.116 ms 133 | 6: 4.156 ms 134 | 7: 8.244 ms 135 | 136 | - "vsh_ct" (int): Shunt Voltage Conversion Time. Bits 3-5 set the conversion time for the shunt voltage measurement. 137 | Combinations: 138 | 0: 140 µs 139 | 1: 204 µs 140 | 2: 332 µs 141 | 3: 588 µs 142 | 4: 1.1 ms (default) 143 | 5: 2.116 ms 144 | 6: 4.156 ms 145 | 7: 8.244 ms 146 | 147 | - "mode" (int): Operating Mode. Bits 0-2 select continuous, triggered, or power-down mode of operation. These bits default to continuous shunt and bus measurement mode. 148 | Combinations: 149 | 0: Power-Down (or Shutdown) 150 | 1: Shunt Voltage, Triggered 151 | 2: Bus Voltage, Triggered 152 | 3: Shunt and Bus, Triggered 153 | 4: Power-Down (or Shutdown) 154 | 5: Shunt Voltage, Continuous 155 | 6: Bus Voltage, Continuous 156 | 7: Shunt and Bus, Continuous (default) 157 | """ 158 | # Read the current value of CONFIG_REG 159 | config_value = self.i2c_read_word(self.CONFIG_REG) 160 | 161 | # Parse the configuration settings 162 | config = { 163 | "reset": bool(config_value & 0x8000), 164 | "avg": (config_value >> 9) & 0x07, 165 | "vbus_ct": (config_value >> 6) & 0x07, 166 | "vsh_ct": (config_value >> 3) & 0x07, 167 | "mode": config_value & 0x07 168 | } 169 | 170 | return config 171 | 172 | def set_config(self, avg=0, vbus_ct=4, vsh_ct=4, mode=7): 173 | """ 174 | Set the configuration of the INA226 sensor. 175 | 176 | Args: 177 | avg (int): Averaging Mode. Bits 9-11 determine the number of samples that are collected and averaged. 178 | Combinations: 179 | 0: 1 (default) 180 | 1: 4 181 | 2: 16 182 | 3: 64 183 | 4: 128 184 | 5: 256 185 | 6: 512 186 | 7: 1024 187 | 188 | vbus_ct (int): Bus Voltage Conversion Time. Bits 6-8 set the conversion time for the bus voltage measurement. 189 | Combinations: 190 | 0: 140 µs 191 | 1: 204 µs 192 | 2: 332 µs 193 | 3: 588 µs 194 | 4: 1.1 ms (default) 195 | 5: 2.116 ms 196 | 6: 4.156 ms 197 | 7: 8.244 ms 198 | 199 | vsh_ct (int): Shunt Voltage Conversion Time. Bits 3-5 set the conversion time for the shunt voltage measurement. 200 | Combinations: 201 | 0: 140 µs 202 | 1: 204 µs 203 | 2: 332 µs 204 | 3: 588 µs 205 | 4: 1.1 ms (default) 206 | 5: 2.116 ms 207 | 6: 4.156 ms 208 | 7: 8.244 ms 209 | 210 | mode (int): Operating Mode. Bits 0-2 select continuous, triggered, or power-down mode of operation. 211 | These bits default to continuous shunt and bus measurement mode. 212 | Combinations: 213 | 0: Power-Down (or Shutdown) 214 | 1: Shunt Voltage, Triggered 215 | 2: Bus Voltage, Triggered 216 | 3: Shunt and Bus, Triggered 217 | 4: Power-Down (or Shutdown) 218 | 5: Shunt Voltage, Continuous 219 | 6: Bus Voltage, Continuous 220 | 7: Shunt and Bus, Continuous (default) 221 | 222 | Returns: 223 | int: Result code (0 for success, -1 for failure). 224 | """ 225 | # Calculate the value to write to the CONFIG_REG 226 | config_value = 0 227 | config_value |= (avg & 0x07) << 9 228 | config_value |= (vbus_ct & 0x07) << 6 229 | config_value |= (vsh_ct & 0x07) << 3 230 | config_value |= mode & 0x07 231 | 232 | # Write the value to the CONFIG_REG 233 | return self.i2c_write_word(self.CONFIG_REG, config_value) 234 | 235 | def get_shunt_voltage(self): 236 | """ 237 | Get the shunt voltage in microvolts. 238 | 239 | Returns: 240 | float: Shunt voltage in microvolts(uV). 241 | """ 242 | raw_data = self.i2c_read_word(self.SHUNT_VOLTAGE_REG) 243 | 244 | if raw_data > 0x7fff: 245 | raw_data = 0x7fff - raw_data 246 | 247 | voltage = raw_data * 2.5 # Convert raw data to voltage (uV) 248 | return voltage 249 | 250 | def get_bus_voltage(self): 251 | """ 252 | Get the bus voltage in millivolts. 253 | 254 | Returns: 255 | float: Bus voltage in millivolts(mV). 256 | """ 257 | raw_data = self.i2c_read_word(self.BUS_VOLTAGE_REG) 258 | voltage = raw_data * 1.25 # Convert raw data to voltage (mV) 259 | return voltage 260 | 261 | def get_power(self): 262 | """ 263 | Get the power consumption in milliwatts. 264 | 265 | Returns: 266 | float: Power consumption in milliwatts. 267 | """ 268 | raw_data = self.i2c_read_word(self.POWER_REG) 269 | power = raw_data * 62.5 / self.r_shunt # Convert raw data to power (mA) 270 | return power 271 | 272 | def get_current(self): 273 | """ 274 | Get the current draw in microamps. 275 | 276 | Returns: 277 | float: Current draw in microamps(uA). 278 | """ 279 | raw_data = self.i2c_read_word(self.CURRENT_REG) 280 | current = raw_data * 2500 / self.r_shunt # Convert raw data to current (uA) 281 | return current 282 | 283 | def get_calibration(self): 284 | """ 285 | Get the calibration value from the CALIBRATION_REG register. 286 | 287 | Returns: 288 | int: The calibration value as a 16-bit integer (hexadecimal). 289 | """ 290 | return self.i2c_read_word(self.CALIBRATION_REG) 291 | 292 | def set_calibration(self, calibration): 293 | """ 294 | Set the calibration for the INA226 sensor. 295 | 296 | Args: 297 | calibration (int): A float value representing the calibration factor to be applied to the raw data. 298 | Returns: 299 | int: Result code (0 for success, -1 for failure). 300 | """ 301 | return self.i2c_write_word(self.CALIBRATION_REG, calibration) 302 | 303 | def get_mask_enable(self): 304 | """ 305 | Get the settings of the Mask/Enable Register. 306 | 307 | Returns: 308 | dict: A dictionary containing the mask/enable settings. 309 | - "SOL" (bool): Shunt Voltage Over-Voltage enabled. 310 | - "SUL" (bool): Shunt Voltage Under-Voltage enabled. 311 | - "BOL" (bool): Bus Voltage Over-Voltage enabled. 312 | - "BUL" (bool): Bus Voltage Under-Voltage enabled. 313 | - "POL" (bool): Power Over-Limit enabled. 314 | - "CNVR" (bool): Conversion Ready enabled. 315 | - "AFF" (bool): Alert Function Flag. 316 | - "CVRF" (bool): Conversion Ready Flag. 317 | - "OVF" (bool): Math Overflow Flag. 318 | - "APOL" (bool): Alert Polarity (inverted or normal). 319 | - "LEN" (bool): Alert Latch Enable (Latch or Transparent). 320 | """ 321 | mask_enable = self.i2c_read_word(self.MASK_ENABLE_REG) 322 | 323 | settings = { 324 | "SOL": bool(mask_enable & 0x8000), 325 | "SUL": bool(mask_enable & 0x4000), 326 | "BOL": bool(mask_enable & 0x2000), 327 | "BUL": bool(mask_enable & 0x1000), 328 | "POL": bool(mask_enable & 0x0800), 329 | "CNVR": bool(mask_enable & 0x0400), 330 | "AFF": bool(mask_enable & 0x0010), 331 | "CVRF": bool(mask_enable & 0x0008), 332 | "OVF": bool(mask_enable & 0x0004), 333 | "APOL": bool(mask_enable & 0x0002), 334 | "LEN": bool(mask_enable & 0x0001) 335 | } 336 | 337 | return settings 338 | 339 | def set_mask_enable(self, bit_name): 340 | """ 341 | Enable a specific function in the Mask/Enable Register. 342 | 343 | Args: 344 | bit_name (str): The name of the bit to enable. Valid bit names are: 345 | - 'SOL': Shunt Voltage Over-Voltage 346 | - 'SUL': Shunt Voltage Under-Voltage 347 | - 'BOL': Bus Voltage Over-Voltage 348 | - 'BUL': Bus Voltage Under-Voltage 349 | - 'POL': Power Over-Limit 350 | - 'CNVR': Conversion Ready 351 | - 'AFF': Alert Function Flag 352 | - 'CVRF': CVRF (Conversion Ready Flag) 353 | - 'OVF': Math Overflow Flag 354 | - 'APOL': Alert Polarity 355 | - 'LEN': Alert Latch Enable 356 | 357 | Returns: 358 | int: Result code (0 for success, -1 for failure). 359 | """ 360 | # 定义每个位的名称和对应的位值 361 | bit_values = { 362 | "SOL": 0x8000, # Shunt Voltage Over-Voltage 363 | "SUL": 0x4000, # Shunt Voltage Under-Voltage 364 | "BOL": 0x2000, # Bus Voltage Over-Voltage 365 | "BUL": 0x1000, # Bus Voltage Under-Voltage 366 | "POL": 0x0800, # Power Over-Limit 367 | "CNVR": 0x0400, # Conversion Ready 368 | "AFF": 0x0010, # Alert Function Flag 369 | "CVRF": 0x0008, # CVRF (Conversion Ready Flag) 370 | "OVF": 0x0004, # Math Overflow Flag 371 | "APOL": 0x0002, # Alert Polarity 372 | "LEN": 0x0001 # Alert Latch Enable 373 | } 374 | 375 | # 检查输入的位名是否有效 376 | if bit_name in bit_values: 377 | # 获取位值 378 | bit_value = bit_values[bit_name] 379 | # 写入 Mask/Enable 寄存器 380 | return self.i2c_write_word(self.MASK_ENABLE_REG, bit_value) 381 | else: 382 | print(f"Invalid bit name: {bit_name}") 383 | return -1 384 | 385 | def get_alert_limit(self): 386 | """ 387 | Get the value from the Alert Limit Register. 388 | 389 | Returns: 390 | int: The value from the Alert Limit Register. 391 | """ 392 | return self.i2c_read_word(self.ALERT_LIMIT_REG) 393 | 394 | def set_alert_limit(self, value): 395 | """ 396 | Set the value in the Alert Limit Register. 397 | 398 | Args: 399 | value (int): The value to be set in the Alert Limit Register. 400 | 401 | Returns: 402 | int: Result code (0 for success, -1 for failure). 403 | """ 404 | return self.i2c_write_word(self.ALERT_LIMIT_REG, value) 405 | 406 | def get_manufacturer_id(self): 407 | """ 408 | Read the manufacturer ID. 409 | 410 | Returns: 411 | int: Manufacturer ID value. 412 | """ 413 | return self.i2c_read_word(self.MANUFACTURER_ID_REG) 414 | 415 | def get_die_id(self): 416 | """ 417 | Read the die ID. 418 | 419 | Returns: 420 | int: Die ID value. 421 | """ 422 | return self.i2c_read_word(self.DIE_ID_REG) 423 | 424 | def close(self): 425 | """ 426 | Close the CH347 device. 427 | """ 428 | self.driver.close_device() 429 | 430 | 431 | if __name__ == "__main__": 432 | sensor = INA226() 433 | print(sensor.get_config()) 434 | # sensor.reset() 435 | sensor.set_alert_limit(0x1000) 436 | print(sensor.get_calibration()) 437 | print(sensor.get_shunt_voltage(), 'uV') 438 | print(sensor.get_bus_voltage(), 'mV') 439 | print(sensor.get_current(), 'uA') 440 | print(sensor.get_power(), 'mW') 441 | # sensor.set_mask_enable('SOL') 442 | # print(sensor.get_mask_enable()) 443 | print(hex(sensor.get_manufacturer_id())) 444 | print(hex(sensor.get_die_id())) 445 | sensor.set_config() 446 | sensor.close() 447 | -------------------------------------------------------------------------------- /ch347/include/CH347DLL_EN.H: -------------------------------------------------------------------------------- 1 | /***************************************************************************** 2 | ** Copyright (C) WCH 2001 - 2025 ** 3 | ** Web: http://wch.cn ** 4 | ******************************************************************************/ 5 | // USB bus interface chip CH341/7 parallel port application layer interface library. CH347/CH339W extends UART/SPI/I2C/JTAG/SWD based on the 480Mbps high-speed USB bus. 6 | // CH347-DLL V1.4 7 | // Support OS: Windows 98/ME, Windows 2000/XP, WIN7/8/10/11, and later. 8 | // Support USB chips: CH341, CH341A, CH347, CH339W 9 | // USB => Parallel, I2C, SPI, JTAG, SWD, PARALLEL, UART ... 10 | // Notes: 11 | // Copyright (C) 2025 Nanjing Qinheng Microelectronics Co., Ltd. 12 | 13 | #ifndef _CH347_DLL_H 14 | #define _CH347_DLL_H 15 | 16 | #ifdef __cplusplus 17 | extern "C" { 18 | #endif 19 | 20 | #ifdef _WIN64 21 | #define mOFFSET(s, m) ((ULONG_PTR) & (((s *)0)->m)) // Defines a macro that gets the relative offset address of a structure member 22 | #else 23 | #define mOFFSET(s, m) ((ULONG) & (((s *)0)->m)) // Defines a macro that gets the relative offset address of a structure member 24 | #endif 25 | 26 | #ifndef max 27 | #define max(a, b) (((a) > (b)) ? (a) : (b)) // Determine the maximum value 28 | #endif 29 | 30 | #ifndef min 31 | #define min(a, b) (((a) < (b)) ? (a) : (b)) // Determine the minimum value 32 | #endif 33 | 34 | #ifdef ExAllocatePool 35 | #undef ExAllocatePool // Delete memory allocation with TAG 36 | #endif 37 | 38 | #ifndef NTSTATUS 39 | typedef LONG NTSTATUS; // Return status 40 | #endif 41 | 42 | // Sharing CH341WDM driver with CH341DLL 43 | #ifndef _CH341_DLL_H 44 | typedef struct _USB_SETUP_PKT { // USB control transfer structure 45 | UCHAR mUspReqType; // 00H request type 46 | UCHAR mUspRequest; // 01H request code 47 | union { 48 | struct { 49 | UCHAR mUspValueLow; // 02H Value parameter low byte 50 | UCHAR mUspValueHigh; // 03H Value parameter high byte 51 | }; 52 | USHORT mUspValue; // 02H-03H value parameters 53 | }; 54 | union { 55 | struct { 56 | UCHAR mUspIndexLow; // 04H index parameter low byte 57 | UCHAR mUspIndexHigh; // 05H index parameter high byte 58 | }; 59 | USHORT mUspIndex; // 04H-05H index parameter 60 | }; 61 | USHORT mLength; // 06H-07H data length 62 | } mUSB_SETUP_PKT, *mPUSB_SETUP_PKT; 63 | 64 | typedef struct _WIN32_COMMAND { // Define the WIN32 command interface structure 65 | union { 66 | ULONG mFunction; // Specify function code or pipe number when inputting 67 | NTSTATUS mStatus; // return operation status when output 68 | }; 69 | ULONG mLength; // access length, return the length of subsequent data 70 | union { 71 | mUSB_SETUP_PKT mSetupPkt; // Data request in the setup phase of USB control transfer 72 | UCHAR mBuffer[512]; // data buffer, the length is 0 to 255B 73 | }; 74 | } mWIN32_COMMAND, *mPWIN32_COMMAND; 75 | // WIN32 Application layer interface command 76 | #define IOCTL_CH341_COMMAND (FILE_DEVICE_UNKNOWN << 16 | FILE_ANY_ACCESS << 14 | 0x0f34 << 2 | METHOD_BUFFERED) // private interface 77 | 78 | #define mWIN32_COMMAND_HEAD mOFFSET(mWIN32_COMMAND, mBuffer) // Header length of WIN32 command interface 79 | 80 | #define mCH341_MAX_NUMBER 32 // Maximum number of CH341/7 connected at the same time 81 | 82 | #define mMAX_BUFFER_LENGTH 0x1000 // The maximum length of the data buffer is 4MB 83 | 84 | #define mMAX_COMMAND_LENGTH (mWIN32_COMMAND_HEAD + mMAX_BUFFER_LENGTH) // maximum data length and the command structure header length 85 | 86 | #define mDEFAULT_BUFFER_LEN 0x0400 // The data buffer default length is 1024 87 | 88 | #define mDEFAULT_COMMAND_LEN (mWIN32_COMMAND_HEAD + mDEFAULT_BUFFER_LEN) // default data length and the command structure header length 89 | 90 | // CH341 endpoint address 91 | #define mCH347_ENDP_DATA_UP 0x86 // The data upload endpoint of CH347 92 | #define mCH347_ENDP_DATA_DOWN 0x06 // The data download endpoint of CH347 93 | 94 | // Pipeline operation command provided by equipment layer interface 95 | #define mPipeDeviceCtrl 0x00000004 // CH347 integrated control pipeline 96 | #define mPipeDataUp 0x00000006 // CH347 data block upload pipeline 97 | #define mPipeDataDown 0x00000007 // CH347 data block download pipeline 98 | 99 | // Function code of application layer interface 100 | #define mFuncNoOperation 0x00000000 // no operation 101 | #define mFuncGetVersion 0x00000001 // Get the driver version number 102 | #define mFuncGetConfig 0x00000002 // Get the USB device configuration descriptor 103 | #define mFuncSetTimeout 0x00000009 // Set USB communication timeout 104 | #define mFuncSetExclusive 0x0000000b // Set exclusive use 105 | #define mFuncResetDevice 0x0000000c // Reset the USB device 106 | #define mFuncResetPipe 0x0000000d // Reset the USB pipe 107 | #define mFuncAbortPipe 0x0000000e // Cancel the data request of the USB pipe 108 | #define mFuncBufferMode 0x00000020 // Set the buffer upload mode and query the data length in the buffer 109 | #define mFuncBufferModeDn 0x00000021 // Set the buffer download mode and query the data length in the buffer 110 | #define mFuncGetVersionEx 0x00000022 // Get the driver version number and chip model 111 | // USB device standard request code 112 | #define mUSB_CLR_FEATURE 0x01 113 | #define mUSB_SET_FEATURE 0x03 114 | #define mUSB_GET_STATUS 0x00 115 | #define mUSB_SET_ADDRESS 0x05 116 | #define mUSB_GET_DESCR 0x06 117 | #define mUSB_SET_DESCR 0x07 118 | #define mUSB_GET_CONFIG 0x08 119 | #define mUSB_SET_CONFIG 0x09 120 | #define mUSB_GET_INTERF 0x0a 121 | #define mUSB_SET_INTERF 0x0b 122 | #define mUSB_SYNC_FRAME 0x0c 123 | 124 | // CH341 manufacturer specific request type for control transmission 125 | #define mCH341_VENDOR_READ 0xC0 // CH341 vendor-specific read operation through control transfer 126 | #define mCH341_VENDOR_WRITE 0x40 // CH341 vendor-specific write operation through control transfer 127 | 128 | #define mCH341A_CMD_I2C_STREAM 0xAA // The command package of the I2C interface, starting from the secondary byte, is the I2C command stream 129 | #define mCH341A_CMD_UIO_STREAM 0xAB // The command package of the UIO interface, starting from the secondary byte, is the command stream 130 | #define mCH341A_CMD_PIO_STREAM 0xAE // The command package of PIO interface, starting from the secondary byte, is the data stream 131 | // CH341A manufacturer specific request code for control transmission 132 | #define mCH341A_BUF_CLEAR 0xB2 // Clear incomplete data 133 | #define mCH341A_I2C_CMD_X 0x54 // Issue the command of I2C interface and execute it immediately 134 | #define mCH341A_DELAY_MS 0x5E // Delay the specified time in microseconds 135 | #define mCH341A_GET_VER 0x5F // Get chip version 136 | 137 | #define mCH341A_CMD_I2C_STM_STA 0x74 // Command flow of I2C interface: generate start bit 138 | #define mCH341A_CMD_I2C_STM_STO 0x75 // Command flow of I2C interface: generate stop bit 139 | #define mCH341A_CMD_I2C_STM_OUT 0x80 // Command flow of I2C interface: output data, bit 5- bit 0 is the length, subsequent bytes are data, and length 0 only sends one byte and returns an answer 140 | #define mCH341A_CMD_I2C_STM_IN 0xC0 // I2C interface command flow: input data, bit 5-bit 0 is the length, and 0 length only receives one byte and sends no response 141 | #define mCH341A_CMD_I2C_STM_MAX (min(0x3F, mCH341_PACKET_LENGTH)) // The maximum length of input and output data of a single command in the command stream of I2C interface 142 | #define mCH341A_CMD_I2C_STM_SET 0x60 // Command flow of I2C interface: set parameters, bit 2=i/o number of SPI (0= single input single output, 1= double input double output), bit 1 0=i2c speed (00= low speed, 01= standard, 10= fast, 11= high speed) 143 | #define mCH341A_CMD_I2C_STM_US 0x40 // Command flow of I2C interface: delay in microseconds, bit 3- bit 0 as delay value 144 | #define mCH341A_CMD_I2C_STM_MS 0x50 // Command flow of I2C interface: delay in microseconds, bit 3-bit 0 as delay value 145 | #define mCH341A_CMD_I2C_STM_DLY 0x0F // Maximum value of single command delay of command flow of I2C interface 146 | #define mCH341A_CMD_I2C_STM_END 0x00 // Command flow of I2C interface: Command package ends in advance 147 | 148 | #define mCH341A_CMD_UIO_STM_IN 0x00 // Command flow of UIO interface: input data D7-D0 149 | #define mCH341A_CMD_UIO_STM_DIR 0x40 // Command flow of UIO interface: set i/o direction D5-D0, bit 5- bit 0 as direction data 150 | #define mCH341A_CMD_UIO_STM_OUT 0x80 // Command flow of UIO interface: output data D5-D0, bit 5-bit 0 is data 151 | #define mCH341A_CMD_UIO_STM_US 0xC0 // Command flow of UIO interface: delay in microseconds, bit 5- bit 0 as delay value 152 | #define mCH341A_CMD_UIO_STM_END 0x20 // Command flow of UIO interface: Command package ends in advance 153 | 154 | #define MAX_DEVICE_PATH_SIZE 128 // Maximum number of characters for device name 155 | #define MAX_DEVICE_ID_SIZE 64 // Maximum number of characters for device ID 156 | #endif _CH341_DLL_H 157 | 158 | // Drive Interface 159 | #define CH347_USB_VENDOR 0 160 | #define CH347_USB_HID 2 161 | #define CH347_USB_VCP 3 162 | 163 | // CH347_USB_VENDOR support CH341/CH347T/CH347F/CH339W 164 | #define CHIP_TYPE_CH341 0 165 | #define CHIP_TYPE_CH347 1 166 | #define CHIP_TYPE_CH347F 2 167 | #define CHIP_TYPE_CH339W 3 168 | #define CHIP_TYPE_CH347T CHIP_TYPE_CH347 169 | 170 | // Chip Function Interface Number 171 | #define CH347_FUNC_UART 0 172 | #define CH347_FUNC_SPI_IIC 1 173 | #define CH347_FUNC_JTAG_IIC 2 174 | #define CH347_FUNC_JTAG_IIC_SPI 3 // CH347F SPI&I2C&JTAG interfaces can be used simultaneously 175 | 176 | #define DEFAULT_READ_TIMEOUT 500 // Default read timeout (mS) 177 | #define DEFAULT_WRITE_TIMEOUT 500 // Default write timeout (mS) 178 | 179 | #define mCH347_PACKET_LENGTH 512 // Length of packets supported by CH347 180 | #pragma pack(1) 181 | // SPI Controller Configuration 182 | typedef struct _SPI_CONFIG { 183 | UCHAR iMode; // 0-3:SPI Mode0/1/2/3 184 | UCHAR iClock; // 0=60MHz, 1=30MHz, 2=15MHz, 3=7.5MHz, 4=3.75MHz, 5=1.875MHz, 6=937.5KHz, 7=468.75KHz 185 | UCHAR iByteOrder; // 0=LSB first(LSB), 1=MSB first(MSB) 186 | USHORT iSpiWriteReadInterval; // The SPI interface routinely reads and writes data command, the unit is uS 187 | UCHAR iSpiOutDefaultData; // SPI MOSI default data when it reads data 188 | ULONG iChipSelect; // Piece of selected control, if bit 7 is 0, CS selection control is ignored, if bit 7 is 1, the parameter is valid: 189 | // Bit 1 Bit 0 is 00/01 and CS1/CS2 pins are selected as low level active chip options respectively 190 | UCHAR CS1Polarity; // Bit 0: CS1 polarity control: 0: effective low level; 1: effective lhigh level; 191 | UCHAR CS2Polarity; // Bit 0: CS2 polarity control: 0: effective low level; 1: effective lhigh level; 192 | USHORT iIsAutoDeativeCS; // Whether to undo CS selection automatically after the operation is complete 193 | USHORT iActiveDelay; // Set the latency for read/write operations after CS selection,the unit is uS 194 | ULONG iDelayDeactive; // Delay time for read and write operations after CS selection is unselected,the unit is uS 195 | } mSpiCfgS, *mPSpiCfgS; 196 | 197 | // Device Information 198 | typedef struct _DEV_INFOR { 199 | UCHAR iIndex; // Current open index 200 | UCHAR DevicePath[MAX_PATH]; // Device link name, used for CreateFile 201 | UCHAR UsbClass; // 0:CH347_USB_CH341, 2:CH347_USB_HID,3:CH347_USB_VCP 202 | UCHAR FuncType; // 0:CH347_FUNC_UART, 1:CH347_FUNC_SPI_I2C, 2:CH347_FUNC_JTAG_I2C 203 | CHAR DeviceID[64]; // USB\VID_xxxx&PID_xxxx 204 | UCHAR ChipMode; // Chip Mode, 0:Mode0(UART0/1); 1:Mode1(Uart1+SPI+I2C); 2:Mode2(HID Uart1+SPI+I2C) 3:Mode3(Uart1+Jtag+I2C) 4:CH347F(Uart*2+Jtag/SPI/I2C) 205 | HANDLE DevHandle; // Device handle 206 | USHORT BulkOutEndpMaxSize; // Upload endpoint size 207 | USHORT BulkInEndpMaxSize; // downstream endpoint size 208 | UCHAR UsbSpeedType; // USB Speed type, 0:FS,1:HS,2:SS 209 | UCHAR CH347IfNum; // USB interface number: CH347T: IF0:UART; IF1:SPI/IIC/JTAG/GPIO 210 | // CH347F: IF0:UART0; IF1:UART1; IF2:SPI/IIC/JTAG/GPIO 211 | UCHAR DataUpEndp; // The endpoint address 212 | UCHAR DataDnEndp; // The endpoint address 213 | CHAR ProductString[64]; // Product string in USB descriptor 214 | CHAR ManufacturerString[64]; // Manufacturer string in USB descriptor 215 | ULONG WriteTimeout; // USB write timeout 216 | ULONG ReadTimeout; // USB read timeout 217 | CHAR FuncDescStr[64]; // Interface functions 218 | UCHAR FirewareVer; // Firmware version 219 | 220 | } mDeviceInforS, *mPDeviceInforS; 221 | #pragma pack() 222 | 223 | // CH347 Mode Common Function,support open,close,USB read,USB written and HID of all modes. 224 | // Open USB device 225 | HANDLE WINAPI CH347OpenDevice(ULONG DevI); // Specifies the device number 226 | 227 | // Close USB device 228 | BOOL WINAPI CH347CloseDevice(ULONG iIndex); // Specifies the device number 229 | 230 | // Get the USB serial number of the device 231 | BOOL WINAPI CH347GetSerialNumber(ULONG iIndex, // Specifies the device number 232 | PUCHAR iSerialNumberStr); // Point to the obtained device serial number 233 | 234 | // Get Device Information 235 | BOOL WINAPI CH347GetDeviceInfor(ULONG iIndex, mDeviceInforS *DevInformation); 236 | 237 | // Get CH347 type:0:CHIP_TYPE_CH341,1:CHIP_TYPE_CH347/CHIP_TYPE_CH347T,2:CHIP_TYPE_CH347F,3:CHIP_TYPE_CH339W 238 | UCHAR WINAPI CH347GetChipType(ULONG iIndex); 239 | 240 | // Obtain driver version, library version, device version and chip type(CH341(FS)/CH347HS) 241 | BOOL WINAPI CH347GetVersion(ULONG iIndex, 242 | PUCHAR iDriverVer, 243 | PUCHAR iDLLVer, 244 | PUCHAR ibcdDevice, 245 | PUCHAR iChipType); // CHIP_TYPE_CH341/7 246 | 247 | typedef VOID(CALLBACK *mPCH347_NOTIFY_ROUTINE)( // Device event notification callback routine 248 | ULONG iEventStatus); // Device event and current status (refer to the description below): 0=Device unplug event, 3=Device insertion event 249 | 250 | #define CH347_DEVICE_ARRIVAL 3 // Device insertion event,has been inserted 251 | #define CH347_DEVICE_REMOVE_PEND 1 // Device wil be unplugged 252 | #define CH347_DEVICE_REMOVE 0 // Device unplug event,has been pulled out 253 | 254 | BOOL WINAPI CH347SetDeviceNotify( // Configure device event notifier 255 | ULONG iIndex, // Specifies the device number,bit 0 corresponds to the first device 256 | PCHAR iDeviceID, // Optional parameter,pointing to a string terminated by \0,specifying the ID of the monitored device 257 | mPCH347_NOTIFY_ROUTINE iNotifyRoutine); // Specifies the port device event callback program. If it is NULL, the event notification will be canceled. Otherwise, the program will be called when an event is detected. 258 | 259 | // Read USB data block 260 | BOOL WINAPI CH347ReadData(ULONG iIndex, // Specifies the device number 261 | PVOID oBuffer, // Points to a buffer large enough to save the read data 262 | PULONG ioLength); // Points to the length unit, the length to be read when input is the length to be read, and the actual read length after return 263 | 264 | // Write USB data block 265 | BOOL WINAPI CH347WriteData(ULONG iIndex, // Specifies the device number 266 | PVOID iBuffer, // Points to a buffer large enough to save the written data 267 | PULONG ioLength); // Points to the length unit,the input length is the intended length, and the return length is the actual length 268 | 269 | // Set the timeout of USB data read and write 270 | BOOL WINAPI CH347SetTimeout(ULONG iIndex, // Specifies the device number 271 | ULONG iWriteTimeout, // Specifies the timeout for USB to write data blocks, in milliseconds mS,0xFFFFFFFF specifies no timeout (default) 272 | ULONG iReadTimeout); // Specifies the timeout for USB to read data blocks, in milliseconds mS,0xFFFFFFFF specifies no timeout (default) 273 | 274 | /***************SPI********************/ 275 | // SPI Controller Initialization 276 | BOOL WINAPI CH347SPI_Init(ULONG iIndex, mSpiCfgS *SpiCfg); 277 | 278 | // Set the SPI clock frequency, and after calling this interface, you need to call CH347SPI_Init again for reinitialisation. 279 | BOOL WINAPI CH347SPI_SetFrequency(ULONG iIndex, // Specify device number 280 | ULONG iSpiSpeedHz);// Set the SPI clock, with the unit in Hz. 281 | 282 | // CH347F set the SPI databit 283 | BOOL WINAPI CH347SPI_SetDataBits(ULONG iIndex, // Specify device number 284 | UCHAR iDataBits); // 0=8bit,1=16bit 285 | 286 | // Get SPI controller configuration information 287 | BOOL WINAPI CH347SPI_GetCfg(ULONG iIndex, mSpiCfgS *SpiCfg); 288 | 289 | // Before setting the chip selection status, call CH347SPI_Init to set CS 290 | BOOL WINAPI CH347SPI_ChangeCS(ULONG iIndex, // Specify device number 291 | UCHAR iStatus); // 0=Cancel the piece to choose,1=Set piece selected 292 | 293 | // Set SPI slice selection 294 | BOOL WINAPI CH347SPI_SetChipSelect(ULONG iIndex, // Specify device number 295 | USHORT iEnableSelect, // The lower octet is CS1 and the higher octet is CS2. A byte value of 1= sets CS, 0= ignores this CS setting 296 | USHORT iChipSelect, // The lower octet is CS1 and the higher octet is CS2. A byte value of 1= sets CS, 0= ignores this CS setting 297 | ULONG iIsAutoDeativeCS, // The lower 16 bits are CS1 and the higher 16 bits are CS2. Whether to undo slice selection automatically after the operation is complete 298 | ULONG iActiveDelay, // The lower 16 bits are CS1 and the higher 16 bits are CS2. Set the latency of read/write operations after chip selection, the unit is us 299 | ULONG iDelayDeactive); // The lower 16 bits are CS1 and the higher 16 bits are CS2. Delay time for read and write operations after slice selection the unit is us 300 | 301 | // SPI4 write data 302 | BOOL WINAPI CH347SPI_Write(ULONG iIndex, // Specify device number 303 | ULONG iChipSelect, // CS selection control, when bit 7 is 0, slice selection control is ignored, and when bit 7 is 1, slice selection operation is performed 304 | ULONG iLength, // Number of bytes of data to be transferred 305 | ULONG iWriteStep, // The length of a single block to be read 306 | PVOID ioBuffer); // Point to a buffer to place the data to be written out from MOSI 307 | 308 | // SPI4 read data, no need to write data first, much more efficient than CH347SPI_WriteRead 309 | BOOL WINAPI CH347SPI_Read(ULONG iIndex, // Specify device number 310 | ULONG iChipSelect, // Slice selection control, when bit 7 is 0, slice selection control is ignored, and when bit 7 is 1, slice selection operation is performed 311 | ULONG oLength, // Number of bytes to send 312 | PULONG iLength, // Number of bytes of data to be read in 313 | PVOID ioBuffer); // Points to a buffer that place the data to be written out from DOUT, return the data read in from DIN 314 | 315 | // Handle SPI data stream 4-wire interface 316 | BOOL WINAPI CH347SPI_WriteRead(ULONG iIndex, // Specify the device number 317 | ULONG iChipSelect, // Selection control, if the film selection control bit 7 is 0, ignore the film selection control bit 7 is 1 and operate the film selection 318 | ULONG iLength, // Number of bytes of data to be transferred 319 | PVOID ioBuffer); // Points to a buffer that place the data to be written out from DOUT, return the data read in from DIN 320 | 321 | // place the data to be written from MOSI, return the data read in from MISO 322 | BOOL WINAPI CH347StreamSPI4(ULONG iIndex, // Specify the device number 323 | ULONG iChipSelect, // Film selection control, if bit 7 is 0, slice selection control is ignored.If bit 7 is 1, the parameter is valid:Bit 1 bit 0 is 00/01/10. Select D0/D1/D2 pins as low level active chip options respectively 324 | ULONG iLength, // Number of bytes of data to be transferred 325 | PVOID ioBuffer); // Points to a buffer, places data to be written out from DOUT, and returns data to be read in from DIN 326 | 327 | /***************JTAG********************/ 328 | // JTAG interface initialization, set mode and speed 329 | BOOL WINAPI CH347Jtag_INIT(ULONG iIndex, 330 | UCHAR iClockRate); // Communication speed; The value ranges from 0 to 5. A larger value indicates a faster communication speed 331 | 332 | // Gets Jtag speed configuration 333 | BOOL WINAPI CH347Jtag_GetCfg(ULONG iIndex, // Specify the device number 334 | UCHAR *ClockRate); // Communication speed; The value ranges from 0 to 5. A larger value indicates a faster communication speed 335 | 336 | // Function that performs state switching by changing the value of TMS 337 | BOOL WINAPI CH347Jtag_TmsChange(ULONG iIndex, // Specify the device number 338 | PUCHAR tmsValue, // The TMS values that need to be switched form one byte of data in the switching order 339 | ULONG Step, // The number of bit values that need to be read from the tmsValue value 340 | ULONG Skip); // Count from the skip bit of tmsValue to step 341 | 342 | // Read/write data in the shift-dr/ir state and Exit DR/IR after the command is executed 343 | // State machine :Shift-DR/ ir.rw.->Exit DR/IR 344 | BOOL WINAPI CH347Jtag_IoScan(ULONG iIndex, 345 | PUCHAR DataBits, // Bits of data that need to be transferred 346 | ULONG DataBitsNb, // The number of bits of data to be transmitted 347 | BOOL IsRead); // Whether to read data 348 | 349 | // Switch to the shift-dr/ir state for reading/writing. After the command is executed, if the last packet is displayed, switch to Exit DR/IR. If not, stop at Shift-DR/IR 350 | // State machine :Shift-DR/IR.RW.. ->[Exit DR/IR] 351 | BOOL WINAPI CH347Jtag_IoScanT(ULONG iIndex, // Specify the device number 352 | PUCHAR DataBits, // Bits of data that need to be transferred 353 | ULONG DataBitsNb, // The number of bits of data to be transmitted 354 | BOOL IsRead, // Whether to read data 355 | BOOL IsLastPkt); // Whether to the last packet 356 | 357 | // Reset the Tap status function. The TMS is high for more than six clocks, Tap state on Test-Logic Reset 358 | ULONG WINAPI CH347Jtag_Reset(ULONG iIndex); 359 | 360 | // Perform TRST operations to reset hardware 361 | BOOL WINAPI CH347Jtag_ResetTrst(ULONG iIndex, // Specify the device number 362 | BOOL iLevel); // 0=Set lower,1=Set High 363 | 364 | // Bit band mode JTAG IR/DR data read and write. Suitable for reading and writing small amounts of data. Such as command operation, state machine switching and other control transmission. For batch data transmission, you are advised to use CH347Jtag_WriteRead_Fast 365 | // Command packages are read and written in batches in 4096 bytes 366 | // The state machine: Run-Test->Shift-IR/DR..->Exit IR/DR -> Run-Test 367 | BOOL WINAPI CH347Jtag_WriteRead(ULONG iIndex, // Specify the device number 368 | BOOL IsDR, // =TRUE: DR data read/write, =FALSE:IR data read/write 369 | ULONG iWriteBitLength, // Write length, the length to be written 370 | PVOID iWriteBitBuffer, // Points to a buffer to place data ready to be written out 371 | PULONG oReadBitLength, // Points to the length unit and returns the length actually read 372 | PVOID oReadBitBuffer); // Points to a buffer large enough to hold the read data 373 | 374 | // JTAG IR/DR data batch read and write The IR/DR data is used for multi-byte continuous read and write. For example, download firmware. The hardware has four buffers. If the buffer is written before the buffer is read, the length cannot exceed 4096 bytes. Buffer size can be adjusted 375 | // The state machine: Run-Test->Shift-IR/DR..->Exit IR/DR -> Run-Test 376 | BOOL WINAPI CH347Jtag_WriteRead_Fast(ULONG iIndex, // Specify the device number 377 | BOOL IsDR, // =TRUE: in SHIFT-DR read/write, =FALSE:IR data read/write 378 | ULONG iWriteBitLength, // Write length. The length to be written 379 | PVOID iWriteBitBuffer, // Points to a buffer to place data ready to be written out 380 | PULONG oReadBitLength, // Point to the length unit and return the actual read length 381 | PVOID oReadBitBuffer); // Points to a buffer large enough to hold the read data 382 | 383 | // Bitband mode JTAG IR/DR Data read and write. It is suitable for reading and writing small amounts of data. Such as instruction operation, state machine switching and other control class transmission. For batch data transfer, CH347Jtag_WriteRead_Fast is recommended 384 | // State switch:Run-Test-> Shift-IR/DR..->Exit IR/DR -> Run-Test 385 | BOOL WINAPI CH347Jtag_WriteReadEx(ULONG iIndex, // Specify the device number 386 | BOOL IsInDrOrIr, // =TRUE: In SHIFT-DR read/write ==FALSE: Run-Test->Shift-IR/DR.(data read/write).->Exit IR/DR -> Run-Test 387 | BOOL IsDR, // =TRUE: in SHIFT-DR read/write, =FALSE:IR data read/write 388 | ULONG iWriteBitLength, // Write length. The length to be written 389 | PVOID iWriteBitBuffer, // Points to a buffer to place data ready to be written out 390 | PULONG oReadBitLength, // Point to the length unit and return the actual read length 391 | PVOID oReadBitBuffer); // 392 | 393 | // JTAG IR/DR Read and write data in batches. It is used to read and write multi-byte data continuously. For example, download JTAG firmware. Because the hardware has 4K buffer, such as write before read, the length does not exceed 4096 bytes. The buffer size can be adjusted 394 | // State switch:Run-Test->Shift-IR/DR..->Exit IR/DR -> Run-Test 395 | BOOL WINAPI CH347Jtag_WriteRead_FastEx(ULONG iIndex, // Specify the device number 396 | BOOL IsInDrOrIr, // =TRUE: In SHIFT-DR read/write ==FALSE: Run-Test->Shift-IR/DR.(data read/write).->Exit IR/DR -> Run-Test 397 | BOOL IsDR, // =TRUE: in SHIFT-DR read/write, =FALSE:IR data read/write 398 | ULONG iWriteBitLength, // Write length. The length to be written 399 | PVOID iWriteBitBuffer, // Points to a buffer to place data ready to be written out 400 | PULONG oReadBitLength, // Point to the length unit and return the actual read length 401 | PVOID oReadBitBuffer); // Points to a buffer large enough to hold the read data 402 | 403 | // Single-step switching of the JTAG state machine must be executed in order. 404 | BOOL WINAPI CH347Jtag_SwitchTapState(UCHAR TapState); // state to switch 405 | // 0:Test-Logic Reset,1:Run-Test/Idle,2:Run-Test/Idle -> Shift-DR,3:Shift-DR -> Run-Test/Idle 406 | // 4:Run-Test/Idle -> Shift-IR, 5:Shift-IR -> Run-Test/Idle, 6:Exit1-DR/IR -> Update-DR/IR -> Run-Test/Idle 407 | 408 | // Single-step switching of the JTAG state machine allows for the specification of the operating device iIndex. 409 | BOOL WINAPI CH347Jtag_SwitchTapStateEx(ULONG iIndex, // Specify the device number 410 | UCHAR TapState); // state to switch 411 | 412 | // JTAG DR Write, in bytes, for multi-byte sequential reads and writes. For example, download firmware. 413 | // The state machine: Run-Test->Shift-DR..->Exit DR -> Run-Test 414 | BOOL WINAPI CH347Jtag_ByteWriteDR(ULONG iIndex, // Specify the device number 415 | ULONG iWriteLength, // Write length, length of bytes to be written 416 | PVOID iWriteBuffer); // Points to a buffer to place data ready to be written out 417 | 418 | // JTAG DR Read, read multiple bytes consecutively in bytes. 419 | // The state machine: Run-Test->Shift-DR..->Exit DR -> Run-Test 420 | BOOL WINAPI CH347Jtag_ByteReadDR(ULONG iIndex, // Specify the device number 421 | PULONG oReadLength, // Points to the length unit and returns the length of the bytes actually read 422 | PVOID oReadBuffer); // Points to a buffer large enough to hold the read data 423 | 424 | // JTAG IR write, written in bytes, multiple bytes are written consecutively. 425 | // The state machine: Run-Test->Shift-IR..->Exit IR -> Run-Test 426 | BOOL WINAPI CH347Jtag_ByteWriteIR(ULONG iIndex, // Specify the CH347 device number 427 | ULONG iWriteLength, // Write length, the length of bytes to be written 428 | PVOID iWriteBuffer); // Points to a buffer to place data ready to be written out 429 | 430 | // JTAG IR read, readen in bytes, multiple bytes are readen consecutively. 431 | // The state machine: Run-Test->Shift-IR..->Exit IR -> Run-Test 432 | BOOL WINAPI CH347Jtag_ByteReadIR(ULONG iIndex, // Specify the device number 433 | PULONG oReadLength, // Points to the length unit and returns the length of the bytes actually read 434 | PVOID oReadBuffer); // Points to a buffer large enough to hold the read data 435 | 436 | // Bit band mode JTAG DR data write. Suitable for reading and writing small amounts of data. Such as command operation, state machine switching and other control transmission. For batch data transmission, CH347Jtag_ByteWriteDR is recommended 437 | // The state machine: Run-Test->Shift-DR..->Exit DR -> Run-Test 438 | BOOL WINAPI CH347Jtag_BitWriteDR(ULONG iIndex, // Specify the device number 439 | ULONG iWriteBitLength, // Points to the length unit and returns the length of the bytes actually read 440 | PVOID iWriteBitBuffer); // Points to a buffer large enough to hold the read data 441 | 442 | // Bit band mode JTAG IR data write. Suitable for reading and writing small amounts of data. Such as command operation, state machine switching and other control transmission. For batch data transmission, CH347Jtag_ByteWriteIR is recommended 443 | // The state machine: Run-Test->Shift-IR..->Exit IR -> Run-Test 444 | BOOL WINAPI CH347Jtag_BitWriteIR(ULONG iIndex, // Specify the device number 445 | ULONG iWriteBitLength, // Points to the length unit and returns the length of the bytes actually read 446 | PVOID iWriteBitBuffer); // Points to a buffer large enough to hold the read data 447 | 448 | // Bit band mode JTAG IR data read. Suitable for reading and writing small amounts of data. Such as command operation, state machine switching, etc. For batch data transmission, CH347Jtag_ByteReadIR is recommended. 449 | // The state machine: Run-Test->Shift-IR..->Exit IR -> Run-Test 450 | BOOL WINAPI CH347Jtag_BitReadIR(ULONG iIndex, // Specify the device number 451 | PULONG oReadBitLength, // Points to the length unit and returns the length of the bytes actually read 452 | PVOID oReadBitBuffer); // Points to a buffer large enough to hold the read data 453 | 454 | // Bit band mode JTAG DR data read. Suitable for reading and writing small amounts of data. For batch and high-speed data transmission, CH347Jtag_ByteReadDR is recommended 455 | // The state machine: Run-Test->Shift-DR..->Exit DR -> Run-Test 456 | BOOL WINAPI CH347Jtag_BitReadDR(ULONG iIndex, // Specify the device number 457 | PULONG oReadBitLength, // Points to the length unit and returns the length of the bytes actually read 458 | PVOID oReadBitBuffer); // Points to a buffer large enough to hold the read data 459 | 460 | /***************GPIO********************/ 461 | // Get the GPIO direction and pin level of CH347 462 | BOOL WINAPI CH347GPIO_Get(ULONG iIndex, 463 | UCHAR *iDir, // Pin direction: GPIo0-7 corresponding bit 0-7,0: input; 1: output 464 | UCHAR *iData); // GPIO0 level: GPIO0-7 corresponding bit 0-7,0: low level; 1: high level 465 | 466 | // Set the GPIO direction and pin level of CH347 467 | BOOL WINAPI CH347GPIO_Set(ULONG iIndex, 468 | UCHAR iEnable, // Data validity flag: The corresponding bits 0-7 correspond to GPIO0-7. 469 | UCHAR iSetDirOut, // Sets the I/O direction, with pin 0 corresponding to input and pin 1 corresponding to output. Gpio0-7 corresponds to bits 0-7. 470 | UCHAR iSetDataOut); // Outputs data. If the I/O direction is output, then a pin outputs low level at a clear 0 and high level at a position 1 471 | 472 | typedef VOID(CALLBACK *mPCH347_INT_ROUTINE)( // Interrupt service routine 473 | PUCHAR iStatus); // For interrupt status data, refer to the bit description below 474 | // 8 byte GPIO0-7 pin status, the bits per byte are defined as follows. 475 | // Bit7:Current GPIO0 direction, 0:Input; 1:Output 476 | // Bit6:Current GPIO0 level, 0:Low level;1:High level 477 | // Bit5:Whether the current GPIO0 is set to interrupt; 0:Query mode; 1:Interrupt mode 478 | // Bit4-3:Set the GPIO0 interrupt mode, 00:Falling edge trigger; 01:Rising edge trigger; 10:Double edge trigger;11: reserved; 479 | // Bit2-0:reserved; 480 | 481 | // Set GPIO interrupt service routine 482 | BOOL WINAPI CH347SetIntRoutine(ULONG iIndex, // Specify the device number 483 | UCHAR Int0PinN, // INT0 GPIO pin number greater than 7: Disable this interrupt source. 0-7 corresponds to gpio0-7 484 | UCHAR Int0TripMode, // INT0 type: 00:Falling edge trigger; 01:Rising edge trigger; 02:Double edge trigger; 03:reserve; 485 | UCHAR Int1PinN, // INT1 GPIO pin number. If it is greater than 7, this interrupt source is not enabled. 0-7 corresponds to gpio0-7 486 | UCHAR Int1TripMode, // INT1 type: 00:Falling edge trigger; 01:Rising edge trigger; 02:Double edge trigger; 03:reserve; 487 | mPCH347_INT_ROUTINE iIntRoutine); // Specifies the interrupt service program. If NULL is specified, the interrupt service is canceled. Otherwise, the program is invoked when the interrupt is performed 488 | 489 | // Read interrupt data 490 | BOOL WINAPI CH347ReadInter(ULONG iIndex, // Specify the device number 491 | PUCHAR iStatus); // Refers to the 8-byte unit used to hold read GPIO pin state data, refer to the bit instructions below 492 | 493 | // Abandon interrupt data read operation 494 | BOOL WINAPI CH347AbortInter(ULONG iIndex); // Specify the device number 495 | 496 | // Enter the IAP firmware upgrade mode 497 | BOOL WINAPI CH347StartIapFwUpate(ULONG iIndex, 498 | ULONG FwSize); // The length of the firmware 499 | 500 | /**************HID/VCP Serial Port*********************/ 501 | // Open serial port 502 | HANDLE WINAPI CH347Uart_Open(ULONG iIndex); 503 | 504 | // Close serial port 505 | BOOL WINAPI CH347Uart_Close(ULONG iIndex); 506 | 507 | // Set the device event notification program 508 | BOOL WINAPI CH347Uart_SetDeviceNotify( 509 | ULONG iIndex, // Specify the device number, 0 corresponds to the first device 510 | PCHAR iDeviceID, // Optional parameter,points to a string specifying the ID of the device to be monitored, terminated with \0 511 | mPCH347_NOTIFY_ROUTINE iNotifyRoutine); // Specifies the device event callback. NULL cancels event notification, otherwise it will be called when an event is detected 512 | 513 | // Obtain UART hardware configuration 514 | BOOL WINAPI CH347Uart_GetCfg(ULONG iIndex, // Specify the device number 515 | PULONG BaudRate, // Baud rate 516 | PUCHAR ByteSize, // Data bits (5,6,7,8,16) 517 | PUCHAR Parity, // Parity bits(0:None; 1:Odd; 2:Even; 3:Mark; 4:Space) 518 | PUCHAR StopBits, // Stop bits (0:1 stop bits; 1:1.5 stop bit; 2:2 stop bit); 519 | PUCHAR ByteTimeout); // Byte timeout 520 | 521 | // Set UART configuration 522 | BOOL WINAPI CH347Uart_Init(ULONG iIndex, // Specify the device number 523 | DWORD BaudRate, // Baud rate 524 | UCHAR ByteSize, // Data bits (5,6,7,8,16) 525 | UCHAR Parity, // Check bit (0:None; 1:Odd; 2:Even; 3:Mark; 4:Space) 526 | UCHAR StopBits, // Stop bits 0:1 Stop bit; 1:1.5 stop bit; 2:2 stop bit); 527 | UCHAR ByteTimeout); // Byte Timeout duration, in unit of 100uS 528 | 529 | // Set the timeout of USB data read and write 530 | BOOL WINAPI CH347Uart_SetTimeout(ULONG iIndex, // Specify the device number 531 | ULONG iWriteTimeout, // Specifies the timeout for USB to write data blocks, in milliseconds mS,0xFFFFFFFF specifies no timeout (default) 532 | ULONG iReadTimeout); // Specifies the timeout for USB to read data blocks, in milliseconds mS,0xFFFFFFFF specifies no timeout (default) 533 | 534 | // Read data block 535 | BOOL WINAPI CH347Uart_Read(ULONG iIndex, // Specify the device number 536 | PVOID oBuffer, // Points to a buffer large enough to hold the read data 537 | PULONG ioLength); // Refers to the length unit. The input is the length to be read and the return is the actual length to be read 538 | 539 | // Write out blocks of data 540 | BOOL WINAPI CH347Uart_Write(ULONG iIndex, // Specify the device number 541 | PVOID iBuffer, // Points to a buffer to place data ready to be written out 542 | PULONG ioLength); // Point to the length unit. The input is the intended length and the return is the actual length 543 | 544 | // Query how many bytes are unfetched in the read buffer 545 | BOOL WINAPI CH347Uart_QueryBufUpload(ULONG iIndex, // Specify the device number 546 | LONGLONG *RemainBytes); 547 | 548 | // Obtaining Device Information 549 | BOOL WINAPI CH347Uart_GetDeviceInfor(ULONG iIndex, mDeviceInforS *DevInformation); 550 | 551 | /********I2C***********/ 552 | // Set the serial port flow mode 553 | BOOL WINAPI CH347I2C_Set(ULONG iIndex, // Specify the device number 554 | ULONG iMode); // See downlink for the specified mode 555 | // bit 1-bit 0: I2C interface speed /SCL frequency, 00= low speed /20KHz,01= standard /100KHz(default),10= fast /400KHz,11= high speed /750KHz 556 | // Other reservations, must be 0 557 | 558 | // Set I2C Clock Stretch 559 | BOOL WINAPI CH347I2C_SetStretch(ULONG iIndex, // Specify the device number 560 | BOOL iEnable); // I2C Clock Stretch enable, 1:enable,0:disable 561 | 562 | // Set the hardware asynchronous delay to a specified number of milliseconds before the next stream operation 563 | BOOL WINAPI CH347I2C_SetDelaymS(ULONG iIndex, // Specify the device number 564 | ULONG iDelay); // Specifies the delay duration (mS) 565 | 566 | // Set the I2C pins drive mode. 567 | BOOL WINAPI CH347I2C_SetDriverMode(ULONG iIndex, // Specify the device number 568 | UCHAR iMode); // 0=open-drain mode; 1=push-pull mode 569 | 570 | // Process I2C data stream, 2-wire interface, clock line for SCL pin, data line for SDA pin 571 | BOOL WINAPI CH347StreamI2C(ULONG iIndex, // Specify the device number 572 | ULONG iWriteLength, // The number of bytes of data to write 573 | PVOID iWriteBuffer, // Points to a buffer to place data ready to be written out, the first byte is usually the I2C device address and read/write direction bit 574 | ULONG iReadLength, // Number of bytes of data to be read 575 | PVOID oReadBuffer); // Points to a buffer to place data ready to be read in 576 | 577 | // Process I2C data stream, 2-wire interface, SCL pin for clock line, SDA pin for data line (quasi-bidirectional I/O), speed of about 56K bytes, and return the number of ACK obtained by the host side 578 | BOOL WINAPI CH347StreamI2C_RetACK(ULONG iIndex, // Specify the device number 579 | ULONG iWriteLength, // The number of bytes of data to write 580 | PVOID iWriteBuffer, // Points to a buffer to place data ready to be written out, the first byte is usually the I2C device address and read/write direction bit 581 | ULONG iReadLength, // Number of bytes of data to be read 582 | PVOID oReadBuffer, // Points to a buffer to place data ready to be read in 583 | PULONG rAckCount); // Refers to the ACK value returned by read/write 584 | 585 | #ifndef _CH341_DLL_H 586 | typedef enum _EEPROM_TYPE { // EEPROM type 587 | ID_24C01, 588 | ID_24C02, 589 | ID_24C04, 590 | ID_24C08, 591 | ID_24C16, 592 | ID_24C32, 593 | ID_24C64, 594 | ID_24C128, 595 | ID_24C256, 596 | ID_24C512, 597 | ID_24C1024, 598 | ID_24C2048, 599 | ID_24C4096 600 | } EEPROM_TYPE; 601 | #endif 602 | 603 | // Reads data blocks from EEPROM at a speed of about 56 KB 604 | BOOL WINAPI CH347ReadEEPROM(ULONG iIndex, // Specify the device number 605 | EEPROM_TYPE iEepromID, // Specifies the EEPROM model 606 | ULONG iAddr, // Specifies the address of data unit 607 | ULONG iLength, // Number of bytes of data to be read 608 | PUCHAR oBuffer); // Points to a buffer to place data ready to be read in 609 | 610 | // Writes a data block to the EEPROM 611 | BOOL WINAPI CH347WriteEEPROM(ULONG iIndex, // Specify the device number 612 | EEPROM_TYPE iEepromID, // Specifies the EEPROM model 613 | ULONG iAddr, // Specifies the address of data unit 614 | ULONG iLength, // Number of bytes of data to be written out 615 | PUCHAR iBuffer); // Points to a buffer to place data ready to be written out 616 | 617 | // Set the low cycle delay time for the 8th clock, applicable only to CH347T. 618 | BOOL WINAPI CH347I2C_SetAckClk_DelayuS(ULONG iIndex, // Specify the device number 619 | ULONG iDelay); // Specified delay in microseconds. 620 | 621 | // This function is used to query the current enable status of each interface function of the CH339W chip with the specified index. 622 | // Return value bit definition: 623 | // Bit 7 (0x80): USB to JTAG enable status (1 = enabled, 0 = disabled) 624 | // Bit 6 (0x40): USB to SPI enable status (1 = enabled, 0 = disabled) 625 | // Bit 5 (0x20): USB to UART enable (without flow control) (1 = enabled, 0 = disabled) 626 | // Bit 4 (0x10): USB to UART flow control enable (1 = enabled, 0 = disabled) 627 | // Bit 3 (0x08): USB to I2C enable status (1 = enabled, 0 = disabled) 628 | // Bits 2 - 0: Reserved bits 629 | UCHAR WINAPI CH339GetChipFuncState( ULONG iIndex ); // Specify the device number 630 | 631 | #ifdef __cplusplus 632 | } 633 | #endif 634 | 635 | #endif // _CH347_DLL_H 636 | -------------------------------------------------------------------------------- /ch347/ch347.py: -------------------------------------------------------------------------------- 1 | import ctypes 2 | from typing import List 3 | 4 | 5 | class DeviceInfo(ctypes.Structure): 6 | MAX_PATH = 260 7 | _fields_ = [ 8 | ("DeviceIndex", ctypes.c_ubyte), # 当前打开序号 9 | ("DevicePath", ctypes.c_char * MAX_PATH), # 设备路径 10 | ( 11 | "UsbClass", 12 | ctypes.c_ubyte, 13 | ), # USB设备类别: 0=CH341 Vendor; 1=CH347 Vendor; 2=HID 14 | ("FuncType", ctypes.c_ubyte), # 设备功能类型: 0=UART1; 1=SPI+I2C; 2=JTAG+I2C 15 | ("DeviceID", ctypes.c_char * 64), # USB设备ID: USB\VID_xxxx&PID_xxxx 16 | ( 17 | "ChipMode", 18 | ctypes.c_ubyte, 19 | ), # 芯片模式: 0=Mode0(UART*2); 1=Mode1(Uart1+SPI+I2C); 2=Mode2(HID Uart1+SPI+I2C); 3=Mode3(Uart1+Jtag+I2C) 20 | ("DevHandle", ctypes.c_void_p), # 设备句柄 21 | ("BulkOutEndpMaxSize", ctypes.c_ushort), # 上传端点大小 22 | ("BulkInEndpMaxSize", ctypes.c_ushort), # 下传端点大小 23 | ("UsbSpeedType", ctypes.c_ubyte), # USB速度类型: 0=FS; 1=HS; 2=SS 24 | ("CH347IfNum", ctypes.c_ubyte), # USB接口号 25 | ("DataUpEndp", ctypes.c_ubyte), # 端点地址 26 | ("DataDnEndp", ctypes.c_ubyte), # 端点地址 27 | ("ProductString", ctypes.c_char * 64), # USB产品字符串 28 | ("ManufacturerString", ctypes.c_char * 64), # USB厂商字符串 29 | ("WriteTimeout", ctypes.c_ulong), # USB写超时 30 | ("ReadTimeout", ctypes.c_ulong), # USB读超时 31 | ("FuncDescStr", ctypes.c_char * 64), # 接口功能描述符 32 | ("FirmwareVer", ctypes.c_ubyte), # 固件版本 33 | ] 34 | 35 | 36 | class SPIConfig(ctypes.Structure): 37 | _fields_ = [ 38 | ("Mode", ctypes.c_ubyte), # 0-3: SPI Mode0/1/2/3 39 | ( 40 | "Clock", 41 | ctypes.c_ubyte, 42 | ), # 0=60MHz, 1=30MHz, 2=15MHz, 3=7.5MHz, 4=3.75MHz, 5=1.875MHz, 6=937.5KHz, 7=468.75KHz 43 | ("ByteOrder", ctypes.c_ubyte), # 0=LSB first(LSB), 1=MSB first(MSB) 44 | ( 45 | "SPIWriteReadInterval", 46 | ctypes.c_ushort, 47 | ), # Regular interval for SPI read/write commands, in microseconds 48 | ( 49 | "SPIOutDefaultData", 50 | ctypes.c_ubyte, 51 | ), # Default output data when reading from SPI 52 | ( 53 | "ChipSelect", 54 | ctypes.c_ulong, 55 | ), # Chip select control. Bit 7 as 0 ignores chip select control, 56 | # Bit 7 as 1 makes the parameters valid: 57 | # Bit 1 and Bit 0 as 00/01 selects CS1/CS2 pin as the active low chip select. 58 | ( 59 | "CS1Polarity", 60 | ctypes.c_ubyte, 61 | ), # Bit 0: CS1 polarity control, 0: active low, 1: active high 62 | ( 63 | "CS2Polarity", 64 | ctypes.c_ubyte, 65 | ), # Bit 0: CS2 polarity control, 0: active low, 1: active high 66 | ( 67 | "IsAutoDeactiveCS", 68 | ctypes.c_ushort, 69 | ), # Automatically de-assert chip select after the operation is completed 70 | ( 71 | "ActiveDelay", 72 | ctypes.c_ushort, 73 | ), # Delay time for executing read/write operations after chip select is set, in microseconds 74 | ( 75 | "DelayDeactive", 76 | ctypes.c_ulong, 77 | ), # Delay time for executing read/write operations after chip select is de-asserted, in microseconds 78 | ] 79 | 80 | 81 | class CH347: 82 | # MAX devices number 83 | MAX_DEVICE_NUMBER = 8 84 | 85 | # Define the callback function type 86 | NOTIFY_ROUTINE = ctypes.CFUNCTYPE(None, ctypes.c_ulong) 87 | 88 | # Define the callback function type 89 | INTERRUPT_ROUTINE = ctypes.CFUNCTYPE(None, ctypes.POINTER(ctypes.c_ubyte)) 90 | 91 | INVALID_HANDLE_VALUE = ctypes.c_void_p(-1).value 92 | 93 | def __init__(self, device_index=0, dll_path=None): 94 | """ 95 | Initialize the CH347 interface. 96 | 97 | Args: 98 | device_index (int): The index of the device to open (default: 0). 99 | dll_path (str, optional): Path to the CH347 DLL file. If None, the system will 100 | search for the DLL in system directories. 101 | """ 102 | if dll_path is None: 103 | # Let Windows find the DLL in system directories 104 | self.ch347dll = ctypes.WinDLL("CH347DLLA64") 105 | else: 106 | # Use the specified path 107 | self.ch347dll = ctypes.WinDLL(dll_path) 108 | 109 | self.device_index = device_index 110 | 111 | # 创建回调函数对象并绑定到实例属性 112 | self.callback_func = self.NOTIFY_ROUTINE(self.event_callback) 113 | # 创建中断回调函数对象并绑定到实例属性 114 | self.interrupt_cb_func = self.INTERRUPT_ROUTINE(self.interrupt_callback) 115 | # 创建回调函数对象并绑定到实例属性 116 | self.uart_callback_func = self.NOTIFY_ROUTINE(self.uart_event_callback) 117 | 118 | # Set the function argument types and return type for CH347OpenDevice 119 | self.ch347dll.CH347OpenDevice.argtypes = [ctypes.c_ulong] 120 | self.ch347dll.CH347OpenDevice.restype = ctypes.c_void_p 121 | 122 | # Set the function argument types and return type for CH347CloseDevice 123 | self.ch347dll.CH347CloseDevice.argtypes = [ctypes.c_ulong] 124 | self.ch347dll.CH347CloseDevice.restype = ctypes.c_bool 125 | 126 | # Set the function argument types and return type for CH347GetDeviceInfor 127 | self.ch347dll.CH347GetDeviceInfor.argtypes = [ 128 | ctypes.c_ulong, 129 | ctypes.POINTER(DeviceInfo), 130 | ] 131 | self.ch347dll.CH347GetDeviceInfor.restype = ctypes.c_bool 132 | 133 | # Set the function argument types and return type for CH347GetVersion 134 | self.ch347dll.CH347GetVersion.argtypes = [ 135 | ctypes.c_ulong, 136 | ctypes.POINTER(ctypes.c_ubyte), 137 | ctypes.POINTER(ctypes.c_ubyte), 138 | ctypes.POINTER(ctypes.c_ubyte), 139 | ctypes.POINTER(ctypes.c_ubyte), 140 | ] 141 | self.ch347dll.CH347GetVersion.restype = ctypes.c_bool 142 | 143 | # Set the function argument types and return type for CH347SetDeviceNotify 144 | self.ch347dll.CH347SetDeviceNotify.argtypes = [ 145 | ctypes.c_ulong, 146 | ctypes.c_char_p, 147 | ctypes.CFUNCTYPE(None, ctypes.c_ulong), 148 | ] 149 | self.ch347dll.CH347SetDeviceNotify.restype = ctypes.c_bool 150 | 151 | # Set the function argument types and return type for CH347ReadData 152 | self.ch347dll.CH347ReadData.argtypes = [ 153 | ctypes.c_ulong, 154 | ctypes.c_void_p, 155 | ctypes.POINTER(ctypes.c_ulong), 156 | ] 157 | self.ch347dll.CH347ReadData.restype = ctypes.c_bool 158 | 159 | # Set the function argument types and return type for CH347WriteData 160 | self.ch347dll.CH347WriteData.argtypes = [ 161 | ctypes.c_ulong, 162 | ctypes.c_void_p, 163 | ctypes.POINTER(ctypes.c_ulong), 164 | ] 165 | self.ch347dll.CH347WriteData.restype = ctypes.c_bool 166 | 167 | # Set the function argument types and return type for CH347SetTimeout 168 | self.ch347dll.CH347SetTimeout.argtypes = [ 169 | ctypes.c_ulong, 170 | ctypes.c_ulong, 171 | ctypes.c_ulong, 172 | ] 173 | self.ch347dll.CH347SetTimeout.restype = ctypes.c_bool 174 | 175 | # Set the function argument types and return type for CH347SPI_Init 176 | self.ch347dll.CH347SPI_Init.argtypes = [ 177 | ctypes.c_ulong, 178 | ctypes.POINTER(SPIConfig), 179 | ] 180 | self.ch347dll.CH347SPI_Init.restype = ctypes.c_bool 181 | 182 | # Set the function argument types and return type for CH347SPI_GetCfg 183 | self.ch347dll.CH347SPI_GetCfg.argtypes = [ 184 | ctypes.c_ulong, 185 | ctypes.POINTER(SPIConfig), 186 | ] 187 | self.ch347dll.CH347SPI_GetCfg.restype = ctypes.c_bool 188 | 189 | # Set the function argument types and return type for CH347SPI_ChangeCS 190 | self.ch347dll.CH347SPI_ChangeCS.argtypes = [ctypes.c_ulong, ctypes.c_ubyte] 191 | self.ch347dll.CH347SPI_ChangeCS.restype = ctypes.c_bool 192 | 193 | # Set the function argument types and return type for CH347SPI_SetChipSelect 194 | self.ch347dll.CH347SPI_SetChipSelect.argtypes = [ 195 | ctypes.c_ulong, 196 | ctypes.c_ushort, 197 | ctypes.c_ushort, 198 | ctypes.c_ulong, 199 | ctypes.c_ulong, 200 | ctypes.c_ulong, 201 | ] 202 | self.ch347dll.CH347SPI_SetChipSelect.restype = ctypes.c_bool 203 | 204 | # Set the function argument types and return type for CH347SPI_Write 205 | self.ch347dll.CH347SPI_Write.argtypes = [ 206 | ctypes.c_ulong, 207 | ctypes.c_ulong, 208 | ctypes.c_ulong, 209 | ctypes.c_ulong, 210 | ctypes.c_void_p, 211 | ] 212 | self.ch347dll.CH347SPI_Write.restype = ctypes.c_bool 213 | 214 | # Set the function argument types and return type for CH347SPI_Read 215 | self.ch347dll.CH347SPI_Read.argtypes = [ 216 | ctypes.c_ulong, 217 | ctypes.c_ulong, 218 | ctypes.c_ulong, 219 | ctypes.POINTER(ctypes.c_ulong), 220 | ctypes.c_void_p, 221 | ] 222 | self.ch347dll.CH347SPI_Read.restype = ctypes.c_bool 223 | 224 | # Set the function argument types and return type for CH347SPI_WriteRead 225 | self.ch347dll.CH347SPI_WriteRead.argtypes = [ 226 | ctypes.c_ulong, 227 | ctypes.c_ulong, 228 | ctypes.c_ulong, 229 | ctypes.c_void_p, 230 | ] 231 | self.ch347dll.CH347SPI_WriteRead.restype = ctypes.c_bool 232 | 233 | # Set the function argument types and return type for CH347StreamSPI4 234 | self.ch347dll.CH347StreamSPI4.argtypes = [ 235 | ctypes.c_ulong, 236 | ctypes.c_ulong, 237 | ctypes.c_ulong, 238 | ctypes.c_void_p, 239 | ] 240 | self.ch347dll.CH347StreamSPI4.restype = ctypes.c_bool 241 | 242 | # Set the function argument types and return type for CH347I2C_Set 243 | self.ch347dll.CH347I2C_Set.argtypes = [ctypes.c_ulong, ctypes.c_ulong] 244 | self.ch347dll.CH347I2C_Set.restype = ctypes.c_bool 245 | 246 | # Set the function argument types and return type for CH347I2C_SetDelaymS 247 | self.ch347dll.CH347I2C_SetDelaymS.argtypes = [ctypes.c_ulong, ctypes.c_ulong] 248 | self.ch347dll.CH347I2C_SetDelaymS.restype = ctypes.c_bool 249 | 250 | # Set the function argument types and return type for CH347StreamI2C 251 | self.ch347dll.CH347StreamI2C.argtypes = [ 252 | ctypes.c_ulong, 253 | ctypes.c_ulong, 254 | ctypes.c_void_p, 255 | ctypes.c_ulong, 256 | ctypes.c_void_p, 257 | ] 258 | self.ch347dll.CH347StreamI2C.restype = ctypes.c_bool 259 | 260 | self.ch347dll.CH347GPIO_Get.argtypes = [ 261 | ctypes.c_ulong, 262 | ctypes.POINTER(ctypes.c_ubyte), 263 | ctypes.POINTER(ctypes.c_ubyte), 264 | ] 265 | self.ch347dll.CH347GPIO_Get.restype = ctypes.c_bool 266 | 267 | self.ch347dll.CH347GPIO_Set.argtypes = [ 268 | ctypes.c_ulong, 269 | ctypes.c_ubyte, 270 | ctypes.c_ubyte, 271 | ctypes.c_ubyte, 272 | ] 273 | self.ch347dll.CH347GPIO_Set.restype = ctypes.c_bool 274 | 275 | 276 | self.ch347dll.CH347SetIntRoutine.argtypes = [ 277 | ctypes.c_ulong, 278 | ctypes.c_ubyte, 279 | ctypes.c_ubyte, 280 | ctypes.c_ubyte, 281 | ctypes.c_ubyte, 282 | self.INTERRUPT_ROUTINE, 283 | ] 284 | self.ch347dll.CH347SetIntRoutine.restype = ctypes.c_bool 285 | 286 | self.ch347dll.CH347ReadInter.argtypes = [ctypes.c_ulong, ctypes.POINTER(ctypes.c_ubyte)] 287 | self.ch347dll.CH347ReadInter.restype = ctypes.c_bool 288 | 289 | self.ch347dll.CH347AbortInter.argtypes = [ctypes.c_ulong] 290 | self.ch347dll.CH347AbortInter.restype = ctypes.c_bool 291 | 292 | self.ch347dll.CH347Uart_Open.argtypes = [ctypes.c_ulong] 293 | self.ch347dll.CH347Uart_Open.restype = ctypes.c_void_p 294 | 295 | self.ch347dll.CH347Uart_Close.argtypes = [ctypes.c_ulong] 296 | self.ch347dll.CH347Uart_Close.restype = ctypes.c_bool 297 | 298 | self.ch347dll.CH347Uart_SetDeviceNotify.argtypes = [ 299 | ctypes.c_ulong, 300 | ctypes.POINTER(ctypes.c_ubyte), 301 | self.NOTIFY_ROUTINE, 302 | ] 303 | self.ch347dll.CH347Uart_SetDeviceNotify.restype = ctypes.c_bool 304 | 305 | 306 | self.ch347dll.CH347Uart_GetCfg.argtypes = [ 307 | ctypes.c_ulong, 308 | ctypes.POINTER(ctypes.c_ulong), 309 | ctypes.POINTER(ctypes.c_ubyte), 310 | ctypes.POINTER(ctypes.c_ubyte), 311 | ctypes.POINTER(ctypes.c_ubyte), 312 | ctypes.POINTER(ctypes.c_ubyte), 313 | ] 314 | self.ch347dll.CH347Uart_GetCfg.restype = ctypes.c_bool 315 | 316 | self.ch347dll.CH347Uart_Init.argtypes = [ 317 | ctypes.c_ulong, 318 | ctypes.c_uint32, 319 | ctypes.c_ubyte, 320 | ctypes.c_ubyte, 321 | ctypes.c_ubyte, 322 | ctypes.c_ubyte, 323 | ] 324 | self.ch347dll.CH347Uart_Init.restype = ctypes.c_bool 325 | 326 | 327 | self.ch347dll.CH347Uart_SetTimeout.argtypes = [ 328 | ctypes.c_ulong, 329 | ctypes.c_ulong, 330 | ctypes.c_ulong, 331 | ] 332 | self.ch347dll.CH347Uart_SetTimeout.restype = ctypes.c_bool 333 | 334 | 335 | self.ch347dll.CH347Uart_Read.argtypes = [ 336 | ctypes.c_ulong, 337 | ctypes.c_void_p, 338 | ctypes.POINTER(ctypes.c_ulong), 339 | ] 340 | self.ch347dll.CH347Uart_Read.restype = ctypes.c_bool 341 | 342 | self.ch347dll.CH347Uart_Write.argtypes = [ 343 | ctypes.c_ulong, 344 | ctypes.c_void_p, 345 | ctypes.POINTER(ctypes.c_ulong), 346 | ] 347 | self.ch347dll.CH347Uart_Write.restype = ctypes.c_bool 348 | 349 | 350 | self.ch347dll.CH347Uart_QueryBufUpload.argtypes = [ 351 | ctypes.c_ulong, 352 | ctypes.POINTER(ctypes.c_longlong), 353 | ] 354 | self.ch347dll.CH347Uart_QueryBufUpload.restype = ctypes.c_bool 355 | 356 | self.ch347dll.CH347Uart_GetDeviceInfor.argtypes = [ 357 | ctypes.c_ulong, 358 | ctypes.POINTER(DeviceInfo), 359 | ] 360 | self.ch347dll.CH347Uart_GetDeviceInfor.restype = ctypes.c_bool 361 | 362 | 363 | def list_devices(self): 364 | # List all devices 365 | num_devices = 0 366 | dev_info = DeviceInfo() 367 | for i in range(self.MAX_DEVICE_NUMBER): 368 | if self.ch347dll.CH347OpenDevice(i) == self.INVALID_HANDLE_VALUE: 369 | break 370 | num_devices += 1 371 | if self.ch347dll.CH347GetDeviceInfor(i, ctypes.byref(dev_info)): 372 | for field_name, _ in dev_info._fields_: 373 | value = getattr(dev_info, field_name) 374 | print(f"{field_name}: {value}") 375 | print("-" * 40) 376 | self.ch347dll.CH347CloseDevice(i) 377 | print(f"Number of devices: {num_devices}") 378 | return num_devices 379 | 380 | @staticmethod 381 | def event_callback(self, event_status): 382 | # Callback function implementation 383 | print("Callback event status:", event_status) 384 | if event_status == 0: 385 | # Device unplug event 386 | print("Device unplugged") 387 | elif event_status == 3: 388 | # Device insertion event 389 | print("Device inserted") 390 | 391 | @INTERRUPT_ROUTINE 392 | def interrupt_callback(self, istatus): 393 | # Callback function implementation 394 | # Cast to pointer to array of 8 unsigned bytes 395 | byte_array = ctypes.cast(istatus, ctypes.POINTER(ctypes.c_ubyte * 8)).contents 396 | print("Interrupt received:", ' '.join(f'0x{b:02X}' for b in byte_array)) 397 | 398 | @NOTIFY_ROUTINE 399 | def uart_event_callback(self, event_status): 400 | # Callback function implementation 401 | print("Uart callback event status:", event_status) 402 | if event_status == 0: 403 | # Device unplug event 404 | print("Uart unplugged") 405 | elif event_status == 3: 406 | # Device insertion event 407 | print("Uart inserted") 408 | 409 | 410 | def open_device(self): 411 | """ 412 | Open USB device. 413 | 414 | Returns: 415 | int: Handle to the opened device if successful, None otherwise. 416 | """ 417 | handle = self.ch347dll.CH347OpenDevice(self.device_index) 418 | if handle != self.INVALID_HANDLE_VALUE: 419 | return handle 420 | else: 421 | return None 422 | 423 | def close_device(self): 424 | """ 425 | Close USB device. 426 | 427 | Returns: 428 | bool: True if successful, False otherwise. 429 | """ 430 | result = self.ch347dll.CH347CloseDevice(self.device_index) 431 | return result 432 | 433 | def get_device_info(self): 434 | """ 435 | Retrieve the information of the connected device. 436 | 437 | This method uses the device index to call the CH347 DLL and obtain the device's details. 438 | 439 | Returns: 440 | DeviceInfo: An instance of DeviceInfo with the device details if successful. 441 | None: If the retrieval fails. 442 | """ 443 | dev_info = DeviceInfo() 444 | result = self.ch347dll.CH347GetDeviceInfor( 445 | self.device_index, ctypes.byref(dev_info) 446 | ) 447 | if result: 448 | return dev_info 449 | else: 450 | return None 451 | 452 | def get_version(self): 453 | """ 454 | Obtain driver version, library version, device version, and chip type. 455 | 456 | This method retrieves various versions related to the CH347 device and returns them as a tuple. 457 | 458 | Returns: 459 | tuple or None: A tuple containing the following information if successful: 460 | - driver_ver (int): The driver version. 461 | - dll_ver (int): The library version. 462 | - device_ver (int): The device version. 463 | - chip_type (int): The chip type. 464 | Returns None if the retrieval fails. 465 | """ 466 | # Create variables to store the version information 467 | driver_ver = ctypes.c_ubyte() 468 | dll_ver = ctypes.c_ubyte() 469 | device_ver = ctypes.c_ubyte() 470 | chip_type = ctypes.c_ubyte() 471 | 472 | # Call the CH347GetVersion function 473 | result = self.ch347dll.CH347GetVersion( 474 | self.device_index, 475 | ctypes.byref(driver_ver), 476 | ctypes.byref(dll_ver), 477 | ctypes.byref(device_ver), 478 | ctypes.byref(chip_type), 479 | ) 480 | if result: 481 | return driver_ver.value, dll_ver.value, device_ver.value, chip_type.value 482 | else: 483 | return None 484 | 485 | def set_device_notify(self, device_id, notify_routine=event_callback): 486 | """ 487 | Configure device event notifier. 488 | 489 | Args: 490 | device_id (str): Optional parameter specifying the ID of the monitored device. 491 | notify_routine (callable): Callback function to handle device events. 492 | 493 | Returns: 494 | bool: True if successful, False otherwise. 495 | """ 496 | callback = self.NOTIFY_ROUTINE(notify_routine) 497 | result = self.ch347dll.CH347SetDeviceNotify( 498 | self.device_index, device_id, callback 499 | ) 500 | return result 501 | 502 | def read_data(self, buffer, length): 503 | """ 504 | Read USB data block. 505 | 506 | Args: 507 | buffer (ctypes.c_void_p): Pointer to a buffer to store the read data. 508 | length (ctypes.POINTER(ctypes.c_ulong)): Pointer to the length unit. Contains the length to be read as input and the actual read length after return. 509 | 510 | Returns: 511 | bool: True if successful, False otherwise. 512 | """ 513 | result = self.ch347dll.CH347ReadData(self.device_index, buffer, length) 514 | return result 515 | 516 | def write_data(self, buffer, length): 517 | """ 518 | Write USB data block. 519 | 520 | Args: 521 | buffer (ctypes.c_void_p): Pointer to a buffer containing the data to be written. 522 | length (ctypes.POINTER(ctypes.c_ulong)): Pointer to the length unit. Input length is the intended length, and the return length is the actual length. 523 | 524 | Returns: 525 | bool: True if successful, False otherwise. 526 | """ 527 | result = self.ch347dll.CH347WriteData(self.device_index, buffer, length) 528 | return result 529 | 530 | def set_timeout(self, write_timeout, read_timeout): 531 | """ 532 | Set the timeout of USB data read and write. 533 | 534 | Args: 535 | write_timeout (int): Timeout for USB to write data blocks, in milliseconds. Use 0xFFFFFFFF to specify no timeout (default). 536 | read_timeout (int): Timeout for USB to read data blocks, in milliseconds. Use 0xFFFFFFFF to specify no timeout (default). 537 | 538 | Returns: 539 | bool: True if successful, False otherwise. 540 | """ 541 | result = self.ch347dll.CH347SetTimeout( 542 | self.device_index, write_timeout, read_timeout 543 | ) 544 | return result 545 | 546 | def spi_init(self, spi_config: SPIConfig) -> bool: 547 | """ 548 | Initialize the SPI Controller. 549 | 550 | Args: 551 | spi_config (SPIConfig): The configuration for the SPI controller. 552 | 553 | Returns: 554 | bool: True if initialization is successful, False otherwise. 555 | """ 556 | result = self.ch347dll.CH347SPI_Init( 557 | self.device_index, ctypes.byref(spi_config) 558 | ) 559 | return result 560 | 561 | def spi_get_config(self): 562 | """ 563 | Retrieves the SPI controller configuration information. 564 | 565 | Returns: 566 | SPIConfig: An instance of the SPIConfig class containing the configuration information 567 | if the operation was successful. If the operation failed, returns None. 568 | 569 | The returned SPIConfig object will contain the configuration details retrieved from the SPI 570 | controller if the operation was successful. Otherwise, it will be None. 571 | """ 572 | spi_config = SPIConfig() 573 | result = self.ch347dll.CH347SPI_GetCfg( 574 | self.device_index, ctypes.byref(spi_config) 575 | ) 576 | if result: 577 | return spi_config 578 | else: 579 | return None 580 | 581 | def spi_change_cs(self, status): 582 | """ 583 | Change the chip selection status. 584 | 585 | Args: 586 | status (int): Chip selection status. 0 = Cancel the piece to choose, 1 = Set piece selected. 587 | 588 | Returns: 589 | bool: True if successful, False otherwise. 590 | """ 591 | result = self.ch347dll.CH347SPI_ChangeCS(self.device_index, status) 592 | return result 593 | 594 | def spi_set_chip_select( 595 | self, 596 | enable_select, 597 | chip_select, 598 | is_auto_deactive_cs, 599 | active_delay, 600 | delay_deactive, 601 | ): 602 | """ 603 | Set SPI chip selection. 604 | 605 | Args: 606 | enable_select (int): Enable selection status. The lower octet is CS1 and the higher octet is CS2. 607 | A byte value of 1 sets CS, 0 ignores this CS setting. 608 | chip_select (int): Chip selection status. The lower octet is CS1 and the higher octet is CS2. 609 | A byte value of 1 sets CS, 0 ignores this CS setting. 610 | is_auto_deactive_cs (int): Auto deactivation status. The lower 16 bits are CS1 and the higher 16 bits are CS2. 611 | Whether to undo slice selection automatically after the operation is complete. 612 | active_delay (int): Latency of read/write operations after chip selection, in microseconds. 613 | The lower 16 bits are CS1 and the higher 16 bits are CS2. 614 | delay_deactive (int): Delay time for read and write operations after slice selection, in microseconds. 615 | The lower 16 bits are CS1 and the higher 16 bits are CS2. 616 | 617 | Returns: 618 | bool: True if successful, False otherwise. 619 | """ 620 | result = self.ch347dll.CH347SPI_SetChipSelect( 621 | self.device_index, 622 | enable_select, 623 | chip_select, 624 | is_auto_deactive_cs, 625 | active_delay, 626 | delay_deactive, 627 | ) 628 | return result 629 | 630 | def spi_write( 631 | self, chip_select: int, write_data: List[int], write_step: int = 512 632 | ) -> bool: 633 | """ 634 | SPI write data. 635 | 636 | Args: 637 | chip_select (int): Chip selection control. When bit 7 is 0, chip selection control is ignored. 638 | When bit 7 is 1, chip selection operation is performed. 639 | write_data (List[int]): List of integers to write. 640 | write_step (int, optional): The length of a single block to be read. Default is 512. 641 | 642 | Returns: 643 | bool: True if successful, False otherwise. 644 | """ 645 | write_length = len(write_data) 646 | write_buffer = ctypes.create_string_buffer(bytes(write_data)) 647 | result = self.ch347dll.CH347SPI_Write( 648 | self.device_index, chip_select, write_length, write_step, write_buffer 649 | ) 650 | return result 651 | 652 | def spi_read( 653 | self, chip_select: int, write_data: List[int], read_length: int 654 | ) -> List[int]: 655 | """ 656 | SPI read data. 657 | 658 | Args: 659 | chip_select (int): Chip selection control. When bit 7 is 0, chip selection control is ignored. 660 | When bit 7 is 1, chip selection operation is performed. 661 | write_data (List[int]): List of integers to write. 662 | read_length (int): Number of bytes to read. 663 | 664 | Returns: 665 | List[int]: Data read in from the SPI stream if successful, None otherwise. 666 | """ 667 | write_length = len(write_data) 668 | 669 | # Create ctypes buffer for write data 670 | write_buffer = ctypes.create_string_buffer(bytes(write_data)) 671 | 672 | # Create ctypes buffer for read data 673 | read_buffer = ctypes.create_string_buffer(read_length) 674 | 675 | # Create combined buffer for read and write data 676 | combined_buffer = ctypes.create_string_buffer( 677 | write_buffer.raw[:write_length] + read_buffer.raw 678 | ) 679 | 680 | result = self.ch347dll.CH347SPI_Read( 681 | self.device_index, 682 | chip_select, 683 | write_length, 684 | ctypes.byref(ctypes.c_ulong(read_length)), 685 | combined_buffer, 686 | ) 687 | 688 | if result: 689 | # Extract the read data from the combined buffer 690 | read_data = list(combined_buffer[:read_length]) 691 | return read_data 692 | else: 693 | return None 694 | 695 | def spi_write_read(self, chip_select, length, io_buffer): 696 | """ 697 | Handle SPI data stream 4-wire interface. 698 | 699 | Args: 700 | chip_select (int): Selection control. If the film selection control bit 7 is 0, ignore the film selection control. 701 | If bit 7 is 1, perform the film selection. 702 | length (int): Number of bytes of data to be transferred. 703 | io_buffer (ctypes.c_void_p): Points to a buffer that places the data to be written out from DOUT. 704 | Returns the data read in from DIN. 705 | 706 | Returns: 707 | bool: True if successful, False otherwise. 708 | """ 709 | result = self.ch347dll.CH347SPI_WriteRead( 710 | self.device_index, chip_select, length, io_buffer 711 | ) 712 | return result 713 | 714 | def stream_spi4(self, chip_select, length, io_buffer): 715 | """ 716 | Handle SPI data stream 4-wire interface. 717 | 718 | Args: 719 | chip_select (int): Film selection control. If bit 7 is 0, slice selection control is ignored. 720 | If bit 7 is 1, the parameter is valid: Bit 1 bit 0 is 00/01/10. 721 | Select D0/D1/D2 pins as low-level active chip options, respectively. 722 | length (int): Number of bytes of data to be transferred. 723 | io_buffer (ctypes.c_void_p): Points to a buffer that places data to be written out from DOUT. 724 | Returns data to be read in from DIN. 725 | 726 | Returns: 727 | bool: True if successful, False otherwise. 728 | """ 729 | result = self.ch347dll.CH347StreamSPI4( 730 | self.device_index, chip_select, length, io_buffer 731 | ) 732 | return result 733 | 734 | def i2c_set(self, interface_speed): 735 | """ 736 | Set the serial port flow mode. 737 | 738 | Args: 739 | interface_speed (int): I2C interface speed / SCL frequency. Bit 1-bit 0: 740 | 0 = low speed / 20KHz 741 | 1 = standard / 100KHz (default) 742 | 2 = fast / 400KHz 743 | 3 = high speed / 750KHz 744 | 745 | Returns: 746 | bool: True if successful, False otherwise. 747 | """ 748 | result = self.ch347dll.CH347I2C_Set(self.device_index, interface_speed) 749 | return result 750 | 751 | def i2c_set_delay_ms(self, delay_ms): 752 | """ 753 | Set the hardware asynchronous delay to a specified number of milliseconds before the next stream operation. 754 | 755 | Args: 756 | delay_ms (int): Delay duration in milliseconds (ms). 757 | 758 | Returns: 759 | bool: True if successful, False otherwise. 760 | """ 761 | result = self.ch347dll.CH347I2C_SetDelaymS(self.device_index, delay_ms) 762 | return result 763 | 764 | def stream_i2c(self, write_data, read_length): 765 | """ 766 | Process I2C data stream. 767 | 768 | Args: 769 | write_data (bytes): Data to write. The first byte is usually the I2C device address and read/write direction bit. 770 | read_length (int): Number of bytes of data to read. 771 | 772 | Returns: 773 | bytes: Data read from the I2C stream. 774 | """ 775 | write_length = len(write_data) 776 | 777 | # Convert write_data to ctypes buffer 778 | write_buffer = ctypes.create_string_buffer(bytes(write_data)) 779 | 780 | # Create ctypes buffer for read data 781 | read_buffer = ctypes.create_string_buffer(read_length) 782 | 783 | result = self.ch347dll.CH347StreamI2C( 784 | self.device_index, write_length, write_buffer, read_length, read_buffer 785 | ) 786 | 787 | if result: 788 | return read_buffer[:read_length] 789 | else: 790 | return None 791 | 792 | def spi_set_frequency(self, spi_speed_hz: int) -> bool: 793 | """ 794 | Set the SPI clock frequency. 795 | 796 | After calling this interface, you need to call spi_init again for reinitialization. 797 | 798 | Args: 799 | spi_speed_hz (int): Set the SPI clock frequency in Hz. 800 | 801 | Returns: 802 | bool: True if successful, False otherwise. 803 | """ 804 | # Set the function argument types and return type if not already set 805 | if not hasattr(self.ch347dll.CH347SPI_SetFrequency, "argtypes"): 806 | self.ch347dll.CH347SPI_SetFrequency.argtypes = [ 807 | ctypes.c_ulong, 808 | ctypes.c_ulong, 809 | ] 810 | self.ch347dll.CH347SPI_SetFrequency.restype = ctypes.c_bool 811 | 812 | result = self.ch347dll.CH347SPI_SetFrequency(self.device_index, spi_speed_hz) 813 | return result 814 | 815 | def spi_set_data_bits(self, data_bits: int) -> bool: 816 | """ 817 | Set the SPI data bits (only supported by CH347F). 818 | 819 | Args: 820 | data_bits (int): 0=8bit, 1=16bit 821 | 822 | Returns: 823 | bool: True if successful, False otherwise. 824 | """ 825 | # Set the function argument types and return type if not already set 826 | if not hasattr(self.ch347dll.CH347SPI_SetDataBits, "argtypes"): 827 | self.ch347dll.CH347SPI_SetDataBits.argtypes = [ 828 | ctypes.c_ulong, 829 | ctypes.c_ubyte, 830 | ] 831 | self.ch347dll.CH347SPI_SetDataBits.restype = ctypes.c_bool 832 | 833 | result = self.ch347dll.CH347SPI_SetDataBits(self.device_index, data_bits) 834 | return result 835 | 836 | def get_serial_number(self) -> str: 837 | """ 838 | Get the USB serial number of the device. 839 | 840 | Returns: 841 | str: The device serial number if successful, None otherwise. 842 | """ 843 | # Set the function argument types and return type if not already set 844 | if not hasattr(self.ch347dll.CH347GetSerialNumber, "argtypes"): 845 | self.ch347dll.CH347GetSerialNumber.argtypes = [ 846 | ctypes.c_ulong, 847 | ctypes.c_char_p, 848 | ] 849 | self.ch347dll.CH347GetSerialNumber.restype = ctypes.c_bool 850 | 851 | # Create a buffer for the serial number 852 | serial_number = ctypes.create_string_buffer(64) 853 | 854 | result = self.ch347dll.CH347GetSerialNumber( 855 | self.device_index, 856 | serial_number, 857 | ) 858 | 859 | if result: 860 | return serial_number.value.decode("utf-8") 861 | else: 862 | return None 863 | 864 | def get_chip_type(self) -> int: 865 | """ 866 | Get the CH347 chip type. 867 | 868 | Returns: 869 | int: 0=CHIP_TYPE_CH341, 1=CHIP_TYPE_CH347/CHIP_TYPE_CH347T, 870 | 2=CHIP_TYPE_CH347F, 3=CHIP_TYPE_CH339W 871 | """ 872 | # Set the function argument types and return type if not already set 873 | if not hasattr(self.ch347dll.CH347GetChipType, "argtypes"): 874 | self.ch347dll.CH347GetChipType.argtypes = [ctypes.c_ulong] 875 | self.ch347dll.CH347GetChipType.restype = ctypes.c_ubyte 876 | 877 | result = self.ch347dll.CH347GetChipType(self.device_index) 878 | return result 879 | 880 | def i2c_set_stretch(self, enable: bool) -> bool: 881 | """ 882 | Set I2C Clock Stretch. 883 | 884 | Args: 885 | enable (bool): I2C Clock Stretch enable, True=enable, False=disable 886 | 887 | Returns: 888 | bool: True if successful, False otherwise. 889 | """ 890 | # Set the function argument types and return type if not already set 891 | if not hasattr(self.ch347dll.CH347I2C_SetStretch, "argtypes"): 892 | self.ch347dll.CH347I2C_SetStretch.argtypes = [ 893 | ctypes.c_ulong, 894 | ctypes.c_bool, 895 | ] 896 | self.ch347dll.CH347I2C_SetStretch.restype = ctypes.c_bool 897 | 898 | result = self.ch347dll.CH347I2C_SetStretch(self.device_index, enable) 899 | return result 900 | 901 | def i2c_set_driver_mode(self, mode: int) -> bool: 902 | """ 903 | Set the I2C pins drive mode. 904 | 905 | Args: 906 | mode (int): 0=open-drain mode, 1=push-pull mode 907 | 908 | Returns: 909 | bool: True if successful, False otherwise. 910 | """ 911 | # Set the function argument types and return type if not already set 912 | if not hasattr(self.ch347dll.CH347I2C_SetDriverMode, "argtypes"): 913 | self.ch347dll.CH347I2C_SetDriverMode.argtypes = [ 914 | ctypes.c_ulong, 915 | ctypes.c_ubyte, 916 | ] 917 | self.ch347dll.CH347I2C_SetDriverMode.restype = ctypes.c_bool 918 | 919 | result = self.ch347dll.CH347I2C_SetDriverMode(self.device_index, mode) 920 | return result 921 | 922 | def stream_i2c_ret_ack(self, write_data, read_length) -> tuple: 923 | """ 924 | Process I2C data stream, 2-wire interface, and return the number of ACK obtained by the host side. 925 | 926 | Args: 927 | write_data (bytes): Data to write. The first byte is usually the I2C device address and read/write direction bit. 928 | read_length (int): Number of bytes of data to read. 929 | 930 | Returns: 931 | tuple: (bool, bytes, int) - A tuple containing: 932 | - bool: True if successful, False otherwise 933 | - bytes: Data read from the I2C stream 934 | - int: The number of ACK values returned by read/write 935 | """ 936 | # Set the function argument types and return type if not already set 937 | if not hasattr(self.ch347dll.CH347StreamI2C_RetACK, "argtypes"): 938 | self.ch347dll.CH347StreamI2C_RetACK.argtypes = [ 939 | ctypes.c_ulong, 940 | ctypes.c_ulong, 941 | ctypes.c_void_p, 942 | ctypes.c_ulong, 943 | ctypes.c_void_p, 944 | ctypes.POINTER(ctypes.c_ulong), 945 | ] 946 | self.ch347dll.CH347StreamI2C_RetACK.restype = ctypes.c_bool 947 | 948 | write_length = len(write_data) 949 | 950 | # Convert write_data to ctypes buffer 951 | write_buffer = ctypes.create_string_buffer(bytes(write_data)) 952 | 953 | # Create ctypes buffer for read data 954 | read_buffer = ctypes.create_string_buffer(read_length) 955 | 956 | # Create a variable to store the ACK count 957 | ack_count = ctypes.c_ulong(0) 958 | 959 | result = self.ch347dll.CH347StreamI2C_RetACK( 960 | self.device_index, 961 | write_length, 962 | write_buffer, 963 | read_length, 964 | read_buffer, 965 | ctypes.byref(ack_count), 966 | ) 967 | 968 | if result: 969 | return result, read_buffer[:read_length], ack_count.value 970 | else: 971 | return result, None, 0 972 | 973 | def read_eeprom(self, eeprom_id: int, addr: int, length: int) -> bytes: 974 | """ 975 | Reads data blocks from EEPROM. 976 | 977 | Args: 978 | eeprom_id (int): EEPROM model ID (see EEPROM_TYPE enum in header file). 979 | addr (int): The address of data unit. 980 | length (int): Number of bytes of data to be read. 981 | 982 | Returns: 983 | bytes: The data read from the EEPROM if successful, None otherwise. 984 | """ 985 | # Set the function argument types and return type if not already set 986 | if not hasattr(self.ch347dll.CH347ReadEEPROM, "argtypes"): 987 | self.ch347dll.CH347ReadEEPROM.argtypes = [ 988 | ctypes.c_ulong, 989 | ctypes.c_int, # EEPROM_TYPE enum 990 | ctypes.c_ulong, 991 | ctypes.c_ulong, 992 | ctypes.POINTER(ctypes.c_ubyte), 993 | ] 994 | self.ch347dll.CH347ReadEEPROM.restype = ctypes.c_bool 995 | 996 | # Create a buffer for the data 997 | buffer = (ctypes.c_ubyte * length)() 998 | 999 | result = self.ch347dll.CH347ReadEEPROM( 1000 | self.device_index, 1001 | eeprom_id, 1002 | addr, 1003 | length, 1004 | buffer, 1005 | ) 1006 | 1007 | if result: 1008 | return bytes(buffer) 1009 | else: 1010 | return None 1011 | 1012 | def write_eeprom(self, eeprom_id: int, addr: int, data: bytes) -> bool: 1013 | """ 1014 | Writes a data block to the EEPROM. 1015 | 1016 | Args: 1017 | eeprom_id (int): EEPROM model ID (see EEPROM_TYPE enum in header file). 1018 | addr (int): The address of data unit. 1019 | data (bytes): Data to be written. 1020 | 1021 | Returns: 1022 | bool: True if successful, False otherwise. 1023 | """ 1024 | # Set the function argument types and return type if not already set 1025 | if not hasattr(self.ch347dll.CH347WriteEEPROM, "argtypes"): 1026 | self.ch347dll.CH347WriteEEPROM.argtypes = [ 1027 | ctypes.c_ulong, 1028 | ctypes.c_int, # EEPROM_TYPE enum 1029 | ctypes.c_ulong, 1030 | ctypes.c_ulong, 1031 | ctypes.POINTER(ctypes.c_ubyte), 1032 | ] 1033 | self.ch347dll.CH347WriteEEPROM.restype = ctypes.c_bool 1034 | 1035 | length = len(data) 1036 | 1037 | # Convert data to ctypes buffer 1038 | buffer = (ctypes.c_ubyte * length)(*data) 1039 | 1040 | result = self.ch347dll.CH347WriteEEPROM( 1041 | self.device_index, 1042 | eeprom_id, 1043 | addr, 1044 | length, 1045 | buffer, 1046 | ) 1047 | 1048 | return result 1049 | 1050 | ##################################################################### 1051 | # 1052 | # GPIO Interfaces 1053 | # 1054 | ##################################################################### 1055 | 1056 | def gpio_get(self, io_dir, io_data): 1057 | """ 1058 | Get GPIO Direction and Pin Level of CH347 1059 | 1060 | Args: 1061 | index (int): Specifies the device index. 1062 | io_dir (ctypes.c_void_p): GPIO direction: bits 0–7 correspond to GPIO0–GPIO7. 0 = input; 1 = output. 1063 | io_data (ctypes.c_void_p): GPIO level: bits 0–7 correspond to GPIO0–GPIO7. 0 = low level; 1 = high level. 1064 | """ 1065 | dir_val = ctypes.c_ubyte(0) 1066 | data_val = ctypes.c_ubyte(0) 1067 | result = self.ch347dll.CH347GPIO_Get( 1068 | self.device_index, ctypes.byref(dir_val), ctypes.byref(data_val) 1069 | ) 1070 | io_dir[0] = dir_val.value 1071 | io_data[0] = data_val.value 1072 | return result 1073 | 1074 | 1075 | def gpio_set(self, io_mask, io_dir, io_data): 1076 | """ 1077 | Get the GPIO direction and pin level values of CH347. 1078 | 1079 | Args: 1080 | index (int): Specifies the device index. 1081 | io_mask (int): Data valid mask: bits 0-7 correspond to GPIO0-7. 1082 | io_dir (int): Sets the I/O direction; if a bit is 0, the corresponding pin is input; if a bit is 1, the corresponding pin is output. GPIO0-7 correspond to bits 0-7. 1083 | io_data (int): Output data; if the I/O direction is output, then when a bit is 0, the corresponding pin outputs a low level; when a bit is 1, it outputs a high level. 1084 | """ 1085 | result = self.ch347dll.CH347GPIO_Set( 1086 | self.device_index, io_mask, io_dir, io_data 1087 | ) 1088 | return result 1089 | 1090 | def set_interrupt(self, int0_pin, int0_mode, int1_pin, int1_mode, interrupt_callback_func=interrupt_callback): 1091 | """ 1092 | Set CH347 GPIO Interrupt Service Routine 1093 | 1094 | Args: 1095 | int0_pin (int): Interrupt GPIO pin number 0-7 or >7 to disable 1096 | int0_mode (int): Interrupt mode for int0 (0=falling,1=rising,2=both,3=reserved) 1097 | int1_pin (int): Interrupt GPIO pin number 0-7 or >7 to disable 1098 | int1_mode (int): Interrupt mode for int1 (same as int0_mode) 1099 | interrupt_callback_func (callable or None): Callback function or None to disable ISR 1100 | 1101 | Returns: 1102 | bool: True if successful, False otherwise 1103 | """ 1104 | 1105 | if interrupt_callback_func is None: 1106 | # Create a NULL callback pointer to disable interrupt 1107 | self.interrupt_cb_func = ctypes.cast(0, self.INTERRUPT_ROUTINE) 1108 | else: 1109 | # Wrap the Python callback with the C function prototype 1110 | self.interrupt_cb_func = self.INTERRUPT_ROUTINE(interrupt_callback_func) 1111 | 1112 | result = self.ch347dll.CH347SetIntRoutine( 1113 | self.device_index,int0_pin,int0_mode,int1_pin,int1_mode,self.interrupt_cb_func 1114 | ) 1115 | return result 1116 | 1117 | def read_interrupt_status(self, istatus): 1118 | """ 1119 | This function is used to read interrupt data 1120 | Args: 1121 | iIndex: Specifies the device index to operate on 1122 | iStatus: Pointer to a byte used to store the read GPIO pin status data; refer to the bit description below 1123 | Returns: 1124 | bool: Returns True if successful, False otherwise. 1125 | """ 1126 | # Create a buffer for the serial number 1127 | int_val = (ctypes.c_ubyte * 8)() 1128 | #result = self.ch347dll.CH347ReadInter(self.device_index, int_val) 1129 | result = False 1130 | print("there is error for this function") 1131 | istatus[:] = list(int_val) 1132 | return result 1133 | 1134 | 1135 | def abort_interrupt(self): 1136 | """ 1137 | Cancel GPIO Interrupt Service 1138 | Returns: 1139 | bool: Returns True if successful, False otherwise. 1140 | """ 1141 | return self.ch347dll.CH347AbortInter(self.device_index) 1142 | 1143 | 1144 | ##################################################################### 1145 | # 1146 | # Uart Interface 1147 | # 1148 | ##################################################################### 1149 | 1150 | def uart_open(self): 1151 | """ 1152 | Open serial port 1153 | """ 1154 | 1155 | handle = self.ch347dll.CH347Uart_Open(self.device_index) 1156 | if handle != self.INVALID_HANDLE_VALUE: 1157 | return handle 1158 | else: 1159 | return None 1160 | 1161 | def uart_close(self): 1162 | """ 1163 | Close serial port 1164 | """ 1165 | result = self.ch347dll.CH347Uart_Close(self.device_index) 1166 | return result 1167 | 1168 | def uart_set_notify(self, device_id, uart_notify_routine=uart_event_callback): 1169 | """ 1170 | Set the device event notification program 1171 | """ 1172 | ###################################################### 1173 | # 1174 | # Not tested yet 1175 | # 1176 | ###################################################### 1177 | 1178 | if uart_notify_routine is None: 1179 | # Create a NULL callback pointer to disable interrupt 1180 | self.uart_callback_func = ctypes.cast(0, self.NOTIFY_ROUTINE) 1181 | else: 1182 | # Wrap the Python callback with the C function prototype 1183 | self.uart_callback_func = self.NOTIFY_ROUTINE(uart_notify_routine) 1184 | 1185 | result = self.ch347dll.CH347Uart_SetDeviceNotify( 1186 | self.device_index, device_id, self.uart_callback_func 1187 | ) 1188 | return result 1189 | 1190 | def uart_getcfg(self, baudrate, bytesize, parity, stopbits, timeout): 1191 | """ 1192 | Obtain UART hardware configuration 1193 | """ 1194 | ###################################################### 1195 | # 1196 | # Tested, seems not working 1197 | # 1198 | ###################################################### 1199 | baudrate_val = ctypes.c_ulong(1) 1200 | bytesize_val = ctypes.c_ubyte(1) 1201 | parity_val = ctypes.c_ubyte(1) 1202 | stopbits_val = ctypes.c_ubyte(1) 1203 | timeout_val = ctypes.c_ubyte(1) 1204 | result = self.ch347dll.CH347Uart_GetCfg( 1205 | self.device_index, 1206 | ctypes.byref(baudrate_val), 1207 | ctypes.byref(bytesize_val), 1208 | ctypes.byref(parity_val), 1209 | ctypes.byref(stopbits_val), 1210 | ctypes.byref(timeout_val) 1211 | ) 1212 | baudrate[0] = baudrate_val.value 1213 | bytesize[0] = bytesize_val.value 1214 | parity[0] = parity_val.value 1215 | stopbits[0] = stopbits_val.value 1216 | timeout[0] = timeout_val.value 1217 | 1218 | return result 1219 | 1220 | def uart_init(self, baudrate, bytesize, parity, stopbits, timeout): 1221 | """ 1222 | Set UART configuration 1223 | """ 1224 | result = self.ch347dll.CH347Uart_Init( 1225 | self.device_index, baudrate, bytesize, parity, stopbits, timeout 1226 | ) 1227 | return result 1228 | 1229 | def uart_set_timeout(self, write_timeout, read_timeout): 1230 | """ 1231 | Set the timeout of USB data read and write 1232 | """ 1233 | result = self.ch347dll.CH347Uart_SetTimeout( 1234 | self.device_index, write_timeout, read_timeout 1235 | ) 1236 | return result 1237 | 1238 | def uart_read(self, io_buffer, length): 1239 | """ 1240 | Read data block 1241 | """ 1242 | read_buffer = ctypes.create_string_buffer(length[0]) 1243 | length_val = ctypes.c_ulong(length[0]) 1244 | result = self.ch347dll.CH347Uart_Read( 1245 | self.device_index, read_buffer, ctypes.byref(length_val) 1246 | ) 1247 | ctypes.memmove(io_buffer, read_buffer, length_val.value) 1248 | length[0] = length_val.value 1249 | return result 1250 | 1251 | def uart_write(self, io_buffer, length): 1252 | """ 1253 | Write out blocks of data 1254 | """ 1255 | write_buffer = ctypes.create_string_buffer(bytes(io_buffer)) 1256 | length_val = ctypes.c_ulong(length[0]) 1257 | result = self.ch347dll.CH347Uart_Write( 1258 | self.device_index, write_buffer, ctypes.byref(length_val) 1259 | ) 1260 | length[0] = length_val.value 1261 | return result 1262 | 1263 | 1264 | def uart_query_buffer_upload(self, remain_bytes): 1265 | """ 1266 | Query how many bytes are unfetched in the read buffer 1267 | """ 1268 | ###################################################### 1269 | # 1270 | # Not tested yet 1271 | # 1272 | ###################################################### 1273 | length_val = ctypes.c_longlong() 1274 | result = self.ch347dll.CH347Uart_QueryBufUpload( 1275 | self.device_index, ctypes.byref(length_val) 1276 | ) 1277 | remain_bytes[0] = length_val.value 1278 | return result 1279 | 1280 | def uart_get_device_info(self): 1281 | """ 1282 | Obtaining Device Information 1283 | """ 1284 | dev_info = DeviceInfo() 1285 | result = self.ch347dll.CH347Uart_GetDeviceInfor( 1286 | self.device_index, ctypes.byref(dev_info) 1287 | ) 1288 | if result: 1289 | return dev_info 1290 | else: 1291 | return None --------------------------------------------------------------------------------