i.MX6裸机开发(9):CCM时钟控制模块

本章参考资料:《IMX6ULRM》(参考手册)。

学习本章时,配合《IMX6ULRM》第18章Clock Controller Module (CCM),效果会更佳,特别是涉及到寄存器说明的部分。

本章我们主要讲解时钟部分,芯片内部的各个设备都在时钟的驱动下运行,了解整个芯片的时钟树、时钟配置,那么对i.MX 6U的一切时钟的来龙去脉都会了如指掌。

1. 时钟控制模块(CCM)的主要作用

i.MX 6U的时钟系统由时钟控制模块CCM进行控制,其主要功能如下:

  • 使用PLL锁相环电路将参考时钟倍频,得到频率更高的时钟。为芯片内核和外设提供可选的时钟源。i.MX 6U共有7个PLL锁相环电路,分别为:

    ARM PLL(PLL 1)、System PLL(PLL 2)、USB1 PLL(PLL 3)、Audio PLL(PLL 4)、Video PLL(PLL5)ENET PLL(PLL 6)、USB2 PLL(PLL 7)。

  • 提供PLL控制寄存器、时钟选择寄存器、时钟分频寄存器。灵活控制输出到外设和内核的时钟频率。

  • 控制低功耗机构。

时钟控制模块

CCM_CLK_IGNITION模块

管理从外部晶振时钟到稳定的根时钟输出的整个过程。CCM完成重置之后CCM_CLK_IGNITION模块立即启动。 GPC是General Power Controller的缩写,即总电源管理模块,它不属于CCM,系统电压与时钟关系密切, 简单来说,系统电压影响系统最高的时钟频率,CCM又可以控制总电源管理模块(GPC)进入待机或低功耗状态。 相关内容将会在GPC章节详细介绍。

PLL时钟产生

CCM_ANALOG为CCM的模拟部分,作用是将频率较低的参考时钟(例如24MHz的XTALOSC时钟)使用PLL锁相环电路倍频到更高的时钟。 CCM_CLK_SWITCHER模块接收来自CCM_ANALOG模块的锁相环时钟输输出,以及锁相环的旁路时钟, 并为CCM_CLK_ROOT_GEN子模块生成切换时钟输出(pll3_sw_clk)。i.MX 6U共有7个PLL锁相环电路,可以独立配置。 其中PLL2与PLL3结合PFD能够输出多个频率可调的时钟。

根时钟生成

CCM_CLK_ROOT_GEN接收来自CCM_CLK_SWITCHER模块的PLL或PFD时钟,经过时钟的选择、分频等操作之后产生并输出根时钟。根时钟将会作内核或外设的时钟源。

时钟同步

当更改某些时钟的时钟源时需要进行时钟的同步CCM_HND_SK模块用于管理时钟握手,即时钟的同步。

低功耗管理与时钟启用模块

CCM_LPM用于管理低功耗模式,管理时钟的开启与关闭。CCM_CLK_LOGIC,根据来自CCM_LPM模块和CCM_IP的信号产生时钟启用或关闭信号。

低功耗时钟门控模块(LPCG)

低功耗时钟门控模块(LPCG)根据CCM_CLK_LOGIC模块输出信号控制时钟输出。时钟越多、频率越高功耗也就越高。关闭没有使用的时钟或降低时钟频率能够有效的降低功耗。

2. 时钟树简介

i.MX 6U芯片时钟的结构以时钟树的方式进行描述。当我们设置外设时钟时大多会参考时钟树进行设置, 本章配套程序将以ARM_CLK_ROOT(CPU根时钟)为例讲解时钟的配置方法。

时钟树

时钟树

2.1. PLL与PFD时钟产生

i.MX 6U外部连接了两个晶振,分别用于提供32.768KHz和24MHz时钟。其中32.768KHz晶振称为外部低速时钟(CKIL), 当芯片上电后要保持该时钟一直处于运行状态,为需要的外设提供时钟。24Mhz晶振产生的时钟称为外部高速时钟(CKIH)。芯片利用内部振荡器产生基准时钟(OSC),内部振荡器产生基准时钟(OSC)基于外部高速时钟(CKIH),产生24MHz的参考时钟, 24MHz参考时钟常用作PLL锁相环电路的输入时钟。

