(5)STM32 USB设备开发-USB键盘

讲解视频:2、USB键盘-下_哔哩哔哩_bilibili

例程:STM32USBdevice: 基于STM32的USB设备例子程序 - Gitee.com

本篇为使用使用STM32模拟USB键盘的例程,没有知识,全是实操,按照步骤就能获得一个STM32的USB键盘。本例子是在野火F103MINI开发板上验证的,如果代码中出现一些外设的配置,可以参考野火F103MINI开发板原理图对照。

设置外部晶振,必须要使用外部晶振,因为USB控制器需要48M的系统时钟,内部晶振无法倍频出48M。

配置外部时钟

配置调试口和系统基准源

开启USB设备

中间件中设备USB设备类型

标蓝色的部分需要根据你之前有没有使用过这两VID和PID,如果使用过最好换一下,避免使用之前的驱动引起一些奇奇怪怪的问题。

我使用了freertos v2

配置外部系统时钟

配置独立C和H文件

下面就讲一下设备描述符,设备描述符就像一个身份证一样,它包含了这个USB设备的全部信息,说明了USB设备的通用信息,包含应用到全部设备和所有设备配置的信息。USB设备只有一个设备描述符。设备描述符是在设备连接时主机读取的第一个描述符。设备描述符所含的信息,被主机用来取得设备的额外内容。设备描述符提供了关于设备、设备的配置以及任何设备所归属的类的信息。

如果你对USB协议足够了解,可以手写,但是还可以使用官方提供了一个专门的工具来生成,下载地址:HID Descriptor Tool | USB-IF

界面如下:

我们通过菜单的 FILE/Open,弹出需要打开的HID报告描述符。

这里我们选择键盘的报告描述符:keybrd.hid

如需要修改某些值,可以双击选中需要修改的行,如我们双击 INPUT(Data,Var,Abs) 81 02,弹出其修改页:

如果需要添加item可以在界面左侧 HID Items 栏中是一系列的 Item,通过双击需要的 Item 添加到右侧 Report Descriptor 中。添加过程中该工具会根据不同的 Item 让你选择或者填入值。

点击 Parser Descriptor 就会显示解析的结果

如果描述符有错,会给予提示,例如把例子中的 END_COLLECTION 去掉,再进行校验就会有如下提示:

修改完成以后,点击“File -> Save As”,保存为.h格式。保存完成后打开,效果如下:

char ReportDescriptor[63] = {0x05, 0x01,                    // USAGE_PAGE (Generic Desktop)0x09, 0x06,                    // USAGE (Keyboard)0xa1, 0x01,                    // COLLECTION (Application)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
};

在上面的描述符中可以看到INPUT给电脑的是一个大小为REPORT_SIZE (8)的Ary数组的Data变量,

所以我们需要创建一个8字节的数组。

键盘发送给PC的数据每次8个字节

BYTE1 BYTE2 BYTE3 BYTE4 BYTE5 BYTE6 BYTE7 BYTE8

定义分别是:

BYTE0 --(0 = OFF,1 = ON,CONSTANT为保留位)

|--bit0: Left Control是否按下,按下为1

|--bit1: Left Shift 是否按下,按下为1

|--bit2: Left Alt 是否按下,按下为1

|--bit3: Left GUI 是否按下,按下为1

|--bit4: Right Control是否按下,按下为1

|--bit5: Right Shift 是否按下,按下为1

|--bit6: Right Alt 是否按下,按下为1

|--bit7: Right GUI 是否按下,按下为1

BYTE1 -- 为常量值,保留字节

BYTE2--BYTE7 -- 这六个为普通按键

代码需要修改如下:

在Middlewares\ST\STM32_USB_Device_Library\Class\HID\Src\usbd_hid.c文件中添加

__ALIGN_BEGIN static uint8_t HID_KEYBOARD_ReportDesc[HID_KEYBOARD_REPORT_DESC_SIZE]  __ALIGN_END = {0x05, 0x01,                    // USAGE_PAGE (Generic Desktop)0x09, 0x06,                    // USAGE (Keyboard)0xa1, 0x01,                    // COLLECTION (Application)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
};

在文件Middlewares\ST\STM32_USB_Device_Library\Class\HID\Inc\usbd_hid.h中添加

#define HID_KEYBOARD_REPORT_DESC_SIZE 63U

将程序中所有HID_MOUSE_ReportDesc替换为HID_KEYBOARD_ReportDesc

将程序中所有HID_MOUSE_REPORT_DESC_SIZE替换为HID_KEYBOARD_REPORT_DESC_SIZE.

