还在老一套?STM32使用新KEIL5的IDE,全新开发模式RTE介绍及使用
Keil新版本出来了,推出了一种全新开发模式RTE框架( Run-Time Environment),更好用了。然而网上的教程资料竟还都是把Keil5当成Keil4来用,直接不使用这个功能。当前正点原子或野火的教程提供的例程虽有提到Keil5,但也是基本上当Keil4来用,还是传统的模式或标准库或HAL库。当然这用来学习挺好,但是如果用来开发建议还是使用下Keil5的RTE框架。
现在的单片机开发早已经变天,早已不是十年前刀耕火种的样子,开发和使用越来越友好了。单片机的开发趋势越来越接近于软件层开发,底层驱动的封装变成了芯片厂家做的工作。这是一种趋势,对于开发使用人员而言,可以更专注于需求和效率。
RTE框架直接给你提供了现成的板级驱动包和可视化模块配置,你需要做的只是配置和在驱动层之上开发应用就行了。且包含了包管理功能,包和组件使用和升级更方便了,这是一大特色,其他厂家都可以贡献和提供驱动包,其他三方都可以提供模块组件,开放共建。包括嵌入式内核你都不要移植,且可以切换想用哪个用哪个,不得不佩服老外IDE做的是真用心啊。
希望国产IDE软件也多学习学习老外,如果国产软件也能这么强大好用该多好,那时候国产芯片想不火都难。STM32的火是有原因的,因为它的操作使用上比起其他单片机都简单。这得益于ST这家公司的睿智,先是提供标准库,后HAL库,如今ST推出的STM32CubeMX配置软件,就是想让你更简单更好用,易用性上甩其他厂家几条街,就是让你只用它家的就行了,以此来笼络你,垄断你,但它只能耕stm32这一亩三分地。
在单片机领域,基于Arm Cortex M内核的产品已经占据很大的一部分份额,比较知名的ST的STM32系列,NXP的LPC系列都是其中非常棒的产品。各个厂家为了用户能更好的使用这些单片机产品,都推出了各自的软件工具和SDK,以便帮助用户简单快速的开发软件。Arm公司为了防止Arm软件开发的碎片化,提出并实施了CMSIS,这个是Arm公司与多家不同的芯片和软件供应商一起紧密合作定义的,提供了内核与外设、实时操作系统和中间设备之间的通用接口。后续用户需要做的仅是在应用层上做开发就可以了。
CMSIS的整体架构:
前言
曾记得十年前使用STM32时那叫一个麻烦,参考正点原子和野火的教程学习直接操作寄存器。
其实这也没啥,毕竟这是传统的单片机的开发方式。如之前的51,avr,pic,msp430,我想这几款单片机都玩过的肯定都是古董玩家,想当年这几款单片机没少折腾我,当然都是对着手册操作寄存器,也没啥难的,就是很低效,换一种单片机就得先熟悉它的风格。
现在单片机开发变简单了许多,这归功于stm32做的不错。之所以stm32它一直很火,是因为它开创了一个先河,j降低了单片机的门槛,让单片机操作越来越简单了。ST官方最先推出了标准库,然而依旧觉得不是很简单。再后来ST官方又推出了HAL库,接着STM32CubeMX配置软件的横空出世,让STM32可视化配置硬件参数和寄存器,变更好用了。但STM32CubeMX仅用在ST的产品上,深度绑定。
这里要介绍下Keil5的全新的开发方式RTE框架的使用。
可视化的最新的keil5软件真强大,但网上提供的教程大多是基于传统keil4的开发模式,有的虽然标题提到了RTE这种全新的包管理模式,但是还是混杂着标准库或HAL库的概念让人混淆不清,还在教你如何手工添加库和文件,没体现RTE的好处和使用。
其实RET这种模式下就不要提什么标准库或HAL库了,就当是各个厂家提供好了现成的板级驱动就好。你需要在做些配置且在驱动层之上开发应用。驱动层之下不用你过多关注,驱动层和常用模块都已提供好了。这是未来的一种趋势,因此建议尝试和使用下Keil5的RTE开发模式。
我通过查阅官网拿到第一手资料,在此整理下我的总结分享给大家。后续再看到应用层直接操作寄存器和板级接口操作的,不要再这样混着用啦,这种非RTE框架模式的本意。
不知你有没有觉察到,Keil5引入的这一开发理念很先进。会成为今后的主流趋势,值得我们学习借鉴。以后软件开发者可以把精力放在快速的开发应用和创新上,驱动操作封装成了芯片厂家要做的事。当多数国人还在卷单片机寄存器操作的时候,老外又一次走在了前列。Keil5不愧是王者,IDE好用易用性上排第一。以后不但芯片硬件上可替换,软件应用层也可以做到很少改动。
以下是我的分享,希望对大家有所帮助。另外,本文中如果有需改善的地方,欢迎留言,谢谢!
什么是RTE?
Keil5 最新的 RTE 框架是一种用于配置嵌入式软件应用程序的软件组件。它提供了图形用户界面 (GUI),使开发人员可以轻松配置系统组件,如处理器外设、通信接口和内存管理,而不需要手动编写代码。相比原来使用的标准库或 HAL 库的方式,Keil5 RTE 框架的优点在于它简化了开发流程,提供了一种简单直观的配置方法,并减少了必须手动编写的代码数量,可以节省时间并减少错误。
软件组件介绍
MDK 提供软件组件(Software Components),用于使用称为运行时环境(RTE) 的框架创建应用程序。这些组件以独立于 µVision 安装的软件包形式提供。软件包由 Arm 或第三方提供。
Software Components
图为Arm Keil提供的软件包,结构清晰。软件包可以包含设备支持,包括驱动程序、CMSIS 库和中间件组件。其中的设备系列包,由各个芯片厂家提供。
设备系列包 (DFP):包含用于微控制器设备系列的 CMSIS 系统/启动、驱动程序和闪存算法。
CMSIS:包含通用 CMSIS 组件(CORE、DSP 和 NN 库,以及 RTOS 实现)。
MDK-Middleware:包含属于 MDK-Professional 一部分的中间件库。
在项目中使用软件组件:
1.使用Pack Installer安装或更新包含预构建软件组件的软件包。
2.使用窗口Manage Run-Time Environment将软件组件添加到项目中。添加的组件显示在“项目”窗口中。
3.(可选)设置组件的选项以指定属性。
4.(可选)为特定目标选择软件包。
新项目结构介绍
不要再用以往传统的手动创建所有目录,再引入HAL库的落后方式了。当然可以在创建一些应用层的目录。其他层的按照配置出来的已经很清晰了。以下举例介绍下最的项目结构。
以上目录中,默认配置生成的是绿色显示的。Device就相当于底层驱动层,驱动层里面小写字母开头的文件,是各个厂家官方提供的固件操作功能。大写字母开头的就是驱动接口及实现。在应用层使用时最好严格分层,不要混合调用固件接口或操作寄存器了,而是应通过大写字母开头的驱动接口去配置和调用。(注:截图部分的代码仅为示例。它不好的地方是,里面掺杂使用了如GPIOD-BSRR这种寄存器操作,后文有正解。)
CMSIS是一个标准,以上CMSIS里的是标准驱动的实现,其中的串口驱动和操作系统内核放置在里面。那个Board Support算是个模块儿化组件,提供了一些通用的硬件模块操作,如以上的是个跟板子硬件相关的LED灯的操作实现,可以拿来复用,不过需要根据板子的实际管脚分配来配置。
应用层使用介绍
Board Support这个文件夹,虽然是配置出来的,但是它实际就是一个应用层功能实现。可以用来学习,看下它是如何使用GPIO接口驱动了。那么后续你的使用基本跟它类似。它的文件内容如下:
#include "Board_LED.h"
#include "GPIO_STM32F10x.h"const GPIO_PIN_ID Pin_LED[] = {{ GPIOE, 8 },{ GPIOE, 9 },{ GPIOE, 10 },{ GPIOE, 11 },{ GPIOE, 12 },{ GPIOE, 13 },{ GPIOE, 14 },{ GPIOE, 15 },
};#define LED_COUNT (sizeof(Pin_LED)/sizeof(GPIO_PIN_ID))/**\fn int32_t LED_Initialize (void)\brief Initialize LEDs\returns- \b 0: function succeeded- \b -1: function failed
*/
int32_t LED_Initialize (void) {uint32_t n;/* Configure pins: Push-pull Output Mode (50 MHz) with Pull-down resistors */for (n = 0; n < LED_COUNT; n++) {GPIO_PortClock (Pin_LED[n].port, true);GPIO_PinWrite (Pin_LED[n].port, Pin_LED[n].num, 0);GPIO_PinConfigure(Pin_LED[n].port, Pin_LED[n].num,GPIO_OUT_PUSH_PULL,GPIO_MODE_OUT2MHZ);}return 0;
}/**\fn int32_t LED_Uninitialize (void)\brief De-initialize LEDs\returns- \b 0: function succeeded- \b -1: function failed
*/
int32_t LED_Uninitialize (void) {uint32_t n;/* Configure pins: Input mode, without Pull-up/down resistors */for (n = 0; n < LED_COUNT; n++) {GPIO_PinConfigure(Pin_LED[n].port, Pin_LED[n].num,GPIO_IN_FLOATING,GPIO_MODE_INPUT);}return 0;
}/**\fn int32_t LED_On (uint32_t num)\brief Turn on requested LED\param[in] num LED number\returns- \b 0: function succeeded- \b -1: function failed
*/
int32_t LED_On (uint32_t num) {int32_t retCode = 0;if (num < LED_COUNT) {GPIO_PinWrite(Pin_LED[num].port, Pin_LED[num].num, 1);}else {retCode = -1;}return retCode;
}/**\fn int32_t LED_Off (uint32_t num)\brief Turn off requested LED\param[in] num LED number\returns- \b 0: function succeeded- \b -1: function failed
*/
int32_t LED_Off (uint32_t num) {int32_t retCode = 0;if (num < LED_COUNT) {GPIO_PinWrite(Pin_LED[num].port, Pin_LED[num].num, 0);}else {retCode = -1;}return retCode;
}/**\fn int32_t LED_SetOut (uint32_t val)\brief Write value to LEDs\param[in] val value to be displayed on LEDs\returns- \b 0: function succeeded- \b -1: function failed
*/
int32_t LED_SetOut (uint32_t val) {uint32_t n;for (n = 0; n < LED_COUNT; n++) {if (val & (1<<n)) {LED_On (n);} else {LED_Off(n);}}return 0;
}/**\fn uint32_t LED_GetCount (void)\brief Get number of LEDs\return Number of available LEDs
*/
uint32_t LED_GetCount (void) {return LED_COUNT;
}
代码分析:该代码属于应用层,为应用提供了几个操作LED的接口(Board_LED.h声明功能接口),代码里包含了GPIO_STM32F10x.h驱动接口头文件,使用了GPIO接口驱动。通过它可以看出,应用层的功能封装和实现,基本不涉及寄存器操作。都是通过驱动接口配置和访问的。
main入口函数介绍
/***************************************************************************//**
* \file main.c
*
* \details LED Blinking using RTX CMSIS V2 RTOS
*
* \author EmbeTronicX
*
* \Tested with Proteus
*
* *****************************************************************************/
#include "RTE_Components.h"
#include CMSIS_device_header
#include "cmsis_os2.h"
#include "stm32f10x.h"#include "Board_LED.h"/*
** This thread will turns ON and turns OFF the PORT-D LEDs with 1second delay.
**
** Arguments:
** arg -> Argument of this thread. osThreadNew()'s 2nd arg has to come here.
**
*/
__NO_RETURN static void LED_Blink_PortD( void *arg )
{(void)arg; //unused variable//set Port D as output//GPIOD->MODER = 0x55555555;for (;;) //infinite for loop{//Turn ON the LED of Port-DLED_On(3);osDelay(1000); //1sec delay//Turn OFF the LED of Port-DLED_Off(2);//GPIOD->BSRR = 0xFFFF0000;osDelay(1000); //1sec delay}
}/*
** This thread will turns ON and turns OFF the PORT-E LEDs with 3second delay.
**
** Arguments:
** arg -> Argument of this thread. osThreadNew()'s 2nd arg has to come here.
**
*/
__NO_RETURN static void LED_Blink_PortE( void *arg )
{(void)arg; //unused variable//set Port E as output//GPIOE->MODER = 0x55555555;for (;;) //infinite for loop{//Turn ON the LED of Port-E//GPIOE->BSRR = 0x0000FFFF;LED_SetOut(0);osDelay(3000); //3sec delay//Turn OFF the LED of Port-E//GPIOE->BSRR = 0xFFFF0000;osDelay(3000); //3sec delayLED_SetOut(1);}
}/*
** main function
**
** Arguments:
** none
**
*/
int main (void)
{// System InitializationSystemCoreClockUpdate();LED_Initialize();osKernelInitialize(); // Initialize CMSIS-RTOSosThreadNew(LED_Blink_PortD, NULL, NULL); // Create application main threadosThreadNew(LED_Blink_PortE, NULL, NULL); // Create application main threadosKernelStart(); // Start thread executionfor (;;) {//Dummy infinite for loop.}
}
以上是main入口函数的实现,你有在里面发现寄存器和特殊的硬件操作吗?没有的,应用层看不到跟某一平台的相关性,更别提会出现寄存器操作了。这跟以往的开发模式有很大的不同,结构更清晰合理了。如果你的应用中混杂了寄存器或板级的驱动接口调用,请留意这样用是否合适,有没有其他的方式。
以上代码未移植嵌入式内核就可以使用RTX嵌入式系统啦,是不是很简单和方便?如果想换系统怎么办?只需要改配置就行。如果要换单片机呢?那么也放心,你的很多应用层代码都是可以复用了,仅是改下驱动层的接口参数配置,这就是这一开发模式的好处。以上代码示例不是针对stm32这一种,在keil中支持的任一单片机我都可以轻易切换。
串口驱动使用
可以看下串口的使用有多么的简单,完全颠覆了以往传统的开发方式。只需先配置好串口硬件管脚后,剩下的就是像上位机纯软件的开发一样调用接口函数即可,以下示例可以看下够简单吧。
在RTE_device.h文件中,配置串口一的硬件管脚如下:
使用如下(main.c):
#include <string.h>
#include "RTE_Components.h"
#include CMSIS_device_header
#include "cmsis_os2.h"
#include "stm32f10x.h"#include "Board_LED.h"
#include "Driver_USART.h"extern ARM_DRIVER_USART Driver_USART1;/* Variable definitions ------------------------------------------------------*/
static uint8_t rxBuffer[1024] = {0};
static uint8_t txBuffer[1024] = {0};/* Function declarations -----------------------------------------------------*/
static void USART1_Callback(uint32_t event);
/*** @brief USART1 callback function.* @param event: USART events notification mask.* @return None.*/
static void USART1_Callback(uint32_t event)
{if(event & ARM_USART_EVENT_RX_TIMEOUT){Driver_USART1.Control(ARM_USART_ABORT_RECEIVE, 1);uint32_t length = Driver_USART1.GetRxCount();memcpy(txBuffer, rxBuffer, length);Driver_USART1.Send(txBuffer, length);Driver_USART1.Receive(rxBuffer, sizeof(rxBuffer));}
}void uart_init()
{Driver_USART1.Initialize(USART1_Callback);Driver_USART1.PowerControl(ARM_POWER_FULL);Driver_USART1.Control(ARM_USART_MODE_ASYNCHRONOUS |ARM_USART_DATA_BITS_8 |ARM_USART_PARITY_NONE |ARM_USART_STOP_BITS_1 |ARM_USART_FLOW_CONTROL_NONE, 115200);Driver_USART1.Control(ARM_USART_CONTROL_TX, 1);Driver_USART1.Control(ARM_USART_CONTROL_RX, 1);Driver_USART1.Receive(rxBuffer, sizeof(rxBuffer));
}
关于CMSIS
CMSIS 是一个独立于供应商的硬件抽象层,适用于基于 Arm® Cortex® 处理器的微控制器。CMSIS( Cortex 微控制器软件接口标准)的主要目标是提高软件在不同微控制器和工具链之间的可移植性和可重用性。这允许来自不同来源的软件无缝集成在一起。一旦学会,CMSIS 就可以通过使用标准化的软件功能来帮助加快软件开发。
CMSIS 具有模块化结构,每个模块都可以独立使用或更新。一些模块是特定于设备的,因此对于每个 ARM Cortex 微控制器系列,都会创建一个 CMSIS 模块集合,即设备系列包 (DFP)。有助于简化软件重用、减少微控制器开发人员的学习曲线、加速项目构建和调试,从而缩短新应用程序的上市时间。
CMSIS 最初是作为基于 Arm® Cortex®-M 的处理器的独立于供应商的硬件抽象层,后来扩展为支持基于 Arm Cortex-A 的入门级处理器。为了简化访问,CMSIS 定义了通用工具接口,并通过为处理器和外围设备提供简单的软件接口来实现一致的设备支持。
CMSIS 是与各种芯片和软件供应商密切合作而定义的,它提供了一种通用方法来连接外围设备、实时操作系统和中间件组件。它旨在支持来自多个供应商的软件组件的组合。
CMSIS 的创建是为了帮助行业实现标准化。它在广泛的开发工具和微控制器中实现了一致的软件层和设备支持。
CMSIS 是在GitHub上开源和协作开发的。
CMSIS 驱动程序规范
是一种软件 API,它描述了中间件堆栈和用户应用程序的外围驱动程序接口。CMSIS-Driver API 设计为通用且独立于特定的 RTOS,使其可在广泛的受支持微控制器设备中重复使用。CMSIS-Driver API 涵盖了受支持外设类型的广泛用例,但无法考虑所有潜在用例。随着时间的推移,CMSIS-Driver API 将扩展到更多的组以涵盖新的用例。
CMSIS 软件包在带有头文件和文档的组件类CMSIS 驱动程序下发布 API 接口。这些头文件是实现标准化外设驱动接口的参考。这些实现通常在组件类CMSIS 驱动程序下的相关微控制器系列的设备系列包中发布。设备系列包可能包含组件类设备中的附加接口,以使用附加设备特定接口(例如内存总线、GPIO 或 DMA)扩展此 CMSIS 驱动程序规范涵盖的标准外设驱动程序。
由上图可以看出,中间件或上层应用,通过CMSIS驱动接口,跟底层的硬件进行了隔离。建议组件和应用开发上直接使用CMSIS提供的驱动接口,最好不要直接调用Device(跟特定平台相关的标准库或HAL库)的接口,除非迫不得已。这样可以做到底层硬件的变动而不影响中间件和上层业务。
CMSIS驱动接口使用文档地址:Usage and Description
结语
最后,欢迎大家体验Keil5的全新的RTE开发模式。总之使用也很简单,关键是要转变思想,一定要有软件分层的思想,摒弃以往不好的开发习惯,尤其是长期以来习惯操作寄存器的思维惯性。让代码分封更清晰,模块化更利于复用,让维护更简单,让升级更方便。
当咱们还在卷寄存器操作的时候,老外又一次走在了前列。推广下这一单片机开发理念,这一开发理念是未来的趋势!今后人人都可以根据需要使用不同的单片机快速的进行业务开发。会一种就行,不用去卷各家不同单片机的操作。驱动封装是芯片或IDE厂家的事。国产IDE软件应该像keil学习,让广大开发者好用,才有市场,才能国产化崛起!
risc-v单片机是国产化弯道超车的机会,但就是碎片化严重,驱动封装的不太好用,且没有好用的国产IDE。要是像stm32那般好用,想不火都难。各家的IDE各自为战,重复的造轮子,没有统一的支持各个厂家的不同芯片。risc-v的世界缺乏像ARM这种的统一领导和规范,得形成合力避免重复和各自为战,才能扩大生态,开放共建实现共赢。开发者没选一个芯片就得换套IDE?就得重新熟悉一遍操作吗?
市面上的各大 RISC-V 芯片或 IP 公司,都在做自己的 IDE 用自己的工具链。那么为什么各家都在做各自的工具链?我认为还是没有形成类似于 ARM 的 CMSIS 这样的嵌入式软件接口标准,来统一管理底层软件接口,于是乎就变成了各自玩各的没有统一。这也是导致 RISC-V 软件生态薄弱,碎片化的一个重要因素,严重影响其生态。大部分的 RISC-V 厂商的开发 IDE,还是基于开源的 eclipse + gcc toolchains + openocd 的方案来开发和调试芯片产品,虽然这种方案实现快些,但是IDE太大太臃肿,且仅支持自家的芯片。如果能基于notepad--用QT实现一款,像Keil一样好用该多好。
最后,期待国产单片机开发IDE软件也能朝着更好用,易用的方向发展,变得更好。
其他资源
CMSIS 驱动程序规范
STM32 RTOS - GPIO Tutorial (CMSIS V2)
Documentation – Arm Developer
CMSIS-Driver Version 2.8.0
https://www.icxbk.com/article/detail?aid=483
相关文章:

