ADC在STM32F1系列的使用详解

目录

1.  ADC简介

2.  逐次逼近型ADC(ADC0809)

3.  ADC框图(STM32)

4.  ADC基本结构

5.  输入通道

6.  转换模式

6.1  单次转换

6.1.1  非扫描模式

6.1.2  扫描模式

6.2  连续转换

6.2.1  非扫描模式

6.2.2  扫描模式

7.  触发控制

8.  转换时间

9.  代码编写

9.1  引脚使能

9.2  预分频器配置

9.3  ADC配置介绍

9.4  ADC相关配置

9.4.1  开启时钟

9.4.2  规组通道配置

9.4.3  ADC初始化

9.4.4  ADC使能

9.4.5  ADC校准

9.5  获取AD转换的值

10.  完整代码


1.  ADC简介

        ADC全称是(Analog-to-Digital Converter)模拟-数字转换器,一般我们把模拟信号(Analog signal) 用A来进行简写,数字信号(digital signal) 用D来表示。主要用于将连续传输的模拟信号转换为数字信号,便于数字系统(如中央处理器CPU、微控制器MCU等)对传输信息进行快速处理和分析。

         模拟信号是指用连续变化的物理量所表达的信息,如温度、湿度、压力、电压、电流等。ADC模块所采集的模拟信号是连续变化的电压或电流信号,其数值在一定范围内连续变化,如下所示:

        在单片机的使用中,我们可以通过ADC的转换将引脚上连续变化的模拟电压转换为内存中存储的数字变量,建立模拟电路到数字电路的桥梁。

         ADC采样的实现方式主要有两种:外接采样芯片控制核心内部的采样模块。许多MCU和DSP内部集成了ADC模块。例如,STM32F103C8T6具备一个12位的ADC,1us的转换时间,其电压输出范围在0~3.3V,转换结果范围0~4095(即2^12-1),其ADC资源有ADC1、ADC2,10个外部输入通道。

2.  逐次逼近型ADC(ADC0809)

        我们先来了解一下ADC0809的工作原理。

        首先是通道选择开关,通过通道选择开关,选择(IN0~IN7)其中的一路,输入到这个点进行转换:

        然后是地址锁存和译码,这里你想要选择哪一路,需要将通道号放到(ADDA、ADDB、ADDC)这三个脚上,然后通过ALE给出锁存信号,对应的通道选择开关就会自动拨好了:

        比较器是一个电压比较器,他可以判断两个输入信号电压的大小关系,其中通道选择开关输入的是待测的电压,另一端是DAC(数模转换器)的电压:

        这里待测电压是未知的,但是DAC所出的电压是我们通过数模转换来的,其编码是已知的,我们通过比较器进行比较,当待测电压大于DAC所示电压,我们就调大DAC的值,如果待测电压小于DAC所示电压,我们就调小DAC的值,直到待测电压等于DAC所示电压,这样DAC的输入数据就是待测电压的数据,我们就可以知道待测电压的编码(类似于二分法)。

        得出待测电压的编码通过三态锁存缓冲器进行输出,8位就有8根线,12位就有12根:

3.  ADC框图(STM32)

        STM32F1系列其芯片内部有多达18个通道,可测量16个外部和2个内部信号源:

        通道检测数据进入搭配模拟多路开关,模拟多路开关将数据输出的“模拟至数字转换器”(这里的作用类似于上面介绍的逐次逼近ADC),将转换数据传至数据寄存器,取出结果:

        对于这里,我们可以将注入通道或者规则通道比作饭店上菜的菜单,注入通道的通道相当于菜单上的菜,菜单上有菜(有数据),厨房做菜(注入通道寄存器接受数据),菜单上有多少菜做多少,从上往下一道一道做(数据逐次注入);

        规则通道有16个通道(也就是菜单上能写16个),但是规则通道数据寄存器只有一个,所以每当有新菜,就会将之前的菜划掉,只能保存最后一个数据,这里就需要搭配DMA来使用(作用就是当接收到一个数据之后,将这个数据挪到其他地方去,防止被覆盖):

        这里相当于ADC0809的START信号(开始转换信号),对于STM32的ADC触发ADC开始转换的信号又两种:软件触发硬件触发

        软件触发就是在程序中手动调用一条代码,就可以启动转换。

        硬件触发如下,由于ADC需要经过一个固定时间转换一次,因此我们需要通过定时器进行控制时间,但是若是实时采集数据,会频繁进入定时器,频繁进入中断,而中断又有优先级,若是多处地方需要频繁进入中断,这将会导致程序卡死,不过对于这种需要频繁进入中断,并且中断之进行简单的操作,一般会有硬件的支持:

        这里需要注意一点,ADC最大范围14MHz,如果分频系数选择2或者4分频,最大分别是36MHz和18MHz超范围了,因此只能选择6或者8分频:

4.  ADC基本结构

5.  输入通道

6.  转换模式

6.1  单次转换

6.1.1  非扫描模式

        只有第一个序列1的位置有效,在其中选择转换的通道,例如选择通道2,这样ADC就会对通道2进行模数转换,过一会转换完成,将其放到数据寄存器里,将EOC标志位置1,表示转换完成:

6.1.2  扫描模式

        其与非扫描模式不同的是,非扫描模式只使用了序列1,而扫描模式将下面序列使用了起来,在结构体初始化时,需要初始化通道数目:

6.2  连续转换

6.2.1  非扫描模式

        这个和单次转换非扫描模式有些类似,但是不同的是,这个在转换结束后不需要停止,可以直接进行下一次转换,不需要多次触发:

6.2.2  扫描模式

        这个就是在单次转换扫描模式基础上,使其连续触发:

7.  触发控制

ADC1和ADC2用于规则通道的外部触发

触发源类型EXTSEL[2:0]
TIM1_CC1事件来自片上定时器的内部信号000
TIM1_CC2事件001
TIM1_CC3事件010
TIM2_CC2事件011
TIM3_TRGO事件100
TIM4_CC4事件101
EXTI线11/TIM8_TRGO事件外部引脚/来自片上定时器的内部信号110
SWSTART软件控制位111

8.  转换时间

        ADC转换步骤:采样、保持、量化、编码

STM32的ADC的总转换时间为:

T_{CONV}=采样时间+12.5个ADC周期

例如:

当ADCCLK=14MHz,采样时间为1.5个ADC周期

T_{CONV}=1.5+12.5=14个ADC周期=1us

9.  代码编写

9.1  引脚使能

        首先初始化GPIO口:

void AD_Init(void)
{RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);	//开启GPIOA的时钟/*GPIO初始化*/GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);					//将PA0引脚初始化为模拟输入}

9.2  预分频器配置

        然后是RCC预分频器的配置,找到stm32f10x_rcc.h找到:

void RCC_ADCCLKConfig(uint32_t RCC_PCLK2);

        其可以对APB2的72MHz时钟选择2、4、6、8分频,输入到ADCCLK:

/** @defgroup ADC_clock_source * @{*/#define RCC_PCLK2_Div2                   ((uint32_t)0x00000000)
#define RCC_PCLK2_Div4                   ((uint32_t)0x00004000)
#define RCC_PCLK2_Div6                   ((uint32_t)0x00008000)
#define RCC_PCLK2_Div8                   ((uint32_t)0x0000C000)
#define IS_RCC_ADCCLK(ADCCLK) (((ADCCLK) == RCC_PCLK2_Div2) || ((ADCCLK) == RCC_PCLK2_Div4) || \((ADCCLK) == RCC_PCLK2_Div6) || ((ADCCLK) == RCC_PCLK2_Div8))

例如设置成6分频:

	/*设置ADC时钟*/RCC_ADCCLKConfig(RCC_PCLK2_Div6);						//选择时钟6分频,ADCCLK = 72MHz / 6 = 12MHz

9.3  ADC配置介绍

        ADC相关配置存放在stm32f10x_adc相关文件内:

/** @defgroup ADC_Exported_Functions* @{*/void ADC_DeInit(ADC_TypeDef* ADCx);
void ADC_Init(ADC_TypeDef* ADCx, ADC_InitTypeDef* ADC_InitStruct);
void ADC_StructInit(ADC_InitTypeDef* ADC_InitStruct);
void ADC_Cmd(ADC_TypeDef* ADCx, FunctionalState NewState);
void ADC_DMACmd(ADC_TypeDef* ADCx, FunctionalState NewState);
void ADC_ITConfig(ADC_TypeDef* ADCx, uint16_t ADC_IT, FunctionalState NewState);
void ADC_ResetCalibration(ADC_TypeDef* ADCx);
FlagStatus ADC_GetResetCalibrationStatus(ADC_TypeDef* ADCx);
void ADC_StartCalibration(ADC_TypeDef* ADCx);
FlagStatus ADC_GetCalibrationStatus(ADC_TypeDef* ADCx);
void ADC_SoftwareStartConvCmd(ADC_TypeDef* ADCx, FunctionalState NewState);
FlagStatus ADC_GetSoftwareStartConvStatus(ADC_TypeDef* ADCx);
void ADC_DiscModeChannelCountConfig(ADC_TypeDef* ADCx, uint8_t Number);
void ADC_DiscModeCmd(ADC_TypeDef* ADCx, FunctionalState NewState);
void ADC_RegularChannelConfig(ADC_TypeDef* ADCx, uint8_t ADC_Channel, uint8_t Rank, uint8_t ADC_SampleTime);
void ADC_ExternalTrigConvCmd(ADC_TypeDef* ADCx, FunctionalState NewState);
uint16_t ADC_GetConversionValue(ADC_TypeDef* ADCx);
uint32_t ADC_GetDualModeConversionValue(void);
void ADC_AutoInjectedConvCmd(ADC_TypeDef* ADCx, FunctionalState NewState);
void ADC_InjectedDiscModeCmd(ADC_TypeDef* ADCx, FunctionalState NewState);
void ADC_ExternalTrigInjectedConvConfig(ADC_TypeDef* ADCx, uint32_t ADC_ExternalTrigInjecConv);
void ADC_ExternalTrigInjectedConvCmd(ADC_TypeDef* ADCx, FunctionalState NewState);
void ADC_SoftwareStartInjectedConvCmd(ADC_TypeDef* ADCx, FunctionalState NewState);
FlagStatus ADC_GetSoftwareStartInjectedConvCmdStatus(ADC_TypeDef* ADCx);
void ADC_InjectedChannelConfig(ADC_TypeDef* ADCx, uint8_t ADC_Channel, uint8_t Rank, uint8_t ADC_SampleTime);
void ADC_InjectedSequencerLengthConfig(ADC_TypeDef* ADCx, uint8_t Length);
void ADC_SetInjectedOffset(ADC_TypeDef* ADCx, uint8_t ADC_InjectedChannel, uint16_t Offset);
uint16_t ADC_GetInjectedConversionValue(ADC_TypeDef* ADCx, uint8_t ADC_InjectedChannel);
void ADC_AnalogWatchdogCmd(ADC_TypeDef* ADCx, uint32_t ADC_AnalogWatchdog);
void ADC_AnalogWatchdogThresholdsConfig(ADC_TypeDef* ADCx, uint16_t HighThreshold, uint16_t LowThreshold);
void ADC_AnalogWatchdogSingleChannelConfig(ADC_TypeDef* ADCx, uint8_t ADC_Channel);
void ADC_TempSensorVrefintCmd(FunctionalState NewState);
FlagStatus ADC_GetFlagStatus(ADC_TypeDef* ADCx, uint8_t ADC_FLAG);
void ADC_ClearFlag(ADC_TypeDef* ADCx, uint8_t ADC_FLAG);
ITStatus ADC_GetITStatus(ADC_TypeDef* ADCx, uint16_t ADC_IT);
void ADC_ClearITPendingBit(ADC_TypeDef* ADCx, uint16_t ADC_IT);

        方便后续需要使用我们简单介绍一下:

        DeInit恢复缺省配置(恢复默认初始状态),Init初始化,StructInit结构体初始化:

void ADC_DeInit(ADC_TypeDef* ADCx);
void ADC_Init(ADC_TypeDef* ADCx, ADC_InitTypeDef* ADC_InitStruct);
void ADC_StructInit(ADC_InitTypeDef* ADC_InitStruct);

        用于给ADC上电:

void ADC_Cmd(ADC_TypeDef* ADCx, FunctionalState NewState);

        用于开启DMA输出信号,如果使用DMA转运数据,需要调用这个:

void ADC_DMACmd(ADC_TypeDef* ADCx, FunctionalState NewState);

        用于中断输出控制:

void ADC_ITConfig(ADC_TypeDef* ADCx, uint16_t ADC_IT, FunctionalState NewState);

        复位校准,获取复位校准状态:

void ADC_ResetCalibration(ADC_TypeDef* ADCx);
FlagStatus ADC_GetResetCalibrationStatus(ADC_TypeDef* ADCx);

        开始校准,获取开始校准状态:

void ADC_StartCalibration(ADC_TypeDef* ADCx);
FlagStatus ADC_GetCalibrationStatus(ADC_TypeDef* ADCx);

        ADC软件开始转换控制,这个就是用于软件触发的函数:

void ADC_SoftwareStartConvCmd(ADC_TypeDef* ADCx, FunctionalState NewState);

         ADC获取软件开始转换状态:

FlagStatus ADC_GetSoftwareStartConvStatus(ADC_TypeDef* ADCx);

        用于间断模式控制,第一个函数表示每隔几个通道间断一次,第二个函数表示启用间断模式:

void ADC_DiscModeChannelCountConfig(ADC_TypeDef* ADCx, uint8_t Number);
void ADC_DiscModeCmd(ADC_TypeDef* ADCx, FunctionalState NewState);

        用于ADC规则组通道配置,配置选定的ADC常规通道、其在序列器中的对应等级及其采样时间::

void ADC_RegularChannelConfig(ADC_TypeDef* ADCx, uint8_t ADC_Channel, uint8_t Rank, uint8_t ADC_SampleTime);

ADCx: 其中x可以是1、2或3,以选择ADC外设。

ADC_Channel: 要配置的ADC通道。

此参数可以是以下值之一:@arg ADC_Channel_0: ADC Channel0 selected@arg ADC_Channel_1: ADC Channel1 selected@arg ADC_Channel_2: ADC Channel2 selected@arg ADC_Channel_3: ADC Channel3 selected@arg ADC_Channel_4: ADC Channel4 selected@arg ADC_Channel_5: ADC Channel5 selected@arg ADC_Channel_6: ADC Channel6 selected@arg ADC_Channel_7: ADC Channel7 selected@arg ADC_Channel_8: ADC Channel8 selected@arg ADC_Channel_9: ADC Channel9 selected@arg ADC_Channel_10: ADC Channel10 selected@arg ADC_Channel_11: ADC Channel11 selected@arg ADC_Channel_12: ADC Channel12 selected@arg ADC_Channel_13: ADC Channel13 selected@arg ADC_Channel_14: ADC Channel14 selected@arg ADC_Channel_15: ADC Channel15 selected@arg ADC_Channel_16: ADC Channel16 selected@arg ADC_Channel_17: ADC Channel17 selected

Rank: 在常规组序列器中的等级。该参数必须在1到16之间。

ADC_SampleTime: 要为所选通道设置的采样时间值。

此参数可以是以下值之一:@arg ADC_SampleTime_1Cycles5: 采样时间等于1.5个周期@arg ADC_SampleTime_7Cycles5: 采样时间等于7.5个周期@arg ADC_SampleTime_13Cycles5: 采样时间等于13.5个周期@arg ADC_SampleTime_28Cycles5: 采样时间等于28.5个周期@arg ADC_SampleTime_41Cycles5: 采样时间等于41.5个周期@arg ADC_SampleTime_55Cycles5: 采样时间等于55.5个周期@arg ADC_SampleTime_71Cycles5: 采样时间等于71.5个周期@arg ADC_SampleTime_239Cycles5: 采样时间等于239.5个周期

        获取AD转换的数据寄存器,读取转换结果:

uint16_t ADC_GetConversionValue(ADC_TypeDef* ADCx);

        双ADC读取转换结果的函数:

uint32_t ADC_GetDualModeConversionValue(void);

9.4  ADC相关配置

9.4.1  开启时钟

	/*开启时钟*/RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);	//开启ADC1的时钟

9.4.2  规组通道配置

	/*规则组通道配置*/ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5);		//规则组序列1的位置,配置为通道0

9.4.3  ADC初始化

	/*ADC初始化*/ADC_InitTypeDef ADC_InitStructure;						//定义结构体变量ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;		//模式,选择独立模式,即单独使用ADC1ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;	//数据对齐,选择右对齐ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;	//外部触发,使用软件触发,不需要外部触发ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;		//连续转换,失能,每转换一次规则组序列后停止ADC_InitStructure.ADC_ScanConvMode = DISABLE;			//扫描模式,失能,只转换规则组的序列1这一个位置ADC_InitStructure.ADC_NbrOfChannel = 1;					//通道数,为1,仅在扫描模式下,才需要指定大于1的数,在非扫描模式下,只能是1ADC_Init(ADC1, &ADC_InitStructure);						//将结构体变量交给ADC_Init,配置ADC1

         ADC_InitTypeDef相关参数介绍:

typedef struct
{uint32_t ADC_Mode;                      /*!< 配置ADC以独立模式或双模工作。此参数可以是 @ref ADC_mode 的值。 */FunctionalState ADC_ScanConvMode;       /*!< 指定转换是否在扫描(多通道)或单通道模式下进行。此参数可以设置为 ENABLE 或 DISABLE。 */FunctionalState ADC_ContinuousConvMode; /*!< 指定转换是连续模式还是单次模式进行。此参数可以设置为 ENABLE 或 DISABLE。 */uint32_t ADC_ExternalTrigConv;          /*!< 定义用于启动常规通道的模拟到数字转换的外部触发。此参数可以是 @ref ADC_external_trigger_sources_for_regular_channels_conversion 的值。 */uint32_t ADC_DataAlign;                 /*!< 指定ADC数据对齐方式是左对齐还是右对齐。此参数可以是 @ref ADC_data_align 的值。 */uint8_t ADC_NbrOfChannel;               /*!< 指定将通过序列器转换的ADC通道数量用于常规通道组。此参数的范围必须在1到16之间。 */
} ADC_InitTypeDef;

9.4.4  ADC使能

	/*ADC使能*/ADC_Cmd(ADC1, ENABLE);									//使能ADC1,ADC开始运行

9.4.5  ADC校准

	/*ADC校准*/ADC_ResetCalibration(ADC1);								//固定流程,内部有电路会自动执行校准while (ADC_GetResetCalibrationStatus(ADC1) == SET);ADC_StartCalibration(ADC1);while (ADC_GetCalibrationStatus(ADC1) == SET);

9.5  获取AD转换的值

        我们配置的是单次转换非扫描模式,因此根据下图,编写获取AD转换的值的代码:

uint16_t AD_GetValue(void)
{ADC_SoftwareStartConvCmd(ADC1, ENABLE);					//软件触发AD转换一次while (ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET);	//等待EOC标志位,即等待AD转换结束return ADC_GetConversionValue(ADC1);					//读数据寄存器,得到AD转换的结果
}

10.  完整代码

AD.c代码:

#include "stm32f10x.h"                  // Device header/*** 函    数:AD初始化* 参    数:无* 返 回 值:无*/
void AD_Init(void)
{/*开启时钟*/RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);	//开启ADC1的时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);	//开启GPIOA的时钟/*设置ADC时钟*/RCC_ADCCLKConfig(RCC_PCLK2_Div6);						//选择时钟6分频,ADCCLK = 72MHz / 6 = 12MHz/*GPIO初始化*/GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);					//将PA0引脚初始化为模拟输入/*规则组通道配置*/ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5);		//规则组序列1的位置,配置为通道0/*ADC初始化*/ADC_InitTypeDef ADC_InitStructure;						//定义结构体变量ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;		//模式,选择独立模式,即单独使用ADC1ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;	//数据对齐,选择右对齐ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;	//外部触发,使用软件触发,不需要外部触发ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;		//连续转换,失能,每转换一次规则组序列后停止ADC_InitStructure.ADC_ScanConvMode = DISABLE;			//扫描模式,失能,只转换规则组的序列1这一个位置ADC_InitStructure.ADC_NbrOfChannel = 1;					//通道数,为1,仅在扫描模式下,才需要指定大于1的数,在非扫描模式下,只能是1ADC_Init(ADC1, &ADC_InitStructure);						//将结构体变量交给ADC_Init,配置ADC1/*ADC使能*/ADC_Cmd(ADC1, ENABLE);									//使能ADC1,ADC开始运行/*ADC校准*/ADC_ResetCalibration(ADC1);								//固定流程,内部有电路会自动执行校准while (ADC_GetResetCalibrationStatus(ADC1) == SET);ADC_StartCalibration(ADC1);while (ADC_GetCalibrationStatus(ADC1) == SET);
}/*** 函    数:获取AD转换的值* 参    数:无* 返 回 值:AD转换的值,范围:0~4095*/
uint16_t AD_GetValue(void)
{ADC_SoftwareStartConvCmd(ADC1, ENABLE);					//软件触发AD转换一次while (ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET);	//等待EOC标志位,即等待AD转换结束return ADC_GetConversionValue(ADC1);					//读数据寄存器,得到AD转换的结果
}

AD.h代码:

#ifndef __AD_H
#define __AD_Hvoid AD_Init(void);
uint16_t AD_GetValue(void);#endif

主函数代码:

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "AD.h"uint16_t ADValue;			//定义AD值变量
float Voltage;				//定义电压变量int main(void)
{/*模块初始化*/OLED_Init();			//OLED初始化AD_Init();				//AD初始化/*显示静态字符串*/OLED_ShowString(1, 1, "ADValue:");OLED_ShowString(2, 1, "Voltage:0.00V");while (1){ADValue = AD_GetValue();					//获取AD转换的值Voltage = (float)ADValue / 4095 * 3.3;		//将AD值线性变换到0~3.3的范围,表示电压OLED_ShowNum(1, 9, ADValue, 4);				//显示AD值OLED_ShowNum(2, 9, Voltage, 1);				//显示电压值的整数部分OLED_ShowNum(2, 11, (uint16_t)(Voltage * 100) % 100, 2);	//显示电压值的小数部分Delay_ms(100);			//延时100ms,手动增加一些转换的间隔时间}
}

STM32学习笔记_时光の尘的博客-CSDN博客

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

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

相关文章

计算机网络—静态路由

1.0 网络拓扑结构 星型拓扑结构是一个中心&#xff0c;多个分节点。它结构简单&#xff0c;连接方便&#xff0c;管理和维护都相对容易&#xff0c;而且扩展性强。网络延迟时间较小&#xff0c;传输误差低。中心无故障&#xff0c;一般网络没问题。中心故障&#xff0c;网络就出…

Android 内存优化——常见内存泄露及优化方案

看到了一篇关于内存泄漏的文章后&#xff0c;就想着分享给大家&#xff0c;最后一起学习&#xff0c;一起进步&#xff1a; 如果一个无用对象&#xff08;不需要再使用的对象&#xff09;仍然被其他对象持有引用&#xff0c;造成该对象无法被系统回收&#xff0c;以致该对象在…

汽车开发流程管理工具赋能安全与质量

随着数字化、人工智能、自动化系统及物联网技术的迅速发展&#xff0c;工程驱动型企业正面临重大转型挑战&#xff0c;亟需加速并深化其变革步伐。众多企业正试图通过采用基于模型的系统工程(MBSE)、产品线工程(PLE)、ASPICE、安全、网络安全、软件定义汽车、敏捷和精益开发实践…

漏洞挖掘JS构造新手向

前置思路文章 JS逆向混淆前端对抗 油猴JS逆向插件 JS加解密之mitmproxy工具联动Burp JS挖掘基础 伪协议 JavaScript伪协议是一种在浏览器中模拟网络请求的方法。它使用window.XMLHttpRequest对象或fetch()方法来模拟发送HTTP请求&#xff0c;而不是通过实际的网络请求来获…

最牛4G模组展示文件系统如何存储温湿度数据,有手就会还不牛?

有手就会的保姆级流程&#xff0c;展示大家常用的低功耗模组实用功能。 1.编写脚本 1.1 准备资料 780E开发板购买链接 780E开发板设计资料 LuatOS-Air780E-文件系统的使用-程序源码demo 合宙的TCP/UDP测试服务器 API使用介绍 780E开发板和DHT11 1.2 程序详解 第一步&a…

【C++ 算法进阶】算法提升五

先序遍历改二叉搜索树 &#xff08;二叉树的递归套路&#xff09; 题目 本题为LC原题目 题目如下 题目分析 本题为一道经典的二叉树递归套路题目 我们只需要想好一个递归函数 之后让左右节点分别执行即可 我们这里想到的递归函数为 TreeNode* process(vector<int>&a…

asp.net core mvc发布时输出视图文件Views

