USB转I2C芯片操作EEPROM--CH347应用-程序员宅基地

技术标签: USB转I2C  物联网  嵌入式硬件  USB转接产品  单片机  

USB转I2C芯片简介

        高速USB转接芯片CH347是一款集成480Mbps高速USB接口、JTAG接口、SPI接口、I2C接口、异步UART串口、GPIO接口等多种硬件接口的转换芯片。

接口示意图:

CH347-I2C接口特点

  • USB传输采用USB2.0高速(480Mbps)
  • 工作在 Host/Master主机模式;
  • 硬件信号:SCL、SDA;
  • 支持4种传输速度:低速20KHz、标准100KHz、快速400KHz、高速750KHz;
  • 支持I2C时序参数调节;
  • 提供计算机端驱动程序和USB转I2C函数库,支持二次开发

使用芯片准备工作

选择CH347工作模式

        CH347包括CH347T和CH347F两款型号,CH347T芯片使用I2C功能需要额外模式选择,芯片在复位时,会根据DTR1(CFG0)和RTS1(CFG1)引脚的电平状态配置其工作模式,各工作模式及功能说明如下:

工作模式

模式说明

CFG0

CFG1

Mode0

480Mbps高速USB转双UART(Baudrate最高9Mbps)

1

1

Mode1

480Mbps高速USB转UART+SPI+I2C(厂商驱动模式)

0

1

Mode2

480Mbps高速USB转UART+SPI+I2C(系统HID驱动模式)

1

0

Mode3

480Mbps高速USB转UART+JTAG(厂商驱动模式)

0

0

        CH347可使用I2C的模式有两种,其区别在Mode1需要安装厂商驱动,Mode3可以使用系统内置HID驱动无需额外安装,只需在编程时调用CH347动态库进行软件编程即可,本博客使用Mode1来进行操作。

        CH347F芯片不需要进行额外的模式选择,该型号默认支持所有接口功能。

驱动安装

window驱动安装

        从WCH官网下载CH347转SPI/I2C/JTAG/GPIO驱动:CH341PAR.EXE - 南京沁恒微电子股份有限公司

        驱动下载后进行一次安装,后续即可实现系统“免驱”效果无需二次安装。未插入设备时安装会显示“驱动预安装成功”,此时驱动已经正常安装,硬件即插即用。

        Windows驱动通过微软数字签名认证,支持32/64位 Windows 11/10/8.1/8/7/VISTA/XP/2000,SERVER 2019/2016/2012/2008/2003等系统,无需担心Windows不同系统兼容性问题。

        官方同时提供驱动资源包CH341PAR.ZIP - 南京沁恒微电子股份有限公司,可将驱动安装文件打包至成熟产品一齐发布,且支持无界面安装操作,可通过软件编程调用命令行操作,只需执行“SETUP /S”命令即可静默驱动安装。

        点击安装之后,等待弹出安装成功窗口后点击确定即可。

Linux驱动安装

        下载Linux驱动、应用库和软件包:CH341PAR_LINUX.ZIP - 南京沁恒微电子股份有限公司USB转JTAG/SPI/I2C/并口/GPIO等接口的Linux设备驱动程序,支持CH341的USB转SPI/I2C/EPP并口/MEM并口等,支持CH347的480Mbps高速USB转JTAG/SPI/I2C/GPIO等,支持32/64位操作系统。icon-default.png?t=N7T8https://www.wch.cn/downloads/CH341PAR_LINUX_ZIP.html         按照压缩包内的说明资料使用驱动和其他资料。

        1、执行 sudo make install 可自动编译与安装驱动;

        2、插入USB设备可查看到生成前缀为ch34x_pis的设备节点。

使用USB操作EEPROM

        本次操作CH347开发板板载EEPROM:24LC515H。

        除此之外,CH347也可操作AT24xxx、M24xxx等等EEPROM

调用函数

        WCH提供了一套公用的库函数接口,即Windows&Linux平台接口函数名称与参数一致,其库函数接口特性如下:

        操作SPI、I2C、GPIO等的接口在任何工作模式下都可使用同一API,在进行软件编写时,只需调用接口完成代码操作逻辑而不用关注当前硬件工作模式。提供插拔检测函数可动态监测设备插拔信息,更方便进行设备管理。

        具体详细内容可参考官方开发手册:CH347EVT.ZIP - 南京沁恒微电子股份有限公司 【目录:CH347EVT\EVT\PUB\CH347应用开发手册.PDF】

/***************插拔监测函数************/
BOOL    WINAPI  CH347SetDeviceNotify(                                           // 设定设备事件通知程序
                                     ULONG                  iIndex,             // 指定设备序号,0对应第一个设备
                                     PCHAR                  iDeviceID,          // 可选参数,指向字符串,指定被监控的设备的ID,字符串以\0终止
                                     mPCH347_NOTIFY_ROUTINE iNotifyRoutine );   // 指定设备事件回调程序,为NULL则取消事件通知,否则在检测到事件时调用该程序
/********IIC接口函数通用于Mode1/2***********/
// 设置串口流模式
BOOL    WINAPI  CH347I2C_Set(ULONG          iIndex,  // 指定设备序号
                             ULONG          iMode );  // 指定模式,见下行
// 位1-位0: I2C接口速度/SCL频率, 00=低速/20KHz,01=标准/100KHz(默认值),10=快速/400KHz,11=高速/750KHz
// 其它保留,必须为0

