STM32_USB

概述

本文是使用HAL库的USB驱动

因为官方cubeMX生成的hal库做组合设备时过于繁琐

所以这里使用某大神的插件,可以集成在cubeMX里自动生成组合设备

有小bug会覆盖生成文件里自己写的内容,所以生成一次后注意保存

插件安装

下载地址 https://github.com/alambe94/I-CUBE-USBD-Composite/releases/tag/V01.00.03

下载pack文件

打开cubeMX

image-20250327112752157

点击这个 之后选择下载的文件 安装

image-20250327112827185

出现这个即为安装成功

image-20250327113051627

生成代码

打开USB 设为设备模式,打开中断

image-20250327143923426

注意设置时钟树

USB需要较为精确的时钟 建议用外部晶振

image-20250327144003378

选择库文件

image-20250327150139349

目前打勾的这俩必选

其余根据需要选择

image-20250327150230465

根据需要选择,注意要在上步开启的库文件中选

image-20250327150308199

在初始化后加入这个函数

MX_USB_DEVICE_Init();

image-20250327161540514

可以去usbd_desc.c里设置VID/PID 某些名称等参数,不同设备有些许不同

image-20250328203448138

虚拟串口(CDC)

概述

串口名是由PC的驱动来决定的,没法在STM32端设置

设置

打开这个

可以在这里设置虚拟的串口数量

注意一个串口要占用2个IN端点和一个OUT端点

image-20250328133231954

也可以设置AL94.I-CUBE-USBD-COMPOSITE_conf.h文件中的_USBD_CDC_ACM_COUNT设置虚拟的CDC串口数量

发送

开始发送

类型名称功能
uint8_tch通道
uint8_t *Buf缓冲区地址
uint16_tLen发送数量
uint8_t输出已经发送的数量
uint8_t CDC_Transmit(uint8_t ch, uint8_t *Buf, uint16_t Len)

发送完成

类型名称功能
uint8_tcdc_ch通道
uint8_t *Buf缓冲区地址
uint32_tLen发送数量
uint8_tepnum端点号
uint8_t错误码
int8_t CDC_TransmitCplt(uint8_t cdc_ch, uint8_t *Buf, uint32_t *Len, uint8_t epnum)

接收

类型名称功能
uint8_tcdc_ch通道
uint8_t *Buf缓冲区地址
uint32_tLen发送数量
int8_t错误码

接收到数据会自动调用这函数

int8_t CDC_Receive(uint8_t cdc_ch, uint8_t *Buf, uint32_t *Len)

在这个函数里调用这俩句,来接收下个数据包

  USBD_CDC_SetRxBuffer(cdc_ch, &hUsbDevice, &Buf[0]);USBD_CDC_ReceivePacket(cdc_ch, &hUsbDevice);

控制函数

类型名称功能
uint8_tcdc_ch通道
uint8_tcmd命令类型
uint8_t *pbuf命令缓冲区
uint16_tlength长度
int8_t错误码
int8_t CDC_Control(uint8_t cdc_ch, uint8_t cmd, uint8_t *pbuf, uint16_t length)

cmdCDC_SET_LINE_CODING时收到来自主机的命令

具体内容生成的函数中有注释

人体工学设备(HID)

概述

全部使用自定义HID设备

根据不同设备设置描述符即可

HID间的复合直接复制就行

eg:鼠标+键盘 直接把鼠标的描述符和键盘的描述符写到一起即可

建立工程

image-20250329144505389

image-20250329144515151

设置

usbd_customhid.h

名称功能
CUSTOM_HID_STR_DESCHID描述
CUSTOM_HID_EPIN_SIZE输入缓冲大小(一般设为64)
CUSTOM_HID_EPOUT_SIZE输出缓冲大小(一般设为64)
USBD_CUSTOMHID_OUTREPORT_BUF_SIZEHID缓冲区(一般设为64)
CUSTOM_HID_FS_BINTERVAL包间隔时间