还在老一套?STM32使用新KEIL5的IDE,全新开发模式RTE介绍及使用
Keil新版本出来了,推出了一种全新开发模式RTE框架( Run-Time Environment),更好用了。然而网上的教程资料竟还都是把Keil5当成Keil4来用,直接不使用这个功能。当前正点原子或野火的教程提供的例程虽有提到Keil5,但也是基本上当Kei…...

Java时间类(十一) -- Date类工具类 -- Java获取当天、本周、本月、本年 开始及结束时间
目录 1. 今天的日期如下: 2. DateUtils工具类的源代码: 3. 测试类 1. 今天的日期如下:...

Alma Linux 9.2、Rocky Linux 9.2现在是RHEL 9.2的替代品
随着Red Hat Enterprise Linux (RHEL) 9.2的发布,Alma Linux 9.2和Rocky Linux 9.2成为了RHEL 9.2的备选替代品。这两个Linux发行版旨在提供与RHEL兼容的功能和稳定性,以满足那些需要企业级操作系统的用户需求。本文将详细介绍Alma Linux 9.2和Rocky Lin…...

推荐5款提高生活和工作效率的好帮手
在这个数字化时代,软件工具已经深深地影响和改变了我们的生活和工作。有着各种各样的软件工具,它们都可以在特定的领域内让我们变得更加高效,完成复杂的任务。选择一款适合你的软件工具,不但可以极大地释放生产力,也可以让生活变得更加便捷。 1.桌面图标管理工具——TileIconi…...