下面以ARM PLL(PLL 1)为例讲解从24MHz参考时钟到ARM PLL(PLL 1)时钟的过程,如下所示。

24MHz参考时钟到ARM PLL(PLL 1)时钟

ARM PLL(PLL 1)只有一个控制寄存器CCM_ANALOG_PLL_ARMn,CCM_ANALOG_PLL_ARMn[BYPASS_CLK_SRC]用于选择输入钟源(Fin), 一般我们使用24MHz参考时钟,当然也可以选择外部引脚输入引脚(CLK1_N /CLK1_P)输入的外部时钟。CCM_ANALOG_PLL_ARMn[DIV_SELECT]位选择锁相环分频值(DIV_SELECT)。 取值范围为54到108。输出频率计算公式为 ARM_PLL = Fin * DIV_SELECT/2.0。如果选择24MHz参考时钟作为时钟输入,DIV_SELECT选择88则ARM PLL的输出频率为1056MHz。 CCM_ANALOG_PLL_ARMn[ENABLE]用于配置是否使能ARM PLL输出,如果要使用ARM PLL就需要将该位设置为1。

从上图中可以看出,PLL2与PLL3下方各有一组PFD。PFD是Phase Fractional Dividers的缩写,我们这里翻译为分数分频。 PFD的频率由PLL2或PLL3输出频率分频产生。我们以PLL2产生PFD0~PFD3讲解PFD的产生过程,如下所示。

PFD产生过程

寄存器CCM_ANALOG_PFD_528n用于控制PFD0~PFD3的输出频率。以PFD0为例,CCM_ANALOG_PFD_528n[PFD0_FRAC]用于设置分频值, 取值范围为12到35,PFD0的输出频率计算公式为:PFD0_out =528*18/ PFD0_FRAC如果PFD0_FRAC = 27,PFD0的输出频率为352MHz。 CCM_ANALOG_PFD_528n[PFD0_CLKGATE]门控寄存器,用于设置是否使能PFD0的输出。PLL输出时钟和PFD输出时钟经选择和分频后将作为内核或外设的根时钟。

2.2. 根时钟生成模块CLOCK ROOT GEN

根时钟生成模块主要完成两个工作,第一选择时钟,第二设置时钟分频。

时钟选择器(CLOCK SWITCHER)输出了多个频率不同的PLL时钟和PFD时钟。每个外设时钟通过MUX模块连接到多个PLL时钟输出, 每个MUX实际是一个寄存器,用于选择时钟源。时钟分频也是一个寄存器位。总之就是通过配置对应的寄存器为外设选择时钟源并设置时钟频率。 具体是那些寄存器?在图中标记的很清楚,根据图中标注可以很容易、灵活的配置时钟。

下面以“ARM_CLK_ROOT”时钟为例讲解,时钟配置步骤。“ARM_CLK_ROOT”时钟是CPU时钟,也就是我们常说的主频。 修改该时钟之前首先要将CPU时钟切换到另外一个可用的时钟,修改完成后再切换回来。 具体下所示。(图片太大,图片中的字看不清楚,请查阅《IMX6ULRM》18.5.1 Clock Generation P631页)

CPU时钟切换

上图中,标号①与标号②处是CCSR时钟选择寄存器的两个配置位,用于设置时钟源。这里假设要将CPU时钟修改为792MHz。 步骤如下:(具体代码在讲解代码时会介绍)

  1. 配置CCSR寄存器,将 “ARM_CLK_ROOT”时钟切换到osc_clk(24MHz)

CCSR寄存器如下所示:

CCSR寄存器

这里共用到了CCSR寄存器的两个控制位,

第一处CCSR[STEP_SEL] 对应图标号①处,设置CCSR[STEP_SEL] = 0,表示选择24MHz的osc_clk时钟,osc_clk时钟是固定的,默认我们选择这个时钟。CCSR[STEP_SEL] =1,表示选择secondary_clk时钟,这个时钟暂时用不到,不用关心。 第二处CCSR[PLL1_SW_CLK_SEL]对应图标号②处,设置CCSR[PLL1_SW_CLK_SEL] = 0,表示选择pll1_main_clk时钟。CCSR[PLL1_SW_CLK_SEL] =1,表示选择step_clk时钟。