配置描述符需要修改,在文件Middlewares\ST\STM32_USB_Device_Library\Class\HID\Src\usbd_hid.c中

修改端点大小,在文件Middlewares\ST\STM32_USB_Device_Library\Class\HID\Inc\usbd_hid.h中,因为我们上边定义的input是8个字节,所以这里改为8

我是在Core\Src\freertos.c文件中添加了按键检测的代码

    /* init code for USB_DEVICE */MX_USB_DEVICE_Init();/* USER CODE BEGIN StartDefaultTask */uint8_t keyBoard[8] = {0};uint8_t key1Status = 0;uint8_t key2Status = 0;TickType_t xLastFlashTime = osKernelGetTickCount();/* Infinite loop */for (;;) {// 获取当前时间戳TickType_t xCurrentTime = osKernelGetTickCount();// 检查是否已经过了 1 秒(pdMS_TO_TICKS 函数将毫秒转换为系统时钟节拍)if ((xCurrentTime - xLastFlashTime) >= pdMS_TO_TICKS(1000)) {// 切换 LED1 的状态HAL_GPIO_TogglePin(LED1_GPIO_Port, LED1_Pin);// HAL_GPIO_TogglePin(LED2_GPIO_Port, LED2_Pin);// 更新上一次的时间戳xLastFlashTime = xCurrentTime;}if (HAL_GPIO_ReadPin(KEY1_GPIO_Port, KEY1_Pin) == GPIO_PIN_SET) {if (key1Status == 0) {keyBoard[2] = 4;  // 设置a按键状态USBD_HID_SendReport(&hUsbDeviceFS, keyBoard, 8);key1Status = 1;}} else {if (key1Status == 1) {key1Status = 0;keyBoard[2] = 0;  // 清除按键状态USBD_HID_SendReport(&hUsbDeviceFS, keyBoard, 8);}}if (HAL_GPIO_ReadPin(KEY2_GPIO_Port, KEY2_Pin) == GPIO_PIN_SET) {if (key2Status == 0) {keyBoard[2] = 5;  // 设置b按键状态USBD_HID_SendReport(&hUsbDeviceFS, keyBoard, 8);key2Status = 1;}} else {if (key2Status == 1) {key2Status = 0;keyBoard[2] = 0;  // 清除按键状态USBD_HID_SendReport(&hUsbDeviceFS, keyBoard, 8);}}osDelay(1);}/* USER CODE END StartDefaultTask */

当检测到KEY1按下的时候我们在数组2中写入4,对应下图按键Usage ID“a”字母,当检测到KEY2按下的时候我们在数组2中写入5,对应下图按键Usage ID“b”字母。

这里主要是一个发送的函数需要我们来实现,函数名称为:USBD_HID_SendReport,我们可以跳转到这个函数的定义,函数说明

/*** @brief  USBD_HID_SendReport*         Send HID Report* @param  pdev: device instance* @param  buff: pointer to report* @retval status*/
uint8_t USBD_HID_SendReport(USBD_HandleTypeDef  *pdev,uint8_t *report,uint16_t len)

第一个参数为USB设备的枚举,第二个设备为要发送的报文信息,第三个为报文的长度,这里我们先定义一个报文的数组,之后在不断发送数据即可。

第一个参数需要从#include "usbd_def.h"头文件中引出

/* USER CODE BEGIN Variables */
extern USBD_HandleTypeDef hUsbDeviceFS;
/* USER CODE END Variables */

引用头文件

/* USER CODE BEGIN Includes */
#include "usbd_def.h"
#include "usbd_hid.h"
/* USER CODE END Includes */

编译下载后能看到键盘设备

按下按键会发现文本中有字符输出。

键盘灯支持

我们在报告描述符中加入了一个output端点信息,就是灯信息。

我们要在代码中接收到灯信息的消息,因为我们是从鼠标工程的基础上进行修改的,而鼠标只有输入,没有输出,所以我们要增加一些输出端点的代码,内容有点多,下面一个个来。

首先我们在USB_DEVICE\Target\usbd_conf.c文件中需要修改PMA的端点映射,让PMA为输出端点分配内存

在USBD_LL_Init函数中,添加修改如下内容

  /* USER CODE BEGIN EndPoint_Configuration */HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , 0x00 , PCD_SNG_BUF, 0x20);HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , 0x80 , PCD_SNG_BUF, 0x60);/* USER CODE END EndPoint_Configuration *//* USER CODE BEGIN EndPoint_Configuration_HID */HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , 0x81 , PCD_SNG_BUF, 0xA0);HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , 0x01 , PCD_SNG_BUF, 0xE0);/* USER CODE END EndPoint_Configuration_HID */