usbd_custom_hid_if.c

CUSTOM_HID_ReportDesc 设置HID描述符 USBD_CUSTOM_HID_REPORT_DESC_SIZE 同时也要设置配置符大小

image-20250329154822531

APIs

发送数据
类型名称功能
USBD_HandleTypeDef *pdevUSB句柄
uint8_t *report缓冲区
uint16_tlen数据长度
uint8_t错误码
  uint8_t USBD_CUSTOM_HID_SendReport(USBD_HandleTypeDef *pdev,uint8_t *report, uint16_t len);
接收数据回调

usbd_custom_hid_if.c

类型名称功能
uint8_tevent_idx
uint8_tstate
uint8_t错误码
int8_t CUSTOM_HID_OutEvent(uint8_t event_idx, uint8_t state)

在内部调用以获取数据

USBD_CUSTOM_HID_HandleTypeDef *hhid = (USBD_CUSTOM_HID_HandleTypeDef *)HZ_DAP_USB_Handle.pClassData_HID_Custom;
// hhid->Report_buf;
USBD_CUSTOM_HID_ReceivePacket(&HZ_DAP_USB_Handle);

鼠标

鼠标的配置描述符

0x05, 0x01, 
0x09, 0x02, 
0xa1, 0x01, 0x85, 0x02, // 报告ID (2)0x09, 0x01, 
0xa1, 0x00, 
0x05, 0x09, 
0x19, 0x01, 
0x29, 0x03,
0x15, 0x00, 
0x25, 0x01, 
0x95, 0x03, 
0x75, 0x01,
0x81, 0x02, 
0x95, 0x01, 
0x75, 0x05,
0x81, 0x03, 
0x95, 0x03,
0x75, 0x08,
0x05, 0x01,
0x09, 0x30,
0x09, 0x31,
0x09, 0x38,
0x15, 0x81,
0x25, 0x7f,
0x81, 0x06,
0xc0,
0xc0 

需要发送的命令 含义

位置功能
Bit0报告ID
Bit1[0]左键(0未按1按下)
Bit1[1]右键(0未按1按下)
Bit1[2]中键(0未按1按下)
Bit2x轴(正右负左 -127~127)
Bit3y轴(正下负上 -127~127)
Bit4滚动(正上负下 -127~127)

这个函数是自己封装的

extern USBD_HandleTypeDef hUsbDevice;/*** @brief 控制鼠标* @param key_l 左键(仅bit0 0未按1按下)* @param key_r 右键(仅bit0 0未按1按下)* @param key_m 中键(仅bit0 0未按1按下)* @param x x轴(正右负左 -127~127)* @param y y轴(正下负上 -127~127)* @param ec 滚动(正上负下 -127~127)* @author HZ12138* @date 2025-03-28 20:17:39*/
void HZ_Mouse_set(uint8_t key_l, uint8_t key_r, uint8_t key_m, int8_t x, int8_t y, int8_t ec)
{uint8_t buf[5];key_l &= 0x01;key_r &= 0x01;key_m &= 0x01;buf[0] = 0x02; // 报告ID 鼠标是0x02buf[1] = (key_m << 2) | (key_r << 1) | (key_l << 0);buf[2] = (uint8_t)x;buf[3] = (uint8_t)y;buf[4] = (uint8_t)ec;USBD_CUSTOM_HID_SendReport(&hUsbDevice, buf, 5);
}

键盘

描述符

0x85后面跟的是报告ID 0保留

