STM32 ADC学习日记

news2024/12/23 23:20:10

STM32 ADC学习日记

1. ADC简介

ADC 即模拟数字转换器,英文详称 Analog-to-digital converter,可以将外部的模拟信号转换为数字信号。

STM32F103 系列芯片拥有 3 个 ADC(C8T6 只有 2 个),这些 ADC 可以独立使用,其中ADC1 和 ADC2 还可以组成双重模式(提高采样率)。STM32 的 ADC 是 12 位逐次逼近型的模拟数字转换器。它有 18 个通道,可测量 16 个外部和 2 个内部信号源,其中 ADC3 根据 CPU 引脚的不同其通道数也不同,一般有 8 个外部通道。ADC 中的各个通道的 A/D 转换可以单次、连续、扫描或间断模式执行。ADC 的结果可以以左对齐或者右对齐存储在 16 位数据寄存器中。

STM32F103 的 ADC 主要特性我们可以总结为以下几条:

  1. 12 位分辨率;

  2. 转换结束、注入转换结束和发生模拟看门狗事件时产生中断

  3. 单次和连续转换模式

  4. 自校准

  5. 带内嵌数据一致性的数据对齐

  6. 采样间隔可以按通道分别编程

  7. 规则转换和注入转换均有外部触发选项

  8. 间断模式

  9. 双重模式(带 2 个或以上 ADC 的器件)

  10. ADC 转换时间:时钟为 72MHz 为 1.17us

  11. ADC 供电要求:2.4V 到 3.6V

  12. ADC 输入范围:VREF–≤VIN≤VREF+

  13. 规则通道转换期间有 DMA 请求产生

2. ADC原理

{12B25F3E-0ED7-4640-94BE-4B32BCBA481A}

ADC作用就是将模拟信号转化为数字信号,模拟信号就是电压等传感器传出的信号,ADC将其转化为数字量,转交给单片机处理。

3. 常见ADC类型

ADC电路类型优点缺点
并联比较型转换速度最快成本高、功耗高,分辨率低
逐次逼近型结构简单,功耗低转换速度较慢

并联比较型工作示意图

image-20241009111342678

优点:转换速度快

缺点:成本高、功耗高、分辨率低

逐次逼近型工作示意图

image-20241009111455696

优点:结构简单、低功耗

缺点:转换速度较慢

特点:分辨率和采样速度相互矛盾,分辨率越高,采样速率越低

{71FF9D70-1C9D-44C3-9D87-F8CD4F738B71}

4. ADC框图

image-20241009110652978

图中,我们按照 ADC 的配置流程标记了七处位置,分别如下:

4.1 ① 输入电压

在前面 ADC 的主要特性也对输入电压有所提及,ADC 输入范围 VREF–≤VIN≤VREF+,最终还是由 VREF–、VREF+、VDDA和 VSSA决定的。下面看一下这几个参数的关系,如图所示:(开发板型号为STM32F103正点原子战舰v4)

{9932588B-67FB-4A52-9AF7-522E4B2B05C8}

{597C62EB-D6DA-4DEB-971F-B171943803E2}

由图可见,VREF+(参考电压)接的是VDDA(3.3v),VREF-接的是GND,所有ADC的输入电压应在0~3.3v内。

4.2 ② 输入通道

在确定好了 ADC 输入电压后,如何把外部输入的电压输送到 ADC 转换器中呢,在这里引入了 ADC 的输入通道,在前面也提及到了 ADC1 和 ADC2 都有 16 个外部通道和 2 个内部通道;而 ADC3 就有 8 个外部通道。外部通道对应的是上图中的 ADCx_IN0、ADCx_IN1…ADCx_IN15。ADC1 的通道 16 就是内部通道,连接到芯片内部的温度传感器,通道 17 连接到Vrefint。而 ADC2 的通道 16 和 17 连接到内部的 VSS。ADC3 的通道 9、14、15、16 和 17 连接到的是内部的 VSS。具体的 ADC 通道表见表 30.1.1 所示:

{F0C5B663-7A94-47A3-8709-4CDAD84CCCBA}

4.3 ③ 转换顺序

当 ADC 的多个通道以任意顺序进行转换就诞生了成组转换,这里有两种成组转换类型:规则组和注入组。规则组就是图中的规则通道,注入组就是图中的注入通道。为了避免大家对输入通道,以及规则通道和注入通道的理解混淆,后面规则通道以规则组来代称,注入通道以注入组来代称。规则组最多允许 16 个输入通道进行转换,而注入组最多允许 4 个输入通道进行转换。这里讲解一下规则组和注入组。