我们设置CCSR[STEP_SEL] = 0、CCSR[PLL1_SW_CLK_SEL] = 1,这样CPU时钟源被切换到了24MHz的osc_clk时钟,下一步就可以修改PLL1的输出时钟。

  1. 修改PLL1输出时钟。PLL1输出时钟设置方法已经在55.3 时钟树简介,第一小节介绍,这里不再赘述。

  2. 设置CCSR[PLL1_SW_CLK_SEL] = 1,将CPU时钟切换到pll1_main_clk即PLL1输出时钟。

  3. 修改时钟分频。如下所示。

修改时钟分频。如下所示。

从上8可以看出,从PLL1到“ARM_CLK_ROOT”还要经过CACRR[ARM_PODF] 时钟分频寄存器。 进过上一步PLL1的输出时钟被设置为792MHz,所以这里设置CACRR[ARM_PODF] = 0,不分频。

3. 设置系统时钟实验

系统时钟设置实验代码由“interrupt_init”代码修改得到,本章也只讲解新增部分代码,完整请参考本章配套源码。

本章的示例代码目录为:bare_metal/clock_init

野火裸机下载工具download_tool路径为:bare_metal/download-tool/download-tool.tar.bz2

3.1. 实验说明

3.1.1. 硬件介绍

本实验只用RGB灯显示大致显示CPU运行速度,没有用到其他外部电路。

3.1.2. 原理图分析
  • RGB灯原理图请参考 第四章节汇编点亮LED灯 章节。

3.2. 实验代码讲解

3.2.1. 编程思路

拷贝“bare_metal/interrupt_init”程序,并重命名为“bare_metal/clock_init”。 分别在“bare_metal/clock_init/device”、“bare_metal/clock_init/include”文件夹下添加clock.c、clock.h。 添加了新的源文件,当然要将其添加到makefile 中。

打开“bare_metal/clock_init/device”目录下的“makefile”文件,将clock.o添加到最终目标的依赖中,代码如下所示。

all : button.o  led.o system_MCIMX6Y2.o clock.oarm-none-eabi-ld -r $^  -o device.o

 

3.2.2. 代码分析
3.2.2.1. 添加时钟初始化代码

时钟初始化代码主要实现更改CPU时钟、设置PLL2、PLL3的输出时钟以及对应的PFD时钟。如下所示。