美团小组长薪资被应届生员工倒挂7K,不把老员工当人?
一位美团的小管理爆出,无意中看到了整个部门薪资,本以为自己算比较高的,但看完之后整个人都傻眼了。小组长的职位月薪28K,而手下组员却是35K,当天晚上抽了一包烟也没想明白是为什么。 楼主表示,自己是美团的…...

【Java多线程案例】使用阻塞队列实现生产者消费者模型
前言 本篇文章讲解多线程案例之阻塞队列。主要讲解阻塞队列的特性、实际开发中常用的到的生产者消费者模型,以及生产者消费者模型解耦合、削峰填谷的好处。并且使用 Java 多线程模拟实现一个生产者消费者模型、阻塞队列版的生产者消费者模型。 文章从什么是阻塞队列…...

Spark 3:Spark Core RDD持久化
RDD 的数据是过程数据 RDD 的缓存 # coding:utf8 import timefrom pyspark import SparkConf, SparkContext from pyspark.storagelevel import StorageLevelif __name__ __main__:conf SparkConf().setAppName("test").setMaster("local[*]")sc SparkC…...

字节跳动五面都过了,结果被刷了,问了hr原因竟说是...
摘要 说在前面,面试时最好不要虚报工资。本来字节跳动是很想去的,几轮面试也通过了,最后没offer,自己只想到几个原因:1、虚报工资,比实际高30%;2、有更好的人选,这个可能性不大&…...

