STM32 DAC实战教程:从入门到精通的全方位指导

目录

前言:

1、实验要求

任务1:配置DAC,确保输出值精确达至1.49V,同时在显示屏上清晰展示或通过串口打印输出,实现精准控制与可视化监测的完美结合。

任务 2:在任务一的基础上面,PA10 复用为 DAC0_OUT,定时 1ms,每毫秒将 DAC0 数据转换输出值自加 1,数据转换值大于 1023 后,将数据转换值清 0,如此循环。使用示波器观察 PA10 输出波形。

2、理论知识储备

2.1 了解DAC的工作原理以及作用

2.2清楚如何设置 DAC 参考电压,输出电压

2.3清楚 DAC 特性参数

2.4DAC的计算公式(十位)

同理,以下是8位和12位的计算公式

2.5什么是触发源

3、软件显示

3.1任务1:DAC的配置输出

3.1.1GPIO 配置 +  DAC初始化

3.1.2获取DAC的数值

3.1.3主函数

3.1.4实验效果:

3.2 任务二 DAC + 定时器

3.2.1DAC的配置输出

GPIO配置+DAC配置

3.2.2定时器配置:

3.2.3中断配置:

3.2.4定时器中断函数:

3.2.5 主函数

3.2.6实验效果:

4、总结:


前言:

本文将引领您踏上一段探寻DAC(数模转换器)奥秘的旅程,深入剖析其工作原理与配置技巧。将细致解读DAC的运作机制,并通过两个鲜活的案例——DAC的配置输出、DAC定时自加输出——来助您轻松掌握其应用精髓。旨在让读者能够更好地理解并掌握DAC的工作原理和应用技巧,为后续的STM32开发奠定坚实基础。

1、实验要求

任务1:配置DAC,确保输出值精确达至1.49V,同时在显示屏上清晰展示或通过串口打印输出,实现精准控制与可视化监测的完美结合。

任务 2:在任务一的基础上面,PA10 复用为 DAC0_OUT,定时 10ms,每毫秒将 DAC0 数据转换输出值自加 1,数据转换值
大于 1023 后,将数据转换值清 0,如此循环。使用示波器观察 PA10 输出波形。

2、理论知识储备

2.1 了解DAC的工作原理以及作用

这个DAC的框图描述了数字到模拟信号的转换过程。

首先,传感器收集环境数据,并将其转换成电信号。这些电信号经过模数转换器(ADC)转换成数字量,然后被单片机处理。接着,单片机根据需要的输出信号,通过数字到模拟转换器(DAC)将数字量转换为模拟信号。最后,这个模拟信号被用于控制模拟控制系统中的各种设备。

2.2清楚如何设置 DAC 参考电压,输出电压

DAC_InitStruct.DAC_Vref_Select = DAC_Vref_Avdd;						   //参考源配置
DAC_SetDac_10B_Data(DAC0, DAC_Align_10B_R, 0x1D1);

2.3清楚 DAC 特性参数

2.4DAC的计算公式(十位)

同理,以下是8位和12位的计算公式

2.5什么是触发源

3、软件显示

3.1任务1:DAC的配置输出

3.1.1GPIO 配置 +  DAC初始化
void DAC0_Config()
{
    GPIO_InitTypeDef GPIO_InitStruct;
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_4;                                    // 配置PA4引脚
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AN;                                 // 模拟输入模式
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_Level_2;                           // GPIO速度为10MHz
    GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;                                // 推挽输出
    GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;                              // 无上下拉
    GPIO_Init(GPIOA, &GPIO_InitStruct);                                       // 初始化GPIOA
    GPIO_PinAFConfig(GPIOA, GPIO_PinSource4, GPIO_AF_2);                      // 将PA4引脚的复用功能设置为DAC0_OUT

    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_5;                                    // 配置PA5引脚
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AN;                                 // 模拟输入模式
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_Level_2;                           // GPIO速度为10MHz
    GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;                                // 推挽输出
    GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;                              // 无上下拉
    GPIO_Init(GPIOA, &GPIO_InitStruct);                                       // 初始化GPIOA
    GPIO_PinAFConfig(GPIOA, GPIO_PinSource5, GPIO_AF_2);                      // 将PA5引脚的复用功能设置为DAC0_OUT

    DAC_InitTypeDef DAC_InitStruct;
    DAC_InitStruct.DAC_EXTrigger_Edge = DAC_EXTrigger_edge_dis;               // 外部触发边缘禁用
    DAC_InitStruct.DAC_Trigger_Source = DAC_Trigger_Software;                 // 软件触发源
    DAC_InitStruct.DAC_WaveGeneration = DAC_WaveGeneration_None;              // 波形生成模式为无
    DAC_InitStruct.DAC_LFSRUnmask_TriangleAmplitude = DAC_LFSRUnmask_Bit0;    // DAC_LFSRUnmask_Bit0
    DAC_InitStruct.DAC_OutputBuffer = DAC0_OutputBuffer_Enable;               // 输出缓冲器使能
    DAC_InitStruct.DAC_Vref_Select = DAC_Vref_Avdd;                           // 选择参考电压为AVDD
    DAC_InitStruct.DAC_DmaMode = DISABLE;                                     // DMA模式禁用
    DAC_InitStruct.DAC_DMAUDR_IE = DISABLE;                                   // DMA中断禁用
    DAC_Init(DAC0, &DAC_InitStruct);                                          // 初始化DAC0

    DAC_Cmd(DAC0, ENABLE);                                                    // 启用DAC0
    DAC_SetDac_10B_Data(DAC0, DAC_Align_10B_R, 0x200);                        // 设置DAC0的输出数据为0x200,10位右对齐
    /* 软件触发使能;标示触发 DAC 的一次触发传输(选择软件触发条件) */
    DAC_SoftwareTriggerCmd(DAC0, ENABLE);                                     // 启用软件触发
}

以上代码是针对STM32的DAC0进行配置的函数。首先,配置了GPIO引脚(PA4和PA5)为模拟输入模式,并将其复用功能设置为DAC0输出。然后,对DAC进行初始化设置,包括设置触发源、波形生成模式、输出缓冲器使能等。接着启用DAC0,并设置输出数据为0x200,即512(10位右对齐)。最后,启用软件触发功能,以触发DAC一次传输。

3.1.2获取DAC的数值
uint16_t DAC_GetValue(void)
{
    // 通过 DAC_GetDataOutputValue_10bit 函数获取 DAC 的输出值
    return DAC_GetDataOutputValue_10bit(DAC0);
}
3.1.3主函数
uint16_t ADValue;			//定义AD值变量
float Voltage;				//定义电压变量
int main()
{
	SystemInit();
	SetSysClock(); //主频配置/set CPU frequency
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_ANACTRL, ENABLE);
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);

	DAC0_Config();
	OLED_Init();
	/*显示静态字符串*/
    OLED_ShowString(1, 1, "DAValue:");
    OLED_ShowString(2, 1, "Voltage:0.00V");
	
    OLED_ShowNum(2, 11, (uint16_t)(Voltage * 100) % 100, 2);	//显示电压值的小数部分
	
	while (1)
	{
		ADValue = DAC_GetValue();
		Voltage = (float)ADValue / 1024 * 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);	//显示电压值的小数部分
	}
}

这段代码首先进行了系统初始化和时钟配置,然后启用了模拟控制器和GPIOA的时钟。接着调用了DAC0_Config()函数对DAC进行配置初始化,并初始化了OLED显示屏。在主循环中,通过DAC_GetValue()函数获取DAC输出的值,然后将其转换为电压值,并在OLED上显示AD值和电压值。整个代码实现了DAC输出值到电压值的转换,并在OLED上进行实时显示。

3.1.4实验效果:

根据计算公式 DAC_OUT = VREF * DOR/1024,以及给定的 DAValue = 0465,我们得到以下结果:1.49 = 3.3 * 0465 / 1024。因此,可以得出结论:任务一中配置的 DAC 输出实验效果是正确的。

