STM32 CubeMX学习:7. ADC模数转化
STM32 CubeMX学习:7. ADC模数转化
系列文章目录
- 前期的准备
- 点亮 LED
- 闪烁 LED
- 定时器闪烁LED
- PWM控制LED亮度
- 常见的PWM设备
- 按键的外部中断
- ADC模数转换
- 串口收发
- 串口打印遥控器数据
- 未完待续…
文章目录
- STM32 CubeMX学习:7. ADC模数转化
- 0 前言
- 1. 基础知识学习
- 1.1 ADC原理介绍
- 1.2 STM32F4x ADC介绍
- 2. 程序学习
- 2.1 ADC在CubeMX的配置
- 2.2 内部VREFINT电压的使用
- 2.3 相关函数介绍
- 2.4 程序执行流程
- 3. 进阶学习
- 总结
0 前言
我们已经知道,在单片机中传输的信号均为数字信号,通过离散的高低电平表示数字逻辑的1和0,但是在现实的物理世界中,只存在模拟信号,即连续变化的信号,将这些连续变化的信号——比如热,光,声音,速度通过各种传感器转化成连续的电信号,再通过ADC功能将连续的模拟信号转化成离散的数字信号给单片机进行处理。
1. 基础知识学习
1.1 ADC原理介绍
一般ADC的工作流程为采样,比较,转换。
- 采样:是指对某一时刻的模拟电压进行采集,
- 比较:是指将采样的电压在比较电路中进行比较,
- 转换:是指将比较电路中结果转换成数字量。
在我们这里,使用的STM32f4采用12位逐次逼近型ADC(SAR-ADC)。现在,以下图为例,介绍3位ADC的比较过程:
不同的位数分别赋予1/2,1/4,1/8的权值,模拟信号的采样值为Vin,
1)与1/2Vref进行比较,Vin大于1/2Vref,则将第一位标记为1,
2)与3/4Vref进行比较,Vin小于3/4Vref,则将第二位标记为0,
3)与5/8Vref进行比较,Vin小于5/8Vref,则将第三位标记为0。
图中的Vin通过这个三位的ADC后输出的结果为100。转换的结果为1/2Vref,通过这样逐次比较过程,将采样取得的模拟电压和内部参考电压Vref的加权值进行比较,不同的位数赋予不同的权值。
但是,如果我们输入为一个未知的电压,完整的比较流程图如下:
STM32支持最高12位ADC,一般ADC的位数越多则转换精度越高,但与此同时转换的速度也会变慢。
此外,STM32内部有一个校准电压VREFINT,电压为1.2V,当供电电压不为3.3V,可以使用内部的vrefint通道采集1.2V电压作为Vref,以提高精度。
1.2 STM32F4x ADC介绍
- STM32F4x ADC特点
- STM32F40x系列ADC外部通道和引脚对应关系
2. 程序学习
2.1 ADC在CubeMX的配置
这次我们使用引脚PF10作为电源ADC引脚,使用ADC3的通道8,STM32内部的1.2V校准电压Vrefint在ADC1中。所以我们需要按如下方式在CubeMX里进行配置。
- 开启ADC1和ADC3分别用于内部1.2V的Vrefint通道读取和电池电压ADC3的通道8读取。
- 在cubeMX中开启ADC1,在设置中将Vrefint Channel勾选,用于读取内部参考电压。ADC在cubeMX中的设置如图采样频率设置为PCLK2/4,采样位数为12位,数据设置为右对齐,其余均保持默认。其中Vrefine在stm32内部完成,没有对应引脚。
最终ADC配置如图所示。
《补充》:但是,这里还是把其中参数的具体含义给大家科普一下吧
名称 | 功能 |
---|---|
Clock Prescaler | 设置采样时钟频率 |
Resolution | 设置采样精度 |
Data Alignment | 设置数据对齐方式 |
Scan Conversion Mode | 扫描转换模式开启/关闭 |
Continuous Conversion Mode | 连续转换模式开启/关闭 |
Discontinuous Conversion Mode | 非连续转换模式开启/关闭 |
DMA Continuous Requests | DMA连续启动开启/关闭 |
End of Conversion Selection | 每个通道转换结束后发送EOC标志/所有通道转换结束后发送EOC标志 |
在CubeMX中开启ADC3,并打开其IN8用于电池电压的读取,其设置和ADC1一致。可以看到引脚图像中ADC3对应的PF10变绿。
这里我们需要把PC0,PC1,PC2引脚设置为输入模式,且这三个引脚拉为高电平,如图所示
点击GENERATE CODE生成代码
2.2 内部VREFINT电压的使用
VREFINT 即 ADC的内部参照电压1.2V。通过将采样内部参照电压1.2V的 ADC 值和 Vref 的加权值进行比较,进而得到 ADC 的输出值。一般来说STM32的 ADC 采用 Vcc 作为 Vref ,但为了防止 Vcc 存在波动较大导致Vref不稳定,进而导致采样值的比较结果不准确,STM32可以通过内部已有的参照电压VREFINT来进行校准,接着以VREFINT为参照来比较ADC的采样值,从而获得比较高的精度,VREFINT的电压为1.2V。
通过一个函数对1.2V的电压进行多次采样,并计算其平均值,接着将其与ADC采出的数据值做对比,得到单位数字电压对应的模拟电压值voltage_vrefint_proportion,其计算公式如下,设采样得到的数字值为average_adc:
average_adc=total_dac200average\_adc=\frac{total\_dac}{200} average_adc=200total_dac
voltage_vrefint_proportion=1.2vaverage_adc=200∗1.2total_adcvoltage\_vrefint\_proportion=\frac{1.2v}{average\_adc}=\frac{200 ∗ 1.2}{total\_adc} voltage_vrefint_proportion=average_adc1.2v=total_adc200∗1.2
其代码如下所示
void init_vrefint_reciprocal(void)
{ uint8_t i = 0; uint32_t total_adc = 0; for(i = 0; i < 200; i++){total_adc += adcx_get_chx_value(&hadc1, ADC_CHANNEL_VREFINT);} voltage_vrefint_proportion = 200 * 1.2f / total_adc;
}
2.3 相关函数介绍
Hal库提供了以下很多有关ADC的函数
- ADC通道设置函数
HAL_StatusTypeDef HAL_ADC_ConfigChannel(ADC_HandleTypeDef* hadc, ADC_ChannelConfTypeDef* sConfig)
函数作用 | 设置ADC通道的各个属性值,包括转换通道,序列排序,采样时间等 |
---|---|
返回值 | HAL_StatusTypeDef,HAL库定义的几种状态,如果成功使ADC开始工作,则返回HAL_OK |
参数1 | TIM_HandleTypeDef * hadc即ADC的句柄指针,如果是adc1就输入&hadc1,adc2就输入&adc2 |
参数2 | ADC_ChannelConfTypeDef* sConfig即指向ADC设置的结构体指针。我们先对sConfig结构体进行赋值,然后再将其指针作为参数输入函数 |
- ADC开启采样函数
HAL_StatusTypeDef HAL_ADC_Start(ADC_HandleTypeDef* hadc)
函数作用 | 开启ADC的采样 |
---|---|
返回值 | HAL_StatusTypeDef,HAL库定义的几种状态,如果成功使ADC开始工作,则返回HAL_OK |
参数 | TIM_HandleTypeDef * hadc即ADC的句柄指针,如果是adc1就输入&hadc1,adc2就输入&adc2 |
- 等待ADC转换结束函数
HAL_StatusTypeDef HAL_ADC_PollForConversion(ADC_HandleTypeDef* hadc, uint32_t Timeout)
函数作用 | 等待ADC转换结束 |
---|---|
返回值 | HAL_StatusTypeDef,HAL库定义的几种状态,如果成功使ADC开始工作,则返回HAL_OK |
参数1 | TIM_HandleTypeDef * hadc即ADC的句柄指针,如果是adc1就输入&hadc1,adc2就输入&adc2 |
参数2 | HAL_StatusTypeDef,HAL库定义的几种状态,如果成功使ADC开始工作,则返回HAL_OK |
- 获取ADC函数
uint32_t HAL_ADC_GetValue(ADC_HandleTypeDef* hadc)
函数作用 | 获取ADC值 |
---|---|
返回值 | HAL_StatusTypeDef,HAL库定义的几种状态,如果成功使ADC开始工作,则返回HAL_OK |
参数 | TIM_HandleTypeDef * hadc即ADC的句柄指针,如果是adc1就输入&hadc1,adc2就输入&adc2 |
2.4 程序执行流程
本程序中,首先对内部参考电压电压VREFINT进行adc采样将其作为校准值,在init_vrefint_reciprocal中,对VREFINT电压进行200次的采样,接着求其均值后使用VREFINT的电压值1.2V去除以该ADC采样得到的均值,算出voltage_vrefint_proportion,后续ADC中采样到的电压值与voltage_vrefint_proportion相乘就可以计算出以内部参考电压做过校准的ADC值了。
void init_vrefint_reciprocal(void)
{ uint8_t i = 0; uint32_t total_adc = 0; for(i = 0; i < 200; i++) { total_adc += adcx_get_chx_value(&hadc1, ADC_CHANNEL_VREFINT); } voltage_vrefint_proportion = 200 * 1.2f / total_adc;
}
接着通过ADC对经过了分压电路的电池电压值进行采样,将该采样结果与voltage_vrefint_proportion相乘,就得到了取值范围在0-3.3V之间的ADC采样值,由于这个采样值是分压后的结果,需要反向计算出电压的值。分压的电阻值为200KΩ和22KΩ,由于 (22K Ω + 200K Ω) / 22K Ω = 10.09,乘以这个值之后就可以得到电池的电压值。
(注意,这里我们是根据一个具体的分压电路给出的计算公式,当我们实际使用时,要根据自己所使用的具体电路修改对应的计算方法,)
fp32 get_battery_voltage(void)
{ fp32 voltage; uint16_t adcx = 0; adcx = adcx_get_chx_value(&hadc3, ADC_CHANNEL_8);//(22K Ω + 200K Ω) / 22K Ω = 10.090909090909090909090909090909 voltage = (fp32)adcx * voltage_vrefint_proportion * 10.090909090909090909090909090909f; return voltage;
}
其实,我们还可以通过ADC获得板载的温度传感器的温度值,同样是先经过ADC值进行采样,采样结束后,将ADC采样结果adc带入公式temperate = (adc - 0.76f) * 400.0f + 25.0f,从而计算出温度值。
fp32 get_temprate(void)
{ uint16_t adcx = 0; fp32 temperate; adcx = adcx_get_chx_value(&hadc1, ADC_CHANNEL_TEMPSENSOR);temperate = (fp32)adcx * voltage_vrefint_proportion;temperate = (temperate - 0.76f) * 400.0f + 25.0f;return temperate;
}
在我们完成代码时,还需要像以前一样自己新建一个Boards目录,在里面新建一个bsp_adc.c文件
#include "bsp_adc.h"
#include "main.h"
extern ADC_HandleTypeDef hadc1;
extern ADC_HandleTypeDef hadc3;volatile fp32 voltage_vrefint_proportion = 8.0586080586080586080586080586081e-4f;static uint16_t adcx_get_chx_value(ADC_HandleTypeDef *ADCx, uint32_t ch)
{static ADC_ChannelConfTypeDef sConfig = {0};sConfig.Channel = ch;sConfig.Rank = 1;sConfig.SamplingTime = ADC_SAMPLETIME_3CYCLES;//ADC_SAMPLETIME_3CYCLES;if (HAL_ADC_ConfigChannel(ADCx, &sConfig) != HAL_OK){Error_Handler();}HAL_ADC_Start(ADCx);HAL_ADC_PollForConversion(ADCx, 10);return (uint16_t)HAL_ADC_GetValue(ADCx);}
void init_vrefint_reciprocal(void)
{uint8_t i = 0;uint32_t total_adc = 0;for(i = 0; i < 200; i++){total_adc += adcx_get_chx_value(&hadc1, ADC_CHANNEL_VREFINT);}voltage_vrefint_proportion = 200 * 1.2f / total_adc;
}
fp32 get_temprate(void)
{uint16_t adcx = 0;fp32 temperate;adcx = adcx_get_chx_value(&hadc1, ADC_CHANNEL_TEMPSENSOR);temperate = (fp32)adcx * voltage_vrefint_proportion;temperate = (temperate - 0.76f) * 400.0f + 25.0f;return temperate;
}fp32 get_battery_voltage(void)
{fp32 voltage;uint16_t adcx = 0;adcx = adcx_get_chx_value(&hadc3, ADC_CHANNEL_8);//(22K Ω + 200K Ω) / 22K Ω = 10.090909090909090909090909090909voltage = (fp32)adcx * voltage_vrefint_proportion * 10.090909090909090909090909090909f;return voltage;
}uint8_t get_hardware_version(void)
{uint8_t hardware_version;hardware_version = HAL_GPIO_ReadPin(HW0_GPIO_Port, HW0_Pin)| (HAL_GPIO_ReadPin(HW1_GPIO_Port, HW1_Pin)<<1)| (HAL_GPIO_ReadPin(HW2_GPIO_Port, HW2_Pin)<<2);return hardware_version;
}
接着新建一个对应的bsp_adc.h文件放置于对应目录之下
#ifndef BSP_ADC_H
#define BSP_ADC_H
#include "#ifndef BSP_ADC_H
#define BSP_ADC_H
#include "struct_typedef.h"extern void init_vrefint_reciprocal(void);
extern fp32 get_temprate(void);
extern fp32 get_battery_voltage(void);
extern uint8_t get_hardware_version(void);
#endif
"extern void init_vrefint_reciprocal(void);
extern fp32 get_temprate(void);
extern fp32 get_battery_voltage(void);
extern uint8_t get_hardware_version(void);
#endif
在这个文件中,我们还用到了一个定义结构体的头文件:struct_typedef.h
#ifndef STRUCT_TYPEDEF_H
#define STRUCT_TYPEDEF_Htypedef signed char int8_t;
typedef signed short int int16_t;
typedef signed int int32_t;
typedef signed long long int64_t;/* exact-width unsigned integer types */
typedef unsigned char uint8_t;
typedef unsigned short int uint16_t;
typedef unsigned int uint32_t;
typedef unsigned long long uint64_t;
typedef unsigned char bool_t;
typedef float fp32;
typedef double fp64;#endif
最终的main.c文件如下所示
/* USER CODE BEGIN Header */
/********************************************************************************* @file : main.c* @brief : Main program body******************************************************************************* @attention** <h2><center>© Copyright (c) 2021 STMicroelectronics.* All rights reserved.</center></h2>** This software component is licensed by ST under BSD 3-Clause license,* the "License"; You may not use this file except in compliance with the* License. You may obtain a copy of the License at:* opensource.org/licenses/BSD-3-Clause********************************************************************************/
/* USER CODE END Header *//* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "adc.h"
#include "gpio.h"/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "bsp_adc.h"
/* USER CODE END Includes *//* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD *//* USER CODE END PTD *//* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD *//* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM *//* USER CODE END PM *//* Private variables ---------------------------------------------------------*//* USER CODE BEGIN PV *//* USER CODE END PV *//* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP *//* USER CODE END PFP *//* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
fp32 voltage;
fp32 temperature;
uint8_t handware_version;
/* USER CODE END 0 *//*** @brief The application entry point.* @retval int*/
int main(void)
{/* USER CODE BEGIN 1 *//* USER CODE END 1 *//* MCU Configuration--------------------------------------------------------*//* Reset of all peripherals, Initializes the Flash interface and the Systick. */HAL_Init();/* USER CODE BEGIN Init *//* USER CODE END Init *//* Configure the system clock */SystemClock_Config();/* USER CODE BEGIN SysInit *//* USER CODE END SysInit *//* Initialize all configured peripherals */MX_GPIO_Init();MX_ADC1_Init();MX_ADC3_Init();/* USER CODE BEGIN 2 *///use vrefint voltage to calibrate//使用基准电压来校准init_vrefint_reciprocal();/* USER CODE END 2 *//* Infinite loop *//* USER CODE BEGIN WHILE */while (1){/* USER CODE END WHILE *//* USER CODE BEGIN 3 *///get battery voltage//获取电源电压voltage = get_battery_voltage();//get chip temperate//获取片内温度temperature = get_temprate();//get handware version//获取硬件版本handware_version = get_hardware_version();HAL_Delay(100);}/* USER CODE END 3 */
}/*** @brief System Clock Configuration* @retval None*/
void SystemClock_Config(void)
{RCC_OscInitTypeDef RCC_OscInitStruct = {0};RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};/** Configure the main internal regulator output voltage */__HAL_RCC_PWR_CLK_ENABLE();__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);/** Initializes the CPU, AHB and APB busses clocks */RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;RCC_OscInitStruct.HSEState = RCC_HSE_ON;RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;RCC_OscInitStruct.PLL.PLLM = 6;RCC_OscInitStruct.PLL.PLLN = 168;RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;RCC_OscInitStruct.PLL.PLLQ = 4;if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK){Error_Handler();}/** Initializes the CPU, AHB and APB busses clocks */RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5) != HAL_OK){Error_Handler();}
}/* USER CODE BEGIN 4 *//* USER CODE END 4 *//*** @brief This function is executed in case of error occurrence.* @retval None*/
void Error_Handler(void)
{/* USER CODE BEGIN Error_Handler_Debug *//* User can add his own implementation to report the HAL error return state *//* USER CODE END Error_Handler_Debug */
}#ifdef USE_FULL_ASSERT
/*** @brief Reports the name of the source file and the source line number* where the assert_param error has occurred.* @param file: pointer to the source file name* @param line: assert_param error line source number* @retval None*/
void assert_failed(uint8_t *file, uint32_t line)
{ /* USER CODE BEGIN 6 *//* User can add his own implementation to report the file name and line number,tex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) *//* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT *//************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
3. 进阶学习
逐次型ADC是通过一位位进行比较得出转换值,其原理图如下所示。
如图所示,我们发现整个电路由比较器、D/A转换器、缓冲寄存器和若干控制逻辑电路构成。作用如下所示:
- 比较器:用于输入电压值D/A转换器输出电压进行比较,当输入电压大于D/A转换器电压时,输出为1,反之输出为0;
- D/A转换器:ADC的逆向过程,将缓冲寄存器的记录数字量转换成模拟量。
- 缓冲寄存器:记录当前转换的数字量。
整个过程如下:
- 将缓冲寄存器清零;
- 将逐次逼近寄存器 最高位 置1;
- 把数字值送入D/A转换器,经D/A转换后的模拟量送入比较器,称为 Vo;
- Vo与比较器的待转换的模拟量Vi 比较,若Vo<Vi,该位被保留,否则被清0。
- 再置 寄存器 次高位为1,将寄存器中新的数字量送D/A转换器,
- 输出的 Vo再与Vi比较,若Vo<Vi,该位被保留,否则被清0。
- 循环此过程,直到寄存器最低位,得到数字量的输出。
在这个过程中,D/A转换器使用的电压为STM32的电源电压Vref+和Vref-,如图所示:
STM32的标准电压为 3.3V。例如第一次转换时,输出为 1/2Vre电压,即1.65V。但由于外部供电电压不一定为 3.3V。故而为了在这种情况提高ADC精度,STM32内部有VERFINT1.2V稳定电压,可以使用ADC采样该电压来提高ADC的精度。
代码我已经放到了我的GitHub仓库,如有需要可以下载使用:
CubeMX学习
总结
这次的博客,我们重点学习了ADC的相关知识,ADC是模拟量变成数字量的过程,单片机获取电压,电流传感器等模拟量的方法。通过ADC功能,我们能够获取各种传感器的模拟值,并将其转化为单片机可以处理的数字量,也可以获取电池的电压值,并将其通过电量形式显示出来。
STM32 CubeMX学习:7. ADC模数转化相关推荐
- stm32学习笔记 ADC模拟数字转换器 ADC模数转化器详解
一.为什么要使用ADC模拟数字转换器 STM32主要是数字电路,数字电路只有高低电平,没有几V电压的概念,所以如果想要读取电压值,就需要借助ADC模数转化器来实现.可以说ADC是模拟 ...
- STM32 CubeMx LL库-ADC操作
STM32 CubeMx LL库-ADC操作 硬件选型 stm32f103c8t6 USB-TTL 关于STM32的ADC介绍这里有时间再补充,直接进入代码编写. 1. CubeMx-ADC配置 2. ...
- STM32 CubeMX配置ADC+DMA进行FFT(1)
STM32 CubeMX配置ADC+DMA,计算采样率和ADC电压 原创:睿智嵌入式 最近,因为项目需要处理音频信号,对AD采集的音频信号进行FFT运算,记录一下学习过程. 主要内容: 1.ADC和D ...
- stm32+cubemx+adc+time定时采集+dma多通道采集
cubemx中的时钟及下载方式配置忽略,不了解的道友可以看我其他博客进行配置 打开cubemx中的adc打开4个通道 打开dma并配置 adc配置如下 要注意打开扫描模式及dma,还有ADC模式中的通 ...
- Proteus STM32仿真学习板
一 全球IC爆冷,价格猛涨,ST系列单片机更是一马当先,贵到你怀疑人生.这个时候想学习STM32的基础去购买开发板是很不划算的,而且用完就搁置了,吃灰到永远,能半价甩卖就已经很好了. 为了让大家方便快 ...
- STM32 进阶教程 18 – ADC间断模式
前言 STM32 的ADC拥有连续扫描模式,也有间断模式,间断模式较扫描模式需要更多的触发事件才能完成所有的通道转换操作,在实际工程应用中,可以利用间断模式实现一些特殊应用.关于间断模式,在STM32 ...
- STM32 进阶教程 17 - ADC注入通道
前言 STM32 的ADC的一个强大功能是支持触发注入功能,在103中每个ADC模块支持4个注入通道,每个注入通道具有独立的结果突存器,注入通道具有较规划通道更高的优先级,在实际工程应用中,注入通道更 ...
- c语言定时器定时1ms程序,STM32 Cubemx 配置定时器定时1mS
最近才发现原来我把定时器里的配置参数代表的意义给搞混了,这里记录一下,防止以后自己忘记. 以建立一个定时1mS定时器为例: 1.先打开定时器 2.配置好时钟 3.配置定时器设置 重点来了,以前在这里我 ...
- 嵌入式学习笔记——ADC模数转换器
ADC模数转换器 前言 ADC介绍 ADC概述 ADC的数量 ADC的特性 ADC框图 芯片外部框图 芯片内部框图 转换部分框图 状态输出部分 条件触发框图 寄存器介绍 编程思路 模式选择 规则通道的 ...
最新文章
- Android 的权限级别小记
- Shell(10)——sed(2)
- 自定义权限 android,Android权限控制之自定义权限
- Inbound process in CRM
- 使用nodejs将某个简书用户的文章进行导出
- PHP-RSA加密跨域通讯实战
- 再学 GDI+[91]: TGPImage(11) - 转灰度图像
- Kinect for Windows SDK发布
- 使用ADO.NET访问数据库
- LinkedHashSet类
- kubelet配置cni插件_Kubernetes CNI网络插件
- element表格图片放大_利用element-ui怎么实现一个点击放大图片功能
- python物业管理系统_小型物业管理系统的设计与实现研究背景及意义
- ubuntu boot修复
- 综合船桥系统电子海图数据库设计研究
- java 微软雅黑_网站能使用微软雅黑字体吗?是否会构成侵权?微软雅黑有版本吗? - 飞天奔月的java博客 - ITeye博客...
- SetupAPI简介与设备管理
- 2014省赛----神奇算式(填空)
- #455 科技乱炖:看完《流浪地球2》,我们为行星发动机设计了网络架构
- 在线协作文档综合评测 :金山文档、腾讯文档、石墨文档