Python日期带时区转换工具类总结
文章目录 1.背景2. 遇到的坑3. 一些小案例3.1 当前日期、日期时间、UTC日期时间3.2 昨天、昨天UTC日期、昨天现在这个时间点的时间戳3.3 日期转时间戳3.4 时间戳转日期3.5 日期加减、小时的加减 4. 总结5. 完整的编码 1.背景 最近项目是国际项目,所以需要经常需要用…...

视频会议产品对比分析
内网视频会议系统如何选择?有很多单位为了保密,只能使用内部网络,无法连接互联网,那些SaaS视频会议就无法使用。在内网的优秀视频会议也有很多可供选择,以下是几个常用的: 1. 宝利通:它支持多种…...

每日一练 | 华为认证真题练习Day47
1、某台路由器输出信息如下,下列说法错误的是?(多选) A. 本路由器开启了区域认证 B. 本设备出现故障,配置的Router Id和实际生效的Router ID不一致 C. 本设备生效的Router Id为10.0.12.1 D. 本设备生效的Router Id为…...

ChatIE(LLM大模型用于信息抽取)
Zero-Shot Information Extraction via Chatting with ChatGPT paper:https://arxiv.org/abs/2302.10205 利用ChatGPT实现零样本信息抽取(Information Extraction,IE),看到零样本就能大概明白这篇文章将以ChatGPT作为…...