规则组(规则通道)

规则组,按字面理解,“规则”就是按照一定的顺序,相当于正常运行的程序,平常用到最多也是规则组。

注入组(注入通道)

注入组,按字面理解,“注入”就是打破原来的状态,相当于中断。当程序执行的时候,中断是可以打断程序的执行。同这个类似,注入组转换可以打断规则组的转换。假如在规则组转换过程中,注入组启动,那么注入组被转换完成之后,规则组才得以继续转换。为了便于理解,下面看一下规则组和注入组的执行优先级对比图,如图 30.1.3 所示:

image-20241009112855845

了解了规则组和注入组的概念后,下面来看看它们的转换顺序,即转换序列。转换序列可以分为规则序列和注入序列。下面分别来介绍它们。

规则序列

规则组最多允许 16 个输入通道进行转换,那么就需要设置通道转换的顺序,即规则序列。规则序列寄存器有 3 个,分别为 SQR1、SQR2 和 SQR3。SQR3 控制规则序列中的第 1 个到第6 个转换;SQR2 控制规则序列中第 7 个到第 12 个转换;SQR1 控制规则序列中第 13 个到第 16个转换。规则序列寄存器控制关系汇总如表 30.1.2 所示:

{8324062F-CEB5-4B65-849B-0C13F025AE48}

{E5A7ED24-023C-4F17-AEA8-8A72F12611DA}

从上表可以知道,当我们想设置 ADC 的某个输入通道在规则序列的第 1 个转换,只需要把相应的输入通道号的值写入 SQR3 寄存器中的 SQ1[4:0]位即可。例如想让输入通道 5 先进行转换,那么就可以把 5 这个数值写入 SQ1[4:0]位。如果还想让输入通道 8 在第 2 个转换,那么就可以把 8 这个数值写入 SQ2[4:0]位。最后还要设置你的这个规则序列的输入通道个数,只需把输入通道个数写入 SQR1 的 SQL[3:0]位。注意:写入 0 到 SQL[3:0]位,表示这个规则序列有1个输入通道的意思,而不是 0 个输入通道。

注入序列

注入序列,跟规则序列差不多,决定的是注入组的顺序。注入组最大允许 4 个通道输入,它的注入序列由 JSQR 寄存器配置。注入序列寄存器 JSQR 控制关系如表 30.1.3 所示:

{951DE252-6303-4AE9-90DA-671C3A4E4BA2}

注入序列有多少个输入通道,只需要把输入通道个数写入到 JL [ 1 : 0 ]位,范围是 0~3。注意:写入 0 表示这个注入序列有一个输入通道,而不是 0 个输入通道。这个内容很简单。编程时容易犯错的是注入序列的转换顺序问题,下面给大家讲解一下。

如果 JL[ 1 : 0 ]位的值小于 3,即设置注入序列要转换的通道个数小于 4,则注入序列的转换顺序是从 JSQx[ 4 : 0 ](x=4-JL[1:0])开始。例如:JL [ 1 : 0 ]=10、JSQ4 [ 4 : 0 ]= 00100、JSQ3 [ 4 : 0 ]= 00011、JSQ2 [ 4 : 0 ]= 00111、JSQ1 [ 4 : 0 ]= 00010,意味着这个注入序列的转换顺序是:7、3、4,而不是 2、7、3。如果 JL[ 1 : 0 ]=00,那么转换顺序是从 JSQ4 [ 4 : 0 ]开始。

4.4 ④ 触发源

在配置好输入通道以及转换顺序后,就可以进行触发转换了。ADC 的触发转换有两种方法:分别是通过 ADON 位或外部事件触发转换。

(1)ADON位触发转换

当 ADC_CR2 寄存器的 ADON 位为 1 时,再独立给 ADON 位写 1(其它位不能一起改变,这是为了防止误触发),这时会启动转换。这种控制 ADC 启动转换的方式非常简单。

{BA4CA0A2-73C2-4C2C-B41A-8F067CC557F6}

(2)外部触发转换

