ECE STM32开发 DAC输出正弦波

一、实验目的

了解 DAC 的结构和功能,学习使用 TIM 触发 DAC 输出正弦波

二、实验过程

  1. 编写 DAC 的配置初始化函数 DAC_Config。功能包括使能 GPIOA 时钟、使能 DAC 时钟、配置 DAC 的 GPIO、配置 DAC 通道 1&2,使能通道 1 由 PA4 输出、使能通道 2 由 PA5 输出、使能 DAC 的 DMA 请求。

  2. 编写用来触发 DAC 的通用定时器 TIM2 的配置初始化函数 DAC_TIM_Config。功能包括配置 TIM2 触发源、使能 TIM2 时钟、使能 TIM2,将 TIM2 的 ARR 设置为 19,预分频为0,时钟分频系数为 20,向上计数。

  3. 编写用来向 DAC 传输波形数据的 DMA 的配置初始化函数 DAC_DMA_Config。功能包括使能 DMA2 时钟、配置 DMA2、使能 DMA2-14 通道。

  4. 使用 matlab 生成正弦波形数据,使用 32 个点拟合正弦波的一个周期。

  5. 编写 USART 的配置初始化函数 USART_Config。功能包括打开串口 GPIO 的时钟、打开串口外设的时钟、将 USART Tx 的 GPIO 配置为推挽复用模式、将 USART Rx 的 GPIO 配置为浮空输入模式、配置串口的工作参数、使能串口。

  6. 编写主函数。功能包括调用 DAC_Mode_Init 和 USART_Config 函数、当接收到更改频率指令时根据指令更改 TIM2 的 ARR。

  7. 将开发板连接到示波器。

i. 初次启动时可以观察到正弦波频率为 112.507kHz,符合预期值

在这里插入图片描述

ii. 在串口调试助手输入指令,更改频率为 56250Hz,在示波器上观察到正弦波频率为 56.2536kHz,符合预期值。

在这里插入图片描述
在这里插入图片描述

iii. 在串口调试助手输入指令,更改频率为 37500Hz,在示波器上观察到正弦波频率为 37.5024kHz,符合预期值。

在这里插入图片描述

在这里插入图片描述

三、附录

main.c

#include "string.h"
#include "stm32f10x.h"
#include "bsp_dac.h"
#include "bsp_usart.h"

static void Show_Message(void);

int main(void)
{
    DAC_Mode_Init();
    USART_Config();
    while(1){
        char ch[5];
        Show_Message();
        scanf("%s",ch);
        if(strcmp(ch,"9999")==0){
            int f_sin = 0;
            printf("请输入更改后的频率:\n");
            scanf("%d",&f_sin);
            TIM2->ARR = (int)((72000000/32/f_sin)-1);
            printf("ARR=%d\n",TIM2->ARR);
            printf("频率更改成功,当前频率为:%d\n",(int)(72000000/32/(TIM2->ARR+1)));
        }
        else{
            printf("输入无效,请重新输入\n");
        }
  }
}

static void Show_Message(void)
{
  printf("\n该程序可以更改DAC输出正弦波的频率\n");
  printf("使用USART参数为:%d 8-N-1 \n",DEBUG_USART_BAUDRATE);
  printf("若要更改频率,请输入9999\n");
}

bsp_dac.c

#include "bsp_dac.h"

//正弦波单个周期的点数
#define POINT_NUM 32

/* 波形数据 ---------------------------------------------------------*/
const uint16_t Sine12bit[POINT_NUM] = {
    2048    , 2460  , 2856  , 3218  , 3532  , 3786  , 3969  , 4072  ,
    4093    , 4031  , 3887  , 3668  , 3382  , 3042  ,   2661    , 2255  , 
    1841    , 1435  , 1054  , 714       , 428       , 209       , 65        , 3         ,
    24      , 127       , 310       , 564       , 878       , 1240  , 1636  , 2048
};

uint32_t DualSine12bit[POINT_NUM];

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_Pin_5;
    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);
    /* 配置DAC 通道2 */
    DAC_Init(DAC_Channel_2, &DAC_InitStructure);
    /* 使能通道1 由PA4输出 */
    DAC_Cmd(DAC_Channel_1, ENABLE);
    /* 使能通道2 由PA5输出 */
    DAC_Cmd(DAC_Channel_2, ENABLE);
    /* 使能DAC的DMA请求 */
    DAC_DMACmd(DAC_Channel_2, ENABLE);
}

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 = 0x0;//预分频,不分频 72M / (0+1) = 72M
    TIM_TimeBaseStructure.TIM_ClockDivision = 0x0;//时钟分频系数
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;//向上计数模式
    TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
    /* 配置TIM2触发源 */
    TIM_SelectOutputTrigger(TIM2, TIM_TRGOSource_Update);
    /* 使能TIM2 */
    TIM_Cmd(TIM2, ENABLE);
}