提升企业管理效率的利器——ADManager Plus
在当今信息时代,企业的规模和复杂性不断增长,管理各个方面变得愈发具有挑战性。而在企业管理中,活跃目录(Active Directory)起着至关重要的作用。它是一种用于组织内部的用户、计算机、组和其他对象进行集中管理的目录…...
《入侵的艺术》读书心得:第六章:渗透测试中的智慧与愚昧
第六章:渗透测试中的智慧与愚昧 这些想法是愚昧的 1.任何期待渗透测试结果是“毫无破绽”、“无懈可击”…都是极其愚昧的: 第一层含义:测试的不可穷尽性原理(同软件测试) 第二层含义:作为优秀甚至只是合…...

SAP-MM-采购申请-价值特性
采购申请审批在维护价值特性时要注意是抬头价值还是行价值,要确定选择哪个,配置时对应配置。 1、创建价值特性CT04 字段名称:CEBAN-GSWRT,和CEBAN-GFWRT 抬头总价值:CEBAN-GFWRT;如果选择的是抬头审批&am…...

设计模式 - 代理模式
基本介绍: 代理模式:为一个对象提供一个替身,以控制对这个对象的访问。即通过代理 对象访问目标对象.这样做的好处是:可以在目标对象实现的基础上,增强额外的 功能操作,即扩展目标对象的功能。被代理的对象可以是远程对象、创建开销大的对象或需要安全控…...

