#include <Wire.h>
byte i2c_rcv=0; // data received from I2C bus
void setup() {
Wire.begin(0x08); // join I2C bus as Slave with address 0x08
// event handler initializations
Wire.onReceive(dataRcv); // register an event handler for received data
Wire.onRequest(dataRqst); // register an event handler for data request
Serial.begin(115200);
}
void loop() {
}
//received data handler function
void dataRcv(int numBytes) {
Serial.print("Slave Received ");
Serial.print(numBytes);
Serial.println("Bytes");
while (Wire.available()) { // read all bytes received
i2c_rcv = Wire.read();
Serial.print("[");
Serial.print(i2c_rcv);
Serial.print("]");
}
Serial.println("");
}
// requests data handler function
void dataRqst() {
Wire.write(i2c_rcv); // send potentiometer position
Serial.print("Slave send ");
Serial.print(i2c_rcv,HEX);
}
/**
Cause the shell to parse and execute a command line.
This function creates a nested instance of the shell and executes the specified
command (CommandLine) with the specified environment (Environment). Upon return,
the status code returned by the specified command is placed in StatusCode.
If Environment is NULL, then the current environment is used and all changes made
by the commands executed will be reflected in the current environment. If the
Environment is non-NULL, then the changes made will be discarded.
The CommandLine is executed from the current working directory on the current
device.
The EnvironmentVariables pararemeter is ignored in a pre-UEFI Shell 2.0
environment. The values pointed to by the parameters will be unchanged by the
ShellExecute() function. The Output parameter has no effect in a
UEFI Shell 2.0 environment.
@param[in] ParentHandle The parent image starting the operation.
@param[in] CommandLine The pointer to a NULL terminated command line.
@param[in] Output True to display debug output. False to hide it.
@param[in] EnvironmentVariables Optional pointer to array of environment variables
in the form "x=y". If NULL, the current set is used.
@param[out] Status The status of the run command line.
@retval EFI_SUCCESS The operation completed sucessfully. Status
contains the status code returned.
@retval EFI_INVALID_PARAMETER A parameter contains an invalid value.
@retval EFI_OUT_OF_RESOURCES Out of resources.
@retval EFI_UNSUPPORTED The operation is not allowed.
**/
EFI_STATUS
EFIAPI
ShellExecute (
IN EFI_HANDLE *ParentHandle,
IN CHAR16 *CommandLine OPTIONAL,
IN BOOLEAN Output OPTIONAL,
IN CHAR16 **EnvironmentVariables OPTIONAL,
OUT EFI_STATUS *Status OPTIONAL
)
从Log可以看到执行的是下面这段代码:
//
// Check for UEFI Shell 2.0 protocols
//
if (gEfiShellProtocol != NULL) {
//
// Call UEFI Shell 2.0 version (not using Output parameter)
//
return (gEfiShellProtocol->Execute (
ParentHandle,
CommandLine,
EnvironmentVariables,
Status
));
}
// Pure FILE_HANDLE operations are passed to FileHandleLib
// these functions are indicated by the *
EFI_SHELL_PROTOCOL mShellProtocol = {
EfiShellExecute,
EfiShellGetEnv,
……
};
EFI_STATUS
EFIAPI
EfiShellExecute (
IN EFI_HANDLE *ParentImageHandle,
IN CHAR16 *CommandLine OPTIONAL,
IN CHAR16 **Environment OPTIONAL,
OUT EFI_STATUS *StatusCode OPTIONAL
)
串口是最常见的接口,因为它足够简单,几乎在所有的调试场合都能看到它的身影。从编程的角度来说,这种接口的代码已经非常成熟,从 C 到 Python 都能够支持这种接口。实际工作生产中最常见的就是USB转串口。美中不足的是,这USB转串口在编程的时候存在着如下缺陷:
某些USB转串口设备需要额外安装驱动才能使用;
当同一台电脑存在多个COM Port时,查找特定的设备会比较麻烦;
打开串口后很难得知串口另外一端的设备是否已经准备好;
传输速度有限制,最常见的波特率只有115200,意味着一秒只能传输11KB左右的数据。
针对上述问题,这次使用南京沁恒微电子公司出品的 CH32V208 制作一个双USB串口数据交换器(Dual USB SERIAL DATA EXCHANGER, 简称DUSDE)。CH32V208是一款基于32位RISC-V设计的无线型微控制器,配备了硬件堆栈区、快速中断入口,在标准RISC-V基础上大大提高了中断响应速度。搭载V4C内核,加入内存保护单元,同时降低硬件除法周期。除了片上集成2Mbps低功耗蓝牙BLE 通讯模块、10M以太网MAC+PHY模块、CAN控制器等接口之外还带有2个USB2.0全速设备+主机/设备接口。这次的双USB串口数据交换器就是将自身模拟为2个 USB 串口设备分别连接到两台电脑上,从而实现数据传输的功能。
CH32V208 架构
从上面的系统结构图可以看到,CH32V208WBU6支持两个USB2.0 Full Speed设备,其中一个可以作为 HOST或者Device,另外一个只能作为 Device 使用。我们通过编程的方式,让他们都工作在Device 模式下,下图就是我们设备的框图。
双USB串口数据交换器架构
针对前面的需要额外驱动的问题,通过编程在DUSDC上实现USB CDC协议,这样在Windows 8 及其以上的系统无需额外安装驱动。Windows 识别所属的 Class 之后,会自动加载内置驱动。
再比如,我们用设备连接两台电脑,USB Port1有程序打开过端口,并且用 “?ACV”查询过当前设备,那么USB Port 2这端程序以2022波特率打开端口后,再发送“?CFG”就能得到“REDY”的回复,表示对面的端口(USB1)已经准备好,反之如果收到“NRDY”则表示另外端口没有收到过“?ACV”命令;
主机的轮询发送,在main.c中,主要动作是将数据通过 USBFS_Endp_DataUp() 函数转发给USBFS 的 Port 上。
// If USB2 is connected, we will send data to USB2
if (USBFS_DevEnumStatus == 1) {
if ((USBFS_Endp_Busy[DEF_UEP3]==0)&&(USB1_Tx_Counter!=0)&&(USB1Replay==0xFF)) {
printf("A[%d]\r\n",USB1_Tx_Counter);
// Send data to USB2
USBFS_Endp_DataUp( ENDP3, &USB1_Tx_Buf[0], USB1_Tx_Counter, DEF_UEP_CPY_LOAD);
// If data length is 64, we should send a null package
if (USB1_Tx_Counter==DEF_USBD_UEP0_SIZE) {
// Wait until USB2 is free
while (USBFS_Endp_Busy[DEF_UEP3]!=0) {
}
// Send a NULL package
USBFS_Endp_DataUp( ENDP3, &USB1_Tx_Buf[0], 0, DEF_UEP_CPY_LOAD);
}
USB1_Tx_Counter=0;
// Enable USB1 Endpoint2
SetEPRxValid( ENDP2);
}
} else {
//If USB2 is NOT connected, data from USB1 would be dropped
USB1_Tx_Counter=0;
SetEPRxValid( ENDP2);
}
//
// Retrieve HII package list from ImageHandle
//
Status = gBS->OpenProtocol (
gImageHandle,
&gEfiHiiPackageListProtocolGuid,
(VOID **) &PackageListHeader,
gImageHandle,
NULL,
EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
if (EFI_ERROR (Status)) {
Print(L"HII Image Package with logo not found in PE/COFF resource section\n");
return Status;
}
b.取得资源
//Step2. Parser HII Image
ImageHeader=(EFI_HII_IMAGE_PACKAGE_HDR*)(PackageListHeader+1);
ImageData=(UINT8 *)(ImageHeader+1);
c.解析资源之后拷贝到分配的内存中
// Look for Ram Disk Protocol
Status = gBS->LocateProtocol (
&gEfiRamDiskProtocolGuid,
NULL,
&MyRamDisk
);
if (EFI_ERROR (Status)) {
Print(L"Couldn't find RamDiskProtocol\n");
return EFI_ALREADY_STARTED;
}
//Allocate a memory for Image
Status = gBS->AllocatePool (
EfiReservedMemoryType,
(UINTN)FileSize,
(VOID**)&StartingAddr
);
if(EFI_ERROR (Status)) {
Print(L"Allocate Memory failed!\n");
return EFI_SUCCESS;
}
CopyMem(StartingAddr,&ImageData[5],FileSize);
//
// Register the newly created RAM disk.
//
Status = MyRamDisk->Register (
((UINT64)(UINTN) StartingAddr),
FileSize,
&gEfiVirtualDiskGuid,
NULL,
&DevicePath
);
if (EFI_ERROR (Status)) {
Print(L"Can't create RAM Disk!\n");
return EFI_SUCCESS;
}