要输出正弦波,需要好几个外设配合:Timer、DAC、DMA。TImer用来设置正弦波的频率的;DAC顾名思义将数字量转换成模拟量,在这里就是转化成电压信号;DMA直接控制DAC输出,而不用麻烦芯片内核。

下面讲讲它们之间如何配合工作。首先要配置定时器的频率,并设置定时器为输出触发。然后配置DAC的触发源为定时器触发,并打开DAC的MDA功能。接下去轮到DMA的工作了,设置DMA的操作对象为DAC。按上面配置好后,三个外设就可以正常工作了:定时器每次计数值递增,就触发DAC工作,然后DMA就控制DAC输出相对应的电压值,在一个定时周期内,DAC输出电压值输出按正弦波的变化,这样就产生了正弦波!

下面开始讲讲STM32的 代码,仍然还是在我自己的规范工程做修改。

1、工程的改动

1)代码中需要用到定时器,所以添加stm32f10x_tim.c到STM32F10x_StdPeriod_Driver工作组中。

2)除了定时器,还需要用到DAC,故添加stm32f10x_dac.c到STM32F10x_StdPeriod_Driver工作组中。

3)最后还需要添加stm32f10x_dma.c到STM32F10x_StdPeriod_Driver工作组中

4)打开stm32f10x_conf.h文件,把stm32f10x_tim.h、stm32f10x_dac.h、stm32f10x_dma.h包含进来,也就是将原先屏蔽的包含这些文件的语句去掉屏蔽。

5)新建SineWave.c与sineWave.h这两个文件分别保存在BSP文件夹中的src与inc中,并将SineWave.c添加进工程的BSP中。

2、SineWave.c与SineWave.h的程序编写

在代码中,我设置两路正弦波输出,一路输出频率为800Hz的正弦波,另一路输出频率为1600Hz的正弦波,他们分别对应的DAC通道1的PA4引脚,与DAC通道2的PA5引脚。所以代码中首先初始化这两个引脚:

/*************************************************************

Function : SineWave_GPIO_Config

Deion: 引脚配置

Input : none

return : none

*************************************************************/

static void SineWave_GPIO_Config(void)

{

GPIO_InitTypeDef GPIO_InitStructure;

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //初始化引脚时钟

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5;//DAC channel1和channel2对应的引脚

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出

GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

GPIO_Init(GPIOA, &GPIO_InitStructure);

}

接下去要配置定时器了,定时器的作用就是设置正弦波的频率,由于要输出两路频率不相同的定时器,所以这里需要配置两路的定时器,一路设置频率为800Hz,另一路设置频率为1600Hz,代码如下:

#define _800Hz (u16)(72000000/sizeof(Sine12bit)*2/800) //800Hz

#define _1600Hz (u16)(72000000/sizeof(Sine12bit)*2/1600) //1600Hz

const u16 Sine12bit[256] = { //正弦波描点

2048, 2098, 2148, 2198, 2248, 2298, 2348, 2398, 2447, 2496,

2545, 2594, 2642, 2690, 2737, 2785, 2831, 2877, 2923, 2968,

3013, 3057, 3100, 3143, 3185, 3227, 3267, 3307, 3347, 3385,

3423, 3460, 3496, 3531, 3565, 3598, 3631, 3662, 3692, 3722,

3750, 3778, 3804, 3829, 3854, 3877, 3899, 3920, 3940, 3958,

3976, 3992, 4007, 4021, 4034, 4046, 4056, 4065, 4073, 4080,

4086, 4090, 4093, 4095, 4095, 4095, 4093, 4090, 4086, 4080,

4073, 4065, 4056, 4046, 4034, 4021, 4007, 3992, 3976, 3958,

3940, 3920, 3899, 3877, 3854, 3829, 3804, 3778, 3750, 3722,

3692, 3662, 3631, 3598, 3565, 3531, 3496, 3460, 3423, 3385,

3347, 3307, 3267, 3227, 3185, 3143, 3100, 3057, 3013, 2968,

2923, 2877, 2831, 2785, 2737, 2690, 2642, 2594, 2545, 2496,

2447, 2398, 2348, 2298, 2248, 2198, 2148, 2098, 2047, 1997,

1947, 1897, 1847, 1797, 1747, 1697, 1648, 1599, 1550, 1501,

1453, 1405, 1358, 1310, 1264, 1218, 1172, 1127, 1082, 1038,

995, 952, 910, 868, 828, 788, 748, 710, 672, 635,

599, 564, 530, 497, 464, 433, 403, 373, 345, 317,

291, 266, 241, 218, 196, 175, 155, 137, 119, 103,

88, 74, 61, 49, 39, 30, 22, 15, 9, 5,

2, 0, 0, 0, 2, 5, 9, 15, 22, 30,

39, 49, 61, 74, 88, 103, 119, 137, 155, 175,

196, 218, 241, 266, 291, 317, 345, 373, 403, 433,

464, 497, 530, 564, 599, 635, 672, 710, 748, 788,

828, 868, 910, 952, 995, 1038, 1082, 1127, 1172, 1218,

1264, 1310, 1358, 1405, 1453, 1501, 1550, 1599, 1648, 1697,

1747, 1797, 1847, 1897, 1947, 1997 };