/********************第一部分***********************/
/* 外部 XTAL (OSC) 时钟频率 */
uint32_t g_xtalFreq = 24000000;
/*外部 RTC XTAL 时钟频率 */
uint32_t g_rtcXtalFreq = 32768;void system_clock_init(void)
{/********************第二部分***********************//******************* PLL 输出时钟设置************************/if ((CCM->CCSR & (0x01 << 2)) == 0) //CPU 使用的是 ARM PLL{/*将CPU时钟切换到XTAL (OSC) 时钟*/CCM->CCSR &= ~(0x01 << 8); //控制CCSR: step_sel ,选择 osc_clk 作为时钟源CCM->CCSR |= (0x01 << 2); //设置GLITCHLESS MUX 选择 step_clk 作为时钟源}/********************第三部分***********************//*设置PLL1输出时钟为792MHz,它将作为CPU时钟*/CCM_ANALOG->PLL_ARM |= (0x42 << 0);/*将CPU 时钟重新切换到 ARM PLL*/CCM->CCSR &= ~(0x01 << 2);/*设置时钟分频系数为0,即不分频*/CCM->CACRR &= ~(0x07 << 0); //清零分频寄存器   30秒大约闪烁45次// CCM->CACRR |= (0x07 << 0); //清零分频寄存器 30秒大约闪烁20次/********************第四部分***********************//*设置PLL2(System PLL) 输出时钟*//* Configure SYS PLL to 528M */CCM_ANALOG->PLL_SYS_SS &= ~(0x8000);     //使能PLL2 PFD输出CCM_ANALOG->PLL_SYS_NUM &= ~(0x3FFFFFFF);//设置分频系数为0,即不分频。CCM_ANALOG->PLL_SYS |= (0x2000); //使能PLL2 输出CCM_ANALOG->PLL_SYS |= (1 << 0); //设置输出频率为528Mwhile ((CCM_ANALOG->PLL_SYS & (0x80000000)) == 0) //等待设置生效{}/*设置PLL3(System PLL) 输出时钟*//* Configure USB PLL to 480M */CCM_ANALOG->PLL_USB1 |= (0x2000);    //使能 PLL3时钟输出CCM_ANALOG->PLL_USB1 |= (0x1000);    //PLL3上电使能CCM_ANALOG->PLL_USB1 |= (0x40);      // 使能USBPHYnCCM_ANALOG->PLL_USB1 &= ~(0x01 << 0);//设置输出频率为480MHzwhile ((CCM_ANALOG->PLL_SYS & (0x80000000)) == 0)//等待设置生效{}/*关闭暂时不使用的 PLL4 、PLL5  、PLL6 、PLL7*/CCM_ANALOG->PLL_AUDIO = (0x1000);    //关闭PLL4CCM_ANALOG->PLL_VIDEO = (0x1000);    //关闭PLL5CCM_ANALOG->PLL_ENET =  (0x1000);    //关闭PLL6CCM_ANALOG->PLL_USB2 =  (0x00);           //关闭PLL7/********************第五部分***********************//******************PFD 输出时钟设置*******************//*禁用PLL2 的所有PFD输出*/CCM_ANALOG->PFD_528 |=(0x80U) ;      //关闭PLL2 PFD0CCM_ANALOG->PFD_528 |=(0x8000U) ;    //关闭PLL2 PFD1// CCM_ANALOG->PFD_528 |=(0x800000U) ;  //关闭PLL2 PFD2 ,DDR使用的是该时钟源,关闭后程序不能运行。暂时不关闭CCM_ANALOG->PFD_528 |=(0x80000000U); //关闭PLL2 PFD3/*设置PLL2 的PFD输出频率*/CCM_ANALOG->PFD_528 &= ~(0x3FU); //清零PLL2 PFD0 时钟分频CCM_ANALOG->PFD_528 &= ~(0x3F00U); //清零PLL2 PFD1 时钟分频CCM_ANALOG->PFD_528 &= ~(0x3F00U); //清零PLL2 PFD2 时钟分频CCM_ANALOG->PFD_528 &= ~(0x3F00U); //清零PLL2 PFD3 时钟分频CCM_ANALOG->PFD_528 |= (0x1B << 0); //设置PLL2 PFD0 输出频率为 352MCCM_ANALOG->PFD_528 |= (0x10 << 8); //设置PLL2 PFD0 输出频率为 594MCCM_ANALOG->PFD_528 |= (0x18 << 16); //设置PLL2 PFD0 输出频率为 396MCCM_ANALOG->PFD_528 |= (0x30 << 24); //设置PLL2 PFD0 输出频率为 198M/*启用PLL2 的所有PFD输出*/CCM_ANALOG->PFD_528 &= ~(0x80U) ;      //开启PLL2 PFD0CCM_ANALOG->PFD_528 &= ~(0x8000U) ;    //开启PLL2 PFD1CCM_ANALOG->PFD_528 &= ~(0x800000U) ;  //开启PLL2 PFD2CCM_ANALOG->PFD_528 &= ~(0x80000000U); //开启PLL2 PFD3/********************第六部分***********************//*禁用PLL3 的所有PFD输出*/CCM_ANALOG->PFD_480 |=(0x80U) ;      //关闭PLL3 PFD0CCM_ANALOG->PFD_480 |=(0x8000U) ;    //关闭PLL3 PFD1CCM_ANALOG->PFD_480 |=(0x800000U) ;  //关闭PLL3 PFD2CCM_ANALOG->PFD_480 |=(0x80000000U); //关闭PLL3 PFD3/*设置PLL3 的PFD输出频率*/CCM_ANALOG->PFD_480 &= ~(0x3FU);   //清零PLL3 PFD0 时钟分频CCM_ANALOG->PFD_480 &= ~(0x3F00U); //清零PLL3 PFD1 时钟分频CCM_ANALOG->PFD_480 &= ~(0x3F00U); //清零PLL3 PFD2 时钟分频CCM_ANALOG->PFD_480 &= ~(0x3F00U); //清零PLL3 PFD3 时钟分频CCM_ANALOG->PFD_480 |= (0xC << 0); //设置PLL3 PFD0 输出频率为 720MCCM_ANALOG->PFD_480 |= (0x10 << 8); //设置PLL3 PFD0 输出频率为 540MCCM_ANALOG->PFD_480 |= (0x11 << 16); //设置PLL3 PFD0 输出频率为 508.2MCCM_ANALOG->PFD_480 |= (0x13 << 24); //设置PLL3 PFD0 输出频率为 454.7M/*启用PLL3 的所有PFD输出*/CCM_ANALOG->PFD_480 &= ~(0x80U) ;      //开启PLL3 PFD0CCM_ANALOG->PFD_480 &= ~(0x8000U) ;    //开启PLL3 PFD1CCM_ANALOG->PFD_480 &= ~(0x800000U) ;  //开启PLL3 PFD2CCM_ANALOG->PFD_480 &= ~(0x80000000U); //开启PLL3 PFD3/********************第七部分***********************//******************常用外设根时钟设置****************/CCM->CSCDR1 &= ~(0x01 << 6); //设置UART选择 PLL3 / 6 = 80MHzCCM->CSCDR1 &= ~(0x3F);     //清零/*设置串口根时钟分频值为1,UART根时钟频率为:80M / (dev + 1) = 40MHz*/CCM->CSCDR1 |= (0x01 << 0); //
}

