|
楼主 |
发表于 2021-8-8 21:34:49
|
显示全部楼层
本帖最后由 SUPER_CRJ 于 2021-8-8 21:39 编辑
二楼,占楼,自己修改的代码。
beepMusic.c文件如下:
- #include "beepMusic.h"
- //触发发声标志,这不是布尔量,而是选择歌曲的曲目数字。
- //为什么叫:triger,因为选择曲目后,就会变为0。就是一个triger一样。
- u8 Music_Vol=137; //音量
- vu8 Music_Triger=0; //触发播放哪一支,例如播放“叮~~~”,则任意地方调用 Music_Triger=MUSIC_DING;
- u8 flg_MusicPlaying=0; //非零播放中。
- //乐曲表指针
- // 下面指针表示,指向的数据内容不变!
- u16 const* music_frq_tab;
- u8 const* music_interval_tab;
- //定义不同的乐曲数组,0 结束
- //频率表,对应BUZ-H的输出频率,0 结束
- const u16 Music1_FrqTab[]={_18,_18,_21,_21,_24,_24,_28,_28,0,};
- //BUZ-L输出时间,偶数高电平,奇数为低电平尾音,x4ms,0 结束
- // 时间上要乘4ms,为什么是4ms?为什么不是其他数字?
- // 当然:关于时间中断上,最好是时间越大越好。减少中断的次数。
- // 当然:1ms的时间中断根据μc/OS-Ⅲ和一些经常需要1ms调用上,时间上是可以接受的。
- // 发现也有用10ms的,beep的时间规范可能不是那么明显。一般音调都是有时间,人是取值不同而已。
- // 猜测应该是这个公约数好约?当然人对于时间的敏感显然比较好处理?不够敏感!
- // 还有个问题:关于时间,为什么最后一个的时间都是:255(255*4=1020ms,这应该是准备最后一个不断降低的“伪和弦”)
- // 查看了些文档最后一个音调都比较长,应该是需要最后一个有慢慢降低的感觉。
- const u8 Music1_TimeTab[]={ 20,30,20,30,20,30,20,255,0,};
- const u16 Music2_FrqTab[] = {_21,_21,_22,_22,_24,_24,0};
- const u8 Music2_TimeTab[] = {10,18,10,18,10,255,0,};
- const u16 Music3_FrqTab[]={_24,_24,0,};
- const u8 Music3_TimeTab[] ={ 18,255,0,};
- const u16 Music4_FrqTab[] ={_28,_28,_24,_24,_21,_21,0};
- const u8 Music4_TimeTab[]={ 10,18,10,18,10,255,0,};
- const u16 Music5_FrqTab[] ={_16,_16,_18,_18,_21,_21,0};
- const u8 Music5_TimeTab[] ={6,16,6,16,6,255,0,};
- const u16 Music6_FrqTab[] ={_21,_21,_18,_18,_16,_16,0};
- const u8 Music6_TimeTab[] ={6,16,6,16,6,255,0,};
- // 这是超级玛丽的1UP音乐。
- // 可以看出,以前的游戏音乐存储是真的简单和少。
- const u16 Music7_FrqTab[]={1324,1324,1574,1574,2645,2645,2114,2114,2347,2347,3154,3154,0,};
- const u8 Music7_TimeTab[]={15,25,15,25,15,25,15,25,15,25,15,255,0,};
- /**
- * \b 设置音量,根据分析,其音量大小为占空比的大小。具体逻辑要进行分析!
- * 分析到:应该到一定程度没有到0就应该不行了。大到一定程度就也应该声音就模糊了。
- 下面的代码就应该要具体分析了。
- 突然想到以后:是不是应该要做低功耗应用!蜂鸣器其实挺浪费电的。
- 其实这个程序能用,但是相对也比较混乱,单纯的配置声音可以,放入了一些其他过多的东西!
- */
- void Music_SetVolumn(u8 vol)
- {
- u32 lTmp;
- lTmp=TIM3->ARR.AutoReloadValue_RW;
- lTmp*=vol;
- lTmp/=255;
- TIM3->CCR3.CaptureCompareValue_RW = lTmp; // 设置到这里就可以了!
- }
- /**
- * \b 改变频率,务必导出其中的算法,中间还带有音量
- 做音乐的一个比较重要的应该是计算出任意频率了
- 之前一直没有做任意频率的,这次应该做了。
-
- 实际计算方法:由于定时器设计关于,假设已知:定时器入口频率:tFre(HZ),需要生成的频率:pwmFre,占空比大小:duty(0-255大小)
- 需要计算:ARR值与CCR值。
- 根据下面两个计算pwm周期的两个公式。
- Tpwm = 1/pwmFre
- Tpwm = (ARR+1)*(1/tFre)
- 最终得到:ARR = ( tFre/pwmFre ) - 1;
- 而占空比(注意有效电平,这里假设有效电平:1):
- CCR = duty/255*ARR值
-
- 经常计算后:下面的设计满足要求。(当然,实际中要注意计算只能保留整数,但是:蜂鸣器控制的音乐,其精度是完全足够的!)
- 一个重要性能:使用固定的频率输入,其生成的PWM频率范围是多少
- pwmFre = tFre/(ARR+1) 其中ARR范围:1-0xFFFF (0不应该被纳入其中)
- 当:tFre为7.2Mhz时:范围:109HZ - 3.6MHZ,显然这个范围下,时钟是完全够用的。
- 注意:里面同时改变了频率和声音的大小!
- */
- void Music_ChangeFrq(u16 frq)
- { //改变频率,与音量
- u32 clk;
- clk=7200000; // 7.2M hz的输出频率,就用这个时钟进行计算
- clk/=frq; //
- clk--;
- /* Time base configuration */
- TIM3->ARR.AutoReloadValue_RW = clk; // 这里应该可以导出频率
- clk*=Music_Vol;
- clk/=255;
- TIM3->CCR3.CaptureCompareValue_RW = clk;
- }
- // 之前使用的是PA0,TIM2_CH1-ETR引脚(看来这个CH1-ETR引脚也可以使用PWM输出!)
- void Music_Frq_Enable(void){
- TIM3->CCER.CaptureCompareOutputEnable3_RW = 1; // 使能捕获比较通道输出
- }
- void Music_Frq_Diable(void){
- TIM3->CCER.CaptureCompareOutputEnable3_RW = 0; // 关闭输出
- }
- /**
- * \b IO口引脚初始化、定时器初始化
- 使用了:PB0,TIM3-CH3
- */
- void Music_Init(void)
- {
- { // PB0,TIM3-CH3引脚初始化
- RCC->APB2ENR.IOPBClockEnable_RW = ENABLE;
- GPIOB->CRL.InOutMode0_RW = GPIO_CR_INOUNTMODE_OUTPUT_50MHZ;
- GPIOB->CRL.PinConfig0_RW = GPIO_CR_PINCONFG_OUT_ALTERNATE_PUSHPULL;
- { // 供电控制引脚,此处没有对应的硬件电路,这里保持为空,为了日后方便升级这里结束
- }
- BEEPL_1;
- }
- { // 定时器初始化
- RCC->APB1ENR.TIM3ClockEnable_RW = 1;// 36MHZ-APB1时钟,定时器倍频到72MHZ
- TIM3->PSC.PrescaleValue_RW = 0x09; // 产生7.2M给定时器使用
- TIM3->CR1.UpdateRequestSource_RW = 0;
- TIM3->CR1.AutoReloadPreloadEnable_RW = 1; // 需要带缓冲
- {// 公用部分
- // 频率表:越大频率越小,最后使用
- TIM3->ARR.AutoReloadValue_RW = 0xFFFF; // 这里给了最大值
- TIM3->CR1.Direction_RW = 0; // 向上计数模式(默认值)
- TIM3->CR1.Direction_RW = 0; //向上计数
- TIM3->CR2.All = 0; // 默认值
- TIM3->SMCR.All = 0; // 默认值
- TIM3->DIER.All = 0; // 默认值
- }
- { //
- TIM3->Output_CCMR2.OutputCompareMode3_RW = _0b0111; // 巧了,都是3
- TIM3->Output_CCMR2.OutputComparePreloadEnable3_RW = 1;
- TIM3->CCER.CaptureCompareOutputPolarity3_RW = 1; // 高电平有效果
- TIM3->CCER.CaptureCompareOutputEnable3_RW = 0; // 关闭输出
- TIM3->CCR3.CaptureCompareValue_RW = 0xFFFF; // 这里也给了最大值
- }
- // 使能一次
- TIM3->EGR.UpdateGeneration_W = 1; //
- TIM3->CR1.CountEnable_RW = 1;
- }
-
- // 当没有使能时钟的时候,这个设置没有意义。
- Music_ChangeFrq(4000); // 为什么设置4000HZ的频率!
- Music_Frq_Diable();
- }
- void Music_Select(void)
- {
- switch(Music_Triger){
- case MUSIC_PWR_UP:
- music_frq_tab=Music1_FrqTab;
- music_interval_tab=Music1_TimeTab;
- break;
- case MUSIC_TURN_ON:
- music_frq_tab=Music2_FrqTab;
- music_interval_tab=Music2_TimeTab;
- break;
- case MUSIC_DING:
- music_frq_tab=Music3_FrqTab;
- music_interval_tab=Music3_TimeTab;
- break;
- case MUSIC_TURN_OFF:
- music_frq_tab=Music4_FrqTab;
- music_interval_tab=Music4_TimeTab;
- break;
- case MUSIC_UP:
- music_frq_tab=Music5_FrqTab;
- music_interval_tab=Music5_TimeTab;
- break;
- case MUSIC_DOWN:
- music_frq_tab=Music6_FrqTab;
- music_interval_tab=Music6_TimeTab;
- break;
- case MUSIC_1UP:
- music_frq_tab=Music7_FrqTab;
- music_interval_tab=Music7_TimeTab;
- break;
- default:// 发现楼主喜欢:把default放到最后
- flg_MusicPlaying=0;
- return;
- }
- flg_MusicPlaying=1;
- }
- /**
- * \b 核心程序,每4ms调用一次
- 如果被不及时调用会使声音被拉长。4ms的一些应用不算什么。但是也比较占用了时间了!
- 这里就是整个函数的处理核心了。
- 由于代码都是我自己开发,自己使用,所以第个拿来的模块,都应该了解其中的原理,小心使用。完全的懂了,才方便使用!
- 根据分析:里面没有阻塞操作,时间运行比较快,可以放在中断中处理!
- */
- void Music_Srv(void)
- {
- static u8 PlayStep=0; // 这个用来指示频率运行到哪个步骤。
- static u8 PlayCt=0; // 用于计数播放多长时间。
- u8 cTmp;
- if(Music_Triger){
- Music_Select(); // 选择音乐
- Music_Triger=0; // 关闭扳机
- PlayStep=0; // 清零计数
- PlayCt=0; // 清零计数
-
- Music_ChangeFrq(music_frq_tab[PlayStep]); // 这里就是要求快速变换频率。
- Music_Frq_Enable(); // 使能输出
- BEEPL_1; // 开启蜂鸣器电源,就等于输出声音了。
- }
- if(flg_MusicPlaying){ // 当triger之后,这个就使能了。
- cTmp=music_interval_tab[PlayStep]; // 获取对应播放的时间。
- PlayCt++;
- if(PlayCt>=cTmp){ // 当对应的时间到达后,进行切换
- PlayCt=0; // 计数清零
- PlayStep++; // 切换到下一个标签
- Music_ChangeFrq(music_frq_tab[PlayStep]); // 改变频率
- if(music_interval_tab[PlayStep]==0){ // 遇到0表示结束。
- flg_MusicPlaying=0;
- }
- else{ // 可以理解为:0/1的来回切换。用于产生渐低音!
- BEEPL_XOR;
- }
- }
- }
- else{ // 否则认为不在播放
- BEEPL_0;
- Music_Frq_Diable();
- }
- }
复制代码
beepMusic.h 文件
- /**
- * @brief F1 定时器音乐
- * @Author
- */
- #ifndef __BEEP_MUSIC__
- #define __BEEP_MUSIC__
- #include "stm32f10x.h"
- #include "string.h"
- //BUZ-L定义IO
- //#define BEEPL_1 GPIOA_DSRR=BIT1
- //#define BEEPL_0 GPIOA_DRR=BIT1
- //#define BEEPL_XOR {if(BitTst(GPIOA_ODR,BIT1)) BEEPL_0; else BEEPL_1;}
- // 由于没有启用电源电容控制,这些设置为空,同时为了保留设计,此处保留!
- #define BEEPL_1 (0) // 使能蜂鸣器供电
- #define BEEPL_0 (0) // 关闭蜂鸣器供电
- #define BEEPL_XOR (0) //
- //音乐声定义
- #define MUSIC_PWR_UP 1
- #define MUSIC_TURN_ON 2
- #define MUSIC_DING 3
- #define MUSIC_TURN_OFF 4
- #define MUSIC_UP 5
- #define MUSIC_DOWN 6
- #define MUSIC_1UP 7
- // 定义常用频率,数字多少就是多少Hz
- // 这些频率到底代表着什么?
- // 为什么是这几个,为什么是6个。
- // 最后分析:原来是音乐中的“度”,一般都是8度。
- // 实际应用中应该根据不同蜂鸣器进行测试改变频度,这里面用了6度,其实也可以听出来一定的效果。
- // 所以用这几个不用太纠结。
- // 具体到音乐的深层次也没有必要的。
- #define _28 2850
- #define _24 2400
- #define _22 2250
- #define _21 2100
- #define _18 1850
- #define _16 1650
- // 定义duo,rui,mi,fa,so等等
- // 下面好像是节拍,但是最后一个又不满足。所以暂时先不管。
- // 根据最新分析:不是节拍的时间,是“度”。应该是高中低音之类!
- // 注意:下面前面字符是“l”不是“1” (小写的L和1),这个定义比较差!
- #define _l1 130
- #define _l2 146
- #define _l3 164
- #define _l4 174
- #define _l5 196
- #define _l6 220
- #define _l7 246
- #define _1 261
- #define _2 293
- #define _3 329
- #define _4 349
- #define _5 392
- #define _6 440
- #define _7 494
- #define _1d1 523
- #define _2d1 587
- #define _3d1 659
- #define _4d1 698
- #define _5d1 784
- #define _6d1 880
- #define _7d1 987
- #define _1d2 1046
- #define _2d2 1175
- #define _3d2 1318
- #define _4d2 1397
- #define _5d2 1568
- #define _6d2 1760
- #define _7d2 1976
- #define _1d3 (_1d2*2)
- #define _2d3 (_2d2*2)
- #define _3d3 (_3d2*2)
- #define _4d3 (_4d2*2)
- #define _5d3 (_5d2*2)
- #define _6d3 (_6d2*2)
- #define _7d3 (_7d2*2)
- extern vu8 Music_Triger; //触发发声标志
- extern u8 flg_MusicPlaying; //非0表示正在发声
- void Music_Srv(void); //4ms调用一次,为什么是4ms调用一次?
- void Music_Init(void); //初始化Music模块。
- void Music_Select(void);
- void Music_SetVolumn(u8 vol); // 可以单独的设置声音强度
- #endif
复制代码
|
|