// 设置硬件异步延时,调用后很快返回,而在下一个流操作之前延时指定毫秒数
BOOL    WINAPI  CH347I2C_SetDelaymS(ULONG           iIndex,       // 指定设备序号
                                    ULONG           iDelay ) ;    // 指定延时的毫秒数

 // 处理I2C数据流,2线接口,时钟线为SCL引脚,数据线为SDA引脚
BOOL    WINAPI  CH347StreamI2C( ULONG           iIndex,        // 指定设备序号
                                ULONG           iWriteLength,  // 准备写出的数据字节数
                                PVOID           iWriteBuffer,  // 指向一个缓冲区,放置准备写出的数据,首字节通常是I2C设备地址及读写方向位
                                ULONG           iReadLength,   // 准备读取的数据字节数
                                PVOID           oReadBuffer ); // 指向一个缓冲区,返回后是读入的数据
#ifndef _CH341_DLL_H
typedef enum    _EEPROM_TYPE {// EEPROM型号
    ID_24C01,
    ID_24C02,
    ID_24C04,
    ID_24C08,
    ID_24C16,
    ID_24C32,
    ID_24C64,
    ID_24C128,
    ID_24C256,
    ID_24C512,
    ID_24C1024,
    ID_24C2048,
    ID_24C4096
} EEPROM_TYPE;
#endif

// 从EEPROM中读取数据块,速度约56K字节
BOOL    WINAPI  CH347ReadEEPROM(ULONG           iIndex,     // 指定设备序号
                                EEPROM_TYPE     iEepromID,  // 指定EEPROM型号
                                ULONG           iAddr,      // 指定数据单元的地址
                                ULONG           iLength,    // 准备读取的数据字节数
                                PUCHAR          oBuffer );  // 指向一个缓冲区,返回后是读入的数据
// 向EEPROM中写入数据块
BOOL    WINAPI  CH347WriteEEPROM(ULONG          iIndex,     // 指定设备序号
                                 EEPROM_TYPE    iEepromID,  // 指定EEPROM型号
                                 ULONG          iAddr,      // 指定数据单元的地址
                                 ULONG          iLength,    // 准备写出的数据字节数
                                 PUCHAR         iBuffer );  // 指向一个缓冲区,放置准备写出的数据

操作流程

代码示例

Windows例程

        可参考官方开发资料:CH347EVT.ZIP - 南京沁恒微电子股份有限公司 【目录:CH347EVT\EVT\TOOLS\CH347Demo】

        界面读写示例如下:

Linux例程

可参考如下代码,链接地址:CH341PAR_LINUX.ZIP - 南京沁恒微电子股份有限公司USB转JTAG/SPI/I2C/并口/GPIO等接口的Linux设备驱动程序,支持CH341的USB转SPI/I2C/EPP并口/MEM并口等,支持CH347的480Mbps高速USB转JTAG/SPI/I2C/GPIO等,支持32/64位操作系统。icon-default.png?t=N7T8https://www.wch.cn/downloads/CH341PAR_LINUX_ZIP.html

/*
 * ch347 application demo
 *
 * Copyright (C) 2023 Nanjing Qinheng Microelectronics Co., Ltd.
 * Web:      http://wch.cn
 * Author:   WCH <[email protected]>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * Cross-compile with cross-gcc -I /path/to/cross-kernel/include
 *
 * V1.0 - initial version
 * V1.1 - add operation for HID mode
 * V1.2 - add serial port operation in HID and TTY mode
 * V1.3 - update with new library
 * V1.4 - add gpio interrupt funciton, update with new library,
 *      - support more SPI and I2C stretching
 *      - support I2C clock stretch
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <stdint.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <time.h>
#include <endian.h>
#include <linux/hidraw.h>

#include "ch347_lib.h"

#define CMD_FLASH_SECTOR_ERASE 0x20
#define CMD_FLASH_BYTE_PROG    0x02
#define CMD_FLASH_READ	       0x03
#define CMD_FLASH_RDSR	       0x05
#define CMD_FLASH_WREN	       0x06
#define CMD_FLASH_JEDEC_ID     0x9F

#define SPI_FLASH_PerWritePageSize 256

typedef enum _CH347FUNCTYPE {
	FUNC_UART = 0,
	FUNC_SPI_I2C_GPIO,
	FUNC_JTAG_GPIO,
	FUNC_SPI_I2C_JTAG_GPIO,
} CH347FUNCTYPE;

struct ch34x {
	int fd;
	char version[100];
	CHIP_TYPE chiptype;
	uint32_t dev_id;
	CH347FUNCTYPE functype;
};

static struct ch34x ch347device;

bool CH347_SPI_Init()
{
	bool ret;
	mSpiCfgS SpiCfg = { 0 };

	/* set spi interface in [mode3] & [15MHz] & [MSB] & output [0xFF] by default */
	SpiCfg.iMode = 3;
	SpiCfg.iSpiSpeedHz = 30e6;
	SpiCfg.iByteOrder = 1;
	SpiCfg.iSpiOutDefaultData = 0xFF;
	SpiCfg.iChipSelect = 0x80;

	/* init spi interface */
	ret = CH347SPI_Init(ch347device.fd, &SpiCfg);
	if (!ret) {
		printf("Failed to init SPI interface.\n");
		return false;
	}

	return true;
}