这里我们看到一共用到了4个端点,分别是输入端点0X80和0X81,输出端点0X00和0X01,其中0X00端点和0X80端点是供USB使用必须有的,0X81和0X01端点则是MSC设备输入输出端点。

那么一共使用了4个端点,按理来说PMA头部的端点描述大小应该是4X8=32(十六进制的0X20)字节,0X20之后的才是各个端点缓冲区。STM32的PMA一共512字节,也就是缓冲区大小一共可以到0x1FF。

输出端点0缓冲区对应0X20,输入端点的缓冲区是0X60,是因为USB全速设备的最大包是64字节(十进制的0X40),所以这里PMA的划分就是:

头部0X20字节为各个端点的描述

0X20地址开始的64字节为输出端点0的缓冲区

0X60地址开始的64字节为输入端点0的缓冲区

0XA0地址开始的64字节为输入端点1的缓冲区

0XE0地址开始的64字节为输出端点1的缓冲区

因为我们要添加一个输出端点的描述,所以我们还需要为这个输出端点准备一些宏,在Middlewares\ST\STM32_USB_Device_Library\Class\HID\Inc\usbd_hid.h文件中添加如下内容。

#define HID_EPIN_ADDR                 0x81U
#define HID_EPOUT_ADDR                0x01U
#define HID_EPIN_SIZE                 0x08U
#define HID_EPOUT_SIZE                0x02U

这里对应了在USBD_LL_Init函数中的端点地址0x01,还有端点大小2字节,其实我们报告描述符中只定义了一个字节,但是STM32是两字节对齐的,所以我们这里也就需要定义为2字节

我们还需要在Middlewares\ST\STM32_USB_Device_Library\Class\HID\Src\usbd_hid.c文件中修改配置描述符USBD_HID_CfgFSDesc,USBD_HID_CfgHSDesc,USBD_HID_OtherSpeedCfgDesc,因为我们需要告诉PC我们还有一个端点。

在每个变量的末尾添加如下代码,看注释应该就能知道是什么意思了。

0x07,          /* bLength: Endpoint Descriptor size */USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: */HID_EPOUT_ADDR,  /*bEndpointAddress: Endpoint Address (OUT)*/0x03, /* bmAttributes: Interrupt endpoint */HID_EPOUT_SIZE,  /* wMaxPacketSize: 2 Bytes max  */0x00,HID_HS_BINTERVAL,  /* bInterval: Polling Interval *//* 41 */

对比如下:

我们会发现添加完成后我们的变量大小改变了,由原来的34个字节增加到了41个字节,所以我们还需要修改变量大小的宏定义。

在Middlewares\ST\STM32_USB_Device_Library\Class\HID\Inc\usbd_hid.h文件中

下面我们开始添加接收函数,让我们能收到灯变化的消息

我们需要在Middlewares\ST\STM32_USB_Device_Library\Class\HID\Src\usbd_hid.c文件中定义USBD_HID_DataOut函数,并且在USBD_HID变量中添加。

在配置描述符中修改端点数量为2,这一步很重要,也是很容易遗漏的一步。

在函数USBD_HID_Init中添加端点初始化代码,并且执行一次USBD_LL_PrepareReceive函数,为接收端点准备接收变量。这里定义了一个uint8_t pbuf[5];全局变量,因为USBD_LL_PrepareReceive函数需要先调用,后从pbuf中拿到结果,拿到后还需要再次调用USBD_LL_PrepareReceive函数,后面会看到。

uint8_t pbuf[5]; // 定义接收数据全局变量
/*** @brief  USBD_HID_Init*         Initialize the HID interface* @param  pdev: device instance* @param  cfgidx: Configuration index* @retval status*/
static uint8_t  USBD_HID_Init(USBD_HandleTypeDef *pdev, uint8_t cfgidx)
{/* Open EP IN */USBD_LL_OpenEP(pdev, HID_EPIN_ADDR, USBD_EP_TYPE_INTR, HID_EPIN_SIZE);pdev->ep_in[HID_EPIN_ADDR & 0xFU].is_used = 1U;/* Open EP OUT */USBD_LL_OpenEP(pdev, HID_EPOUT_ADDR, USBD_EP_TYPE_INTR, HID_EPOUT_SIZE);pdev->ep_out[HID_EPOUT_ADDR & 0xFU].is_used = 1U;pdev->pClassData = USBD_malloc(sizeof(USBD_HID_HandleTypeDef));if (pdev->pClassData == NULL){return USBD_FAIL;}else{USBD_LL_PrepareReceive(pdev, HID_EPOUT_ADDR , pbuf, HID_EPOUT_SIZE);}((USBD_HID_HandleTypeDef *)pdev->pClassData)->state = HID_IDLE;return USBD_OK;
}