另一种方法是通过外部事件触发转换,例如定时器捕获、EXTI 线和软件触发,可以分为规则组外部触发和注入组外部触发。规则组外部触发使用方法是将 EXTTRIG 位置 1,并且通过 EXTSET[2:0]位选择规则组启动转换的触发源。如果 EXTSET[2:0]位设置为 111,那么可以通过 SWSTART 为启动 ADC 转换,相当于软件触发。

{0BE3EFEE-91C5-4D36-A1F8-AB6AB23EE0AA}

注入组外部触发使用方法是将 JEXTTRIG 位置 1,并且通过 JEXTSET[2:0]位选择注入组启动转换的触发源。如果 JEXTSET[2:0]位设置为 111,那么可以通过 JSWSTART 为启动 ADC 转换,相当于软件触发。

{7C946A04-8A11-4831-854A-BFFB35CEC497}

ADC1 和 ADC2 的触发源是一样的,ADC3 的触发源和 ADC1/2 有所不同,这个需要注意。

{45D63B23-B858-43F9-B293-F85B665E53EC}

4.5 ⑤ 转换时间

(1)ADC时钟

在学习转换时间之前,我们先来了解 ADC 时钟。从标号框出来部分可以看到 ADC 时钟是要经过 ADC 预分频器的,那么 ADC 的时钟源是什么?ADC 预分频器的分频系数可以设置的范围又是多少?以及 ADC 时钟频率的最大值又是多少?下面将为大家解答。

ADC 的输入时钟是由 PCLK2 经过分频产生的,分频系数是由 RCC_CFGR 寄存器的ADCPRE[1:0]位设置的,可选择 2/4/8/16 分频。需要注意的是,ADC 的输入时钟频率最大值是14MHz,如果超过这个值将会导致 ADC 的转换结果准确度下降。

{903957D2-A518-40FF-8BA5-8534AF41DE42}

一般我们设置 PCLK2 为 72MHz。为了不超过 ADC 的最大输入时钟频率 14MHz,我们设置 ADC 的预分频器分频系数为 6,就可以得到 ADC 的输入时钟频率为 72MHz/6,即 12MHz。例程中,我们也是如此设置的。

{A8A2216E-B842-4278-975E-12D42494CA9B}

(2)转换时间

STM32F103 的 ADC 总转换时间的计算公式如下:

TCONV = 采样时间 + 12.5 个周期

采样时间可通过 ADC_SMPR1 和 ADC_SMPR2 寄存器中的 SMPx[2:0]位设置,x=0~17。ADC_SMPR1 控制的是通道 0~9,ADC_SMPR2 控制的是通道 10~17。每个输入通道都支持通过编程来选择不同的采样时间,采样时间可选的范围如下:

{95DFFEBA-04B2-4D88-A1AC-9B48CE6841AF}

可以看出,采样时间最小是 1.5 个时钟周期,设置为这个值,那么我们可以得到最短的转换时间。下面以我们例程的 ADC 时钟配置为例,来给大家计算一下 ADC 的最短转换时间,计算过程如下:

TCONV= 1.5 个 ADC 时钟周期 + 12.5 个 ADC 时钟周期 = 14 个 ADC 时钟周期

例程中,PCLK2 的时钟是 72MHz,经过 ADC 时钟预分频器的 6 分频后,ADC 时钟频率为 12MHz。代入上式可得到:

TCONV = 14 个 ADC 时钟周期 = ( 12000000 1 ) ∗ 14 s = 1.17us

4.6 ⑥数据寄存器

ADC 转换完成后的数据输出寄存器。根据转换组的不同,规则组的完成转换的数据输出到ADC_DR 寄存器,注入组的完成转换的数据输出到 ADC_JDRx 寄存器。假如是使用双重模式,规则组的数据也是存放在 ADC_DR 寄存器。下面给大家简单介绍一下这两个寄存器。

(1)ADC规则数据寄存器(ADC_DR)

ADC 规则组数据寄存器 ADC_DR 是一个 32 位的寄存器,独立模式时只使用到该寄存器低16 位保存 ADC1/2/3 的规则转换数据。在双 ADC 模式下,高 16 位用于保存 ADC2 转换的数据,低 16 位用于保存 ADC1 转换的数据。因为 ADC 的精度是 12 位的,ADC_DR 寄存器无论高 16 位还是低 16,存放数据的位宽都是 16 位的,所以允许选择数据对齐方式。由 ADC_CR2 寄存器的 ALIGN 位设置数据对齐方式,可选择:右对齐或者左对齐。

