NDS自制软件教程6-进程间通信IPC



掌叔
2008-06-15 09:35:24

摘自:ndsbbs
作者:nashi1987

本教程讲解DS的进程间通信的原理和实现方法,是从国外教程中截取的,我将暂时意义不大的部分删掉了。如有需要以后补上。教程中涉及之前教程的内容,请务必先读之前的教程。

大家都知道DS有两块处理器,ARM7和ARM9。其中ARM9是主处理器,有时我们需要由ARM9通知ARM7作一些事情,因为DS的一部分硬件是只有ARM7可访问控制的。进程间通信IPC(以下简称IPC)就是完成这样功能的。

在教程4中大家可能注意到了,只有ARM7可以访问控制声音硬件,但是我们有时需要使用ARM9告诉ARM7我们要做什麽,就要有一种处理器之间的指令传输方法。这不止在声音控制中用到,在无线网等其他功能时依然要用。

指令:

在我们使用的例子中的IPC系统使用一个'Command'结构来定义由ARM9送到ARM7的指令,定义如下:


/* The ARM9 fills out values in this structure to tell the ARM7 what
to do. */
struct Command {
CommandType commandType;
union {
void* data;
PlaySampleSoundCommand playSample;
};
};


在一个'Command'结构中有一个定义指令类型(play sound, connect via wireless等等)的'commandType'成员,和一个包含指令所需数据的联合成员。上面的例子中包含一个无类型的指针变量和一个'PlaySampleSoundCommand'的实例。
'CommandType' 是一个可被用来传输的枚举变量:


/* Enumeration of commands that the ARM9 can send to the ARM7 */
enum CommandType {
PLAY_ONE_SHOT_SAMPLE
};


'Command'结构可以通过添加指令类型到枚举变量,添加指令所需的数据到联合变量中来完成新指令的扩充。

PlaySampleSoundCommand:

本例中通过进程间通信实现ARM9控制ARM7完成一个声音播放功能。PlaySampleSoundCommand包含播放声音采样所需的数据。包含指向声音数据的指针、采样频率、音量等等:


/* Command parameters for playing a sound sample */
struct PlaySampleSoundCommand
{
int channel;
int frequency;
void* data;
int length;
int volume;
};



一个播放声音的指令可用如下代码创建:



Command command;
command.commandType = PLAY_ONE_SHOT_SAMPLE;
command.playSample.channel = 0;
command.playSample.frequency = 11127;
command.playSample.data = [...];
[......]



指令控制:

在一个ARM7和ARM9都可以访问的共享内存区域中有一个循环指令队列。在一个'currentCommand'成员中包含一个索引,用来指向新指令存放在队列中存放的位置:


/* Maximum number of commands */
#define MAX_COMMANDS 20

/* A structure shared between the ARM7 and ARM9. The ARM9
places commands here and the ARM7 reads and acts upon them.
*/
struct CommandControl {
Command command[MAX_COMMANDS];
int currentCommand; //索引用的变量
};

/* Address of the shared CommandControl structure */
#define commandControl ((CommandControl*)((uint32)(IPC) + sizeof(TransferRegion)))


指令和ARM9:

从ARM9 向指令队列添加一个想要执行的指令,需要通过更新 'Command' 中的'currentCommand'来完成新指令的索引,之后'currentCommand'加一。你需要注意在指令数超过MAX_COMMANDS 规定的最大指令数时'currentCommand'应当清零。



void CommandPlayOneShotSample(
int channel,
int frequency,
void* data,
int length,
int volume)
{
Command* command =
&commandControl->command[commandControl->currentCommand];
PlaySampleSoundCommand* ps = &command->playSample;

command->commandType = PLAY_ONE_SHOT_SAMPLE;
ps->channel = channel;
ps->frequency = frequency;
ps->data = data;
ps->length = length;
ps->volume = volume;

commandControl->currentCommand++;
commandControl->currentCommand &= MAX_COMMANDS-1;
}



在ARM9代码部分仍需要初始化一个函数来设置ComandControl共享变量为一个初始状态:


void CommandInit() {
memset(commandControl, 0, sizeof(CommandControl));
}



ARM9指令代码部分在command9.cpp中。可以在这里添加新指令,就像之前提到的在command.h中添加Command union 部分一样。

指令和ARM7:

ARM7 指令相关代码在command7.cpp中。 'CommandProcessCommands' 函数必须被ARM7有规律的执行(检查是否有新指令加入),我们在垂直扫描中断中调用它。他反复扫描CommandControl结构中的指令看是否有未执行的,查出类型并执行:



void CommandProcessCommands() {
static int currentCommand = -1;

while(currentCommand != commandControl->currentCommand) {
Command* command = &commandControl->command[currentCommand];
switch(command->commandType) {
case PLAY_ONE_SHOT_SAMPLE:
CommandPlayOneShotSample(&command->playSample);
break;
}
currentCommand++;
currentCommand &= MAX_COMMANDS-1;
}
}



如果因指令需要需要扩展这个函数,我们需要改变函数的部分内容,添加新指令类型,以及从 Command union中获得所需数据。目前这个例子中我们只用到'CommandPlayOneShotSample',和通常ARM7播放声音的代码一样,在教程 4中有讲解。函数如下:



static void CommandPlayOneShotSample(PlaySampleSoundCommand* ps)
{
int channel = ps->channel;

SCHANNEL_CR(channel) = 0;
SCHANNEL_TIMER(channel) = SOUND_FREQ(ps->frequency);
SCHANNEL_SOURCE(channel) = (uint32)ps->data;
SCHANNEL_LENGTH(channel) = ps->length >> 2;
SCHANNEL_CR(channel) =
SCHANNEL_ENABLE |
SOUND_ONE_SHOT |
SOUND_8BIT |
SOUND_VOL(ps->volume);
}



用法:

有了以上的代码,使用ARM9播放声音就简单多了,我们只需通过调用'CommandPlayOneShotSample' 函数就可以了:



[...]
CommandPlayOneShotSample(current_channel,
current_file->frequency,
sound_buffer,
current_file->length,
0x3F);



以上就是DS的进程间通信的实现方法,今后会给出更多的例子,但一定先将这篇教程搞懂。IPC在DS中应用很广。

如有疏漏错误之处,还望大家多多指正!


小猫
2011-09-18 12:54:30

万岁啊