bool CH347_I2C_Init()
{
	bool ret;
	int iMode;

	/* set i2c interface in 750KHZ */
	iMode = 0x03;

	/* init i2c interface */
	ret = CH347I2C_Set(ch347device.fd, iMode);
	if (!ret) {
		printf("Failed to init I2C interface.\n");
		return false;
	}

	return true;
}

bool Flash_ID_Read()
{
	int iChipSelect;
	int iLength;
	uint8_t ioBuffer[4] = { 0 };
	uint32_t Flash_ID;

	iChipSelect = 0x80;
	iLength = 4;
	ioBuffer[0] = CMD_FLASH_JEDEC_ID;
	memset(ioBuffer + 1, 0xFF, 3);

	if (CH347SPI_WriteRead(ch347device.fd, false, iChipSelect, iLength, ioBuffer) == false)
		return false;
	else {
		ioBuffer[0] = 0x00;
		memcpy(&Flash_ID, ioBuffer, 4);
	}
	Flash_ID = htole32(Flash_ID);
	printf("Read flash ID: 0x%x.\n", Flash_ID);

	if (Flash_ID == 0x000000 || Flash_ID == 0xffffff00) {
		printf("Read flash ID error.\n");
		return false;
	}

	return true;
}

unsigned int Flash_Block_Read(unsigned int address, uint8_t *pbuf, unsigned int len)
{
	int iChipSelect;
	int iLength;
	int oLength;
	uint8_t ioBuffer[8192] = { 0 };

	iChipSelect = 0x80;
	iLength = 0x04;
	oLength = len;
	ioBuffer[0] = CMD_FLASH_READ;
	ioBuffer[1] = (uint8_t)(address >> 16);
	ioBuffer[2] = (uint8_t)(address >> 8);
	ioBuffer[3] = (uint8_t)(address);

	if (!CH347SPI_Read(ch347device.fd, false, iChipSelect, iLength, &oLength, ioBuffer)) {
		printf("Flash_Block_Read read %d bytes failed.\n", len);
		return 0;
	} else
		memcpy(pbuf, ioBuffer, oLength);

	return oLength;
}

bool Flash_Block_Read_Test()
{
	double UseT;
	uint32_t DataLen, FlashAddr, i;
	uint8_t ioBuffer[8192] = { 0 };
	char FmtStr1[8 * 1024 * 3 + 16] = "";

	static struct timeval t1, t2;
	int delta_sec, delta_usec;

	FlashAddr = 0x00;
	DataLen = 0x500;

	gettimeofday(&t1, NULL);
	DataLen = Flash_Block_Read(FlashAddr, ioBuffer, DataLen);
	if (DataLen <= 0) {
		printf("\tFlash Read: Addr[0x%x] read %d bytes failed.\n", FlashAddr, DataLen);
		return false;
	}
	gettimeofday(&t2, NULL);

	delta_sec = t2.tv_sec - t1.tv_sec;
	delta_usec = t2.tv_usec - t1.tv_usec;
	UseT = (float)delta_sec + (float)delta_usec / 1000000;

	printf("\tFlash Read: Addr[0x%x] read %d bytes in %.3f seconds.\n", FlashAddr, DataLen, UseT);
	for (i = 0; i < DataLen; i++)
		sprintf(&FmtStr1[strlen(FmtStr1)], "%02x ", ioBuffer[i]);
	printf("\nRead: \n%s\n\n", FmtStr1);

	return true;
}

bool Flash_Write_Enable()
{
	int iChipSelect;
	int iLength;
	uint8_t ioBuffer;

	iChipSelect = 0x80;
	iLength = 1;
	ioBuffer = CMD_FLASH_WREN;

	return CH347SPI_WriteRead(ch347device.fd, false, iChipSelect, iLength, &ioBuffer);
}

bool Flash_Wait()
{
	int iChipSelect;
	int iLength;
	uint8_t ioBuffer[2];
	uint8_t status;
	int retry_times = 1000;

	iChipSelect = 0x80;
	iLength = 2;

	do {
		ioBuffer[0] = CMD_FLASH_RDSR;
		if (CH347SPI_WriteRead(ch347device.fd, false, iChipSelect, iLength, ioBuffer) == false)
			return false;
		status = ioBuffer[1];
		usleep(100);
	} while ((status & 0x01) && (retry_times--));

	if ((status & 0x01) == 0)
		return true;
	else
		return false;
}

bool Flash_Sector_Erase(uint32_t StartAddr)
{
	int iChipSelect;
	int iLength;
	uint8_t ioBuffer[4];

	if (Flash_Write_Enable() == false)
		return false;

	iChipSelect = 0x80;
	iLength = 4;
	ioBuffer[0] = CMD_FLASH_SECTOR_ERASE;
	ioBuffer[1] = (uint8_t)(StartAddr >> 16 & 0xff);
	ioBuffer[2] = (uint8_t)(StartAddr >> 8 & 0xf0);
	ioBuffer[3] = 0x00;

	if (CH347SPI_WriteRead(ch347device.fd, false, iChipSelect, iLength, ioBuffer) == false)
		return false;

	if (Flash_Wait() == false)
		return false;

	return true;
}