细心的朋友可能发现,规则组最多有 16 个输入通道,而 ADC 规则数据寄存器只有一个,如果一个规则组用到好几个通道,数据怎么读取?如果使用多通道转换,那么这些通道的数据也会存放在 DR 里面,按照规则组的顺序,上一个通道转换的数据,会被下一个通道转换的数据覆盖掉,所以当通道转换完成后要及时把数据取走。比较常用的方法是使用 DMA 模式。当规则组的通道转换结束时,就会产生 DMA 请求,这样就可以及时把转换的数据搬运到用户指定的目的地址存放。注意:只有 ADC1 和 ADC3 可以产生 DAM 请求,而由 ADC2 转换的数据可以通过双 ADC 模式,利用 ADC1 的 DMA 功能传输。

(2)ADC 注入数据寄存器 x(ADC_JDRx)(x=1~4)

ADC 注入数据寄存器有 4 个,注入组最多有 4 个输入通道,刚好每个通道都有自己对应的数据寄存器。ADC_JDRx 寄存器是 32 位的,低 16 位有效,高 16 位保留,数据也同样需要选择对齐方式。也是由 ADC_CR2 寄存器的 ALIGN 位设置数据对齐方式,可选择:右对齐或者左对齐。

4.7 ⑦中断

ADC 中断可分为三种:规则组转换结束中断、注入组转换结束中断、设置了模拟看门狗状态位中断。它们都有独立的中断使能位,分别由 ADC_CR 寄存器的 EOCIE、JEOCIE、AWDIE位设置,对应的标志位分别是 EOC、JEOC、AWD。

模拟看门狗中断

模拟看门狗中断发生条件:首先通过ADC_LTR和ADC_HTR寄存器设置低阈值和高阈值,然后开启了模拟看门狗中断后,当被 ADC 转换的模拟电压低于低阈值或者高于高阈值时,就会产生中断。例如我们设置高阈值是 3.0V,那么模拟电压超过 3.0V 的时候,就会产生模拟看门狗中断,低阈值的情况类似。

DMA 请求

规则组和注入组的转换结束后,除了可以产生中断外,还可以产生 DMA 请求,我们利用DMA 及时把转换好的数据传输到指定的内存里,防止数据被覆盖。注意:只有 ADC1 和 ADC3 可以产生 DAM 请求,DMA 相关的知识请回顾 DMA 实验。

4.8 ⑧单次转换模式和连续转换模式

单次转换模式和连续转换模式在框图中是没有标号,为了更好地学习后续的内容,这里简单给大家讲讲。

{A6952B25-91AD-45A2-BE29-60909E03D2B5}

(1)单次转换模式

通过将 ADC_CR2 寄存器的 CONT 位置 0 选择单次转换模式。该模式下,ADC 只执行一次转换,由 ADC_CR2 寄存器的 ADON 位启动(只适用于规则组),也可以通过外部触发启动(适用于规则组或注入组)。如果规则组的一个输入通道被转换,那么转换的数据被储存在 16 位 ADC_DR 寄存器中、EOC(转换结束)标志位被置 1、如果设置了 EOCIE 位,则产生中断,然后 ADC 停止。如果注入组的一个输入通道被转换,那么转换的数据被储存在 16位ADC_DRJx寄存器中、JEOC(转换结束)标志位被置 1、如果设置了 JEOCIE 位,则产生中断,然后 ADC 停止。

(2)连续转换模式

通过将 ADC_CR2 寄存器的 CONT 位置 1 选择连续转换模式。该模式下,ADC 完成上一个通道的转换后会马上自动地启动下一个通道的转换,由 ADC_CR2 寄存器的 ADON 位启动,也可以通过外部触发启动。如果规则组的一个输入通道被转换,那么转换的数据被储存在 16 位 ADC_DR 寄存器中、EOC(转换结束)标志位被置 1、如果设置了 EOCIE 位,则产生中断。如果注入组的一个输入通道被转换,那么转换的数据被储存在 16位ADC_DRJx寄存器中、JEOC(转换结束)标志位被置 1、如果设置了 JEOCIE 位,则产生中断。

{23CAAE61-6577-44A2-8552-53EA99B99883}

4.9 ⑨扫描模式

