Nios II实用之音频控制

  最近想整理一下割草机里面所设计到的小技术,先大体了解下它的整体框架,它以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是定义的宏,没写出)听起来效果还不错哦。这次就先写到这里吧,就当要写论文前的小练笔吧,写作水平还有待于提高啊。呵呵。

原文地址:https://www.cnblogs.com/kongtiao/p/1993878.html