前提:本人使用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