扫描模式在框图中是没有标号,为了更好地学习后续的内容,这里简单给大家讲讲。可以通过 ADC_CR1 寄存器的 SCAN 位配置是否使用扫描模式。如果选择扫描模式,ADC会扫描所有被 ADC_SQRx 寄存器或 ADC_JSQR 选中的所有通道,并对规则组或者注入组的每个通道执行单次转换,然后停止转换。但如果还设置了 CONT 位,即选择连续转换模式,那么转换不会在选择组的最后一个通道上停止,而是再次从选择组的第一个通道继续转换。如果设置了 DMA 位,在每次 EOC 后,DMA 控制器把规则组通道的转换数据传输到 SRAM中。而注入通道转换的数据总是存储在 ADC_JDRx 寄存器中。

{D6F8748A-561D-4820-A754-410475365A11}

{BD43DCE1-2883-4730-A871-5F976F83C21D}

5. 单通道 ADC 采集实验

单通道ADC采集实验配置步骤

  1. 配置ADC工作参数、ADC校准:HAL_ADC_Init()HAL_ADCEx_Calibration_Start()
  2. ADC MSP初始化:HAL_ADC_MspInit() 配置NVIC、CLOCK、GPIO等
  3. 配置ADC相应通道相关参数:HAL_ADC_ConfigChannel()
  4. 启动A/D转换:HAL_ADC_Start()
  5. 等待规则通道转换完成:HAL_ADC_PollForConversion()
  6. 获取规则通道A/D转换结果:HAL_ADC_GetValue()
函数主要寄存器主要功能
HAL_ADC_Init()CR1、CR2配置ADC工作参数
HAL_ADCEx_Calibration_Start()CR2ADC校准
HAL_ADC_MspInit()存放NVIC、CLOCK、GPIO初始化代码
HAL_RCCEx_PeriphCLKConfig()RCC_CFGR设置扩展外设时钟,如:ADC、RTC等
HAL_ADC_ConfigChannel()SQRx、SMPRx配置ADC相应通道的相关参数
HAL_ADC_Start()CR2启动A/D转换
HAL_ADC_PollForConversion()SR等待规则通道转换完成
HAL_ADC_GetValue()DR获取规则通道A/D转换结果

关键结构体介绍

typedef struct 
{ 
	ADC_TypeDef *Instance; 			/* ADC 寄存器基地址 */ 
	ADC_InitTypeDef Init; 				/* ADC 参数初始化结构体变量 */ 
	DMA_HandleTypeDef *DMA_Handle; 	/* DMA 配置结构体 */

} ADC_HandleTypeDef;

typedef struct
 { 
	uint32_t DataAlign; 					/* 设置数据的对齐方式 */ 
	uint32_t ScanConvMode; 					/* 扫描模式 */ 
	FunctionalState ContinuousConvMode; 	/* 开启单次转换模式或者连续转换模式 */ 	
    uint32_t NbrOfConversion; 				/* 设置转换通道数目 */ 
	FunctionalState DiscontinuousConvMode; 	/* 是否使用规则通道组间断模式 */ 
	uint32_t NbrOfDiscConversion; 			/* 配置间断模式的规则通道个数 */ 
	uint32_t ExternalTrigConv; 				/* ADC 外部触发源选择 */ 
} ADC_InitTypeDef;

typedef struct 
{ 
	uint32_t Channel; 			/* ADC 转换通道*/ 
	uint32_t Rank; 				/* ADC 转换顺序 */ 
	uint32_t SamplingTime; 		/* ADC 采样周期 */ 
}  ADC_ChannelConfTypeDef;

实战代码:单通道采集电压并通过转换

adc.c

ADC_HandleTypeDef g_adc_handle;
void ADC_Init(void)
{
    g_adc_handle.Instance = ADC1; //基地址为ADC1
    g_adc_handle.Init.DataAlign = ADC_DATAALIGN_RIGHT;  //数据对齐方式为右对齐
    g_adc_handle.Init.ScanConvMode = ADC_SCAN_DISABLE;  //不开启扫描模式
    g_adc_handle.Init.ContinuousConvMode = DISABLE;  //单次转换模式
    g_adc_handle.Init.NbrOfConversion = 1;  //转换通道的数量
    g_adc_handle.Init.DiscontinuousConvMode = DISABLE;  //不开启间断模式
    g_adc_handle.Init.ExternalTrigConv = ADC_SOFTWARE_START;    //外部触发源选择软件触发
    
    HAL_ADC_Init(&g_adc_handle);  //初始化ADC
    
    HAL_ADCEx_Calibration_Start(&g_adc_handle);  //校准ADC
    
    ADC_ChannelConfTypeDef adc_channel_handle;
    adc_channel_handle.Channel = ADC_CHANNEL_1; //配置ADC通道1
    adc_channel_handle.Rank = ADC_REGULAR_RANK_1;   //排序为1
    adc_channel_handle.SamplingTime = ADC_SAMPLETIME_239CYCLES_5;   //采样时间为239个周期
    
    HAL_ADC_ConfigChannel(&g_adc_handle,&adc_channel_handle);   //初始化ADC通道
    

}

