Page 1 of 2
Using new I2S Driver
Posted: Wed Apr 02, 2014 1:28 pm
by chiVelle
I have been working with the new I2S driver checked into the 3.0 branch, and so far have been receiving audio from an external wolfson codec. What I am trying to do is write the data coming in from the wolfson to a file with FatFS using the I2scallback function that is placed into the i2sconfig object. Unfortunately, a lot of zeros show up in the file that it is being written. The callback is an I-lock zone from what I can see, so calling f_write() directly in the callback is not available correct? Is there a way to get around this? If not, is there a standard procedure on what to do in that callback to get the data to a file? Also, would enabling double buffering with the DMA be a challenge so that two independent buffers could be used?
Thanks for the help.
Re: Using new I2S Driver
Posted: Wed Apr 02, 2014 1:37 pm
by Giovanni
Callbacks are always invoked from ISRs so only i-class functions there.
You need a buffers system between the callback and a consumer thread. While the the callback fills one buffer the thread is writing the other one.
This assumes that the thread is able to write data faster than the I2S receives, if speeds are close you may need extra buffers.
You may use a pool of free buffers (min 2) and a mailbox between callback and thread.
Giovanni
Re: Using new I2S Driver
Posted: Wed Apr 02, 2014 1:39 pm
by chiVelle
Thanks, that makes sense.
Re: Using new I2S Driver
Posted: Mon Apr 07, 2014 3:19 pm
by Lemming
I've been using the new driver as well, and while copying each half of a single circular buffer into two intermediary buffers for writing to the file, these are the results I've been seeing. I attempted to use memory pools, but they produced the same results. The one thing I did notice was that addressing i2sDriver->config->rx_buffer actually provided more information (IE: smaller gaps) than addressing the buffer i2sDriver->config->rx_buffer points to. I don't think this is an issue of not being able to write to the file fast enough, since using larger buffers produced the same result, albeit with larger chunks of data and silence.
By the way, this was a 5-second sweep from 250Hz to 2kHz. The file recorded a 10-second sweep. I'm assuming this is due to the gaps and not an issue with PLL setup, since the data looks like it should be good once the gaps are removed. It appears that every time we receive a transfer complete interrupt, the buffer is empty. However, we're getting data at the half transfer interrupt, though it is out of order. What could cause this?

- sweep.jpg (258.18 KiB) Viewed 5993 times
Re: Using new I2S Driver
Posted: Mon Apr 07, 2014 8:03 pm
by Giovanni
A bug could cause it

