当前位置: 首页 > news >正文

正点原子嵌入式linux驱动开发——STM32MP1启动详解

STM32单片机是直接将程序下载到内部 Flash中,上电以后直接运行内部 Flash中的程序。

STM32MP157内部没有供用户使用的 Flash,系统都是存放在外部 Flash里面的,比如 EMMC、NAND等,因此 STM32MP157上电以后需要从外部 Flash加载程序到内存中。而且STM32MP157支持多种启动方式,这些启动方式都是怎么运行的,这都涉及到 STM32MP1的启动方式。

STM32MP1启动模式

STM32MP1支持从多种设备启动,比如 EMMC、 SD、 NAND、 NOR、 USB、 UART等。STM32MP1内部有一段 ROM来存放 ST自己编写的程序,这段 ROM空间是不开放给用户使用的,仅供 ST存放自己的 ROM代码, ROM空间如下图所示:
内部ROM空间示意图
如上图中所示,CA7是Cortex-A7的缩写,可看出A7啮合有128KB的ROM空间,起始地址为0X00000000,STM32MP1上电后会先运行这段 ROM代码。 STM32MP157有三个 BOOT引脚: BOOT0~BOOT2,这三个 BOOT引脚通过拉高 /拉低来设置从哪种设备启动,正点原子
STM32MP157开发板上的拨码开关就是控制这三个 BOOT引脚的,正点原子开发板的BOOT原理图如下图所示:
BOOT原理图
从上图可看出,当 BOOT0-BOOT1拨到“ ON”的时候就会接到 3.3V上,此时就是逻辑 1,拨到 OFF的时候 BOOT0-BOOT1就悬空 (也可以外接下拉电阻 ),此时就是逻辑 0(悬空或接地都为逻辑 0),三个引脚不同电平对应的启动模式,如下图所示:
BOOT引脚启动模式
注: STM32MP1 的引脚具有复用功能,因此一个外设有很多不同引脚可以使用,比如 SDMMC2 的D0引脚就可以使用PB14或PE6 。STM32MP1 内部ROM代码肯定会有个默认的引脚,比如内部默认使用PB14,如果你自己绘制的板子用了 PE6 ,那么就会出问题。当然了,可以通过修改OTP来设置启动设备所使用的引脚,但是OTP只能改一次,如果改错了开发板就废了,所以如果自制开发板启动设备的引脚一定要和ST官方一致比较方便!

正点原子 STM32MP157开发板上通过拨码开关来选择启动模式,开发板上有丝印提示如何选择不同的启动方式,如下图所示:
开发板拨码开关
开发板拨码开关3个开关从左到右依次对应: BOOT0 BOOT1和 BOOT2。在查询启动方式的时候引脚号一定要对应好

STM32MP1启动流程详解

STM32MP1内部有一段 ST自己编写的 ROM代码,这段 ROM代码上电以后就会自动运行, ROM代码会读取 BOOT0~BOOT2这三个引脚电平,获取启动模式信息。

内部ROM代码

