嵌入式开发速成:STM32 G431 HAL库中的ADC与DAC应用

一、概念

模数转换器(ADC):它将模拟信号转换为单片机能够处理的数字信号。在很多应用中,比如温度传感器、压力传感器等,信号最初都是模拟形式的。ADC 读取这些模拟信号,然后将它们转换为数字形式,以便单片机可以读取和处理。

数模转换器(DAC):它执行相反的操作,将数字信号转换为模拟信号。这在需要控制模拟设备,如音频设备、某些类型的马达控制器等方面非常有用。通过DAC,单片机可以产生精确的模拟输出信号。

二、ADC与DAC常用的函数

1、 ADC常用的函数

1.HAL_StatusTypeDef HAL_ADC_Start(ADC_HandleTypeDef *hadc)

举例:HAL_ADC_Start(&hadc1);

这一行启动了句柄名为 hadc1 的ADC。HAL_ADC_Start 函数初始化ADC并开始模拟信号到数字信号的转换过程。这里的 hadc1 是一个指向ADC实例的指针,已经在代码的其他部分被配置好了。

2.HAL_StatusTypeDef HAL_ADC_PollForConversion(ADC_HandleTypeDef *hadc, uint32_t Timeout)

举例:HAL_ADC_PollForConversion(&hadc1, 10);

这行代码是一个轮询函数,用来检查ADC转换是否完成。它检查ADC的EOC(End of Conversion)标志,以确定转换是否完成。参数 10 表示超时时间,单位为毫秒。如果在10毫秒内转换没有完成,这个函数将返回一个超时错误。

3.uint32_t HAL_ADC_GetValue(ADC_HandleTypeDef *hadc)

举例:adc = HAL_ADC_GetValue(&hadc1);

这行代码从 hadc1指向的ADC获取转换结果,并将其存储在变量 adc 中。HAL_ADC_GetValue 函数读取ADC的数据寄存器,并返回转换后的数字值。

2、 DAC常用的函数

1.HAL_StatusTypeDef HAL_DAC_Start(DAC_HandleTypeDef *hdac, uint32_t Channel)

举例:HAL_DAC_Start(&hdac1, DAC_CHANNEL_2);
这一行是句柄为 hdac1 的 DAC 的第二个通道DAC_CHANNEL_2)。HAL_DAC_Start 函数激活 DAC,并准备它开始输出模拟信号。这里的 hdac1 是指向 DAC 实例的指针,即句柄。

2.HAL_StatusTypeDef HAL_DAC_SetValue(DAC_HandleTypeDef *hdac, uint32_t Channel, uint32_t Alignment, uint32_t Data)

举例:HAL_DAC_SetValue(&hdac1,DAC_CHANNEL_2,DAC_ALIGN_12B_R,temp);
这行代码设置 DAC 通道2的输出值。HAL_DAC_SetValue 函数用于配置 DAC 输出的具体电压值,它需要几个参数: &hdac1:指向 DAC 实例的指针。DAC_CHANNEL_2:指定操作的是哪个 DAC 通道。DAC_ALIGN_12B_R:指定数据对齐式,这里是 12 位右对齐。temp:决定了 DAC 输出的模拟信号的具体电压,这个值通常是经过计算或从某个来源获取的,DAC 会将这个数字值转换为相应的模拟电压值!

三、LED官方原理图


R37——AD1——PC15 (PC15控制AD1)
R38——AD2——PC12 (PC12控制AD2)

四、配置STM32cubeMX

1、ADC配置

1.将PB12配置为ADC2_IN11,将PB15配置为ADC2_IN15

(配置PB15为ADC2_IN15意味着将微控制器的物理引脚PB15设置为模数转换器(ADC)2的输入通道15。这里的配置过程通常涉及到几个关键步骤和概念,我将逐一解释:引脚命名:在STM32微控制器中,引脚通常以“P”开头,后跟一个字母(表示引脚所在的组,如A、B、C等)和一个数字(表示组内的特定引脚编号)。因此,PB15指的是B组的第15号引脚。ADC2_IN15:这表示ADC2的输入通道15。STM32的ADC模块可以有多个输入通道,用于从不同的物理引脚接收模拟信号。在这种情况下,“ADC2”指的是微控制器上的第二个ADC模块,“IN15”则是该模块的第15个输入通道。配置过程:在STM32的开发环境中,如STM32CubeMX或直接编写代码,你需要将PB15引脚配置为ADC2的输入。这通常涉及到选择PB15作为ADC2的输入源,并设置相关参数,如采样时间、工作模式等。用途:将PB15配置为ADC2_IN15通常是为了从这个引脚读取模拟信号。这意味着可以将一个模拟传感器(如温度、压力或光传感器)连接到PB15,然后通过ADC2将其模拟信号转换为微控制器可以处理的数字信号。实际应用:在实际应用中,这样的配置允许微控制器读取外部环境的模拟数据,例如环境温度、光强度或其他物理量。这对于制作各种嵌入式系统和物联网设备至关重要。总之,配置PB15为ADC2_IN15是一种将STM32微控制器的特定引脚设置为接收模拟信号的过程,这对于实现多种传感器集成和数据采集应用至关重重要。)括号里是概念,非小白直接跳过。

注意:在这个配置中,PB15并不是直接用于读取数字信号,而是用于接收模拟信号,然后由ADC2完成模拟到数字的转换。

2.将ADC1中的通道11和ADC2中的通道15都配置为Single-ended



(Single-ended 模式是指模数转换器(ADC)或类似设备在处理信号时的一种配置方式。在这种模式下,ADC测量的是单个输入信号与地(GND,即“接地”)之间的电压差。这是ADC配置中最基本和常见的方式。下面是一些关于 Single-ended 模式的关键点:测量方法:在 Single-ended 模式中,ADC测量的是单一输入和地之间的电压差。这意味着,每个ADC通道都独立地测量其输入和系统接地之间的电压。
适用场景:这种模式通常用于读取那些提供单端输出的传感器,如温度传感器、光传感器等。简单高效:由于只需要测量单一信号相对于地的电压,这种模式设计起来相对简单,不需要复杂的电路或校准过程。信号完整性:对于许多低频和中频应用,Single-ended 输入提供了足够的信号完整性。虽然它可能不如差分输入(另一种模式,测量两个不同信号之间的电压差)那样能有效抑制噪声,但对于许多应用而言已经足够。资源使用:在资源受限的系统中,使用 Single-ended 模式可以更有效地利用ADC资源,因为它不需要多个物理通道来测量单一的信号。总之,在许多嵌入式系统和微控制器应用中,Single-ended 模式提供了一个简单、高效且足够精确的解决方案,尤其是在资源受限或设计简单性是关键考虑因素的情况下。)括号里是概念,非小白直接跳过。

2、DAC配置

1.将PA7配置为ADC2_IN4模式,将PA5配置为DAC1_OUT2模式,将PB15取消配置。

为什么需要将PB15取消配置?
刚刚在ADC的配置过程中将PB15配置为ADC2_IN15,这样ADC2有两个采集通道就会有冲突! ADC每次只能采集一个通道,如果需要同一个ADC采集多个通道,就需要变换通道。即:对另外的通道进行初始化。本例中不需要这么复杂,只需要一个ADC采集一个通道,所以将PB15取消配置。

(配置PB5为DAC1_OUT2在STM32微控制器的环境中意味着将PB5引脚设置为数字模拟转换器(DAC)1的第二个输出通道(OUT2)。这里的配置涉及到几个重要的步骤和概念:引脚命名:这个在DAC的时候已经讲过。DAC1_OUT2:这表示DAC1(数字模拟转换器1)的第二个输出通道。STM32的DAC模块通常有多个输出通道,可用于将数字信号转换为模拟信号。在这种情况下,“DAC1”指的是微控制器上的第一个DAC模块,“OUT2”则是该模块的第二个输出通道。配置过程:在STM32的开发环境中,如STM32CubeMX或直接编写代码,你需要将PB5引脚配置为DAC1的输出。这通常涉及到选择PB5作为DAC1的输出源,并设置相关参数,如输出电压范围、输出模式等。用途:将PB5配置为DAC1_OUT2通常是为了从这个引脚输出模拟信号。这意味着可以通过编程控制DAC1,将数字信号转换为模拟信号,并通过PB5引脚输出。这对于控制模拟设备,如音频设备、某些类型的马达或灯光系统等是非常有用的。实际应用:在实际应用中,这样的配置允许微控制器生成精确的模拟输出,可以用于各种控制和信号处理应用,如生成音频信号、控制模拟传感器等。总之,配置PB5为DAC1_OUT2是一种将STM32微控制器的特定引脚设置为输出模拟信号的过程,这对于实现数字信号到模拟信号的转换和对外部模拟设备的控制至关重要。)括号里是概念,非小白直接跳过。

2.将DAC1配置为"Connected to external pin only" 模式

(在使用 STM32CubeMX 配置 STM32 微控制器的 DAC(数字模拟转换器)时,选择 “Connected to external pin only” 的选项通常是基于以下原因:直接输出:选择 “Connected to external pin only” 表明 DAC 的输出将直接连接到微控制器的一个外部引脚。这允许DAC产生的模拟信号能够直接输出到微控制器外部,以驱动外部设备或系统,如音频设备、电机控制器等。硬件简化:这种配置简化了硬件设计,因为它不需要额外的内部连接或复杂的电路来从DAC获得模拟信号。它提供了一种简单直接的方式,通过指定的引脚输出模拟信号。易于配置和集成:在STM32CubeMX中,将DAC配置为“仅连接到外部引脚”可以使得配置过程更为直观和简单。用户可以清楚地看到哪个引脚被用作DAC的输出,这有助于在设计电路板时规划引脚布局。适用于大多数应用:大多数应用中,DAC的输出是需要传递到微控制器外部的。例如,生成模拟信号来控制音频放大器或调节LED的亮度。选择这个选项确保了DAC的输出能头被适当地利用。减少内部资源使用:在某些情况下,DAC还可以配置为内部连接,比如用于内部参考或调试。但在大多数实际应用中,将DAC输出限定在外部引脚可以减少微控制器内部资源的使用,使设计更高效。
总之,选择 “Connected to external pin only” 是为了确保DAC输出的简单性、直接性和高效性,满足大多数通用应用的需求。
)括号里是概念,非小白直接跳过。