the driver is experimental and missing a good test. Is the driver providing the data fast enough or it has gaps by itself?
Giovanni
Re: Using new I2S Driver
Posted: Mon Apr 07, 2014 9:24 pm
by Lemming
Since the driver is firing both half transfer and transfer complete interrupts, I assume it thinks it has filled the buffer. It just seems like the offset into the buffer is incorrect, or perhaps it's not circling properly.
Could you elaborate more on what you mean by the driver not being fast enough? Even when I just address the rx_buffer itself, I see the final portion of the buffer is populated with zeros. It does appear to be writing new data, so I don't think it's an issue of the driver not supplying data fast enough. It just isn't filling the buffer.
Re: Using new I2S Driver
Posted: Mon Apr 07, 2014 10:31 pm
by Lemming
Here's my I2SConfig and the receiver code. I've set the PLLI2SN and PLLI2SR values to 192 and 2, as recommended by the reference manual for 8kHz, 16-bit audio.
Code: Select all
#define I2S_BUF_SIZE 4096
#define I2S_HALF_BUF I2S_BUF_SIZE/2
int16_t i2s_rx_buf[I2S_BUF_SIZE] __attribute__((aligned(sizeof(stkalign_t)))); // alignment added for memory pool use
static const I2SConfig i2scfg =
{
NULL, // tx buffer
i2s_rx_buf, // rx buffer
I2S_BUF_SIZE, // size of the buffers
i2sReceive, // streaming data callback
0x000, // I2SCFGR register setup (I2S master/receive, I2S output, I2S mode are defaults set by the driver)
0x01BB, // I2SPR register setup (odd, divider = 187)
};
Code: Select all
unsigned int g_n1, g_n2; // used to maintain proper byte sizes for external buffers
int16_t i2s_store1[I2S_HALF_BUF] __attribute__((aligned(sizeof(stkalign_t))));
Int16_t i2s_store2[I2S_HALF_BUF] __attribute__((aligned(sizeof(stkalign_t))));
void i2sReceive(I2SDriver *i2sp, size_t offset, size_t n)
{
(void)i2sp;
(void)offset;
(void)n;
chSysLockFromIsr();
if(offset == 0)
{
g_n1 = n*2;
memcpy(i2s_store1, i2s_rx_buf, g_n1);
chEvtSignalI(p_audioThread, EVENT_MASK(HALF_EVENT));
// HALF TRANSFER COMPLETE
}
else
{
g_n2 = n*2;
memcpy(i2s_store2, i2s_rx_buf + offset, g_n2);
chEvtSignalI(p_audioThread, EVENT_MASK(FULL_EVENT));
// TRANSFER COMPLETE
}
chSysUnlockFromIsr();
}
The audio thread waits for the half and full events, then uses f_write to write the appropriate i2s_storeX buffer to the SD card. The driver is consistently providing half and full events in alternating order.
Re: Using new I2S Driver
Posted: Tue Apr 08, 2014 4:31 pm
by Giovanni
Hi,
Not sure, I don't understand the problem entirely. Could you try and confirm if the driver fills the whole circular buffer correctly independently from the card writing part? We need to understand if it is a problem with the driver itself or not.
About the buffering, instead of copying that data and then sending the event, you could as well just send the event and write directly from the I2S buffer, copying the data that way does not help decoupling the two processes IMHO.
Giovanni
Re: Using new I2S Driver
Posted: Wed Apr 09, 2014 7:44 pm
by Lemming
Alright, I've updated the callback so it doesn't copy anything and the thread addresses the buffer directly. Having seeded the buffer with a recognizable pattern, you can see that the buffer isn't being completely filled. There is some weirdness though, in that depending how I address the buffer, I'm seeing two different results.
With the following code, I'm never seeing any data in the second half.
Code: Select all
__attribute__((noreturn))
msg_t audioThread(void *arg)
{
(void)arg;
EventListener el;
EventSource es;
chEvtRegisterMask(&es, &el, EVENT_MASK(FULL_EVENT) | EVENT_MASK(HALF_EVENT);
int16_t pattern[I2S_HALF_BUF], *i2sPtr;
memset(pattern, 0x77, I2S_HALF_BUF*sizeof(int16_t));
memset(i2s_rx_buf, 0x77, I2S_BUF_SIZE*sizeof(int16_t));
i2sStart(&I2SD2, &i2scfg);
if(I2SD2.state == I2S_READY)
{
printConsole("starting i2s\n");
i2sStartExchange(&I2SD2);
}
while(TRUE)
{
eventmask_t mask = chEvtWaitAny(ALL_EVENTS);
if(mask & EVENT_MASK(HALF_EVENT))
{
i2sPtr = i2s_rx_buf;
if(i2sPtr != NULL)
{
if(memcmp(i2sPtr, pattern, I2S_BUF_SIZE) == 0)
{ printConsole("FIRST HALF NOT FILLED\n"); }
}
}
if(mask & EVENT_MASK(FULL_EVENT))
{
i2sPtr = i2s_rx_buf + I2S_HALF_BUF;
if(i2sPtr != NULL)
{
if(memcmp(i2sPtr, pattern, I2S_BUF_SIZE) == 0)
{ printConsole("SECOND HALF NOT FILLED\n"); }
}
}
chEvtGetAndClearEvents(mask);
}
chThdExit(1);
}
However, if I change "i2s_rx_buf" to "I2SD2.config->rx_buffer", then the first 3/4 of the buffer is filled. The last quarter still shows the 0x77 pattern.
Re: Using new I2S Driver
Posted: Wed Apr 09, 2014 8:12 pm
by Giovanni
I don't understand the buffer thing, is the address of the pointer the same base address of the buffer?
Anyway, I will give the driver a try when I can but I will be out for travel the whole next week.
Giovanni