0x05, 0x01, // USAGE_PAGE (Generic Desktop)
0x09, 0x06, // USAGE (Keyboard)
0xa1, 0x01, // COLLECTION (Application)0x85, 0x01, // 报告ID (1)0x05, 0x07, //   USAGE_PAGE (Keyboard)
0x19, 0xe0, //   USAGE_MINIMUM (Keyboard LeftControl)
0x29, 0xe7, //   USAGE_MAXIMUM (Keyboard Right GUI)
0x15, 0x00, //   LOGICAL_MINIMUM (0)
0x25, 0x01, //   LOGICAL_MAXIMUM (1)
0x75, 0x01, //   REPORT_SIZE (1)
0x95, 0x08, //   REPORT_COUNT (8)
0x81, 0x02, //   INPUT (Data,Var,Abs)
0x95, 0x01, //   REPORT_COUNT (1)
0x75, 0x08, //   REPORT_SIZE (8)
0x81, 0x03, //   INPUT (Cnst,Var,Abs)
0x95, 0x05, //   REPORT_COUNT (5)
0x75, 0x01, //   REPORT_SIZE (1)
0x05, 0x08, //   USAGE_PAGE (LEDs)
0x19, 0x01, //   USAGE_MINIMUM (Num Lock)
0x29, 0x05, //   USAGE_MAXIMUM (Kana)
0x91, 0x02, //   OUTPUT (Data,Var,Abs)
0x95, 0x01, //   REPORT_COUNT (1)
0x75, 0x03, //   REPORT_SIZE (3)
0x91, 0x03, //   OUTPUT (Cnst,Var,Abs)
0x95, 0x06, //   REPORT_COUNT (6)
0x75, 0x08, //   REPORT_SIZE (8)
0x15, 0x00, //   LOGICAL_MINIMUM (0)
0x25, 0x65, //   LOGICAL_MAXIMUM (101)
0x05, 0x07, //   USAGE_PAGE (Keyboard)
0x19, 0x00, //   USAGE_MINIMUM (Reserved (no event indicated))
0x29, 0x65, //   USAGE_MAXIMUM (Keyboard Application)
0x81, 0x00, //   INPUT (Data,Ary,Abs)
0xc0        // END_COLLECTION

数据内容

Bit2~8 发送的是按下的键号 如果不按下写0即可

位置功能
Bit0报告ID
Bit1[0]左CTRL(0未按1按下)
Bit1[1]左SHIFT(0未按1按下)
Bit1[2]左ALT(0未按1按下)
Bit1[3]左GUI(0未按1按下)
Bit1[4]右CTRL(0未按1按下)
Bit1[5]右SHIFT(0未按1按下)
Bit1[6]右ALT(0未按1按下)
Bit1[7]右GUI(0未按1按下)
Bit2保留(0x00)
Bit3按键1
Bit4按键2
Bit5按键3
Bit6按键4
Bit7按键5
Bit8按键6

封装的发送按键函数和单个按键按下函数

extern USBD_HandleTypeDef hUsbDevice;
/*** @brief 发送按键* @param keys 按键* @param key_num 数量最大6个* @param ctrl_l 如名(0未按1按下)* @param shift_l 如名(0未按1按下)* @param alt_l 如名(0未按1按下)* @param gui_l 如名(0未按1按下)* @param ctrl_r 如名(0未按1按下)* @param shift_r 如名(0未按1按下)* @param alt_r 如名(0未按1按下)* @param gui_r 如名(0未按1按下)* @author HZ12138* @date 2025-03-28 22:52:09*/
void HZ_KeyBoard_set(uint8_t *keys, uint8_t key_num,uint8_t ctrl_l, uint8_t shift_l, uint8_t alt_l, uint8_t gui_l,uint8_t ctrl_r, uint8_t shift_r, uint8_t alt_r, uint8_t gui_r)
{uint8_t buf[9];ctrl_l &= 0x01;shift_l &= 0x01;alt_l &= 0x01;gui_l &= 0x01;ctrl_r &= 0x01;shift_r &= 0x01;alt_r &= 0x01;gui_r &= 0x01;buf[0] = 0x01;buf[1] = (gui_r << 7) | (alt_r << 6) | (shift_r << 5) | (ctrl_r << 4) |(gui_l << 3) | (alt_l << 2) | (shift_l << 1) | (ctrl_l << 0);buf[2] = 0x00;for (int i = 0; i < key_num; i++)buf[3 + i] = keys[i];USBD_CUSTOM_HID_SendReport(&hUsbDevice, buf, 9);
}
/*** @brief 发送单个按键 * @param key 按键值* @author HZ12138* @date 2025-03-28 22:52:12*/
void HZ_KeyBoard_one_key(uint8_t key)
{uint8_t temp[6];temp[0] = key;HZ_KeyBoard_set(temp, 6, 0, 0, 0, 0, 0, 0, 0, 0);
}

