定时器开关怎么设置的TRGO是什么事件

stm32&DAC输出WAV音频文件
&记录自己所做过的项目,鉴于自己是新手,做这个项目的时候只会GPIO口的配置输出点亮LED灯。写下来,记录自己的成长与进步。
1.&&WAV音频文件格式
WAV是微软公司开发的一种声音文件格式,它符合RIFF(Resource
Interchange File Format)文件规范,用于保存windows平台音频信息资源,被windows平台以及应用程序广泛支持。标准格式化的WAV文件和CD格式一样,因此在声音文件质量和CD相差无几!
通常使用三个参数来表示声音,量化位数,取样频率和采样点振幅。量化位数分为8位,16位,24位三种,声道分为单声道和立体声,单声道振幅数据为n*1矩阵点,立体声为n*2矩阵点,取样频率一般有11025Hz(11khz),22050Hz(22khz),44100Hz(44Khz)三种,不过尽管音质出色,但压缩后的文件体积过大!相对其他音频的格式而言是一个缺点,其文件大小的计算方式为:WAV格式文件所占容量(B)&=&(取样频率*量化位数(每个样本的位数)*声道数)*时间/8(字节)。
RIFF的格式:WAV符合RIFF文件规范,RIFF可以看成一种树状结构,其基本构成单位为chunk,犹如树状结构的节点,每个chunk由“辨别码”、“数据大小”及“数据”所组成如图1。
辨别码(4bytes)
数据大小(4bytes)
&&&&&&&&&&&&图1
chunk的结构示意图
辨别码由4个ASCII码所构成,数据大小则标示出紧随其后数据的长度(单位为byte),而数据大小本身也要用掉4bytes,所以事实上一个chunk的长度为数据大小加8字节。一般而言,chunk本身并不允许内部再包含chunk,但是有2种例外,分别为以“RIFF”和“LIST”位辨别码的chunk。征对这两种chunk,RIFF又从原先的“数据”中切出4个Bytes。这4个字节称为“格式辨别码”,然而RIFF又规定文件中仅能有一个以“RIFF”为辨别码的chunk,如图2.
RIFF/LIST标识符
格式/列表类型
&&&&&&&图2
RIFF/LIST块结构
&只要我们遵循此一结构的文件,我们均称之为RIFF档。此种结构提供了一种系统化的分类。如果和MS—DOS文件系统作比较,“RIFF”chunk就好比是一台硬盘的根目录,其格式辨别码便是此硬盘上的逻辑代码(C:或D:),而“LIST”chunk即为其下的子目录,其他的chunk则为一般的文件。至于在RIFF文件的处理方面,微软提供了相关的函数。视窗下的各种多媒体文件格式就如同磁盘机下规定仅能放怎么样的目录,而在该目录下仅能放何种数据。
&&&&WAV文件格式:WAVE文件是非常简单的一种RIFF文件,它的格式类型为“WAVE”如图3。RIFF块包含了两个子块,这两个子块的ID分别为“FMT”和“data”,其中“fmt”子块由结构PCMWAVEFORMAT所组成,其子块的大小就是sizeof(PCMWAVEFORMAT),数据组成就是PCMWAVEFORMAT结构中的数据。
标识符(RIFF)
格式类型“WAVE”
Sizeof(PCMWAVEFORMAT)
PCMWAVEFORMAT
数据声音的大小
&&&&&&&&&&&&图3&&&&&&WAVE文件的结构
&&&其实说了这么多,我自己也不是很懂,直接看转化之后的WAV文件吧。利用hexedit打开WAV音频文件,就拿自己用的,如图4
<img ALT="本地图片,请重新上传" STYLE="vertical-align:"
TITLE="stm32&DAC输出WAV音频文件" /><img ALT="图片" src="/blog7style/images/common/sg_trans.gif" real_src ="http://r.photo./psb?/V10xPoUq2THZ2t/9ZB1UE60toYZ4IRJV1WekvwTFfBO02fcTyxj6CmRqIw!/o/dPYAAAAAAAAA&bo=nwH2AJ8B9gADACU!" STYLE="vertical-align: width: 415 height: 246"
TITLE="stm32&DAC输出WAV音频文件" />
&&&&&&&图4&&&&WAV文件格式实例&&&
下面就说一下上面数字的意思。(首先说明一下WAV文件是小端格式的16进制的,低字节在前面,高字节在后面。不理解也没关系,待会儿看下面就知道了)。当然也可以通过软件插件修改直接看。
00H-03H&&&&&&RIFF,资源交换标志&&H
04H-07H&&&&&&从下一地址开始到文件尾的总字节数&000013ceH
08H-0BH&&&&&&“WAVE”代表WAV文件格式H
0CH-0FH&&&&&&FMT标志,波形格式标志&20746d66H
10H-13H&&&&&&PCM,sizeof(PCMWAVEFORMAT)
14H-15H&&&&格式类别&1:表示线性编码&&1表示有压缩的编码0001H
16H-17H&&&&通道数&1单声道&&2双声道&0001H&单声道
18H-1BH&&&&采样频率&H
1CH-1FH每秒数据量=通道数*采样频率*每个样本位数/8
20H-21H数据块的调整数=通道数*每个样本位数/8
22H-23H&&样本数据位数&0010&每个样本16位
24H-27Hdata数据标记符
28H-2BHWAV文件实际音频数据所占大小&&这里好像有点问题,不要在意
2CH-&&&&&数据
对我们最重要的a.通道数&单声道还是双声道
采样格式如图5
采样1&&&&&&&&&&&&&&&&&采样2
低字节&&高字节&&&&&&&&&&&&低字节&&&高字节
&&&&&&&&&&&&&&&&&采样1
&&&&&&左声道&&&&&&&&&&&&&&&&&右声道
低字节&&高字节&&&&&&&&&&&&低字节&&&高字节
&&&&&&&&&&&图5&采样格式
b.采样频率,根据采样频率设置定时器。c.数据所占大小。
WAV文件掌握上面就差不多了。
Adc即数/模转换器,一种将数字信号转换为模拟信号的装置。
STM32DAC是12位电压输出的数字/模拟转换器。DAC可以配置位或12位的模式,也可以与DMA控制器配合使用。DAC在12位模式工作时,数据可以被设置成左对齐或右对齐。可选择通过输出缓冲器来实现较高的驱动电流。
STM32DAC模块的主要特点有:
&#9312;&2个DAC转换器:每个转换器对应1个输出通道
&#9313;&8位或者12位单调输出
&#9314;&12位模式下数据左对齐或者右对齐
&#9315; 同步更新功能
&#9316; 噪声波形生成
&#9317; 三角波形生成
双DAC通道同时或者分别转换
每个通道都有DMA功能&&
&&&&&&&&&&&单个DAC通道的框图如下:
&&&&&&&&&&&&&&<img ALT="本地图片,请重新上传" STYLE="vertical-align:"
TITLE="stm32&DAC输出WAV音频文件" /><img ALT="图片" src="/blog7style/images/common/sg_trans.gif" real_src ="http://r.photo./psb?/V10xPoUq2THZ2t/p8LdVmcROkL6n1iK4*M57TtmQ4G4B.3XctB5gwuCv3U!/o/dKsAAAAAAAAA&bo=hAFNAYQBTQEDACU!" STYLE="vertical-align: width: 388 height: 333"
TITLE="stm32&DAC输出WAV音频文件" />
&&&&&&&&&&&图&7&&DAC通道模块框图
注:&一旦使能DAC&通道x,&相应的GPIO引脚(PA4&或PA5)&就会自动与DAC的模拟输出相连(DAC_
OUTx)。为了避免寄生的干扰和额外的功耗,PA4或PA5&引脚首先应设置成模拟输入(AIN)。
图中VDDA和VSSA为DAC模块模拟部分的供电,而Vref+则是DAC模块的参考电压。DAC_OUTx就是DAC的输出通道了(对应PA4或者PA5引脚)。
从图7看出,DAC输出是受DORx寄存器直接控制的,但是我们不能直接往DORx寄存器写入数据,而是通过DHRx间接的传给DORx寄存器,实现对DAC输出的控制。前面我们提到,STM32的DAC支持8/12位模式,8位模式的时候是固定的右对齐的,而12位模式又可以设置左对齐/右对齐。单DAC通道x,总共有3种情况:
&#9312;&8位数据右对齐:用户将数据写入DAC_DHR8Rx[7:0]位(实际是存入DHRx[11:4]位)。
&#9313;&12位数据左对齐:用户将数据写入DAC_DHR12Lx[15:4]位(实际是存入DHRx[11:0]位)。
&#9314;&12位数据右对齐:用户将数据写入DAC_DHR12Rx[11:0]位(实际是存入DHRx[11:0]位)。
&如果没有选中硬件触发(寄存器DAC_CR1的TENx位置’0’),存入寄存器DAC_DHRx的数据会在一个APB1时钟周期后自动传至寄存器DAC_DORx。如果选中硬件触发(寄存器DAC_CR1的TENx位置’1’),数据传输在触发发生以后3个APB1时钟周期后完成。一旦数据从DAC_DHRx寄存器装入DAC_DORx寄存器,在经过时间Testling之后,输出即有效,这段时间的长短依电源电压和模拟输出负载的不同会有所变化。我们可以从STM32F103ZET6的数据手册查到如果没有选中硬件触发(寄存器DAC_CR1的TENx位置’0’),存入寄存器DAC_DHRx的数据会在一个APB1时钟周期后自动传至寄存器DAC_DORx。如果选中硬件触发(寄存器DAC_CR1的TENx位置’1’),数据传输在触发发生以后3个APB1时钟周期后完成。&一旦数据从DAC_DHRx寄存器装入DAC_DORx寄存器,在经过时间之后,输出即有效,这段时间的长短依电源电压和模拟输出负载的不同会有所变化。我们可以从STM32F103ZET6的数据手册查到Testling的典型值为3us,最大是4us。所以DAC的转换速度最快是250K左右。
其转换框图如图8
<img ALT="本地图片,请重新上传" STYLE="vertical-align:"
TITLE="stm32&DAC输出WAV音频文件" /><img ALT="图片" src="/blog7style/images/common/sg_trans.gif" real_src ="http://r.photo./psb?/V10xPoUq2THZ2t/XrpxiCUAvzZGkdbgnjjWRjcaN7nE0jQC31Tmf8oouSU!/o/dPgAAAAAAAAA&bo=ewF4AHsBeAADACU!" STYLE="vertical-align: width: 379 height: 120"
TITLE="stm32&DAC输出WAV音频文件" />
&&&&图&8&&TEN=
0(不使用硬件触发),时DAC模块转化时间图
数字输入被线性地转换为模拟输出电压,其范围为0&到VDDA。&任一DAC通道引脚上的模拟输出电压满足以下关系:
VDDA&&DOR /
如果TENx&控制位被置1,DAC&转换可以由某一外部事件触发(&定时器计数器、外部中断线)。配置TSELx[2:0]控制位可以选择下表
的8&个触发事件之一触发DAC&转换。
来自片上定时器的内部信号
定时器3TRGO&事件
Reserved&保留
定时器15 TRG&事件
定时器2 TRGO&事件
Reserved&保留
AIEC&线路9
SWTRIG&软件触发
软件控制位
接下来简单复制一下STM32中文手册里DAC寄存器
a.C控制寄存器&(DAC_CR)
<img ALT="本地图片,请重新上传" STYLE="vertical-align:"
TITLE="stm32&DAC输出WAV音频文件" /><img ALT="图片" src="/blog7style/images/common/sg_trans.gif" real_src ="http://r.photo./psb?/V10xPoUq2THZ2t/dQwqCm2KoD*BC6vAtjsSGnA88wAxmVSzSq443ej3o3A!/o/dK0AAAAAAAAA&bo=OAJ6ADgCegADACU!" STYLE="vertical-align: width: 568 height: 122"
TITLE="stm32&DAC输出WAV音频文件" />
位&31:14&保留。
位13 DMAUDRIE1:
DAC&通道1 DMA&欠载中断使能
该位由软件设置和清除。
DMA&欠载中断关闭
DMA&欠载中断使能
位12 DMAEN1:
DAC&通道1 DMA&使能
该位由软件设置和清除。
0:&关闭DAC&通道1
1:&使能DAC&通道1
位11:8&保留。
位7:6&保留。
位5:3 TSEL1[2:0]:
DAC&通道1&触发器
通道1&触发选择
该位用于选择DAC&通道1&的外部触发事件.
000: Timer 6
001: Timer 8
010: Timer 7
011: Timer 5
100: Timer 2
101: Timer 4
110:&外部中断线9
111:&软件触发
注:该位只能在TEN1=
1(DAC&通道1&触发使能)时使用。
DAC&通道1&触发使能
该位由软件设置和清除,用来使能/&关闭DAC&通道1&的触发。
0:关闭DAC&通道1&触发,写入寄存器DAC_DHRx&的数据在1&个APB1时钟周期后传入寄存器DAC_DOR1;
1:使能DAC&通道1&触发,写入寄存器DAC_DHRx&的数据在3&个APB1时钟周期后传入寄存器DAC_DOR1。
注:当选择软件触发时,写入寄存器DAC_DHRx&的数据只需要1&个APB1&时钟周期就可以传入寄存器DAC_DOR1。
BOFF1:&关闭DAC&通道1&输出缓存
该位由软件设置和清除,用来使能/&关闭DAC&通道1&的输出缓存。
0:使能DAC&通道1&输出缓存;
1:关闭DAC&通道1&输出缓存。
EN1:&:DAC&通道1&使能
该位由软件设置和清除,用来使能/&关闭DAC&通道1。
0:关闭DAC&通道1;
1:使能DAC&通道1。
还有些寄存器就不复制了,自己可以去手册上看。
DAC的使用步骤:
1.&&开启PA口时钟。设置PA4位模拟输入
STM32F051X的DAC通道1在PA4上,所以要先使能PORTA的时钟,然后设置PA4为模拟输入。DAC本身为输出,配置位模拟输入时因为上面注里面写的有,因为一旦使能DACX通道之后,相应的GPIO引脚会自动与DAC的模拟输出相连,设置为输入,是为了避免额外的干扰。
使能GPIOA时钟:
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE
设置PA4为模拟输入:
GPIO_InitStructure.GPIO_Mode&&&&=&&&GPIO_Mode_AN
2.使能DAC时钟。
同其他外设一样,要想使用,必须先开启对应的时钟。STM32的DAC模块时钟是由APB1提供的,所以调用函数
RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC,
来使能DAC时钟。
3.初始化DAC,设置DAC的工作模式。
&&&该部分设置全部通过DAC_CR寄存器实现,包括DAC通道1使能,DAC通道输出缓存,不使用触发等设置。DAC初始化时通过函数完成的
void DAC_Init(uint32_tDAC_Channel, DAC_InitTypeDef*
DAC_InitStruct);
我们先来看看参数设置结构体类型DAC_InitTypeDef的定义:
typedef struct
DAC_LFSRUnmask_TriangleA
uint32_t&;
}DAC_InitTypeD
成员的4个变量:
第一个参数DAC_Trigger用来设置是否使用触发功能。
第二个参数DAC_WaveGeneration用来设置是否使用波形发生。
第三个参数DAC_LFSRUnmask_TriangleAmplitude用来设置屏蔽/增幅选择器,这个变量只在使用波形发生器的时候才用。
第四个参数DAC_OutputBuffer用来设置输出缓存位。
设置完以后要初始化一次。
4使能DAC转换通道
初始化DAC之后,使能DAC转换通道,库函数办法是:
DAC_Cmd(DAC_Channel_1,ENABLE);
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&5.设置DAC的输出值
通过前面的4个步骤的设置,DAC就可以开始工作了,我们使用12位右对齐的数据格式,所以我们通过设置DHR12R1,就可以在DAC输出引脚得到不同的电压值了,库函数的函数时:
DAC_SetChannel1Data(DAC_Align_12b_R,0);
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&我们还可以读出DAC的数值,函数是:
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&DAC_GetDataOutputValue(DAC_Channel_1);
&&&&&&&&&&&&&&&&&&&&&&&&&&&&定时器触发和DMA没有用到所以就不写了。网上资料很多。
3.&&定时器中断
STM32的定时器功能十分强大,有TIME1和TIME8等高级定时器,也有TIME2~TIME5等通用定时器,还有TIME6和TIME7等基本定时器。
STM32的通用定时器是一个通过可编程预分频器(PSC)驱动的16位自动装载计数器(CNT)构成。STM32的通用定时器可以被用于:测量输入信号的脉冲长度(输入捕获)或者产生输出波形(输出比较和PWM)等。 使用定时器预分频器和RCC时钟控制器预分频器,脉冲长度和波形周期可以在几个微秒到几个毫秒间调整。STM32的每个通用定时器都是完全独立的,没有互相共享的任何资源。
STM3的通用TIMx (TIM2、TIM3、TIM4和TIM5)定时器功能包括:
1)16位向上、向下、向上/向下自动装载计数器(TIMx_CNT)。
2)16位可编程(可以实时修改)预分频器(TIMx_PSC),计数器时钟频率的分频系数为1~65535之间的任意数值。
3)4个独立通道(TIMx_CH1~4),这些通道可以用来作为:
A.输入捕获
B.输出比较
C.PWM生成(边缘或中间对齐模式)
D.单脉冲模式输出
4)可使用外部信号(TIMx_ETR)控制定时器和定时器互连(可以用1个定时器控制另外一个定时器)的同步电路。
5)如下事件发生时产生中断/DMA:
A.更新:计数器向上溢出/向下溢出,计数器初始化(通过软件或者内部/外部触发)
B.触发事件(计数器启动、停止、初始化或者由内部/外部触发计数)
C.输入捕获
D.输出比较
E.支持针对定位的增量(正交)编码器和霍尔传感器电路
F.触发输入作为外部时钟或者按周期的电流管理
关于定时器的各个寄存器就不介绍了,手册上都有。
首先要提到的是,定时器相关的库函数主要集中在固件库文件stm32f10x_tim.h和stm32f10x_tim.c文件中。
1.TIM3时钟使能。
&&&&TIM3是挂载在APB1之下,所以我们通过APB1总线下的使能使函数来使能TIM3。调用函数:
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);
2.初始化定时器参数,设置自动重装值,分频系数,计数方式等。
在库函数中,定时器的初始化参数都是通过初始化函数TIM_TimeBaseInit实现的:
voidTIM_TimeBaseInit(TIM_TypeDef*TIMx,TIM_TimeBaseInitTypeDef*TIM_TimeBaseInitStruct);
第一个参数确定哪个定时器,第二个参数是定时器初始化参数结构体指针。结构体的定义为:
&&&typedefstruct
uint16_t TIM_P
uint16_t TIM_CounterM
uint16_t TIM_P
uint16_t TIM_ClockD
uint8_t TIM_RepetitionC
} TIM_TimeBaseInitTypeD
这个结构体一共有5个变量,要说明的是,对于通用定时器只有前面四个参数有用,最后一个参数TIM_RepetitionCounter是高级定时器才用的,就不用多解释,以为我也不知道有啥子用。
第一个参数TIM_Prescaler是设置分频系数的。
第二个参数TIM_CounterMode是设置技术方式,可以设置为向上计数,向下计数,还有中央对齐计数方式。
第三个参数TIM_Period是设置自动重载计数周期值。
第四个参数TIM_ClockDivision用来设置时钟分频因子。
3.设置TIM3_DIER允许更新中断。
&&&&&&&&&因为我们要使用TIM3的更新中断,寄存器的相应位便可使能更新中断,在库函数里面定时器中断使能是通过TIM_ITConfig函数来实现的:
void TIM_ITConfig(TIM_TypeDef* TIMx,uint16_t TIM_IT,
FunctionalState NewState);
第一个参数表示定时器号。第二个参数非常关键,用来指明我们使能的定时器中断的类型,定时器中断的类型很多种,包括更新中断TIM_IT_Update,触发中断TIM_IT_Trigger,以及输入捕获中断等等。第三个参数就是使能还是失能。例如我们要设置定时器3更新中断使能。
TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE);
4.TIM3中断优先级设置。
&&&&&&&&&在定时器中断使能之后,因为要产生中断,必不可少的要设置NVIC相关寄存器,设置中断优先级。之前多次讲解到用NVIC_Init函数实现中断优先级的设置。具体的可以参考手册,我也没看过。首先要讲解的是中断优先级分组函数NVIC_PriorityGroupConfig,函数申明如下:voidNVIC_PriorityGroupConfig(uint32_t
NVIC_PriorityGroup);这个函数的作用是对中断优先级进行分组,这个函数在系统中只能被调用一次,一旦分组确定最好不要改。这个函数我们可以找到其实现:
&&&&&&&void
NVIC_PriorityGroupConfig(uint32_tNVIC_PriorityGroup)
assert_param(IS_NVIC_PRIORITY_GROUP(NVIC_PriorityGroup));
SCB-&AIRCR= AIRCR_VECTKEY_MASK |
NVIC_PriorityG
从函数体可以看出,这个函数唯一目的就是通过设置SCB-&AIRCR寄存器来设置中断优先级分组。而其入口参数通过双击选中函数体里面的“IS_NVIC_PRIORITY_GROUP”然后右键“Go
todefition of&…”可以查看到为:
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&#defineIS_NVIC_PRIORITY_GROUP(GROUP)
(((GROUP) == NVIC_PriorityGroup_0) ||
((GROUP) == NVIC_PriorityGroup_1) || \
((GROUP) == NVIC_PriorityGroup_2) || \
((GROUP) == NVIC_PriorityGroup_3) || \
((GROUP) == NVIC_PriorityGroup_4))
比如我们设置整个系统的中断优先级分组值为2,那么方法是:
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
这样就确定了一共为“2位抢占优先级,2位响应优先级”在看看中断结构体的成员变量:
typedefstruct
uint8_tNVIC_IRQChannelPreemptionPrio
uint8_tNVIC_IRQChannelSubP
FunctionalState;
}NVIC_InitTypeD
结构体中间有3个成员变量:
NVIC_IRQChannel定义初始化的是哪个中断,我们可以在stm32f10x.h中找到每个中断对应的名字。
NVIC_IRQChannelPreemptionPriority:定义这个中断的抢占优先级别。
NVIC_IRQChannelSubPriority:定义这个中断的子优先级别。
NVIC_IRQChannelCmd:该中断是否使能。
比如我们要使能串口1中断,同时设置抢占优先级为1,子优先级为2,初始化的方法为:
USART_InitTypeDefUSART_InitS
NVIC_InitStructure.NVIC_IRQChannel=
USART1_IRQn;//串口1中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1;//&抢占优先级为1
NVIC_InitStructure.NVIC_IRQChannelSubPriority=
2;//&子优先级位2
NVIC_InitStructure.NVIC_IRQChannelCmd= ENABLE;
//IRQ通道使能
NVIC_Init(&NVIC_InitStructure);
总结一下中断优先级设置的步骤:
1.&系统运行开始的时候设置中断分组。确定组号,也就是确定抢占优先级和子优先级的分配位数。调用函数为NVIC_PriorityGroupConfig();
2.&设置所用到的中断的中断优先级别。对每个中断调用函数为NVIC_Init();
5.允许TIM3工作,也就是使能TIM3。
&&&&&&&&&光配置好定时器还不行,没有开启定时器,照样不能用。我们在配置完后要开启定时器,通过TIM3_CR1的CEN位来设置。在固件库里面使能定时器的函数是通过TIM_CMD函数来实现的:
&&&&&&&&&&&&&&&&&&&&&&&&&&voidTIM_Cmd(TIM_TypeDef*
TIMx, FunctionalState NewState);
&&&比如我们要使能TIM3:
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&TIM_Cmd(TIM3,ENABLE);&&&&&&&&&&&&&&
6.编写中断服务函数。
&&&&&&&&&在最后,还是要编写定时器中断服务函数,通过该函数来处理定时器产生的相应中断。在中断产生后,通过状态寄存器的值来判断此次中断属于什么类型。然后执行相关的操作,我们这里使用的是更新中断,所以在状态寄存器SR的最低位。处理完中断之后应向TIM_SR最低位写0,用来清除中断标志。
在固件库函数里面,用来读取中断状态寄存器的值判断中断类型的函数是:
&&&&&&&&&&&&&&&&&&&&&&&&&&&&ITStatusTIM_GetITStatus(TIM_TypeDef*
TIMx, uint16_t);
该函数的作用是,判断定时器TIMX的中断类型,TIM_IT是否发生中断,比如,我们要判断定时器3是否发生更新中断,方法为:
&&&&&&&&&&&&&if
(TIM_GetITStatus(TIM3, TIM_IT_Update) !=RESET){}
固件库中清楚中断标志位的函数是:
&&&&&&&&void
TIM_ClearITPendingBit(TIM_TypeDef* TIMx, uint16_t
该函数的作用是,清除定时器TIMX的中断TIM_IT标志位,用起来十分简单,比如在发生更新中断后,我们要先清楚中断的标志位(这个是根据寄存器来用的),方法是:
&&&&&&&&TIM_ClearITPendingBit(TIM3,TIM_IT_Update
4.&实现思路
将MAV文件,2CH之后的数据复制,并转化为0X格式,注意数据大小,复制到TXT里时,00会变为20,20不会变,我也不知道怎么回事,不过把20变为00,好像影响不大。也可以直接复制到keil里,后面同学说的。因为学的不多,对内存不了解,只知道不能装很大的数据,最多50KB就是50000个数据,25000个采样量。然后创建成一组数组。通过对定时器的设置,设置频率与采样频率相同(其实不同也可以,但是差别太多,失真严重,频率越快,越急促),通过中断进行DAC采样,给DAC输入数据。DAC再将电压送给喇叭,就放出了音频。因为WAV数据就是数字信号,大神告诉我的。
项目电路图:
<img ALT="本地图片,请重新上传" STYLE="vertical-align:"
TITLE="stm32&DAC输出WAV音频文件" />
<img ALT="图片" src="/blog7style/images/common/sg_trans.gif" real_src ="http://r.photo./psb?/V10xPoUq2THZ2t/.doy8V0OH8uX5RpAXEQGiX5R8vWiOPvEj5ypnJ3KSS4!/o/dLIAAAAAAAAA&bo=mQFCAZkBQgEDACU!" STYLE="vertical-align: width: 409 height: 322"
TITLE="stm32&DAC输出WAV音频文件" />
<img ALT="图片" src="/blog7style/images/common/sg_trans.gif" real_src ="http://r.photo./psb?/V10xPoUq2THZ2t/lLBD2w1pVwB6OQt8PAbkdwD3qd9BdCZnyMR3c7DfdXc!/o/dKUAAAAAAAAA&bo=uAGyALgBsgADACU!" STYLE="vertical-align: width: 440 height: 178"
TITLE="stm32&DAC输出WAV音频文件" />
<img ALT="本地图片,请重新上传" STYLE="vertical-align:"
TITLE="stm32&DAC输出WAV音频文件" />
中间的芯片MSOP8A我也不知道干嘛的
,无视就好。
接下来是项目的代码:
#include"stm32f0xx.h"
&&&&&&&&&&&&&&voiddac_init(void);
gpioa_init(void);
DAC_data_Conven(u16data);
tim3_init(uint32_t t);
void Dac_Start(u8
,uint32_tt);
&&&&&&&&&&&&&&#endif
&&&&&&&&&&&&&&Dac.c文件
&&&&&&&&&&&&&&#include"dac.h"
&&&&&&&&&&&&&&
&&&&&&&&&&&&&&&&&&const
u8wav2_buf[]={0x4e….};
&&&&&&&&&&&&&&&&&&&u8
volume&&=20;&&&//可以调节音量
&&&&&&&&&&&&&&&&&&&u16DApc&&=0;&&&&&//数组下标
&&&&&&&&&&&&&&&&&&&&
&&&&&&&&&&&&
&&&&&&&&&&&&&&voidgpioa_init(void)
&&&&GPIO_InitTypeDef&&GPIO_InitS&
&&&&RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA,
ENABLE); //使能GPIOA
&&&&GPIO_InitStructure.GPIO_Pin&&&&&&&&=&&&GPIO_Pin_4;&&&&//P4口
&&&&GPIO_InitStructure.GPIO_Mode&&&&&&&=&&&GPIO_Mode_AN;&&&&//模拟输入
&&&&GPIO_InitStructure.GPIO_Speed&&&&&&=&&&GPIO_Speed_50MHz;&//速度50MHZ
&&&&GPIO_Init(GPIOA,&GPIO_InitStructure);
//初始化GPIOAP4口
&&&&GPIO_InitStructure.GPIO_Pin&&&&&&&&=&&&GPIO_Pin_8;&&//P8口
&&&&GPIO_InitStructure.GPIO_Mode&&&&&&&=&&&GPIO_Mode_OUT;&&&//输出模式
&&&&GPIO_InitStructure.GPIO_OType&&&&&&=&&&GPIO_OType_PP;//推挽输出
&&&&GPIO_Init(GPIOA,&GPIO_InitStructure);&&//初始化GPIOAP8口
-&ODR|=1&&8;&&&&&&&&&&&&&&&&&&&&//将PA8口置位,输出1,好像是打开喇叭的,不用理会,不然不响,因为这是我的电路图
dac_init(void)
&&&&DAC_InitTypeDef&&&DAC_InitS
&&&&RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC,ENABLE);
//使能DAC时钟
&&&&DAC_DeInit(&&);//清除DAC配置
&&&&DAC_InitStructure.DAC_Trigger&&&&&&=&&&DAC_Trigger_None&&&;&&&&&&&&&&&&//触发方式关闭
&&&&DAC_InitStructure.DAC_WaveGeneration&&&&&&=&&&DAC_WaveGeneration_None&&;&&&&//不生成波形
&&&&DAC_InitStructure.DAC_LFSRUnmask_TriangleAmplitude=D
AC_LFSRUnmask_Bit0;&
&&&&DAC_InitStructure.DAC_OutputBuffer&&&&&=DAC_OutputBuffer_Disable
;&//&输出缓存关闭
&&&&DAC_Init(DAC_Channel_1,&DAC_InitStructure);
//初始化DAC1
&&&&DAC_Cmd(DAC_Channel_1,ENABLE);&&//使能DAC1&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
tim3_init(uint32_t t)
&&&TIM_TimeBaseInitTypeDef&TIM_TimeBaseInitS
&&&NVIC_InitTypeDef
NVIC_InitS
&&&RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,
ENABLE);&&&&&&&&&&&&&&&//使能TIM3
&&&TIM_DeInit(TIM3);
&&&TIM_TimeBaseInitStructure.TIM_Prescaler
24-1;&&&&//分频系数24&&&2MHZ
&&&TIM_TimeBaseInitStructure.TIM_CounterMode&&&&&=&&&TIM_CounterMode_Up;&&//向上计数
&&&TIM_TimeBaseInitStructure.TIM_Period
t;&&&&//计数&&
&&&TIM_TimeBaseInitStructure.TIM_ClockDivision
TIM_CKD_DIV1;&&&//分频因子&&0&&48MHZ&&我用的是48
&&&TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStructure
&&&&&&&&&&//初始化TIM3&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
&&&TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE);&&&&&&&&&&&&&&&&&&&&&&&&
&&&&&&&&&&&//允许更新中断&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
&&&TIM_Cmd(TIM3,
ENABLE);&&&&&&//使能TIM3&&&&&&&&&&&&&&&&&&
&&&NVIC_InitStructure.NVIC_IRQChannel
= TIM3_IRQn;
//TIM3中断
&&&NVIC_InitStructure.NVIC_IRQChannelPriority
//中断优先级那些,我的库函数只有这一个
&&&NVIC_InitStructure.NVIC_IRQChannelCmd
//使能中断
&&&NVIC_Init(&NVIC_InitStructure);
&&&//中断初始化
DAC_data_Conven(u16data)
&&&&DAC_SetChannel1Data(DAC_Align_12b_R,
&&&&DAC_SoftwareTriggerCmd(DAC_Channel_1,ENABLE);
TIM3_IRQHandler(void)
&&&&&&&&&&&&&
&&&&&&&&&&&&&
&&&&&&&&&&&&&&u16
(TIM_GetITStatus(TIM3, TIM_IT_Update) !=RESET)
//判断是否中断,发生中断硬件会置1
&&&&&&&&TIM_ClearITPendingBit(TIM3,TIM_IT_Update);
//清除中断标志
&&&&&&&temp=(((u8)(wav2_buf[DApc+1]-0x80)&&4)|(wav2_buf[DApc]&&4))*10///取2个8位的高12位,至于减去0X80,我现在也没懂,求大神解答
&&&&&&&&DAC_data_Conven(temp);//发送数据这里是同事写的,至于为什么用到了软件触发,我也不知道,软件触发后,会发送数据,需要通过人工复位。就是下面这句
&&&&&&&&DAC_SoftwareTriggerCmd(DAC_Channel_1,DISABLE);
&&&&&&&&DApc+=2;
&&&&&&&&if(DApc==sizeof(wav2_buf))
&&&&&&&&&&&&DApc=0;&
6.项目总结
这次项目是在我仅有GPIO配置经验的情况下进行的,对STM32接触不到1天的情况下,仅了解了跑马灯的做法。一开始是很困难的,因为没有思路,不知道WAV文件其实就是一堆16进制的数字啊可以直接转换为模拟量啊。一开始我按照网上的DAC配置之后,发现没有效果。在了解到WAV文件后想到了中断,一开始中断没用,后面的同学给我了他的代码。然而其实都一样的,但是总算中断起作用了,接下来就是传数据了,第一次传没有将高字节,低字节合在一起,所以失败第二次就成功了。虽然很多都不懂,但是总是做出来了,也看了网上很多种做法,其实我都不理解。大神的世界就是不一样。不过也要感谢后面同学和艹同学还有经理的帮助。
已投稿到:
以上网友发言只代表其个人观点,不代表新浪网的观点或立场。

我要回帖

更多关于 定时器插座 的文章

 

随机推荐