IOC初始化 IOC启动阶段 (Spring容器的启动流程)
[toc](IOC初始化 IOC启动阶段 (Spring容器的启动流程)) IOC初始化 IOC启动阶段 (Spring容器的启动流程) Resource定位过程:这个过程是指定位BeanDefinition的资源,也就是配置文件(如xml)的位置,并将其封装成Resource对…...
Java后端入职第四天,就被要求代码回退(Git回退实战)
一、需求背景 初入职场,由于自己的失误或者对git不熟悉,把被人的代码给冲突掉了,然后需要立马回滚,对于新手开发,应该比较常见吧!或者,比较多一种情况,错误把工程add了到了暂存区,比如一些本地配置,本来就不应该提交的,又或者,开发中只提交部分代码,又想最新的提…...
【swing】SplitPanel
当使用Java的Swing库来实现一个左右风格的SplitPanel时,可以使用JSplitPane作为容器,并在左边的面板中放置三个按钮,以及在右边的面板中显示图片。以下是一个示例代码: import javax.swing.*; import java.awt.*; import java.aw…...

网络货运平台源码 管理平台端+司机端APP+货主端APP源码
网络货运平台系统源码,网络货运平台源码 管理平台端司机端APP货主端APP 遵循政策要求的八项基本功能,结合货主、实际承运人、监管方等多方业务场景,构建人、车、货、企一体的标准化网络货运平台系统。具有信息发布、线上交易、全程监控、金融…...