键码对应表,使用HID码

按键名称HID码虚拟键码
ESC41 [0X29]27 [0x1B]
F158 [0X3a]112 [0x70]
F259 [0X3b]113 [0x71]
F360 [0X3c]114 [0x72]
F461 [0X3d]115 [0x73]
F562 [0X3e]116 [0x74]
F663 [0X3f]117 [0x75]
F764 [0X40]118 [0x76]
F865 [0X41]119 [0x77]
F966 [0X42]120 [0x78]
F1067 [0X43]121 [0x79]
F1168 [0X44]122 [0x7A]
F1269 [0X45]123 [0x7B]
Esc41 [0X29]27 [0x1B]
Back (回退)42 [0X2a]8 [0x08]
Tab43 [0X2b]9 [0x09]
CapLck (大小写)57 [0X39]20 [0x14]
Enter (回车)40 [0X28]13 [0x0D]
Space (空格)44 [0X2c]32 [0x20]
Scroll71 [0X47]145 [0x91]
Pause(暂停)72 [0X48]19 [0x13]
Insert (插入)73 [0X49]45 [0x2D]
PrintScr (截屏)70 [0X46]44 [0x2C]
Delete (删除)76 [0X4c]46 [0x2E]
Home (首页)74 [0X4a]36 [0x24]
End (结尾)77 [0X4d]35 [0x23]
PageUp (上一页)75 [0X4b]33 [0x21]
PageDn (下一页)78 [0X4e]34 [0x22]
Left (左)80 [0X50]37 [0x25]
Up (上)82 [0X52]38 [0x26]
Right (右)79 [0X4f]39 [0x27]
Down (下)81 [0X51]40 [0x28]
Num0 (小键盘)98 [0X62]96 [0x60]
Num1 (小键盘)89 [0X59]97 [0x61]
Num2 (小键盘)90 [0X5a]98 [0x62]
Num3 (小键盘)91 [0X5b]99 [0x63]
Num4 (小键盘)92 [0X5c]100 [0x64]
Num5 (小键盘)93 [0X5d]101 [0x65]
Num6 (小键盘)94 [0X5e]102 [0x66]
Num7 (小键盘)95 [0X5f]103 [0x67]
Num8 (小键盘)96 [0X60]104 [0x68]
Num9 (小键盘)97 [0X61]105 [0x69]
NumAdd (加号)87 [0X57]107 [0x6B]
NumSub (减号)86 [0X56]109 [0x6D]
NumMult (乘号)85 [0X55]106 [0x6A]
NumDiv (除号)84 [0X54]111 [0x6F]
NumDecim (点)99 [0X63]110 [0x6E]
NumLock (数字锁定键)83 [0X53]144 [0x90]
Ctrl1 [0X01]17 [0x11]
LCtrl (左CTR)1 [0X01]162 [0xA2]
RCtrl16 [0X10]163 [0xA3]
Shift2 [0X02]16 [0x10]
LShift2 [0X02]160 [0xA0]
RShift32 [0X20]161 [0xA1]
Alt4 [0X04]18 [0x12]
LAlt4 [0X04]164 [0xA4]
RAlt64 [0X40]165 [0xA5]
WIN8 [0X08]91 [0x5B]
LWIN8 [0X08]91 [0x5B]
RWIN128 [0X80]92 [0x5C]
A4 [0X04]65 [0x41]
B5 [0X05]66 [0x42]
C6 [0X06]67 [0x43]
D7 [0X07]68 [0x44]
E8 [0X08]69 [0x45]
F9 [0X09]70 [0x46]
G10 [0X0a]71 [0x47]
H11 [0X0b]72 [0x48]
I12 [0X0c]73 [0x49]
J13 [0X0d]74 [0x4A]
K14 [0X0e]75 [0x4B]
L15 [0X0f]76 [0x4C]
M16 [0X10]77 [0x4D]
N17 [0X11]78 [0x4E]
O18 [0X12]79 [0x4F]
P19 [0X13]80 [0x50]
Q20 [0X14]81 [0x51]
R21 [0X15]82 [0x52]
S22 [0X16]83 [0x53]
T23 [0X17]84 [0x54]
U24 [0X18]85 [0x55]
V25 [0X19]86 [0x56]
W26 [0X1a]87 [0x57]
X27 [0X1b]88 [0x58]
Y28 [0X1c]89 [0x59]
Z29 [0X1d]90 [0x5A]
039 [0X27]48 [0x30]
130 [0X1e]49 [0x31]
231 [0X1f]50 [0x32]
332 [0X20]51 [0x33]
433 [0X21]52 [0x34]
534 [0X22]53 [0x35]
635 [0X23]54 [0x36]
736 [0X24]55 [0x37]
837 [0X25]56 [0x38]
938 [0X26]57 [0x39]