代码很长,但很简单,只修改了几个时钟配置寄存。结合代码各部分讲解如下。

  • 第一部分,定义变量g_xtalFreq 、g_rtcXtalFreq分别保存XTAL (OSC)时钟 、RTC XTAL时钟。这两个时钟频率后面章节会用到。

  • 第二部分和第三部分。设置CPU时钟频率。这部分内容对应时钟树简介章节第二部分。简单说明如下:第二部分代码首先判断当CPU时钟是否使用pll1_main_clk,如果是,则将其切换到osc_clk时钟。第三部分,修改PLL1输出频率,并将CPU时钟源切换到pll1_main_clk。

  • 第四部分,设置PLL2~PLL7的输出频率。其中PLL2的时钟被设置为528M并开启了PFD输出功能。PLL3的时钟被设置为480M并开启了PFD输出功能。PLL4~PLL7我们暂时用不到,直接关闭时钟输出。关闭不使用的时钟能够有效的减少功耗。

  • 第五部分,设置PLL2的PFD输出。PLL2共有4个PFD输出(PFD0~PFD3),PLL2的PFD设置通过CCM_ANALOG_PFD_528n寄存器实现,如下所示。

CCM_ANALOG_PFD_528n寄存器实现

这里不再介绍CCM_ANALOG_PFD_528寄存器的各个位的作用,结合代码或者参考《IMX6ULRM》手册Chapter 18 Clock Controller Module (CCM)寄存器介 绍部分很容易知道各个位的作用。初学者不必纠结每个控制位的作用,只需要知道这个寄存器有设置PLL2的FPD输出功能即可。具体使用到时查手册即可。

PLL2的PFD输出大致分为三部分,第一,禁用PLL2的PFD输出。第二,设置PFD的输出频率。第三 ,启用PFD输出。特别注意的是这里没有禁用PFD2,因为这 是DDR的时钟源。关闭后程序无法运行。

  • 第六部分,设置PLL3的PFD输出,设置方法与设置PLL2的PFD输出完全相同,只是这里设置的是CCM_ANALOG_PFD_480时钟。

  • 第七部分,设置串口的根时钟。这里只设置了串口,其他外设的时钟频率呢?在BOOT ROM中已经初始化了部分外设的时钟,为减少难度,那些没有使用到的外设或者对频率没有严格要求的外设我们暂时保持默认的时钟频率。这里设置UART根时钟的目的是以UART为例讲解如何设置外设时钟。UART时钟源产生路径如下所示。

UART时钟源产生路径

从图中可以看出,从PLL时钟到UART时钟共用用到了两个时钟选择寄存器(标号①和③),两个时钟分频寄存器(标号②和标号④)。 我们最终目的是将PLL3时钟作为UART根时钟(UART_CLK_ROOT)的根时钟。按照标号顺序讲解如下:

(1) 标号①选择PLL3时钟还是CCM_PLL3_BYP。我们选择PLL3输出时钟,寄存器CCSR[PLL3_SW_CLK_SEL] = 0, 则表示选择PLL3时钟。默认情况下是这样设置的。所以我们代码中并没有设置该寄存器。

