DAC理解(DAC定时器触发DMA转运学习总结)

前提:本人使用STM32F103标准库学习,其他芯片可以参考其原理

基础:理解DAC配置

一、DAC配置

        (1).查找DAC引脚,初始化GPIO

                1.打开STM32F103数据手册,查看引脚定义部分,找到ADC和DAC的引脚

        

                如图: 使用DAC_OUT1即PA4作为我们的DAC输出

标准库配置代码:
GPIO_InitTypeDef GPIO_InitStructure;

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);  //使能GPIOA的时钟

 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4; 
 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;  
 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;  //模拟输入模式,ADC和DAC使用该模式
 GPIO_Init(GPIOA,&GPIO_InitStructure); 

        (2).初始化DAC

                1.打开DAC时钟。

                2.选择DAC触发方式。

                3.是否开启DAC通道噪声波或三角波。

                4.设置LFSR寄存器,置三角波的最大幅值。

                5.设置是否打开DAC通道输出缓冲。

·               6.   初始化Init。

                7.选择数据对齐方式,一般选择12位右对齐即可,还可选12左对齐和8位右对齐可选。

                7.进行一些使能操作,根据具体要求进行使能。

标准库配置代码:
DAC_InitTypeDef DAC_InitStructure;

RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC, ENABLE);    //使能DAC的时钟

DAC_InitStructure.DAC_Trigger = DAC_Trigger_T4_TRGO;   //这里我选择为定时器4触发
DAC_InitStructure.DAC_WaveGeneration = DAC_WaveGeneration_None; 
DAC_InitStructure.DAC_LFSRUnmask_TriangleAmplitude = DAC_LFSRUnmask_Bit0; //配置指定DAC是否产生,噪声波或三角波,这里我用不到,选择None
DAC_InitStructure.DAC_OutputBuffer = DAC_OutputBuffer_Enable;  //DAC通道输出缓冲,这里我开启,需要提高其驱动能力
DAC_Init(DAC_Channel_1, &DAC_InitStructure);

DAC_DMACmd(DAC_Channel_1, ENABLE);              //使能DAC通道1的DMA模式

DAC_SetChannel1Data(DAC_Align_12b_R, 0); //12位右对齐数据格式设置DAC值

DAC_Cmd(DAC_Channel_1, ENABLE);

详细讲解
1.选择DAC触发方式:

参考手册介绍:

标准库使用:

可以选择以上触发方式

分类型讲解:

//  软件触发:

DAC_InitStructure.DAC_Trigger = DAC_Trigger_Software; //换成软件触发

DAC_SoftwareTriggerCmd(DAC_Channel_1,ENABLE); //开启DAC转化

DAC_SoftwareTriggerCmd(DAC_Channel_1,DISABLE); //开启DAC转化


// 自动转换:

DAC_InitStructure.DAC_Trigger = DAC_Trigger_None; //DAC一直自动转换


// 定时器触发:

DAC_InitStructure.DAC_Trigger = DAC_Trigger_T4_TRGO;


//定时器触发一般用DAC配合DMA和定时器TRGO事件使用,后面详细讲解,基础先使用软件触发即可
         2. 噪声生成和三角波这里不用,合并一起简单介绍

        标准库简介:

        DAC_InitStructure.DAC_WaveGeneration = DAC_WaveGeneration_None;//函数具体使用 

        

            选择是否开启该功能

若开启噪声或三角波功能,幅值设置函数

DAC_InitStructure.DAC_LFSRUnmask_TriangleAmplitude = DAC_LFSRUnmask_Bit0

标准库简介:

参考手册简介:

可知,通过该函数可设置三角波或噪声幅值,具体结合手册和标准库使用

3.是否打开DAC通道输出缓冲

标准库简介:

参考手册简介:

这个,我一般输出驱动能力不够时就会打开,给一个有讲其区别的博客

stm32的DAC_OutputBuffer_dac_outputbuffer_enable-CSDN博客

注意:开启会使低电压到不了0V;

接下来一些使能就不详细介绍了,进入到进阶,联合DMA和定时器触发使用DAC

二、定时器触发配置

