基于战舰V3开发板的DAC输出实验&简易正弦波发生器实验

    科技2025-09-30  51

    基于战舰V3开发板的DAC输出实验

    DAC原理解析

    DAC外设工作逻辑框图

    DACx通道与GPIO引脚的对应关系

    DAC_OUT1

    PA4

    DAC_OUT2

    PA5

    DAC转换原理

    不能直接对寄存器DAC_DORx写入数据,任何输出到DAC通道x的数据都必须写入DAC_DHRx寄存器(数据实际写入DAC_DHR8Rx、DAC_DHR12Lx、DAC_DHR12Rx、DAC_DHR8RD、DAC_DHR12LD、或者DAC_DHR12RD寄存器)。

    如果没有选中硬件触发(寄存器DAC_CR1的TENx位置’0’),存入寄存器DAC_DHRx的数据会在一个APB1时钟周期后自动传至寄存器DAC_DORx。如果选中硬件触发(寄存器DAC_CR1的TENx位置’1’),数据传输在触发发生以后3个APB1时钟周期后完成。

    一旦数据从DAC_DHRx寄存器装入DAC_DORx寄存器,在经过时间tSETTLING之后,输出即有效,这段时间的长短依电源电压和模拟输出负载的不同会有所变化。

    可以触发DAC的外部事件

    如果TENx位被置1,DAC转换可以由某外部事件触发(定时器计数器、外部中断线)。配置控制位 TSELx[2:0]可以选择8个触发事件之一触发DAC转换。

    每次DAC接口侦测到来自选中的定时器TRGO输出,或者外部中断线9的上升沿,最近存放在寄存器DAC_DHRx中的数据会被传送到寄存器DAC_DORx中。在3个APB1时钟周期后,寄存器DAC_DORx更新为新值。(为什么是3个APB1周期,详见“DAC转换原理”部分)

    如果选择软件触发,一旦SWTRIG位置’1’,转换即开始。在数据从DAC_DHRx寄存器传送到DAC_DORx寄存器后,SWTRIG位由硬件自动清’0’。

    注意:

    ① 不能在DAC通道使能时改变TSELx[2:0]位,即DAC工作过程中不可以改变触发源;

    ② 如果选择软件触发,数据从寄存器DAC_DHRx传送到寄存器DAC_DORx只需要一个APB1时钟周期。

    DAC中模拟量与数字量的关系

    疑难问题

    将GPIO配置为模拟输入模式后,GPIO相应引脚属性的变化

    当I/O端口被配置为模拟输入配置时:

    ● 输出缓冲器被禁止;

    ● 禁止施密特触发输入,实现了每个模拟I/O引脚上的零消耗。施密特触发输出值被强置

    为’0’;

    ● 弱上拉和下拉电阻被禁止;

    ● 读取输入数据寄存器时数值为’0’。

    下图示出了I/O端口位的高阻抗模拟输入配置:

    为什么将GPIO配置为模拟输入模式?

    我们看到模拟输入模式中信号行走的路线上,没有用于“数字信号处理的施密特触发器”和“输入数据寄存器”,并且此时的上下拉电阻已经被关闭。

    这个施密特触发器被关闭了,GPIO的数字输入功能被取消。如果此时读取输入数据寄存器的值,结果恒为0。由于关闭了施密特触发器,上面提到的因它而起的电平跳变噪声和相应的额外功耗就没有了。

    换句话说,当GPIO状态由浮空或上下拉输入状态改为模拟输入状态时,既消除了因为施密特触发器带来的噪声,同时又因它的关闭而降低了芯片动态功耗。

    另外,配置在模拟输入状态的GPIO引脚属于高阻态,这点也有利于保持模拟信号的真实性(说白了,就是信号传输通道无任何信号处理装置,可以将原汁原味的模拟信号在CPU与外设之间进行传输)。

    DAC应该是输出模拟信号,但为什么要配置为“模拟输入”?

    因为一但使能 DACx 通道之后,相应的 GPIO 引脚(PA4 或者 PA5)会自动与 DAC 的模拟输出相连。其实,模拟输入通道与模拟输出通道是一起的,当我们将我们所需的引脚配置为“GPIO的模拟输入模式”后,系统会根据我们所使能的DAC/ADC外设自动进行通道类型的选择。

    DAC的毛刺现象是什么?

    http://www.elecfans.com/analog/20150723377582.html

    请问stm32中的ADC的触发模式分别是在什么情况下用的?

    如果想周期性的采集信号,那就用定时器触发;如果想不定时任意时刻的采集信号,那就用软件触发;如果想让外部信号在特定情况下被采集,那就用外部触发。

    DAC库函数配置流程

    ① 使能相应外设的时钟源

    RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC,ENABLE);   RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);  

     

    ② 设置GPIO口模式(根据外设的特性进行设置)

    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;   GPIO_Init(GPIOA,&GPIO_InitStructure);  

     

    ③ DAC外设属性的初始化

    DAC_InitStructure.DAC_LFSRUnmask_TriangleAmplitude = DAC_LFSRUnmask_Bit0; // 幅值设置   DAC_InitStructure.DAC_OutputBuffer = DAC_OutputBuffer_Disable; // 输出缓冲器是否使能   DAC_InitStructure.DAC_Trigger = DAC_Trigger_None; // 是否有事件触发DAC工作   DAC_InitStructure.DAC_WaveGeneration = DAC_WaveGeneration_None; // 是否生成噪声波/三角波   DAC_Init(DAC_Channel_1,&DAC_InitStructure);  

     

    ④ DAC外设使能并且基于DAC_Channelx一个确定的电压值

    DAC_Cmd(DAC_Align_12b_R,ENABLE);      DAC_SetChannel1Data(DAC_Align_12b_R,0);  

     

    单通道DAC一般操作的库函数配置

    Dac.c

    #include "dac.h"   #include "sys.h"   #include "stm32f10x.h"      void DAC_InitConfig()   {       GPIO_InitTypeDef GPIO_InitStructure;       DAC_InitTypeDef DAC_InitStructure;              RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);       RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC,ENABLE);              GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;       GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;       GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;       GPIO_Init(GPIOA,&GPIO_InitStructure);              DAC_InitStructure.DAC_LFSRUnmask_TriangleAmplitude = DAC_LFSRUnmask_Bit0;       DAC_InitStructure.DAC_OutputBuffer = DAC_OutputBuffer_Disable;       DAC_InitStructure.DAC_Trigger = DAC_Trigger_None;       DAC_InitStructure.DAC_WaveGeneration = DAC_WaveGeneration_None;       DAC_Init(DAC_Channel_1,&DAC_InitStructure);              DAC_Cmd(DAC_Channel_1,ENABLE);              DAC_SetChannel1Data(DAC_Align_12b_R,0);   }      void DAC_SetDigitalValue(float VoltageValue)   {       u16 DigitalValue = 0;       DigitalValue = VoltageValue/3.3*(4096-1);       DAC_SetChannel1Data(DAC_Align_12b_R,DigitalValue);   }  

     

    Dac.h

    #ifndef _DAC_H   #define _DAC_H      void DAC_InitConfig();   void DAC_SetDigitalValue(float VoltageValue);      #endif  

     

    Adc.c

    #include "adc.h"   #include "sys.h"   #include "stm32f10x.h"      void ADC_InitConfig()   {       GPIO_InitTypeDef GPIO_InitStructure;       ADC_InitTypeDef ADC_InitStructure;              RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_ADC1,ENABLE);              RCC_ADCCLKConfig(RCC_PCLK2_Div6); // ADC工作频率必须低于14MHz,此时我们将72MHz/6=12MHz              GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;       GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;       GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;       GPIO_Init(GPIOA,&GPIO_InitStructure);              ADC_DeInit(ADC1); // 将ADC1外设复位为缺省值              ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;       ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;       ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;       ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;       ADC_InitStructure.ADC_NbrOfChannel = 1;       ADC_InitStructure.ADC_ScanConvMode = DISABLE;       ADC_Init(ADC1,&ADC_InitStructure);              ADC_Cmd(ADC1,ENABLE);              ADC_ResetCalibration(ADC1);       while(ADC_GetResetCalibrationStatus(ADC1) == SET); // 复位ADC1校准功能标志清除轮询结束              ADC_StartCalibration(ADC1);       while(ADC_GetCalibrationStatus(ADC1) == SET); // 初始化ADC1校准功能标志清除轮询结束   }      u16 ADC_GetDigitalValue()   {       ADC_RegularChannelConfig(ADC1,ADC_Channel_1,1,ADC_SampleTime_28Cycles5); // 单个规则通道配置              ADC_SoftwareStartConvCmd(ADC1,ENABLE);       while(ADC_GetFlagStatus(ADC1,ADC_FLAG_EOC) == RESET); // ADC1转换标志置位轮询结束              return ADC_GetConversionValue(ADC1);   }  

     

    Adc.h

    #ifndef _ADC_H   #define _ADC_H      #include "sys.h"      void ADC_InitConfig();   u16 ADC_GetDigitalValue();      #endif  

     

    设备配置

    我们将PA1(ADC1_Channel_1)与PA4(DAC_Channel_1)用跳线帽连接起来,DAC与ADC的功能如下:

    DAC_Channel_1

    将数字量转换为模拟量输出到PA4端口上

    ADC1_Channel_1

    读取PA1端口上的模拟量并且转换为数字量

    定时器中断+DAC“实现简易正弦波信号发生器

    设备配置介绍

    定时器

    在间隔相等的时间内更新DAC转换所需的数字电压值(电压值范围为[0,4095])

    DAC外设

    在主函数中用while(1)不断轮询实时将数字电压值转换为模拟量进行输出

    LED外设

    为了凸显DAC输出信号在不断变化,我们将“实际数字量/数字量的峰值”作为PWM的占空比,将PWM输出至PB5端口上对小灯进行实施亮度调节

     

    Main.c

    #include "dac.h"   #include "adc.h"   #include "timer.h"   #include "led.h"   #include "usart.h"   #include "delay.h"   #include "stm32f10x.h"      int main()   {              u16 temp = 0;              NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);       LED_InitConfig();       TIMER_InitConfig();       DAC_InitConfig();       ADC_InitConfig();       delay_init();       uart_init(115200);              while(1)       {           DAC_SetDigitalValue();           delay_ms(50);           temp = ADC_GetDigitalValue();           printf("数字量为%d\n",temp);       }   }  

     

    在主函数中,我们主要是通过轮询的方式,将更新的数字量通过DAC外设准换为模拟量输出至指定端口上。

    Timer.c

    #include "stm32f10x.h"   #include "sys.h"   #include "timer.h"   #include "delay.h"   #include "math.h"   #include "led.h"      u16 DigitalValue = 0;   u16 Peak = 1000;   u16 Cycle = 1000;   extern float LightLevel;      void TIMER_InitConfig()   {       TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;       NVIC_InitTypeDef NVIC_InitStructure;              RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);          TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;       TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;       TIM_TimeBaseInitStructure.TIM_Period = 1000;       TIM_TimeBaseInitStructure.TIM_Prescaler = 0; // 更新时间为13.89us       TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);              TIM_Cmd(TIM2,ENABLE);              TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);              NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;       NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;       NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;       NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;       NVIC_Init(&NVIC_InitStructure);              LED_InitConfig();       delay_init();   }      void TIM2_IRQHandler()   {          static u16 counter = 0;              counter++;       counter %= Cycle;              if(TIM_GetITStatus(TIM2,TIM_IT_Update) == SET)       {           DigitalValue = ceil(((double)Peak)*sin(((double)counter)/((double)Cycle)*3.14));           LightLevel = ((float)DigitalValue)/((float)Peak);       }              TIM_ClearITPendingBit(TIM2,TIM_IT_Update);   }  

     

    在定时器配置中,我们主要是在定时器溢出中断时,对此时输出的数字量进行更新。

    Timer.h

    #ifndef _TIMER_H   #define _TIMER_H      #include "sys.h"      void TIMER_InitConfig();   u16 CycleCalculate();      #endif  

     

    Dac.h

    #ifndef _DAC_H   #define _DAC_H      #include "sys.h"      void DAC_InitConfig();   void DAC_SetDigitalValue();      #endif  

     

    Dac.c

    #include "stm32f10x.h"   #include "sys.h"   #include "dac.h"   #include "timer.h"      extern u16 DigitalValue;      void DAC_InitConfig()   {       GPIO_InitTypeDef GPIO_InitStructure;       DAC_InitTypeDef DAC_InitStructure;              RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC,ENABLE);       RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);              GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;       GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;       GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;       GPIO_Init(GPIOA,&GPIO_InitStructure);              DAC_InitStructure.DAC_LFSRUnmask_TriangleAmplitude = DAC_LFSRUnmask_Bit0;       DAC_InitStructure.DAC_OutputBuffer = DAC_OutputBuffer_Disable;       DAC_InitStructure.DAC_Trigger = DAC_Trigger_None;       DAC_InitStructure.DAC_WaveGeneration = DAC_WaveGeneration_None;       DAC_Init(DAC_Channel_1,&DAC_InitStructure);              DAC_Cmd(DAC_Align_12b_R,ENABLE);              DAC_SetChannel1Data(DAC_Align_12b_R,0);   }      void DAC_SetDigitalValue()   {       DigitalValue = DigitalValue>=4095?4095:DigitalValue;       DAC_SetChannel1Data(DAC_Align_12b_R,DigitalValue);   }  

     

    Adc.h

    ​ #ifndef _ADC_H   #define _ADC_H      #include "sys.h"      void ADC_InitConfig();   u16 ADC_GetDigitalValue();      #endif  ​

     

    Adc.c

    #include "adc.h"   #include "sys.h"   #include "stm32f10x.h"      void ADC_InitConfig()   {       GPIO_InitTypeDef GPIO_InitStructure;       ADC_InitTypeDef ADC_InitStructure;              RCC_ADCCLKConfig(RCC_PCLK2_Div6);              RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_ADC1,ENABLE);            ADC_DeInit(ADC1);              GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;       GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;       GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;       GPIO_Init(GPIOA,&GPIO_InitStructure);              ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;       ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;       ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;       ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;       ADC_InitStructure.ADC_NbrOfChannel = 1;       ADC_InitStructure.ADC_ScanConvMode = DISABLE;       ADC_Init(ADC1,&ADC_InitStructure);              ADC_Cmd(ADC1,ENABLE);              ADC_ResetCalibration(ADC1);       while(ADC_GetResetCalibrationStatus(ADC1) == SET);              ADC_StartCalibration(ADC1);       while(ADC_GetCalibrationStatus(ADC1) == SET);   }      u16 ADC_GetDigitalValue()   {       ADC_RegularChannelConfig(ADC1,ADC_Channel_1,1,ADC_SampleTime_28Cycles5);       ADC_SoftwareStartConvCmd(ADC1,ENABLE);       while(ADC_GetFlagStatus(ADC1,ADC_FLAG_EOC) == RESET);              return ADC_GetConversionValue(ADC1);   }  

     

     

    Processed: 0.015, SQL: 8