使用ECE STM32开发板实现DAC输出正弦波
一、实验目的
了解 DAC 的结构和功能,学习使用 TIM 触发 DAC 输出正弦波
二、实验过程
-
编写 DAC 的配置初始化函数 DAC_Config。功能包括使能 GPIOA 时钟、使能 DAC 时钟、配置 DAC 的 GPIO、配置 DAC 通道 1&2,使能通道 1 由 PA4 输出、使能通道 2 由 PA5 输出、使能 DAC 的 DMA 请求。
-
编写用来触发 DAC 的通用定时器 TIM2 的配置初始化函数 DAC_TIM_Config。功能包括配置 TIM2 触发源、使能 TIM2 时钟、使能 TIM2,将 TIM2 的 ARR 设置为 19,预分频为0,时钟分频系数为 20,向上计数。
-
编写用来向 DAC 传输波形数据的 DMA 的配置初始化函数 DAC_DMA_Config。功能包括使能 DMA2 时钟、配置 DMA2、使能 DMA2-14 通道。
-
使用 matlab 生成正弦波形数据,使用 32 个点拟合正弦波的一个周期。
-
编写 USART 的配置初始化函数 USART_Config。功能包括打开串口 GPIO 的时钟、打开串口外设的时钟、将 USART Tx 的 GPIO 配置为推挽复用模式、将 USART Rx 的 GPIO 配置为浮空输入模式、配置串口的工作参数、使能串口。
-
编写主函数。功能包括调用 DAC_Mode_Init 和 USART_Config 函数、当接收到更改频率指令时根据指令更改 TIM2 的 ARR。
-
将开发板连接到示波器。
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,'.') %把这些点画出来
作者:zinc_96