(1)初始化TIM4

               1.开启TIM4时钟

                2.TIM_DeInit(TIM4) 将指定的定时器(TIM4)的寄存器重置为其默认值,确保你在重新配置定时器之前拥有一个干净的状态:寄存器清零、禁用中断、恢复默认设置

                3.设置自动重装载值和预分频系数

                4.设置时钟分频系数

                5.设置计数方式

                6.初始化定时器,设置触发方式,使能定时器

标准库配置代码:
void ConfigurTIM4(u16 arr, u16 psc)
{
  TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;   //TIM_TimeBaseStructure用于存放定时器的参数
   
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);   //使能定时器的时钟

  TIM_DeInit(TIM4);                                 //重置为缺省值
  TIM_TimeBaseStructure.TIM_Period        = arr;    //设置自动重装载值   
  TIM_TimeBaseStructure.TIM_Prescaler     = psc;    //设置预分频器值    
  TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;        //设置时钟分割:tDTS = tCK_INT
  TIM_TimeBaseStructure.TIM_CounterMode   = TIM_CounterMode_Up;  //设置向上计数模式 
  TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure);                //根据参数初始化定时器

  TIM_SelectOutputTrigger(TIM4, TIM_TRGOSource_Update);          //选择更新事件为触发输入

  TIM_Cmd(TIM4, ENABLE);  //使能定时器
}
详细讲解:

这里都是很基础的配置

1.  TIM_SelectOutputTrigger(TIM4, TIM_TRGOSource_Update); 选择TIM4触发输出模式

标准库源码,可选择的触发模式:

#define TIM_TRGOSource_Reset               ((uint16_t)0x0000) //
#define TIM_TRGOSource_Enable              ((uint16_t)0x0010)
#define TIM_TRGOSource_Update              ((uint16_t)0x0020)
#define TIM_TRGOSource_OC1                 ((uint16_t)0x0030)
#define TIM_TRGOSource_OC1Ref              ((uint16_t)0x0040)
#define TIM_TRGOSource_OC2Ref              ((uint16_t)0x0050)
#define TIM_TRGOSource_OC3Ref              ((uint16_t)0x0060)
#define TIM_TRGOSource_OC4Ref              ((uint16_t)0x0070)

 参考手册中关于可选择的触发方式的介绍:

 

这里我选择用更新事件作为触发,即当计数值加到重装载值产生一个触发事件。

三、DMA转运配置

(1)选择DMA通道、查找对应的要搬运的数据地址

        1.DMA通道选择,通过查找参考手册,可看见DMA的每个通道的作用,这里选择DMA2_CH3

即选择DAC1。

        

        2.因为DMA需要从一个地址搬运到另一个地址,这里讲一下怎么查找,查找参考手册可知,我们配置DAC时选择的是12位右对齐,所以找到该寄存器地址,可以&DAC->DHR12R1找到该地址

        

(2)初始化DMA

        1.开启DMA时钟

        2.设置外设地址

        3.设置内存地址

        4.设置传输数据长度(个数)

        5.设置数据传输方向

        6.设置外设地址和内存地址是否递增

        7.设置传输数据大小(8位、16位、32位)

        8.设置DMA模式-----循环模式/普通模式

        9.设置DMA的软件优先级

        10.是否开启从内存到内存传输

标准库配置代码:
void ConfigDMA2Ch3ForDAC1(u16* addr, u16 length)
{

  DMA_InitTypeDef   DMA_InitStructure;  //DMA_InitStructure用于存放DMA的参数
  NVIC_InitTypeDef  NVIC_InitStructure; //NVIC_InitStructure用于存放NVIC的参数
  
  RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA2, ENABLE);  //使能DMA2的时钟

  DMA_DeInit(DMA2_Channel3);  //将DMA1_CH1寄存器设置为默认值
  
  DMA_InitStructure.DMA_PeripheralBaseAddr  = (uint32_t)&DAC->DHR12R1;   //设置外设地址
  DMA_InitStructure.DMA_MemoryBaseAddr      = (uint32_t)addr;            //设置内部地址
  DMA_InitStructure.DMA_BufferSize          = (uint32_t)length;          //传入数据长度
  DMA_InitStructure.DMA_DIR                 = DMA_DIR_PeripheralDST;     //设置传输方向(这里设置外设作为目的地址,即内部数据输出到外设)
  DMA_InitStructure.DMA_PeripheralInc       = DMA_PeripheralInc_Disable; //外设地址是否递增,这里设置禁止递增
  DMA_InitStructure.DMA_MemoryInc           = DMA_MemoryInc_Enable;      //内存地址是否递增,这里使能内部地址递增
  DMA_InitStructure.DMA_MemoryDataSize      = DMA_PeripheralDataSize_HalfWord; //转运外部地址的数据大小
  DMA_InitStructure.DMA_PeripheralDataSize  = DMA_MemoryDataSize_HalfWord;  //转运内部地址数据大小
  DMA_InitStructure.DMA_Mode                = DMA_Mode_Circular;  //DMA转运模式
  DMA_InitStructure.DMA_Priority            = DMA_Priority_High;  //中断优先级(这里设置高)
  DMA_InitStructure.DMA_M2M                 = DMA_M2M_Disable;

  DMA_Init(DMA2_Channel3, &DMA_InitStructure); //根据参数初始化DMA2_Channel3



  DMA_Cmd(DMA2_Channel3, ENABLE); //使能DMA2


}
详细讲解:

1.外设地址为DAC采集存入的地址,内存地址为我们定义的数组地址。

2.数据长度一般为1即可,按顺序转运到DAC地址,若要进行一些数据处理可按自己需求配置合适的长度,每传输一个数据,DMA_BufferSize减一,但DMA_BufferSize==0,传输完成;

3.DAC数据传输设置为DMA_DIR_PeripheralDST即外设作为目的地,从内存传到外设,配置为DMA_DIR_PeripheralSRC则表示外设作为源,从外设传到内存

4.地址自增配置,按实际情况配置,这里我们外设地址不变,内存数组地址递增。

5.设置数据大小,根据上门配置可知,我们配置的DAC为12位,故这里我配置Halword即16位,即可。

6.模式选择:普通模式:完成一次传输后就停止工作,需要重新配置启动

                     循环模式:完成一次传输后,会自动重置,重新开始下一次传输,无需手动重新配置

7.优先级和M2M功能一般这样配置即可,目前没研究其使用,然后进行初始化和使能

四、整合使用

上面已经配置好了TIM4触发DAC采集和DMA转运

注意:通过查参考手册可知,使用外部触发,每触发一次产生一个DMA请求

   故:每触发一次,DMA将把数组中的设置长度的数据转运到DAC的指定寄存器中

          依据这种原理,可以输出指定频率和指定占空比的波形。

这里贴一个博客

STM32 DAC + DMA + TIM 输出正弦波,三角波,方波信号_32方波输出_暖暖的纠结的博客-CSDN博客

 这个博客介绍的就是个的应用,我也参考了该博主的博客,理解了可以参考该博客去实操一下。

完整代码:

DAC.C

#include "DAC.h"

u16 square_wave [100]= {};


void ConfigDAC1(void)
{
    DAC_InitTypeDef DAC_InitStructure;
    GPIO_InitTypeDef GPIO_InitStructure;

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);  //使能GPIOA的时钟
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC, ENABLE);    //使能DAC的时钟

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4; //
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
    GPIO_Init(GPIOA,&GPIO_InitStructure); //
		GPIO_SetBits(GPIOA,GPIO_Pin_4)    ;//PA.4 输出高
	
	
    DAC_InitStructure.DAC_Trigger = DAC_Trigger_T4_TRGO;
    DAC_InitStructure.DAC_WaveGeneration = DAC_WaveGeneration_None;
    DAC_InitStructure.DAC_LFSRUnmask_TriangleAmplitude = DAC_LFSRUnmask_Bit0;
    DAC_InitStructure.DAC_OutputBuffer = DAC_OutputBuffer_Enable;
    DAC_Init(DAC_Channel_1, &DAC_InitStructure);


    DAC_DMACmd(DAC_Channel_1, ENABLE);              //使能DAC通道1的DMA模式

    DAC_SetChannel1Data(DAC_Align_12b_R, 0); //12位右对齐数据格式设置DAC值

    DAC_Cmd(DAC_Channel_1, ENABLE);
    

}

void ConfigDMA2Ch3ForDAC1(u16* addr, u16 length)
{

  DMA_InitTypeDef   DMA_InitStructure;  //DMA_InitStructure用于存放DMA的参数
  
  RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA2, ENABLE);  //使能DMA2的时钟

  DMA_DeInit(DMA2_Channel3);  //将DMA1_CH1寄存器设置为默认值
  
  DMA_InitStructure.DMA_PeripheralBaseAddr  = (uint32_t)&DAC->DHR12R1;   //设置
  DMA_InitStructure.DMA_MemoryBaseAddr      = (uint32_t)addr;
  DMA_InitStructure.DMA_BufferSize          = (uint32_t)length; 
  DMA_InitStructure.DMA_DIR                 = DMA_DIR_PeripheralDST;
  DMA_InitStructure.DMA_PeripheralInc       = DMA_PeripheralInc_Disable;
  DMA_InitStructure.DMA_MemoryInc           = DMA_MemoryInc_Enable;
  DMA_InitStructure.DMA_MemoryDataSize      = DMA_PeripheralDataSize_HalfWord;
  DMA_InitStructure.DMA_PeripheralDataSize  = DMA_MemoryDataSize_HalfWord;
  DMA_InitStructure.DMA_Mode                = DMA_Mode_Circular;
  DMA_InitStructure.DMA_Priority            = DMA_Priority_High;
  DMA_InitStructure.DMA_M2M                 = DMA_M2M_Disable;

  DMA_Init(DMA2_Channel3, &DMA_InitStructure); //根据参数初始化DMA2_Channel3


  DMA_Cmd(DMA2_Channel3, ENABLE); //使能DMA2


}

void ConfigurTIM4(u16 arr, u16 psc)
{
  TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;   //TIM_TimeBaseStructure用于存放定时器的参数
   
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);   //使能定时器的时钟

  TIM_DeInit(TIM4);                                 //重置为缺省值
  TIM_TimeBaseStructure.TIM_Period        = arr;    //设置自动重装载值   
  TIM_TimeBaseStructure.TIM_Prescaler     = psc;    //设置预分频器值    
  TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;        //设置时钟分割:tDTS = tCK_INT
  TIM_TimeBaseStructure.TIM_CounterMode   = TIM_CounterMode_Up;  //设置向上计数模式 
  TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure);                //根据参数初始化定时器

  TIM_SelectOutputTrigger(TIM4, TIM_TRGOSource_Update);          //选择更新事件为触发输入

  TIM_Cmd(TIM4, ENABLE);  //使能定时器
}


void InitDAC(void)
{
    ConfigurTIM4(10, 720);
    ConfigDMA2Ch3ForDAC1(square_wave, 100);
    ConfigDAC1();

    dac1_SetRectWave(50, 4095, 10);
}
/*
*********************************************************************************************************
*	函 数 名: dac1_SetRectWave
*	功能说明: DAC1输出方波
*	形    参: _low  : 低电平时DAC, 
*			  _high : 高电平时DAC
*			  _freq : 频率 Hz
*			  _duty : 占空比 2% - 98%, 调节步数 1%
*	返 回 值: 无
*********************************************************************************************************
*/
void dac1_SetRectWave(u16 _low, u16 _high, u16 _duty)
{	
	uint16_t i;
	TIM_Cmd(TIM4, DISABLE);
	
	for (i = 0; i < _duty; i++)
	{
		square_wave[i] = _high;
	}
	for (; i < 100; i++)
	{
		square_wave[i] = _low;
	}
	
	ConfigDMA2Ch3ForDAC1(&square_wave,(u16)100);

  TIM_Cmd(TIM4, ENABLE);


}

 DAC.h

#ifndef _DAC_H_
#define _DAC_H_

#include "DataType.h"
#include "stm32f10x_conf.h"

void InitDAC(void);
void ConfigDAC1(void);
void ConfigDMA2Ch3ForDAC1(u16* addr, u16 length);
void dac1_SetRectWave(u16 _low, u16 _high, u16 _duty);
#endif

                

  • 26
    点赞
  • 37
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值