USB转SPI芯片简介
高速USB转接芯片CH347是一款集成480Mbps高速USB接口、JTAG接口、SPI接口、I2C接口、异步UART串口、GPIO接口等多种硬件接口的转换芯片。
接口示意图:
CH347-SPI接口特点
- CH347-SPI接口特点
- USB传输采用USB2.0高速(480Mbps)
- 工作在 Host/Master主机模式;
- 内置硬件DMA,支持批量数据的快速发送和读取;
- 支持SPI模式0/1/2/3,支持传输频率配置,传输频率可达60MHz;
- 硬件信号:SCS0、SCS1、SCK、MISO和MOSI;
- 传输位序:MSB/LSB;
- 数据结构:8位/16位传输;
- 提供计算机端驱动程序和USB转SPI函数库,支持二次开发;
使用芯片准备工作
选择CH347工作模式
CH347芯片在复位时,会根据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可使用SPI的模式有两种,其区别在Mode1需要安装厂商驱动,Mode3可以使用系统内置HID驱动无需额外安装,只需在编程时调用CH347动态库进行软件编程即可,此处我们使用Mode1来进行操作。
驱动安装
windows驱动安装
从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驱动安装
联系WCH技术支持获取到CH347-Linux驱动,然后进行安装
1、执行make编译驱动;
2、执行make load动态加载驱动,或执行make install后可实现重新启动自动检测硬件并加载驱动;
3、插入设备可查看到生成前缀为ch34x_pis的设备节点。
使用USB操作FLASH
本次操作CH347开发板板载FLASH:W25Q16JVSSIQ。
除此之外,CH347也可操作常见AT25/26、GD25等FLASH
调用函数
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则取消事件通知,否则在检测到事件时调用该程序
/***************SPI接口函数通用于Mode1/2********************/
// SPI控制器初始化
BOOL WINAPI CH347SPI_Init(ULONG iIndex,mSpiCfgS *SpiCfg);
//获取SPI控制器配置信息
BOOL WINAPI CH347SPI_GetCfg(ULONG iIndex,mSpiCfgS *SpiCfg);
//设置片选状态,使用前需先调用CH347SPI_Init对CS进行设置
BOOL WINAPI CH347SPI_ChangeCS(ULONG iIndex, // 指定设备序号
UCHAR iStatus); // 0=撤消片选,1=设置片选
//设置SPI片选
BOOL WINAPI CH347SPI_SetChipSelect(ULONG iIndex, // 指定设备序号
USHORT iEnableSelect, // 低八位为CS1,高八位为CS2; 字节值为1=设置CS,为0=忽略此CS设置
USHORT iChipSelect, // 低八位为CS1,高八位为CS2;片选输出,0=撤消片选,1=设置片选
ULONG iIsAutoDeativeCS, // 低16位为CS1,高16位为CS2;操作完成后是否自动撤消片选
ULONG iActiveDelay, // 低16位为CS1,高16位为CS2;设置片选后执行读写操作的延时时间,单位us
ULONG iDelayDeactive); // 低16位为CS1,高16位为CS2;撤消片选后执行读写操作的延时时间,单位us
//SPI4写数据
BOOL WINAPI CH347SPI_Write(ULONG iIndex, // 指定设备序号
ULONG iChipSelect, // 片选控制, 位7为0则忽略片选控制, 位7为1进行片选操作
ULONG iLength, // 准备传输的数据字节数
ULONG iWriteStep, // 准备读取的单个块的长度
PVOID ioBuffer); // 指向一个缓冲区,放置准备从MOSI写出的数据
//SPI4读数据.无需先写数据,效率较CH347SPI_WriteRead高很多
BOOL WINAPI CH347SPI_Read(ULONG iIndex, // 指定设备序号
ULONG iChipSelect, // 片选控制, 位7为0则忽略片选控制, 位7为1进行片选操作
ULONG oLength, // 准备发出的字节数
PULONG iLength, // 准备读入的数据字节数
PVOID ioBuffer); // 指向一个缓冲区,放置准备从DOUT写出的数据,返回后是从DIN读入的数据
// 处理SPI数据流,4线接口
BOOL WINAPI CH347SPI_WriteRead(ULONG iIndex, // 指定设备序号
ULONG iChipSelect, // 片选控制, 位7为0则忽略片选控制, 位7为1则操作片选
ULONG iLength, // 准备传输的数据字节数
PVOID ioBuffer ); // 指向一个缓冲区,放置准备从DOUT写出的数据,返回后是从DIN读入的数据
// 处理SPI数据流,4线接口
BOOL WINAPI CH347StreamSPI4(ULONG iIndex, // 指定设备序号
ULONG iChipSelect, // 片选控制, 位7为0则忽略片选控制, 位7为1则参数有效
ULONG iLength, // 准备传输的数据字节数
PVOID ioBuffer ); // 指向一个缓冲区,放置准备从DOUT写出的数据,返回后是从DIN读入的数据
操作流程
代码示例
Windows例程
可参考官方开发资料:CH347EVT.ZIP - 南京沁恒微电子股份有限公司 【目录:CH347EVT\EVT\TOOLS\CH347Demo】
界面读写示例如下:
Linux例程
/*
* ch347 application demo
*
* Copyright (C) 2023 Nanjing Qinheng Microelectronics Co., Ltd.
* Web: http://wch.cn
* Author: WCH <tech@wch.cn>
*
* 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;
}
}
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_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;
default:
break;
}
/* close the device */
if (CH347CloseDevice(ch347device.fd)) {
printf("Close device succeed.\n");
}
return 0;
}
执行截图: