【STM32】ADC模数转换器

news2024/9/25 11:20:21

1 ADC简介

ADC(Analog-Digital Converter)模拟-数字转换器

ADC可以将引脚上连续变化的模拟电压转换为内存中存储的数字变量,建立模拟电路到数字电路的桥梁

STM32是数字电路,只有高低电平,没有几V电压的概念,想读取电压值,就需要借助ADC模数转换器了。DAC则是相反的功能。

12位逐次逼近型ADC1us转换时间

分辨率:一般用多少位来表示,0 ~ 2 ^ 12 - 1(量化结果0~4095)
转换时间:也是转换频率,从转换开始到产生结果需要花1us时间,即转换频率是1MHz

输入电压范围:0~3.3V,转换结果范围:0~4095

线性对应

18个输入通道,可测量16个外部和2个内部信号源

16个GPIO口、内部温度传感器(测量CPU的温度)和内部参考电压(1.2V的基准电压)

规则组和注入组两个转换单元

模拟看门狗自动监测输入电压范围

STM32F103C8T6 ADC资源:ADC1ADC210个外部输入通道

1.1 逐次逼近型ADC

ADC0809的内部结构图,独立的8位逐次逼近型ADC芯片。

左边是8路输入通道,通过通道选择开关,选中一路,输入到比较器前进行转换;下面是地址锁存和译码(想选中哪个通道,就把通道号放在这三个引脚上,再给一个锁存信号,上面对应的通路开关就可以自动拨好了,相当于38译码器)

比较器有两个输入端,一个是待测电压,另一个是DAC(数模转换器)的电压输出端;如果DAC输出的电压比较大,就调小DAC数据;如果DAC输出的电压比较小,就增大DAC数据;直到DAC输出的电压和外部通道输入的电压近似相等,这样DAC输入的数据就是外部电压的编码数据了,这就是DAC的实现原理。一般使用二分法调节。

1.2 ADC框图

具体的

有温度传感器、内部参考电压,总共18个输入通道,接到模拟多路开关,其输出接到模数转换器,即逐次比较,转换结果放在数据寄存器里。

普通的的流程是:多路开关选中某一个通道,开始转换,等待转换完成,取出结果。

这里比较高级,可以同时选中多个,而且在转换的时候,还分成了两个组,规则通道组注入通道组其中规则组可以一次性最多选择16个通道,注入组一次最多选择4个通道。规则组的数据寄存器只能存储一个结果,如果不想之前的结果被覆盖,那在转换完成之后,要尽快把结果拿走(使用DMA转运数据);注入组可以存4个结果,不用担心数据覆盖。

左下角是触发转换部分,对于STM32的ADC,触发ADC开始转换的信号有两种:一种是软件触发,程序中调用一条代码;另一种是硬件触发,就是这些触发源。上面是注入组触发源,下面是规则组触发源。这些触发源主要来自定时器,有定时器的各个通道,还有TRGO定时器主模式的输出。定时器可以通向ADC、DAC这些外设,用于触发转换。因为ADC经常需要过一个固定的时间段转换一次,正常的思路是:用定时器,每隔1ms申请一次中断,在中断里手动开始一次转换;但是频繁进中断对程序是有影响的,所以对这种需要频繁进中断,并且在中断里只完成简单的工作的情况,一般都会有硬件的支持。还可以选择外部中断引脚来触发转换。

VREF+、VREF-是ADC的参考电压,决定了ADC的输入电压范围,VDDA、VSSA是ADC的供电引脚,一般情况下VREF+接VDDA,VREF-接VSSA。

右边这里是ADCCLK是ADC的时钟,是用于驱动内部逐次比较的时钟,这个时钟来自ADC的预分频器,而ADC预分频器来自RCC的。

模拟看门狗里面可以存储阈值高限和阈值底限,如果指定了模拟看门狗,并且指定了看门的通道,越限之后,它就会乱叫,在上面申请一个模拟看门狗中断,最后通向NVIC。

对于规则组和注入组,转换完成之后,也会有一个EOC转换完成的信号,EOC是规则组的完成信号,JEOC是注入组完成的信号,这两个信号会在状态寄存器里置一个标志位,读取这个标志位就可以知道是不是转换完成了;同时这两个标志位也可以去到NVIC,申请中断,如果开启了NVIC对应的通道,它们就会触发中断。

1.3 ADC基本结构

左边是输入通道,16个GPIO口外加两个内部通道,然后进入AD转换器。AD转换器里有两个组,一个是规则组,一个是注入组,规则组最多选择16个通道,注入组最多选择4个通道,然后转换的结果存在AD数据寄存器里,其中规则组只有一个数据寄存器,而注入组有4个数据寄存器。下面有触发控制,触发控制可以选择软件触发和硬件触发,硬件触发主要是来自定时器,当然也可以选择外部中断的引脚;右边是来自RCC的ADC时钟CLOCK,ADC逐次比较的功能就是这个时钟推动的。然后上面可以布置一个模拟看门狗用于检测转换结果的范围,如果超出设定的阈值,就通过中断输出控制,向NVIC申请中断。另外规则组和注入组完成之后会有个EOC信号,它会置一个标志位,当然也可以通向NVIC。右下角有个开关控制,在库函数中就是ADC_Cmd,用于给ADC上电的。

1.4 输入通道

通道

ADC1

ADC2

ADC3

通道0

PA0

PA0

PA0

通道1

PA1

PA1

PA1

通道2

PA2

PA2

PA2

通道3

PA3

PA3

PA3

通道4

PA4

PA4

PF6

通道5

PA5

PA5

PF7

通道6

PA6

PA6

PF8

通道7

PA7

PA7

PF9

通道8

PB0

PB0

PF10

通道9

PB1

PB1

通道10

PC0

PC0

PC0

通道11

PC1

PC1

PC1

通道12

PC2

PC2

PC2

通道13

PC3

PC3

PC3

通道14

PC4

PC4

通道15

PC5

PC5

通道16

温度传感器

通道17

内部参考电压

ADC通道和引脚复用的关系。

ADC12_IN0的意思是ADC1和ADC2的IN0都是在PA0上的。以此类推。双ADC模式。

1.5 规则组的转换模式

在ADC初始化中会有两个参数,一个是选择单次转换还是连续转换;另一个是扫描模式还是非扫描

1.5.1 单次转换,非扫描模式

这个列表就是规则组里面的菜单,有16个空位。可以在这里写入要转换的通道,比如通道2,在非扫描的模式下,这个菜单就只有第一个序列1的位置有效。这时菜单同时选中一组的方式就退化成简单地选中一个的方式了,在这里可以在序列1的位置指定想转换的通道,比如通道2,然后触发转换,ADC就会对通道2进行模数转换,过一小段时间后,转换完成,转换结果放在数据寄存器里,同时给EOC标志位置1,整个转换过程就结束了。判断EOC标志位来确定转换是否完成。如果想再启动一次转换,那就需要再触发一次,转换结束,置EOC标志位,读结果。如果想换一个通道转换,那就在转换之前把第一个位置的通道2改成其他通道,再启动转换就可以了。这就是单次转换、非扫描的转换模式。

1.5.2 连续转换,非扫描模式

首先还是非扫描模式。所以菜单列表就只用第一个,然后与上一个单次转换不同的是它在一次转换结束后不会停止,而是立刻开始下一轮的转换,然后一直持续下去。只触发一次就可以转换了。

好处是:开始转换之后不需要等待一段时间,因为一直在转换,所以也不需要手动开始转换,也不用判断是否结束,想要读AD值的时候,直接从寄存器取就是了。

1.5.3 单次转换,扫描模式

单次转换,每触发一次,转换结束后,就会停下来。扫描模式会用到菜单列表,选择通道,可以任意指定,可以重复,初始化结构体中有个参数指定通道数目。这里为了防止数据被覆盖,就需要用DMA及时将数据挪走,7个通道转换完成之后,产生EOC信号,转换结束。再触发下一次,开始新的转换。

1.5.4 连续转换,扫描模式

在上一个模式的基础上变了,就是一次转换完成后,立刻开始下一次转换。

还有间断模式,每隔几个转换,暂停一次需要再次触发才能继续。

1.6 触发控制

这个表是规则组的触发源,有来自定时器的信号,也有来自外部引脚/片上外设的信号,具体需要AFIO重映射。还有软件触发。这些触发信号通过右边寄存器的位来完成。

1.7 数据对齐

ADC是12位的,但是数据寄存器是16位的,因此存在一个数据对齐的问题

数据右对齐(一般是这个)

数据左对齐

有点像大端模式,小端模式

1.8 转换时间

AD转换的步骤:采样,保持,量化,编码

STM32 ADC的总转换时间为:TCONV = 采样时间 + 12.5ADC周期(12位)

例如:当ADCCLK=14MHz,采样时间为1.5ADC周期

TCONV = 1.5 + 12.5 = 14ADC周期 = 1μs

1.9 校准

ADC有一个内置自校准模式。校准可大幅减小因内部电容器组的变化而造成的准精度误差。校准期间,在每个电容器上都会计算出一个误差修正码(数字值),这个码用于消除在随后的转换中每个电容器上产生的误差

建议在每次上电后执行一次校准

启动校准前, ADC必须处于关电状态超过至少两个ADC时钟周期

1.10 硬件电路

第一个是电位器产生一个可调的电压,这里电位器的两个固定端,一个接3.3,一个接GND,这样中间的滑动端就可以输出一个0~3.3的可调电压输出了。这里可以接ADC的输入通道,比如PA0口;滑动端往上滑时,电压增大,往下滑时,电压减小。阻值不宜太小。

第二个是传感器输出电压的电路,一般是光敏电阻、热敏电阻、红外接收管、麦克风等。串联分压,当传感器阻值变小时,下拉作用变强,输出端电压就下降;反之,输出端电压增大。

第三个是电压转换电路,比如想测0~5V的VIN电压,但是ADC只能接收0~3.3V的电压,分压。

手册

2 AD单通道

2.1 接线图

2.2 模块封装

按这个初始化

相关库函数

// 在rcc.h中
// 配置ADCCLK分频器,2/4/6/8分频
void RCC_ADCCLKConfig(uint32_t RCC_PCLK2);



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);             // 给ADC上电的

// 中断输出控制,用于某个中断,能不能通往NVIC
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);

// 配置间断模式
void ADC_DiscModeChannelCountConfig(ADC_TypeDef* ADCx, uint8_t Number);    // 每隔几个通道间断一次
void ADC_DiscModeCmd(ADC_TypeDef* ADCx, FunctionalState NewState);        // 是否启用间断模式

// ADC规则组通道配置
void ADC_RegularChannelConfig(ADC_TypeDef* ADCx, uint8_t ADC_Channel, uint8_t Rank, uint8_t ADC_SampleTime);

// ADC外部触发转换控制
void ADC_ExternalTrigConvCmd(ADC_TypeDef* ADCx, FunctionalState NewState);

// ADC获取转换值
uint16_t ADC_GetConversionValue(ADC_TypeDef* ADCx);

// ADC获取双模式转换值
uint32_t ADC_GetDualModeConversionValue(void);

// 获取标志位状态/清除标志位状态/获取中断状态/清除中断挂起位
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);

// 带Injected是注入组的函数

版本1:单次转换非扫描

AD.c

#include "stm32f10x.h"                  // Device header

// AD初始化函数
void AD_Init(void)
{
	// 1开启RCC时钟,ADC、GPIO
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
	
	RCC_ADCCLKConfig(RCC_PCLK2_Div6);			// 72M / 6 = 12M
	
	// 2配置GPIO模拟输入模式
	GPIO_InitTypeDef GPIO_InitStructure;
 	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;        // 模拟输入
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
 	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	// 3配置多路开关,选择规则组的输入通道,指定通道/规则组序列器的次序/通道采样时间
	ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5);
	
	// 4配置ADC转换器
	ADC_InitTypeDef ADC_InitStructure;
	ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;					// 右对齐
	ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;		// 外部触发选择,不使用外部触发
	ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;						// 工作模式(独立/双ADC)
	ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;						// 单次/连续转换
	ADC_InitStructure.ADC_NbrOfChannel = 1;									// 通道数目
	ADC_InitStructure.ADC_ScanConvMode = DISABLE;							// 扫描/非扫描转换
	// 单次转换非扫描模式
	// 单次转换/连续转换  扫描/非扫描
	ADC_Init(ADC1, &ADC_InitStructure);
	
	// 5开关控制
	ADC_Cmd(ADC1, ENABLE);
	
	// 6校准
	ADC_ResetCalibration(ADC1);								// 复位校准
	while(ADC_GetResetCalibrationStatus(ADC1) == SET);		// 等待复位校准完成
	ADC_StartCalibration(ADC1);								// 开始校准
	while(ADC_GetCalibrationStatus(ADC1) == SET);			// 等待校准完成
}

// 获取转换结果
uint16_t AD_GetValue(void)
{
	ADC_SoftwareStartConvCmd(ADC1, ENABLE);  				// 软件触发转换
	while (ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET);	// 等待转换完成
	return ADC_GetConversionValue(ADC1);					// 返回转换值
}

版本2:连续转换非扫描

#include "stm32f10x.h"                  // Device header

// AD初始化函数
void AD_Init(void)
{
	// 1开启RCC时钟,ADC、GPIO
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
	
	RCC_ADCCLKConfig(RCC_PCLK2_Div6);			// 72M / 6 = 12M
	
	// 2配置GPIO模拟输入模式
	GPIO_InitTypeDef GPIO_InitStructure;
 	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;        // 模拟输入
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
 	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	// 3配置多路开关,选择规则组的输入通道,指定通道/规则组序列器的次序/通道采样时间
	ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5);
	
	// 4配置ADC转换器
	ADC_InitTypeDef ADC_InitStructure;
	ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;					// 右对齐
	ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;		// 外部触发选择,不使用外部触发
	ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;						// 工作模式(独立/双ADC)
//	ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;						// 单次/连续转换
	ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;						// 单次/连续转换
	ADC_InitStructure.ADC_NbrOfChannel = 1;									// 通道数目
	ADC_InitStructure.ADC_ScanConvMode = DISABLE;							// 扫描/非扫描转换
	// 单次转换非扫描模式
	// 单次转换/连续转换  扫描/非扫描
	ADC_Init(ADC1, &ADC_InitStructure);
	
	// 5开关控制
	ADC_Cmd(ADC1, ENABLE);
	
	// 6校准
	ADC_ResetCalibration(ADC1);								// 复位校准
	while(ADC_GetResetCalibrationStatus(ADC1) == SET);		// 等待复位校准完成
	ADC_StartCalibration(ADC1);								// 开始校准
	while(ADC_GetCalibrationStatus(ADC1) == SET);			// 等待校准完成
	
	// 加到这里触发一次
	ADC_SoftwareStartConvCmd(ADC1, ENABLE);  				// 软件触发转换
}

// 获取转换结果
uint16_t AD_GetValue(void)
{
//	ADC_SoftwareStartConvCmd(ADC1, ENABLE);  				// 软件触发转换
//	while (ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET);	// 等待转换完成
	return ADC_GetConversionValue(ADC1);					// 返回转换值
}

2.3 主函数

main.c

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "AD.h"

uint16_t ADValue;
float voltage;

int main()
{
	OLED_Init();								// 初始化OLED
	AD_Init();
	
	OLED_ShowString(1, 1, "ADValue:");
	OLED_ShowString(2, 1, "Voltage:0.00v");		// 将转换结果换算成电压值
	
	
	while (1)
	{
		ADValue = AD_GetValue();
		voltage = (float)ADValue / 4095 * 3.3;						// 实际电压值
		OLED_ShowNum(1, 9, ADValue, 4);
		OLED_ShowNum(2, 9, voltage, 1);								// 显示整数部分
		OLED_ShowNum(2, 11, (uint16_t)(voltage * 100) % 100, 2);	// 显示小数
		Delay_ms(100);
	}
}

3 AD多通道

3.1 接线图

使用了4个通道

3.2 模块封装

单次转换非扫描

#include "stm32f10x.h"                  // Device header

// AD初始化函数
void AD_Init(void)
{
	// 1开启RCC时钟,ADC、GPIO
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
	
	RCC_ADCCLKConfig(RCC_PCLK2_Div6);			// 72M / 6 = 12M
	
	// 2配置GPIO模拟输入模式
	GPIO_InitTypeDef GPIO_InitStructure;
 	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;        // 模拟输入
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3;
 	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	// 3配置多路开关,选择规则组的输入通道,指定通道/规则组序列器的次序/通道采样时间
	ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5);
	
	// 4配置ADC转换器
	ADC_InitTypeDef ADC_InitStructure;
	ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;					// 右对齐
	ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;		// 外部触发选择,不使用外部触发
	ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;						// 工作模式(独立/双ADC)
	ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;						// 连续转换
	ADC_InitStructure.ADC_NbrOfChannel = 1;									// 通道数目
	ADC_InitStructure.ADC_ScanConvMode = DISABLE;							// 非扫描转换
	// 单次转换非扫描模式
	// 单次转换/连续转换  扫描/非扫描
	ADC_Init(ADC1, &ADC_InitStructure);
	
	// 5开关控制
	ADC_Cmd(ADC1, ENABLE);
	
	// 6校准
	ADC_ResetCalibration(ADC1);								// 复位校准
	while(ADC_GetResetCalibrationStatus(ADC1) == SET);		// 等待复位校准完成
	ADC_StartCalibration(ADC1);								// 开始校准
	while(ADC_GetCalibrationStatus(ADC1) == SET);			// 等待校准完成
}

// 获取转换结果
uint16_t AD_GetValue(uint8_t ADC_Channel)
{
	// 3配置多路开关,选择规则组的输入通道,指定通道/规则组序列器的次序/通道采样时间
	// 填充通道
	ADC_RegularChannelConfig(ADC1, ADC_Channel, 1, ADC_SampleTime_55Cycles5);
	ADC_SoftwareStartConvCmd(ADC1, ENABLE);  				// 软件触发转换
	while (ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET);	// 等待转换完成
	return ADC_GetConversionValue(ADC1);					// 返回转换值
}

3.3 主函数

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "AD.h"

uint16_t AD0, AD1, AD2, AD3;

int main()
{
	OLED_Init();       						// 初始化
	AD_Init();
	OLED_ShowString(1, 1, "AD0:");
	OLED_ShowString(2, 1, "AD1:");
	OLED_ShowString(3, 1, "AD2:");
	OLED_ShowString(4, 1, "AD3:");
	
	
	while (1)
	{
		AD0 = AD_GetValue(ADC_Channel_0);
		AD1 = AD_GetValue(ADC_Channel_1);
		AD2 = AD_GetValue(ADC_Channel_2);
		AD3 = AD_GetValue(ADC_Channel_3);
		
		OLED_ShowNum(1, 5, AD0, 4);
		OLED_ShowNum(2, 5, AD1, 4);
		OLED_ShowNum(3, 5, AD2, 4);
		OLED_ShowNum(4, 5, AD3, 4);
		
		Delay_ms(100);
		
	}
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

21. python __init__.py 文件的行为

重复打印行为分析 说明结论主模块主模块所在位置不会被python认为是包 说明 我在调试代码的时候,发现上面的print打印了两次,如果将图片中的 from aaa.F import Cat 改成 from F import Cat 则print只会打印一次。这是为什么呢? 结论 from …

华为海思、燧原、海光等齐力打破封锁,谁主AI芯片江山| 百能云芯

近期,美国对英伟达出口进行了限制,导致英伟达无法向中国大陆销售AI芯片,这一局势催生了中国本土IC设计企业的崛起,包括华为旗下的海思科技、腾讯旗下的燧原科技,以及海光信息和新创公司天数智芯等纷纷抢占市场。 据百能…

微信公众服务号升级订阅号

服务号和订阅号有什么区别?服务号转为订阅号有哪些作用?首先我们要知道服务号和订阅号有什么区别。服务号侧重于对用户进行服务,每月可推送4次,每次最多8篇文章,发送的消息直接显示在好友列表中。订阅号更侧重于信息传…

深度探索Linux操作系统 —— Linux图形原理探讨

系列文章目录 深度探索Linux操作系统 —— 编译过程分析 深度探索Linux操作系统 —— 构建工具链 深度探索Linux操作系统 —— 构建内核 深度探索Linux操作系统 —— 构建initramfs 深度探索Linux操作系统 —— 从内核空间到用户空间 深度探索Linux操作系统 —— 构建根文件系统…

基于SpringBoot+Thymeleaf+Mybatis学生信息管理系统(源码+数据库)

一、项目简介 本项目是一套基于SpringBootThymeleafMybatis学生信息管理系统,主要针对计算机相关专业的正在做bishe的学生和需要项目实战练习的Java学习者。 包含:项目源码、数据库脚本等,该项目可以直接作为bishe使用。 项目都经过严格调试…

字符雨canvas

整体思路: 确定好字符雨的具体字符是什么,需要多少行多少列这里是写死的其实也可以用循环加随机的方式生成不一样的字符雨,行列也可以读一下宽度然后做一下出发算一下也行首先得有一张画布搞起,然后循环列数去绘画字符定时器循环…

SQL语句的执行顺序怎么理解?

SQL语句的执行顺序怎么理解? 我们常常会被SQL其书写顺序和执行顺序之间的差异所迷惑。理解这两者的区别,对于编写高效、可靠的SQL代码至关重要。今天,让我们用一些生动的例子和场景来深入探讨SQL的执行顺序。 一、书写顺序 VS 执行顺序 SQ…

数据结构和算法(全)

1.了解数据结构和算法 1.1 二分查找 二分查找(Binary Search)是一种在有序数组中查找特定元素的搜索算法。它的基本思想是将数组分成两半,然后比较目标值与中间元素的大小关系,从而确定应该在左半部分还是右半部分继续查找。这个…

后台业务管理系统原型模板,Axure后台组件库(整套后台管理页面)

后台业务系统需要产品经理超强的逻辑思维能力和业务理解能力,整理了一批后台原型组件及完整的用 Axure 8 制作的后台系统页面,方便产品经理们快速上手制作后台原型。 包括交互元件、首页、商品、订单、库存、用户、促销、运营、内容、统计、财务、设置、…

步进电机驱动芯片TB6600HG部分翻译

有些参数没看懂。一边设计,一边修正。 目录 1.芯片梗概 2.引脚配置 1)引脚含义 2)内部逻辑 3.功能详解 1)励磁模式设置 2)功能设置 3)初始模式 4)100% 电流设置(电流值) 5)OSC 6)衰减…

【unity】【WebRTC】从0开始创建一个Unity远程媒体流app-构建可同步场景

【背景】 最近在研究远程画面,所以就实践了一下。技术采用我认为比较合适的WebRTC。 这篇文章的基础是我的另一篇博文,如果希望顺利完成本篇操作,请先关注我后查询我的如下博文: 【WebRTC】【Unity】Unity Web RTC1-Unity中简单实现远程画面 上一篇地址: 【WebRTC】【Uni…

【PWN】学习笔记(三)【返回导向编程】(中)

目录 课程回顾动态链接过程 课程 课程链接:https://www.bilibili.com/video/BV1854y1y7Ro/?vd_source7b06bd7a9dd90c45c5c9c44d12e7b4e6 课程附件: https://pan.baidu.com/s/1vRCd4bMkqnqqY1nT2uhSYw 提取码: 5rx6 回顾 管道符 | 把前一个指令的输出作…

一文了解java中volatile关键字

认识volatile volatile关键字的作用有两个:变量修改对其他线程立即可见、禁止指令重排。 第二个作用我们后面再讲,先主要讲一下第一个作用。通俗点来说,就是我在一个线程对一个变量进行了修改,那么其他线程马上就可以知道我修改…

树莓派zero w入坑指南

树莓派zero w入坑指南 入坑契机 说起创客不得不提到开源硬件Raspberry Pi(树莓派)。它是一款基于ARM的微型电脑主板,以MicroSD卡为硬盘,提供HDMI和USB等外部接口,可连接显示器和键鼠。以上部件全部整合在一张仅比信用卡稍大的主板上&#x…

MHA高可用实验(故障模拟+恢复)

实验前准备 MHA manager节点服务器:192.168.188.13 MHA node和manager组件 Master节点服务器:192.168.188.14 mysql5.7、MHA node组件 Slave节点服务器1:192.168.188.15 mysql5.7、MHA node组件 Slave节点服务器2:192.…

uniapp x 相比于其他的开发系统框架怎么样?

首先我们要知道niapp这是一种基于Vue.js开发的跨平台应用框架,可以将同一套代码同时运行在多个平台上,包括iOS、Android、H5等。相比其他开发系统框架,他有什么优点呢?让我们共同探讨一下吧! 图片来源:unia…

python每日学10:关于python实用版本的选择

用python也有好几年了,也会经常安装python,因为有工作需要,可能在各个地方使用python,自己的电脑也经常重装,重装后会装python,还有的时候,装的包太多了,影响整个环境的使用&#xf…

Meta 开启 Ray-Ban人工智能眼镜多模态 AI 功能测试,可识别物体、翻译语言

Meta 公司宣布他们将向部分用户推送其 Meta Ray-Ban 智能眼镜的多模态 AI 功能。这项功能允许 AI 助手通过眼镜的摄像头和麦克风了解佩戴者所看到和听到的事物,并提供相关信息和帮助。 Meta CEO 马克・扎克伯格在 Instagram 上展示了这项功能,他让眼镜推…

【WSL】Windows下的Linux子系统使用方法指南

▒ 目录 ▒ 🛫 导读需求开发环境 1️⃣ WSL安装启用或关闭windows功能安装分发命令行启动Linux 2️⃣ WSL 的基本命令显示帮助列出可用的 Linux 发行版列出已安装的 Linux 发行版检查 WSL 版本更新 WSL通过 PowerShell 或 CMD 运行特定的 Linux 发行版关闭WSL全部服…

Amazon SageMaker:让机器学习变得更简单、更强大

授权说明:本篇文章授权活动官方亚马逊云科技文章转发、改写权,包括不限于在 亚马逊云科技开发者社区, 知乎,自媒体平台,第三方开发者媒体等亚马逊云科技官方渠道。 前言: 在大数据时代的浪潮中,数据不再只…