最近想整理一下割草机里面所设计到的小技术,先大体了解下它的整体框架,它以FPGA为核心,两个PIC对传感器的数据进行处理,然后通过串口发送给FPGA数据。在FPGA中,Nios处理器添加必要的中断,捕捉传感器信号,进行简单的防卫功能。
今天想对车子上的声音控制做一个总结,声音是通过PWM来控制的,PWM的频率能变化出不同的音调,音节的长短,可以通过定时器来控制,当选择好一个音节后,音节响的过程中是不占用处理器的。
软核部分:
1、在SOPC Builder中添加PWM,
这里我只是针对声音的控制,还有的模块添加没有说明,当一切都添加完后,对Nios处理器进行编译,我们就能得到最终的模块,如下图,
2、在SOPC Builder中添加定时器,
编译好后,在NIOS II IDE中的system.h中会生成如下内容:
View Code 1 /* 2 * pwm_speaker configuration 3 * 4 */ 5 #define PWM_SPEAKER_NAME "/dev/pwm_speaker" 6 #define PWM_SPEAKER_TYPE "pwm_avalon_interface" 7 #define PWM_SPEAKER_BASE 0x08002270 8 #define PWM_SPEAKER_SPAN 16 9 #define PWM_SPEAKER_HDL_PARAMETERS "" 10 #define ALT_MODULE_CLASS_pwm_speaker pwm_avalon_interface 11 12 /* 13 * timer_2_ms configuration 14 * 15 */ 16 #define TIMER_2_MS_NAME "/dev/timer_2_ms" 17 #define TIMER_2_MS_TYPE "altera_avalon_timer" 18 #define TIMER_2_MS_BASE 0x080020c0 19 #define TIMER_2_MS_SPAN 32 20 #define TIMER_2_MS_IRQ 13 21 #define TIMER_2_MS_ALWAYS_RUN 0 22 #define TIMER_2_MS_FIXED_PERIOD 0 23 #define TIMER_2_MS_SNAPSHOT 1 24 #define TIMER_2_MS_PERIOD 1.0 25 #define TIMER_2_MS_PERIOD_UNITS "ms" 26 #define TIMER_2_MS_RESET_OUTPUT 0 27 #define TIMER_2_MS_TIMEOUT_PULSE_OUTPUT 0 28 #define TIMER_2_MS_LOAD_VALUE 49999 29 #define TIMER_2_MS_MULT 0.001 30 #define TIMER_2_MS_FREQ 50000000 31 #define ALT_MODULE_CLASS_timer_2_ms altera_avalon_timer 他们对应先前添加的2个模块,在后面的代码中会用到他们的基地址PWM_SPEAKER_BASE和TIMER_2_MS_BASE。
软件部分:
下面首先是准备音乐的前期工作,定义好它的音调与音调长度(说的有点不专业啊- -!)
音调定义:
View Code 1 /* 低音 */ 2 #define _1DO 262 3 #define _1RE 294 4 #define _1MI 330 5 #define _1FA 349 6 #define _1SO 392 7 #define _1LA 440 8 #define _1TI 494 9 10 /* 中音 */ 11 #define _DO 523 12 #define _RE 587 13 #define _MI 659 14 #define _FA 698 15 #define _SO 784 16 #define _LA 880 17 #define _TI 988 18 19 /* 高音 */ 20 #define _DO1 1047 21 #define _RE1 1175 22 #define _MI1 1319 23 #define _FA1 1397 24 #define _SO1 1568 25 #define _LA1 1760 26 #define _TI1 1976 音调长度定义:
View Code 1 // 以4分音符为1拍 2 #define TEMPO 8 3 #define _0 0 4 #define _1 TEMPO*4 // 全音符 5 #define _1d TEMPO*6 // 附点全音符 6 #define _2 TEMPO*2 // 2音符 7 #define _2d TEMPO*3 // 附点2音符 8 #define _4 TEMPO*1 // 4分音符 9 #define _4d TEMPO*3/2 // 附点4分音符 10 #define _8 TEMPO*1/2 // 8分音符 11 #define _8d TEMPO*3/4 // 附点8音符 12 #define _16 TEMPO*1/4 // 16分音符 13 #define _16d TEMPO*3/8 // 附点16分音符 14 #define _32 TEMPO*1/8 // 32分音符 15 #define _END 100 // 音频结束 歌谱:
从网上找了2个家喻户晓的曲子,一首是欢乐颂,一首是茉莉花,根据感觉用上面的音调和音调长度谱了下面2首:
View Code 1 int Music_Buf27[] = // 欢乐颂 2 { 3 _MI,_4,_MI,_4,_FA,_4,_SO,_4, 4 _SO,_4,_FA,_4,_MI,_4,_RE,_4, 5 _DO,_4,_DO,_4,_RE,_4,_MI,_4, 6 _MI,_4d,_RE,_8,_RE,_2, 7 _MI,_4,_MI,_4,_FA,_4,_SO,_4, 8 _SO,_4,_FA,_4,_MI,_4,_RE,_4, 9 _DO,_4,_DO,_4,_RE,_4,_MI,_4, 10 _RE,_4d,_DO,_8,_DO,_2, 11 _RE,_4,_RE,_4,_MI,_4,_DO,_4, 12 _RE,_4,_MI,_8,_FA,_8,_MI,_4,_DO,_4, 13 _RE,_4,_MI,_8,_FA,_8,_MI,_4,_RE,_4, 14 _DO,_4,_RE,_4,_1SO,_4,_MI,_4, 15 _MI,_4,_MI,_4,_FA,_4,_SO,_4, 16 _SO,_4,_FA,_4,_MI,_4,_FA,_8,_RE,_8, 17 _DO,_4,_DO,_4,_RE,_4,_MI,_4, 18 _RE,_4d,_DO,_8,_DO,_2,_0,_4,_END, 19 }; 20 21 int Music_Buf28[] = // 茉莉花 22 { 23 _MI,_4,_MI,_8,_SO,_8,_LA,_8,_DO1,_8,_DO1,_8,_LA,_8, 24 _SO,_4,_SO,_8,_LA,_8,_SO,_2, 25 _MI,_4,_MI,_8,_SO,_8,_LA,_8,_DO1,_8,_DO1,_8,_LA,_8, 26 _SO,_4,_SO,_8,_LA,_8,_SO,_2, 27 _SO,_4,_SO,_4,_SO,_4,_MI,_8,_SO,_8, 28 _LA,_4,_LA,_4,_SO,_2, 29 _MI,_4,_RE,_8,_MI,_8,_SO,_4,_MI,_8,_RE,_8, 30 _DO,_4,_DO,_8,_RE,_8,_DO,_2, 31 _MI,_8,_SO,_8,_DO,_8,_MI,_8,_RE,_4d,_MI,_8, 32 _SO,_4,_LA,_8,_DO1,_8,_SO,_2, 33 _RE,_4,_MI,_8,_SO,_8,_RE,_8,_MI,_8,_DO,_8,_1LA,_8, 34 _1SO,_2,_1LA,_4,_DO,_4, 35 _RE,_4d,_MI,_8,_DO,_8,_RE,_8,_DO,_8,_1LA,_8, 36 _1SO,_2d,_0,_4,_END, 37 }; 注:数组的结构是:一个音调和一个节拍相间隔,数字越大,节拍越短。
下面就是代码的主体部分,音乐的接口函数:
View Code 1 void music ( unsigned int type ) 2 { 3 if ( ( type != OFF ) && ( Music_Replay > 1 ) ) return ; 4 altera_avalon_pwm_disable ( PWM_SPEAKER_BASE ); 5 alt_irq_disable ( TIMER_2_MS_IRQ ); 6 if ( type == OFF ) { Music_Replay = 0 ; return ;} 7 8 Music_Replay = ( type & 0xffff00 ) >> 8 ; 9 if ( Music_Replay == 0 ) Music_Replay = 1 ; 10 Music_Syllable = 0 ; 11 int Track = ( type & 0xff ); // 曲目缓冲 12 switch ( Track ) 13 { 14 case MUSIC_9_3: 15 Music_Buf = Music_Buf27; 16 break ; 17 case MUSIC_9_4: 18 Music_Buf = Music_Buf28; 19 break ; 20 } 21 22 if ( Music_Buf[Music_Syllable] != _0 ) 23 { 24 int PWMMR0 = 35000000 / Music_Buf[Music_Syllable]; // 设置输出频率 25 altera_avalon_pwm_init ( PWM_SPEAKER_BASE,PWMMR0,PWMMR0 * 3 / 4 ); 26 altera_avalon_pwm_enable ( PWM_SPEAKER_BASE ); 27 } 28 Music_Syllable ++ ; // 设置延时 29 int delay = Music_Buf[Music_Syllable] * TIMER_1_SECOND * 0.025 ; 30 IOWR_ALTERA_AVALON_TIMER_PERIODL ( TIMER_2_MS_BASE, ( delay & 0xffff ) ); 31 IOWR_ALTERA_AVALON_TIMER_PERIODH ( TIMER_2_MS_BASE, ( ( delay >> 16 ) & 0xffff ) ); 32 IOWR_ALTERA_AVALON_TIMER_CONTROL ( TIMER_2_MS_BASE, ALTERA_AVALON_TIMER_CONTROL_ITO_MSK | ALTERA_AVALON_TIMER_CONTROL_START_MSK | ALTERA_AVALON_TIMER_CONTROL_CONT_MSK ); 33 IOWR_ALTERA_AVALON_TIMER_STATUS ( TIMER_2_MS_BASE, 0 ); // 清TO标志 34 alt_irq_enable ( TIMER_2_MS_IRQ ); 35 } 注:
第8行:变量Music_Replay是控制音乐循环播放的次数,默认是播放1遍;
第24~26行:对音调处理,然后通过PWM表现,初始并且使能它;
第29~34行:将定时器的时间设置成节拍的时间长度值,最后使能定时器。
对定时器的控制,先是初始化:
View Code 1 void init_timer2() 2 { 3 IOWR_ALTERA_AVALON_TIMER_PERIODL ( TIMER_2_MS_BASE, TIMER_1_SECOND & 0xffff ); 4 IOWR_ALTERA_AVALON_TIMER_PERIODH ( TIMER_2_MS_BASE, ( TIMER_1_SECOND >> 16 ) & 0xffff ); 5 IOWR_ALTERA_AVALON_TIMER_CONTROL ( TIMER_2_MS_BASE, ALTERA_AVALON_TIMER_CONTROL_ITO_MSK | ALTERA_AVALON_TIMER_CONTROL_START_MSK | ALTERA_AVALON_TIMER_CONTROL_CONT_MSK ); 6 alt_irq_register ( TIMER_2_MS_IRQ, NULL, timer2_ISR ); 7 alt_irq_disable ( TIMER_2_MS_IRQ ); 8 } 然后是定时器中断函数的编写:
View Code 1 static void timer2_ISR ( void * context, alt_u32 id ) 2 { 3 altera_avalon_pwm_disable ( PWM_SPEAKER_BASE ); 4 alt_irq_disable ( TIMER_2_MS_IRQ ); 5 Music_Syllable ++ ; 6 if ( Music_Buf[Music_Syllable] == _END ) 7 { 8 Music_Replay -- ; 9 if ( Music_Replay == 0 ) return ; 10 Music_Syllable = 0 ; 11 } 12 13 if ( Music_Buf[Music_Syllable] != _0 ) 14 { 15 int PWMMR0 = 35000000 / Music_Buf[Music_Syllable]; // 设置输出频率 16 altera_avalon_pwm_init ( PWM_SPEAKER_BASE,PWMMR0,PWMMR0 * 3 / 4 ); 17 altera_avalon_pwm_enable ( PWM_SPEAKER_BASE ); 18 } 19 Music_Syllable ++ ; // 设置延时 20 int delay = Music_Buf[Music_Syllable] * TIMER_1_SECOND * 0.04 ; 21 IOWR_ALTERA_AVALON_TIMER_PERIODL ( TIMER_2_MS_BASE, ( delay & 0xffff ) ); 22 IOWR_ALTERA_AVALON_TIMER_PERIODH ( TIMER_2_MS_BASE, ( ( delay >> 16 ) & 0xffff ) ); 23 IOWR_ALTERA_AVALON_TIMER_CONTROL ( TIMER_2_MS_BASE, ALTERA_AVALON_TIMER_CONTROL_ITO_MSK | ALTERA_AVALON_TIMER_CONTROL_START_MSK | ALTERA_AVALON_TIMER_CONTROL_CONT_MSK ); 24 IOWR_ALTERA_AVALON_TIMER_STATUS ( TIMER_2_MS_BASE, 0 ); // 清TO标志 25 alt_irq_enable ( TIMER_2_MS_IRQ ); 26 } 注:
第6~10行是对播放次数的分析;下面的基本同于上面一个函数。
最后当然是测试音乐的效果了,
调用函数music(MUSIC_9_3);则播放欢乐颂;
调用函数music(MUSIC_9_4);则播放茉莉花;如果想循环播放3遍,则调用函数music(MUSIC_9_4|0x300);
(MUSIC_9_3、MUSIC_9_4是定义的宏,没写出)听起来效果还不错哦。这次就先写到这里吧,就当要写论文前的小练笔吧,写作水平还有待于提高啊。呵呵。