内部 ROM代码支持如下功能:

  1. Secure boot(安全启动),不管是串行启动还是从 Flash设备启动;
  2. Engineering boot(工程启动),当 BOOT2~BOOT0设置为 100的时候,我们就可以通过 STLINK访问A7或者M4内核;(一般是通过此方法来调试M4内核代码
  3. Secondary core boot(第二个内核启动),复位以后STM32MP157的每个A7内核都会启动,并且运行相同的指令。内部ROM代码会分离执行流,只有Core0才会运行ROM代码另外一个内核会处于一个死循环状态,等待应用程序发送信号来进行下一步操作。这个信号是由SGI(软中断)和另外两个BACKUP寄存器: MAGIC_NUMBER、 BRANCH_ADDRESS组成。(如果要启动Core1,运行在Core0的应用程序需要:将跳转地址写入 BRANCH_ADDRESS寄存器;将 0xCA7FACE1这个值写入到 MAGIC_NUMBER寄存器;向Core1和发送SGI中断)也就是说,只要不手动开启第二个核,那么由于内部 ROM代码的作用,此时STM32MP157就相当于单核A7。这样有利于我们编写的STM32MP157的A7裸机例程(因为只需要考虑单核情况);
  4. RMA boot,RMA是 Return Material Authorization的缩写,正点原子的教程中并没有涉及这种启动方式;
  5. 低功耗唤醒;
  6. 提供安全相关服务。

内部ROM的启动流程可以概括为下图:
ROM流程图
这张图中,红色的区域就是最常用的启动方法,所以我们需要重点关注!这一部分就是上电/复位后的运行流程:

  1. 首先检查当前是不是CPU0运行,如果不是就启动CPU1;(正常上电肯定是CPU0)
  2. 如果是CPU0,检查复位原因;
  3. 检查是否为退出Standby导致的复位,如果不是进入RMA检查;
  4. 检查是否为RMA启动,不是检查是否为ENGI启动;
  5. 检查是否为ENGI启动,不是就直接进冷启动;
  6. 进入冷启动后就从FLASH中加载系统并进行鉴权,如果鉴权成功就运行系统。

安全启动

首先了解两个概念:

  • FSBL 全称为 First stage boot loader,也就是第一阶段启动文件;
  • SSBL 全称为 Second stage boot loader,也就是第二阶段启动文件。

当设置好BOOT2~BOOT0,选择从外部 Flash,比如 EMMC、 NAND或 NOR等启动的时候就会进入安全启动流程。 STM32MP157的安全启动流程比较复杂,这里只做大概介绍:

  1. 首先ROM代码从选定的Flash设备中加载 FSBL镜像文件FSBL镜像就是ROM加载的第一个用户编写的可执行程序一般是TF-A镜像,但是我们可以换成自己编写的程序,比如A7裸机代码。(这个FSBL镜像是有要求的,不是简单的把bin文件丢过来就可以,而是需要在bin文件前面添加一个头部信息
  2. FSBL镜像加载后需要对其进行鉴权
  3. 如果鉴权成功,那么就会跳转到FSBL镜像入口地址,开始运行FSBL固件

内部ROM首先从选定的 Flash设备中读取 FSBL镜像文件并运行,但是此时DDR还没有初始化,FSBL镜像要存放在内部RAMST32MP1内部有 256KB的SYSRAM,如下图所示:
STM32MP1内部SYSRAM

从上图中可看出,SYSRAM地址范围为: 0X2FFC0000~0X2FFFFFFF,一共是 256KB。ROM代码会将FSBL镜像拷贝到0X2FFC2400地址,但是要注意,FSBL镜像的起始地址不是0X2FFC2400,因为FSBL镜像前面还有一个 256(0X100)字节的头部信息,因此 FSBL镜像的真正起始地址为0X2FFC2400+0X100=0X2FFC2500。(搞清楚这个起始地址,是为了方便在之后将FSBL镜像换成A7裸机例程,编译的时候要指定链接起始地址,这个链接起始地址就是 0X2FFC2500)由此可以计算出整个FSBL镜像大小不能超过 0X30000000-0X2FFC2500=252672B=246.75KB

FSBL镜像鉴权成功后, ROM代码会boot上下文的起始地址保存到R0寄存器,然后跳转到 FSBL镜像的入口地址,这个入口地址会定义到头部里面,其实就是上面讲的 0X2FFC2500

boot上下文会保存到SYSRAM的前512字节里面,boot上下文包含了boot信息,比如选定的 boot设备,还有一些和安全启动鉴权有关的服务。结构体boot_api_context_t定义了 boot上下文结构,boot_api_context_t结构体是定义在
TF-A源码里面的(plat/st/stm32mp1/include/boot_api.h),内容如下所示:

typedef struct {/* * Boot interface used to boot : take values from defines * BOOT_API_CTX_BOOT_INTERFACE_SEL_XXX above */ uint16_t boot_interface_selected; uint16_t boot_interface_instance; uint32_t reserved1[12]; uint32_t usb_context; uint32_t otp_afmux_values[3]; uint32_t reserved[2]; /** Log to boot context, what was the kind of boot action * takes values from defines BOOT_API_BOOT_ACTION_XXX above*/uint32_t boot_action;/** STANDBY Exit status to be checked by FSBL in case* field 'boot_action' == BOOT_API_CTX_BOOT_ACTION_WAKEUP_STANDBY* take values from defines above 'BOOT_API_CTX_STBY_EXIT_STATUS_XXX'* depending on encountered situation*/uint32_t stby_exit_status;/* * CSTANDBY Exit status to be checked by FSBL in case* boot_action == BOOT_API_CTX_BOOT_ACTION_WAKEUP_CSTANDBY * take values from defines above 'BOOT_API_CTX_CSTBY_EXIT_STATUS_XXX'* depending on encountered situation*/uint32_t cstby_exit_status;uint32_t auth_status;/** Pointers to bootROM External Secure Services* - ECDSA check key* - ECDSA verify signature* - ECDSA verify signature and go*/uint32_t (*bootrom_ecdsa_check_key)(uint8_t *pubkey_in,uint8_t *pubkey_out);uint32_t (*bootrom_ecdsa_verify_signature)(uint8_t *hash_in,uint8_t *pubkey_in,uint8_t *signature,uint32_t ecc_algo);uint32_t (*bootrom_ecdsa_verify_and_go)(uint8_t *hash_in,uint8_t *pub_key_in,uint8_t *signature,uint32_t ecc_algo,uint32_t *entry_in);/** Information specific to an SD boot* Updated each time an SD boot is at least attempted,* even if not successful* Note : This is useful to understand why an SD boot failed* in particular*/uint32_t sd_err_internal_timeout_cnt;uint32_t sd_err_dcrc_fail_cnt;uint32_t sd_err_dtimeout_cnt;uint32_t sd_err_ctimeout_cnt;uint32_t sd_err_ccrc_fail_cnt;uint32_t sd_overall_retry_cnt;/** Information specific to an eMMC boot* Updated each time an eMMC boot is at least attempted,* even if not successful* Note : This is useful to understand why an eMMC boot failed* in particular*/uint32_t emmc_xfer_status;uint32_t emmc_error_status;uint32_t emmc_nbbytes_rxcopied_tosysram_download_area;uint32_t hse_clock_value_in_hz;/** Boot partition :* ie FSBL partition on which the boot was successful*/uint32_t boot_partition_used_toboot;/** Address of SSP configuration structure :* given and defined by bootROM* and used by FSBL. The structure is of type* 'boot_api_ssp_config_t'*/boot_api_ssp_config_t *p_ssp_config;/** boot context field containing bootROM updated SSP Status* Values can be of type BOOT_API_CTX_SSP_STATUS_XXX*/uint32_t ssp_status;/* Pointer on ROM constant containing ROM information */const boot_api_rom_version_info_t *p_rom_version_info;} __packed boot_api_context_t;

boot_api_context_t结构体目前不需要去研究,后面学习 TF-A的时候根据实际情况在看是
否有必要学习。

串行启动

当我们设置BOOT2~BOOT0为串行启动,也就是从USB或UART启动的时候就会进入此模式。当选择串行启动以后ROM代码就会并行扫描所有可以启动的UART以及USB OTG接口。当扫描到某个活动的串行接口以后, ROM代码就会使用此串行接口,并且忽略掉其他的串行接口

USB启动

内部ROM代码支持USB OTG启动,我们一般使用 STM32CubeProgrammer软件通过USB OTG接口来向STM32MP1烧写系统USB OTG需要一个48M和60M的时钟,这两个时钟由HSE生成。 ROM代码支持的 HSE时钟值如下:

8,10,12,14,16,20,24,25,26,28,32,36,40,48 MHz

正点原子STM32MP157开发板使用24M有源晶振作为HSE时钟源。可通过设置OTP来更改ROM代码的HSE晶振大小,设置如下图所示:
HSE时钟OTP设置

从上图中可以看出,默认情况下HSE选择 24MHz,虽然可以通过修改OTP来更改HSE,但是强烈不建议!因为OTP只能修改一次,一旦修改错误芯片就废了!

UART启动

如果要选择UART启动,也就是通过UART烧写系统,那么只能使用USART2、USART3、UART4、UART5、USART6、UART7或UART8,此时串口工作模式为1位起始位、8位数据位、偶校验、1位停止位、波特率115200

由于STM32的IO复用功能,1个串口可能有多个 IO可以使用,比如UART4的RX(接收)可以使用PI10、PH14、PA1、PA11、PB2、PB8、PC11 PD0或PD2,一共9个IO可以用作UART4_RX引脚,但是ROM代码里面的UART4_RX引脚肯定只会使用这个9个里面的其中一个,所以我们板子的串口引脚要和ROM代码里面的一致,否则就无法使用串口启动。ROM代码里面串口使用的引脚如下图所示:
ROM代码串口默认IO
如果要使用串口启动,那么相关串口IO一定要参考上图中定义的IO引脚,比如正点原子开发板UART4的RX引脚使用PB2,TX引脚使用PG11。

FLASH设备启动要求

STM32MP1支持从SD、EMMC、NAND或NOR等 Flash设备启动,但是不同的Flash设备在启动的时候有不同的要求。linux系统自身编译出来就是一个镜像文件,但是这个镜像文件要运行是需要一大堆的“小弟”来辅助。比如需要uboot来启动,启动以后还需要根文件系统 (rootfs),传统的嵌入式linux有三巨头: uboot、 kernel和 rootfs,但是对于STM32MP1而言,又多了几个需求,比如TF-A、TEE、vendorfs等,所有这一大堆构成了 最终的系统镜像。系统镜像是要烧写到Flash设备中的,这些不同的文件肯定要按照一定的要求,分门别类的烧写并存放到指定位置

针对Flash设备,可以通过创建不同的分区来存放不同的文件,ST针对STM32MP1系列给出了官方分区建议,这些建议包含了Flash分区数量、分区最小空间、分区存放的内容等,如下图所示:
ST官方的Flash分区推荐

NAND启动

NAND前几个块(block)里面包含了多份FSBLROM代码会从第一个块开始扫描,并且加载第一个有效块里面的FSBL。ROM代码支持并行NAND和串行NAND,并行NAND连接到FMC总线上,串行NAND连接到QSPI上

ROM代码支持的并行NAND要求如下图所示:
并行NAND要求
ROM代码支持串行NAND要求如下图所示:
串行NAND要求

EMMC启动

EMMC在物理结构上有boot1、boot2、RPMB(Replay Protected Memory Block)、GPP(General Purpose Partitions GPP最多 4个分区)以及UDA(User Data Area)这5种分区,比如三星的KLM系列EMMC 5.1的分区结构如下图所示:
三星KLM系列EMMC分区结构
一般常用的就是UDA分区,也就是用户数据区域,很少会关心boot1、boot2这样的分区。boot1、 boot2、 RPMB这三个分区大小是固定的,用户不能修改, boot1、 boot2分区
存在的意义就是用于引导系统。正点原子STM32MP157开发板所使用的EMMC型号为KLM8G1GETF,这是三星的一颗8GB EMMC 5.1芯片,boot1、boot2和RPMB分区大小如下图所示:
KLM系列EMMC分区
从图中可以看出,对于三星的8GB的EMMC而言,boot1和boot2分区默认大小为4096KB,RPMB 为512KB

ST会使用EMMC的boot1和boot2这两个分区作为FSBL,但是同一时间只有一个有效,ROM代码会加载有效的那个FSBL。ROM代码使用单bit模式来操作EMMC,默认情况下ROM代码使用连接到SDMMC2上的EMMC,可以通过OTP来修改EMMC所使用的SDMMC接口,但是这里不建议(OTP只能更改一次)!

SD卡启动

SD卡也包含两个FSBL,但是SD卡没有boot1和boot2这样的物理分区。ROM代码默认尝试加载第一个FSBL,如果第一个FSBL加载失败,那么ROM代码就会加载第二个FSBL

ROM代码首先在SD卡上查找GPT分区,如果找到的话就查找名字以“fsbl”开始的两个FSBL分区。如果没有找到GPT分区的话就直接根据物理地址查找两个FSBL,第一个FSBL的起始偏移地址为LBA34,地址为34512=17408=0X4400,所以第一个FSBL的起始地址为0X4400。第二个FSBL的起始偏移地址为LBA546,地址为546512=279552=0X44400,所以第二个FSBL的起始地址为0X44400。

ROM代码默认也是使用单bit模式操作SD卡,并且默认使用连接到SDMMC1接口上的
SD卡

STM32MP1二进制头部信息

之前的内容就是讲了:ROM代码会先读取FSBL代码,一般是TF-A或者Uboot的SPL,也可以是A7 裸机代码。比如TF-A我们直接编译生成二进制bin文件,但是这个bin文件不能直接拿来用,需要在前面添加一段头部信息,这段头部信息也包含了鉴权内容。加入头部信息以后的FSBL代码结构如下图所示:
FSBL镜像组织结构
头部信息一共是256字节,这些头部信息的具体含义如下图所示:
头部信息含义
头部信息不需要手动添加,在编译ST官方提供的TF-A或者Uboot的时候会自动添加,因为ST提供了个名为“stm32image”的工具专门用于在bin文件前面添加头部信息。这个工具就是一个stm32image.c文件,在正点原子的开源资料中是有的,可以直接获取;在编写A7裸机的时候需要自己使用stm32image工具在bin文件前面添加头部信息, stm32image是在Ubuntu下运行的,所以需要先编译,将stm32image.c发送到Ubuntu下,然后输入如下命令编译:

gcc stm32image.c -o stm32image

编译成功以后就会生成一个名为stm32image的可执行文件,如下图所示:
stm32image工具
运行上图中编译出来的 stm32image工具,输入“ “-s”选项可以查看使用方法,stm32image在使用中需要搭配一系列参数:

-s:指定源文件;
-d:生成的目标文件;
-l:加载地址;
-e:入口地址;
-m:主版本号;
-n 次版本号。

在正点原子的开发板光盘里面找到正点原子出厂的tf-a固件,里面的tf-a-stm32mp157d-atk-trusted.stm32就是加入了头部信息的 TF-A可执行文件。使用winhex软件打开 tf-a-stm32mp157d-atk-trusted.stm32,如下图所示:
tf-a-stm32mp157d-atk-trusted.stm32内容
上图tf-a-stm32mp157d-atk-trusted.stm32文件原始数据,其中前 256个字节就是头部信息。可以分析一下tf-a头部信息中几个比较重要的参数:

  • Magic number:起始偏移地址为0,长度为4个字节,值依次为0X53、0X54、0X4D、0X32,合起来就是0X53544D32,这个就是魔术数 ,注意这四个字节的顺序是大端模式,即高字节数据存放在底地址处,低字节数据存放在高字节处;
  • Header Version:起始偏移地址为72,长度为4个字节,也就是上图中第 72~75这4个字节的数据,分别为: 0X00、0X00、0X01和0X00;整个头部信息中,除了Magic number采用大端模式存储以外,其他都是小端模式存储,也就是低字节数据存放在底地址处,高字节数据存放在高字节处。因此0X00、0X00、0X01和0X00这四个字节的拼出结果为 0X00010000;
  • Image length:起始偏移地址为76,长度为4个字节,也就是上图76~79这4个字节的数据,为:0X40、0XB0、0X03和0X00,按照小端模式拼起来就是0X0003B040=241728,约为236.1KB,说明此TF-A的 bin镜像大小为236.1KB
  • Image entry Point:起始偏移地址为80,长度为4个字节,也就是图80~83这4个字节的数据,为:0X00、0X60、0XFD和0X2F,按照小端模式拼起来就是0X2FFD6000,说明入口地址为 0X2FFD6000
  • Load address:起始偏移地址为88,长度为4个字节,也就是上图88~91这4个字节的数据,为:0X00、0X25、0XFC和0X2F,按照小端模式拼起来就是0X2FFC2500,说明加载地址为0X2FFC2500,就是FSBL镜像起始地址
  • Binary type:起始偏移地址为255,也就是最后一个字节,为0X10,表示当前二进制文件是TF-A

STM32MP1 Linux系统启动过程

STM32MP1是面向Linux领域的,因此以上所有启动过程都是为了启动Linux内核。 STM32MP1启动 Linux内核的流程如图所示:
STM32MP1 Linux启动流程
从上图中可以看出,STM32MP1启动linux内核一共分为5个步骤

  1. ROM代码

这是ST自己编写的代码,在STM32MP1出厂的时候就已经烧写进去的,不能被修改的。ROM 代码因为保存在STM32内部ROM里面,因此也就直接简单明了的叫做“ROM 代码”了。它是处理器上电以后首先执行的程序,ROM代码的主要工作就是读取STM32MP1的BOOT引脚电平,然后根据电平判断当前启动设备,最后从选定的启动设备里面读取FSBL代码,并将FSBL代码放到对应的RAM空间

为了安全性的要求,从上图中可以看出。STM32MP1启动Linux内核的过程是一个链式结构:ROM Code→FSBL→SSBL→Linux kernel→rootfs,系统启动的过程中要保证整个链式结构都是安全的。ROM代码作为第一链,首先要对FSBL代码进行鉴权,同样的,FSBL以及后面的每一链都要对下一个阶段的镜像进行鉴权,直到设备系统正确启动。

  1. FSBL

FSBL代码初始化时钟树、初始化外部RAM控制器,也就是DDR。最终FSBL将SSBL加载到DDR里面并运SSBL代码

一般FSBL代码是TF-A或者Uboot的SPL代码,当然也可以换成A7内核裸机代码。

  1. SSBL

由于SSBL代码运行在DDR里面,无需担心空间不够,因此SSBL代码的功能就可以做的很全面,比如使能USB、网络、显示等等。这样我们就可以在SSBL中灵活的加载 linux内核,比如从Flash设备上读取,或者通过网络下载下载等,用户使用起来也非常的友好。SSBL一般是Uboot,用来启动Linux内核

  1. Linux内核

SSBL部分的Uboot就一个使命,启动Linux内核,Uboot会将Linux内核加载到DDR上并运行Linux内核启动过程中会初始化板子上的各种外设

  1. Linux用户空间

系统启动的时候会通过init进程切换到用户空间,在这个过程中会初始化根文件系统里面的各种框架以及服务

总结

这一章的主要内容就是大概介绍了一下STM32MP1系列的启动过程,介绍了STM32MP1的启动模式是通过3个BOOT引脚的切换进行选择;启动通过ROM代码运行FSBL代码(一般是TF-A代码,也可以是A7裸机代码);主要的内容就是启动的部分,可以从USB或者UART启动,也可以Flash设备启动SSBL是第二阶段的启动,这其中就是启动Linux内核和用户空间

相关文章:

正点原子嵌入式linux驱动开发——STM32MP1启动详解

STM32单片机是直接将程序下载到内部 Flash中,上电以后直接运行内部 Flash中的程序。 STM32MP157内部没有供用户使用的 Flash,系统都是存放在外部 Flash里面的,比如 EMMC、NAND等,因此 STM32MP157上电以后需要从外部 Flash加载程序…...

FPGA的数字钟带校时闹钟报时功能VHDL

名称:基于FPGA的数字钟具有校时闹钟报时功能 软件:Quartus 语言:VHDL 要求: 1、计时功能:这是数字钟设计的基本功能,每秒钟更新一次,并且能在显示屏上显示当前的时间。 2、闹钟功能:如果当前的时间与闹钟设置的时…...

分析各种表达式求值过程

目录 算术运算与赋值 编译器常用的两种优化方案 常量传播 常量折叠 加法 Debug编译选项组下编译后的汇编代码分析 Release开启02执行效率优先 减法 Release版下优化和加法一致,不再赘述 乘法 除法 算术结果溢出 自增和自减 关系运算与逻辑运算 JCC指…...

企业风险管理策略终极指南

企业风险管理不一定是可怕的。企业风险管理是一个模糊且难以定义的主题领域。它涵盖了企业的多种风险和程序,与传统的风险管理有很大不同。 那么,企业风险管理到底是什么?在本文中,我们将确定它是什么,提出两种常见的…...

OpenCV之分水岭算法(watershed)

Opencv 中 watershed函数原型: void watershed( InputArray image, InputOutputArray markers ); 第一个参数 image,必须是一个8bit 3通道彩色图像矩阵序列,第一个参数没什么要说的。关键是第二个参数 markers,Opencv官方文档的说…...

npm 命令

目录 初始化 搜索 安装 删除 更新 换源 查看 其他 补充 1.初始化 npm init #初始化一个package.json文件 npm init -y | npm init --yes 2.搜索 npm s jquery | npm search jquery 3.安装 npm install npm -g #更新到最新版本 npm i uniq | npm ins…...

【bug 记录】yolov5_C_demo 部署在 rv1126

问题1:opencv find 不到 在 CMakeLists 中将正确的 OpenCV库 路径添加到 CMAKE_PREFIX_PATH 变量中 set(CMAKE_PREFIX_PATH “/mnt/usr/local” ${CMAKE_PREFIX_PATH}) 问题2: rknn_api.h 找不到 将该文件从别处复制到项目 include 文件夹 问题3&…...

[vue-admin-template实战笔记]

1.克隆项目 git clone gitgitee.com:panjiachen/vue-admin-template.git 2.安装依赖 npm install 3.运行项目就会自动打开网页,并且热部署插件 npm run dev 4.查看代码 //将vue-admin-template拖入到idea中即可查看代码 1)并且发现,常用的东西已经集…...

unity 限制 相机移动 区域(无需碰撞检测)

限制功能原著地址:unity限制相机可移动区域(box collider)_unity限制相机移动区域_manson-liao的博客-CSDN博客 一、创建限制区域 创建一个Cube,Scale大小1,添加组件:BoxCollder,调整BoxColld…...

Hudi第二章:集成Spark

系列文章目录 Hudi第一章:编译安装 Hudi第二章:集成Spark 文章目录 系列文章目录前言一、安装Spark1、安装Spark2.安装hive 二、spark-shell1.启动命令2.插入数据3.查询数据1.转换DF2.查询 3.更新4.时间旅行5.增量查询6.指定时间点查询7.删除数据1.获取…...

springboot和vue:八、vue快速入门

vue快速入门 新建一个html文件 导入 vue.js 的 script 脚本文件 <script src"https://unpkg.com/vuenext"></script>在页面中声明一个将要被 vue 所控制的 DOM 区域&#xff0c;既MVVM中的View <div id"app">{{ message }} </div…...

docker-compose内网本地安装

1&#xff1a;通过包管理器安装 Docker Compose&#xff0c;请按照以下步骤进行操作&#xff1a; 首先&#xff0c;确保你的系统上已经安装了 Docker。如果尚未安装 Docker&#xff0c;请根据你的操作系统使用适当的包管理器进行安装打开终端&#xff0c;并运行以下命令下载 D…...

ThreeJs的场景实现鼠标拖动旋转控制

前面一个章节中已经实现在场景中放置一个正方体&#xff0c;并添加灯光使得正方体可见。但是由于是静态的还不能证明是3D的&#xff0c;我们需要添加一些控制器&#xff0c;使得通过鼠标控制正方体可以动起来&#xff0c;实现真正的3D效果&#xff0c;由此引入OrbitControls组件…...

jdk 管理工具比对 jEnv jabba SDKMAN

jEnv、jabba、SDKMAN 这三个 JDK 管理工具进行的比对&#xff1a; jEnv&#xff1a; 地址&#xff1a;https://github.com/jenv/jenv 作者&#xff1a;Gildas Cuisinier 最后更新时间&#xff1a;2021年5月26日 开发语言&#xff1a;Shell Jabba&#xff1a; 地址&#xff1…...

华为云云耀云服务器L实例评测|部署在线图表和流程图绘制工具drawio

华为云云耀云服务器L实例评测&#xff5c;部署在线图表和流程图绘制工具drawio 一、云耀云服务器L实例介绍1.1 云服务器介绍1.2 优势及其应用场景1.3 支持镜像 二、云耀云服务器L实例配置2.1 重置密码2.2 服务器连接2.3 安全组配置 三、部署 drawio3.1 drawio 介绍3.2 Docker 环…...

elementui引入弹出框报错:this.$alert is not defined 解决方案

1.按需引入文件element.js 注意&#xff1a;引入Message&#xff0c;MessageBox两个组件就行&#xff0c;alert包括在MessageBox里面了。 之前我引入了Alert组件&#xff0c;发现不行 2.在vue的prototype里注册伪名字 3.组件里直接调用就行了 4.实现效果 我发现elementui调用…...

docker的组件和资源管理

Docker是一种开源的容器化平台&#xff0c;它提供了一种轻量级、可移植和可扩展的方式来打包、部署和运行应用程序。Docker的构成包括以下几个关键组件&#xff1a; Docker Engine&#xff1a;Docker Engine是Docker的核心组件&#xff0c;它负责管理容器的生命周期和资源隔离…...

SEO的优化教程(百度SEO的介绍和优化)

百度SEO关键字介绍&#xff1a; 百度SEO关键字是指用户在搜索引擎上输入的词语&#xff0c;是搜索引擎了解网站内容和相关性的重要因素。百度SEO关键字可以分为短尾词、中尾词和长尾词&#xff0c;其中长尾词更具有针对性和精准性&#xff0c;更易于获得高质量的流量。蘑菇号-…...

Tomcat以及UDP

一、Tomcat 服务端 自定义 S Tomcat服务器 S &#xff1a;Java后台开发 客户端 自定义 C 浏览器 B 认识一些常用的目录&#xff1a; bin&#xff1a;存放开始和结束的程序 conf&#xff1a;配置文件 lib&#xff1a;组成包 logs&#xff1a;输出日志 webapps&#x…...

NLP 04(GRU)

一、GRU GRU (Gated Recurrent Unit)也称门控循环单元结构,它也是传统RNN的变体,同LSTM一样能够有效捕捉长序列之间的语义关联&#xff0c; 缓解梯度消失或爆炸现象&#xff0c;同时它的结构和计算要比LSTM更简单,它的核心结构可以分为两个部分去解析: 更新门、重置门 GRU的内…...

BUUCTF reverse wp 51 - 55

findKey shift f12 找到一个flag{}字符串, 定位到关键函数, F5无效, 大概率是有花指令, 读一下汇编 这里连续push两个byte_428C54很奇怪, nop掉下面那个, 再往上找到函数入口, p设置函数入口, 再F5 LRESULT __stdcall sub_401640(HWND hWndParent, UINT Msg, WPARAM wPara…...

WebGL笔记:使用鼠标绘制多个线条应用及绘制动感线性星座

使用鼠标绘制多个线条 多个线条&#xff0c;肯定不是一笔画过的&#xff0c;而是多次画的线条既然是多线&#xff0c;那就需要有个容器来管理它们 1 &#xff09;建立容器对象 建立一个 lineBox 对象&#xff0c;作为承载多边形的容器 // lineBox.js export default class …...

nodejs+vue 汽车销售系统elementui

第三章 系统分析 10 3.1需求分析 10 3.2可行性分析 10 3.2.1技术可行性&#xff1a;技术背景 10 3.2.2经济可行性 11 3.2.3操作可行性&#xff1a; 11 3.3性能分析 11 3.4系统操作流程 12 3.4.1管理员登录流程 12 3.4.2信息添加流程 12 3.4.3信息删除流程 13 第四章 系统设计与…...

leetcode76 Minimum Window Substring

给定两个字符串s和t&#xff0c; 找到s的一个子串&#xff0c;使得t的每个字符都出现在子串中&#xff0c;求最短的子串 由于要每个字符出现&#xff0c;所以顺序其实没有关系 因此我们可以定义一个map&#xff0c;统计t中字符出现次数 然后在s中慢慢挪动滑动窗口&#xff0c;…...

简单工厂模式~

我们以生产手机作为应用场景展开讲解&#xff01; 手机是一个抽象的概念&#xff0c;它包含很多的品牌&#xff0c;例如华为&#xff0c;苹果&#xff0c;小米等等&#xff0c;因此我们可将其抽象为一个接口&#xff0c;如下所示&#xff1a; public interface tel {void pro…...

基于Java的会员管理系统设计与实现(源码+lw+部署文档+讲解等)

文章目录 前言具体实现截图论文参考详细视频演示为什么选择我自己的网站自己的小程序&#xff08;小蔡coding&#xff09;有保障的售后福利 代码参考源码获取 前言 &#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计划导师、全栈领域优质创作…...

数据结构 图 并查集 遍历方法 最短路径算法 最小生成树算法 简易代码实现

文章目录 前言并查集图遍历方法广度优先遍历深度优先遍历 最小生成树算法Kruskal算法Prim算法 最短路径算法Dijkstra算法BellmanFord算法FloydWarshall算法 全部代码链接 前言 图是真的难&#xff0c;即使这些我都学过一遍&#xff0c;再看还是要顺一下过程&#xff1b;说明方…...

idea Springboot 教师标识管理系统开发mysql数据库web结构java编程计算机网页源码maven项目

一、源码特点 springboot 教师标识管理系统是一套完善的信息系统&#xff0c;结合springboot框架和bootstrap完成本系统&#xff0c;对理解JSP java编程开发语言有帮助系统采用springboot框架&#xff08;MVC模式开发&#xff09;&#xff0c;系统 具有完整的源代码和数据库&…...

2023-9-30 JZ36 二叉搜索树与双向链表

题目链接&#xff1a;二叉搜索树与双向链表 import java.util.*; /** public class TreeNode {int val 0;TreeNode left null;TreeNode right null;public TreeNode(int val) {this.val val;}} */ public class Solution {TreeNode pre null;public TreeNode Convert(Tree…...

在windows的ubuntu LTS中安装及使用EZ-InSAR进行InSAR数据处理

EZ-InSAR&#xff08;曾被称为MIESAR&#xff0c;即Matlab界面用于易于使用的合成孔径雷达干涉测量&#xff09;是一个用MATLAB编写的工具箱&#xff0c;用于通过易于使用的图形用户界面&#xff08;GUI&#xff09;进行干涉合成孔径雷达&#xff08;InSAR&#xff09;数据处理…...

wordpress去除顶部工具栏/商城小程序

JavaScript事件绑定的方法说明 本文为转载 原作者不明 罪过罪过事件使得客户端的 JavaScript 有机会被激活&#xff0c;并得以运行。在一个 Web 页面装载之后&#xff0c;运行脚本的唯一方式&#xff0c;就是响应系统或者用户的动作。虽然从第一个支持脚本编程的浏览器面世以来…...

哈尔滨网站建设制作/百度知道官网手机版

当你用AngularJS写的应用越多, 你会越发的觉得它相当神奇. 之前我用AngularJS实现了相当多酷炫的效果, 所以我决定去看看它的源码, 我想这样也许我能知道它的原理. 下面是我从源码中找到的一些可以了解AngularJS那些高级(和隐藏)功能如何实现的代码. 1) 依赖注入的实现原理 依赖…...

网页设计素材为什么拖不进ps/杭州seo托管公司推荐

假设需要在现在的这个网址中截取一级域名&#xff1a; www.cnblogs.com/jstwt/admin/EditPosts.aspx?opt1 可使用如下代码&#xff1a; source_namesubstr( www.cnblogs.com/jstwt/admin/EditPosts.aspx?opt1,/*截取的目标字符串*/ 1,/*起始截取位置*/ find(www.cnblogs.com/…...

做二手房需要用到哪些网站搜集房源/东莞网站设计公司

未完成 博客 松中提高组模拟赛 8.23 的三题LCA未完成的题松中提高组模拟赛 8.24 未改的两题ybtoj 电路维修 逃离噩梦 单词替换ybtoj 背包&#xff0c;区间DP 题目 洛谷 P6374洛谷 P2880SSL 2873校内20201026普及组T3 知识点 RMQ未学会线段树、树状数组、树形DP需复习 &…...

百度网站录入/什么叫外链

概述 在Java中&#xff0c;提供了四种访问权限控制 分别是&#xff1a;default&#xff08;默认&#xff09;、public&#xff08;公开&#xff09;、private&#xff08;私有&#xff09;、protected&#xff08;受保护&#xff09; 区别 访问控制权限&#xff08;√代表可…...

广州做网站一般要多少钱?/上海专业的网络推广

上一篇camera前后摄像头同时预览的后续&#xff0c;前后摄像头同时录像。 直接上重要的代码就好&#xff0c;新手&#xff0c;写得不好的欢迎提出&#xff0c;见谅。 /** * 初始化控件以及回调 */ private void init() { surfaceview1 (SurfaceView) this.findViewById(R.id.s…...