(2) 标号②设置时钟分频,根据之前的设置,PLL3的输出频率为480MHz ,这里的时钟分频是固定的6分频, 经过分频后的时钟为 480MHz / 6 = 80MHz。

(3) 标号③ 再次选择时钟源。一个是PLL3分频得到的80MH时钟,另外一个是OSC时钟即24MHz的系统参考时钟。 设置 CSCDR1[UART_CLK_SEL] = 0,选择第一个(80MHz)时钟。

(4) 标号④再次进行时钟分频。这是一个6位的时钟分频寄存器。分频值为CSCDR1[UART_CLK_PODF]寄存器值加一。 程序中将其设置为1,则分频系数为2,UART_CLK_ROOT时钟频率实际为80MHz / 2 = 40MHz 。

3.2.2.2. 添加简易测试代码

由于程序中只有按键和RGB灯,所以我们暂时使用RGB灯闪烁频率大致判断程序运行速度。测试代码如下所示。

/*简单延时函数*/
void delay(uint32_t count)
{volatile uint32_t i = 0;for (i = 0; i < count; ++i){__asm("NOP"); /* 调用nop空指令 */}
}int main()
{system_clock_init();  //初始化系统时钟rgb_led_init();       //初始化 RGB 灯,初始化后 默认所有灯都不亮。while (1){red_led_off;green_led_on;delay(0xFFFFF);green_led_off;red_led_on;delay(0xFFFFF);}return 0;
}

测试代码很简单,编写一个简易延时函数。在while(1)中控制RGB闪烁。我们可以修改CPU时钟分频,如下所示。

void system_clock_init(void)
{/******************* PLL 输出时钟设置************************/if ((CCM->CCSR & (0x01 << 2)) == 0) //CPU 使用的是 ARM PLL{/*将CPU时钟切换到XTAL (OSC) 时钟*/CCM->CCSR &= ~(0x01 << 8); //控制CCSR: step_sel ,选择 osc_clk 作为时钟源CCM->CCSR |= (0x01 << 2);  //设置GLITCHLESS MUX 选择 step_clk 作为时钟源}/*设置PLL1输出时钟为792MHz,它将作为CPU时钟*/CCM_ANALOG->PLL_ARM |= (0x42 << 0);/*将CPU 时钟重新切换到 ARM PLL*/CCM->CCSR &= ~(0x01 << 2);/*设置时钟分频系数为0,即不分频*/CCM->CACRR &= ~(0x07 << 0); //清零分频寄存器   不分频//CCM->CACRR |= (0x07 << 0);     // 8分频...
}

通过修改时钟分频寄存器修改CPU时钟频率。我们可以对比不分频和8分频的实验效果。

3.3. 实验准备

3.3.1. 编译程序

程序编写完成后,在“clock_init” 文件夹下执行make命令,makefile工具便会自动完成程序的编译、链接、格式转换等工作。 正常情况下我们可以在当前目录看到生成的一些中间文件以及我们期待的.bin文件。

3.3.2. 烧录程序

在编译下载官方SDK程序到开发板章节我们详细讲解了如何将二进制文件烧写到SD卡(烧写工具自动实现为二进制文件添加头)。这里再次说明下载步骤。

  • 将一张空SD卡(烧写一定会破坏SD卡中原有数据!!!烧写前请保存好SD卡中的数据),接入电脑后在虚拟机的右下角状态栏找到对应的SD卡。将其链接到虚拟机。

  • 进入烧写工具目录,执行 ./mkimage.sh <烧写文件路径> 命令,例如要 烧写的led.bin位于home目录下,则烧写命令为 ./mkimage.sh /home/led.bin 。

  • 执行上一步后会列出linux下可烧写的磁盘,选择你插入的SD卡即可。这一步非常危险!!!一定要确定选择的是你插入的SD卡!!,如果选错很可能破坏你电脑 磁盘内容,造成数据损坏!!!。确定磁盘后SD卡以”sd”开头,选择”sd”后面的字符即可。例如要烧写的sd卡是”sdb”则输入”b”即可。

3.4. 程序运行结果

烧写完成,首先将开发板启动方式设置为SD卡启动,将SD卡插入开发板卡槽。 接通电源正常可以看到RGB灯闪烁,修改CPU时钟再次下载可以看到RGB灯闪烁频率加快。

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

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

