当前位置:网站首页>沁恒CH583 USB 自定义HID调试记录
沁恒CH583 USB 自定义HID调试记录
2022-06-25 17:33:00 【iot-lorawan】
使用USB HID主要是为了免驱,通过自定义USB HID可以利用USB口来做很多事,比如串口打印,串口升级都可以通过usb口来实现,这样可以省去一个USB转串口器件同时也不用装驱动,如下实现可以通过usb hid进行自定义收发,类似串口自定义收发。
如下代码基本都是基于沁恒EVT改造而来,
GitHub - iot-lorawan/ch58x_usbhid_custom: custom usb hid in ch58x
usb配置相关,该配置声明了使用usb 自定义hid,
#include "CH58x_common.h"
#define RepDataLoadLen 64
#define DevEP0SIZE 0x40
// 设备描述符
/* Device Descriptor */
const UINT8 MyDevDescr[] =
{
0x12, 0x01, 0x10, 0x01, 0x00, 0x00, 0x00, DevEP0SIZE,
0x86, 0x1A, 0xE1, 0xE6,
0x00, 0x01, 0x01, 0x02, 0x00, 0x01,
};
/* USB配置描述符(全速) */
UINT8 MyCfgDescr[ ] =
{
0x09,0x02,0x29,0x00,0x01,0x01,0x04,0xA0,0x23, //配置描述符
0x09,0x04,0x00,0x00,0x02,0x03,0x00,0x00,0x05, //接口描述符
0x09,0x21,0x00,0x01,0x00,0x01,0x22,0x22,0x00, //HID类描述符
0x07,0x05,0x81,0x03,0x40,0x00,0x01, //端点描述符(全速间隔时间改成1ms)
0x07,0x05,0x01,0x03,0x40,0x00,0x01, //端点描述符
};
/* USB报告描述符 */
const UINT8 CompatibilityHIDRepDesc[ ] =
{
0x06, 0x00,0xff,
0x09, 0x01,
0xa1, 0x01, //集合开始
0x09, 0x02, //Usage Page 用法
0x15, 0x00, //Logical Minimun
0x26, 0x00,0xff, //Logical Maximun
0x75, 0x08, //Report Size
0x95, RepDataLoadLen, //Report Counet
0x81, 0x06, //Input
0x09, 0x02, //Usage Page 用法
0x15, 0x00, //Logical Minimun
0x26, 0x00,0xff, //Logical Maximun
0x75, 0x08, //Report Size
0x95, RepDataLoadLen, //Report Counet
0x91, 0x06, //Output
0xC0
};
/* Language Descriptor */
const UINT8 MyLangDescr[] =
{
0x04, 0x03, 0x09, 0x04
};
/* Manufactor Descriptor */
const UINT8 MyManuInfo[] =
{
0x0E, 0x03, 'w', 0, 'c', 0, 'h', 0, '.', 0, 'c', 0, 'n', 0
};
/* Product Information */
const UINT8 MyProdInfo[] =
{
0x0C, 0x03, 'C', 0, 'H', 0, '1', 0, '0', 0, 'x', 0
};
/* USB序列号字符串描述符 */
const UINT8 MySerNumInfo[ ] =
{
/* 0123456789 */
22,03,48,0,49,0,50,0,51,0,52,0,53,0,54,0,55,0,56,0,57,0
};
/* USB设备限定描述符 */
const UINT8 MyUSBQUADesc[ ] =
{
0x0A, 0x06, 0x00, 0x02, 0xFF, 0x00, 0xFF, 0x40, 0x01, 0x00,
};
/**********************************************************/
uint8_t DevConfig, Ready;
uint8_t SetupReqCode;
uint16_t SetupReqLen;
const uint8_t *pDescr;
uint8_t Report_Value = 0x00;
uint8_t Idle_Value = 0x00;
uint8_t USB_SleepStatus = 0x00; /* USB睡眠状态 */
/*鼠标键盘数据*/
uint8_t HIDMouse[4] = {0x0, 0x0, 0x0, 0x0};
uint8_t HIDKey[8] = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0};
/******** 用户自定义分配端点RAM ****************************************/
__attribute__((aligned(4))) uint8_t EP0_Databuf[64 + 64 + 64]; //ep0(64)+ep4_out(64)+ep4_in(64)
__attribute__((aligned(4))) uint8_t EP1_Databuf[64 + 64]; //ep1_out(64)+ep1_in(64)
__attribute__((aligned(4))) uint8_t EP2_Databuf[64 + 64]; //ep2_out(64)+ep2_in(64)
__attribute__((aligned(4))) uint8_t EP3_Databuf[64 + 64]; //ep3_out(64)+ep3_in(64)USB逻辑交互,放在usb中断函数里执行
void USB_DevTransProcess(void)
{
uint8_t len, chtype;
uint8_t intflag, errflag = 0;
intflag = R8_USB_INT_FG;
if(intflag & RB_UIF_TRANSFER)
{
if((R8_USB_INT_ST & MASK_UIS_TOKEN) != MASK_UIS_TOKEN) // 非空闲
{
switch(R8_USB_INT_ST & (MASK_UIS_TOKEN | MASK_UIS_ENDP))
// 分析操作令牌和端点号
{
case UIS_TOKEN_IN:
{
switch(SetupReqCode)
{
case USB_GET_DESCRIPTOR:
len = SetupReqLen >= DevEP0SIZE ? DevEP0SIZE : SetupReqLen; // 本次传输长度
memcpy(pEP0_DataBuf, pDescr, len); /* 加载上传数据 */
SetupReqLen -= len;
pDescr += len;
R8_UEP0_T_LEN = len;
R8_UEP0_CTRL ^= RB_UEP_T_TOG; // 翻转
break;
case USB_SET_ADDRESS:
R8_USB_DEV_AD = (R8_USB_DEV_AD & RB_UDA_GP_BIT) | SetupReqLen;
R8_UEP0_CTRL = UEP_R_RES_ACK | UEP_T_RES_NAK;
break;
case USB_SET_FEATURE:
break;
default:
R8_UEP0_T_LEN = 0; // 状态阶段完成中断或者是强制上传0长度数据包结束控制传输
R8_UEP0_CTRL = UEP_R_RES_ACK | UEP_T_RES_NAK;
break;
}
}
break;
case UIS_TOKEN_OUT:
{
len = R8_USB_RX_LEN;
if(SetupReqCode == 0x09)
{
if(pEP0_DataBuf[0])
PRINT("Light on Num Lock LED!\n");
else if(pEP0_DataBuf[0] == 0)
PRINT("Light off Num Lock LED!\n");
}
}
break;
case UIS_TOKEN_OUT | 1:
{
if(R8_USB_INT_ST & RB_UIS_TOG_OK)
{ // 不同步的数据包将丢弃
R8_UEP1_CTRL ^= RB_UEP_R_TOG;
len = R8_USB_RX_LEN;
DevEP1_OUT_Deal(len);
}
}
break;
case UIS_TOKEN_IN | 1:
R8_UEP1_CTRL ^= RB_UEP_T_TOG;
R8_UEP1_CTRL = (R8_UEP1_CTRL & ~MASK_UEP_T_RES) | UEP_T_RES_NAK;
break;
case UIS_TOKEN_OUT | 2:
{
if(R8_USB_INT_ST & RB_UIS_TOG_OK)
{ // 不同步的数据包将丢弃
R8_UEP2_CTRL ^= RB_UEP_R_TOG;
len = R8_USB_RX_LEN;
DevEP2_OUT_Deal(len);
}
}
break;
case UIS_TOKEN_IN | 2:
R8_UEP2_CTRL ^= RB_UEP_T_TOG;
R8_UEP2_CTRL = (R8_UEP2_CTRL & ~MASK_UEP_T_RES) | UEP_T_RES_NAK;
break;
case UIS_TOKEN_OUT | 3:
{
if(R8_USB_INT_ST & RB_UIS_TOG_OK)
{ // 不同步的数据包将丢弃
R8_UEP3_CTRL ^= RB_UEP_R_TOG;
len = R8_USB_RX_LEN;
DevEP3_OUT_Deal(len);
}
}
break;
case UIS_TOKEN_IN | 3:
R8_UEP3_CTRL ^= RB_UEP_T_TOG;
R8_UEP3_CTRL = (R8_UEP3_CTRL & ~MASK_UEP_T_RES) | UEP_T_RES_NAK;
break;
case UIS_TOKEN_OUT | 4:
{
if(R8_USB_INT_ST & RB_UIS_TOG_OK)
{
R8_UEP4_CTRL ^= RB_UEP_R_TOG;
len = R8_USB_RX_LEN;
DevEP4_OUT_Deal(len);
}
}
break;
case UIS_TOKEN_IN | 4:
R8_UEP4_CTRL ^= RB_UEP_T_TOG;
R8_UEP4_CTRL = (R8_UEP4_CTRL & ~MASK_UEP_T_RES) | UEP_T_RES_NAK;
break;
default:
break;
}
R8_USB_INT_FG = RB_UIF_TRANSFER;
}
if(R8_USB_INT_ST & RB_UIS_SETUP_ACT) // Setup包处理
{
R8_UEP0_CTRL = RB_UEP_R_TOG | RB_UEP_T_TOG | UEP_R_RES_ACK | UEP_T_RES_NAK;
SetupReqLen = pSetupReqPak->wLength;
SetupReqCode = pSetupReqPak->bRequest;
chtype = pSetupReqPak->bRequestType;
len = 0;
errflag = 0;
if((pSetupReqPak->bRequestType & USB_REQ_TYP_MASK) != USB_REQ_TYP_STANDARD)
{
/* 非标准请求 */
/* 其它请求,如类请求,产商请求等 */
if(pSetupReqPak->bRequestType & 0x40)
{
/* 厂商请求 */
}
else if(pSetupReqPak->bRequestType & 0x20)
{
switch(SetupReqCode)
{
case DEF_USB_SET_IDLE: /* 0x0A: SET_IDLE */
Idle_Value = EP0_Databuf[3];
break; //这个一定要有
case DEF_USB_SET_REPORT: /* 0x09: SET_REPORT */
break;
case DEF_USB_SET_PROTOCOL: /* 0x0B: SET_PROTOCOL */
Report_Value = EP0_Databuf[2];
break;
case DEF_USB_GET_IDLE: /* 0x02: GET_IDLE */
EP0_Databuf[0] = Idle_Value;
len = 1;
break;
case DEF_USB_GET_PROTOCOL: /* 0x03: GET_PROTOCOL */
EP0_Databuf[0] = Report_Value;
len = 1;
break;
default:
errflag = 0xFF;
}
}
}
else /* 标准请求 */
{
switch(SetupReqCode)
{
case USB_GET_DESCRIPTOR:
{
switch(((pSetupReqPak->wValue) >> 8))
{
case USB_DESCR_TYP_DEVICE:
/* 获取设备描述符 */
pDescr = MyDevDescr;
len = MyDevDescr[0];
break;
case USB_DESCR_TYP_CONFIG:
/* 获取配置描述符 */
pDescr = MyCfgDescr;
len = sizeof( MyCfgDescr );
break;
case USB_DESCR_TYP_STRING:
/* 获取字符串描述符 */
switch( (pSetupReqPak->wValue)&0xff )
{
case 0:
/* 语言字符串描述符 */
pDescr = MyLangDescr;
len = MyLangDescr[0];
break;
case 1:
/* USB产商字符串描述符 */
pDescr = MyManuInfo;
len = MyManuInfo[0];
break;
case 2:
/* USB产品字符串描述符 */
pDescr = MyProdInfo;
len = MyProdInfo[0];
break;
case 3:
/* USB序列号字符串描述符 */
pDescr = MySerNumInfo;
len = sizeof( MySerNumInfo );
break;
default:
errflag = 0xFF;
break;
}
break;
case USB_DESCR_TYP_REPORT:
/* USB设备报告描述符 */
pDescr = CompatibilityHIDRepDesc;
len = sizeof( CompatibilityHIDRepDesc );
break;
case USB_DESCR_TYP_QUALIF:
/* 设备限定描述符 */
pDescr = ( PUINT8 )&MyUSBQUADesc[ 0 ];
len = sizeof( MyUSBQUADesc );
break;
case USB_DESCR_TYP_SPEED:
/* 其他速度配置描述符 */
errflag = 0xFF;
break;
default :
errflag = 0xff;
break;
}
if(SetupReqLen > len)
SetupReqLen = len; //实际需上传总长度
len = (SetupReqLen >= DevEP0SIZE) ? DevEP0SIZE : SetupReqLen;
memcpy(pEP0_DataBuf, pDescr, len);
pDescr += len;
}
break;
case USB_SET_ADDRESS:
SetupReqLen = (pSetupReqPak->wValue) & 0xff;
break;
case USB_GET_CONFIGURATION:
pEP0_DataBuf[0] = DevConfig;
if(SetupReqLen > 1)
SetupReqLen = 1;
break;
case USB_SET_CONFIGURATION:
DevConfig = (pSetupReqPak->wValue) & 0xff;
break;
case USB_CLEAR_FEATURE:
{
if((pSetupReqPak->bRequestType & USB_REQ_RECIP_MASK) == USB_REQ_RECIP_ENDP) // 端点
{
switch((pSetupReqPak->wIndex) & 0xff)
{
case 0x83:
R8_UEP3_CTRL = (R8_UEP3_CTRL & ~(RB_UEP_T_TOG | MASK_UEP_T_RES)) | UEP_T_RES_NAK;
break;
case 0x03:
R8_UEP3_CTRL = (R8_UEP3_CTRL & ~(RB_UEP_R_TOG | MASK_UEP_R_RES)) | UEP_R_RES_ACK;
break;
case 0x82:
R8_UEP2_CTRL = (R8_UEP2_CTRL & ~(RB_UEP_T_TOG | MASK_UEP_T_RES)) | UEP_T_RES_NAK;
break;
case 0x02:
R8_UEP2_CTRL = (R8_UEP2_CTRL & ~(RB_UEP_R_TOG | MASK_UEP_R_RES)) | UEP_R_RES_ACK;
break;
case 0x81:
R8_UEP1_CTRL = (R8_UEP1_CTRL & ~(RB_UEP_T_TOG | MASK_UEP_T_RES)) | UEP_T_RES_NAK;
break;
case 0x01:
R8_UEP1_CTRL = (R8_UEP1_CTRL & ~(RB_UEP_R_TOG | MASK_UEP_R_RES)) | UEP_R_RES_ACK;
break;
default:
errflag = 0xFF; // 不支持的端点
break;
}
}
else if((pSetupReqPak->bRequestType & USB_REQ_RECIP_MASK) == USB_REQ_RECIP_DEVICE)
{
if(pSetupReqPak->wValue == 1)
{
USB_SleepStatus &= ~0x01;
}
}
else
{
errflag = 0xFF;
}
}
break;
case USB_SET_FEATURE:
if((pSetupReqPak->bRequestType & USB_REQ_RECIP_MASK) == USB_REQ_RECIP_ENDP)
{
/* 端点 */
switch(pSetupReqPak->wIndex)
{
case 0x83:
R8_UEP3_CTRL = (R8_UEP3_CTRL & ~(RB_UEP_T_TOG | MASK_UEP_T_RES)) | UEP_T_RES_STALL;
break;
case 0x03:
R8_UEP3_CTRL = (R8_UEP3_CTRL & ~(RB_UEP_R_TOG | MASK_UEP_R_RES)) | UEP_R_RES_STALL;
break;
case 0x82:
R8_UEP2_CTRL = (R8_UEP2_CTRL & ~(RB_UEP_T_TOG | MASK_UEP_T_RES)) | UEP_T_RES_STALL;
break;
case 0x02:
R8_UEP2_CTRL = (R8_UEP2_CTRL & ~(RB_UEP_R_TOG | MASK_UEP_R_RES)) | UEP_R_RES_STALL;
break;
case 0x81:
R8_UEP1_CTRL = (R8_UEP1_CTRL & ~(RB_UEP_T_TOG | MASK_UEP_T_RES)) | UEP_T_RES_STALL;
break;
case 0x01:
R8_UEP1_CTRL = (R8_UEP1_CTRL & ~(RB_UEP_R_TOG | MASK_UEP_R_RES)) | UEP_R_RES_STALL;
break;
default:
/* 不支持的端点 */
errflag = 0xFF; // 不支持的端点
break;
}
}
else if((pSetupReqPak->bRequestType & USB_REQ_RECIP_MASK) == USB_REQ_RECIP_DEVICE)
{
if(pSetupReqPak->wValue == 1)
{
/* 设置睡眠 */
USB_SleepStatus |= 0x01;
}
}
else
{
errflag = 0xFF;
}
break;
case USB_GET_INTERFACE:
pEP0_DataBuf[0] = 0x00;
if(SetupReqLen > 1)
SetupReqLen = 1;
break;
case USB_SET_INTERFACE:
break;
case USB_GET_STATUS:
if((pSetupReqPak->bRequestType & USB_REQ_RECIP_MASK) == USB_REQ_RECIP_ENDP)
{
/* 端点 */
pEP0_DataBuf[0] = 0x00;
switch(pSetupReqPak->wIndex)
{
case 0x83:
if((R8_UEP3_CTRL & (RB_UEP_T_TOG | MASK_UEP_T_RES)) == UEP_T_RES_STALL)
{
pEP0_DataBuf[0] = 0x01;
}
break;
case 0x03:
if((R8_UEP3_CTRL & (RB_UEP_R_TOG | MASK_UEP_R_RES)) == UEP_R_RES_STALL)
{
pEP0_DataBuf[0] = 0x01;
}
break;
case 0x82:
if((R8_UEP2_CTRL & (RB_UEP_T_TOG | MASK_UEP_T_RES)) == UEP_T_RES_STALL)
{
pEP0_DataBuf[0] = 0x01;
}
break;
case 0x02:
if((R8_UEP2_CTRL & (RB_UEP_R_TOG | MASK_UEP_R_RES)) == UEP_R_RES_STALL)
{
pEP0_DataBuf[0] = 0x01;
}
break;
case 0x81:
if((R8_UEP1_CTRL & (RB_UEP_T_TOG | MASK_UEP_T_RES)) == UEP_T_RES_STALL)
{
pEP0_DataBuf[0] = 0x01;
}
break;
case 0x01:
if((R8_UEP1_CTRL & (RB_UEP_R_TOG | MASK_UEP_R_RES)) == UEP_R_RES_STALL)
{
pEP0_DataBuf[0] = 0x01;
}
break;
}
}
else if((pSetupReqPak->bRequestType & USB_REQ_RECIP_MASK) == USB_REQ_RECIP_DEVICE)
{
pEP0_DataBuf[0] = 0x00;
if(USB_SleepStatus)
{
pEP0_DataBuf[0] = 0x02;
}
else
{
pEP0_DataBuf[0] = 0x00;
}
}
pEP0_DataBuf[1] = 0;
if(SetupReqLen >= 2)
{
SetupReqLen = 2;
}
break;
default:
errflag = 0xff;
break;
}
}
if(errflag == 0xff) // 错误或不支持
{
// SetupReqCode = 0xFF;
R8_UEP0_CTRL = RB_UEP_R_TOG | RB_UEP_T_TOG | UEP_R_RES_STALL | UEP_T_RES_STALL; // STALL
}
else
{
if(chtype & 0x80) // 上传
{
len = (SetupReqLen > DevEP0SIZE) ? DevEP0SIZE : SetupReqLen;
SetupReqLen -= len;
}
else
len = 0; // 下传
R8_UEP0_T_LEN = len;
R8_UEP0_CTRL = RB_UEP_R_TOG | RB_UEP_T_TOG | UEP_R_RES_ACK | UEP_T_RES_ACK; // 默认数据包是DATA1
}
R8_USB_INT_FG = RB_UIF_TRANSFER;
}
}
else if(intflag & RB_UIF_BUS_RST)
{
R8_USB_DEV_AD = 0;
R8_UEP0_CTRL = UEP_R_RES_ACK | UEP_T_RES_NAK;
R8_UEP1_CTRL = UEP_R_RES_ACK | UEP_T_RES_NAK;
R8_UEP2_CTRL = UEP_R_RES_ACK | UEP_T_RES_NAK;
R8_UEP3_CTRL = UEP_R_RES_ACK | UEP_T_RES_NAK;
R8_USB_INT_FG = RB_UIF_BUS_RST;
}
else if(intflag & RB_UIF_SUSPEND)
{
if(R8_USB_MIS_ST & RB_UMS_SUSPEND)
{
;
} // 挂起
else
{
;
} // 唤醒
R8_USB_INT_FG = RB_UIF_SUSPEND;
}
else
{
R8_USB_INT_FG = intflag;
}
}
__INTERRUPT
__HIGH_CODE
void USB_IRQHandler(void) /* USB中断服务程序,使用寄存器组1 */
{
USB_DevTransProcess();
}
USB端点收发部分接口,IN OUT都是从host端即电脑端而言
/*********************************************************************
* @fn DevEP1_OUT_Deal
*
* @brief 端点1数据处理
*
* @return none
*/
void DevEP1_OUT_Deal(uint8_t l)
{ /* 用户可自定义 */
uint8_t i;
for(i = 0; i < l; i++)
{
pEP1_IN_DataBuf[i] = pEP1_OUT_DataBuf[i];
}
DevEP1_IN_Deal(l);
PRINT("DevEP1_OUT_Deal:%d\r\n",l);
}
/*********************************************************************
* @fn DevEP2_OUT_Deal
*
* @brief 端点2数据处理
*
* @return none
*/
void DevEP2_OUT_Deal(uint8_t l)
{ /* 用户可自定义 */
uint8_t i;
PRINT("DevEP2_OUT_Deal:%d\r\n",l);
for(i = 0; i < l; i++)
{
pEP2_IN_DataBuf[i] = pEP2_OUT_DataBuf[i];
}
DevEP2_IN_Deal(l);
}
/*********************************************************************
* @fn DevEP3_OUT_Deal
*
* @brief 端点3数据处理
*
* @return none
*/
void DevEP3_OUT_Deal(uint8_t l)
{ /* 用户可自定义 */
uint8_t i;
PRINT("DevEP3_OUT_Deal:%d\r\n",l);
for(i = 0; i < l; i++)
{
pEP3_IN_DataBuf[i] = pEP3_OUT_DataBuf[i];
}
DevEP3_IN_Deal(l);
}
/*********************************************************************
* @fn DevEP4_OUT_Deal
*
* @brief 端点4数据处理
*
* @return none
*/
void DevEP4_OUT_Deal(uint8_t l)
{ /* 用户可自定义 */
uint8_t i;
PRINT("DevEP4_OUT_Deal:%d\r\n",l);
for(i = 0; i < l; i++)
{
pEP4_IN_DataBuf[i] = pEP4_OUT_DataBuf[i];
}
DevEP4_IN_Deal(l);
}主函数:
int main()
{
SetSysClock(CLK_SOURCE_PLL_60MHz);
DebugInit();
PRINT("start\n");
pEP0_RAM_Addr = EP0_Databuf;
pEP1_RAM_Addr = EP1_Databuf;
pEP2_RAM_Addr = EP2_Databuf;
pEP3_RAM_Addr = EP3_Databuf;
USB_DeviceInit();
PFIC_EnableIRQ(USB_IRQn);
while(1)
{
mDelaymS(100);
#if 0
uint8_t i;
for(i = 0; i < 64; i++)
{
pEP1_IN_DataBuf[i] = 0x30+i;
}
DevEP1_IN_Deal(64);
#endif
}
}
使用usb hid收发助手调试