3.2 任务二 DAC + 定时器

每10毫秒将 DAC0 数据转换输出值自加 1,数据转换值大于 1023 后,将数据转换值清 0

3.2.1DAC的配置输出
GPIO配置+DAC配置


配置两个GPIO引脚,分别用于连接DAC0输出。配置为模拟输入模式,速度为10MHz,推挽输出,无上拉下拉
配置DAC0,设置外部触发边缘,软件触发源,波形生成模式为三角波,波形幅值为1,输出缓冲器使能,参考电压选择AVDD。同时,禁用DMA模式和DMA中断。

void DAC0_Config()
{
	GPIO_InitTypeDef GPIO_InitStruct;
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_4;
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AN;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_Level_2;
	GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
	GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;
	GPIO_Init(GPIOA, &GPIO_InitStruct);
	GPIO_PinAFConfig(GPIOA, GPIO_PinSource4, GPIO_AF_1); // DAC0_OUT

	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10;
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AN;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_Level_2;
	GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
	GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;
	GPIO_Init(GPIOA, &GPIO_InitStruct);
	GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_2); // DAC0_OUT

	DAC_InitTypeDef DAC_InitStruct;
	DAC_InitStruct.DAC_EXTrigger_Edge = DAC_EXTrigger_edge_dis;			   //触发边沿
	DAC_InitStruct.DAC_Trigger_Source = DAC_Trigger_Software;			   //触发方式
	DAC_InitStruct.DAC_WaveGeneration = DAC_WaveGeneration_Triangle;		   //产生波形配置:无;三角波;噪声波
	DAC_InitStruct.DAC_LFSRUnmask_TriangleAmplitude = DAC_TriangleAmplitude_1; //产生波形幅值
	DAC_InitStruct.DAC_OutputBuffer = DAC0_OutputBuffer_Enable;			   // DAC驱动管使能控制
	DAC_InitStruct.DAC_Vref_Select = DAC_Vref_Avdd;						   //参考源配置
	DAC_InitStruct.DAC_DmaMode = DISABLE;								   // DAC DMA 使能位
	DAC_InitStruct.DAC_DMAUDR_IE = DISABLE;								   // DAC DMA欠载中断使能配置
	DAC_Init(DAC0, &DAC_InitStruct);

	DAC_Cmd(DAC0, ENABLE);
	DAC_SetDac_10B_Data(DAC0, DAC_Align_10B_R, 0x1D1);
	DAC_SoftwareTriggerCmd(DAC0, ENABLE); //软件触发使能;标示触发 DAC 的一次触发传输(选择软件触发条件)
}

3.2.2定时器配置:

配置TIM3作为定时器,我们设定预分频系数为1599,并选择向上计数模式,确保其周期为100个计数周期。接下来,我们启用该定时器并激活其更新中断功能。

考虑到我们使用的主频为16Mhz,依据溢出时间的计算公式,

定时器的溢出时间计算为1600乘以100再除以16,得出结果为10,000微秒,即10毫秒。

这样的配置使得TIM3能够精确地以10毫秒的间隔触发更新中断,为我们的应用提供了稳定可靠的时间基准。

}
static void tim3_cfg_init()
{
  TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;

  TIM_TimeBaseStructure.TIM_Prescaler = 1599; //预分频系数设置位1599,即TIM_CLK=16/(1599+1)=10K;
  TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
  TIM_TimeBaseStructure.TIM_Period = 100;
  TIM_TimeBaseStructure.TIM_ClockDivision = 0;
  TIM_TimeBaseStructure.TIM_RepetitionCounter = 0;
  TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);

  TIM_ARRPreloadConfig(TIM3, ENABLE);
  TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE);
  TIM_Cmd(TIM3, ENABLE);
}
3.2.3中断配置:


配置TIM3的更新中断,并设置优先级。

void NVIC_Configuration()
{
  NVIC_InitTypeDef NVIC_InitStructure;

  NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;
  NVIC_InitStructure.NVIC_IRQChannelPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);
}
3.2.4定时器中断函数:

在TIM3定时器中断处理函数中,首先检查是否发生更新事件,若有则清除中断标志位。然后每毫秒增加DAC输出值,使能软件触发DAC0。ADValue自增,若超过1023则清零。这段代码确保了定时器每毫秒产生一次中断,更新DAC输出值。(可以定义一个num值,然后检测定时器的配置是否正确)

void TIM3_Handler(void)
{
  if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET) {
        TIM_ClearITPendingBit(TIM3, TIM_IT_Update);

        // 每毫秒增加 DAC 输出值
        DAC_SetDac_10B_Data(DAC0, DAC_Align_10B_R, ADValue);
        DAC_SoftwareTriggerCmd(DAC0, ENABLE);
	  
        ADValue++;  // DAC 输出值自加 1
        if (ADValue > 1023) {
            ADValue = 0;  // 数据转换值大于 1023 时,清零
        }
    }
  
	//测试代码 判断是否可以进入定时器中断
//	if (TIM_GetITStatus(TIM3, TIM_IT_Update) == SET)		//判断是否是TIM3的更新事件触发的中断
//	{
//		Num ++;												//Num变量自增,用于测试定时中断
//		TIM_ClearITPendingBit(TIM3, TIM_IT_Update);			//清除TIM2更新事件的中断标志位
//															//中断标志位必须清除
//															//否则中断将连续不断地触发,导致主程序卡死
//	}
}

3.2.5 主函数
uint16_t ADValue;			//定义AD值变量
float Voltage;				//定义电压变量
uint16_t Num;			//定义在定时器中断里自增的变量

int main()
{
	SystemInit();
	SetSysClock(); //主频配置
	RCC_APB0PeriphClockCmd(RCC_APB0Periph_TIM3, ENABLE);
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_EXTI, ENABLE);
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_ANACTRL, ENABLE);
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);
	tim3_cfg_init();
	DAC0_Config();
	OLED_Init();
	NVIC_Configuration();
//	OLED_ShowString(1,1,"hello world");
//	DAC_Trigger_GPIO_INIT();
//	EXTI_Config();
//	DAC1_Config();
	
	OLED_Init();
	/*显示静态字符串*/
    OLED_ShowString(1, 1, "DAValue:");
    OLED_ShowString(2, 1, "Voltage:0.00V");
	
	
	/*显示静态字符串*/
	OLED_ShowString(3, 1, "Num:");			//1行1列显示字符串Num:
	
	
	while (1)
	{
		
//		ADValue = DAC_GetValue();
		Voltage = (float)ADValue / 1024 * 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);	//显示电压值的小数部分
		
		
		OLED_ShowNum(3, 5, Num, 5);			//不断刷新显示Num变量
	}
}
3.2.6实验效果:

DAC+TIM

调试小技巧:在编程时,建议首先定义一个变量num用于计数。随后,在定时器中断服务程序中执行判断逻辑,并实时观察num数值的变动。通过这一方式,我们能够直观判断定时器是否配置正确并正常工作,从而更有效地进行代码调试。

使用示波器观察波形,只见波形自底部缓缓升起,再逐渐回落,如此往复循环。这一景象清晰地展示了任务二实验效果的完美实现。

4、总结:

在任务1中,我们成功配置了DAC以输出精确的1.49V电压,这为我们后续的开发工作提供了稳定的模拟信号源。

而在任务2中,我们进一步利用DAC的定时自加输出功能,通过示波器观察到了波形从下到上、再从上到下的循环变化,充分展示了DAC的灵活性和实用性。

提供的代码可能不适用于所有情况,但其逻辑结构和编程思路对于DAC的初始化、配置和使用同样具有指导意义。理解这些步骤后,您可以根据DAC的特性和您的具体需求进行相应的修改和优化。

希望本文能对读者有所帮助,欢迎大家探索和实践!

以下是优质的视频推荐,助您快速掌握DAC的理论精髓。

传送门

  • 16
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值