Reading SD card While Executing wav_play and servo_angle
BTL24
Posts: 54
I have an application that requires an SD card to be read that contains 2 files, one a wav file and one a data file with servo commands. The concept is to play the wav file while the servos (in animatronic figure) move in sync with sound. The propeller with multiple cogs seems ideal for this application.
These are rather lengthy performances (minutes) so the SD card seemed to be the right place to store all the servo commands created while puppeteering the animatronic figure. Otherwise I would have used an array.
First, I have created a list of servo commands stored on an SD card file name test.txt (using data logger example program) and successfully have read the values back and executed the servo commands using servo_angle function.
Second, I have successfully played back a wav file (caliope1.wav) also stored on the SD card using the wav_player.
The trouble begins when I try and combine the two programs into one where the SD card needs to be read by both routines (See code below)...
The wav file starts playing back and the servo is moving according to the commands from the test.txt file until about halfway through the file. Then the serial terminal reports very high numbers for several servo commands and the servo stops moving (as evidenced with the serial terminal print out)... then it goes back to properly sending the correct commands...but servo still is dormant.
I believe the problem occurs because the main routine is attempting to read the SD card while wav player is also accessing the card. There is no apparent arbitration of the SD card I/O between the two routines and the error occurs (main routine read sees the data on bus from wav_play read). I believe I will need to lock out one routine while the other is reading the card. This would require buffering and a more complex interface.
Note: I have been reading other posts related to two functions accessing the same SD card, using 2 separate files, however, the conclusion is the same... let one cog do the I/O and send it off to other cogs for processing.
Any comments or thoughts? ....or will I have to write a custom wave player and servo commander functions?
Regards,
Brian (BTL24)
These are rather lengthy performances (minutes) so the SD card seemed to be the right place to store all the servo commands created while puppeteering the animatronic figure. Otherwise I would have used an array.
First, I have created a list of servo commands stored on an SD card file name test.txt (using data logger example program) and successfully have read the values back and executed the servo commands using servo_angle function.
Second, I have successfully played back a wav file (caliope1.wav) also stored on the SD card using the wav_player.
The trouble begins when I try and combine the two programs into one where the SD card needs to be read by both routines (See code below)...
#include "simpletools.h" // Include simpletools header #include "servo.h" // Include servo header #include "wavplayer.h" // Inculde Wave Player const char yay[] = {"yay.wav"}; const char caliope1[] = {"caliope1.wav"}; int DO = 22, CLK = 23, DI = 24, CS = 25; // SD I/O pins int main() // main function { sd_mount(DO, CLK, DI, CS); // Mount SD card FILE* fp = fopen("test.txt", "r"); // Open a file for reading int n = 0; wav_play(caliope1); //play caliopoe music while servos move wav_volume(6); pause(500); while(1) { int status = fread(&n, 4, 1, fp); // Read from SD & get status if(!status) break; // If no more, break loop n = n * 10; // Scale up value by 10... (10ths of deg for servo_angle) print("n scaled = %d\n", n); // Display value servo_angle(16, n); // Command servo with variable n on pin 16 pause(1000); // Pause for Servo to catch up } fclose(fp); // Close the file print("All DONE.....\n"); servo_stop(); wav_play(yay); pause(3000); wav_stop(); }
The wav file starts playing back and the servo is moving according to the commands from the test.txt file until about halfway through the file. Then the serial terminal reports very high numbers for several servo commands and the servo stops moving (as evidenced with the serial terminal print out)... then it goes back to properly sending the correct commands...but servo still is dormant.
I believe the problem occurs because the main routine is attempting to read the SD card while wav player is also accessing the card. There is no apparent arbitration of the SD card I/O between the two routines and the error occurs (main routine read sees the data on bus from wav_play read). I believe I will need to lock out one routine while the other is reading the card. This would require buffering and a more complex interface.
Note: I have been reading other posts related to two functions accessing the same SD card, using 2 separate files, however, the conclusion is the same... let one cog do the I/O and send it off to other cogs for processing.
Any comments or thoughts? ....or will I have to write a custom wave player and servo commander functions?
Regards,
Brian (BTL24)
Comments
I think the wav_play starts a new cog which then accesses the sd card while your main loop also accesses the sd card. So two different cogs try to read from SD.
I had a similar question some time ago, link: http://forums.parallax.com/showthread.php/149906-SD-Card-access-on-different-COG-s
Outcome was that it isn't possible.
What you need is code which runs in an own cog (not cogc because it won't fit with sd access) and acts as a file server to other cogs. That means you have to change the wav player to not directly access
the SD card but to use the worker cog. Also your code which reads the text file directly from SD card needs to use the file server cog.
But I don't have a example or interface to provide how to do this. But I'm interested in ideas, if someone has a good suggestion
Christian
Please notice:
1) its in FORTH, so you would have to translate the code to SPIN, or use forth
2) it does not use FAT, it directly accesses the 512 byte block on the SD. It very fast and simple, but is not compatible this swapping the SD to the PC. You send the files to the prop, and have the prop write them to SD; and have the prop send files to the PC. This is trivial but different, and unusual for folk that are used to swapping the SD to the PC.
Christian,
I agree. In fact it is your original thread/post that got me started down this path. I believe I will have to use one cog to act as file server as you say.
Thanks for the tip...
Regards,
Brian (BTL24)
prof_braino,
Wow...Forth. Hmmm... unfortunately, I don't know Forth, but it is good to hear that routines do exist that can allow two separate cogs access to same memory card.
I may read up on this and convert to C. Now if I can find the time.
BTW... I love your icon photo!
Regards,
Brian (BTL24)
If you've a C solution, let me know
Christian
Kye,
Boy, would be a great solution if I could fit the servo commands into prop memory!
I require very fluid/smooth movements of servos so I update them at 20 Hz rate minimum. One animatronic figure can consume up to 8 servo channels (mouth, pitch, tilt, yaw, eyes, brows, ears, nose, etc). If I assume 8 bits (1 Byte) per servo channel and a 20 Hz rate that would run a show for 10 minutes, the memory requirement starts to push 100 KB minimum. For Example...20 Hz x 60 sec/min x 10 min X 8 channels = 96 KB. If I use higher resolution, then numbers go up further.
It appears the SD card (GB of memory) would be the best solution to store these large files and retrieve commands as needed. However, I may have missed something....Is there enough RAM built into the Prop chip to manage this size? If not.. can extra external RAM be added (another possible slant to your solution)?
EDIT: Duh.... found this thread. Looks like external memory is available...http://forums.parallax.com/showthread.php/150469-Some-questions-regarding-the-Propeller-memory-card-by-Parallax?p=1214336#post1214336
Regards,
Brian (BTL24)
Absolutely!
Brian
If you want to ensure that you never get stalls from reading the SD card you would need to use a double-buffer and more cogs. You could use one cog to read both files into two buffers while the wave player and servo controller are reading from buffers that have already been filled. This could be done without locks, or you might use one lock for each pair of buffers.
I don't want to be a party pooper, but, the tools for doing everything you want to do in SPIN already exist. You'd be able to get to the goal much faster if you just need to do servos and play a wav file.
...
I thought the prop gcc file system driver used locks? Weird.
The best approach would be to extract the .wav player code from libwavplayer to your project and modify it to suit your purpose.
That is, define a new function for fread() that uses a lock, lock_fread(), so that the file access is moderated so that there are no collisions reading the SD Card. Then replace all fread() with lock_fread().
Maybe someone will contribute lock_fread() here? ... I may look at it later - no time right now.
There is no documentation for the feature, so whoever added that should probably speak up
Any idea how to use it?
If it's just for low level access, then it won't do what Brian needs.
Writing an lock_fread() would be better.
Btw, not every application needs locked access so it is not necessarily appropriate to add a lock to fread or other accesses in the Propeller library. However, a real operating system will have a mutex lock (or coincidentally named spin_lock) to protect file access. This could be revisited if necessary, but that requires rebuilding the distribution.
Kye...Looks like I will have to pull up the Spin Manual and take a look. Thanks for the tip.
Regards,
Brian (BTL24)
I utilize a program called Visual Show Animation (VSA) that allows a fairly extensive programming of servo based shows for animatronic characters. Using a joystick, one can puppeteer an array of servos and record their movements in sync with a sound track. One great feature of VSA is that they allow you to then export that servo show file to other applications.
I created a quick "Hello How are you" soundtrack, imported it into VSA, and then animated/recorded a mouth servo, in synch with that audio track. I then exported the data file of servo commands (.bin), loaded it onto my SD card, and then successfully had the propeller activity board read the file and play back this mini show via the servo_angle function... It worked great!
FYI....I played the audio on my computer while I started the Propeller board with servo attached. The timing was trimmed up using a Pause command to keep in sync with the audio file. Looks like half of my problem is solved.
Now to get the Propeller to play the audio too.... the next big step.
How do you cook an elephant?....cut it up into pieces....same as solving a problem.
Regards,
Brian {BLT24)
Regards,
Brian (BTL24)
hmmm...I have to think about that. My plan was to have the wav_play routine automatically be locked out when the servo read occurs every 30 ms for just a few u sec. I was going to use his code as an example of how yo modify wav_play.
Will have to give this more thought.
Regards,
Brian (BTL24)