Home Forums Sparkfun MP3 Shield Library Support Forum Interference with millis() function

Viewing 9 posts - 1 through 9 (of 9 total)
  • Author
    Posts
  • #2168
    Anonymous
    Inactive

    The library is working great for me, but I am having one problem:

    My project uses the Sparkfun mp3 Shield to send out short voice prompts on a schedule controlled by calls to the Adrduino’s millis() function. Each prompt is in a separate mp3 file. I find that each call to playMP3 causes the millis() to lose about 1 second.

    To demonstate this, I added a command “x” to the demo sketch that has it print the current value of millis(), like this:

    } else if(key_command == ‘x’) {

    Serial.print(F(“Current time is: “));

    Serial.println(millis());

    If I enter x, wait 30 seconds, then enter x again, the time difference is just about 30000 as expected.

    If I do the same but listen to three 8 second files between the x commands, the time difference is about 27000.

    So it appears it is dropping timer interrupts somewhere along the way.

    Is there an easy remedy for this?

     

    Thanks for your help!

    #2169

    Try changing the following in SFEMP3ShieldConfig.h from

    #define USE_MP3_REFILL_MEANS USE_MP3_INTx

    to

    #define USE_MP3_REFILL_MEANS USE_MP3_Polled

    This will make the demo run without using the interrupts. The SFEMP3Shield library does not directly affect the millis() or its sourced components. I suspect the above change will confirm that it is an interference of interrupts. Between the Arduino’s core library for millis() sourced by Timer0’s Overflow interrupt vector and the VS1053 DREQ. Where the MP3 driver reads ahead the MP3 file, while in the interrupt, and the TIMER0_OVF_vect’s increamenting of the timer0_millis value.

    I have toyed with reducing this overhead or blocking of other interrupts. But it is a major change and lot of work.

    I think changing to USE_MP3_Polled is a quick fix. Note that you need the MP3player.available(); in the main loop.

     

    #2171
    Bill
    Member

    From the Arduino Reference on attachInterrupt()

    Note

    Inside the attached function, delay() won’t work and the value returned by millis() will not increment. Serial data received while in the function may be lost. You should declare as volatile any variables that you modify within the attached function.

    I imagine this is because the user’s function is nested inside the Interrupt vector and therefore global interrupts remain disabled while the user’s function runs. The millis() method uses timer overflow interrupt to increment.

    AVRs support nested interrupts. I wonder if there’s any harm in just re-enabling global interrupts first thing in our refill function Michael? Might cause unexpected delays in the SPI stream to the decoder/SD card but that might be tolerable.

    Jim, What Michael suggested should work. But if your willing try editing your version of the library as such:

    void SFEMP3Shield::refill() {

    //Serial.println(F(“filling”));

    while(digitalRead(MP3_DREQ)){

    to

    void SFEMP3Shield::refill() {

    sei();

    //Serial.println(F(“filling”));

    while(digitalRead(MP3_DREQ)){

    and you might have to #include <avr/interrupt.h> at the top of the file. See if that breaks anything.

    #2172
    Anonymous
    Inactive

    Michael,

    I made the change to the header file and that fixed the problem. Changing it back made the problem return, so I think we can conclude that it is interrupt-related.

     

    Bill,

    Staying with the original header file, I made the change you suggested to the library, re-enabling the interrupts during the refill routine. That also appears to have fixed the problem. I did not have to include avr/interrupt.h.

    I will run it overnight to make sure the timing stays accurate and nothing overflows or anything, but I think that has solved the problem.

     

    Thanks to both of you for you prompt responses!

    #2173
    Anonymous
    Inactive

    I have tested this some more and it appears to be working fine with Bill’s fix – re-enabling the interrupts during the refill routine.

    Now the only remaining problem is with the poor accuracy of the Uno’s clock. Mine loses about 2 sec/hr. I have a Leonardo, which has a genuine crystal oscillator rather than the Uno’s ceramic resonator and appears to be much more accurate.

    Off-topic, but to get the Leo to work with the Sparkfun MP3 Shield, should I solder 3 jumpers D11 – ICSP4, D12 – ICSP1, and D13 – ICSP3 and no cuts on the Leo board, or is there a better way to do this rewiring?

    I can imagine there might be trouble (smoke?) if pins 11,12, or 13 were configured as outputs with these jumpers in place.

    Thanks again for your help!

    #2174
    Anonymous
    Inactive

    Further update:

     

    The modified library with the interrupts re-enabled during the refill routine is continuing to work well, with apparently no dropped millis() interrupts.

    I did notice however that when playing a file a couple of minutes long (3.92 MB) there is a delay of about 1.8 sec after the playTrack and before control is returned to my sketch. In other words,

    long startTime = millis();

    MP3player.playTrack(2);

    Serial.println(millis() – startTime);

    prints about 1800.

    I realize there is a lot going on during that time – opening the file and doing the initial buffer fill, but the library would be even more useful if that delay was not present.

    Once the song starts to play, it looks like my sketch gets about 77% of the CPU – not bad! I measured this by observing that a loop that takes 54 ms to execute when no song is playing takes about 70 if the song is playing, after the 1.8 sec delay.

    #2175

    First thing that comes to mind:

    Comment out <a href=https://github.com/mpflaga/Sparkfun-MP3-Player-Shield-Arduino-Library/blob/master/SFEMP3Shield/SFEMP3Shield.cpp#L827>SFEMP3Shield.cpp Line 827</a>

    You can see that if the file type is detected as MP3 that getBitRateFromMP3File(fileName); attempts to scan the file for the header to predetermine the bitrate. This is optional and may remove your noticeable delay for larger files.

    #2179
    Anonymous
    Inactive

    Commenting out the call to getBitRateFromMP3File() reduced the startup time from 1800 ms to 176.

     

    Thanks!

    #2181

    About the your CPU % observation. It is significantly dependent upon the bit rate of the audio file. A 192K will take a lot more than a 44K. Additionally you can significantly increase efficiency by setting:

    #define USE_MP3_REFILL_MEANS USE_MP3_Timer1

    or

    #define USE_MP3_REFILL_MEANS USE_MP3_SimpleTimer

    along with setting MP3_REFILL_PERIOD to an appropriate period to accommodate the bitrate of the audio file.

    This will more efficiently bundle the Reads from the SdCard and subsequent writes to the VS1053 into larger chunks. Noting that the SdFat Library pre-reads a cache of 500 bytes and the MP3 library writes little chunks of 32 bytes at a time, until the DREQ is full. Where I have seen a logic analyzer traces of it showing a lot of chunking that is removed when down more as a periodic refill. Better matching the SdFat’s 500 byte read.

Viewing 9 posts - 1 through 9 (of 9 total)
  • You must be logged in to reply to this topic.