完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
|
|
相关推荐
1个回答
|
|
要输出正弦波,需要好几个外设配合: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!rn"); while(1) { 1_Toggle(); Delay_ms(1000); } } 4、测试 用 检查输出波形,可以检测到频率分别为800Hz与1600Hz的正弦波。其实这不是测试的重点,重点是观察使用Sine12bit[32]与Sine12bit[256]输出波形的区别。我采集输出频率为1600Hz的波形,分别使用Sine12bit[32]与Sine12bit[256],他们分别对应下面的第一张图与第二张图: 两张图对比中,可以看出使用Sine12bit[32]作为描点输出的波形中间有点断续,而使用Sine12bit[256]作为描点输出的正弦波则是非常地连续平滑的。 |
|
|
|
只有小组成员才能发言,加入小组>>
调试STM32H750的FMC总线读写PSRAM遇到的问题求解?
1786 浏览 1 评论
X-NUCLEO-IHM08M1板文档中输出电流为15Arms,15Arms是怎么得出来的呢?
1622 浏览 1 评论
1089 浏览 2 评论
STM32F030F4 HSI时钟温度测试过不去是怎么回事?
730 浏览 2 评论
ST25R3916能否对ISO15693的标签芯片进行分区域写密码?
1680 浏览 2 评论
1942浏览 9评论
STM32仿真器是选择ST-LINK还是选择J-LINK?各有什么优势啊?
739浏览 4评论
STM32F0_TIM2输出pwm2后OLED变暗或者系统重启是怎么回事?
576浏览 3评论
599浏览 3评论
stm32cubemx生成mdk-arm v4项目文件无法打开是什么原因导致的?
561浏览 3评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-12-25 23:20 , Processed in 0.615679 second(s), Total 44, Slave 39 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号