《TQ2440的学习——IIS接口的使用(基本概念和基本功能实验)》中的轮询最大的缺陷就是性能低下,所以开启DMA传输是很大的优势的。虽然比轮询稍微多点代码。
DMA的播放/录音实现和轮询实现的区别在于,轮询需要自己编写数据输出/读取代码,但是DMA方式下不用,只需要指明源地址和目标地址以及数据大小就可以由硬件自动传输数据了,只是要注意的是S3C2440下的DMA使用时候数据大小是有限制的,一次最多可以传输的字节大小为:DSZ×TSZ×TC,DSZ表示的是数据大小(字节1、半字2、字4);TSZ表示的是传输大小(单元传输还是突发传输),TC表示传输计数值(寄存器DCONn的低20位存放的数据),超过这个数据大小的数据就必须要分次进行传输。至于DMA寄存器其它的位的功能描述,可以参见《S3C2440芯片资料DMA阅读笔记——基本概念》中的相关总结。
这段代码是本次试验的相关主控部分。同样,实验的时候现录音再播放:
01 else if(!(no_system_strcmp("iis record play dma",cmd_buf)))
02 {
03 no_system_memset(cmd_buf,0,50);
04 GPBCON = 0x015550;
05 GPBUP = 0x7ff;
06 GPBDAT = 0x1e4;
07 GPEUP = (GPEUP & (~(0x1f))) | 0x1f;
08 GPECON = (GPECON & (~(0x3ff))) | 0x2aa;
09
10 uart0_printf("play begin\n\r");
11 playsound_dma((unsigned char *)0x31000000,6400 * 400);
12 uart0_printf("play end\n\r");
13 }
14 else if(!(no_system_strcmp("iis record dma",cmd_buf)))
15 {
16 no_system_memset(cmd_buf,0,50);
17 GPBCON = 0x015550;
18 GPBUP = 0x7ff;
19 GPBDAT = 0x1e4;
20 GPEUP = (GPEUP & (~(0x1f))) | 0x1f;
21 GPECON = (GPECON & (~(0x3ff))) | 0x2aa;
22
23 uart0_printf("record begin\n\r");
24 record_dma((unsigned char *)0x31000000,6400 * 400);
25 uart0_printf("record end\n\r");
26 }
这段代码实现了播放一段2声道、44.1KHz音频数据的功能。和轮询方式不同的,这里主要就是配置了IIS和DMA2的寄存器(放音使用通道2):
01 void playsound_dma(unsigned char *buffer,unsigned long int length)
02 {
03 DMA_SRC_SIZE = length;//全局变量,会在中断处理中用到
04
05 GPBDAT = (GPBDAT & (~(L3M | L3C | L3D))) | (L3M | L3C);
06
07 WriteL3(0x14 + 2,1);
08 WriteL3(0x60,0);
09
10 WriteL3(0x14 + 2,1);
11 WriteL3(0x10,0);
12
13 WriteL3(0x14 + 2,1);
14 WriteL3(0xc1,0);
15
16 IISPSR = PRESCANLER_A(2) | PRESCANLER_B(2);
17 IISCON = TX_DMA_ENABLE | RX_CH_IDLE_CMD | IIS_PRESCALER;
18 IISMOD = TX_RX_MODE(TX_MODE) | S_DATA_BIT(BIT16) | MA_CLOCK(FS384) | \
19 S_CLOCK(FS32);
20 IISFCON = TX_FIFO_AC(DMA) | TX_FIFO_EN;
21 //unsigned long int address = buffer;
22 DISRC2 = 0x31000000;
23 DISRCC2 = (0 << 1) | (0 << 0);//AHB,incresment
24 DIDST2 = 0x55000010;//FIFO is destination
25 DIDSTC2 = (0 << 2) | (1 << 1) | (1 << 0);//when 0 interrupt,APB,fixed
26
27 DCON2 = (1 << 31) | (0 << 30) | (1 << 29) | (0 << 28) | (0 << 27) | \
28 (0 << 24) | (1 << 23) | (1 << 22) | (1 << 20) | \
29 ((length > 0x200000)?0xfffff:((length >> 1) & 0xfffff));
30 DMASKTRIG2 = (0 << 2) | (1 << 1) | 0;
31 IISCON |= 0x1;
32 }
这段代码实现了录制一段2声道、44.1KHz音频数据的功能(使用DMA通道):
01 void record_dma(unsigned char *buffer,unsigned long length)
02 {
03 DMA_SRC_SIZE = length;
04 GPBDAT = (GPBDAT & (~(L3M | L3C | L3D))) | (L3M | L3C);
05
06 WriteL3(0x14 + 2,1);
07 WriteL3(0x60,0);
08
09 WriteL3(0x14 + 2,1);
10 WriteL3(0x10,0);
11
12 WriteL3(0x14 + 2,1);
13 WriteL3(0xc1,0);
14
15 WriteL3(0x14 + 2,1);
16 WriteL3(0xe3,0);
17
18 WriteL3(0x14 + 0,1);
19 WriteL3(0x7b,0);
20
21 WriteL3(0xc4,0);
22 WriteL3(0xf0,0);
23
24 WriteL3(0xc0,0);
25 WriteL3(0xe0,0);
26
27 WriteL3(0xc1,0);
28 WriteL3(0xe0,0);
29
30 WriteL3(0xc2,0);
31 WriteL3(0xf9,0);
32
33 IISPSR = PRESCANLER_A(2) | PRESCANLER_B(2);
34 IISCON = RX_DMA_ENABLE | TX_CH_IDLE_CMD | IIS_PRESCALER;
35 IISMOD = TX_RX_MODE(RX_MODE) | S_DATA_BIT(BIT16) | \
36 MA_CLOCK(FS384) | S_CLOCK(FS32);
37 IISFCON = RX_FIFO_AC(DMA) | RX_FIFO_EN;
38
39 DISRC1 = 0x55000010;
40 DISRCC1 = (1 << 1) | (1 << 0);//APB,fixed
41 DIDST1 = 0x31000000;
42 DIDSTC1 = (0 << 2) | (0 << 1) | (0 << 0);
43 DCON1 = (1 << 31) | (0 << 30) | (1 << 29) | (0 << 28) | (0 << 27) | \
44 (2 << 24) | (1 << 23) | (1 << 22) | (1 << 20) | \
45 ((length > 0x200000)?0xfffff:((length >> 1) & 0xfffff));
46 DMASKTRIG1 = (0 << 2) | (1 << 1) | 0;
47 IISCON |= 0x1;
48 }
这段代码是DMA通道中断的处理代码,一次数据传完后就会触发这个中断。中断需要处理的就是判断数据是否传完,传完就关闭DMA和IIS,否则就传输下一片内存的数据,具体的方法是修改源地址,使其偏移到未传输数据的内存起始地址,由于录音和放音使用的是不同的DMA通道,所以中断处理函数都是大体相同的,区别仅仅存在于进入中断后所屏蔽的中断位(这里就以放音的DMA中断为例):
01 void DMA_interrupt(void)
02 {
03 SRCPND |= 0x1 << 19;
04 INTPND |= 0x1 << 19;
05 /**
06 *思路:通过计算得出在单元传输模式下(一次一个)
07 *传送16位数据,最大的一次传送总数为0x200000(因为TC计数器最大为0x100000)
08 *如果数据小于0x200000进入中断的时候数据肯定都已经传完了,这个时候关闭DMA和IIS
09 */
10 if(DMA_SRC_SIZE >= 0x200000)//not end
11 {
12 DISRC2 += 0x200000;
13 DMA_SRC_SIZE -= 0x200000;
14 if(0 != DMA_SRC_SIZE)//will end
15 {
16 //一次传送的数据为半字
17 DCON2 = (DCON2 & (~0xfffff)) | ((DMA_SRC_SIZE > 0x200000)?0xfffff:((DMA_SRC_SIZE >> 1) & 0xfffff));
18 DMASKTRIG2 = (0 << 2) | (1 << 1) | 0;
19 }
20 else//end DMA_SRC_SIZE == 0
21 {
22 IISCON = 0x0;
23 IISFCON = 0x0;
24 DMASKTRIG2 = 1 << 2;
25 }
26 }
27 else//DMA_SRC_SIZE < 0x200000 already end
28 {
29 IISCON = 0x0;
30 IISFCON = 0x0;
31 DMASKTRIG2 = 1 << 2;
32 }
33 }
总结
在配置S3C2440的寄存器的时候,一定要注意寄存器的位宽,如果在C语言中采用的数据类型描述该寄存器的时候,如果数据类型过大或者过小,会造成很奇怪的问题(例子:DMASKTRIGx寄存器在芯片资料上只有3个位,所以在C语言下要定义成unsigned char类型才能正常使用)。IIS在播放不出声音的时候,首先要确定寄存器配置是否正确,然后再看内存空间是否得到正确的访问(不要忘记偏移)。
写到现在,其实最深的感觉是要用一个东西简单,用好确实非常难的。看看S3C2440都出了多少年了,已经算是很落后的芯片了。但是又有多少应用它的产品将它的性能完全发挥出来了?偶然看到新闻上介绍的一个489的平板,看评论上有的说连耍个小鸟砸猪都卡死。那个平板配置了1GHz的ARM11 CPU啊,应该带了图形加速处理单元吧。真不明白小鸟用了啥复杂的运算还是咋了。想想NDS吧,66MHz的ARM9 CPU + 33MHz的ARM7 CPU(在耍NDS游戏的时候还只能用ARM9的CPU负责图像处理)。耍最终幻想的时候那画面不比小鸟差吧,还3D且双屏显示带触控。一味的追求高性能硬件又有啥用?结果平板卖的比(水货)NDS卖的还便宜。虽然买这个平板的人大概也没指望多高的性能。但是1GHz的CPU只能用来当做玩玩简单游戏的MP4是一件让人很不爽的事。