Kafka入门-生产者
生产者 生产者发送流程: 延迟时间为0ms时,也就意味着每当有数据就会直接发送 异步发送API 异步发送和同步发送的不同在于:异步发送不需要等待结果,同步发送必须等待结果才能进行下一步发送。 普通异步发送 首先导入所需的k…...

LabVIEW双光子成像系统技术
双光子成像技术的核心特性 双光子成像通过双低能量光子协同激发机制,展现出显著的技术优势: 深层组织穿透能力:适用于活体组织深度成像 高分辨率观测性能:满足微观结构的精细研究需求 低光毒性特点:减少对样本的损伤…...
前端中slice和splic的区别
1. slice slice 用于从数组中提取一部分元素,返回一个新的数组。 特点: 不修改原数组:slice 不会改变原数组,而是返回一个新的数组。提取数组的部分:slice 会根据指定的开始索引和结束索引提取数组的一部分。不包含…...

Cilium动手实验室: 精通之旅---13.Cilium LoadBalancer IPAM and L2 Service Announcement
Cilium动手实验室: 精通之旅---13.Cilium LoadBalancer IPAM and L2 Service Announcement 1. LAB环境2. L2公告策略2.1 部署Death Star2.2 访问服务2.3 部署L2公告策略2.4 服务宣告 3. 可视化 ARP 流量3.1 部署新服务3.2 准备可视化3.3 再次请求 4. 自动IPAM4.1 IPAM Pool4.2 …...