static void DAC_DMA_Config(void)
{   
    DMA_InitTypeDef  DMA_InitStructure;
    /* 使能DMA2时钟 */
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA2, ENABLE);
    /* 配置DMA2 */
    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);
}

void DAC_Mode_Init(void)
{
    uint32_t Idx = 0;  
    DAC_Config();
    DAC_TIM_Config();   
    /* 填充正弦波形数据,双通道右对齐*/
  for (Idx = 0; Idx < POINT_NUM; Idx++){
    DualSine12bit[Idx] = (Sine12bit[Idx] << 16) + (Sine12bit[Idx]);
  }
    DAC_DMA_Config();
}

bsp_dac.h

#ifndef __DAC_H
#define __DAC_H

#include "stm32f10x.h"

//DAC DHR12RD寄存器,12位、右对齐、双通道
#define DAC_DHR12RD_ADDRESS (DAC_BASE+0x20) 

void DAC_Mode_Init(void);

#endif /* __DAC_H */

bsp_usart.c

#include "bsp_usart.h"

void USART_Config(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	USART_InitTypeDef USART_InitStructure;

	// 打开串口GPIO的时钟
	DEBUG_USART_GPIO_APBxClkCmd(DEBUG_USART_GPIO_CLK, ENABLE);
	
	// 打开串口外设的时钟
	DEBUG_USART_APBxClkCmd(DEBUG_USART_CLK, ENABLE);

	// 将USART Tx的GPIO配置为推挽复用模式
	GPIO_InitStructure.GPIO_Pin = DEBUG_USART_TX_GPIO_PIN;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(DEBUG_USART_TX_GPIO_PORT, &GPIO_InitStructure);

  // 将USART Rx的GPIO配置为浮空输入模式
	GPIO_InitStructure.GPIO_Pin = DEBUG_USART_RX_GPIO_PIN;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
	GPIO_Init(DEBUG_USART_RX_GPIO_PORT, &GPIO_InitStructure);
	
	// 配置串口的工作参数
	// 配置波特率
	USART_InitStructure.USART_BaudRate = DEBUG_USART_BAUDRATE;
	// 配置 针数据字长
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;
	// 配置停止位
	USART_InitStructure.USART_StopBits = USART_StopBits_1;
	// 配置校验位
	USART_InitStructure.USART_Parity = USART_Parity_No ;
	// 配置硬件流控制
	USART_InitStructure.USART_HardwareFlowControl = 
	USART_HardwareFlowControl_None;
	// 配置工作模式,收发一起
	USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
	// 完成串口的初始化配置
	USART_Init(DEBUG_USARTx, &USART_InitStructure);	
	
	// 使能串口
	USART_Cmd(DEBUG_USARTx, ENABLE);	    
}

///重定向c库函数printf到串口,重定向后可使用printf函数
int fputc(int ch, FILE *f)
{
		/* 发送一个字节数据到串口 */
		USART_SendData(DEBUG_USARTx, (uint8_t) ch);
		
		/* 等待发送完毕 */
		while (USART_GetFlagStatus(DEBUG_USARTx, USART_FLAG_TXE) == RESET);		
	
		return (ch);
}

///重定向c库函数scanf到串口,重写向后可使用scanf、getchar等函数
int fgetc(FILE *f)
{
		/* 等待串口输入数据 */
		while (USART_GetFlagStatus(DEBUG_USARTx, USART_FLAG_RXNE) == RESET);

		return (int)USART_ReceiveData(DEBUG_USARTx);
}

bsp_usart.h

#ifndef __USART_H
#define __USART_H


#include "stm32f10x.h"
#include <stdio.h>

/** 
  * 串口宏定义,不同的串口挂载的总线不一样,移植时需要修改这几个宏
  */
#define DEBUG_USARTx USART1
#define DEBUG_USART_CLK RCC_APB2Periph_USART1
#define DEBUG_USART_APBxClkCmd RCC_APB2PeriphClockCmd
#define DEBUG_USART_BAUDRATE 115200

// USART GPIO 引脚宏定义
#define DEBUG_USART_GPIO_CLK (RCC_APB2Periph_GPIOA)
#define DEBUG_USART_GPIO_APBxClkCmd RCC_APB2PeriphClockCmd
    
#define DEBUG_USART_TX_GPIO_PORT GPIOA   
#define DEBUG_USART_TX_GPIO_PIN GPIO_Pin_9
#define DEBUG_USART_RX_GPIO_PORT GPIOA
#define DEBUG_USART_RX_GPIO_PIN GPIO_Pin_10

#define DEBUG_USART_IRQ USART1_IRQn
#define DEBUG_USART_IRQHandler USART1_IRQHandler

void USART_Config(void);
void Usart_SendByte( USART_TypeDef * pUSARTx, uint8_t ch);
void Usart_SendString( USART_TypeDef * pUSARTx, char *str);
void Usart_SendHalfWord( USART_TypeDef * pUSARTx, uint16_t ch);

