CH347介绍

CH347作为一款USB转接芯片,之前的博客中实现了他的SPI、JTAG、I2C的功能。

https://blog.csdn.net/qq_43010294/article/details/141712680

本次介绍其GPIO功能,我们可以使用沁官方的CH343PT库对WCH的U串系列进行GPIO控制,同时该库还有很多实用功能,例如对WCH的U串芯片的配置数据进行改写,串口热插拔检测等。
CH343PT库下载链接:https://www.wch.cn/downloads/CH343SER_ZIP.html
使用介绍可参考:https://blog.csdn.net/WCH_TechGroup/article/details/127514913

开源项目libmodbus

一个易用的Modbus开源库,目前Github 3.5K star,支持Modbus RTU、Modbus TCP与Modbus ascii。纯C编写,支持多平台。
项目地址:https://github.com/stephane/libmodbus

实现流程

利用modbus RTU的读写保持线圈功能(功能码:0x01)去控制CH347的GPIO电平。
借助CH347转双串口的功能,将一个串口作为modbus rtu协议的slave端,另一串口使用CH343PT库去控制GPIO。
master端每隔五秒读取slave端的两个保持线圈并将他们翻转,再写入。
slave端则读取GPIO电平并每五秒回复一次GPIO的状态。
代码如下:

#include "modbus.h"
#include <iostream>
#include <Windows.h>
#include <stdio.h>
#include <string>
#include "CH343PT.H"
using namespace std;
struct ModbusInfo
{
	modbus_t* mb;
	modbus_mapping_t* mbMap;
};
struct ModbusInfo MBInfo;
HANDLE hCom; //串口句柄
UCHAR ChipType;
ChipPropertyS ChipPro = { 0 };

//打开串口
BOOL OpenCom(string comname)
{
	std::string strComName;
	strComName += "\\\\.\\";
	strComName += comname;
	BOOL Retval;
	hCom = CreateFile(strComName.c_str(), GENERIC_READ | GENERIC_WRITE,
		0, NULL, OPEN_EXISTING,
		NULL,
		NULL);
	if (hCom == INVALID_HANDLE_VALUE)
	{
		return FALSE;
	}
	ChipType = CH343PT_GetChipProperty(hCom, &ChipPro); //获取串口芯片信息
	Retval = (ChipType != 0xFF);
	if (!Retval)
	{
		printf("芯片信息获取失败!");
		CloseHandle(hCom);
		hCom = INVALID_HANDLE_VALUE;
		return FALSE;
	}
	return TRUE;
}

BOOL OpenModbus(string comname)
{
	string com = string("\\\\.\\") + comname;
	MBInfo.mb = modbus_new_rtu(com.c_str(), 115200, 'N', 8, 1);
	if (MBInfo.mb == NULL) {
		modbus_free(MBInfo.mb);
		printf("new rtu failed: %s\n", modbus_strerror(errno));
		return FALSE;
	}
	//gpio:3~7
	MBInfo.mbMap = modbus_mapping_new_start_address(0, 2, 0, 0, 0, 0, 0, 0);
	if (MBInfo.mbMap == NULL)
	{
		modbus_free(MBInfo.mb);
		printf("new mapping failed: %s\n", modbus_strerror(errno));
		return FALSE;
	}
	modbus_set_slave(MBInfo.mb, 1);
	if (modbus_connect(MBInfo.mb) == -1) {
		printf("modbus_connect err.");
		return FALSE;
	}
	return TRUE;
}

int main(int argc, char* argv[])
{
	ULONG gpioEnable = ULONG_MAX;
	ULONG gpioStatus = 0;
	uint8_t query[50];
	if (argc < 3) {
		printf("Missing necessary input parameters\n");
		return -1;
	}
	if (!OpenModbus(argv[1])) {
		printf("打开ModBus失败!");
		return -1;
	}
	if (!OpenCom(argv[2])) {
		printf("打开串口失败!");
		return -1;
	}
	CH910x_GpioGet(hCom, &ChipPro, &gpioStatus);
	for (size_t i = 0; i < 2; i++) {
		MBInfo.mbMap->tab_bits[i] = (gpioStatus >> i) & 0x01 ? 1 : 0;
	}
	int ret = 0;
	std::cout << "Modbus slave running!\n";
	Sleep(100);
	while (1) {
		do {
			ret = modbus_receive(MBInfo.mb, query);    //轮询串口数据,
		} while (ret == 0);
		if (ret > 0) {
			printf("receive data\n");
			modbus_reply(MBInfo.mb, query, ret, MBInfo.mbMap);
		} else {
			printf("modbus_receive err.\n");
			break;
		}
		for (size_t i = 0; i < 2; i++) {
			if (MBInfo.mbMap->tab_bits[i]) {
				gpioStatus |= (0x01 << i);
			} else {
				gpioStatus &= ((ULONG_MAX - 1) << i);
			}
		}
		printf("gpioStatus = %x\n", gpioStatus);
		CH910x_GpioSet(hCom, &ChipPro, gpioEnable, gpioStatus);
	}
	modbus_close(MBInfo.mb);
	modbus_free(MBInfo.mb);
}

试验结果

这里的COM16是与CH347串口相连的CH9102。
在这里插入图片描述
通过逻辑分析仪可看到CH347的两个GPIO口每隔五秒发生了一次翻转:
在这里插入图片描述

引伸

1、如需进行更多的GPIO控制,可使用CH348(USB2.0转8串口)或CH348+CH334(USB2.0 4端口USB Hub)实现。
2、实现modbus RTU与modbus TCP网关,实现功能更加完善的远程GPIO控制。

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