边栏推荐
- How to solve the problem of network disconnection after enabling hotspot sharing in win10?
- CONDA modifying a mirror source
- Introduction to the container of() function
- Accumulation of some common knowledge points
- Operating steps for installing CUDA in win10 (continuous improvement)
- MVDR beam MATLAB, MVDR beam forming matlab[easy to understand]
- [matlab] data interpolation
- Website arrangement of super all metal PBR multi-channel mapping materials
- Find the longest substring length satisfying the condition
- Automatic submission for the next education day
猜你喜欢

Why do we need ankeri's active power filter in frequency converter occasions?

About Equilibrium - Simplified bottleneck model

Mathematical modeling - nonlinear programming

Treasure and niche Chinese painting 3D texture material website sharing

A development of student management system based on PHP

观察者模式之通用消息发布与订阅

College Students' hot summer exchange, Rog star product phantom 16 flipped version / phantom 13 / phantom x appointment

HMS Core机器学习服务实现同声传译,支持中英文互译和多种音色语音播报
![[matlab] data interpolation](/img/b8/d7e1a5f7c6f56c8312a1fb5d517ac6.png)
[matlab] data interpolation

Introduction to the container of() function
随机推荐
Mobx学习之路----强大的“状态管理工具”
[matlab] data statistical analysis
微博评论的计算架构
智能对话01-redis的安装
杰理之如何给外界输出一个时钟源使用【篇】
Can I open an account? Is it safe to open an account
How Jerry used to output a clock source to the outside world [chapter]
匯編語言(5)寄存器(內存訪問)
Precautions for using Jerry's timer [chapter]
Remote terminal control artifact - mobaxterm
VSCode 自动生成头文件的#ifndef #define #endif
【编译原理】词法分析
MVDR beam MATLAB, MVDR beam forming matlab[easy to understand]
Which of the top ten securities companies has the lowest commission? Is it safe to open an account
golang list to string
TLV decoding
Distributed remote management of distribution room environment
启牛的涨乐财付通如何?安全靠谱吗
中断操作:AbortController学习笔记
How high does UART baud rate require for clock accuracy?