基于DAC的波形发生器设计
1.设计要求
1.输出方波、三角波、正弦波三种波形
2.各种波形幅值、频率在一定范围内可调
3.串口控制波形数据
4.基于stm32f103芯片
2.具体实现
2.1采样点及精度调整
/*采样点及精度调整*/
#define pi 3.1415926
#define POINT_NUM 128
#define angle 2*pi/POINT_NUM
2.2 GPIO配置
初学到这里一开始也很懵逼,明明是输出模拟信号凭啥配置成模拟输入模式?
后来看了优快云几篇大神的文章终于明白了:
因为一但使能 DACx 通道之后,相应的 GPIO 引脚(PA4 或者 PA5)会自动与 DAC 的模拟输出相连。其实,模拟输入通道与模拟输出通道是一起的(本来就是一条通路嘛),其实也可以理解为这里的”输入”是相对于片上外设DAC而言,IO是作为输入,但本质上是一条通路。
那么如此配置的好处嘞
- 可以看到信号行走的路线上(下图红色)没有触发器、数据寄存器和上下拉电阻,所以因它而起的电平跳变噪声和相应的额外功耗就没有了。同时也降低了芯片的动态功耗。
- 配置在模拟输入状态的GPIO引脚属于高阻态,这点也有利于保持模拟信号的真实性(说白了,就是信号传输通道无任何信号处理装置,可以将原汁原味的模拟信号在CPU与外设之间进行传输)
给出配置函数:
void DAC1_GPIO_config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
/* DAC的GPIO配置,模拟输入 */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_Init(GPIOA, &GPIO_InitStructure);
}
2.3定时器配置
使用定时器6作为DMA搬运的触发源。
至此,采样点和定时器配置确定了,输出波形的频率也确定了。
可以由以下公式计算:
N是采样点个数,这里设为N=128
systick有系统时钟频率决定,F1这里为72M。
为了提高输出信号的带宽,一般把定时器预分频系数设为0。
void DAC1_TIM_Config(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
/* 使能TIM6时钟,TIM2CLK 为72M */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, ENABLE);
/*定时器初始周期*/
TIM_TimeBaseStructure.TIM_Period = 0XF;
/* 预分频,不分频 72M / (0+1) = 72M */
TIM_TimeBaseStructure.TIM_Prescaler = 0x0;
/*时钟分频系数*/
TIM_TimeBaseStructure.TIM_ClockDivision = 0x0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM6, &TIM_TimeBaseStructure);
/* 配置TIM6触发源 */
TIM_SelectOutputTrigger(TIM6, TIM_TRGOSource_Update);
/* 使能TIM6 */
TIM_Cmd(TIM6, ENABLE);
}
2.4 DAC与DMA的配置
首先是DAC配置,这里采用DAC1(32上一共有俩)
值得注意的是DAC输出缓冲的结构体成员,使能输出缓冲可以提高输出信号的驱动能力,这里不使能。
其次是DMA配置,查询手册,发现DAC1对应的DMA通道为DMA2-CH3
这里的外设数据地址要查询手册下面给出地址映射:
#define DAC_DHR12RD_ADDRESS (DAC_BASE+0x20) //双通道输出地址
#define DAC_DHR8R1_Address