在USBD_HID_DeInit函数中添加端点关闭函数。

static uint8_t  USBD_HID_DeInit(USBD_HandleTypeDef *pdev,uint8_t cfgidx)
{/* Close HID EPs */USBD_LL_CloseEP(pdev, HID_EPIN_ADDR);pdev->ep_in[HID_EPIN_ADDR & 0xFU].is_used = 0U;USBD_LL_CloseEP(pdev, HID_EPOUT_ADDR);pdev->ep_out[HID_EPOUT_ADDR & 0xFU].is_used = 0U;/* FRee allocated memory */if (pdev->pClassData != NULL){USBD_free(pdev->pClassData);pdev->pClassData = NULL;}return USBD_OK;
}

USBD_HID_DataOut函数实现,这里我做了个LED2的闪烁,并打印接收到的消息,从pbuf中拿到结果,拿到后还需要再次调用USBD_LL_PrepareReceive函数。

static uint8_t  USBD_HID_DataOut(USBD_HandleTypeDef *pdev, uint8_t epnum)
{HAL_GPIO_TogglePin(LED2_GPIO_Port, LED2_Pin);printf("data out: %x %x\n", pbuf[0], pbuf[1]);USBD_LL_PrepareReceive(pdev, HID_EPOUT_ADDR , pbuf, HID_EPOUT_SIZE);return USBD_OK;
}

我们街道电脑上后,当按另一个键盘上的能够让灯亮的按键:Caps Lock,Num Lock, Scroll Lock时板子会收到灯消息。

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

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

相关文章

初步认识操作系统(Operator System)