大容量存储(MSC)

建立工程

image-20250404233158298

image-20250404233251426

设置

usbd_msc.h 中的 MSC_MEDIA_PACKET 要设为扇区(sector)大小

w25Qxx为4096 SD卡为512

STORAGE_LUN_NBR为虚拟磁盘卷数量 一般把一个设备设为一个卷 设置这个可以虚拟出多个磁盘

usbd_storage_if.cSTORAGE_BLK_NBR 为最小操作单元数量(一般写扇区数量)

usbd_storage_if.cSTORAGE_BLK_SIZ 为最小操作单元大小 (一般写扇区大小) 单位(Byte)

这俩相乘即可得到总大小 单位(Byte)

注意这俩的blk所指的块与FLASH的不同,也是我写成最小操作单元的原因

可以修改这个最后三项来更改显示名称

usbd_storage_if.cSTORAGE_Inquirydata

image-20250405214854576

APIs

存储读取(必写)

描述名称功能
uint8_tlun卷标
uint8_t *buf缓冲区
uint32_tblk_addr最小操作单元起始地址(*STORAGE_BLK_SIZ后得到Byte起始地址 )
uint16_tblk_len最小操作单元的数量(*STORAGE_BLK_SIZ后得到Byte数量 )
int8_t输出错误码

usbd_storage_if.c

需要根据自己内容填写

int8_t STORAGE_Read(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len)

存储写入(必写)

描述名称功能
uint8_tlun卷标
uint8_t *buf缓冲区
uint32_tblk_addr最小操作单元起始地址(*STORAGE_BLK_SIZ后得到Byte起始地址 )
uint16_tblk_len最小操作单元的数量(*STORAGE_BLK_SIZ后得到Byte数量 )
int8_t输出错误码

usbd_storage_if.c

需要根据自己内容填写

