This document uses the USB firmware library and customizes it based on it to complete a USB-HID device. First, the usb_desc.c file stores the existence of various USB descriptors.
#include "usb_desc.h"
//USB standard device descriptor
const u8 DinkUsbDeviceDescriptor[DINK_USB_SIZ_DEVICE_DESC] = {
USB_DEVICE_DESC_SIZE, //bLength field. The length of the device descriptor is 18 (0x12) bytes
USB_DEVICE_DESCRIPTOR_TYPE, //bDescriptorType field. The device descriptor number is 0x01
WBVAL(0x0200), //bcdUSB field. Here the version is set to USB1.1, which is 0x0110.
0x00, //bDeviceClass field. We do not define the device class in the device descriptor.
0x00, //bDeviceSubClass field. When the bDeviceClass field is 0, this field is also 0.
0x00, //bDeviceProtocol field. When the bDeviceClass field is 0, this field is also 0.
0x40, //bMaxPacketSize0 field. Maximum packet length of endpoint 0.
WBVAL(0x7777), //idVender field. Vendor ID number, we use 0x8888 here, for experimental use only.
WBVAL(0x8888), //idProduct field. Product ID number. Since this is the first experiment, we use 0x0001. \
WBVAL(0x0100), //Device version
0x01, //iManufacturer field. The index value of the manufacturer string, for easy memory and management
0x02, //iProduct field. Index value of the product string. We just used 1, so let’s use 2 here.
0x03, //iSerialNumber field. The serial number string index value of the device.
0x01 //bNumConfigurations field. The number of configurations that the device has.
};
//USB report descriptor definition
const u8 HID_ReportDescriptor[]=
{
0x06,0xA0,0xFF,//Usage page (FFA0h, vendor defined)
0x09, 0x01, // usage (vendor defined)
0xA1, 0x01, // Application
0x09, 0x02 ,//Usage (vendor defined)
0xA1, 0x00, // Collection (Physical)
0x06,0xA1,0xFF,//Usage page (vendor defined)
// Input report
0x09, 0x03, // usage (vendor defined)
0x09, 0x04, // Usage (vendor defined)
0x15, 0x80, //Logical minimum value (0x80 or -128)
0x25, 0x7F, //Logical maximum value (0x7F or 127)
0x35, 0x00, //Physical minimum value (0)
0x45,0xFF,//Physical maximum value (255)
0x75, 0x08, //Report size (8 bits)
0x95, 0x40, //report value (64 fields)
0x81, 0x02, // input (data, variable, absolute)
// Output report
0x09, 0x05, // Usage (vendor defined)
0x09, 0x06, // Usage (vendor defined)
0x15, 0x80, //Logical minimum value (0x80 or -128)
0x25, 0x7F, //Logical maximum value (0x7F or 127)
0x35, 0x00, //Physical minimum value (0)
0x45,0xFF,//Physical maximum value (255)
0x75,0x08,//report length (8 bits)
0x95, 0x40, //report value (64 fields)
0x91, 0x02, // output (data, variable, absolute)
0xC0, //End of collection (Physical)
0xC0 // End of collection (Application)
};
//Through the definition of the report descriptor above, we know that the returned input report has 8 bytes.
//The output report also has 64 bytes. As for what these 64 bytes of data are used for, it is up to the user to determine what they are used for.
//It’s up to you to decide.
//////////////////////////////Report descriptor completed//////////////////////////////
//usb configuration descriptor
const u8 DinkUsbConfigDescriptor[DINK_USB_SIZ_CONFIG_DESC] = {
/***************Configuration Descriptor***************************/
USB_CONFIGUARTION_DESC_SIZE, //bLength field. The length of the configuration descriptor is 9 bytes.
USB_CONFIGURATION_DESCRIPTOR_TYPE, //bDescriptorType field. The configuration descriptor number is 0x02.
//wTotalLength field. The total length of the configuration descriptor set,
//Including the configuration descriptor itself, interface descriptor, class descriptor, endpoint descriptor, etc.
WBVAL(
USB_CONFIGUARTION_DESC_SIZE + //Configuration descriptor
USB_INTERFACE_DESC_SIZE + //Interface 1 descriptor
9 + //hid descriptor
USB_ENDPOINT_DESC_SIZE + //Endpoint descriptor
USB_ENDPOINT_DESC_SIZE //Endpoint descriptor
),
0x01, //bNumInterfaces field. The number of interfaces included in this configuration, only one interface.
0x01, //bConfiguration field. The value of this configuration is 1.
0x00, //iConfigurationz field, string index of the configuration. None here, 0.
USB_CONFIG_BUS_POWERED , //bmAttributes field, the attributes of the device
USB_CONFIG_POWER_MA(500), //bMaxPower field, the maximum current required by the device
/*************************The first interface descriptor, hid device**********************/
USB_INTERFACE_DESC_SIZE, //bLength field. The length of the interface descriptor is 9 bytes.
USB_INTERFACE_DESCRIPTOR_TYPE, //bDescriptorType field. The interface descriptor number is 0x04.
0x00, //bInterfaceNumber field. The number of the interface, the first interface, is numbered 0.
0x00, //bAlternateSetting field. The alternate number of this interface is 0.
0x02, //bNumEndpoints field. The number of non-zero endpoints. This interface has 2 batch endpoints
USB_DEVICE_CLASS_HUMAN_INTERFACE, //bInterfaceClass field. The class used by this interface. The code for the mass storage device interface class is 0x08. ,
0x00, //bInterfaceSubClass field. The subclass used by this interface. In the HID1.1 protocol,
//Only one subclass is specified: the subclass that supports BIOS boot.
//USB keyboard and mouse belong to this subclass, and the subclass code is 0x01.
//But here we are a custom HID device, so we don't use subclasses.
0x00, //bInterfaceProtocol field. If the subclass is a subclass that supports booting,
//The protocol can select mouse and keyboard. The keyboard code is 0x01 and the mouse code is 0x02.
//Custom HID device, no protocol is used.
0x00, //iConfiguration field. The string index value of the interface. None here, 0.
/*************************HID report descriptor****************************/
//bLength field. There is only one subordinate descriptor under this HID descriptor, so the length is 9 bytes.
0x09,
//bDescriptorType field. The HID descriptor number is 0x21.
0x21,
//bcdHID field. This protocol uses the HID1.1 protocol. Note that the low byte comes first.
0x10,
0x01,
//bCountyCode field. The country code for the device. Here, the United States is selected, code 0x21.
0x21,
//bNumDescriptors field. The number of subordinate descriptors. We only have one report descriptor.
0x01,
//bDescriptorType field. The type of the lower-level descriptor is a report descriptor, numbered 0x22.
0x22,
//bDescriptorLength field. The length of the lower-level descriptor. The lower-level descriptor is the report descriptor.
sizeof(HID_ReportDescriptor)&0xFF,
(sizeof(HID_ReportDescriptor)>>8)&0xFF,
/*************************Endpoint Descriptor**********************************/
/* Endpoint descriptor */
USB_ENDPOINT_DESC_SIZE, //bLength field. The endpoint descriptor length is 7 bytes.
USB_ENDPOINT_DESCRIPTOR_TYPE, //bDescriptorType field. The endpoint descriptor number is 0x05.
USB_ENDPOINT_IN(1), //bEndpointAddress field. The address of the endpoint. We use the input endpoint 1 of D12.
USB_ENDPOINT_TYPE_INTERRUPT, //bmAttributes field. D1~D0 is the endpoint transfer type selection.
WBVAL(0x0040), //wMaxPacketSize field. The maximum packet length of this endpoint. The maximum packet length is 64 bytes.
0x01, //bInterval field. Endpoint query time, endpoint query time, meaningless here.
/***********************Endpoint Descriptor*******************************************/
USB_ENDPOINT_DESC_SIZE, //bLength field. The endpoint descriptor length is 7 bytes.
USB_ENDPOINT_DESCRIPTOR_TYPE, //bDescriptorType field. The endpoint descriptor number is 0x05.
USB_ENDPOINT_OUT(1), //bEndpointAddress field. The address of the endpoint. We use the input endpoint 1 of D12.
USB_ENDPOINT_TYPE_INTERRUPT, //bmAttributes field. D1~D0 is the endpoint transfer type selection.
WBVAL(0x0040), //wMaxPacketSize field. The maximum packet length of this endpoint. The maximum packet length is 64 bytes.
0x01, //bInterval field. Endpoint query time, endpoint query time, meaningless here.
};
/****************************Definition of language ID********************/
const u8 DinkUsbLanguageId[DINK_USB_SIZ_STRING_LANGID]=
{
0x04, //The length of this descriptor
0x03, // string descriptor
//0x0409 is the ID for American English
0x09,
0x04
};
////////////////////////////Language ID completed////////////////////////////////////
//Unicode string descriptor
//Deng Xiaojun's USB mouse
const u8 DinkUsbManufacturerStringDescriptor[DINK_USB_SIZ_STRING_VENDOR]=
{
32, //The length of the descriptor is 32 bytes
0x03, //The type code of the string descriptor is 0x03
0x44, 0x00, //D
0x49, 0x00, //I
0x4e, 0x00, //N
0x4b, 0x00, //K
0x5f, 0x00, //_
0x48, 0x00, //H
0x49, 0x00, //I
0x44, 0x00, //D
0x5f, 0x00, //_
0x44, 0x00, //D
0x45, 0x00, //E
0x56, 0x00, //V
0x49, 0x00, //I
0x43, 0x00, //C
0x45, 0x00 //E
};
///////////////////////////End of manufacturer string/////////////////////////////
//Product string descriptor
const u8 DinkUsbProductStringDescriptor[DINK_USB_SIZ_STRING_PRODUCT]=
{
32, //The length of the descriptor is 32 bytes
0x03, //The type code of the string descriptor is 0x03
0x44, 0x00, //D
0x49, 0x00, //I
0x4e, 0x00, //N
0x4b, 0x00, //K
0x5f, 0x00, //_
0x48, 0x00, //H
0x49, 0x00, //I
0x44, 0x00, //D
0x5f, 0x00, //_
0x44, 0x00, //D
0x45, 0x00, //E
0x56, 0x00, //V
0x49, 0x00, //I
0x43, 0x00, //C
0x45, 0x00 //E
};
//////////////////////////End of product string/////////////////////////////
//Unicode encoding of the string "2008-07-07"
//8-bit little-endian format
const u8 DinkUsbSerialNumberStringDescriptor[DINK_USB_SIZ_STRING_SERIAL]={
22, //The length of the descriptor is 22 bytes
0x03, //The type code of the string descriptor is 0x03
0x32, 0x00, //2
0x30, 0x00, //0
0x31, 0x00, //1
0x35, 0x00, //5
0x2d, 0x00, //-
0x30, 0x00, //0
0x33, 0x00, //3
0x2d, 0x00, //-
0x32, 0x00, //2
0x31, 0x00 //1
};
///////////////////////End of product serial number string/////////////////////////
//Product Serial Number
u8 DinkUsbStringSerialUniqueId[DINK_USB_SIZ_STRING_SERIAL_UNIQUE_ID] =
{
DINK_USB_SIZ_STRING_SERIAL_UNIQUE_ID, //descriptor length
0x03 //Descriptor type encoding
/* Serial number The code will be
You can modify this file to implement different devices. The second is the usb_prop.c file, which defines a series of callback functions used in the USB enumeration phase.
#include "usb_prop.h"
u32 ProtocolValue;
//Indicates how many endpoints and how many configurations there are
DEVICE Device_Table =
{
EP_NUM,
1
};
//static u8 s_Request = 0; //Record the current request value
//Device descriptor
ONE_DESCRIPTOR Device_Descriptor =
{
(u8*)DinkUsbDeviceDescriptor,
DINK_USB_SIZ_DEVICE_DESC
};
//Configuration descriptor
ONE_DESCRIPTOR Config_Descriptor =
{
(u8*)DinkUsbConfigDescriptor,
DINK_USB_SIZ_CONFIG_DESC
};
//Report descriptor
ONE_DESCRIPTOR DinkUsb_Report_Descriptor =
{
(u8*)HID_ReportDescriptor,
HID_ReportDescSize
};
//Report descriptor
ONE_DESCRIPTOR DinkUsb_Hid_Descriptor =
{
(u8*)(DinkUsbConfigDescriptor+9),
9
};
// string descriptor
ONE_DESCRIPTOR String_Descriptor[4] =
{
{(u8*)DinkUsbLanguageId, DINK_USB_SIZ_STRING_LANGID},
{(u8*)DinkUsbManufacturerStringDescriptor, DINK_USB_SIZ_STRING_VENDOR},
{(u8*)DinkUsbProductStringDescriptor, DINK_USB_SIZ_STRING_PRODUCT},
{(u8*)DinkUsbSerialNumberStringDescriptor, DINK_USB_SIZ_STRING_SERIAL}
};
//USB process processing function array
DEVICE_PROP Device_Property =
{
Dink UsbIns,
DinkUsbReset,
DinkUsbStatus_In,
DinkUsbStatus_Out,
DinkUsbData_Setup,
DinkUsbNoDataSetup,
DinkUsbGetInterfaceSetting,
DinkUsbGetDeviceDescriptor,
DinkUsbGetConfigDescriptor,
DinkUsbGetStringDescriptor,
0,
0x40 /*MAX PACKET SIZE*/
};
//USB standard data request structure
//Only two are implemented, the rest are solved with nop method
USER_STANDARD_REQUESTS User_Standard_Requests =
{
DinkUsbGetConfiguration,
DinkUsbSetConfiguration,
DinkUsbGetInterface,
DinkUsbSetInterface,
DinkUsbGetStatus,
DinkUsbClearFeature,
DinkUsbSetEndPointFeature,
DinkUsbSetDeviceFeature,
DinkUsbSetDeviceAddress
};
//Device initialization
void DinkUsbInit(void)
{
Get_SerialNum(); //Build string descriptor
pInformation->Current_Configuration = 0; //The currently selected configuration is 0
PowerOn(); //Connect USB
_SetISTR(0);
wInterrupt_Mask = IMR_MSK;
_SetCNTR(wInterrupt_Mask);
bDeviceState = UNCONNECTED; //Initialize the device status to unconnected
usb_debug_printf("USB Init\r\n");
}
// Device reset
void DinkUsbReset(void)
{
Device_Info.Current_Configuration = 0; //Select the current configuration as 0
pInformation->Current_Feature = DinkUsbConfigDescriptor[7]; //Get the current device attribute in the configuration descriptor
pInformation->Current_Interface = 0; //Set the current device interface
SetBTABLE(BTABLE_ADDRESS); //Set the buffer address
SetEPType(ENDP0, EP_CONTROL); //Control endpoint
SetEPTxStatus(ENDP0, EP_TX_STALL);
SetEPRxAddr(ENDP0, ENDP0_RXADDR); //Set endpoint buffer address
SetEPTxAddr(ENDP0, ENDP0_TXADDR);
Clear_Status_Out(ENDP0);
SetEPRxCount(ENDP0, Device_Property.MaxPacketSize); //Set the maximum length of receiving
SetEPRxValid(ENDP0);
SetEPType(ENDP1, EP_INTERRUPT); //Initialize endpoint 1 to interrupt transfer mode to report some status
SetEPTxAddr(ENDP1, ENDP1_TXADDR); //Set endpoint address
SetEPRxAddr(ENDP1, ENDP1_RXADDR); //Set endpoint address
SetEPRxStatus(ENDP1, EP_RX_VALID); //Enable reception
SetEPTxStatus(ENDP1, EP_TX_NAK); //Disable transmission
SetEPRxCount(ENDP1, 64); //Set the maximum length of receiving
Clear_Status_Out(ENDP1);
bDeviceState = ATTACHED; //Device inserted
SetDeviceAddress(0); //Set the current address to 0
usb_debug_printf("USB Reset\r\n");
}
//I don't know what it's for
void DinkUsbStatus_In(void)
{
return;
}
//I don't know what it's for
void DinkUsbStatus_Out(void)
{
return;
}
u8 *DinkUsbGetReportDescriptor(u16 Length)
{
usb_debug_printf("Get report descriptor\r\n");
return Standard_GetDescriptorData(Length, &DinkUsb_Report_Descriptor);
}
u8 *DinkUsbGetHIDDescriptor(u16 Length)
{
usb_debug_printf("Get HID descriptor\r\n");
return Standard_GetDescriptorData(Length, &DinkUsb_Hid_Descriptor);
}
u8 *DinkUsbGetProtocolValue(u16 Length)
{
usb_debug_printf("Get protocol\r\n");
if (Length == 0)
{
pInformation->Ctrl_Info.Usb_wLength = 1;
return NULL;
}
else
{
return (u8 *)(&ProtocolValue);
}
}
RESULT DinkUsbData_Setup(u8 RequestNo)
{
u8 *(*CopyRoutine)(u16);
CopyRoutine = NULL;
if ((RequestNo == GET_DESCRIPTOR)
&& (Type_Recipient == (STANDARD_REQUEST | INTERFACE_RECIPIENT))
&& (pInformation->USBwIndex0 == 0))
{
//Get the report descriptor
if (pInformation->USBwValue1 == REPORT_DESCRIPTOR)
{
CopyRoutine = DinkUsbGetReportDescriptor;
}
//Get the HID descriptor
else if (pInformation->USBwValue1 == HID_DESCRIPTOR_TYPE)
{
CopyRoutine = DinkUsbGetHIDDescriptor;
}
}
/*** GET_PROTOCOL ***/
else if ((Type_Recipient == (CLASS_REQUEST | INTERFACE_RECIPIENT))
&& RequestNo == GET_PROTOCOL)
{
CopyRoutine = DinkUsbGetProtocolValue; //Get protocol value
}
if (CopyRoutine == NULL)
{
return USB_UNSUPPORT;
}
pInformation->Ctrl_Info.CopyData = CopyRoutine;
pInformation->Ctrl_Info.Usb_wOffset = 0;
(*CopyRoutine)(0);
return USB_SUCCESS;
}
RESULT DinkUsbSetProtocol(void)
{
u8 wValue0 = pInformation->USBwValue0;
ProtocolValue = wValue0;
return USB_SUCCESS;
}
RESULT DinkUsbNoDataSetup(u8 RequestNo)
{
if ((Type_Recipient == (CLASS_REQUEST | INTERFACE_RECIPIENT))
&& (RequestNo == SET_PROTOCOL))
{
usb_debug_printf("Set protocol\r\n");
return DinkUsbSetProtocol();
}
else
{
return USB_UNSUPPORT;
}
}
RESULT DinkUsbGetInterfaceSetting(u8 Interface, u8 AlternateSetting)
{
if (AlternateSetting > 0) //Configuration quantity
{
usb_debug_printf("Set configuration\r\n");
return USB_UNSUPPORT;
}
else if (Interface > 1) //Number of interfaces
{
usb_debug_printf("Set interface\r\n");
return USB_UNSUPPORT;
}
return USB_SUCCESS;
}
//Get the device descriptor
u8 *DinkUsbGetDeviceDescriptor(u16 Length)
{
usb_debug_printf("Get device descriptor\r\n");
return Standard_GetDescriptorData(Length, &Device_Descriptor);
}
//Configuration descriptor
u8 *DinkUsbGetConfigDescriptor(u16 Length)
{
usb_debug_printf("Get configuration descriptor\r\n");
return Standard_GetDescriptorData(Length, &Config_Descriptor);
}
// string descriptor
u8 *DinkUsbGetStringDescriptor(u16 Length)
{
u8 wValue0 = pInformation->USBwValue0;
usb_debug_printf("Get string descriptor%d\r\n",wValue0);
if (wValue0 > 4)
{
return NULL;
}
else
{
return Standard_GetDescriptorData(Length, &String_Descriptor[wValue0]); //Return string descriptor
}
}
//Upload the device status to the configuration data
void DinkUsbSetConfiguration(void)
{
DEVICE_INFO *pInfo = &Device_Info;
usb_debug_printf("Set configuration\r\n");
if (pInfo->Current_Configuration != 0)
{
bDeviceState = CONFIGURED;
}
}
//Set the address to upload
void DinkUsbSetDeviceAddress (void)
{
usb_debug_printf("Set address\r\n");
bDeviceState = ADDRESSED;
}
The two most core functions are reset and initialization. When resetting, the endpoint should be configured, and it is best to enable reception, otherwise data cannot be received (you can enable it yourself later). Then there is the endpoint processing function usb_endp.c
#include "usb_endp.h"
//Set to 1 if sending is complete Set to 0 if sending is not complete
u8 sendOk = 1;
//When receiving data, this setting should be set to 1, and changed to 0 after data processing is completed
u8 ReceiveOk = 0;
void EP1_IN_Callback(void)
{
//Callback function for the device to send data to the host
sendOk = 1; //1 if the sending is successful
SetEPTxStatus(ENDP1, EP_TX_NAK); //Send successfully and wait for the second time to be set as valid
}
void EP1_OUT_Callback(void)
{
//After receiving data once, wait for data processing and set the receiving response to NAK
//Set to VALID after processing is completed
SetEPRxStatus(ENDP1, EP_RX_NAK); //NAK reception
ReceiveOk = 1; //Data flag is 1
}
To enable these functions, you need to turn on the endpoint response function
In addition, the microcontroller should process or send data, which is done by the usb_data_process.h file.
#include "usb_data_process.h"
//HID sends data
//Return 1 if sending fails, return 0 if sending succeeds
u8 HID_Send_Data(u8* buffer,u8 length)
{
if(sendOk == 1)
{
if(length == 0)
{
SetEPTxStatus(ENDP1, EP_TX_NAK); //Do not send
}
else
{
UserToPMABufferCopy(buffer, GetEPTxAddr(ENDP1), length);
SetEPTxCount(ENDP1, length);
SetEPTxValid(ENDP1); //Enable sending
sendOk = 0; //Set the sending status to unfinished, waiting for the sending callback function to send data to the host
}
return 0;
}
else
{
return 1; //The last data has not been sent out, so this sending fails
}
}
//HID receive data processing
u8 HID_Receive_Data(u8* buffer)
{
u16 length = 0; //Get the length of the received data
u8 i = 0;
if(ReceiveOk == 1)//There is data
{
length = GetEPRxCount(ENDP1);
if(length == 0)return 0;
else
{
PMAToUserBufferCopy(buffer, GetEPRxAddr(ENDP1), length);
SetEPRxValid(ENDP1); //Enable receiving
ReceiveOk = 0;
printf("hid receive : ");
for(i = 0; i < length; i++)
{
printf("%c ",buffer[i]);
}
printf("\r\n");
return length; //Return the received data
}
}
else
{
//No data, directly 0
return 0;
}
}
Previous article:HCSR04 Ultrasonic Sensor Driver
Next article:STM32 general timer TIMx system understanding
Recommended ReadingLatest update time:2024-11-16 19:45
- Popular Resources
- Popular amplifiers
- Virtualization Technology Practice Guide - High-efficiency and low-cost solutions for small and medium-sized enterprises (Wang Chunhai)
- usb_host_device_code
- Transplantation of real-time operating system RT-ThreadSmart on STM32MP1
- Design of switching power supply with adjustable output voltage based on STM32
Professor at Beihang University, dedicated to promoting microcontrollers and embedded systems for over 20 years.
- Innolux's intelligent steer-by-wire solution makes cars smarter and safer
- 8051 MCU - Parity Check
- How to efficiently balance the sensitivity of tactile sensing interfaces
- What should I do if the servo motor shakes? What causes the servo motor to shake quickly?
- 【Brushless Motor】Analysis of three-phase BLDC motor and sharing of two popular development boards
- Midea Industrial Technology's subsidiaries Clou Electronics and Hekang New Energy jointly appeared at the Munich Battery Energy Storage Exhibition and Solar Energy Exhibition
- Guoxin Sichen | Application of ferroelectric memory PB85RS2MC in power battery management, with a capacity of 2M
- Analysis of common faults of frequency converter
- In a head-on competition with Qualcomm, what kind of cockpit products has Intel come up with?
- Dalian Rongke's all-vanadium liquid flow battery energy storage equipment industrialization project has entered the sprint stage before production
- Allegro MicroSystems Introduces Advanced Magnetic and Inductive Position Sensing Solutions at Electronica 2024
- Car key in the left hand, liveness detection radar in the right hand, UWB is imperative for cars!
- After a decade of rapid development, domestic CIS has entered the market
- Aegis Dagger Battery + Thor EM-i Super Hybrid, Geely New Energy has thrown out two "king bombs"
- A brief discussion on functional safety - fault, error, and failure
- In the smart car 2.0 cycle, these core industry chains are facing major opportunities!
- The United States and Japan are developing new batteries. CATL faces challenges? How should China's new energy battery industry respond?
- Murata launches high-precision 6-axis inertial sensor for automobiles
- Ford patents pre-charge alarm to help save costs and respond to emergencies
- New real-time microcontroller system from Texas Instruments enables smarter processing in automotive and industrial applications
- E-ON China Online Cloud Sharing Laboratory——"Microcomputer Principles and Applications"
- Improving Power Supply Network Efficiency Using Fixed Ratio Converters
- Large amplitude sine wave signal frequency acquisition circuit
- 【Share】Output adjustable
- [N32L43x Review] 6. Software and Hardware I2C Driver for 0.96-inch OLED
- GD32E231 DIY Competition (2) - Unboxing and Testing
- Some bytes of code in the application memory area of MSP430FR5949IDAR FRAM are tampered
- Overview of RFID Anti-collision Technology
- MSP430 low power mode - while loop fails
- [MM32 eMiniBoard Review] Give feedback on this board