void HAL_ADC_MspInit(ADC_HandleTypeDef* hadc)   //HAL_ADC_Init的回调函数,在该函数里主要初始化GPIO与时钟
{
    if(hadc ->Instance == ADC1)
    {
        GPIO_InitTypeDef gpio_handle;
        __HAL_RCC_GPIOA_CLK_ENABLE();   //初始化GPIOA的时钟
        __HAL_RCC_ADC1_CLK_ENABLE();    //初始化ADC1的时钟
        
        gpio_handle.Pin = GPIO_PIN_1;   
        gpio_handle.Mode = GPIO_MODE_ANALOG;
        gpio_handle.Speed = GPIO_SPEED_FREQ_MEDIUM;
        gpio_handle.Pull = GPIO_PULLUP;
        
        HAL_GPIO_Init(GPIOA,&gpio_handle);  //初始化GPIO
        
        RCC_PeriphCLKInitTypeDef adc_rcc_handle;
        adc_rcc_handle.PeriphClockSelection = RCC_PERIPHCLK_ADC;    //外设时钟选择为ADC
        adc_rcc_handle.AdcClockSelection = RCC_ADCPCLK2_DIV6;   //选择6分频
        
        HAL_RCCEx_PeriphCLKConfig(&adc_rcc_handle); //配置ADC的时钟

    
    }
    
}
uint16_t ADC_Get_Result(void)   //启动ADC转换
{
    HAL_ADC_Start(&g_adc_handle);   //开启ADC转换
    HAL_ADC_PollForConversion(&g_adc_handle,10);    //等待ADC转换完成
    return (uint16_t)HAL_ADC_GetValue(&g_adc_handle);   //返回转换结果

}

main.c

#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/usart/usart.h"
#include "./SYSTEM/delay/delay.h"
#include "./USMART/usmart.h"
#include "./BSP/LED/led.h"
#include "./BSP/LCD/lcd.h"
#include "./BSP/ADC/adc.h"


int main(void)
{
    uint16_t adcx;
    float temp;
    
    HAL_Init();                             /* 初始化HAL库 */
    sys_stm32_clock_init(RCC_PLL_MUL9);     /* 设置时钟, 72Mhz */
    delay_init(72);                         /* 延时初始化 */
    usart_init(115200);                     /* 串口初始化为115200 */
    led_init();                             /* 初始化LED */
    lcd_init();                             /* 初始化LCD */
    ADC_Init();                             /* 初始化ADC */

    lcd_show_string(30, 50, 200, 16, 16, "STM32", RED);
    lcd_show_string(30, 70, 200, 16, 16, "ADC TEST", RED);
    lcd_show_string(30, 90, 200, 16, 16, "ATOM@ALIENTEK", RED);
    lcd_show_string(30, 110, 200, 16, 16, "ADC1_CH1_VAL:", BLUE);
    lcd_show_string(30, 130, 200, 16, 16, "ADC1_CH1_VOL:0.000V", BLUE); /* 先在固定位置显示小数点 */

    while (1)
    {
        adcx = ADC_Get_Result();
        lcd_show_xnum(134, 110, adcx, 5, 16, 0, BLUE);  /* 显示ADCC采样后的原始值 */
  
        temp = (float)adcx * (3.3 / 4096);              /* 获取计算后的带小数的实际电压值,比如3.1111 */
        adcx = temp;                                    /* 赋值整数部分给adcx变量,因为adcx为u16整形 */
        lcd_show_xnum(134, 130, adcx, 1, 16, 0, BLUE);  /* 显示电压值的整数部分,3.1111的话,这里就是显示3 */

        temp -= adcx;                                   /* 把已经显示的整数部分去掉,留下小数部分,比如3.1111-3=0.1111 */
        temp *= 1000;                                   /* 小数部分乘以1000,例如:0.1111就转换为111.1,相当于保留三位小数。 */
        lcd_show_xnum(150, 130, temp, 3, 16, 0X80, BLUE);/* 显示小数部分(前面转换为了整形显示),这里显示的就是111. */

        LED0_TOGGLE();
        delay_ms(100);
    }
}