相关文章

摄影曝光:曝光模式认知

写在前面 学习整理《摄影曝光&#xff1a;拍出好照片的49个关键技法》读书笔记博文内容涉及曝光模式简单认知适合小白认知理解不足小伙伴帮忙指正 &#x1f603;,生活加油 99%的焦虑都来自于虚度时间和没有好好做事&#xff0c;所以唯一的解决办法就是行动起来&#xff0c;认真…

【Docker】Docker Consul

docker consul Docker Consul 是一个用于服务发现和配置的开源工具&#xff0c;它是 HashiCorp 公司推出的一个项目。Consul 提供了一个中心化的服务注册和发现系统&#xff0c;可以帮助开发人员轻松地在 Docker 容器和集群之间进行服务发现和配置管理。 Consul 使用基于 HTT…

SQL注入漏洞的基础知识

目录 SQL注入漏洞的定义和原理 SQL注入的类型和攻击方法 SQL注入的防御措施 示例代码 深入研究 SQL注入漏洞的常见攻击场景有哪些&#xff1f; 如何有效防范SQL注入攻击&#xff1f; SQL注入与跨站脚本攻击&#xff08;XSS&#xff09;之间有什么区别&#xff1f; 主要…

CORS错误

说明&#xff1a;记录一次CORS&#xff08;跨域&#xff09;错误&#xff0c;及解决方法。 场景 在vscode里面运行前端项目&#xff0c;idea中运行后端项目&#xff0c;登录时&#xff0c;访问接口&#xff0c;报CORS错误&#xff0c;如下&#xff1a; 解决 在后端项目的网关…

electron仿微信,高度还原,入门项目

效果展示 Electron仿写微信-效果展示 目前完成了一些基础的功能&#xff0c;还在持续开发中&#xff0c;后期会整理开源。 有些样式没有追求百分百还原&#xff0c;只是通过该项目&#xff0c;让大家了解一下Electron创建桌面应用&#xff0c;各种窗口的创建及销毁、事件传递机…

GATK ReadsPathDataSource类介绍

GATK&#xff08;Genome Analysis Toolkit&#xff09;是一个广泛使用的基因组分析工具包&#xff0c;它的核心库之一是htsjdk&#xff0c;用于处理高通量测序数据。在GATK中&#xff0c;ReadsPathDataSource类是负责管理和提供读取高通量测序数据文件&#xff08;如BAM、SAM、…

【UE】尝试一种老派的平面假反射做法,与进一步改进效果的思路

在实践中&#xff0c;常常需要为类似荧幕&#xff0c;LED广告牌等平面制作反射。 但会遇到各种问题&#xff0c;例如在使用屏幕空间反射时&#xff0c;平面必须在画面内 平面反射捕获与光线追踪又代价高昂 因此&#xff0c;在一些情况下依然会使用一种历史悠久的反射手法 这种…

Windows SDK(九)登录框和计算器练习

这节课我们分别开始讲解登录框和计算机的实现 登录框实现 我们以上节课所学&#xff0c;自行创建一个对话框&#xff0c;ID为IDD_DIALOG1并将他编辑为一个登录框的样式。其中我们将账户的编辑框ID设置为IDC_ENIT_USERNAME&#xff0c;密码的编辑框ID设置为IDC_ENIT_PASSWORD。…

Pytorch构建网络模型结构都有哪些方式

目录 前言 1.使用nn.Module基类 2.使用nn.Sequential容器 3. 使用nn.ModuleList 4. 使用nn.ModuleDict 5. 混合使用nn.Module和原生Python代码 6.表格总结 前言 nn.Module&#xff1a;最通用、最灵活的方式&#xff0c;适用于几乎所有场景。nn.Sequential&#xff1a;适…

【HTML】为网页添加表单(控件)

1、表单 表单控件&#xff1a;包含了具体的表单功能项&#xff0c;如单行文本输入框、密码输入框、复选框、提交按钮、重置按钮等。 提示信息&#xff1a;一个表单中通常需要包含一些说明性的文字&#xff0c;提示用户进行填写和操作。 表单域&#xff1a;相当于一个容器&…

改造小蚁摄像头支持免费无限容量云储存(Samba挂载篇)