bool W25X_Flash_Write_Page(uint8_t *pBuf, uint32_t address, uint32_t len)
{
	int iChipSelect;
	int iLength;
	uint8_t ioBuffer[8192];

	if (!Flash_Write_Enable())
		return false;

	iChipSelect = 0x80;
	iLength = len + 4;
	ioBuffer[0] = CMD_FLASH_BYTE_PROG;
	ioBuffer[1] = (uint8_t)(address >> 16);
	ioBuffer[2] = (uint8_t)(address >> 8);
	ioBuffer[3] = (uint8_t)address;
	memcpy(&ioBuffer[4], pBuf, len);

	if (CH347SPI_Write(ch347device.fd, false, iChipSelect, iLength, SPI_FLASH_PerWritePageSize + 4, ioBuffer) ==
	    false)
		return false;

	memset(ioBuffer, 0, sizeof(uint8_t) * len);

	if (!Flash_Wait())
		return false;

	return true;
}

bool Flash_Block_Write()
{
	int ret;
	int i = 0;
	uint32_t DataLen, FlashAddr, BeginAddr, NumOfPage, NumOfSingle;
	uint8_t ioBuffer[0x500] = { 0 };
	uint8_t *pbuf = ioBuffer;
	double UseT;

	static struct timeval t1, t2;
	int delta_sec, delta_usec;

	/* write flash from addr 0 */
	FlashAddr = 0x00;
	BeginAddr = FlashAddr;
	DataLen = 0x500;

	for (i = 0; i < DataLen; i++)
		ioBuffer[i] = i;

	NumOfPage = DataLen / SPI_FLASH_PerWritePageSize;
	NumOfSingle = DataLen % SPI_FLASH_PerWritePageSize;

	/* caculate flash write time */
	gettimeofday(&t1, NULL);
	while (NumOfPage--) {
		ret = W25X_Flash_Write_Page(pbuf, FlashAddr, SPI_FLASH_PerWritePageSize);
		if (ret == false)
			goto exit;
		pbuf += SPI_FLASH_PerWritePageSize;
		FlashAddr += SPI_FLASH_PerWritePageSize;
	}
	if (NumOfSingle) {
		ret = W25X_Flash_Write_Page(pbuf, FlashAddr, NumOfSingle);
		if (ret == false)
			goto exit;
	}
	gettimeofday(&t2, NULL);

	delta_sec = t2.tv_sec - t1.tv_sec;
	delta_usec = t2.tv_usec - t1.tv_usec;
	UseT = ((float)delta_sec + (float)delta_usec / 1000000);

	printf("\tFlash Write: Addr[0x%x] write %d bytes in %.3f seconds.\n", BeginAddr, DataLen, UseT / 1000);

	return true;

exit:
	printf("\tFlash Write: Addr [0x%x] write %d bytes failed.\n", BeginAddr, DataLen);
	return false;
}

bool EEPROM_Read()
{
	bool ret = false;
	EEPROM_TYPE eeprom;
	int iAddr;
	int iLength;
	int i;
	uint8_t oBuffer[256] = { 0 };

	eeprom = ID_24C02;
	iAddr = 0;
	iLength = 256;

	ret = CH347ReadEEPROM(ch347device.fd, eeprom, 0, iLength, oBuffer);
	if (ret == false)
		goto exit;

	printf("\nRead EEPROM data:\n");
	for (i = 0; i < iLength; i++) {
		printf("%02x ", oBuffer[i]);
		if (((i + 1) % 10) == 0)
			putchar(10);
	}
	putchar(10);

exit:
	return ret;
}

bool EEPROM_Write()
{
	bool ret = false;
	EEPROM_TYPE eeprom;
	int iAddr;
	int iLength;
	int i;
	uint8_t iBuffer[256] = { 0 };

	eeprom = ID_24C02;
	iAddr = 0;
	iLength = 256;
	for (i = 0; i < 256; i++)
		iBuffer[i] = i;

	printf("\nWrite EEPROM data:\n");
	ret = CH347WriteEEPROM(ch347device.fd, eeprom, iAddr, iLength, iBuffer);
	if (ret == false)
		goto exit;

	for (i = 0; i < iLength; i++) {
		printf("%02x ", iBuffer[i]);
		if (((i + 1) % 10) == 0)
			putchar(10);
	}
	putchar(10);

exit:
	return ret;
}

void ch34x_demo_flash_operate()
{
	bool ret = false;

	ret = CH347_SPI_Init();
	if (ret == false) {
		printf("Failed to init CH347 SPI interface.\n");
		return;
	}
	printf("CH347 SPI interface init succeed.\n");

	/* read flash ID */
	ret = Flash_ID_Read();
	if (!ret) {
		printf("Failed to read flash ID.\n");
		return;
	}

	/* read flash block data */
	ret = Flash_Block_Read_Test();
	if (!ret) {
		printf("Failed to read flash.\n");
		return;
	}

	/* erase flash sector data */
	ret = Flash_Sector_Erase(0x00);
	if (!ret) {
		printf("Failed to erase flash.\n");
		return;
	}
	printf("Erase one sector from Addr[0x%x] of flash succeed.\n", 0x00);

	/* write flash block data */
	ret = Flash_Block_Write();
	if (!ret) {
		printf("Failed to write flash.\n");
		return;
	}

	/* read flash block data */
	ret = Flash_Block_Read_Test();
	if (!ret) {
		printf("Failed to read flash.\n");
		return;
	}
}