在这里插入图片描述

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

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

相关文章

10.9 Qt事件处理机制

键盘按键调整label移动 #include "widget.h" #include "ui_widget.h" #include <QDebug> #include <QKeyEvent>Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this);}Widget::~Widget() {delete ui;…

解决新版Android studio不能连接手机的问题

我要说的是一个特例&#xff0c;装了22年的版本AS可以正常连接手机&#xff0c;装了23年以后新版本&#xff0c;AS不能正常连接手机了&#xff0c;但是在CMD控制台可以正常的执行adb命令&#xff0c;并且CMD和AS都是指向D:\android_sdk\platform-tools\adb.exe 一、 为什么会出…

【万字长文】Word2Vec计算详解(二)Skip-gram模型

【万字长文】Word2Vec计算详解&#xff08;二&#xff09;Skip-gram模型 写在前面 本篇介绍Word2Vec中的第二个模型Skip-gram模型 【万字长文】Word2Vec计算详解&#xff08;一&#xff09;CBOW模型 markdown行 9000 【万字长文】Word2Vec计算详解&#xff08;二&#xff09;S…

HCIA——one

推荐电影&#xff1a;《模仿游戏》《黑客帝国》《头号玩家》 图灵机每秒五千次计算&#xff0c;当今计算机4080ti算力每秒21万亿次的计算。 OSI七层模型 应用层&#xff1a;人机交互&#xff0c;将抽象语言转换成编码 表示层&#xff1a;将编码转换成二进制 介质访问控制层…

Vue3 + TypeScript + Vite + Echarts + DataV

Vue3 TypeScript Vite Echarts DataV 官网&#xff1a; datav datav-vue3 1、创建工程 npm create vitelatestcd datav-app npm install npm run dev2、安装项目依赖模块 npm install types/node --save-devnpm install vue-router4npm install animate.css --save npm i…

【093】基于SpringBoot+Vue实现的精品水果线上销售系统

系统介绍 视频演示 基于SpringBootVue实现的精品水果线上销售系统&#xff08;有文档&#xff09; 基于SpringBootVue实现的精品水果线上销售系统采用前后端分离的架构方式&#xff0c;系统设计了管理员、商家、用户三种角色&#xff0c;实现了公告类型管理、商家信誉类型管理…

网络编程(16)——asio多线程模型IOServicePool

目录 十六、day16 1. 什么是多线程&#xff1f; 2. IOServicePool实现 3. 服务器修改 4. 客户端修改 5. 总结 1. boost::asio::io_context::work的作用&#xff1f; 十六、day16 在之前的设计中&#xff0c;我们对 ASIO 的使用都是采用单线程模式。为了提升网络 I/O 并…

基于多能互补的热电联供型微网优化运行【matlab代码】

目录 1 主要内容 多能互补模型 算例分析 2 部分代码 3 程序结果 4 下载链接 1 主要内容 该程序基本复现《基于多能互补的热电联供型微网优化运行》&#xff0c;在需求侧对负荷类型进行分类&#xff0c;利用电负荷的弹性和系统供热方式的多样性&#xff0c;构建含电负荷时…

常见的图像处理算法:中值滤波----median filter

一、中值滤波是什么 中值滤波法是一种非线性平滑技术&#xff0c;可用于消除孤立的噪声点。中值滤波在滤除噪声的同时&#xff0c;能够保护信号的边缘&#xff0c;使之不被模糊&#xff0c;这些特性是线性滤波方法所不具有的。 二、中值滤波的原理 中值滤波是将每一像素点的灰度…

数据结构进阶:二叉搜索树_C++

目录 前言&#xff1a; 一、二叉搜索树 1.1二叉搜索树概念 2.2 二叉搜索树操作 1. 二叉搜索树的插入 1.1、插入过程 1.2、代码实现 2、二叉树的删除 2.1、结点删除情况 2.2、替换删除法 1、替换思路 2、代码实现&#xff1a; 3、二叉搜索树的查找 3.1、查找规则 …

