(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时板子会收到灯消息。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

java后端之登录认证

基础登录功能:根据提供的用户名和密码判断是否存在于数据库 LoginController.java RestController Slf4j public class LoginController {Autowiredprivate UserService userService;PostMapping("/login")public Result login(RequestBody User user) {…

Spring--Bean的生命周期和循环依赖

Bean的生命周期和循环依赖 Bean 的生命周期了解么?Spring中的循环引用什么是循环引用?三级缓存解决循环依赖总结构造方法出现了循环依赖怎么解决? Bean 的生命周期了解么? 整体上可以简单分为四步:实例化 —> 属性赋值 —> 初始化 —…

【云安全】云原生-Docker(五)容器逃逸之漏洞利用

漏洞利用逃逸 通过漏洞利用实现逃逸,主要分为以下两种方式: 1、操作系统层面的内核漏洞 这是利用宿主机操作系统内核中的安全漏洞,直接突破容器的隔离机制,获得宿主机的权限。 攻击原理:容器本质上是通过 Linux 的…

【Uniapp-Vue3】request各种不同类型的参数详解

一、参数携带 我们调用该接口的时候需要传入type参数。 第一种 路径名称?参数名1参数值1&参数名2参数值2 第二种 uni.request({ url:"请求路径", data:{ 参数名:参数值 } }) 二、请求方式 常用的有get,post和put 三种,默认是get请求。…

基于SpringBoot的软件产品展示销售系统

作者:计算机学姐 开发技术:SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等,“文末源码”。 专栏推荐:前后端分离项目源码、SpringBoot项目源码、Vue项目源码、SSM项目源码、微信小程序源码 精品专栏:…

hedfs和hive数据迁移后校验脚本

先谈论校验方法,本人腾讯云大数据工程师。 1、hdfs的校验 这个通常就是distcp校验,hdfs通过distcp迁移到另一个集群,怎么校验你的对不对。 有人会说,默认会有校验CRC校验。我们关闭了,为什么关闭?全量迁…

mysql学习笔记-数据库的设计规范

1、范式简介 在关系型数据库中,关于数据表设计的基本原则、规则就称为范式。 1.1键和相关属性的概念 超键:能唯一标识元组的属性集叫做超键。 候选键:如果超键不包括多余的属性,那么这个超键就是候选键 主键:用户可以从候选键中选择一个作为主键。 外…

高并发问题的多维度解决之道

‍‌​​‌‌​‌​‍‌​​​‌‌​​‍‌​​​‌​‌​‍‌​​‌​​‌​‍‌‌​​‌​‌​‍‌​‌​‌‌​​‍‌​‌​‌​​​‍‌​‌​‌​‌​‍‌​‌‌​​‌​‍‌​‌‌​​​​‍‌‌​​‌‌‌‌‍‌‌​​‌​‌‌‍‌​​​‌‌​​‍‌​​‌‌‌​​‍‌…

Windows Defender添加排除项无权限的解决方法

目录 起因Windows Defender添加排除项无权限通过管理员终端添加排除项管理员身份运行打开PowerShell添加/移除排除项的命令 起因 博主在打软件补丁时,遇到 Windows Defender 一直拦截并删除文件,而在 Windows Defender 中无权限访问排除项。尝试通过管理…

数据结构——堆(C语言)

基本概念: 1、完全二叉树:若二叉树的深度为h,则除第h层外,其他层的结点全部达到最大值,且第h层的所有结点都集中在左子树。 2、满二叉树:满二叉树是一种特殊的的完全二叉树,所有层的结点都是最…

工业相机 SDK 二次开发-Halcon 插件

本文介绍了 Halcon 连接相机时插件的使用。通过本套插件可连接海康 的工业相机。 一. 环境配置 1. 拷贝动态库 在 用 户 安 装 MVS 目 录 下 按 照 如 下 路 径 Development\ThirdPartyPlatformAdapter 找到目录为 HalconHDevelop 的文 件夹,根据 Halcon 版本找到对…

Vue3 + TS 实现批量拖拽 文件夹和文件 组件封装

一、html 代码&#xff1a; 代码中的表格引入了 vxe-table 插件 <Tag /> 是自己封装的说明组件 表格列表这块我使用了插槽来增加扩展性&#xff0c;可根据自己需求&#xff0c;在组件外部做调整 <template><div class"dragUpload"><el-dial…

CF 339A.Helpful Maths(Java实现)

题目分析 输入一串式子&#xff0c;输出从小到大排列的式子 思路分析 如上所说核心思路&#xff0c;但是我要使用笨方法&#xff0c;输入一串式子用split分割开&#xff0c;但是此时需要用到转义字符&#xff0c;即函数内参数不能直接使用“”&#xff0c;而是“\\”。分割开后…

naivecv的设计与实现(3): NV12到RGB的转换

准备 NV12 图像 在 github 搜索关键字 “YUVViewer", 找到样例文件&#xff1a; https://github.com/LiuYinChina/YUVViewer/blob/master/Output/720X576-NV12.yuv 它是二进制文件&#xff0c;没有文件头信息&#xff0c;只有像素内容, 排布方式: 先 Y 平面&#xff0c…

我谈区域偏心率

偏心率的数学定义 禹晶、肖创柏、廖庆敏《数字图像处理&#xff08;面向新工科的电工电子信息基础课程系列教材&#xff09;》P312 区域的拟合椭圆看这里。 Rafael Gonzalez的二阶中心矩的表达不说人话。 我认为半长轴和半短轴不等于特征值&#xff0c;而是特征值的根号。…

ansible自动化运维实战--软件包管理模块、服务模块、文件模块和收集模块setup(4)

文章目录 一、软件包管理模块1.1、功能1.2、常用参数1.3、示例 二、服务模块2.1、功能2.2、服务模块常用参数2.3、示例 三、文件与目录模块3.1、file功能3.2、常用参数3.3、示例 四、收集模块-setup4.1、setup功能4.2、示例 一、软件包管理模块 1.1、功能 Ansible 提供了多种…

Elasticsearch 性能测试工具 Loadgen 之 004——高级用法示例

在性能测试中&#xff0c;能够灵活地模拟不同的应用场景是至关重要的。 Loadgen 提供了多种高级用法&#xff0c;帮助用户更好地评估系统在不同负载下的表现。 本文将介绍如何使用 Loadgen 模拟批量摄取、限制客户端负载以及限制总请求数。 一、模拟批量摄取 在实际应用中&…

将点云转换为 3D 网格:Python 指南

3D 数据的世界往往是一个碎片化的景观。 存在点云&#xff0c;其细节丰富&#xff0c;但缺乏表面信息。 有3D 网格&#xff0c;它明确地定义表面&#xff0c;但创建起来通常很复杂。 将点云转换为网格弥补了这一差距并开启了许多可能性&#xff0c;从真实模拟到 3D 数字环境…

智能电动汽车系列 --- 智能汽车向车载软件转型

我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 简单,单纯,喜欢独处,独来独往,不易合同频过着接地气的生活,除了生存温饱问题之外,没有什么过多的欲望,表面看起来很高冷,内心热情,如果你身…

不解释快上车

聊一聊 最近有小伙伴问我有小红书图片和短视频下载的软件吗&#xff0c;我心想&#xff0c;下载那上面的图片和视频做什么&#xff1f;也许是自己没有这方面的需求&#xff0c;不了解。 不过话又说回来&#xff0c;有些很多下载器可能作者没有持续的维护&#xff0c;所以可能…