void ch34x_demo_eeprom_operate()
{
	bool ret = false;

	ret = CH347_I2C_Init();
	if (!ret) {
		printf("Failed to init CH347 I2C.\n");
		return;
	}
	printf("CH347 I2C interface init succeed.\n");

	ret = EEPROM_Read();
	if (!ret) {
		printf("Failed to read eeprom.\n");
		return;
	}

	ret = EEPROM_Write();
	if (!ret) {
		printf("Failed to write eeprom.\n");
		return;
	}

	ret = EEPROM_Read();
	if (!ret) {
		printf("Failed to read eeprom.\n");
		return;
	}
}

void ch34x_demo_jtag_operate()
{
	int i;
	int oReadLength;
	uint8_t retue[32] = { 0 };
	uint8_t IDCODE[4096] = { 0 };

	oReadLength = 32;
	/* init jtag tck clock */
	CH347Jtag_INIT(ch347device.fd, 4);
	/* reset target jtag device */
	CH347Jtag_SwitchTapState(ch347device.fd, 0);
	/* SHIFT-DR Read the Target IDCODE */
	CH347Jtag_ByteReadDR(ch347device.fd, &oReadLength, &retue);

	printf("Target IDCODE: \n");
	for (i = 0; i < 4; i++) {
		printf("0x%2x", retue[3 - i]);
	}
	puts("");

	return;
}

bool CH347_SPI_Slave_Init()
{
	bool ret;
	mSpiCfgS SpiCfg = { 0 };

	/* set spi interface in spi slave mode, [mode3] & [MSB] & output [0xFF] by default */
	SpiCfg.iByteOrder = 1;
	SpiCfg.iSpiOutDefaultData = 0xFF;
	SpiCfg.iMode = 0x83;

	/* init spi interface */
	ret = CH347SPI_Init(ch347device.fd, &SpiCfg);
	if (!ret) {
		printf("Failed to init SPI interface.\n");
		return false;
	} else {
		printf("SPI init slave ok.\n");
	}

	return true;
}

void ch34x_demo_spi_slave_operate(bool enable)
{
	bool ret = false;
	uint8_t oBuffer[SPI_SLAVE_MAX_LENGTH];
	uint32_t oLength;
	int i;

	if (enable) {
		ret = CH347_SPI_Slave_Init();
		if (ret == false) {
			printf("Failed to init CH347 SPI interface.\n");
			return;
		}
		printf("CH347 SPI interface init succeed.\n");
		ret = CH347SPI_Slave_Control(ch347device.fd, true);
		if (!ret)
			return;
		printf("Begin read data in slave mode.\n");
		while (1) {
			ret = CH347SPI_Slave_QweryData(ch347device.fd, &oLength);
			if (!ret) {
				printf("CH347SPI_Slave_QweryData failed.\n");
				goto exit;
			}
			if (oLength == 0) {
				usleep(10 * 1000);
				continue;
			}
			ret = CH347SPI_Slave_ReadData(ch347device.fd, oBuffer, &oLength);
			if (!ret) {
				printf("CH347SPI_Slave_ReadData failed.\n");
				goto exit;
			}
			printf("\nRead Slave data, len: %d, contents:\n", oLength);
			for (i = 0; i < oLength; i++) {
				printf("%02x ", oBuffer[i]);
				if (((i + 1) % 20) == 0)
					putchar(20);
			}
			putchar(20);
		}
	} else
		ret = CH347SPI_Slave_Control(ch347device.fd, false);

	return;

exit:
	CH347SPI_Slave_Control(ch347device.fd, false);
}

static void ch34x_demo_gpio_input_operate()
{
	bool ret;
	int i, j;
	int gpiocount = 8;
	uint8_t iDir = 0xff;
	uint8_t iData = 0x00;

	ret = CH347GPIO_Get(ch347device.fd, &iDir, &iData);
	if (ret == false) {
		printf("CH347GPIO_Set error.\n");
		return;
	}

	printf("\n********** GPIO Input Start **********\n\n");
	for (i = 0; i < gpiocount; i++) {
		if ((iData & (1 << i)))
			printf("H");
		else
			printf("L");
	}
	printf("\n");
	printf("\n********** GPIO Input End **********\n\n");
}

static void ch34x_demo_gpio_output_operate()
{
	bool ret;
	int i, j;
	int gpiocount = 8;
	uint8_t iEnable = 0xff;
	uint8_t iSetDirOut = 0xff;
	uint8_t iSetDataOut = 0x00;

	/* analog leds here */
	CH347GPIO_Set(ch347device.fd, iEnable, iSetDirOut, iSetDataOut);

	printf("\n********** GPIO Output Start **********\n");
	for (i = 0; i < gpiocount; i++) {
		iSetDataOut = 1 << i;
		ret = CH347GPIO_Set(ch347device.fd, iEnable, iSetDirOut, iSetDataOut);
		if (ret == false) {
			printf("CH347GPIO_Set error.\n");
			return;
		}
		printf("\n");
		for (j = 0; j < gpiocount; j++) {
			if (j == i)
				printf("H");
			else
				printf("L");
		}
		printf("\n");
		usleep(200 * 1000);
	}
	iSetDataOut = 0x00;
	CH347GPIO_Set(ch347device.fd, iEnable, iSetDirOut, iSetDataOut);
	printf("\n********** GPIO Output End **********\n\n");
}

static void ch34x_demo_isr_handler(int signo)
{
	static int int_times = 0;

	printf("ch34x interrupt times: %d\n", int_times++);
}

