stm32中利用TIM+DMA+DAC产生10kHz正弦波

整个工作流程为:

  1. 先建立一个含N个点的数据表(正弦波的一个周期由N个点构成)
  2. 计算所需的定时器定时周期(根据输出信号的周期和周期内的点数计算)
  3. 配置DAC
  4. 配置定时器
  5. 配置DMA用于自动转运数据表中数据到DAC中

1. 建立数据表(存放要输入到DAC的数据点)

实验可知:32个数据点就可以得到一个完整的波形,这里选择50个;

#include <stdio.h>
#include <stdlib.h>
#include <math.h>

#define TABLE_SIZE 50
#define PI 3.14159265358979323846

int sineWaveTable[TABLE_SIZE];

void PointGenerate(void)
{
    double step = 2 * PI / TABLE_SIZE;
    double radian = 0;
    
    for (int i = 0; i < TABLE_SIZE; i++)
    {
        double out_voltage = 2047 * (sin(radian) + 1);
        sineWaveTable[i] = (int)out_voltage;
        radian += step;
    }
}

int main(void)
{
    PointGenerate();
    
    //打印表格中数据,10个数据一行
    for (int i = 0; i < TABLE_SIZE; i++)
    {
        printf("%4d ", sineWaveTable[i]);
        if ((i + 1) % 10 == 0)
            printf("\n");
    }

    printf("\nPress Enter to continue...");
    getchar();
    return 0;
}

2. 配置DAC

函数DAC_Config()

1. 开启GPIO时钟,开启DAC时钟

2. GPIO初始化,DAC初始化

3. 使能DAC

DAC_Cmd(DAC_Channel_1, ENABLE); //打开DAC的通道1

4. 使能DAC的DMA(直接内存访问)请求

这样无需CPU干预,DMA就会自动把数据搬运到DAC

DAC_DMACmd(DAC_Channel_1, ENABLE);

代码:

static void DAC_Config(void)
{
  GPIO_InitTypeDef GPIO_InitStructure;
	DAC_InitTypeDef  DAC_InitStructure;
 
  /* 使能GPIOA时钟 */
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);	
	
	/* 使能DAC时钟 */	
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC, ENABLE);
	
  /* DAC的GPIO配置,模拟输入 */
  GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_4;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;//没有模拟输出,选这个可以实现模拟输出
  GPIO_Init(GPIOA, &GPIO_InitStructure);
	
 
 
  /* 配置DAC 通道1 */
  DAC_InitStructure.DAC_Trigger = DAC_Trigger_T2_TRGO;		      //使用TIM2作为触发源
  DAC_InitStructure.DAC_WaveGeneration = DAC_WaveGeneration_None;	//不使用波形发生器
  DAC_InitStructure.DAC_OutputBuffer = DAC_OutputBuffer_Disable;	//不使用DAC输出缓冲
  DAC_Init(DAC_Channel_1, &DAC_InitStructure);
 
 
  //使能通道1
  DAC_Cmd(DAC_Channel_1, ENABLE);

 
  //使能DAC的DMA请求
  DAC_DMACmd(DAC_Channel_1, ENABLE);
}

3. 配置定时器,得到所需定时周期

要点:设置定时器定时周期,时间到了定时器就溢出(称为定时器更新事件),就会立马把一个数据传输到DAC,因此传输一个点的时间为Tupdate,而一个正弦波是由50个点组成,输出50个点需要的时间为Tupdate*50,这个时间就等于输出信号的周期;

正弦信号输出频率fout=10kHz, 也就说输出信号的周期T = 1/fout;

因此:Tupdate*50 = 1/fout                                                            (1)

接下来求出所需的Tupdate即可

假设使用单片机的时钟频率F = 280MHz

那么分频后周期:T1 = (PSC+1)/ F

定时周期:Tupdate= T1*(ARR+1)                  //计数计到ARR+1后溢出

因此:

 

代码部分:


static void DAC_TIM_Config(void)
{
	
	TIM_TimeBaseInitTypeDef    TIM_TimeBaseStructure;
	
	/* 使能TIM2时钟,TIM2CLK 为72M */
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
	
   /* TIM2基本定时器配置 */
   // TIM_TimeBaseStructInit(&TIM_TimeBaseStructure); 
  TIM_TimeBaseStructure.TIM_Period = 20-1;    //定时周期 20  
  TIM_TimeBaseStructure.TIM_Prescaler = 28-1;      	//预分频, 280M / (28-1+1) = 10M
  TIM_TimeBaseStructure.TIM_ClockDivision = 0x0;    	//时钟分频系数
  TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  	//向上计数模式
  TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);

  TIM_SelectOutputTrigger(TIM2, TIM_TRGOSource_Update); 
//选择发送触发信号的触发源为定时器更新,即计数器溢出时产生触发信号
//(在DAC中设置这个触发信号作为触发DAC转换的信号源)
 
	/* 使能TIM2 */
  TIM_Cmd(TIM2, ENABLE);

}

 选择触发源为更新时间触发(定时器溢出),定时时间到了以后就会输出触发信号。

在DAC_Config中设置这个触发信号作为DAC转换的触发信号源:

 DAC_InitStructure.DAC_Trigger = DAC_Trigger_T2_TRGO;

4. 配置DMA


static void DAC_DMA_Config(void)
{	

  RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA2, ENABLE);//开启DMA2时钟


  DMA_InitTypeDef  DMA_InitStructure;
	
  DMA_InitStructure.DMA_PeripheralBaseAddr = DAC_DHR12RD_ADDRESS;		
  //外设数据地址
  
  DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&DualSine12bit ;		
  //内存数据地址 DualSine12bit

  DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;											 
  //数据传输方向内存至外设

  DMA_InitStructure.DMA_BufferSize = POINT_NUM;																	 
  //缓存大小为POINT_NUM字节
	
  DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;	   
  //外设数据地址固定	
  DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;									 
  //内存数据地址自增
  
  DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word; 
  //外设数据以字为单位
 
  DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Word;
  //内存数据以字为单位	
 
  DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;													 
  //循环模式
  
  DMA_InitStructure.DMA_Priority = DMA_Priority_High;											 
  //高DMA通道优先级
 
  DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;														 
  //非内存至内存模式	
 
  DMA_Init(DMA2_Channel4, &DMA_InitStructure);
	
  //使能DMA2-14通道
  DMA_Cmd(DMA2_Channel4, ENABLE);
}

DAC初始化函数:

运行上面的三个函数完成DAC配置、TIM配置、DMA配置;


void DAC_Mode_Init(void)
{

 
	DAC_Config();
	DAC_TIM_Config();	

	DAC_DMA_Config();

}

主函数:

int main(void)
{

    DAC_Mode_Init();

    while (1);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值