#虚拟串口模拟CH340#
后续代码更新放gitee 上
一、思路
1. 确定通信接口:CH340是一款USB转串口芯片,因此您需要选择STM32上的某个USB接口来实现USB通信。通常情况下,STM32系列芯片都有内置的USB接口,您可以根据您的具体型号选择合适的接口。
2. 实现USB功能:在STM32上启用USB功能,您需要在代码中初始化USB接口,并配置相关的参数,例如USB模式、中断等。您可以参考STM32官方提供的库函数和示例代码来实现USB功能。
3. 实现串口功能:虚拟串口的核心功能是数据的收发,您需要实现串口的初始化、配置和中断处理等。在STM32上,UART或USART模块通常用于串口通信。您可以根据需求选择合适的串口模块,并在代码中进行相应的配置。
4. 实现CH340协议:CH340芯片有自己的通信协议,您需要在STM32上实现类似的协议。根据CH340的功能,您需要处理一些特定命令和数据格式,例如设置波特率、读写寄存器等。您可以在代码中定义相应的结构体和函数来处理CH340协议。
5. 编写数据收发逻辑:虚拟串口的关键是数据的收发,您需要编写代码来处理上位机发送的数据和发送数据给上位机。在接收数据时,您需要处理数据的帧格式和校验等,确保数据的完整性和正确性。在发送数据时,您需要按照CH340协议的要求组织数据并发送给上位机。
6. 测试与调试:完成代码编写后,您需要进行测试与调试,确保虚拟串口功能正常工作。您可以使用串口调试助手等工具来与虚拟串口进行通信并进行功能验证。
二、关键代码
1.CH340设备信息代码
/**
******************************************************************************
* @file usb_desc.c
* @brief Descriptors for Virtual Com Port Demo
******************************************************************************
* @attention
*
* <h2><center>© COPYRIGHT 2013 STMicroelectronics</center></h2>
*
* Licensed under MCD-ST Liberty SW License Agreement V2, (the "License");
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.st.com/software_license_agreement_liberty_v2
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
******************************************************************************
*/
/* 包含文件 ------------------------------------------------------------------*/
#include "usb_lib.h"
#include "usb_desc.h"
/* USB 标准设备描述符 */
const uint8_t Virtual_Com_Port_DeviceDescriptor[] =
{
0x12, /* bLength */
USB_DEVICE_DESCRIPTOR_TYPE, /* bDescriptorType */
0x10,
0x01, /* bcdUSB = 1.10 */
0xff, /* bDeviceClass: CDC */
0x00, /* bDeviceSubClass */
0x00, /* bDeviceProtocol */
0x40, /* bMaxPacketSize0 */
0x86,
0x1a, /* idVendor = 0x1A86 */
0x23,
0x75, /* idProduct = 0x7523 */
0x64,
0x02, /* bcdDevice = 2.00 */
1, /* 制造商描述符的索引 */
2, /* 产品描述符的索引 */
3, /* 设备序列号的描述符的索引 */
0x01 /* 配置描述符的数量 */
};
const uint8_t Virtual_Com_Port_ConfigDescriptor[] =
{
/* 配置描述符 */
0x09, /* bLength: 配置描述符的长度 */
USB_CONFIGURATION_DESCRIPTOR_TYPE, /* bDescriptorType: Configuration */
VIRTUAL_COM_PORT_SIZ_CONFIG_DESC, /* wTotalLength: 所有返回的字节数 */
0x00,
0x01, /* bNumInterfaces: 接口的数量 */
0x01, /* bConfigurationValue: 配置值 */
0x00, /* iConfiguration: 描述该配置的字符串描述符的索引 */
0x80, /* bmAttributes: 自供电 */
0x30, /* MaxPower: 0 mA */
/* 接口描述符 */
0x09, /* bLength: 接口描述符的长度 */
USB_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType: Interface */
/* 接口描述符类型 */
0x00, /* bInterfaceNumber: 接口的编号 */
0x00, /* bAlternateSetting: 替代设置 */
0x03, /* bNumEndpoints: 使用的端点数 */
0xff, /* bInterfaceClass: 通信接口类 */
0x01, /* bInterfaceSubClass: 抽象控制模型 */
0x02, /* bInterfaceProtocol: 通用 AT 命令 */
0x00, /* iInterface: 描述该接口的字符串描述符的索引 */
/* 端点2输入描述符 */
0x07, /* bLength: 端点描述符的长度 */
USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType: Endpoint */
0x82, /* bEndpointAddress: (IN2) */
0x02, /* bmAttributes: 传输类型为批量传输 */
0x20, /* wMaxPacketSize: 最大数据包大小 */
0x00,
0x00, /* bInterval: 传输间隔 */
/* 端点2输出描述符 */
0x07, /* bLength: 端点描述符的长度 */
USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType: Endpoint */
0x02, /* bEndpointAddress: (OUT2) */
0x02, /* bmAttributes: 传输类型为批量传输 */
0x20, /* wMaxPacketSize: 最大数据包大小 */
0x00,
0x00, /* bInterval: 传输间隔 */
/* 端点1输入描述符 */
0x07, /* bLength: 端点描述符的长度 */
USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType: Endpoint */
0x81, /* bEndpointAddress: (IN1) */
0x03, /* bmAttributes: 传输类型为中断传输 */
0x08, /* wMaxPacketSize: 最大数据包大小 */
0x00,
0x01 /* bInterval: 传输间隔 */
};
/* USB 字符串描述符 */
const uint8_t Virtual_Com_Port_StringLangID[VIRTUAL_COM_PORT_SIZ_STRING_LANGID] =
{
VIRTUAL_COM_PORT_SIZ_STRING_LANGID,
USB_STRING_DESCRIPTOR_TYPE,
0x09,
0x04 /* LangID = 0x0409: U.S. English */
};
const uint8_t Virtual_Com_Port_StringVendor[VIRTUAL_COM_PORT_SIZ_STRING_VENDOR] =
{
VIRTUAL_COM_PORT_SIZ_STRING_VENDOR, /* 制造商字符串的大小 */
USB_STRING_DESCRIPTOR_TYPE, /* bDescriptorType*/
/* 制造商名称: "wch.cn" */
'w', 0, 'c', 0, 'h', 0, '.', 0, 'c', 0, 'n', 0
};
const uint8_t Virtual_Com_Port_StringProduct[VIRTUAL_COM_PORT_SIZ_STRING_PRODUCT] =
{
VIRTUAL_COM_PORT_SIZ_STRING_PRODUCT, /* bLength */
USB_STRING_DESCRIPTOR_TYPE, /* bDescriptorType */
/* 产品名称: "USB 串口" */
'U', 0, 'S', 0, 'B', 0, ' ', 0, 'S', 0, 'e', 0, 'r', 0, 'i', 0, 'a', 0, 'l', 0
};
uint8_t Virtual_Com_Port_StringSerial[VIRTUAL_COM_PORT_SIZ_STRING_SERIAL] =
{
VIRTUAL_COM_PORT_SIZ_STRING_SERIAL, /* bLength */
USB_STRING_DESCRIPTOR_TYPE, /* bDescriptorType */
/* 设备序列号: "0123456789" */
'0', 0, '1', 0, '2', 0, '3', 0, '4', 0, '5', 0 , '6', 0, '7', 0, '8', 0, '9', 0
};
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
2.添加CH340特有初始化代码
- `Virtual_Com_Port_Init`函数:初始化虚拟串口设备,包括获取设备的唯一序列号、设置设备的配置和连接状态等。
- `Virtual_Com_Port_Reset`函数:重置虚拟串口设备,包括设置设备的初始状态和清零设备配置和接口信息。
- `Virtual_Com_Port_SetConfiguration`函数:根据设备的配置信息更新设备状态。
- `Virtual_Com_Port_SetDeviceAddress`函数:设置设备的地址。
- `Virtual_Com_Port_Status_In`和`Virtual_Com_Port_Status_Out`函数:处理设备的状态 IN 和 OUT 请求。
- `Virtual_Com_Port_Data_Setup`函数:处理数据类特定的请求,如获取和设置线编码等。
- `Virtual_Com_Port_NoData_Setup`函数:处理非数据类特定的请求,如设置串口控制线状态等。
- `Virtual_Com_Port_GetDeviceDescriptor`、`Virtual_Com_Port_GetConfigDescriptor`和`Virtual_Com_Port_GetStringDescriptor`函数:获取设备描述符、配置描述符和字符串描述符。
/******************** (C) COPYRIGHT 2008 STMicroelectronics ********************
* File Name : usb_prop.c
* Author : MCD Application Team
* Version : V2.2.0
* Date : 06/13/2008
* Description : All processing related to Virtual Com Port Demo
********************************************************************************
* THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS
* WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE TIME.
* AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY DIRECT,
* INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING FROM THE
* CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE CODING
* INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
*******************************************************************************/
/* Includes ------------------------------------------------------------------*/
#include "usb_lib.h"
#include "usb_conf.h"
#include "usb_prop.h"
#include "usb_desc.h"
#include "usb_pwr.h"
#include "hw_config.h"
u32 ch341_state=0xeeff;
/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
u8 Request = 0;
void USART_Config_Default(void)
{
}
LINE_CODING linecoding =
{
115200, /* baud rate*/
0x00, /* stop bits-1*/
0x00, /* parity - none*/
0x08 /* no. of bits 8*/
};
/* -------------------------------------------------------------------------- */
/* Structures initializations */
/* -------------------------------------------------------------------------- */
DEVICE Device_Table =
{
EP_NUM,
1
};
DEVICE_PROP Device_Property =
{
Virtual_Com_Port_init,
Virtual_Com_Port_Reset,
Virtual_Com_Port_Status_In,
Virtual_Com_Port_Status_Out,
Virtual_Com_Port_Data_Setup,
Virtual_Com_Port_NoData_Setup,
Virtual_Com_Port_Get_Interface_Setting,
Virtual_Com_Port_GetDeviceDescriptor,
Virtual_Com_Port_GetConfigDescriptor,
Virtual_Com_Port_GetStringDescriptor,
0,
0x40 /*MAX PACKET SIZE*/
};
USER_STANDARD_REQUESTS User_Standard_Requests =
{
Virtual_Com_Port_GetConfiguration,
Virtual_Com_Port_SetConfiguration,
Virtual_Com_Port_GetInterface,
Virtual_Com_Port_SetInterface,
Virtual_Com_Port_GetStatus,
Virtual_Com_Port_ClearFeature,
Virtual_Com_Port_SetEndPointFeature,
Virtual_Com_Port_SetDeviceFeature,
Virtual_Com_Port_SetDeviceAddress
};
ONE_DESCRIPTOR Device_Descriptor =
{
(u8*)Virtual_Com_Port_DeviceDescriptor,
VIRTUAL_COM_PORT_SIZ_DEVICE_DESC
};
ONE_DESCRIPTOR Config_Descriptor =
{
(u8*)Virtual_Com_Port_ConfigDescriptor,
VIRTUAL_COM_PORT_SIZ_CONFIG_DESC
};
ONE_DESCRIPTOR String_Descriptor[4] =
{
{(u8*)Virtual_Com_Port_StringLangID, VIRTUAL_COM_PORT_SIZ_STRING_LANGID},
{(u8*)Virtual_Com_Port_StringVendor, VIRTUAL_COM_PORT_SIZ_STRING_VENDOR},
{(u8*)Virtual_Com_Port_StringProduct, VIRTUAL_COM_PORT_SIZ_STRING_PRODUCT},
{(u8*)Virtual_Com_Port_StringSerial, VIRTUAL_COM_PORT_SIZ_STRING_SERIAL}
};
/* Extern variables ----------------------------------------------------------*/
/* Private function prototypes -----------------------------------------------*/
/* Extern function prototypes ------------------------------------------------*/
/* Private functions ---------------------------------------------------------*/
/*******************************************************************************
* Function Name : Virtual_Com_Port_init.
* Description : Virtual COM Port Mouse init routine.
* Input : None.
* Output : None.
* Return : None.
*******************************************************************************/
void Virtual_Com_Port_init(void)
{
/* Update the serial number string descriptor with the data from the unique
ID*/
Get_SerialNum();
pInformation->Current_Configuration = 0;
/* Connect the device */
PowerOn();
/* USB interrupts initialization */
/* clear pending interrupts */
_SetISTR(0);
wInterrupt_Mask = IMR_MSK;
/* set interrupts mask */
_SetCNTR(wInterrupt_Mask);
/* configure the USART 1 to the default settings */
USART_Config_Default();
bDeviceState = UNCONNECTED;
}
/*******************************************************************************
* Function Name : Virtual_Com_Port_Reset
* Description : Virtual_Com_Port Mouse reset routine
* Input : None.
* Output : None.
* Return : None.
*******************************************************************************/
void Virtual_Com_Port_Reset(void)
{
/* Set Virtual_Com_Port DEVICE as not configured */
pInformation->Current_Configuration = 0;
/* Current Feature initialization */
pInformation->Current_Feature = Virtual_Com_Port_ConfigDescriptor[7];
/* Set Virtual_Com_Port DEVICE with the default Interface*/
pInformation->Current_Interface = 0;
SetBTABLE(BTABLE_ADDRESS);
/* Initialize Endpoint 0 */
SetEPType(ENDP0, EP_CONTROL);
SetEPTxStatus(ENDP0, EP_TX_STALL);
SetEPRxAddr(ENDP0, ENDP0_RXADDR);
SetEPTxAddr(ENDP0, ENDP0_TXADDR);
Clear_Status_Out(ENDP0);
SetEPRxCount(ENDP0, Device_Property.MaxPacketSize);
SetEPRxValid(ENDP0);
/* Initialize Endpoint 1 */
SetEPType(ENDP1, EP_INTERRUPT);
SetEPTxAddr(ENDP1, ENDP1_TXADDR);
SetEPTxStatus(ENDP1, EP_TX_NAK);
SetEPRxStatus(ENDP1, EP_RX_DIS);
// SetEPRxValid(ENDP1);
/* Initialize Endpoint 2 */
SetEPType(ENDP2, EP_BULK);
SetEPTxAddr(ENDP2, ENDP2_TXADDR);
SetEPTxStatus(ENDP2, EP_TX_NAK);
SetEPRxStatus(ENDP2, EP_RX_VALID);
// SetEPRxValid(ENDP2);
/* Initialize Endpoint 2 */
SetEPType(ENDP2, EP_BULK);
SetEPRxAddr(ENDP2, ENDP2_RXADDR);
SetEPRxCount(ENDP2, VIRTUAL_COM_PORT_DATA_SIZE);
/* Set this device to response on default address */
SetDeviceAddress(DADDR_EF|0);
// SetDeviceAddress(0);
bDeviceState = ATTACHED;
}
/*******************************************************************************
* Function Name : Virtual_Com_Port_SetConfiguration.
* Description : Udpade the device state to configured.
* Input : None.
* Output : None.
* Return : None.
*******************************************************************************/
void Virtual_Com_Port_SetConfiguration(void)
{
DEVICE_INFO *pInfo = &Device_Info;
if (pInfo->Current_Configuration != 0)
{
/* Device configured */
bDeviceState = CONFIGURED;
}
}
/*******************************************************************************
* Function Name : Virtual_Com_Port_SetConfiguration.
* Description : Udpade the device state to addressed.
* Input : None.
* Output : None.
* Return : None.
*******************************************************************************/
void Virtual_Com_Port_SetDeviceAddress (void)
{
bDeviceState = ADDRESSED;
}
/*******************************************************************************
* Function Name : Virtual_Com_Port_Status_In.
* Description : Virtual COM Port Status In Routine.
* Input : None.
* Output : None.
* Return : None.
*******************************************************************************/
void Virtual_Com_Port_Status_In(void)
{
if (Request == SET_LINE_CODING)
{
USART_Config();
Request = 0;
}
}
/*******************************************************************************
* Function Name : Virtual_Com_Port_Status_Out
* Description : Virtual COM Port Status OUT Routine.
* Input : None.
* Output : None.
* Return : None.
*******************************************************************************/
void Virtual_Com_Port_Status_Out(void)
{}
/*******************************************************************************
* Function Name : Virtual_Com_Port_Data_Setup
* Description : handle the data class specific requests
* Input : Request Nb.
* Output : None.
* Return : USB_UNSUPPORT or USB_SUCCESS.
*******************************************************************************/
static u8 vender_request;
u8 *Vender_Handle_CH341(u16 Length)
{
u16 wValue = pInformation->USBwValues.w;
u16 wIndex = pInformation->USBwIndexs.w;
static u8 buf1[2]={0x30,0};
static u8 buf2[2]={0xc3,0};
change_byte(wValue);
change_byte(wIndex);
if (Length == 0 && (vender_request==0x5f||vender_request==0x95))
{
pInformation->Ctrl_Info.Usb_wLength=2;
return 0;
}
switch(vender_request)
{
case 0x5f:
return buf1;
case 0x95:
if (wValue==0x2518)
{
return buf2;
}
else if(wValue==0x0706)
{
return (u8 *)&ch341_state;
}
}
return 0;
}
RESULT Virtual_Com_Port_Data_Setup(u8 RequestNo)
{
u8 *(*CopyRoutine)(u16);
CopyRoutine = NULL;
if (RequestNo == GET_LINE_CODING)
{
if (Type_Recipient == (CLASS_REQUEST | INTERFACE_RECIPIENT))
{
CopyRoutine = Virtual_Com_Port_GetLineCoding;
}
}
else if (RequestNo == SET_LINE_CODING)
{
if (Type_Recipient == (CLASS_REQUEST | INTERFACE_RECIPIENT))
{
CopyRoutine = Virtual_Com_Port_SetLineCoding;
}
Request = SET_LINE_CODING;
}
else
{
vender_request=RequestNo;
CopyRoutine = Vender_Handle_CH341;
}
if (CopyRoutine == NULL)
{
return USB_UNSUPPORT;
}
pInformation->Ctrl_Info.CopyData = CopyRoutine;
pInformation->Ctrl_Info.Usb_wOffset = 0;
(*CopyRoutine)(0);
return USB_SUCCESS;
}
/*******************************************************************************
* Function Name : Virtual_Com_Port_NoData_Setup.
* Description : handle the no data class specific requests.
* Input : Request Nb.
* Output : None.
* Return : USB_UNSUPPORT or USB_SUCCESS.
*******************************************************************************/
#define CH341_BAUDBASE_FACTOR 1532620800L
RESULT Virtual_Com_Port_NoData_Setup(u8 RequestNo)
{
if (Type_Recipient == (CLASS_REQUEST | INTERFACE_RECIPIENT))
{
if (RequestNo == SET_COMM_FEATURE)
{
return USB_SUCCESS;
}
else if (RequestNo == SET_CONTROL_LINE_STATE)
{
return USB_SUCCESS;
}
}
else//CH341 specific
{
u16 wValue = pInformation->USBwValues.w;
u16 wIndex = pInformation->USBwIndexs.w;
static u32 baud_factor;
static u8 divisor;
change_byte(wValue);
change_byte(wIndex);
switch(RequestNo)
{
case 0XA1:
break;
case CH341_REQ_WRITE_REG:
switch(wValue)
{
case 0x1312:
baud_factor&=0x00ff;
baud_factor|=(wIndex&0xff00);
divisor=wIndex&0x03;
break;
case 0x0f2c:
baud_factor&=0xff00;
baud_factor|=wIndex;
break;
}
break;
case 0xA4:
ch341_state=0xee9f;
break;
}
return USB_SUCCESS;
}
return USB_UNSUPPORT;
}
/*******************************************************************************
* Function Name : Virtual_Com_Port_GetDeviceDescriptor.
* Description : Gets the device descriptor.
* Input : Length.
* Output : None.
* Return : The address of the device descriptor.
*******************************************************************************/
u8 *Virtual_Com_Port_GetDeviceDescriptor(u16 Length)
{
return Standard_GetDescriptorData(Length, &Device_Descriptor);
}
/*******************************************************************************
* Function Name : Virtual_Com_Port_GetConfigDescriptor.
* Description : get the configuration descriptor.
* Input : Length.
* Output : None.
* Return : The address of the configuration descriptor.
*******************************************************************************/
u8 *Virtual_Com_Port_GetConfigDescriptor(u16 Length)
{
return Standard_GetDescriptorData(Length, &Config_Descriptor);
}
/*******************************************************************************
* Function Name : Virtual_Com_Port_GetStringDescriptor
* Description : Gets the string descriptors according to the needed index
* Input : Length.
* Output : None.
* Return : The address of the string descriptors.
*******************************************************************************/
u8 *Virtual_Com_Port_GetStringDescriptor(u16 Length)
{
u8 wValue0 = pInformation->USBwValue0;
if (wValue0 > 4)
{
return NULL;
}
else
{
return Standard_GetDescriptorData(Length, &String_Descriptor[wValue0]);
}
}
/*******************************************************************************
* Function Name : Virtual_Com_Port_Get_Interface_Setting.
* Description : test the interface and the alternate setting according to the
* supported one.
* Input1 : u8: Interface : interface number.
* Input2 : u8: AlternateSetting : Alternate Setting number.
* Output : None.
* Return : The address of the string descriptors.
*******************************************************************************/
RESULT Virtual_Com_Port_Get_Interface_Setting(u8 Interface, u8 AlternateSetting)
{
if (AlternateSetting > 0)
{
return USB_UNSUPPORT;
}
else if (Interface > 1)
{
return USB_UNSUPPORT;
}
return USB_SUCCESS;
}
/*******************************************************************************
* Function Name : Virtual_Com_Port_GetLineCoding.
* Description : send the linecoding structure to the PC host.
* Input : Length.
* Output : None.
* Return : Inecoding structure base address.
*******************************************************************************/
u8 *Virtual_Com_Port_GetLineCoding(u16 Length)
{
if (Length == 0)
{
pInformation->Ctrl_Info.Usb_wLength = sizeof(linecoding);
return NULL;
}
return(u8 *)&linecoding;
}
/*******************************************************************************
* Function Name : Virtual_Com_Port_SetLineCoding.
* Description : Set the linecoding structure fields.
* Input : Length.
* Output : None.
* Return : Linecoding structure base address.
*******************************************************************************/
u8 *Virtual_Com_Port_SetLineCoding(u16 Length)
{
if (Length == 0)
{
pInformation->Ctrl_Info.Usb_wLength = sizeof(linecoding);
return NULL;
}
return(u8 *)&linecoding;
}
三、测试
1.下载程序后,USB连接
2.用串口自发自收测试