static void ch34x_demo_irq_operate(bool enable)
{
	bool ret;
	int gpioindex = 6;

	ret = CH347GPIO_IRQ_Set(ch347device.fd, gpioindex, enable, IRQ_TYPE_EDGE_BOTH, ch34x_demo_isr_handler);
	if (!ret) {
		printf("Failed to set CH347 irq function.");
		return;
	}
}

void ch34x_demo_uart_operate()
{
	bool ret = false;
	uint8_t iBuffer[256];
	uint8_t oBuffer[256];
	uint32_t ioLength;
	int i;

	ioLength = 256;
	for (i = 0; i < 256; i++)
		iBuffer[i] = i;

	ret = CH347Uart_Init(ch347device.fd, 115200, 3, 0, 0, 1);
	if (!ret) {
		printf("Failed to init CH347 UART interface.\n");
		return;
	}
	printf("CH347 UART interface init succeed.\n");

	ret = CH347Uart_Write(ch347device.fd, iBuffer, &ioLength);
	if (ret == false) {
		printf("CH347Uart_Write failed.\n");
		return;
	}
	printf("Uart wrote %d bytes already.\n", ioLength);

	ret = CH347Uart_Read(ch347device.fd, oBuffer, &ioLength);
	if (ret == false) {
		printf("CH347Uart_Read failed.\n");
		return;
	}

	printf("\nRead Uart data:\n");
	for (i = 0; i < ioLength; i++) {
		printf("%02x ", oBuffer[i]);
		if (((i + 1) % 10) == 0)
			putchar(10);
	}
	putchar(10);
}

bool Show_DevMsg(char *pathname)
{
	unsigned char buf[256];
	int ret;
	int i;
	struct hidraw_devinfo info;
	uint16_t vendor, product;
	CHIP_TYPE chiptype;

	if (strstr(pathname, "tty")) {
		printf("Device operating has function [UART].\n");
		ch347device.functype = FUNC_UART;
	} else if (strstr(pathname, "hidraw")) {
		/* Get Raw Name */
		ret = ioctl(ch347device.fd, HIDIOCGRAWNAME(256), buf);
		if (ret < 0) {
			perror("HIDIOCGRAWNAME");
			goto exit;
		} else
			printf("Raw Name: %s\n", buf);

		/* Get Raw Info */
		ret = ioctl(ch347device.fd, HIDIOCGRAWINFO, &info);
		if (ret < 0) {
			perror("HIDIOCGRAWINFO");
			goto exit;
		} else {
			printf("Raw Info:\n");
			printf("\tvendor: 0x%04hx\n", info.vendor);
			printf("\tproduct: 0x%04hx\n", info.product);
		}

		if (info.vendor == 0x1a86) {
			if (info.product == 0x55dc)
				ch347device.chiptype = CHIP_CH347T;
			else if (info.product == 0x55e5)
				ch347device.chiptype = CHIP_CH347F;
			else {
				printf("Current HID device PID is not CH347.\n");
				return -1;
			}
		} else {
			printf("Current HID device VID is not CH347.\n");
			return -1;
		}

		/* Get Physical Location */
		ret = ioctl(ch347device.fd, HIDIOCGRAWPHYS(256), buf);
		if (ret < 0) {
			perror("HIDIOCGRAWPHYS");
			goto exit;
		} else
			printf("Raw Phys: %s\n", buf);

		if (ch347device.chiptype == CHIP_CH347T) {
			if (strstr(buf, "input0")) {
				ch347device.functype = FUNC_UART;
				printf("Device operating has function [UART].\n");
			} else {
				ch347device.functype = FUNC_SPI_I2C_GPIO;
				printf("Device operating has function [SPI+I2C+GPIO].\n");
			}
		} else {
			if (strstr(buf, "input0")) {
				ch347device.functype = FUNC_UART;
				printf("Device operating has function [UART].\n");
			} else if (strstr(buf, "input2")) {
				ch347device.functype = FUNC_UART;
				printf("Device operating has function [UART].\n");
			} else {
				ch347device.functype = FUNC_SPI_I2C_GPIO;
				printf("Device operating has function [SPI+I2C+JTAG+GPIO].\n");
			}
		}
	} else if (strstr(pathname, "ch34x_pis")) {
		/* Get Driver Version */
		ret = CH34x_GetDriverVersion(ch347device.fd, ch347device.version);
		if (ret == false) {
			printf("CH34x_GetDriverVersion error.\n");
			goto exit;
		}
		printf("Driver version: %s\n", ch347device.version);

		/* Get Chip Type */
		ret = CH34x_GetChipType(ch347device.fd, &ch347device.chiptype);
		if (ret == false) {
			printf("CH34x_GetChipType error.\n");
			goto exit;
		}
		if (ch347device.chiptype == CHIP_CH341) {
			printf("Current chip operating is CH341, please use ch341_demo.\n");
			goto exit;
		}

		/* Get Device ID */
		ret = CH34X_GetDeviceID(ch347device.fd, &ch347device.dev_id);
		if (ret == false) {
			printf("CH34X_GetDeviceID error.\n");
			goto exit;
		}
		vendor = ch347device.dev_id;
		product = ch347device.dev_id >> 16;
		printf("Vendor ID: 0x%4x, Product ID: 0x%4x\n", vendor, product);
		if (product == 0x55db) {
			ch347device.functype = FUNC_SPI_I2C_GPIO;
			printf("Device operating has function [SPI+I2C+GPIO].\n");
		} else if (product == 0x55dd) {
			ch347device.functype = FUNC_JTAG_GPIO;
			printf("Device operating has function [JTAG+GPIO].\n");
		} else if (product == 0x55de) {
			ch347device.functype = FUNC_SPI_I2C_JTAG_GPIO;
			printf("Device operating has function [SPI+I2C+JTAG+GPIO].\n");
		}
	}
	return true;

exit:
	return false;
}

