stm32学习总结:5、Proteus8+STM32CubeMX+MDK仿真串口并使用串口打印日志(注意重定向printf到串口打印的问题)
stm32学习总结:5、Proteus8+STM32CubeMX+MDK仿真串口并使用串口打印日志(注意重定向printf到串口打印的问题)
文章目录
- stm32学习总结:5、Proteus8+STM32CubeMX+MDK仿真串口并使用串口打印日志(注意重定向printf到串口打印的问题)
- 一、前言
- 二、资料收集
- 三、注意事项
- 四、STM32CubeMX配置
- 五、MDK工程相关代码
- 1、非中断方式的按键处理
- 2、开关机业务
- 3、printf重定向到串口打印
- 4、日志打印封装
- 六、Proteus项目配置
- 七、仿真测试结果
- 八、最后
一、前言
上一节模拟实现了串口收发打印,一般我们裸机打印日志通过串口或者JLINK工具等带的RTT打印,对于仿真,我们选择使用串口打印再合适不过了,这里总结一下重定向printf到串口打印日志的过程;期间,尝试了CLion+arm gcc的方式,发现stm32f10x的flash还是支撑不起来未裁剪的标准库,只要使用stdio相关标准库编译时就很容易flash超标。
二、资料收集
https://blog.csdn.net/m0_54490453/article/details/128921674
https://www.cnblogs.com/pianist/p/3315801.html
https://blog.51cto.com/u_13682052/5670642
STM32串口使用printf打印日志:
https://community.st.com/t5/stm32-mcus-products/how-to-get-printf-example-working-in-another-project/m-p/392014
https://community.st.com/t5/stm32-mcus-products/stm32g0-redirect-printf-to-write-and-use-uart-how-to/m-p/66318
三、注意事项
如果是使用arm gcc编译器的,尽量不要使用printf,这会引入标准库,而对应库不像mdk的microlib做了裁剪,它是比较占用flash的,而stm3210x的flash最多只有32KB,很容易在编译时出现section .rodata’ will not fit in region FLASH'也就是超出flash范围的问题:
网上所说的修改xxx.ld配置文件这些方法很多时候是无效的,不能盲目去修改flash配置。
可以使用比如RTT打印等方式来打印日志,也可以换一些资源比较丰富的板子,也许官方可以出一些裁剪过的利用arm-none-eabi gcc编译的标准库(后面有机会的话我会来尝试一下,用stm32F10x的话arm gcc基本上没办法用printf,引入标准库加上一两个简单的外设接口就肯定会flash超标,用mdk原有的编译器就不会有这个问题)。
四、STM32CubeMX配置
这次彻底精简一下相关配置:
- 1、一个按钮BUTTON,PA1配置GPIO OUTPUT用来接入按钮,使用默认配置即可,默认低电平,未拉高拉低,添加用户标签BUTTON:

- 2、五个LED,PA4-PA8配置GPIO OUTPUT用来接入LED,使用默认配置即可,默认低电平,未拉高拉低,添加用户标签LED_1到LED_5:

- 3、开启USART1,PA9\PA10来作为打印的串口,配置只发送,波特率设置为9600,不需要配置全局中断,我们使用该串口作为打印串口,只需要发送即可,所以不需要配置中断方式来接收:

然后生成代码即可。
五、MDK工程相关代码
1、非中断方式的按键处理
通过读取IO口的电平判断是否按下按钮,之后通过全局变量确认按下松开以及长短按,这种方式在理解上比较直观(按键这里的处理逻辑是判断LED灯1的电平变化来确定是否开关机,开关机的逻辑我们通过控制LED灯的亮灭来展示):
#include "gpio.h"
#include "key.h"
#include "pwr.h"
//#include "log.h"// 按键的键值
#define KEY_Press 1// 读取IO口的电平
#define KEY_PWR HAL_GPIO_ReadPin(GPIOA, BUTTON_Pin)uint8_t key_old, count;uint8_t ScanKey(void)
{if (GPIO_PIN_RESET == KEY_PWR) {HAL_Delay(40);//延时10-20ms,防抖if (GPIO_PIN_SET == KEY_PWR) {count++;return KEY_Press;}} else {HAL_Delay(40);}return 0;
}void DealKey(void)
{uint8_t key_value = 0;//获取键值key_value = ScanKey();if (key_value != key_old) {//与上一次的键值比较 如果不相等,表明有键值的变化,开始计时key_old = key_value;count = 0;} else {//如果没有键值的改变 说明没有新按键按下或松开key_value = 0;}if (key_value)// 短按处理{switch(key_value) {case 1 : {//LOG(LOG_DEBUG, "KEY1 switch");if (GPIO_PIN_SET == HAL_GPIO_ReadPin(GPIOA, LED_1_Pin)) {//LOG(LOG_DEBUG, "pwr on");PWROn();} else {//LOG(LOG_DEBUG, "pwr off");PWROff();key_old = key_value;}}break;case 2 : {
// LOG(DEBUG, "KEY2 switch");}break;}key_value = 0;}return;
}
#ifndef __KEY_H
#define __KEY_H#include "main.h"void DealKey(void);#endif
2、开关机业务
这里暂时通过LED灯亮灭来模拟,后续可增加底板电路的控制、蜂鸣器的控制等,基本都是通过控制IO口高低电平方式来控制的:
#include "pwr.h"
#include "log.h"
#include "gpio.h"void TurnOnLED(int flag)
{switch(flag){case 1:HAL_GPIO_WritePin(GPIOA, LED_1_Pin, GPIO_PIN_RESET);break;case 2:HAL_GPIO_WritePin(GPIOA, LED_2_Pin, GPIO_PIN_RESET);break;case 3:HAL_GPIO_WritePin(GPIOA, LED_3_Pin, GPIO_PIN_RESET);break;case 4:HAL_GPIO_WritePin(GPIOA, LED_4_Pin, GPIO_PIN_RESET);break;case 5:HAL_GPIO_WritePin(GPIOA, LED_5_Pin, GPIO_PIN_RESET);break;}
}void TurnOffLED(int flag)
{switch(flag){case 1 :HAL_GPIO_WritePin(GPIOA, LED_1_Pin, GPIO_PIN_SET);break;case 2 :HAL_GPIO_WritePin(GPIOA, LED_2_Pin, GPIO_PIN_SET);break;case 3 :HAL_GPIO_WritePin(GPIOA, LED_3_Pin, GPIO_PIN_SET);break;case 4 :HAL_GPIO_WritePin(GPIOA, LED_4_Pin, GPIO_PIN_SET);break;case 5 :HAL_GPIO_WritePin(GPIOA, LED_5_Pin, GPIO_PIN_SET);break;}
}void PWROn(void)
{LOG(LOG_DEBUG, "PWROn LED blink...");for (int i = 1; i< 6; i++) {HAL_Delay(100);TurnOnLED(i);}
}void PWROff(void)
{LOG(LOG_DEBUG, "PWROn LED off...");for (int i = 1; i< 6; i++) {HAL_Delay(100);TurnOffLED(i);}
}
#ifndef __PWR_H
#define __PWR_H#include "main.h"void PWROn(void);void PWROff(void);#endif
3、printf重定向到串口打印
通过stm32的官方论坛和一些参考文章发现是通过对printf进行重写来将发送到终端输出设备的内容通过串口发送出来,不同的编译器链接的库的printf底层调用方式可能有差异,需要注意一下。目前官方论坛上给到的方式是通过宏PUTCHAR_PROTOTYPE控制,我尝试去寻找对应printf的源码,mdk库的printf源码没有找到,据网上说其裁剪的标准库实现的printf是用fputc来将字符发送出去的,而gun c标准库的printf最终查看源码发现是通过调用write函数写入到显示设备的,而该函数为系统调用,系统通过驱动调用硬件IO口去控制显示设备将写入的内容显示,对于没有系统的stm32裸机来说其封装了一个__io_putchar的类似系统调用(其文件名字就叫syscalls.c)接口来让我们重写输出方式,这样我们重写PUTCHAR_PROTOTYPE即可,目前在main.c中添加对应宏控制并重写将写入的ch字符通过串口写入:
#ifdef __GNUC__/* With GCC, small printf (option LD Linker->Libraries->Small printfset to 'Yes') calls __io_putchar() */#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)#else#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)#endif /* __GNUC__ */PUTCHAR_PROTOTYPE{/* Place your implementation of fputc here *//* e.g. write a character to the EVAL_COM1 and Loop until the end of transmission */HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xFFFF);return ch;}
整体main.c,大循环中调用按钮监听处理:
/* USER CODE BEGIN Header */
/********************************************************************************* @file : main.c* @brief : Main program body******************************************************************************* @attention** Copyright (c) 2024 STMicroelectronics.* All rights reserved.** This software is licensed under terms that can be found in the LICENSE file* in the root directory of this software component.* If no LICENSE file comes with this software, it is provided AS-IS.********************************************************************************/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "usart.h"
#include "gpio.h"/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "key.h"
#include "stdio.h"/* USER CODE BEGIN 0 *//* USER CODE END Includes *//* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD *//* USER CODE END PTD *//* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD *//* USER CODE END PD *//* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM *//* USER CODE END PM *//* Private variables ---------------------------------------------------------*//* USER CODE BEGIN PV *//* USER CODE END PV *//* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP *//* USER CODE END PFP *//* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 *//* USER CODE END Includes *//* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD *//* USER CODE END PTD *//* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD *//* USER CODE END PD *//* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM *//* USER CODE END PM *//* Private variables ---------------------------------------------------------*//* USER CODE BEGIN PV *//* USER CODE END PV *//* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP *//* USER CODE END PFP *//* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 *//* USER CODE END Includes *//* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD *//* USER CODE END PTD *//* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD *//* USER CODE END PD *//* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM *//* USER CODE END PM *//* Private variables ---------------------------------------------------------*//* USER CODE BEGIN PV *//* USER CODE END PV *//* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP *//* USER CODE END PFP *//* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 *//* USER CODE END Includes *//* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD *//* USER CODE END PTD *//* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD *//* USER CODE END PD *//* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM *//* USER CODE END PM *//* Private variables ---------------------------------------------------------*//* USER CODE BEGIN PV *//* USER CODE END PV *//* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP *//* USER CODE END PFP *//* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 *//* USER CODE END Includes *//* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD *//* USER CODE END PTD *//* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD *//* USER CODE END PD *//* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM *//* USER CODE END PM *//* Private variables ---------------------------------------------------------*//* USER CODE BEGIN PV *//* USER CODE END PV *//* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP *//* USER CODE END PFP *//* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 *//* USER CODE END Includes *//* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD *//* USER CODE END PTD *//* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
#ifdef __GNUC__/* With GCC, small printf (option LD Linker->Libraries->Small printfset to 'Yes') calls __io_putchar() */#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)#else#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)#endif /* __GNUC__ */PUTCHAR_PROTOTYPE
{/* Place your implementation of fputc here *//* e.g. write a character to the EVAL_COM1 and Loop until the end of transmission */HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xFFFF);return ch;
}
/* USER CODE END PD *//* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM *//* USER CODE END PM *//* Private variables ---------------------------------------------------------*//* USER CODE BEGIN PV *//* USER CODE END PV *//* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP *//* USER CODE END PFP *//* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 *//* USER CODE END Includes *//* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD *//* USER CODE END PTD *//* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD *//* USER CODE END PD *//* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM *//* USER CODE END PM *//* Private variables ---------------------------------------------------------*//* USER CODE BEGIN PV *//* USER CODE END PV *//* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP *//* USER CODE END PFP *//* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 *//* USER CODE END 0 *//*** @brief The application entry point.* @retval int*/
int main(void)
{/* USER CODE BEGIN 1 *//* USER CODE END 1 *//* MCU Configuration--------------------------------------------------------*//* Reset of all peripherals, Initializes the Flash interface and the Systick. */HAL_Init();/* USER CODE BEGIN Init *//* USER CODE END Init *//* Configure the system clock */SystemClock_Config();/* USER CODE BEGIN SysInit *//* USER CODE END SysInit *//* Initialize all configured peripherals */MX_GPIO_Init();MX_USART1_UART_Init();/* USER CODE BEGIN 2 *//* USER CODE END 2 *//* Infinite loop *//* USER CODE BEGIN WHILE */while (1){/* USER CODE END WHILE *//* USER CODE BEGIN 3 */DealKey();}/* USER CODE END 3 */
}/*** @brief System Clock Configuration* @retval None*/
void SystemClock_Config(void)
{RCC_OscInitTypeDef RCC_OscInitStruct = {0};RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};/** Initializes the RCC Oscillators according to the specified parameters* in the RCC_OscInitTypeDef structure.*/RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;RCC_OscInitStruct.HSIState = RCC_HSI_ON;RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK){Error_Handler();}/** Initializes the CPU, AHB and APB buses clocks*/RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI;RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK){Error_Handler();}
}/* USER CODE BEGIN 4 *//* USER CODE END 4 *//*** @brief This function is executed in case of error occurrence.* @retval None*/
void Error_Handler(void)
{/* USER CODE BEGIN Error_Handler_Debug *//* User can add his own implementation to report the HAL error return state */__disable_irq();while (1){}/* USER CODE END Error_Handler_Debug */
}#ifdef USE_FULL_ASSERT
/*** @brief Reports the name of the source file and the source line number* where the assert_param error has occurred.* @param file: pointer to the source file name* @param line: assert_param error line source number* @retval None*/
void assert_failed(uint8_t *file, uint32_t line)
{/* USER CODE BEGIN 6 *//* User can add his own implementation to report the file name and line number,ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) *//* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
4、日志打印封装
然后我们将printf函数稍微封装一下,也可以使用easylogger日志库:
#include "log.h"
#include "usart.h"char* get_log_level_str(const int level)
{if (level == LOG_DEBUG) {return "DEBUG";}else if (level == LOG_INFO) {return "INFO";}else if (level == LOG_WARN) {return "WARN";}return "UNLNOW";
}void my_log(const int level,const char* fun, const int line ,const char* fmt, ...)
{
#ifdef OPEN_LOGva_list arg;va_start(arg, fmt);char buf[50] = { 0 };vsnprintf(buf, sizeof(buf), fmt, arg);va_end(arg);if (level >= LOG_LEVEL)printf("[%-5s] [%-20s%4d] %s \r\n", get_log_level_str(level), fun, line, buf);
#endif
}
#ifndef __LOG_H_
#define __LOG_H_#include <stdarg.h>
#include <stdio.h>
#define OPEN_LOG 1
#define LOG_LEVEL LOG_DEBUGtypedef enum
{LOG_DEBUG = 0,LOG_INFO,LOG_WARN
}E_LOGLEVEL;void my_log(const int level, const char* fun, const int line, const char* fmt, ...);
#define LOG(level,fmt,...) my_log(level,__FUNCTION__,__LINE__,fmt, ##__VA_ARGS__)#endif
这样我们调用LOG()进行日志打印就可以了,和常规软件开发的日志打印接口基本就一致了。
六、Proteus项目配置
Proteus创建串口和虚拟终端、LED灯、电阻灯我们之前已经总结过了,不再重复,这里再添加一个按键即可,按下P,搜索BUTTON即可添加,之后按如下方式接线:
串口TX、RX和之前接法一样,还需要配置其波特率,也和之前一样,虚拟终端这里有一些区别,上节我们是TX、RX是和串口对应一致的,但是这里我们要显示串口发送的内容,所以RX接入串口的TX,不要搞错了,而且由于是显示日志的打印串口,我们配置的只发送,所以理论上RX是可以不接的。至于其它的接线基本没有什么注意的,和之前的大体一致,主要注意控制开关的高低电平即可,一端接电源,另一端设置低电平即导通,电流流过。
七、仿真测试结果
按下按键根据key.c中的处理逻辑就会来回判断进行开关机业务处理了,这里模拟开关机通过LED灯的亮灭来展示,串口的TX发送接入虚拟终端RX将发送的日志直接显示出来了,串口工具在Windows上打开对端的串口也是可以看到对应的信息的(快去试一下吧):
八、最后
下一节我们来试下ADC和蜂鸣器的使用吧,蜂鸣器这种简单的音频提示元器件也是用的比较广泛的,比较常见的像小区的门禁基本都会加蜂鸣器提示刷卡是否成功等,ADC接口则常常用来读取一些传感器的信息或者电压信息等,也是比较常用的。最近的一些总结基本都是用为主,对于这些IO口的更深层次的配置及控制原理我们暂时不做深入分析,只需要了解怎么用即可,这些东西感觉总结C51的时候来分析总结更合适一些,C51的应用相对简单一些,STM32的应用开发已经比较接近Linux应用开发这种层级,很多时候不用太考虑底层的实现,只需要会用接口开发比较复杂的应用即可(所以这种应用开发实时操作系统的学习就比较重要了),等到解决一些疑难杂症时,再对其做深入了解和理解,新手直接深入容易被劝退。
相关文章:
stm32学习总结:5、Proteus8+STM32CubeMX+MDK仿真串口并使用串口打印日志(注意重定向printf到串口打印的问题)
stm32学习总结:5、Proteus8STM32CubeMXMDK仿真串口并使用串口打印日志(注意重定向printf到串口打印的问题) 文章目录 stm32学习总结:5、Proteus8STM32CubeMXMDK仿真串口并使用串口打印日志(注意重定向printf到串口打印…...
SAFe大规模敏捷企业级实训
课程简介 SAFe – Scaled Agile Framework是目前全球运用最广泛的大规模敏捷框架,也是成长最快、最被认可、最有价值的规模化敏捷框架,目前全球SAFe认证专业人士已达80万人,福布斯100强的70%都在实施SAFe。本课程是一个2天的 SAFe权威培训课…...
中医电子处方系统,西医个体诊所门诊卫生室病历记录查询软件教程
中医电子处方系统,西医个体诊所门诊卫生室病历记录查询软件教程 一、软件程序问答 1、电子处方软件如何快速开单? 如下图,软件以 佳易王诊所电子处方管理系统V17.1版本为例说明 在开电子处方的时候可以按单个药品开,也可以直…...
搞定ESD(八):静电放电之原理图设计
文章目录 一、防护对象识别方法1.1 根据应用手册识别防护对象1.2 根据端口信号类型识别防护对象1.3 根据信号类型识别防护对象二、电路级ESD防护设计2.1 静电尖峰脉冲电压钳位设计(ESD器件并联)2.1.1 高速差分信号ESD防护设计2.1.2 低速信号ESD防护设计2.2 静电放电电流限制设…...
微前端 Micro App
MicroApp 官网链接 MicroApp 链接...
Java amr格式转mp3格式
1.问题描述 微信返回的语音是amr格式的,浏览器不能直接使用,所以需要转为mp3 注意:不能直接使用IO流转为mp3,不然H5还是用不了。转换之后的语音只能在播放器上播放,内里的文件格式其实还是amr 2.使用以下方式转换 音…...
Vue2面试题:说一下虚拟DOM的原理?
虚拟dom是对真实dom的抽象,本质是JS对象 在生成真实DOM之前,vue会把模板编译为一个虚拟dom,当里面某个DOM节点发生变动时,通过diff算法对比新旧虚拟DOM,发现不一样的地方直接修改在真实的DOM上 优点: 可以…...
Spring对bean的管理
一.bean的实例化 1.spring通过反射调用类的无参构造方法 在pom.xml文件中导入坐标: <dependencies><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.3.29<…...
Character Controller Smooth
流畅的角色控制器 Unity的FPS解决方案! 它是一种具有非常平滑运动和多种设置的解决方案: - 移动和跳跃 - 坐的能力 - 侧翻角度 - 不平整表面的处理 - 惯性守恒 - 重力 - 与物理物体的碰撞。 - 支持没有家长控制的平台 此解决方案适用于那些需要角色控制器…...
企业内训系统源码开发实战:搭建实践与经验分享
本篇文章中,小编将带领读者深入探讨企业内训系统的源码开发实战,分享在搭建过程中遇到的挑战与解决方案。 一、项目规划与需求分析 通过对企业内训需求的深入了解,我们可以更好地定义系统架构和数据库设计。 二、技术栈选择 在内训系统开发…...
15.三数之和(双指针,C解答附详细分析)
题目描述: 给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i ! j、i ! k 且 j ! k ,同时还满足 nums[i] nums[j] nums[k] 0 。请 你返回所有和为 0 且不重复的三元组。 注意:答案中不可以包含…...
SpringCloud微服务 【实用篇】| Dockerfile自定义镜像、DockerCompose
目录 一:Dockerfile自定义镜像 1. 镜像结构 2. Dockerfile语法 3. 构建Java项目 二: Docker-Compose 1. 初识DockerCompose 2. 部署微服务集群 前些天突然发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,…...
Vue3+TS+ElementPlus的安装和使用教程【详细讲解】
前言 本文简单的介绍一下vue3框架的搭建和有关vue3技术栈的使用。通过本文学习我们可以自己独立搭建一个简单项目和vue3的实战。 随着前端的日月更新,技术的不断迭代提高,如今新vue项目首选用vue3 typescript vite pinia……模式。以前我们通常使用…...
浅析锂电池保护板(BMS)系统设计思路(四)SOC算法-扩展Kalman滤波算法
BMS开发板 1 SOC估算方法介绍 电池SOC的估算是电池管理系统的核心,自从动力电池出现以来,各种各样的电池SOC估算方法不断出现。随着电池管理系统的逐渐升级,电池SOC估算方法的效率与精度不断提高,下面将介绍常用几种电池SOC估算方…...
构建异步高并发服务器:Netty与Spring Boot的完美结合
前言 「作者主页」:雪碧有白泡泡 「个人网站」:雪碧的个人网站 ChatGPT体验地址 文章目录 前言IONetty1. 引入依赖2. 服务端4. 客户端结果 总结引导类-Bootstarp和ServerBootstrap连接-NioSocketChannel事件组-EventLoopGroup和NioEventLoopGroup 送书…...
uniapp实现文字超出宽度自动滚动(在宽度范围之内不滚动、是否自动滚动、点击滚动暂停)
效果如下: 文字滚动 组件代码: <template><view class="tip" id="tip" @tap.stop="clickMove"><view class=...
win11 电脑睡眠功能失效了如何修复 win11 禁止鼠标唤醒
1、win11睡眠不管用怎么办,win11电脑睡眠功能失效了如何修复 在win11系统中拥有许多令人激动的新功能和改进,有些用户在使用win11电脑时可能会遇到一个问题:睡眠模式不起作用。当他们尝试将计算机置于睡眠状态时,却发现系统无法进…...
内坐标转换计算
前言 化学这边的库太多了。 cs这边的库太少了。 去看化学的库太累了。 写一个简单的实现思路,让cs的人能看懂。 向量夹角的范围 [0, pi) 这是合理的。 因为两个向量只能构成一个平面系统,平面系统内的夹角不能超过pi。 二面角的范围 涉及二面角&…...
vue中 components自动注册,不需要一个个引入注册方法
1.在compontents文件夹新建js文件 componentRegister 不能引用文件夹里的组件** import Vue from "vue"; function capitalizeFirstLetter(string) { return string.charAt(0).toUpperCase() string.slice(1); } const requireComponent require.context( ".…...
web自动化测试从入门到持续集成
在很多刚学习自动化的可能会认为我只需要会运用selenium,我只需要在一个编辑器中实用selenium java编写了一些脚本那么就会自动化了,是真的吗?答案肯定是假的。自动化肯定是需要做到真的完全自动化,那如何实现呢?接着往…...
UE5 学习系列(二)用户操作界面及介绍
这篇博客是 UE5 学习系列博客的第二篇,在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下: 【Note】:如果你已经完成安装等操作,可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作,重…...
反向工程与模型迁移:打造未来商品详情API的可持续创新体系
在电商行业蓬勃发展的当下,商品详情API作为连接电商平台与开发者、商家及用户的关键纽带,其重要性日益凸显。传统商品详情API主要聚焦于商品基本信息(如名称、价格、库存等)的获取与展示,已难以满足市场对个性化、智能…...
CMake基础:构建流程详解
目录 1.CMake构建过程的基本流程 2.CMake构建的具体步骤 2.1.创建构建目录 2.2.使用 CMake 生成构建文件 2.3.编译和构建 2.4.清理构建文件 2.5.重新配置和构建 3.跨平台构建示例 4.工具链与交叉编译 5.CMake构建后的项目结构解析 5.1.CMake构建后的目录结构 5.2.构…...
java调用dll出现unsatisfiedLinkError以及JNA和JNI的区别
UnsatisfiedLinkError 在对接硬件设备中,我们会遇到使用 java 调用 dll文件 的情况,此时大概率出现UnsatisfiedLinkError链接错误,原因可能有如下几种 类名错误包名错误方法名参数错误使用 JNI 协议调用,结果 dll 未实现 JNI 协…...
【机器视觉】单目测距——运动结构恢复
ps:图是随便找的,为了凑个封面 前言 在前面对光流法进行进一步改进,希望将2D光流推广至3D场景流时,发现2D转3D过程中存在尺度歧义问题,需要补全摄像头拍摄图像中缺失的深度信息,否则解空间不收敛…...
鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个生活电费的缴纳和查询小程序
一、项目初始化与配置 1. 创建项目 ohpm init harmony/utility-payment-app 2. 配置权限 // module.json5 {"requestPermissions": [{"name": "ohos.permission.INTERNET"},{"name": "ohos.permission.GET_NETWORK_INFO"…...
什么是Ansible Jinja2
理解 Ansible Jinja2 模板 Ansible 是一款功能强大的开源自动化工具,可让您无缝地管理和配置系统。Ansible 的一大亮点是它使用 Jinja2 模板,允许您根据变量数据动态生成文件、配置设置和脚本。本文将向您介绍 Ansible 中的 Jinja2 模板,并通…...
Linux nano命令的基本使用
参考资料 GNU nanoを使いこなすnano基础 目录 一. 简介二. 文件打开2.1 普通方式打开文件2.2 只读方式打开文件 三. 文件查看3.1 打开文件时,显示行号3.2 翻页查看 四. 文件编辑4.1 Ctrl K 复制 和 Ctrl U 粘贴4.2 Alt/Esc U 撤回 五. 文件保存与退出5.1 Ctrl …...
适应性Java用于现代 API:REST、GraphQL 和事件驱动
在快速发展的软件开发领域,REST、GraphQL 和事件驱动架构等新的 API 标准对于构建可扩展、高效的系统至关重要。Java 在现代 API 方面以其在企业应用中的稳定性而闻名,不断适应这些现代范式的需求。随着不断发展的生态系统,Java 在现代 API 方…...
深度学习之模型压缩三驾马车:模型剪枝、模型量化、知识蒸馏
一、引言 在深度学习中,我们训练出的神经网络往往非常庞大(比如像 ResNet、YOLOv8、Vision Transformer),虽然精度很高,但“太重”了,运行起来很慢,占用内存大,不适合部署到手机、摄…...