//const u16 Sine12bit[32] = { //正弦描点

// 2047, 2447, 2831, 3185, 3498, 3750, 3939, 4056, 4095, 4056,

// 3939, 3750, 3495, 3185, 2831, 2447, 2047, 1647, 1263, 909,

// 599, 344, 155, 38, 0, 38, 155, 344, 599, 909, 1263, 1647};

/*************************************************************

Function : SineWave_TIM_Config

Deion: 定时器配置

Input : none

return : none

*************************************************************/

static void SineWave_TIM_Config(void)

{

TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;

RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2|RCC_APB1Periph_TIM6, ENABLE);//初始化与6的时钟

TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);

TIM_TimeBaseStructure.TIM_Period = _800Hz; //正弦波1频率设置

TIM_TimeBaseStructure.TIM_Prescaler = 0x0; //没有预分频

TIM_TimeBaseStructure.TIM_ClockDivision = 0x0; //时钟不分频

TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;//增计数

TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);

TIM_TimeBaseStructure.TIM_Period = _1600Hz; //正弦波2频率设置

TIM_TimeBaseInit(TIM6, &TIM_TimeBaseStructure);

TIM_SelectOutputTrigger(TIM2, TIM_TRGOSource_Update);//更新TIM2输出触发

TIM_SelectOutputTrigger(TIM6, TIM_TRGOSource_Update);//更新TIM6输出触发

}

上面代码中宏定义中的sizeof(Sine12bit)是输出正弦波的数组的大小,它的类型是u16,所以在要求出数组的元素的个数sizeof(Sine12bit)要除以2。之所以要用sizeof(Sine12bit)/2这张种方式而不是直接用常量256,是因为在上面代码中,我定义了两个Sine12bit数组,其中一个被我屏蔽掉了,如果想要使用被屏蔽的那个Sine12bit数组,在解除它的屏蔽与屏蔽另一个Sine12bit数组后,_800Hz和_1600Hz的值就会自定改变,以满足定时器的正确频率输出。上面两个Sine12bit[]数组中,一个数组为Sine12bit[256]共256个元素,另一个屏蔽的是数组是Sine12bit[32]共32个元素。这两个数组的元素如果在坐标上描点出来的话,就是一个周期的正弦波,只不过具有256个元素的数组描出来的曲线比只有32元素的数组描出来的曲线精确的多。这两个数组,我都给出了,以便以后根据场合选择。在这个工程里我选着选择的是Sine12bit[256],画出精确的正弦波。

接下去配置DAC,DAC总共2路通道,由于要输出正弦波,所以这两个通道都需要配置。代码如下:

/*************************************************************

Function : SineWave_DAC_Config

Deion: DAC配置

Input : none

return : none

*************************************************************/

static void SineWave_DAC_Config(void)

{

DAC_InitTypeDef DAC_InitStructure;

RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC, ENABLE);//初始化DAC的时钟

DAC_StructInit(&DAC_InitStructure);

DAC_InitStructure.DAC_Trigger = DAC_Trigger_T2_TRGO;//指定DAC1的触发定时器TIM2

DAC_InitStructure.DAC_WaveGeneration = DAC_WaveGeneration_None;//无波形产生

