Using new I2S Driver
Moderator: RoccoMarco
Using new I2S Driver
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.
Thanks for the help.
- Giovanni
- Site Admin
- Posts: 14892
- Joined: Wed May 27, 2009 8:48 am
- Has thanked: 1202 times
- Been thanked: 996 times
Re: Using new I2S Driver
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
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
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?
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?
- Giovanni
- Site Admin
- Posts: 14892
- Joined: Wed May 27, 2009 8:48 am
- Has thanked: 1202 times
- Been thanked: 996 times
Re: Using new I2S Driver
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
Giovanni
Re: Using new I2S Driver
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.
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
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.
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.
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.
- Giovanni
- Site Admin
- Posts: 14892
- Joined: Wed May 27, 2009 8:48 am
- Has thanked: 1202 times
- Been thanked: 996 times
Re: Using new I2S Driver
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
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
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.
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.
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.
- Giovanni
- Site Admin
- Posts: 14892
- Joined: Wed May 27, 2009 8:48 am
- Has thanked: 1202 times
- Been thanked: 996 times
Re: Using new I2S Driver
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
Anyway, I will give the driver a try when I can but I will be out for travel the whole next week.
Giovanni