前言
CH347为一款USB转JTAG/SPI/IIC/GPIO/UART接口的转换芯片,此处总结一下开发时所遇到的在C/C++、Python、C#下调用CH347DLL的方法,若有其他需要补充的也可一起交流。
基于WCH官方Demo板做的代码验证。
1、C/C++调用
可参考WCH官网CH347EVT/Tools下工程代码,此处则不再重复
2、Python调用
选择Python调用时,需对应上Python和DLL是否都为32或64位,否则会出现调用失败的情况,传入API参数时则只需注意一下特殊的结构体此类参函数即可。
'''
Author: OIDCAT
Date: 2022-07-13 15:22:17
LastEditTime: 2022-08-23 19:19:23
此处需注意Python版本位数与DLL是否匹配
'''
#! /usr/bin/env python
#coding=utf-8
import ctypes
import os
from ctypes import *
#
print("进入程序")
CH347 = windll.LoadLibrary("./CH347DLL.DLL")
DevIndex = 0
I2C_addr = 0xA0
class spi_config(Structure):
_fields_ = [
("iMode", c_ubyte),
("iClock", c_ubyte),
("iByteOrder", c_ubyte),
("iSpiWriteReadInterval", c_ushort),
("iSpiOutDefaultData",c_ubyte),
("iChipSelect", c_ulong),
("CS1Polarity",c_ubyte),
("CS2Polarity", c_ubyte),
("iIsAutoDeativeCS", c_ushort),
("iActiveDelay", c_ushort),
("iDelayDeactive", c_ulong),
]
def init():
if CH347.CH347OpenDevice(DevIndex) != -1:
print("CH347 Open succeeded")
CH347.CH347I2C_Set(DevIndex, 3)
CH347_SPI = spi_config()
CH347_SPI.iMode = 0x03
CH347_SPI.iClock = 0x01
CH347_SPI.iByteOrder = 0x01
CH347_SPI.iSpiOutDefaultData = 0xff
CH347_SPI.iChipSelect = 0x80
CH347.CH347SPI_Init(DevIndex, CH347_SPI)
# CH347.CH347CloseDevice(DevIndex)
else:
print("Open The CH347 Failed")
def read(addr):
if CH347.CH347OpenDevice(DevIndex) != -1:
oBuf = (c_byte *2)()
iBuf = (c_byte *1)()
oBuf[0] = 0xA0
oBuf[1] = addr
CH347.CH347StreamI2C(DevIndex, 2, oBuf, 1, iBuf)
CH347.CH347CloseDevice(DevIndex)
print("CH347 read succeeded")
return iBuf[0] & 0xFF
else:
print("CH347I2C.CH347OpenDevice")
return 0
def spi_readId(DevIndex):
cmd_buf = (c_byte * 4)()
len = 4
cmd_buf[0] = 0x9F
cmd_buf[1] = 0xFF
cmd_buf[2] = 0xFF
cmd_buf[3] = 0xFF
CH347.CH347SPI_WriteRead(DevIndex, 0x80, len, cmd_buf)
print("{0:x} {1:x} {2:x} {3:x}".format(cmd_buf[0], cmd_buf[1], cmd_buf[2], cmd_buf[3]))
return cmd_buf[0] & 0xFF
if __name__ == "__main__":
print("CH347 Test")
init()
read(0x02)
spi_readId(DevIndex)
3、C#调用
使用C#比较麻烦的就是需要将头文件重新换格式,但实际上工作量并不是太大,其中结构体需要注意其地址对齐。
using System;
namespace CH347T_Develop
{
using System.Runtime.InteropServices;
[StructLayout(LayoutKind.Explicit, CharSet = CharSet.Ansi, Size = 8)]
public struct mUSB_SETUP_PKT
{// USB控制传输的建立阶段的数据请求包结构
[FieldOffset(0)]
public byte mUspReqType; // 00H 请求类型
[FieldOffset(1)]
public byte mUspRequest; // 01H 请求代码
[FieldOffset(2)]
public byte mUspValueLow;// 02H 值参数低字节
[FieldOffset(3)]
public byte mUspValueHigh; // 03H 值参数高字节
[FieldOffset(2)]
public UInt16 mUspValue; // 02H-03H 值参数
[FieldOffset(4)]
public byte mUspIndexLow;// 04H 索引参数低字节
[FieldOffset(5)]
public byte mUspIndexHigh; // 05H 索引参数高字节
[FieldOffset(4)]
public UInt16 mUspIndex; // 04H-05H 索引参数
[FieldOffset(6)]
public UInt16 mLength;// 06H-07H 数据阶段的数据长度
}
[StructLayout(LayoutKind.Explicit, CharSet = CharSet.Ansi)]
public struct mWIN32_COMMAND_USB_SETUP_PKT
{ // 定义WIN32命令接口结构
[FieldOffset(0)]
public UInt32 mFunction; // 输入时指定功能代码或者管道号
[FieldOffset(0)]
public Int32 mStatus; // 输出时返回操作状态
[FieldOffset(4)]
public UInt32 mLength; // 存取长度,返回后续数据的长度
[FieldOffset(8)]
public mUSB_SETUP_PKT mSetupPkt; // USB控制传输的建立阶段的数据请求
}
[StructLayout(LayoutKind.Explicit, CharSet = CharSet.Ansi)]
public struct mWIN32_COMMAND_mBuffer
{// 定义WIN32命令接口结构
[FieldOffset(0)]
public UInt32 mFunction;// 输入时指定功能代码或者管道号
[FieldOffset(0)]
public Int32 mStatus;// 输出时返回操作状态
[FieldOffset(4)]
public UInt32 mLength;// 存取长度,返回后续数据的长度
[MarshalAs(UnmanagedType.ByValArray, SizeConst = CH347DLL.mCH341_PACKET_LENGTH), FieldOffset(8)]
public byte[] mBuffer; // 数据缓冲区,长度为0至255B
}
// 此处可能存在问题,待修改
[StructLayout(LayoutKind.Explicit, CharSet = CharSet.Ansi)]
public struct DEV_INFOR
{
[FieldOffset(0)]
public byte iIndex; // 当前打开序号
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 64), FieldOffset(16)]
public byte[] DevicePath; // 设备链接名,用于CreateFile
[FieldOffset(18)]
public byte UsbClass; // 0:CH347_USB_CH341, 2:CH347_USB_HID,3:CH347_USB_VCP
[FieldOffset(19)]
public byte FuncType; // 0:CH347_FUNC_UART,1:CH347_FUNC_SPI_I2C,2:CH347_FUNC_JTAG_I2C
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 64), FieldOffset(35)]
public byte[] DeviceID; // USB\VID_xxxx&PID_xxxx
[FieldOffset(36)]
public byte ChipMode; // 芯片模式,0:Mode0(UART0/1); 1:Mode1(Uart1+SPI+I2C); 2:Mode2(HID Uart1+SPI+I2C) 3:Mode3(Uart1+Jtag+IIC)
[FieldOffset(0)]
IntPtr DevHandle; // 设备句柄
[FieldOffset(0)]
public UInt16 BulkOutEndpMaxSize; // 上传端点大小
[FieldOffset(2)]
public UInt16 BulkInEndpMaxSize; // 下传端点大小
[FieldOffset(37)]
public byte UsbSpeedType; // USB速度类型,0:FS,1:HS,2:SS
[FieldOffset(38)]
public byte CH347IfNum; // 设备接口号: 0:UART,1:SPI/IIC/JTAG/GPIO
[FieldOffset(39)]
public byte DataUpEndp; // 端点地址
[FieldOffset(40)]
public byte DataDnEndp; // 端点地址
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 64), FieldOffset(56)]
public byte[] ProductString; // USB产品字符串
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 64), FieldOffset(72)]
public byte[] ManufacturerString; // USB厂商字符串
[FieldOffset(0)]
public UInt32 WriteTimeout; // USB写超时
[FieldOffset(4)]
public UInt32 ReadTimeout; // USB读超时
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 64), FieldOffset(88)]
public byte[] FuncDescStr; // 接口功能描述符
[FieldOffset(89)]
public byte FirewareVer; // 固件版本
}
class CH347DLL
{
public const int mCH341_PACKET_LENGTH = 64;
public byte Index;
// CH341端点地址
public const int mCH347_ENDP_DATA_UP = 0x86;// CH347的数据块上传端点的地址
public const int mCH347_ENDP_DATA_DOWN = 0x06; // CH347的数据块下传端点的地址
// 设备层接口提供的管道操作命令
public const int mPipeDeviceCtrl = 0x00000004; // CH347的综合控制管道
public const int mPipeDataUp = 0x00000006; // CH347的数据块上传管道
public const int mPipeDataDown = 0x00000007; // CH347的数据块下传管道
// 应用层接口的功能代码
public const int mFuncNoOperation = 0x00000000; // 无操作
public const int mFuncGetVersion = 0x00000001; // 获取驱动程序版本号
public const int mFuncGetConfig = 0x00000002; // 获取USB设备配置描述符
public const int mFuncSetTimeout = 0x00000009; // 设置USB通讯超时
public const int mFuncSetExclusive = 0x0000000b; // 设置独占使用
public const int mFuncResetDevice = 0x0000000c; // 复位USB设备
public const int mFuncResetPipe = 0x0000000d; // 复位USB管道
public const int mFuncAbortPipe = 0x0000000e; // 取消USB管道的数据请求
public const int mFuncBufferMode = 0x00000020; // 设定缓冲上传模式及查询缓冲区中的数据长度
public const int mFuncBufferModeDn = 0x00000021; // 设定缓冲下传模式及查询缓冲区中的数据长度
public const int mFuncGetVersionEx = 0x00000022; // 获取驱动程序版本号及芯片型号
// USB设备标准请求代码
public const int mUSB_CLR_FEATURE = 0x01;
public const int mUSB_SET_FEATURE = 0x03;
public const int mUSB_GET_STATUS = 0x00;
public const int mUSB_SET_ADDRESS = 0x05;
public const int mUSB_GET_DESCR = 0x06;
public const int mUSB_SET_DESCR = 0x07;
public const int mUSB_GET_CONFIG = 0x08;
public const int mUSB_SET_CONFIG = 0x09;
public const int mUSB_GET_INTERF = 0x0a;
public const int mUSB_SET_INTERF = 0x0b;
public const int mUSB_SYNC_FRAME = 0x0c;
// CH341控制传输的厂商专用请求类型
public const int mCH341_VENDOR_READ = 0xC0; // 通过控制传输实现的CH341厂商专用读操作
public const int mCH341_VENDOR_WRITE = 0x40; // 通过控制传输实现的CH341厂商专用写操作
public const int mCH341A_CMD_I2C_STREAM = 0xAA; // I2C接口的命令包,从次字节开始为I2C命令流
public const int mCH341A_CMD_UIO_STREAM = 0xAB; // UIO接口的命令包,从次字节开始为命令流
public const int mCH341A_CMD_PIO_STREAM = 0xAE; // PIO接口的命令包,从次字节开始为数据流
// CH341A控制传输的厂商专用请求代码
public const int mCH341A_BUF_CLEAR = 0xB2; // 清除未完成的数据
public const int mCH341A_I2C_CMD_X = 0x54; // 发出I2C接口的命令,立即执行
public const int mCH341A_DELAY_MS = 0x5E; // 以亳秒为单位延时指定时间
public const int mCH341A_GET_VER = 0x5F; // 获取芯片版本
public const int mCH341A_CMD_I2C_STM_STA = 0x74; // I2C接口的命令流:产生起始位
public const int mCH341A_CMD_I2C_STM_STO = 0x75; // I2C接口的命令流:产生停止位
public const int mCH341A_CMD_I2C_STM_OUT = 0x80; // I2C接口的命令流:输出数据,位5-位0为长度,后续字节为数据,0长度则只发送一个字节并返回应答
public const int mCH341A_CMD_I2C_STM_IN = 0xC0; // I2C接口的命令流:输入数据,位5-位0为长度,0长度则只接收一个字节并发送无应答
public const int mCH341A_CMD_I2C_STM_MAX = ((0x3F < mCH341_PACKET_LENGTH) ? 0x3F : mCH341_PACKET_LENGTH);// I2C接口的命令流单个命令输入输出数据的最大长度
public const int mCH341A_CMD_I2C_STM_SET = 0x60; // I2C接口的命令流:设置参数,位2=SPI的I/O数(0=单入单出,1=双入双出),位1位0=I2C速度(00=低速,01=标准,10=快速,11=高速)
public const int mCH341A_CMD_I2C_STM_US = 0x40; // I2C接口的命令流:以微秒为单位延时,位3-位0为延时值
public const int mCH341A_CMD_I2C_STM_MS = 0x50; // I2C接口的命令流:以亳秒为单位延时,位3-位0为延时值
public const int mCH341A_CMD_I2C_STM_DLY = 0x0F; // I2C接口的命令流单个命令延时的最大值
public const int mCH341A_CMD_I2C_STM_END = 0x00; // I2C接口的命令流:命令包提前结束
public const int mCH341A_CMD_UIO_STM_IN = 0x00; // UIO接口的命令流:输入数据D7-D0
public const int mCH341A_CMD_UIO_STM_DIR = 0x40; // UIO接口的命令流:设定I/O方向D5-D0,位5-位0为方向数据
public const int mCH341A_CMD_UIO_STM_OUT = 0x80; // UIO接口的命令流:输出数据D5-D0,位5-位0为数据
public const int mCH341A_CMD_UIO_STM_US = 0xC0; // UIO接口的命令流:以微秒为单位延时,位5-位0为延时值
public const int mCH341A_CMD_UIO_STM_END = 0x20; // UIO接口的命令流:命令包提前结束
public const int MAX_DEVICE_PATH_SIZE = 128; // 设备名称的最大字符数
public const int MAX_DEVICE_ID_SIZE = 64; // 设备ID的最大字符数
//驱动接口
public const int CH347_USB_VENDOR = 0;
public const int CH347_USB_HID = 2;
public const int CH347_USB_VCP = 3;
//CH347_USB_VENDOR支持CH341/7
public const int CHIP_TYPE_CH341 = 0;
public const int CHIP_TYPE_CH347 = 1;
//芯片功能接口号
public const int CH347_FUNC_UART = 0;
public const int CH347_FUNC_SPI_IIC = 1;
public const int CH347_FUNC_JTAG_IIC = 2;
public const int DEFAULT_READ_TIMEOUT = 500; //默认读超时毫秒数
public const int DEFAULT_WRITE_TIMEOUT = 500; //默认写超时毫秒数
public const int mCH347_PACKET_LENGTH = 512; // CH347支持的数据包的长度
private const string CH347_DLL = "CH347DLL.DLL";
/// <summary>
/// HANDLE WINAPI CH347OpenDevice(ULONG DevI);
/// </summary>
/// <param name="Index"></param>
/// <returns></returns>
[DllImport("CH347DLL.DLL", EntryPoint = "CH347OpenDevice")]
public static extern IntPtr CH347OpenDevice(UInt32 Index);
[DllImport("CH347DLL.DLL", EntryPoint = "CH347CloseDevice")]
public static extern IntPtr CH347CloseDevice(UInt32 Index);
[DllImport("CH347DLL.DLL", EntryPoint = "CH347Uart_Open")]
public static extern IntPtr CH347Uart_Open(UInt32 Index);
[DllImport("CH347DLL.DLL", EntryPoint = "CH347Uart_Close")]
public static extern IntPtr CH347Uart_Close(UInt32 Index);
[DllImport("CH347DLL.DLL", EntryPoint = "CH347GetDeviceInfor")]
public static extern IntPtr CH347GetDeviceInfor(UInt32 Index,
ref DEV_INFOR info);
[DllImport("CH347DLL.DLL", EntryPoint = "CH347GPIO_Get")]
public static extern IntPtr CH347GPIO_Get(UInt32 Index,
ref UInt16 iDir,
ref UInt16 iData);
[DllImport("CH347DLL.DLL", EntryPoint = "CH347GPIO_Set")]
public static extern IntPtr CH347GPIO_Set(UInt32 Index,
UInt32 iEnable,
UInt32 iSetDirOut,
UInt32 iSetDataOut);
}
}