int main(int argc, char *argv[])
{
	bool ret;
	char choice, ch;

	if (argc != 2) {
		printf("Usage: sudo %s [device]\n", argv[0]);
		return -1;
	}

	/* open device */
	ch347device.fd = CH347OpenDevice(argv[1]);
	if (ch347device.fd < 0) {
		printf("CH347OpenDevice false.\n");
		return -1;
	}
	printf("Open device %s succeed, fd: %d\n", argv[1], ch347device.fd);

	ret = Show_DevMsg(argv[1]);
	if (ret == false)
		return -1;
	sleep(1);

	if (strstr(argv[1], "ch34x_pis")) {
		ret = CH34xSetTimeout(ch347device.fd, 2000, 2000);
		if (ret == false) {
			printf("CH34xSetTimeout false.\n");
			return -1;
		}
	}

	switch (ch347device.functype) {
	case FUNC_UART:
		while (1) {
			printf("\npress u to operate uart, q to quit.\n");
			scanf("%c", &choice);
			while ((ch = getchar()) != EOF && ch != '\n')
				;
			if (choice == 'q')
				break;
			switch (choice) {
			case 'u':
				ch34x_demo_uart_operate();
				break;
			default:
				break;
			}
		}
		break;
	case FUNC_SPI_I2C_GPIO:
	case FUNC_SPI_I2C_JTAG_GPIO:
		while (1) {
			printf("\npress f to operate spi flash, e to operate eeprom,\n"
			       "a to get gpio status, g to gpio output test, j to operate jtag interface,\n"
			       "s to enable spi slave mode, o to disable spi slave mode,\n"
			       "i to enable interrupt, d to disable interrupt, q to quit.\n");

			scanf("%c", &choice);
			while ((ch = getchar()) != EOF && ch != '\n')
				;
			if (choice == 'q')
				break;

			switch (choice) {
			case 'f':
				printf("FLASH Test begin.\n");
				ch34x_demo_flash_operate();
				break;
			case 'e':
				printf("EEPROM Test begin.\n");
				ch34x_demo_eeprom_operate();
				break;
			case 'a':
				printf("GPIO Input Test Begin.\n");
				ch34x_demo_gpio_input_operate();
				break;
			case 'g':
				printf("GPIO Output Test Begin.\n");
				ch34x_demo_gpio_output_operate();
				break;
			case 'i':
				printf("IRQ Test Begin.");
				ch34x_demo_irq_operate(true);
				break;
			case 'd':
				printf("IRQ Test Over.\n");
				ch34x_demo_irq_operate(false);
				break;
			case 'j':
				if (ch347device.functype == FUNC_SPI_I2C_JTAG_GPIO) {
					printf("JTAG Test begin.\n");
					ch34x_demo_jtag_operate();
				} else {
					printf("Chip is not CH347F.\n");
				}
				break;
			case 's':
				if (ch347device.chiptype == CHIP_CH347F) {
					printf("SPI Slave Test Begin.\n");
					ch34x_demo_spi_slave_operate(true);
				} else {
					printf("Chip is not CH347F.\n");
				}
				break;
			case 'o':
				if (ch347device.chiptype == CHIP_CH347F) {
					printf("SPI Slave Test Over.\n");
					ch34x_demo_spi_slave_operate(false);
				} else {
					printf("Chip is not CH347F.\n");
				}
				break;
			default:
				printf("Bad choice, please input again.\n");
				break;
			}
		}
		break;
	case FUNC_JTAG_GPIO:
		while (1) {
			printf("\npress j to operate jtag interface, a to get gpio status,\n"
			       "g to gpio output test q to quit.\n");

			scanf("%c", &choice);
			while ((ch = getchar()) != EOF && ch != '\n')
				;
			if (choice == 'q')
				break;

			switch (choice) {
			case 'j':
				printf("JTAG Test begin.\n");
				ch34x_demo_jtag_operate();
				break;
			case 'a':
				printf("GPIO Input Test Begin.\n");
				ch34x_demo_gpio_input_operate();
				break;
			case 'g':
				printf("GPIO Test begin.\n");
				ch34x_demo_gpio_output_operate();
			default:
				printf("Bad choice, please input again.\n");
				break;
			}
		}
		break;
	default:
		break;
	}

	/* close the device */
	if (CH347CloseDevice(ch347device.fd)) {
		printf("Close device succeed.\n");
	}

	return 0;
}

执行截图:

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/WCH_TechGroup/article/details/127519259

智能推荐

DDR3基本概念1 - 存储单元结构和原理_sense amplifier-程序员宅基地

文章浏览阅读1.8k次。一个基本存储单元结构图如下图, storage capacitor为一个基本存储单元,当access transitor被选通时,可读可写: 一个4行3列的DDR 存储器如下图所示: 上图中蓝色的为bit line。相邻行的对应bit的bit line之间有一个两个反相器首尾相连的sense amplifier。红色的为word line,连接了同一行的所有的存储电容的transistor的栅极。 DDR..._sense amplifier

著名游戏"植物大战僵尸",从0到1亿美元 ---- PopCap创始人自述_com.popcap.samguo-程序员宅基地