var builder WebApplication.CreateBuilder(args); builder.Services.AddRazorPages();builder.Services.AddControllersWithViews(ops > {//全局异常过滤器&#xff0c;注册ops.Filters.Add<ExceptionFilter>(); })// Views视图文件输出到发布目录&#xff0c;视图文…

【yolov8旋转框检测】微调yolov8-obb目标检测模型:数据集制作和训练

一、开发环境的准备 1.1 安装roLabelImg 参考【目标检测—旋转框标注】roLabelImg安装与使用文章的介绍&#xff0c;完成roLabelImg的安装。 1.2 Yolov8开发环境的准备 首先创建python虚拟环境&#xff0c;pip install ultralytics 来进行安装。 二、数据集准备 流程&…

FairGuard游戏加固全面适配纯血鸿蒙NEXT

2024年10月8日&#xff0c;华为正式宣布其原生鸿蒙操作系统 HarmonyOS NEXT 进入公测阶段&#xff0c;标志着其自有生态构建的重要里程碑。 作为游戏安全领域领先的第三方服务商&#xff0c;FairGuard游戏加固在早期就加入了鸿蒙生态的开发&#xff0c;基于多项独家技术与十余年…

数据库权限提升GetShell

数据库提权总结 - 随风kali - 博客园 (cnblogs.com) MySQL 漏洞利用与提权 | 国光 (sqlsec.com) sql注入getshell的几种方式 第99天&#xff1a;权限提升-数据库提权&口令获取&MYSQL&MSSQL&Oracle&MSF SQL注入拿shell的方式应该是通用的得到连接数据库…

未来AI的学习能力会达到怎样的水平?

​ 大家好&#xff0c;我是Shelly&#xff0c;一个专注于输出AI工具和科技前沿内容的AI应用教练&#xff0c;体验过300款以上的AI应用工具。关注科技及大模型领域对社会的影响10年。关注我一起驾驭AI工具&#xff0c;拥抱AI时代的到来。 AI工具集1&#xff1a;大厂AI工具【共2…

微软运用欺骗性策略大规模打击网络钓鱼活动

微软正在利用欺骗性策略来打击网络钓鱼行为者&#xff0c;方法是通过访问 Azure 生成外形逼真的蜜罐租户&#xff0c;引诱网络犯罪分子进入以收集有关他们的情报。 利用收集到的数据&#xff0c;微软可以绘制恶意基础设施地图&#xff0c;深入了解复杂的网络钓鱼操作&#xff…

Verilog基础:层次化标识符的使用

相关阅读 Verilog基础https://blog.csdn.net/weixin_45791458/category_12263729.html?spm1001.2014.3001.5482 一、前言 Verilog HDL中的标识符(identifier)是一个为了引用而给一个Verilog对象起的名字&#xff0c;分为两大类&#xff1a;普通标识符大类和层次化标识符大类。…

监控易监测对象及指标之:Kafka中间件JMX监控指标解读

监控易作为一款功能强大的监控软件&#xff0c;旨在为企业提供全方位的IT系统监控服务。其中&#xff0c;针对Kafka中间件的JMX监控是监控易的重要功能之一。本文将详细解读监控易中Kafka的JMX监控指标&#xff0c;帮助企业更好地理解并运用这些数据进行系统性能调优和故障排查…

算法笔记day07

1.最长回文子串 最长回文子串_牛客题霸_牛客网 算法思路&#xff1a; 使用中心扩散算法&#xff0c;枚举所有的中点&#xff0c;向两边扩散&#xff0c;一个中点需要枚举两次&#xff0c;一次当回文串是奇数另一次回文串是偶数的情况。 class Solution { public:int getLong…

mysql--基本查询

目录 搞定mysql--CURD操作&#xff0c;细节比较多&#xff0c;不难&#xff0c;贵在多多练 1、Create--创建 &#xff08;1&#xff09;单行插入 / 全列插入 &#xff08;2&#xff09;插入否则替换 &#xff08;3&#xff09;替换 2、Retuieve--select 1&#xff09;全…

git rebase的常用场景: 交互式变基, 变基和本地分支基于远端分支的变基

文章目录 作用应用场景场景一&#xff1a;交互式变基(合并同一条线上的提交记录) —— git rebase -i HEAD~2场景二&#xff1a;变基(合并分支) —— git rebase [其他分支名称]场景三&#xff1a;本地分支与远端分支的变基 作用 使git的提交记录变得更加简洁 应用场景 场景…

Unity之如何使用Unity Cloud Build云构建

文章目录 前言什么是 UnityCloudBuild?如何使用Unity云构建Unity 团队中的人员不属于 Unity Team 的人员UnityCloudBuild2.0价格表如何使用Unity云构建配置CloudBuild前言 Unity Cloud Build作为Unity平台的一项强大工具,它允许开发团队通过云端自动构建项目,节省了繁琐的手…

基于Springboot在线视频网站的设计与实现

基于Springboot视频网站的设计与实现 开发语言&#xff1a;Java 框架&#xff1a;springboot JDK版本&#xff1a;JDK1.8 服务器&#xff1a;tomcat7 数据库&#xff1a;mysql 5.7 数据库工具&#xff1a;Navicat11 开发软件&#xff1a;idea 源码获取&#xff1a;https://do…

JSON 注入攻击 API

文章目录 JSON 注入攻击 API"注入所有东西"是"聪明的"发生了什么? 什么是 JSON 注入?为什么解析器是问题所在解析不一致 JSON 解析器互操作性中的安全问题处理重复密钥的方式不一致按键碰撞响应不一致JSON 序列化(反序列化)中的不一致 好的。JSON 解析器…