int8_t STORAGE_Write(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/77404.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【文献阅读】Capabilities of Gemini Models in Medicine

​ Google DeepMind Google Research 发表于 2024-04-29 相关链接&#xff1a; 数据集&#xff1a;https://huggingface.co/datasets/katielink/med-gemini-medqa-relabeled 注&#xff1a;长EHR是长的电子健康记录&#xff08;Electronic Health Record&#xff09; 未开…

网络安全小知识课堂(最终完结版)

网络安全入门 &#xff1a;从 “小白” 到 “守护者” 的蜕变之旅 写在完结之际 历经 13 篇的深度探索&#xff0c;我们从 DDoS 攻击的 “流量洪水” 一路闯关到 HTTPS 的 “加密堡垒”&#xff0c;揭开了网络安全世界的层层面纱。感谢每一位读者的陪伴与互动&#xff0c;你们…

Php laravel 留言板 curd 实战

1. 项目创建 首先我们用composer创建项目 &#xff0c; composer会根据当前的php版本 帮我们选择支持的最高版本 composer create-project --prefer-dist laravel/laravel myblog laravel新版本比较激进 &#xff0c;需要最低 php7 支持 2. 项目配置 数据库配置 &#xff0c…

HTTP 压力测试工具autocannon(AI)

简介 autocannon 是一款基于 Node.js 的高性能 HTTP 压力测试工具&#xff0c;适用于评估 Web 服务的并发处理能力和性能瓶颈。 一、工具特点 高性能‌&#xff1a;利用 Node.js 异步非阻塞机制模拟高并发请求‌。‌实时监控‌&#xff1a;测试过程中动态展示请求统计和性能…

LVM 扩容详解

目录 一、LVM扩容 1. 查看磁盘分区情况&#xff1a; 2. 查看pv、vg、lv 情况 3. 将新硬盘分区初始化 4. 将初始化后的分区添加到VG中 5. 查看逻辑卷的设备路径 6. VG分配给lv 二、扩展文件系统 1.确认文件系统类型 三、检验 一、LVM扩容 1. 查看磁盘分区情况&#xff1a; …

每日一题(小白)数组娱乐篇21

由于题意可知我们是要将对应的数字转换为英文&#xff0c;我们要考虑两点一个是进制的转换&#xff0c;也就是类似于我们的十进制一到9就多一位&#xff0c;这里的进制就是Z进制也就是27进制一旦到26下一位则进位&#xff1b;另一方面要考虑数字的转换也就是1~26对应A~Z。解决上…

python爬虫:喜马拉雅案例(破解sign值)

声明&#xff1a; 本文章中所有内容仅供学习交流使用&#xff0c;不用于其他任何目的&#xff0c;严禁用于商业用途和非法用途&#xff0c;否则由此产生的一切后果均与作者无关&#xff01; 根据上一篇文章&#xff0c;我们破解了本网站的&#xff0c;手机号和密码验证&#x…

深入探讨:Spring 如何接入 DeepSeek​

​在当今数字化浪潮中&#xff0c;人工智能&#xff08;AI&#xff09;技术的迅猛发展深刻地改变着各个领域的技术格局。Java 作为一种广泛应用于企业级开发的编程语言&#xff0c;其生态系统也在不断演进以适应 AI 技术的融入。Spring 框架&#xff0c;作为 Java 生态中的中流…

VSCode运行,各类操作缓慢,如何清理

VSCode写代码&#xff0c;随着项目逐步进展&#xff0c;代码量在增加&#xff0c;依赖的第三方头文件也在增加&#xff0c; 先是发现代码提示的速度变慢&#xff0c; 后来格式化代码速度太慢 然后c/c代码的语法检查有时候压根就失败&#xff0c;来个错误提示 还有source contro…

Elasticsearch:加快 HNSW 图的合并速度

作者&#xff1a;来自 Elastic Thomas Veasey 及 Mayya Sharipova 过去&#xff0c;我们曾讨论过搜索多个 HNSW 图时所面临的一些挑战&#xff0c;以及我们是如何缓解这些问题的。当时&#xff0c;我们也提到了一些计划中的改进措施。本文正是这项工作的成果汇总。 你可能会问…

人事|人事管理系统|基于Springboot+vue的人事管理系统设计与实现(源码+数据库+文档)

人事管理系统 目录 基于Springboot的人事管理系统设计与实现 一、前言 二、系统功能设计 三、系统实现 1、管理员登录 2、员工管理 3、公告信息管理 4、公告类型管理 5、培训管理 6、培训类型管理 四、数据库设计 1、实体ER图 五、核心代码 六、论文参考 七、最新…

2.4GHz射频前端噪声系数优化架构

2.4GHz射频前端电路架构由信号处理链路、硬件模块及性能规范构成&#xff0c;其系统组成与参数要求如下&#xff1a; 一、信号发射链路‌ 数字基带信号通过DAC转换为模拟信号‌ 调制电路将信号加载至本地振荡器生成的2.4GHz载波‌ 功率放大器将信号强度提升至20-25dBm范围‌ …

开源 LLM 应用开发平台 Dify 全栈部署指南(Docker Compose 方案)

开源 LLM 应用开发平台 Dify 全栈部署指南&#xff08;Docker Compose 方案&#xff09; 一、部署环境要求与前置检查 1.1 硬件最低配置 组件要求CPU双核及以上内存4GB 及以上磁盘空间20GB 可用空间 1.2 系统兼容性验证 ✅ 官方支持系统&#xff1a; Ubuntu 20.04/22.04 L…

Trae AI 保姆级教程:从安装到调试全流程指南

Trae AI 保姆级教程&#xff1a;从安装到调试全流程指南 Trae AI 是字节跳动推出的一款 AI 原生集成开发环境(IDE)&#xff0c;专为中文开发者设计&#xff0c;集成了 Claude 3.5 和 GPT-4o 等先进 AI 模型&#xff0c;支持通过自然语言交互实现代码生成、项目构建与调试。本教…

博物馆小程序怎么做?从0到1打造数字化文化窗口

博物馆小程序怎么做&#xff1f;从0到1打造数字化文化窗口 一、行业痛点&#xff1a;传统博物馆的数字化困局 在数字化浪潮下&#xff0c;传统博物馆普遍面临三大挑战&#xff1a; ​​客流受限​​&#xff1a;线下接待能力有限&#xff0c;难以触达更广泛人群 ​​互动单一…

基于 Netty 框架的 Java TCP 服务器端实现,用于启动一个 TCP 服务器来处理客户端的连接和数据传输

代码&#xff1a; package com.example.tpson_tcp;import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelOption; import io.netty.channel.EventLoopGroup; imp…

深入解析原生鸿蒙中的 RN 日志系统:从入门到精通!

全文目录&#xff1a; 开篇语&#x1f4d6; 目录&#x1f3af; 前言&#xff1a;鸿蒙日志系统究竟有多重要&#xff1f;&#x1f6e0;️ 鸿蒙 RN 日志系统的基础结构&#x1f4dc; 1. 日志的作用⚙️ 2. 日志分类 &#x1f527; 如何在鸿蒙 RN 中使用日志系统&#x1f58b;️ 1…

算法训练营Day01(二分 双指针)

704. 二分查找 - 力扣&#xff08;LeetCode&#xff09; 关于二分查找 最重要的是要处理好边界问题&#xff0c;每次写完边界可以带入特殊值进行测试确定区间的不变量是什么&#xff1f;比如区间的左闭右闭&#xff0c;和左闭右开&#xff0c;每次二分完的新区间&#xff0c;一…

shadcn 使用步骤与注意点

目录 一、shadcn ui 二、使用流程 1.安装 2.颜色与主题 3.引用blocks 三、使用注意点 四、推荐搭配工具 五、总结 一、shadcn ui 官网&#xff1a;Build your component library - shadcn/ui 为何选择它&#xff1f;因为它是一个基于 Tailwind CSS Radix UI 的组件集…

STM32CubeMX-H7-12-IIC读写MPU6050模块(中)-MPU6050模块详解以及软件IIC驱动

前言 上一篇我们已经完成对IIC代码基本框架的编写&#xff0c;以及获取MPU6050的ID&#xff0c;接下来我们逐一分析这个模块的功能&#xff0c;并用IIC驱动 建议看完上一篇再来看这篇 MPU6050寄存器介绍 1.电源管理寄存器&#xff08;PWR_MGMT_1&#xff0c;地址&#xff1a;0…