LLM - 配置 GraphRAG + Ollama 服务 构建 中文知识图谱

欢迎关注我的CSDN&#xff1a;https://spike.blog.csdn.net/ 本文地址&#xff1a;https://spike.blog.csdn.net/article/details/142795151 免责声明&#xff1a;本文来源于个人知识与公开资料&#xff0c;仅用于学术交流&#xff0c;欢迎讨论&#xff0c;不支持转载。 GraphR…

基于springboot的公司财务管理系统(含源码+sql+视频导入教程+文档+PPT)

&#x1f449;文末查看项目功能视频演示获取源码sql脚本视频导入教程视频 1 、功能描述 基于springboot的公司财务管理系统拥有两种角色 管理员&#xff1a;员工管理、部门管理、工资管理、资产管理、经营管理、利润管理等 员工&#xff1a;查看工资、查看公告、登录注册 1.…

OurTV 3.3.0 |流畅电视直播,收藏无忧

OurTV 是一款流畅的电视直播应用&#xff0c;支持电视版和手机版。增加收藏功能&#xff0c;修正了网络问题和潜在内存泄漏&#xff0c;调整最小版本到22&#xff0c;观看体验更佳。 大小&#xff1a;34M 百度网盘&#xff1a;https://pan.baidu.com/s/1UqEBfQx_1ztIUNx4fWopu…

《神经网络》—— 长短期记忆网络(Long Short-Term Memory,LSTM)

文章目录 一、LSTM的简单介绍二、 LSTM的核心组件三、 LSTM的优势四、 应用场景 一、LSTM的简单介绍 传统RNN循环神经网络的局限&#xff1a; 示例&#xff1a;当出现“我的职业是程序员。。。。。。我最擅长的是电脑”。当需要预测最后的词“电脑”。当前的信息建议下一个词可…

iOS Object-C 将数组倒置(倒叙)

使用NSArray自带的对象方法:reverseObjectEnumerator 代码如下: NSArray * tempArray [[NSArray alloc]initWithObjects:"a","b","c","d", nil]; //将tempArray转换成["d","c","b","a"]; …

PasteForm最佳CRUD实践,实际案例PasteTemplate详解之3000问(四)

无论100个表还是30个表&#xff0c;在使用PasteForm模式的时候&#xff0c;管理端的页面是一样的&#xff0c;大概4个页面&#xff0c; 利用不同操作模式下的不同dto数据模型&#xff0c;通过后端修改对应的dto可以做到控制前端的UI&#xff0c;在没有特别特殊的需求下可以做到…

【光追模组】雷神之锤4光追mod,调色并修改光影,并且支持光追效果,游戏画质大提升

大家好&#xff0c;今天小编我给大家继续引入一款游戏mod&#xff0c;这次这个模组主要是针对雷神之锤4进行修改&#xff0c;如果你觉得游戏本身光影有缺陷&#xff0c;觉得游戏色彩有点失真的话&#xff0c;或者说你想让雷神之锤4这款游戏增加对光线追踪的支持的话&#xff0c…

在docker中安装并运行mysql8.0.31

第一步&#xff1a;命令行拉取mysql镜像 docker pull mysql:8.0.31查看是否拉取成功 docker images mysql:latest第二步&#xff1a;运行mysql镜像&#xff0c;启动mysql实例 docker run -p 3307:3307 -e MYSQL_ROOT_PASSWORD"123456" -d mysql:8.0.313307:3307前…

FMCW 雷达芯片关键技术学习

CLOCK GENERATION 借助外部晶体产生的 50 MHz 时钟&#xff0c;时钟生成模块为 RF 子系统生成 76 至 81 GHz 时钟信号。时钟生成模块包含内置振荡器电路、参考 PLL、FMCW PLL 和 X4 乘法器。内置振荡器电路与外部晶体一起为参考 PLL 生成 50 MHz 时钟。参考 PLL 为 FMCW PLL 和…

腾讯云SDK项目管理

音视频终端 SDK&#xff08;腾讯云视立方&#xff09;控制台提供项目管理功能&#xff0c;您可参照以下步骤为您的应用快速添加音视频通话能力和多人音视频互动能力。 若需正式开发并上线音视频应用&#xff0c;请在完成创建后&#xff0c;参照 集成指南 进行开发包下载、集成…