LM3S9B96使用uDMA同时进行录音和播放的问题



在LM3S9B96的驱动函数库文件sound.c中,中断处理函数:

void
SoundIntHandler(void)
{
unsigned long ulStatus;
unsigned long *pulTemp;

//
// Get the interrupt status and clear any pending interrupts.
//
ulStatus = ROM_I2SIntStatus(I2S0_BASE, 1);

//
// Clear out any interrupts.
//
ROM_I2SIntClear(I2S0_BASE, ulStatus);

//
// Handle the RX channel interrupt
//
if(HWREGBITW(&g_ulDMAFlags, FLAG_RX_PENDING))
{
//
// If the RX DMA is done, then start another one using the same
// RX buffer. This keeps the RX running continuously.
//
if(ROM_uDMAChannelModeGet(UDMA_CHANNEL_I2S0RX | UDMA_PRI_SELECT) ==
UDMA_MODE_STOP)
{
//
// Save a temp pointer so that the current pointer can be set to
// zero before calling the callback.
//
pulTemp = g_sInBuffers[0].pulData;

//
// If at the mid point the refill the first half of the buffer.
//
if(g_sInBuffers[0].pfnBufferCallback)
{
g_sInBuffers[0].pulData = 0;

g_sInBuffers[0].pfnBufferCallback(pulTemp, BUFFER_EVENT_FULL);
}
}
else if(ROM_uDMAChannelModeGet(UDMA_CHANNEL_I2S0RX | UDMA_ALT_SELECT) ==
UDMA_MODE_STOP)
{
//
// Save a temp pointer so that the current pointer can be set to
// zero before calling the callback.
//
pulTemp = g_sInBuffers[1].pulData;

//
// If at the mid point the refill the first half of the buffer.
//
if(g_sInBuffers[1].pfnBufferCallback)
{
g_sInBuffers[1].pulData = 0;
g_sInBuffers[1].pfnBufferCallback(pulTemp, BUFFER_EVENT_FULL);
}
}

//
// If there are no more scheduled buffers then there are no more
// pending DMA transfers.
//
if((g_sInBuffers[0].pulData == 0) && (g_sInBuffers[1].pulData == 0))
{
HWREGBITW(&g_ulDMAFlags, FLAG_RX_PENDING) = 0;
}
}

//
// Handle the TX channel interrupt
//
if(HWREGBITW(&g_ulDMAFlags, FLAG_TX_PENDING))
{
//
// If the TX DMA is done, then call the callback if present.
//
if(ROM_uDMAChannelModeGet(UDMA_CHANNEL_I2S0TX | UDMA_PRI_SELECT) ==
UDMA_MODE_STOP)
{
//
// Save a temp pointer so that the current pointer can be set to
// zero before calling the callback.
//
pulTemp = g_sOutBuffers[0].pulData;

//
// If at the mid point then refill the first half of the buffer.
//
if((g_sOutBuffers[0].pfnBufferCallback) &&
(g_sOutBuffers[0].pulData != 0))
{
g_sOutBuffers[0].pulData = 0;
g_sOutBuffers[0].pfnBufferCallback(pulTemp, BUFFER_EVENT_FREE);
}
}

//
// If the TX DMA is done, then call the callback if present.
//
if(ROM_uDMAChannelModeGet(UDMA_CHANNEL_I2S0TX | UDMA_ALT_SELECT) ==
UDMA_MODE_STOP)
{
//
// Save a temporary pointer so that the current pointer can be set
// to zero before calling the callback.
//
pulTemp = g_sOutBuffers[1].pulData;

//
// If at the mid point then refill the first half of the buffer.
//
if((g_sOutBuffers[1].pfnBufferCallback) &&
(g_sOutBuffers[1].pulData != 0))
{
g_sOutBuffers[1].pulData = 0;
g_sOutBuffers[1].pfnBufferCallback(pulTemp, BUFFER_EVENT_FREE);
}
}

//
// If no more buffers are pending then clear the flag.
//
if((g_sOutBuffers[0].pulData == 0) && (g_sOutBuffers[1].pulData == 0))
{
HWREGBITW(&g_ulDMAFlags, FLAG_TX_PENDING) = 0;
}
}
}

在系统启动后,只启动录音时,中断服务程序只执行

if(ROM_uDMAChannelModeGet(UDMA_CHANNEL_I2S0RX | UDMA_PRI_SELECT) ==
UDMA_MODE_STOP) {...  ......}

中的内容,而在启动语音播放后,录音部分的

if(ROM_uDMAChannelModeGet(UDMA_CHANNEL_I2S0RX | UDMA_PRI_SELECT) ==
UDMA_MODE_STOP){  .... }

和else if(ROM_uDMAChannelModeGet(UDMA_CHANNEL_I2S0RX | UDMA_ALT_SELECT) ==
UDMA_MODE_STOP){  .... }才会交替被执行,这事问什么?

另外为什么录音的2个中断处理的if语句之间使用else语句,而播放的

if(ROM_uDMAChannelModeGet(UDMA_CHANNEL_I2S0TX | UDMA_PRI_SELECT) ==
UDMA_MODE_STOP) {  ...  }

if(ROM_uDMAChannelModeGet(UDMA_CHANNEL_I2S0TX | UDMA_ALT_SELECT) ==
UDMA_MODE_STOP)  {  ...  }之间不需要使用else语句?

  • 原因找到了:开始在录音开始时首次调用的函数SoundBufferRead()中,设置了HWREGBITW(&g_ulDMAFlags, FLAG_RX_PENDING) = 1;首次产生的中断也是I2S主接收通道的中断,及执行中断服务程序中的if(ROM_uDMAChannelModeGet(UDMA_CHANNEL_I2S0RX | UDMA_PRI_SELECT) ==UDMA_MODE_STOP){  ....  }中的内容,在执行该通道的中断服务程序时,会启动I2S次接收通道读取语音数据,而I2S次接收通道UDMA_MODE_STOP中断产生时,程序仍然会执行到if(ROM_uDMAChannelModeGet(UDMA_CHANNEL_I2S0RX | UDMA_PRI_SELECT) ==UDMA_MODE_STOP),此时上一次主通道传输结束时的条件ROM_uDMAChannelModeGet(UDMA_CHANNEL_I2S0RX | UDMA_PRI_SELECT) ==UDMA_MODE_STOP仍然满足,仍会执行if(ROM_uDMAChannelModeGet(UDMA_CHANNEL_I2S0RX | UDMA_PRI_SELECT) ==UDMA_MODE_STOP){  ....  }中的语句,而不会执行

    而I2S发送通道开启后,I2S发送通道产生中断时,进入中断处理函数,会遇到if(ROM_uDMAChannelModeGet(UDMA_CHANNEL_I2S0RX | UDMA_PRI_SELECT) ==UDMA_MODE_STOP)不满足条件的情况,这时才会执行else if(ROM_uDMAChannelModeGet(UDMA_CHANNEL_I2S0RX | UDMA_ALT_SELECT) ==UDMA_MODE_STOP) {  ...  }中的语句。