DAC_InitStructure.DAC_OutputBuffer = DAC_OutputBuffer_Disable; //不是能DAC输出缓冲

DAC_Init(DAC_Channel_1, &DAC_InitStructure);//初始化DAC channel1

DAC_InitStructure.DAC_Trigger = DAC_Trigger_T6_TRGO;//指定DAC2的触发定时器TIM6

DAC_Init(DAC_Channel_2, &DAC_InitStructure);//初始化DAC channel2

DAC_Cmd(DAC_Channel_1, ENABLE); //使能DAC channel1

DAC_Cmd(DAC_Channel_2, ENABLE); //使能DAC channel2

DAC_DMACmd(DAC_Channel_1, ENABLE); //使能DAC Channel1的DMA

DAC_DMACmd(DAC_Channel_2, ENABLE); //使能DAC Channel2的DMA

}

上面代码中,DAC的通道1与通道2的DAC_Trigger分别设置成 定时器2触发DAC_Trigger_T2_TRGO与定时器6触发DAC_Trigger_T6_TRGO。关于DAC的外部触发源,只能是下面的几个:TIM2、TIM4、TIM5、TIM6、TIM7、TIM8、EXIT_Line9、SWTRIG(软件触发),如下图所示:

DAC通道1与通道2配置完后,还要打开DAC的DMA功能:DAC_DMACmd(DAC_Channel_1, ENABLE);DAC_DMACmd(DAC_Channel_2, ENABLE);以便然DMA可以控制DAC输出。

DAC配置后,就临到DAM的配置了。DMA配置代码如下:

#define DAC_DHR12R1 0x40007408 //外设DAC通道1的基地址

#define DAC_DHR12R2 0x40007414 //外设DAC通道2的基地址

/*************************************************************

Function : SineWave_DMA_Config

Deion: DMA配置

Input : none

return : none

*************************************************************/

static void SineWave_DMA_Config(void)

{

DMA_InitTypeDef DMA_InitStructure;

RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA2, ENABLE);//初始化DMA2的时钟

DMA_DeInit(DMA2_Channel3); //将DMA配置成默认值

DMA_InitStructure.DMA_PeripheralBaseAddr = DAC_DHR12R1;//指定DMA2通道3的目标地址为DAC1_DHR12R1

DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&Sine12bit;//指定DMA的源地址为数组Sine12bit

DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;//外设作为数据传输的目的地

DMA_InitStructure.DMA_BufferSize = sizeof(Sine12bit)/2;//DMA缓冲区大小

DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//外设机地址存器不变

DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //内存地址寄存器递增

DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;//外设数据宽度为半字

DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;//内存数据宽度为半字

DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;//工作在循环缓存模式,数据传输数为0时,自动恢复配置初值

DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;//非常高优先级

DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;//通道未被设置成内存到内存模式,与循环模式相对

DMA_Init(DMA2_Channel3, &DMA_InitStructure);//初始化DMA

DMA_DeInit(DMA2_Channel4);

DMA_InitStructure.DMA_PeripheralBaseAddr = DAC_DHR12R2;//指定DMA2通道3的目标地址为DAC2_DHR12R2

DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;

DMA_Init(DMA2_Channel4, &DMA_InitStructure);

DMA_Cmd(DMA2_Channel3, ENABLE); //使能DMA的channel3

DMA_Cmd(DMA2_Channel4, ENABLE); //使能DMA的channel4

}

首先需要说明的是为什么配置是DAM2,而且是DMA2的通道3与通道4。这是因为不同外设都对应不同的DMA与DMA的通道,他们的对应关系如下图所示:

上面的两幅图中看以找到,DAC通道1对应的是DMA2的通道3,DAC通道2对应DMA的通道4。

DMA_PeripheralBaseAddr = DAC_DHR12R1;这句话指定DAC外设的基地址,在宏定义中以已经给出了它的地址。这个外设基地址地址取值可以查询产参考《STM32参考手册》。我以DAC通道1为例:查询《STM32参考手册》的2.3节,可以查询到DAC的寄存器组地址范围为:0x40007400~0x00077FF,如下图所示:

然后可以参考《STM32的11.5.14节》查找DAC寄存器映像表,选择DAC_DHR12R1寄存器,查看其偏移为地址为0x08,如下图所示:

所以上面定义的DAC_DHR12R1=基地址+偏移地址=0x40007400+0x08=0x40007408。下面给出个寄存器的解释,可能过会有助于代码的理解:

DAC_CR:DAC控制寄存器(偏移:0x00)

DAC_SWTRIGR:DAC软件触发寄存器(偏移:0x04)

DAC_DHR12R1:DAC通道 1的 12位右对齐数据保持寄存器(偏移:0x08)

DAC_DHR12L1:DAC通道 1的 12位左对齐数据保持寄存器(偏移:0x0C)

DAC_DHR8R1:DAC通道 1的 8位右对齐数据保持寄存器(偏移:0x10)

DAC_DHR12R2:DAC通道 2的 12位右对齐数据保持寄存器(偏移:0x14)

DAC_DHR12L2:DAC通道 2的 12位左对齐数据保持寄存器(偏移:0x18)

DAC_DHR8R2:DAC通道 2的 8位右对齐数据保持寄存器(偏移:0x1C)

DAC_DHR12LD:双DAC的 12位左对齐数据保持寄存器(偏移:0x20)

DAC_DHR12RD:双DAC的 12位右对齐数据保持寄存器(偏移:0x24)

DAC_DHR8RD:双DAC的 8位右对齐数据保持寄存器(偏移:0x28)

DAC_DOR1:DAC通道 1数据输出寄存器(偏移:0x2C)

DAC_DOR2:DAC通道 2数据输出寄存器(偏移:0x30)

DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&Sine12bit;这句话指定了数据的源地址,也就是说DMA操作期间的数据都到这个地址中取。

DMA_InitStructure.DMA_BufferSize = sizeof(Sine12bit)/2;定义了DMA缓冲区的大小,缓冲区的大小应取与Sine12bit[]数组大小形同的空间。

后面还有其他一些代码就不细讲了,自己看注释。

最后还需要定义一个总函数:SineWave_Init(),它调用上面的这些函数来初始化与正弦波相关的代码:

/*************************************************************

Function : SineWave_Init();

Deion: 正弦波初始化

Input : none

return : none

*************************************************************/

void SineWave_Init(void)

{

SineWave_GPIO_Config(); //配置引脚

SineWave_TIM_Config(); //配置定时器

SineWave_DAC_Config(); //配置DAC

SineWave_DMA_Config(); //配置DMA

TIM_Cmd(TIM2, ENABLE); //打开TIM2

TIM_Cmd(TIM6, ENABLE); //打开TIM6

}

下载讲讲SineWave.h文件,它也仅仅声明了SineWave_Init()函数,以方便其他文件调用。

3、main.c函数编写

main函数需要调用上面定义的SineWave_Init()函数以初始化相关代码:

/*************************************************************

Function : main

Deion: main入口

Input : none

return : none

*************************************************************/

int main(void)

{

BSP_Init();

SineWave_Init();

PRINTF("\nmain() is running!\r\n");

while(1)

{

1_Toggle();

Delay_ms(1000);

}

}

4、测试

用 检查输出波形,可以检测到频率分别为800Hz与1600Hz的正弦波。其实这不是测试的重点,重点是观察使用Sine12bit[32]与Sine12bit[256]输出波形的区别。我采集输出频率为1600Hz的波形,分别使用Sine12bit[32]与Sine12bit[256],他们分别对应下面的第一张图与第二张图:

两张图对比中,可以看出使用Sine12bit[32]作为描点输出的波形中间有点断续,而使用Sine12bit[256]作为描点输出的正弦波则是非常地连续平滑的。