目录 一、概念二、设计OS的目的三、定位四、操作系统上下的分级五、如何理解 "管理"六、总结 一、概念 任何计算机系统都包含一个基本的程序集合,称为操作系统(OS)。操作系统包括: 内核(进程管理,内存管理&#xff0c…

LINUX 平台最快子网路由转发,内核使能选项配置

阅读本文之间,可线性参考以下文献。 Linux 命令行配置为单臂旁路由。_linux单臂路由-CSDN博客 Linux 软路由命令行配置(参考)_linux软路由-CSDN博客 VGW在 Windows 平台上局域网就绪的旁路由器程序_windows旁路由-CSDN博客 本文介绍 LINUX…

vue中onclick如何调用methods中的方法

文章目录 前言一、代码一开始效果二、解决方案 前言 今天在开发vue项目中使用的第三方地图,地图上绘制的marker内容需要自定义,因为绘制的内容是原生HTML,所以遇到点击事件的时候就用了onclick来定义,此时想要调用methods中的方法…

python创建一个httpServer网页上传文件到httpServer

一、代码 1.server.py import os from http.server import SimpleHTTPRequestHandler, HTTPServer import cgi # 自定义请求处理类 class MyRequestHandler(SimpleHTTPRequestHandler):# 处理GET请求def do_GET(self):if self.path /:# 响应200状态码self.send_response(2…

一文讲解Java中的重载、重写及里氏替换原则

提到重载和重写,Java小白应该都不陌生,接下来就通过这篇文章来一起回顾复习下吧! 重载和重写有什么区别呢? 如果一个类有多个名字相同但参数不同的方法,我们通常称这些方法为方法重载Overload。如果方法的功能是一样…

Java Swing 基础组件详解 [论文投稿-第四届智能系统、通信与计算机网络]

大会官网:www.icisccn.net Java Swing 是一个功能强大的 GUI 工具包,提供了丰富的组件库用于构建跨平台的桌面应用程序。本文将详细讲解 Swing 的基础组件,包括其作用、使用方法以及示例代码,帮助你快速掌握 Swing 的核心知识。 一…

前端小案例——520表白信封

前言:我们在学习完了HTML和CSS之后,就会想着使用这两个东西去做一些小案例,不过又没有什么好的案例让我们去练手,本篇文章就提供里一个案例——520表白信封 ✨✨✨这里是秋刀鱼不做梦的BLOG ✨✨✨想要了解更多内容可以访问我的主…

PyTorch广告点击率预测(CTR)利用深度学习提升广告效果

目录 广告点击率预测问题数据集结构广告点击率预测模型的构建1. 数据集准备2. 构建数据加载器3. 构建深度学习模型4. 训练与评估 总结 广告点击率预测(CTR,Click-Through Rate Prediction)是在线广告领域中的重要任务,它帮助广告平…

【信息系统项目管理师-选择真题】2017上半年综合知识答案和详解

更多内容请见: 备考信息系统项目管理师-专栏介绍和目录 文章目录 【第1题】【第2题】【第3题】【第4题】【第5题】【第6题】【第7~8题】【第9题】【第10题】【第11题】【第12题】【第13~14题】【第15题】【第16题】【第17题】【第18题】【第19题】【第20题】【第21题】【第22题…

Github 2025-01-25Rust开源项目日报Top10

根据Github Trendings的统计,今日(2025-01-25统计)共有10个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量Rust项目10Python项目1Vue项目1JavaScript项目1Deno: 现代JavaScript和TypeScript运行时 创建周期:2118 天开发语言:Rust, JavaScript协议类型…

每日一题--合并二叉树

合并二叉树 问题描述 已知两颗二叉树,将它们合并成一颗二叉树。合并规则是:如果节点存在于两棵树中,则将节点值相加;如果某个节点在一棵树中不存在,则直接使用另一棵树的节点值。例如: 两颗二叉树如下&a…

云计算的概念与特点:开启数字化时代的新篇章

在当今数字化时代,云计算(Cloud Computing)已经成为推动技术创新和业务转型的核心力量。无论是大型企业、中小型企业,还是个人用户,云计算都为其提供了高效、灵活和经济的解决方案。本文将深入探讨云计算的概念及其核心特点,帮助读者全面了解这一革命性技术。 © ivw…

Arcgis国产化替代:Bigemap Pro正式发布

在数字化时代,数据如同新时代的石油,蕴含着巨大的价值。从商业决策到科研探索,从城市规划到环境监测,海量数据的高效处理、精准分析与直观可视化,已成为各行业突破发展瓶颈、实现转型升级的关键所在。历经十年精心打磨…

分布式机器学习中【拓扑】与【通信】的区别和联系

在分布式机器学习中,拓扑和通信是两个重要的概念,它们虽然有一定的关联,但侧重点不同。下面我会逐一解释它们的定义、区别及其联系: 1. 拓扑(Topology): 拓扑指的是系统中不同节点&#xff08…

【fly-iot飞凡物联】(20):2025年总体规划,把物联网整套技术方案和实现并落地,完成项目开发和课程录制。

前言 fly-iot飞凡物联专栏: https://blog.csdn.net/freewebsys/category_12219758.html 1,开源项目地址进行项目开发 https://gitee.com/fly-iot/fly-iot-platform 完成项目开发,接口开发。 把相关内容总结成文档,并录制课程。…

MFC结构体数据文件读写实例

程序功能将结构体内数组数据写入文件和读出 2Dlg.h中代码: typedef struct Student {int nNum[1000];float fScore;CString sss;}stu; class CMy2Dlg : public CDialog { // Construction public:CMy2Dlg(CWnd* pParent NULL); // standard constructorstu stu1; ... } 2Dl…

rust 自定义错误(十二)

错误定义: let file_content parse_file("test.txt");if let Err(e) file_content {println!("Error: {:?}", e);}let file_content parse_file2("test.txt");if let Err(e) file_content {match e {ParseFileError::File > …

java+vue项目部署记录

目录 前言 一、java和vue 二、部署记录 1.获取代码 2.运行前端 3.运行后端 三、其他 1.nvm 总结 前言 近期工作需要部署一套javavue前后分离的项目,之前都略有接触,但属于不及皮毛的程度,好在对其他开发语言、html js这些还算熟&am…

零售业革命:改变行业的顶级物联网用例

mpro5 产品负责人Ruby Whipp表示,技术进步持续重塑零售业,其中物联网(IoT)正引领这一变革潮流。 研究表明,零售商们正在采用物联网解决方案,以提升运营效率并改善顾客体验。这些技术能够监控运营的各个方面…

(一)QT的简介与环境配置WIN11

目录 一、QT的概述 二、QT的下载 三、简单编程 常用快捷键 一、QT的概述 简介 Qt(发音:[kjuːt],类似“cute”)是一个跨平台的开发库,主要用于开发图形用户界面(GUI)应用程序,…