为什么要改造&#xff1f; 插卡摄像头最大的一个问题就是频繁的读写会导致内存卡寿命急速下降&#xff0c;哪怕是市面上支持NAS转存的摄像头也是先录制到SD卡里&#xff0c;然后把SD卡上的视频再转存到NAS。同样对内存卡和NAS硬盘寿命都是损耗巨大。而这类监控视频绝大多数情况…

深入理解Elasticsearch:让搜索性能飞起来!

Elasticsearch 概述 Elasticsearch是一个基于lucene、分布式、通过Restful方式进行交互的近实时搜索平台框架。 ELK 技术栈是Elasticsearch、Logstash、Kibana三大开元框架首字母大写简称。 而Elasticsearch 是一个开源的高扩展的分布式全文搜索引擎&#xff0c; 是整个 ELK技术…

ue5远程渲染和本地渲染的区别,及云渲染的联系

UE5这款引擎以其令人惊叹的渲染能力&#xff0c;为游戏开发者们打开了一扇通往视觉盛宴的大门。但是在UE5的世界里&#xff0c;渲染技术其实还有着本地渲染和远程渲染之分&#xff0c;而且它们与时下大热的云渲染技术也有着千丝万缕的联系。本文主要说明UE5中的远程渲染和本地渲…

Flask+LayUI开发手记(四):弹出层实现增删改查功能

在上一节用dataTable实现数据列表时&#xff0c;已经加了表头工具栏和表内工具栏&#xff0c;栏内的按钮功能都是用来完成数据的增删改查了&#xff0c;这又分成两类功能&#xff0c;一类是删除或设置&#xff0c;这类功能简单&#xff0c;只需要选定记录&#xff0c;然后提交到…

golang RSA 解密前端jsencrypt发送的数据时异常 crypto/rsa: decryption error 解决方法

golang中 RSA解密前端&#xff08;jsencrypt&#xff09;发来的密文后出现 "crypto/rsa: decryption error" &#xff0c; 这个问题首先需要确认你的私匙和公匙是否匹配&#xff0c; 如果匹配 那检查入参数据类型&#xff0c; 前端发送来的rsa加密后的数据一般都是…

【算法进阶2-动态规划】斐波那契数列(递归调用、动态规划)、钢条切割问题(自定而下实现、自底向上、切割方案)

1 斐波那契数 2 钢条切割问题 2.1 最优解情况 2.2 钢条切割问题之自定而下实现 2.3 钢条切割问题之自底向上实现 2.4 钢条切割问题-重构解-切割方案 1 斐波那契数 # 1 子问题的重复计算 def fibonacci(n: int) -> int:"""使用递归方式计算第 n 个斐波那契数…

初识C语言指针(4)

目录 1. 字符指针变量 2. 数组指针变量 3. ⼆维数组传参的本质 4. 函数指针变量 5. typedef 关键字 6. 函数指针数组 结语 1. 字符指针变量 字符指针变量就是存储字符或字符串首字符地址的变量&#xff0c;字符指针变量有2种使用方式。 最常用的使用方式&#xff1a…

Datawhale X 李宏毅苹果书 AI夏令营(深度学习入门)task3

实践方法论 在应用机器学习算法时&#xff0c;实践方法论能够帮助我们更好地训练模型。如果在 Kaggle 上的结果不太好&#xff0c;虽然 Kaggle 上呈现的是测试数据的结果&#xff0c;但要先检查训练数据的损失。看看模型在训练数据上面&#xff0c;有没有学起来&#xff0c;再…

智能手机摄影综评:品牌联名与自建影像品牌的战略分析

随着智能手机摄影技术的飞速发展&#xff0c;各大厂商不仅与知名摄影品牌展开合作&#xff0c;还通过自建影像品牌来提升产品的摄影能力和品牌形象。本文将重点分析小米、华为、荣耀、OPPO、Vivo和苹果在摄影品牌联名与自建影像品牌方面的战略&#xff0c;探讨这些策略如何影响…

【案例55】WebSphere非root用户启动方案

问题背景 很多项目为了安全因素考虑&#xff0c;想让在Linux服务器中启动的程序都用非root用户启动。 解决方案 创建用户和组 现在我们用 root 用户登录&#xff0c;并创建用户和组。 ##创建用户 [rootnc-test ~]# useradd wasadmin##修改密码 [rootnc-test~]# passwd was…