【java面试】微服务篇
【java面试】微服务篇 一、总体框架二、Springcloud(一)Springcloud五大组件(二)服务注册和发现1、Eureka2、Nacos (三)负载均衡1、Ribbon负载均衡流程2、Ribbon负载均衡策略3、自定义负载均衡策略4、总结 …...

Linux操作系统共享Windows操作系统的文件
目录 一、共享文件 二、挂载 一、共享文件 点击虚拟机选项-设置 点击选项,设置文件夹共享为总是启用,点击添加,可添加需要共享的文件夹 查询是否共享成功 ls /mnt/hgfs 如果显示Download(这是我共享的文件夹)&…...

工厂方法模式和抽象工厂方法模式的battle
1.案例直接上手 在这个案例里面,我们会实现这个普通的工厂方法,并且对比这个普通工厂方法和我们直接创建对象的差别在哪里,为什么需要一个工厂: 下面的这个是我们的这个案例里面涉及到的接口和对应的实现类: 两个发…...

Python爬虫(52)Scrapy-Redis分布式爬虫架构实战:IP代理池深度集成与跨地域数据采集
目录 一、引言:当爬虫遭遇"地域封锁"二、背景解析:分布式爬虫的两大技术挑战1. 传统Scrapy架构的局限性2. 地域限制的三种典型表现 三、架构设计:Scrapy-Redis 代理池的协同机制1. 分布式架构拓扑图2. 核心组件协同流程 四、技术实…...
大模型的LoRa通讯详解与实现教程
一、LoRa通讯技术概述 LoRa(Long Range)是一种低功耗广域网(LPWAN)通信技术,由Semtech公司开发,特别适合于物联网设备的长距离、低功耗通信需求。LoRa技术基于扩频调制技术,能够在保持低功耗的同时实现数公里甚至数十公里的通信距离。 LoRa的主要特点 长距离通信:在城…...

5. TypeScript 类型缩小
在 TypeScript 中,类型缩小(Narrowing)是指根据特定条件将变量的类型细化为更具体的过程。它帮助开发者编写更精确、更准确的代码,确保变量在运行时只以符合其类型的方式进行处理。 一、instanceof 缩小类型 TypeScript 中的 in…...