-
Notifications
You must be signed in to change notification settings - Fork 508
Description
Hello,
A bit of context: I am trying to implement audio output using both I2S and PWM Audio (an I2S chip is connected to a speaker and I'm using the PWM output as a cheap way to connect headphones when needed). Alas, when PWM is enabled, the sound via the I2S becomes corrputed (e.g. pops occurring). That even when I don't write anymore to I2S (hence the output should defaul to 0s on the I2S I believe). I don't have an oscilloscope to check what is going on with the output. That could be a separate bug report, since this is not what I am after here.
To mitigate the issue I am now trying to start/step completely each output when I want to swich back and forth between speaker and headphones. That works, if I call end() on the PWM side the I2S works properly, and when I2S is ended the speaker becomes silent. But after switching a couple times, the pico crashes (does not respond to anything, reset required). And this is the topic of this bug report.
After narrowing-down the problem it seems that just calling repeapitidely begin() / end() on the I2S causes the crash. I supected at first a memory issue, but checking the heap size with the SDK functions does not seem to indicate a leak (nor did I found one after a quick inspection of the code). The problems occurs more quickly if I output something on the Serial port. Or when I initialisie some variable (I had at first some audio buffer, global scope fixed size arrays, the reason why ), and immediately if I try to reset I2S (end/begin) too fast (twice within a second). Maybe there is something going on with the PIO and the FIFO? Before spending more time digging the code I thought it could be worth to ask if this use-case is even supported.
Here is a minimal code to reproduce the issue (tested with 2.7.1, no optimization for the compilation, with and without TinyUSB stack); sine at fixed frequency, timer to reset periosically I2S.
#define TIME_uS time_us_32()
#define fTime (TIME_uS/1000000.0)
#include <I2S.h>
I2S i2s(OUTPUT);
#define pBCLK 20
#define pWS (pBCLK+1)
#define pDOUT 22
const int sampleRate = 16000;
unsigned long timer0 = 0;
bool PWMon = true;
void setup()
{
Serial.begin(115200);
i2s.setBCLK(pBCLK);
i2s.setDATA(pDOUT);
i2s.setBitsPerSample(16);
delay(100);
timer0 = millis();
}
void loop()
{
if (millis() > 3000)
{
float fAmp = 50.0 * sin(2.0 * PI * fTime * 440.0);
short amp = (short)fAmp;
if (!PWMon) i2s.write16(amp, amp);
if (millis() - timer0 >= 10000)
{
timer0 = millis();
if (PWMon) Serial.println("Before I2S begin : ");
else Serial.println("Before I2S end : ");
if (!PWMon) i2s.end();
else i2s.begin(sampleRate);
if (PWMon) Serial.println("After I2S begin : ");
else Serial.println("After I2S end : ");
PWMon = !PWMon;
}
}
}
Is there something wrong with the code above, that I missed?