STM32 正弦波输出相关推荐

  1. STM32 PWM输出原理和直流电机PWM驱动原理详解及例程

    这次讲一讲STM32输出PWM的原理以及PWM可以作为信号传输数据的原理. PWM会有很多地方用到,有控制灯的亮度,控制电机,控制舵机或者其他一些外设,有时候你的单片机所输出的PWM是作为信号去控制, ...

  2. STM32 PWM输出之TIM_SetCompare函数的应用

    STM32 PWM输出之TIM_SetCompare函数的应用 想必输出PWM大家都应该会了吧,但是如何更加灵活方便的使用PWM,可能大家还存在着问题,今天我给大家介绍一下TIM_SetCompare ...

  3. 正弦波输出变压变频电源系统DSP(http://huarm.taobao.com/ )

    摘要:介绍了正弦波输出变压变频电源系统.对正弦波输出变压变频电源三种SPWM调制方式厦数字化控制策略进行了研究,以期得到一种较理想的调制方式,使变压变频电源的开关管损耗.可靠性及输出电压质量得以改善. ...

  4. STM32定时器输出PWM

    STM32定时器输出PWM 我们设置ARR值,就是设置CNT的上限,设置CCRX的值就是设置了一个临界点,CNT是一直随时间变化而变化的,当CNT>CCRX的时候输出 高/低电平 当CNT< ...

  5. STM32 DAC 输出正弦波、三角波、方波

    DAC可以将数字信号转换成模拟信号,在嵌入式系统开发中运用的十分广泛.在STM32实际运用中,可直接将数值映射成端口的电压值,通过大量的采样点输出,可达到输出指定波形的目的. 1.设置系统时钟(Clo ...

  6. 步进电机-STM32单片机定时器正弦波输出

    前面的文章介绍了单片机控制步进电机四相四拍.四相八拍的方式.用的是持续电平驱动,这种驱动方式电机的噪音大,震动明显,下面介绍正弦波驱动方式,这种方式能很好的解决噪音和震动问题. 首先需要准备下stm3 ...

  7. stm32定时器输出pwmIO口模拟pwm——呼吸灯

    文章目录 前言 一.pwm(脉冲宽度调制) 1.基本原理 2.PWM的优点 3.PWM波的控制方法 二.定时器的相关介绍 1.stm32定时器 2.通用定时器计数模式 3.定时器的基本工作原理 三.定 ...

  8. stm32 PWM输出学习

    STM32 的定时器除了 TIM6 和 7,其他的定时器都可以用来产生 PWM 输出.其中高级定时器 TIM1 和 TIM8 可以同时产生多达 7 路的 PWM 输出.通用定时器也能同时产生多达 4路 ...

  9. STM32 PWM输出(映射)

    STM32 的定时器除了 TIM6 和 7.其他的定时器都可以用来产生 PWM 输出.其中高级定 时器 TIM1 和 TIM8 可以同时产生多达 7 路的 PWM 输出.而通用定时器也能同时产生多达 ...

最新文章

  1. Python之MySQL数据库增删改查操作
  2. c++ string 长度限制_String 有多长?
  3. Java黑皮书课后题第9章:**9.13(Location类)设计一个名为Location的类,定位二维数组中的最大值及其位置。
  4. Oracle-计算岁数
  5. STM32 map文件解析
  6. Delphi下实现全屏快速找图找色 二、矩阵遍历
  7. QT消息,事件,槽的典型用法
  8. 2016.6.29 tomcat卸载后在安装出现错误:failed to install tomcat7 service
  9. 拼多多回应“刷单泛滥”一事:对此零容忍 呼吁全社会共同治理
  10. P5 MySQL数据类型和类型选择
  11. 【干货】前端工程与性能优化
  12. Makefile中的ifeq 多条件使用 ***
  13. MATLAB高斯迭代算法,matlab实现高斯赛德尔迭代法解方程组
  14. 马云卸任演讲全文:青山不改 绿水长流 后会有期
  15. 华为薪资等级结构表_2019年华为新员工薪酬 华为薪酬等级工资表
  16. tomcat的下载和启动
  17. 解决JDK13版本后IntelliJ IDEA导入新字体文件
  18. cocos2d-x的初步学习二十之坦克大战七
  19. 让代码审查扮演更好的角色
  20. 单片机硬件和软件延时、RTOS相对延时和绝对延时

热门文章

  1. QMI Ril和Android
  2. python中scatter函数中参数s解释
  3. Mysql主从同步搭建
  4. Java数据结构之堆
  5. redis 开启和关闭
  6. 51nod 1128 正整数分组 V2(二分)
  7. 自己整理的一张图,关于新个税专项附加扣除
  8. java中缺省包_Java 中的默认包问题
  9. H5+CSS3实现简单表情包
  10. 4、im2bw 和 imbinarize 的区别与图像分割的综合应用