文章浏览阅读844次。PopCap是哪家公司我就不介绍了,如果没有玩过他们的游戏,可以说你没有玩儿过PC游戏 8 )比较震惊的是他所说的最后一句话:“我们始终有一个信念,那就是一定要做出顶尖游戏,那样才能赚到大钱,如果一个游戏只是勉强可玩,那就一分钱也赚不到。” 可以类推到我们所做的其他产品。记得小时候,我有个舅爷,正月卖灯笼,7几年的时候,那种用纸扎的小羊灯笼,底下四个轮子,我们一帮小屁孩拉着可以到处跑的灯笼。但我舅爷的小羊轮子不能转,他说没事,别人卖1块5,我卖1块就可以了。结果。_com.popcap.samguo

spring boot 异常处理_serverproperties getservletprefix() springboot 2.0-程序员宅基地

文章浏览阅读4.9k次。spring boot在异常的处理中,默认实现了一个EmbeddedServletContainerCustomizer并定义了一个错误页面到”/error”中,在ErrorMvcAutoConfiguration源码中可以看到/** * {@link EmbeddedServletContainerCustomizer} that configures the container's error_serverproperties getservletprefix() springboot 2.0

jQuery实现页面滑动时导航背景色切换_jquery仿导航栏向下滑动导航栏变色-程序员宅基地

文章浏览阅读774次,点赞2次,收藏2次。<script type="text/javascript" src="./files-m/jquery-1.9.1.min.js"></script><script >//网页加载时运行$(function(){ navHeader(); $(window).scroll(function () { navHeader(); }) function navHeader() { if ($(windo_jquery仿导航栏向下滑动导航栏变色

错用mybatis映射文件#和$导致的问题_mybatis #{list.size} 预编译失败-程序员宅基地

文章浏览阅读254次。如下Mapper.xml文件:<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" ><mapper namespace="c..._mybatis #{list.size} 预编译失败

rust-lldb调试Rust程序-程序员宅基地

文章浏览阅读1.2k次。rust-lldb如何调试带有选项的程序# rust-lldb -- target/debug/racon --root /opt/run/racon run 2333 --bundle /opt/mycontainer/很简单只需要在rust-lldb与程序之间添加 --_lldb调试rust

随便推点

Visual Studio各版本区别-程序员宅基地

文章浏览阅读119次。Visual Studio 是微软公司推出的开发环境,Visual Studio 可以用来创建 Windows 平台下的 Windows 应用程序和网络应用程序,也可以用来创建网络服务、智能设备应用程序和 Office 插件。V.S.2012  .1、VS2012和VS2010相比,最大的新特性莫过于对Windows 8 Metro开发的支持。Metro天生为云+端而生,简洁、数..._visual studio 各个版本的区别

列表排序-程序员宅基地

文章浏览阅读41次。原理:  1、用rows方法获取到所有的行并放进Array里,不能用for..in的方式获取,因为for..in会获取所有rows方法相关的参数。  2、用sort方法或其他排序方法Function参数进行排序。  3、创建Fragment元素,append方法会将所有原table内容转移至Fragment元素内。  4、table元素加载Fragment。..._"排序\">"

【论文摘要】基于多数投票模式和超混沌加密的彩色图像鲁棒安全零水印算法_多数投票机制 水印-程序员宅基地

文章浏览阅读1.8k次。Robust and secure zero-watermarking algorithm for color images based on majority voting pattern and hyper-chaotic encryption标题:基于多数投票模式和超混沌加密的彩色图像鲁棒安全零水印算法作者:Xiao-bing Kang,Guang-feng Lin,Ya-jun Chen,Fan Zhao,Er-hu Zhang,Cui-ning Jing发布年份:2019摘要:鲁棒零水_多数投票机制 水印

Windows下用anaconda装python,添加jupyter的kernel以及装包_jupyter kernel没有安装包但可以用-程序员宅基地

文章浏览阅读5.1k次。环境:Windows 7 主要问题: 1. 先在anaconda官网上下载了python3+的版本,其他人安装时一般先安装python2再安装python3,没有相关经验可供参考;2. 安装anaconda时选了非默认路径(C盘某处),安装在D盘下,为后来的cmd调用命令造成了困难,无法用cd更换路径解决;3. 安装完python2的jupyter notebook的kernel时_jupyter kernel没有安装包但可以用

即将上线主网的Telegram区块链和Libra,谁的野心更大?-程序员宅基地

文章浏览阅读391次。两者在穿越两年之后十分戏剧化地 “会面” 了。文 | 芦荟 运营 | 盖遥编辑| 卢晓明出品|Odaily星球日报(ID:o-daily)继 Facebook..._fiftusdt

C++基础三_设有一组结点,其权值w={1,4,9,16,25,36,49,64,81,100},画出由这些结点所-程序员宅基地

文章浏览阅读189次。C++基础学习三对象的构造和析构构造函数和析构函数构造函数语法:析构函数语法:构造函数的分类及调用匿名拷贝构造函数(错误)拷贝构造函数的调用时机构造函数调用规则深拷贝和浅拷贝浅拷贝深拷贝多个对象构造和析构初始化列表类对象作为成员explicit关键字动态对象创建对象创建C动态分配内存(隐患)newdelete用于数组的new和delete使用new和delete采用相同形式delete void*..._设有一组结点,其权值w={1,4,9,16,25,36,49,64,81,100},画出由这些结点所构成