五、ADC代码

实验现象:使用电压采集1中的旋转按钮和电压采集2中的旋转按钮分别通过旋转来控制输出对应的数字电压,并将数字电压转换为模拟电压显示在屏幕上。
即:模拟电压通过ADC变成数字电压,数字电压通过公式计算变成模拟电压显示到屏幕上。

1.在/* USER CODE BEGIN 0 /与/ USER CODE END 0 */之间增加如下代码

float getADC_R37(void)
{
	int adc = 0;   //为了节省内存,定义一个16位的整形数据  因为adc是个12位的adc 所以需要16位整形存放数据,也可以用int,会浪费一点内存
	
	HAL_ADC_Start(&hadc2); //开启adc2
	HAL_ADC_PollForConversion(&hadc2,10);//查询函数,查询EOC标志位。每次采样,CUP在这里都要   10ms为超时时间
                                       //等待采样完成才能进行下一步,这段时间CUP没有干其他 
                                       //事,所以降低了CUP使用率
	adc = HAL_ADC_GetValue(&hadc2);  //获取adc3的值赋值给定义的整形adc
              	//这边有个细节:我们cubemx采样时间设为3个周期,转换速度快但是转换精度低
              //此时我们需要在获取adc值前加上判断是否转换结束的库函数,或者加个延时。否则可能adc还没计算完就获取数据导致数据不准确
	
	return adc*3.3/4096;     //返回值为:adc

}

float getADC_R38(void)      //定义一个子函数,无入口参数,返回值为整形
{
	int adc = 0;   //为了节省内存,定义一个16位的整形数据  因为adc是个12位的adc 所以需要16位整形存放数据,也可以用int,会浪费一点内存
	
	HAL_ADC_Start(&hadc1); //开启adc1
	HAL_ADC_PollForConversion(&hadc1,10);//查询函数,查询EOC标志位。每次采样,CUP在这里都要   10ms为超时时间
                                       //等待采样完成才能进行下一步,这段时间CUP没有干其他 
                                       //事,所以降低了CUP使用率
	adc = HAL_ADC_GetValue(&hadc1);  //获取adc1的值赋值给定义的整形adc
              	//这边有个细节:我们cubemx采样时间设为3个周期,转换速度快但是转换精度低
              //此时我们需要在获取adc值前加上判断是否转换结束的库函数,或者加个延时。否则可能adc还没计算完就获取数据导致数据不准确
	return adc*3.3/4096;     //返回值为:adc
}

2.校准
adc初始化完成后要进行校准,不然达不到3.3v!!!!
以下是校准函数,将其放到main.c中的int main(void)函数中

HAL_ADCEx_Calibration_Start(&hadc1, ADC_SINGLE_ENDED);
HAL_ADCEx_Calibration_Start(&hadc2, ADC_SINGLE_ENDED);

3.在while(1)中添加如下代码

	  double value=getADC_R38();  
 	  sprintf(str,"value=%.2f",value);
	  LCD_DisplayStringLine(Line0, (uint8_t*)str);

如上 ADC的代码实现的功能就相当于电压表,测量某个引脚的电压。

六、DAC代码以及ADC和DAC综合

1.在/* USER CODE BEGIN 0 /与/ USER CODE END 0 */之间增加如下代码

// DAC1电压设置函数
void DAC1_Set_Vol(float vol)
{
	int temp;  //  定义电压转换后的数值
	//将电压转换为数值
	temp = (4096 * vol/3.3f);  //  12位的DAC最大数值为4096,vol / 3.3 就是设置的电压所占全部电压(3.3V)的比例
	//将数字信号数字值转化为对应的模拟信号
	HAL_DAC_SetValue(&hdac1, DAC_CHANNEL_2,DAC_ALIGN_12B_R,temp); // 最终的电压设置函数
}


2.在main.c中的int main(void)函数中添加如下代码

	//初始化:
  HAL_DAC_Start(&hdac1,DAC_CHANNEL_2); // 使能DAC1的通道2
  DAC1_Set_Vol(2.4);   // 调用DAC1设置电压函数

3.在while(1)中添加如下代码

	double value=getADC_R37();  
 	sprintf(str,"value=%.2f",value);
	LCD_DisplayStringLine(Line0, (uint8_t*)str);

将杜邦线一端接PA5,一端接PA7。PA5(DAC)端口一直在输出电压,电压为2.4v。PA7(ADC)端口是用来测电压。

如果用杜邦线将PA7跟3V3连接,那么value=3.30。用杜邦线将PA7跟GND连接,value=0.00。

物联沃分享整理
物联沃-IOTWORD物联网 » 嵌入式开发速成:STM32 G431 HAL库中的ADC与DAC应用

发表评论