#endif /* __USART_H */

sinWave.m

%用于产生正弦数据表,输出到文件 dac_sinWave.c 文件中

 n = 2*pi/32 : 2*pi/32 : 2*pi %分成 32 等份

 a = sin(n)+1; %求取 sin 函数值并向上平移一个单位,消除负数值

 a = a * 3.3/2; %调整幅值,使范围限制为 0~3.3
 r = a* (2.^12) /3.3 %求取 dac 数值,12 位 dac LSB = 3.3/2.^12
 r = uint16(r); %把 double 型数据转化成 16 位整型数据

 for i = 1:32
 if r(i) > 4095 %限制数据最大不超过 4095
    r(i) = 4095
 end
 end

 dlmwrite('dac_sinWave.c',r); %把数据写入到文件,方便添加到 stm32 工程中
 plot(n,r,'.') %把这些点画出来
  • 7
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要使用STM32DAC模块产生正弦波,可以使用以下步骤: 1. 初始化DAC模块,包括时钟使能、GPIO配置和DAC配置等; 2. 定义正弦波数组,可以使用sin函数计算出每个采样点的数值; 3. 启动定时器以固定的采样率产生DAC数据; 4. 在定时器中断中,将正弦波数组中下一个采样点数据写入DAC数据寄存器中; 5. 监测DAC中断标志位,确保数据已经输出完毕。 下面是一个基于STM32CubeMX和HAL库的DAC产生正弦波的例子代码: ```c #include "main.h" #include <math.h> #define SAMPLE_RATE 20000 // 20kHz采样率 #define SAMPLE_NUM 100 // 正弦波采样点数 DAC_HandleTypeDef hdac; TIM_HandleTypeDef htim6; static uint16_t sinewave[SAMPLE_NUM]; // 正弦波数据数组 static void MX_GPIO_Init(void); static void MX_DAC_Init(void); static void MX_TIM6_Init(void); void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim); int main(void) { HAL_Init(); MX_GPIO_Init(); MX_DAC_Init(); MX_TIM6_Init(); for (int i = 0; i < SAMPLE_NUM; i++) { sinewave[i] = (uint16_t)(2048 * (1 + sin(2 * M_PI * i / SAMPLE_NUM))); // 生成正弦波数据,最大输出值为4095(12位DAC) } HAL_DAC_Start(&hdac, DAC_CHANNEL_1); HAL_TIM_Base_Start_IT(&htim6); while (1) { // Main loop } } static void MX_DAC_Init(void) { DAC_ChannelConfTypeDef sConfig = {0}; hdac.Instance = DAC; if (HAL_DAC_Init(&hdac) != HAL_OK) { Error_Handler(); } sConfig.DAC_Trigger = DAC_TRIGGER_T6_TRGO; // DAC使用定时器6触发 sConfig.DAC_OutputBuffer = DAC_OUTPUTBUFFER_ENABLE; if (HAL_DAC_ConfigChannel(&hdac, &sConfig, DAC_CHANNEL_1) != HAL_OK) { Error_Handler(); } } static void MX_TIM6_Init(void) { TIM_MasterConfigTypeDef sMasterConfig = {0}; htim6.Instance = TIM6; htim6.Init.Prescaler = HAL_RCC_GetHCLKFreq() / (SAMPLE_NUM * SAMPLE_RATE) - 1; // 定时器时钟频率 = 系统时钟 / PSC htim6.Init.CounterMode = TIM_COUNTERMODE_UP; htim6.Init.Period = SAMPLE_NUM - 1; // 计数器从0到99,共100次 if (HAL_TIM_Base_Init(&htim6) != HAL_OK) { Error_Handler(); } sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE; sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE; if (HAL_TIMEx_MasterConfigSynchronization(&htim6, &sMasterConfig) != HAL_OK) { Error_Handler(); } } void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { if (htim->Instance == TIM6) { static uint8_t index = 0; // 当前采样点索引 uint16_t value = sinewave[index]; HAL_DAC_SetValue(&hdac, DAC_CHANNEL_1, DAC_ALIGN_12B_R, value); // 写入DAC数据寄存器 index = (index + 1) % SAMPLE_NUM; } } static void MX_GPIO_Init(void) { // DAC输出GPIO初始化 GPIO_InitTypeDef GPIO_InitStruct = {0}; __HAL_RCC_GPIOA_CLK_ENABLE(); GPIO_InitStruct.Pin = GPIO_PIN_4; GPIO_InitStruct.Mode = GPIO_MODE_ANALOG; GPIO_InitStruct.Pull = GPIO_NOPULL; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); } void Error_Handler(void) { // 程序发生错误时停机 while (1) { } } ``` 需要注意的是,当DAC模块输出到外部电路时,需要使用一定的滤波电路来去除采样过程中可能产生的高频噪声。另外,如果要实现多声道输出,可以使用不同的DAC通道,或者使用DMA功能直接传输多通道数据。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值