reading binary on an sd card

Go To Last Post
187 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I am posting this here so that the community can benfit from any outcomes...
So I have a binary file stored as a BTc file on an sd card. I am using fatfs by elm chan to access the sd card. No problems there. I want to read the binary file in. The binary file contains sound converted from wav, which I am trying to then play.
If I store the wav file on the avr in an array I have no problem playing the sounds. But I want to be able to store it on the sd so I can have larger files.

The code reads in 512 bytes from the card, plays it and reads the next 512 bytes. At this stage I dont know if it will sound broken up, but Ill come to that if it becomes a problem.

I am dumping each 512 bytes to the serial at the moment to see if it is reading it correctly. What appears is the same as what I get if I open the file in notepad or hexeditor, however I dont know what a binary file should look like, but it doesnt look right.
Also it doesnt play the correct sounds either, I just get what sounds like static ddzzzdzzdzzzdzzz (kind of...)
I get about ten lines of this:

Quote:
00000000 AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA ................
00000010 AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA ..

Then a load of lines that look more like they do store data, say 100 lines or something:
Quote:
00000090 AA AA AA AA AA AA AA AA A9 54 AA AD 6B 56 AA 55 .........T..kV.U
000000A0 29 54 AA AA D5 AD 55 55 4A AA 55 4A AA AD 5A D5 )T....UUJ.UJ..Z.
000000B0 55 55 2A 55 2A AA AB 5A D5 55 55 2A 55 4A AA AA UU*U*..Z.UU*UJ..
000000C0 D5 AB 55 55 52 A9 52 AA AA B5 56 AA B5 52 AA A5 ..UUR.R...V..R..
000000D0 54 B4 B3 55 55 AA AA AA A9 96 4B 2B 2C D5 55 55 T..UU.....K+,.UU
000000E0 69 66 AA 6A 55 35 32 D4 D3 55 55 55 55 69 55 2C if.jU52..UUUUiU,
000000F0 D4 CD 53 2D 2B 55 55 53 4C D3 34 CD 4C B2 B2 D4 ..S-+UUSL.4.L..

And finally more 'AA AA's...

My buffer is global - thats the array that the play_sound function is 'playing', the code that opens and reads the file, and plays the soung is:

res = f_open(&fsrc, "0:0.BTc", FA_OPEN_EXISTING | FA_READ); 
res = f_open(&fdst, "0:btc.txt", FA_CREATE_ALWAYS | FA_WRITE);
for (;;){//loop to complete playing file
res = f_read(&fsrc, buffer, sizeof(buffer), &br);    /* Read a chunk of src file */ 
 for (bp=buffer, ofs = 0; ofs < 0x200; bp+=16, ofs+=16){
					put_dump(bp, ofs, 16);
					}
					play_sound();
	
		if (res || br == 0) break; /* error or eof */ 
		res = f_write(&fdst, buffer, br, &bw);               /* Write it to the dst file */
        if (res || bw < br) break; /* error or disk full */
}

(I am also writing the data to a text file to see what it looks like - the same...)
One thing Im note sure about is whether a .BTc file is holding binary, and whether I should actually be seeing 1's and 0's on the serial

When I have it all working, If people seem interested I was going to post the entire project here because I think it would be useful to people who would like to use fatfs within their projects. There is a lot of stuff here on using it, but while learning how to use it I found it quite hard work to get a barebones version. The one on elm-chan's site is quite over the top for straight forward projects. This version also writes to serial using printf which may be useful for people (like me) who are not electronic engineers, but need to implement embedded electronics (like me), and who are used to programming in C on a pc.

I have removed all of the unecessary files to slim it down to its minimum size.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Fortunately I happen to know that a .BTc file is a binary file generated by Roman Black's BTc sound encoder software. The data you describe is consistent with that I have seen myself, the AAs at the beginning and end are the lead-in and lead-out of the sample. Ideally it would be silence so feel free to truncate those sections or replace the AA's with 00's

If you are still using the play_sound() routine I posted for you a while back then its not going to work very well while reading from sd card. You will most likely need some sort of ring-buffered and possibly interrupt driven system to obtain any kind of acceptable sound quality. My example was purely a demonstration.

The Roman Black audio technique relies on precisely timing a sequence of bits through a carefully designed filter, stops and starts (however small) in the bit stream will make it sound terrible.

Last Edited: Tue. Nov 8, 2011 - 11:43 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ahh maximax, thank you for your response, I was talking about that with clawson earlier. Yes I still am using your play_sound routine. So you think that IS what a binary file should look like.
The thing is the quality I get from the code you originally gave me was very good - I mean very clear not Beethoven, but clear. Now it really is white noise, I would have expected audibility.
What Im not sure of is whether I should be reading it like this, or possibly populating an array with the hex converted from the BTc file. The same array you populated with sound data. I think though that would end up using a HUGE amount of ram because I cant use pgmspace as the array isnt a constant array.
clawson mentioned two buffers, fill one while playing the other? - Surely that would just be an extra line of code within your play_sound function - reading into the buffer and each time round it would swap buffers??? Is that what the term 'ring-buffered' means?
Yeah I planned on truncating the AA's however if it worked with them it would work without...
I also wondered whether you could just read a .wav straight off the sd card? As opposed to having to convert to binary and also would it benefit in any way?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

a.mlw.walker wrote:
I also wondered whether you could just read a .wav straight off the sd card? As opposed to having to convert to binary and also would it benefit in any way?
Yes you can, see AVR appnote 335. Quality may be slightly better though memory/storage usage would be greater (depending on sample rate).

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

And what about the two buffers? would I just call the function to fill those each loop of your function?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

res = f_read(&fsrc, buffer, sizeof(buffer), &br); /* Read a chunk of src file */

sizeof(buffer) == 2

Did you realise?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Did I realise what? That it SHOULD be size 2 not 512? or it IS 2 and should be 512? Im not sure what your saying.

Why is it 2, why not 512?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Should it be something like
fread into buffer one
play sound
fread into buffer two
play sound..

no because that doesnt get around the delay issue.

If i run a function like this from main()

void pwm(void)
{

    DDRB = _BV(PB5); //OCR1A

   
    OCR1A = 0;
    
    /* Setup Timer0 */
  
    TCCR0|=(1<<CS00);
    TCNT0=0;
    TIMSK|=(1<<TOIE0);
    sample_count = 4;
}

that would set up OCR1A for pwm, and timer 0 right?

and then for the ISR of timer0:

ISR(TIMER0_OVF_vect)
{
     
         sample_count--;
         if (sample_count == 0) 
            {
             sample_count = 4;           
             OCR1A = buffer[sample++];
             if(sample>sizeof(buffer))sample=0;
            }
} 

with these two being global:
volatile uint16_t sample;
int sample_count;
(along with buffer)

and so at what point do i read the buffer - or the two buffers??

res = f_read(&fsrc, buffer, sizeof(buffer), &b

Is this the right idea for playing wav from the sd card?
I still dont know why i cant read the binary into the Roman Black method... and use two buffers for that

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ahh! I think I have it working - not using sd card though yet, It is speaking the wav saved in a c header file called pcm_sample. However it is saying it too fast.
I think the problem is on this line:

TCCR1B = _BV(CS10) ; //| _BV(CS11);

I have tried all combinations from this table on this website
http://www.avrbeginners.net/arch...
about 2/5 of the way down, but I think I need to divide the clock by 512 because I am using a 16000000Hz cpu frequency. however the table only has for 256 and 1024.
Am i right in thinking that? Or maybe I need 128??

The data in the header file that was converted from a wav is like this:

 121,120,117,118,121,118,120,123,126,131,134,141,144,146,149,148,148,146,142,139,136,131,127,123,115,102,89,

If I have wav on the sd card, and I read it using the cyclic buffer, can I just fread the wav straight into OCR1A?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

OK yeah Ive got it to play at the right speed now. Speech is fine. Its just working out how to use a buffer to read the .wav now I just cant figure the order
i initialize the IoInit to setup the interupts for the fatfs system, then I initialize the timers for the pwm, at that point I am then calling sei(); to enable interupts.
I need to do that because I am tryinig to read from the sd card into the buffer, but that will also start the timer to play a song wont it? at this point the buffer is empty...

Starting with the loop in main() which continually reads a file from the sd into the buffer until the end of the file.
One timer interupt is reading into the buffer while another is reading from the buffer to play the data stored in the buffer to the pwm. How do I reload the buffer ready for the next part, or swap over buffers - Im confused

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

Did I realise what? That it SHOULD be size 2 not 512? or it IS 2 and should be 512? Im not sure what your saying.

Why is it 2, why not 512?


'buffer' is a pointer. On AVRs pointers are 2 bytes wide so sizeof() returns 2.

Cliff (in cognito)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

But the OP said the buffer was declared global. If it's visible when sizeof
is applied, sizeof will return the size of the array, not 2.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I can see why that may be right, because to dump the buffer the following is used:

for (bp=buffer, ofs = 0; ofs < 0x200; bp+=16, ofs+=16){
					put_dump(bp, ofs, 16);

where put_dump is


static
void put_dump (const BYTE *buff, DWORD ofs, BYTE cnt)
{
	BYTE i;


	printf("%08lX ", ofs);

	for(i = 0; i < cnt; i++)
		printf(" %02X", buff[i]);

	uart_put(' ');
	for(i = 0; i < cnt; i++)
		uart_put((buff[i] >= ' ' && buff[i] <= '~') ? buff[i] : '.');

	uart_put('\n');
}

Therefore should maybe I be doing

for (bp=buffer, ofs = 0; ofs < 0x200; bp+=16, ofs+=16){
}

and then pass bp to OCR1A. If thats so how do i get it to update bp between each pass to OCR1A? - i.e so that it doesnt 'miss' a chunk of sound due to the interupt that updates OCR1A?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I think I am sooo close. In my main function:

while(1){//loop to complete playing file
if(read==1)
	{
		if(buf_1)								//load to first buffor
		{	
		res =	f_read(&fsrc, bufor0, 512, &br);
		}else									//load to second buffor
		{	

		res =	f_read(&fsrc, bufor1, 512, &br);
		}	
		read=0;
	}
				
		if (res || br == 0) break; /* error or eof */ 
	
}

The interupt routine:

ISR(TIMER0_OVF_vect)
{
     
         sample_count--;
         if (sample_count == 0) 
            {
             sample_count = 8;         
	if(buf_1)								
		{		
             if(sample>bufor1){
			 sample=0;
		}
		}	else
		{	
             if(sample>bufor0){
			 sample=0;
		}	
			 }
            }
			playbuffer();
}
void playbuffer()				//interupt soun processing
{
		ii++;
		if(buf_1)								//play from first buffor
		{	
			OCR1A=(unsigned int)bufor1[indeks];		//set PWM VAlue
			OCR1A=(unsigned int)bufor1[indeks+1];
			if(indeks>=(512-2))
			{
				buf_1=0;
				indeks=0;
				read=1;
			}
		}else									//play from second buffor
		{	
			OCR1A=(unsigned int)bufor0[indeks];		//set PWM VAlue
			OCR1A=(unsigned int)bufor0[indeks+1];
			if(indeks>=(512-2))
			{
				buf_1=1;
				indeks=0;
				read=1;

			}
		}	
		indeks=indeks+2;
	
//TCCR1B&=(~((1<<CS12)|(1<<CS11)|(1<<CS10)));
}

Just to confirm these are my globals:

volatile unsigned int ii=0, indeks=0, buf_1=1, play=1, read=1, time1;
volatile unsigned char bufor1[512];
volatile unsigned char bufor0[512];

Now i am using two buffers - am i close!?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I had to re-format this section to be able to understand it, still it does not appear to do very much other than call playbuffer() at the end. Bad idea!

ISR(TIMER0_OVF_vect)
{
	sample_count--;
	if (sample_count == 0)
	{
		sample_count = 8;         
		if(buf_1)                        
		{      
			if(sample>bufor1)
			{
				sample=0;
			}
		}
		else {   
			if(sample>bufor0)
			{
				sample=0;
			}   
		}
	}
	playbuffer();
} 

The ISR should only set a flag, you poll for this flag inside the playback loop and clear it after each overflow.

Hope that makes sense.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

But the OP said the buffer was declared global. If it's visible when sizeof
is applied, sizeof will return the size of the array, not 2.

Do you reckon?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Maximax, its supposed to check whether its at the end of the buffer so to re fill it, are u saying scrap it all and just call playback?
Clawson u seem quite knowledgeable on this topic, I was hoping u could elaborate as to whether the functions I have posted are on the right track and whether u could spot any obvious mistakes?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Yeah ISR function was wrong.

so my while(1) loop in main should continually call playbuffer function?
And then the ISR function should be continually updating the buffers?
pseudo-code:

main(){
fatfs_init();
pwm_init();
sei();
while(1){
playbuffer();
}
}
void pwm_init(void)
{
    /* use OC1A pin as output */
    DDRB = _BV(PB5);

    TCCR1A = _BV(COM1A1) | _BV(WGM10);
      
    TCCR1B = _BV(WGM12) | _BV(CS10);
    OCR1A = 0;
    /* Setup Timer0 */
  
    TCCR0|=(1<<CS00);
    TCNT0=0;
    TIMSK|=(1<<TOIE0);
    }
ISR(TIMER0_OVF_vect) 
{  
    	if(read==1)
	{
		if(buf_1)								//load to first buffor
		{	
		res =	f_read(&fsrc, bufor0, 512, &br);
		}else									//load to second buffor
		{	

		res =	f_read(&fsrc, bufor1, 512, &br);
		}	
		read=0;
	}
}

finally playbuffer:

void playbuffer()				//interupt soun processing
{
		ii++;
		if(buf_1)								//play from first buffor
		{	
			OCR1A=(unsigned int)bufor1[indeks];		//set PWM VAlue

			if(indeks>=(512-1))
			{
				buf_1=0;
				indeks=0;
				read=1;
			}
		}else									//play from second buffor
		{	
			OCR1A=(unsigned int)bufor0[indeks];		//set PWM VAlue

			if(indeks>=(512-1))
			{
				buf_1=1;
				indeks=0;
				read=1;

			}
		}	
		indeks=indeks+1;
	
}

I have been trying to follow this, but his timers are very different, and I dont know if i have grasped what the ISR function should be doing?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Now I'm confused - is the data in the files on the SD in Roman Black .btc format or is it in .WAV PCM format?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

haha im confusing everyone!

the sound on the sd cad is wav converted to .pcm format. From what i understood maximax was saying, the roman black way wont work very well now that it is being read from the sd card. So i read around and found this site:
http://www.pro-qwerty.com/files/...
i am trying to port the code so that it uses real interupts. I am getting myself in a bit of a tiz with this

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Have you just tried a synchronous rather than asynchronous solution for reading sectors of PCM? Exactly how many milliseconds does it take to read a sector anyway?

If it's longer than the playback time between two successive PCM samples (let's say it's 8kHz audio so you have 1/8000s between bytes - that is 125us) then I guess you do need to asynchronously read the data into buffers so I'd put the playback on a 125us interrupt which basically just takes a byte from the active buffer and puts it into OCR then I'd do the sector reading in the foreground as fast as possible though if you find you can read faster than it's consumed you'll have to put a block in so that at times it holds off making the read for a bit - maybe use a "low water mark" in the active buffer to trigger the read of the next buffer?

Cliff

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ok.
Just to be clear, are you suggesting this because the code I posted above 'looks' like it is doing the right thing? I mean I dont really want to move on until im confident (or not) about the above code. Ive never used double buffers sd AND pwm before, and Im slightly out of my understanding at the moment

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

AFAICS you are doing it "the wrong way round". The thing that "must happen on time" is the loading of a value into OCR so that's what I'd be doing in the timer interrupt (set to 8kHz or whatever the audio sample rate is). Each time the timer interrupt occurs just get a byte/word and put it in OCR. Meanwhile try to read the data from the code in the slowly plodding main()::while() loop. the trick is whether the main() code can fill buffers quickly enough to satisfy the time interrupt that is consuming the data. If it gets to the end of buffer 1 before the main loop has had a chance to read the contents of buffer 2 then you have a problem. If it is 8kHz (125us) and you have 512 bytes buffers then you have 125us*512 = 64ms to get the "next" buffer ready before the switch. I'd be astonished if you could not read 512 from a card in 64ms so this should be "do able". In fact I'm guessing that you could probably read it a lot quicker (though the worst case will be when the FAT increments the cluster). Let's say you can read it in 25ms. The you actually have 64-25=39ms.This is why I said you'd possibly have to hold off on reading for a while. It's probably sufficient for the main() code to simply wait for the ISR/timer/OCR code to get to byte 512 in buffer 1 then it signals this to main() (that the buffer switch-over has happened) and now main() can get started on reading the next buffer full.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

What are you using to convert wav to pcm? I cant find anything to do it?
Is pcm the same format as raw?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ahh Ive found switch. Converts anything to pcm raw. got some options though,
I want 8 bit, sample rate 16KHz mono I think and then
there are different pcm versions of raw.
Do I want 16bit PCM Intel/Big Endian or 8 bit signed/unsigned? There are a few others aswell

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

For the purpose of AVR playback you should find that 8bit Mono 8kHz is quite sufficient. If you use 8bit remember it is unsigned with a 0x80 offset so 0x80 is "silence". If, on the other hand, you use 16bit then it is signed and the "silence" point is 0x0000.

In days gone by I used to Audacity for sound conversions but it's become more of an "MP3 creator" than a general audio tool these days and I'm not even sure if it still supports things like 8bit PCM. There used to be a brilliant sound editor called CoolEdit but one of the big conglomerate software companies (was it Adobe?) bought them out simply so they could scrap the competition to their own (inferior!) offering - which is a big pity!

PS I don't know the legality of this website but: http://www.oldapps.com/CoolEdit.php

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ok so on my sd card i have 0.raw - I converted using switch.
pseudo main:

IoInit();//initiate timer 2 for fatfs
pwminit();//Initiate timer 0 for pwm
sei();//start interupts
disk_initialize();//initialze sd card
f_mount();//mounts the disk
f_open("0.raw");//opens the file
/*set flags*/
buf_1=1;
indeks=0;
read=1;
play=1;
/*finished setting flags*/
while(1){
if(read==1)//checks if required to read cos buffer just finished playing
	{
		if(buf_1)								//load to first buffor
		{	
		res =	f_read(&fsrc, bufor0, 512, &bw);

		}else									//load to second buffor
		{	

		res =	f_read(&fsrc, bufor1, 512, &bw);
		}	
		read=0;
	}

Inside ISR(timer0) it is checking which buffer to use, sending value of buffer to OCR1A, adjusting the flag values, and increasing the index to read from buffer. Im not sure if sei(); is in the right place, and im not sure if the timer is setup correctly - thats my main worry:

 /* Setup Timer0 */
  
    TCCR0|=(1<<CS00);
    TCNT0=0;
    TIMSK|=(1<<TOIE0);
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Actually Timer looks good I think. serial shows me i can read the file...
It cant read into a buffer unless read == 1, so that is forcing it not to write faster than its playing. ummmm...

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

a.mlw.walker wrote:
From what i understood maximax was saying, the roman black way wont work very well now that it is being read from the sd card.
What I meant was that my example code would not work well, in particular the play_sound() function.
It, like the pcm method, would need to be buffered somehow.
The issue, as clawson rightly points out, is whether you can read from the sd card fast enough, I would guess most likely you can.
Using the Roman Black method could actually buy you quite a bit more time in this regard since you get 8 single bit samples per byte.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I want to see if i can do it this way now i (think) i am alsmost there.
Could it be the "header" of wav files causing me problems? Could that be why its not working?

Or could it be the fact that Im running 16MHz not 8MHz? just might mean things are having half the time to do something?
What happens on the very first call - the while loop has to fill the buffer but the interupt will spark - it may not have finished.
I just thought I would get something through on the speakers by now even if it was garble.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Please explain what is meant by 'not working' and post a fully compilable copy of your code that demonstrates this problem.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

It compiles fine. 7 warnings but nothing serious. I have tried to follow the tutorial on here for timers, and the advice ive got here to work through the double buffers. I know it can read the 'zero.raw' file because I can get it to dump the buffer to the serial and its the same. i get no sound what so ever through the speaker off OCR1A, which is PB5 on the mega128.
Thanks

Attachment(s): 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

Could it be the "header" of wav files causing me problems? Could that be why its not working?

The header (unless you offset to skip it) will be played as data which will make a (very short!) noise at the start but after that it will play normally. As it's PCM then unlike ADPCM or BTC the subsequent data is not dependent on the preceding data.
Quote:

Or could it be the fact that Im running 16MHz not 8MHz? just might mean things are having half the time to do something?

The CPU clock doesn't matter - it's the frequency of the playback interrupt that matters. All that happens if you get it wrong is that the sound will play too fast/slow - but will still be recognisable.
Quote:

What happens on the very first call - the while loop has to fill the buffer but the interupt will spark - it may not have finished.

You have to seed it with a buffer full before you start so do one buffer read outside the main loop before the process starts.

BTW forget trying to play from the card just yet - have you got some data built into the C as a data array to play already? IOW does your PCM playback actually work - you can't try to debug everything at once so you have to build/test small parts. Only move on to the next when the previous building blocks are proven.

Quote:

7 warnings but nothing serious

What a very curious comment. You say that as if warnings don't matter. Often they are telling you about something that is critically wrong.
Quote:

I have tried to follow the tutorial on here for timers

Well if you can't even trust the timers even forget about trying to play a C array. Just do some work until you are totally convinced you have the timer working correctly. Let's say you run it at 8kHz then inside the ISR have it count to 4000 then toggle an LED. Does that LED flash at 1Hz. Don't move on until it does.

The key thing in all this is don't just write everything on day one, bolt it all together and expect it to work. It won't and when it doesn't you won't have a clue as to where the fault lies. The way you do software development is to write things in small chunks from the bottom up. Do not move to a higher level until you are convinced your "foundations" are solid. Then build the walls. Then finally put the roof on at the end when it is all drawn together.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Oh I am 99.99% sure that the timers work and that I could play the pcm through the pwm pin. The 1% is because Ive been fiddling around since.
I got some pcm from here,
http://avrpcm.blogspot.com/2010/...

and also used it as a basis for my code - It plays "Its working" - quite a good check. The problem starts (I think) when I read from the sd card.
However - because the buffers werent full before the while loop maybe thats the problem But surely I need to stop the timers interupting and playing also before they are full for the first time - because It doesnt matter about the while loop - the interupt still interupts whether or not if its in the while loop.

The warnings have never caused issues to date. On this line

static FILE mystdout = FDEV_SETUP_STREAM(uart_put, uart_get, _FDEV_SETUP_RW);

../main.c:21: warning: initialization from incompatible pointer type

and the fread lines have:

		res =	f_read(&fsrc, bufor0, 512, &bw);

../main.c:272: warning: passing argument 2 of 'f_read' discards qualifiers from pointer target type

But it does still fill the buffer - I can dump it.

Yeah i did make sure the timer is playing the pcm in the array correctly, the only thing in the ISR of the site i posted abouve that I havent got - because I think it isnt relelvent to me - I couldnt see what it was doing wsa the conditions to do with 'samplecount'.

This is the code FROM that website i posted above in ISR. I have removed everything to do with samplecount from my version of the code

 sample_count--;
         if (sample_count == 0) 
            {
             sample_count = 4;           
             OCR1A = pgm_read_byte(&pcm_samples[sample++]);
             if(sample>pcm_length)sample=0;
            }
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

The warnings have never caused issues to date

That's no excuse.
Quote:

../main.c:21: warning: initialization from incompatible pointer type

That's likely telling you that the interface you have provided to uart_put() or uart_get() is wrong. That is pretty serious and it could just be an accident if it appears to work.
Quote:

../main.c:272: warning: passing argument 2 of 'f_read' discards qualifiers from pointer target type

The bufor0 is the wrong type. The qualifier may be 'const' or 'volatile' or something?

The last code you quote appears to be playing the samples at 1/4 the frequency of the timer. I think I know why they did this - it's so the PWM frequency is not in audio range but this does mean the timer needs to run at 32kHz.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Yeah I noticed that if I changed sample_count to 8 it halved the speed. I removed it - my ISR seems logical.
If I change uart_get() to NULL, I have one less error - I never got around to testing reading from the serial - havent require dit as yet, just stuck uart_get in there - have changed it back to NULL now.

bufor0 is volatile unsigned - unsigned because the 8 bit data is unsigned? If I change bw from WORD to UINT then i get another two less errors.
So now down to 4 warnings. To do with the state of bufor0 in fread.
I'll sort that, but at the moment Static through the speakers would be a God send!

How come when I open a pcm file in notepad or whatever it looks like absolute garbage, and the pcm on that website i quoted is nice integers.
Also timer0 is 16 bit right? so do i need to add 128 to each value to get it to the middle of the range - you said 0x80 was zero for 16 bit i think?
Lastly my initial duty cycle is 0. Thats ok right, because we are setting the duty cycle in the ISR - and that decides the length of time the pin is high. It shouldnt be 128 to put it in the middle of the range?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

bufor0 is volatile unsigned - unsigned because the 8 bit data is unsigned?

I guess you made it "volatile" because it's shared between main() and ISR()? On the whole it's not necessary with arrays (assuming that more than one element is being used). If you are going to keep the "volatile" then cast it away in the call to f_read() to quell that warning.
Quote:

How come when I open a pcm file in notepad or whatever it looks like absolute garbage, and the pcm on that website i quoted is nice integers.

You cannot view/edit a binary file with a text editor. To be honest I'm amazed you got this far with SD cards without having a copy of WinHex from Xways?!? That's what you should be using to open/view the contents of a binary file.
Quote:

Also timer0 is 16 bit right? so do i need to add 128 to each value to get it to the middle of the range - you said 0x80 was zero for 16 bit i think?

Don't get things confused - the width of the timer has no bearing on things - it's the width of the sample data that matters.
Quote:

It shouldnt be 128 to put it in the middle of the range?

Well in theory it should. But since that first sample only lasts for 125us it doesn't really matter what the value is.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Yeah I do have a hex editor and did open it in the hex editor, but it doesnt show me integers it shows me hex!

Quote:

81818180808080807F807F7F7F7F7F7F7F7F808080808080808080808080808080
7F7F7F7F7F7F7F7F7F7F7F7F808080808080808080808080808080808181818182
8282828383838383838383828385858586868786858584827F7D7B7874706D6A

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Yeah, so? Even I can see that data is:

0x81, 0x81, 0x81, 0x80, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x80, etc.

That's "silence" at the start. It's only towards the end of the clip that you quote it starts to swing more noticeably away from the 0x80 datum.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

so does it matter whether the OCR1A pin is sent hex or intergers? If it has to be integers could that be the probelm?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

is sent hex or intergers?

If you tell me what the difference between "hex" and "integers" is I might have a hope of answering that. By "hex" are you talking about a parseable ASCII string such as "0x7F" or the value 0x7F? The latter *IS* and integer value.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Here's a way of generating a sine wave in ram to play to test the 8 bit pwm playback

float pi,twopi,fsamp,sigfreq,t,dt,omega;

  twopi=2.0*pi;
  fsamp=50000.0; //samples/sec   >6 x highest filter freq (10khz x 6 = 60KHz + fudge)
  sigfreq=640.0; //generated freq
  dt=1.0/fsamp;  //sec per samp
  omega=twopi*sigfreq;

//------------------
void calcsamps(void){
//calc buf full of 10 bit unipolar input signal freq samples 
unsigned int n;
  
  t=0.0;
  for(n=0; n < buflen; n++){
    t+=dt;
    buf[n]=(int)(255.0*sin(omega*t))+511;
  }
}

In the 8 or 16khz int handler, just grab the next sample out of the buffer and store it in the OCR. You read the next buffer full in the main program.

Imagecraft compiler user

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

When it reads into the buffer, what stops it just reading the FIRST 512 bytes every time it reads:
I mean here:

while(1){//loop to complete playing file
//res = f_read(&fsrc, buffer, sizeof(buffer), &br);    /* Read a chunk of src file */ 
if(read==1)
	{
		if(buf_1)								//load to first buffor
		{	
		res =	f_read(&fsrc, bufor0, 512, &bw);

		}else									//load to second buffor
		{	

		res =	f_read(&fsrc, bufor1, 512, &bw);
		}	
		read=0;

	}
	
}

Nothing tells the buffers to move forwards and read a new section of the file does it?
And should the pwm interupt - ISR(timer0) be disabled until the while loop? Before that should only the fatfs interupt be enabled?

BTW, I did:

res =	f_read(&fsrc, bufor1, 512, &bw);//load bufor1 for the first round
	sei();
	printf("buf1 = [percent]d\n", bufor1);//print error code

to load the buffer before the while loop, however on printing it I get buf1 = 2246 on the serial. Surely thats not right?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

Nothing tells the buffers to move forwards and read a new section of the file does it?

http://elm-chan.org/fsw/ff/en/re...

It tells me it moves the file pointer forward to number of bytes read. So the next read will get the following number of bytes.

you can use f_seek to move the file pointer to where you want it.

Why would you want to disable pwm interrupts?

As the others have said, you want to debug your pwm output stuff separate from your file system stuff. You've been given example code. Onve you've got your buffering and pwm output working, then integrate the filesystem stuff. Do you really think the experts have some magical knowlege that allows them to perform magic? I can tell you we divide and conquer.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

Nothing tells the buffers to move forwards and read a new section of the file does it?

Yes, files by their very nature are "streams". Every time you fgetc()/fread()/fgets()/whatever the "file pointer" moves on by the number of bytes just read. This continues until feof()==TRUE marking the end of the file. If you don't want to read sequentially like this but want to read a specific bit of a file you fseek() which sets the position of the file pointer. The file pointer itself is just one member of the fsrc structure. In fact (amongst other things) it's because reading needs to update it that you pass the address of the fsrc structure in the first place. Inside FatFs it will be doing something like "fsrc->file_pointer += bytes_read".

To be honest I'd forget FatFs and the AVR for a short while. Instead write some file processing programs on the PC using fopen()/fread()/fwrite()/etc. and get the hang of how basic file processing works (which is MUCH easier on a PC as you can (a) use great debuggers and (b) just printf() to stdout with no hassles.

Here's an example file processing program you could easily build on a PC:

#include 
#include 
#include 

typedef int bool;
#define false ((bool)0)
#define true  (!false)

FILE * fin;
FILE * fout;
char * p;
unsigned int n;
bool first = true;

int main(int argc, char * argv[]) {
	char buffer[256];
	int count;

	if (argc != 2) {
		printf("usage: makeavrc file.bin\n");
		exit(0);
	}
	strcpy(buffer, argv[1]);
	if (strchr(buffer, '.')) {
		p = strchr(buffer, '.');
		*p = 0;
	}
	strcat(buffer, ".c");
	fin = fopen(argv[1], "rb");
	fout = fopen(buffer, "wt");
	strcpy(buffer, "unsigned int ");
	strcat(buffer, argv[1]);
	p = strchr(buffer, '.');
	if (p) {
		*p = 0;
	}
	strcat(buffer, "[] = {\n");
	count = 0;
	while(!feof(fin)) {
		fread(&n, 2, 1, fin);
		if (!feof(fin)) {
			fprintf(fout, "%s 0x%04X", first ? buffer: (count == 0) ? ",\n" : ",", n);
			first = false;
			count++;
			if (count == 8) {
				count = 0;
			}
		}
	}
	fprintf(fout, "\n};\n");
	fclose(fin);
	fclose(fout);
}

This is a programs that reads in a .bin file and writes it out as C source but as a uint16_t array rather than the more usual uint8_t array.

You'll notice that the POSIX file functions are fopen()/fread()/fprintf()/fwrite()/flcose()/etc. but they are very similar in operation to Chan's f_*() functions which are loosely modelled on POSIX, (in fact it's a bit of a shame he didn't go the whole hog and just implement POSIX in which case this same PC source code would work with no change on an AVR)

Quote:

however on printing it I get buf1 = 2246

But your printf() is printing "bufor1" (which is the address of the buffer), not "res" (the result code).

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

As two seperate entities, I have no problem getting them working - on its own the fat file system can read the file on the sd card, and on its own the pwm is working - I have this working right now:
http://avrpcm.blogspot.com/2010/...

In fact right now, it initializes the IO for fatfs, prints hello world, mounts the disk, opens the file zero.raw, reads into the first buffer, then cli(); then pwm is initiated then sei(); then while loop which checks which buffer should be filled and fills it, all the time it is saying "Its Working" on the speaker.
ISR(timer0) executes:

sample_count--;
         if (sample_count == 0) 
            {
             sample_count = 8;           
             OCR1A = pgm_read_byte(&pcm_samples[sample++]);
             if(sample>pcm_length)sample=0;
            }

reading from the header file. All no problems at all.
Its just getting it from the buffers that are being filled that is the problem. but this tells me I have setup OCR1A correctly because that code hasnt changed. In fact the only thing that has changed is the contents of ISR(timer0) - its just reading from a different location - the header file instead of the buffers.
But that confirms OCR1A is setup correctly.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Well, get it working with the buffers then and pull your data from progmem. Once that is working, then integrate the filesystem stuff.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Yeah ok, so i am reading from the progmem into my buffers. I am reading the first chunk outside of the while loop:

	for (i=0;i<512; i++){
		bufor1[i]= pgm_read_byte(&pcm_samples[i]);
		printf("bufinit[[percentd] = [percent]d\n",i, bufor1[i]);
		}
cli();
pwm_init();
sei();
printf("beginning\n");
j=1;
while(1){//loop to complete playing file
//res = f_read(&fsrc, buffer, sizeof(buffer), &br);    /* Read a chunk of src file */ 
if(read==1)
	{
		if(buf_1)								//load to first buffor
		{	
		for (i=0;i<512; i++){
		bufor0[i]= pgm_read_byte(&pcm_samples[(j*512) + i]);
		
		j = j++;
		}
		//res =	f_read(&fsrc, bufor0, 512, &bw);
		
		}else									//load to second buffor
		{	
		for (i=0;i<512; i++){
		bufor1[i]= pgm_read_byte(&pcm_samples[(j*512) + i]);
				}
		j=j++;
		//res =	f_read(&fsrc, bufor1, 512, &bw);
		}	
		read=0;

	}
	
	
}

probably not the nicest way of going about it, but as you can see I am printing the initial one to the terminal and that is coming up as 128, 128 etc etc - like it is in progmem.
j is ised to move to the next 512 bytes of progmem.
I get some pretty impressive sounding static.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Yeah ok, so i am reading from the progmem into my buffers. I am reading the first chunk outside of the while loop:

	for (i=0;i<512; i++){
		bufor1[i]= pgm_read_byte(&pcm_samples[i]);
		printf("bufinit[[percentd] = [percent]d\n",i, bufor1[i]);
		}
cli();
pwm_init();
sei();
printf("beginning\n");
j=1;
while(1){//loop to complete playing file
//res = f_read(&fsrc, buffer, sizeof(buffer), &br);    /* Read a chunk of src file */ 
if(read==1)
	{
		if(buf_1)								//load to first buffor
		{	
		for (i=0;i<512; i++){
		bufor0[i]= pgm_read_byte(&pcm_samples[(j*512) + i]);
				
		}
j = j++;
		//res =	f_read(&fsrc, bufor0, 512, &bw);
		
		}else									//load to second buffor
		{	
		for (i=0;i<512; i++){
		bufor1[i]= pgm_read_byte(&pcm_samples[(j*512) + i]);
				}
		j=j++;
		//res =	f_read(&fsrc, bufor1, 512, &bw);
		}	
		read=0;

	}
	
	
}

probably not the nicest way of going about it, but as you can see I am printing the initial one to the terminal and that is coming up as 128, 128 etc etc - like it is in progmem.
j is used to move to the next 512 bytes of progmem.
I get some pretty impressive sounding static - although a very slight squeak at the beginning - maybe its playing very very fast?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

I get some pretty impressive sounding static - although a very slight squeak at the beginning - maybe its playing very very fast?

Run the timer interrupt 1,000's of times slower than it should and have it print the OCR values to a 115200 baud UART to the PC - does the data look sensible?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Or use the sin wave routine I posted to fill a ram buffer up with one cycle of a sin wave. Put a gator clip on the rc filter on the pwm output. Clip it onto the rca cable from the line in on your stereo. If it goes 'ooooooooooo', its working.

Imagecraft compiler user

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ok. That was a brain teaser!
I have it filling the buffer in the main, and successfully playing the buffer out of the OCR1A. I hear "Its Working" through the speaker. Bear in mind this is successfully reading from the progmem array, not the SD card. but it is using a double buffer.

My while(1) loop:

while(1){//loop to complete playing file

if(read==1)
	{
		if(buf_1)								//load to first buffor
		{	
		for (i=0;i<512; i++){
		bufor0[i]= pgm_read_byte(&pcm_samples[(j*512) + i]);
		}
		j = j++;
				
		}else									//load to second buffor
		{	
		for (i=0;i<512; i++){
		bufor1[i]= pgm_read_byte(&pcm_samples[(j*512) + i]);
		}
		j = j++;
		
		}	
		read=0;

	}
	
	
}

And my ISR(timer0):

ISR(TIMER0_OVF_vect) 
{ 
 
		 sample_count--;
         if (sample_count == 0) 
            {
             sample_count = 8;           
			if(buf_1)								//play from first buffor
		{	
			OCR1A=bufor1[sample++];		//set PWM VAlue
			if(sample>511)
			{
				buf_1=0;
				sample=0;
				read=1;
			}
		}else//buf_1 = 0									//play from second buffor
		{	
			OCR1A=bufor0[sample++];		//set PWM VAlue
			if(sample>511)
			{
				buf_1=1;
				sample=0;
				read=1;

			}
		}

            }

}

And that plays the progmem array, and then load of static (prob just need to disable the timer once the array length has been reached).
So now, I should fill the buffer's like this:

//res =	f_read(&fsrc, bufor0, 512, &bw);

to get the file from the sd card, where fsrc points to a .raw file?
because for whatever reason, reading into the buffer like this:

res =	f_read(&fsrc, bufor0, 512, &bw);
		for (i=0;i<512; i++){
		printf("buf0 = [percent]d\n", bufor0[i]);
		}

does not do the trick. When i printed to the terminal like that when I read from the progmem array, i got the correct values, now I get just zeros.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

now I get just zeros.

So all you have to do now is determine why that is and you are done.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

So in an earlier post you mentioned sizeof(bufor0) would be 2. So is me printing to the terminal the way I am in vain? i.e should I be performing a serial dump - and maybe Im not getting '0's?

Is

res =	f_read(&fsrc, bufor1, 512, &bw);

the best way to do it? I mean is that doing the equivelent of

for (i=0;i<512; i++){
		bufor1[i]= pgm_read_byte(&pcm_samples[(j*512) + i]);
		}

?
and lastly, the .raw format will be holding the correct 'type' of data?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

So its an odd one. It works fine when reading from the header file. When reading from the sd card and printing the values of the buffer to the terminal at 115200 this is a summary:
To start with buf0 is all zeros:

Quote:

buf0[0] = 0
buf0[1] ‚Rbuf0[2] ‚Rbuf0[3] = 0
buf0[4] = 0
buf0[5] ‚Rbuf0[6] ‚Rbuf0[7] = 0
buf0[8] = 0
buf0[9] = 0
...
...


at 512 it changes over to buf1, which all the values are 128
Quote:

buf0[507] = 0
buf0[508] ‚Rbuf0[509] = 0
buf0[510] ‚Rbuf0[511] = 0
buf1[0] = 128
buf1[1] Š’ÂRbuf1[2] = 128
buf1[3] = 128
buf1[4] Š’ÂRbuf1[5] = 128
buf1[6] Š’ÂRbuf1[7] = 128
buf1[8] Š’ÊRbuf1[9] Š’ÊRbuf1[10] = 128
buf1[11] = 128
...
...


however it then doesnt change back to buf0:
Quote:

buf1[508] = 128
buf1[509] = 128
buf1[510] Š’ÂRbuf1[511] Š’ÂRbuf1[0] Š’ÂRbuf1[1] = 128
buf1[2] = 128
buf1[3] Š’ÂRbuf1[4] = 128
buf1[5] Š’ÂRbuf1[6] = 128
buf1[7] = 128
...
...

and from then on it never flips back, and buf1 is constantly full of 128's.

The only code that changes is the code within the while(1) loop, and I know the pwm is correct because it plays from the header file fine. So it must be here that the problem is it is not changing buffer and reloading the other one:

while(1){//loop to complete playing file
//res = f_read(&fsrc, buffer, sizeof(buffer), &br);    /* Read a chunk of src file */ 
if(read==1)
	{
		if(buf_1)								//load to first buffor
		{	
		res =	f_read(&fsrc, bufor0, 512, &bw);
		for (i=0;i<512; i++){
		printf("buf0[{perc}d] = {perc}d\n",i, bufor0[i]);
		}
		}else									//load to second buffor
		{	
		res =	f_read(&fsrc, bufor1, 512, &bw);
		for (i=0;i<512; i++){
		printf("buf1[{perc}d] = {perc}d\n",i, bufor1[i]);
		}
		}	
		read=0;
	}
}

Spot the deliberate mistake?
The only thing I can think is that each of my fread's has a warning about how I am reading into the buffer:

Quote:

../main.c:272: warning: passing argument 2 of 'f_read' discards qualifiers from pointer target type

in

res =	f_read(&fsrc, bufor1, 512, &bw);

i.e there is something not so good about the type buforx is, but Im not sure what, and whether that would be the issue
p.s {perc} is percent

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

OK and if you open that binary file using WinHex with the card plugged into the PC what does that show it contains? Is it really all 0x00 and 0x80 as you appear to be seeing?

Note that in WinHex you can highlight some bytes in the hex editor display then Edit-Copy Block-Editor Display and get pasteable text such as:

Offset      0  1  2  3  4  5  6  7   8  9  A  B  C  D  E  F

000002A0   9A 00 9B 9B 9B 00 9C 9C  9C 00 9D 9D 9D 00 9E 9E   š ››› œœœ  žž
000002B0   9E 00 9F 9F 9F 00 A0 A0  A0 00 A1 A1 A1 00 A2 A2   ž ŸŸŸ     ¡¡¡ ¢¢
000002C0   A2 00 A3 A3 A3 00 A4 A4  A4 00 A5 A5 A5 00 A6 A6   ¢ £££ ¤¤¤ ¥¥¥ ¦¦
000002D0   A6 00 A7 A7 A7 00 A8 A8  A8 00 A9 A9 A9 00 AA AA   ¦ §§§ ¨¨¨ ©©© ªª
000002E0   AA 00 AB AB AB 00 AC AC  AC 00 AD AD AD 00 AE AE   ª ««« ¬¬¬ ­­­ ®®
000002F0   AE 00 AF AF AF 00 B0 B0  B0 00 B1 B1 B1 00 B2 B2   ® ¯¯¯ °°° ±±± ²²
00000300   B2 00 B3 B3 B3 00 B4 B4  B4 00 B5 B5 B5 00 B6 B6   ² ³³³ ´´´ µµµ ¶¶
===================================================================================

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

in hex editor, code starts as:

8080808080808080818180808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080

and ends the same way, in the middle is

7C7D7D7D7C7D7F7F7F7F808182848385858687868A8887878786848381807C7A79777472706F6D6D6E707275797E82868C909396999A9999

sorry hex workshop doesnt seem to have such a nice way of pasting the code.
P.S I changed

res =   f_read(&fsrc, bufor1, 512, &bw); 

to

res =   f_read(&fsrc, &bufor1, 512, &bw); 

because the website on f_read said buforx should be a pointer, adn putting the '&' got rid of the warnings, didnt fix the issue tho

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

sorry hex workshop doesnt seem to have such a nice way of pasting the code.

Then why on earth aren't you using WinHex ?!? If you only use it for read-only it is free (but I registered as it's totally invaluable when working with FAT).

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

Then why on earth aren't you using WinHex ?!?

ok ok, i'll switch over, doing that now:
OK:

Offset      0  1  2  3  4  5  6  7   8  9 10 11 12 13 14 15

00000000   80 80 80 80 80 80 80 80  80 80 80 80 80 80 80 80   €€€€€€€€€€€€€€€€
00000016   80 80 80 80 80 80 80 80  80 80 80 80 80 80 80 80   €€€€€€€€€€€€€€€€
00000032   80 80 80 80 80 80 80 80  80 80 80 80 80 80 80 80   €€€€€€€€€€€€€€€€
00000048   80 80 80 80 80 80 80 80  80 80 80 80 80 80 80 80   €€€€€€€€€€€€€€€€
00000064   80 80 80 80 80 80 80 80  80 80 80 80 80 80 80 80   €€€€€€€€€€€€€€€€
00000080   80 80 80 80 80 80 80 80  80 80 80 80 80 80 80 80   €€€€€€€€€€€€€€€€
00000096   80 80 80 80 80 80 80 80  80 80 80 80 80 80 80 80   €€€€€€€€€€€€€€€€

starts like this, then
later like this

Offset      0  1  2  3  4  5  6  7   8  9 10 11 12 13 14 15

00001008   80 80 80 80 80 80 7F 7F  7F 7F 7F 7F 7F 7E 7E 7E   €€€€€€       ~~~
00001024   7E 7E 7F 7F 7F 7F 80 80  81 81 82 82 83 83 83 84   ~~    €€  ‚‚ƒƒƒ„
00001040   84 84 84 84 84 84 83 83  83 82 81 81 80 7F 7F 7E   „„„„„„ƒƒƒ‚  €  ~
00001056   7D 7C 7B 7A 79 79 78 78  78 78 78 78 79 7A 7B 7C   }|{zyyxxxxxxyz{|
00001072   7E 80 82 84 85 87 89 8A  8B 8B 8B 8B 8B 8A 8A 89   ~€‚„…‡‰Š‹‹‹‹‹ŠŠ‰

then finally like the first block again. As far as I understand, buf1 is full of the beginning, and because it never gets flipped over to use the other buffer, it just continually plays 128's and never refreshes, but I cant see why it wouldnt change buffer?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

OK so lots of 128's (that is 0x80) looks like it probably was reading the write thing. To skip that silence at the start if you want it to start reading at 0x1000 then use:

f_open() // as before
f_lseek(&fsrc, 0x1000UL);
...

That will immediately start within the active data in the file.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Cool, I'll try that, but does that explain why the buffer is not changing over now to the other one? Worked fine when reading from the header file

EDIT:
Just tried that and that is working fine. I can hear it "trying" to say something - but as the buffer isnt flipping over it just repeats the same thing

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

So all you have to do now is work out why that is and you are done.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

True. True.
So my question is really:
Using this technique to read from the header file

/*for (i=0;i<512; i++){
		bufor0[i]= pgm_read_byte(&pcm_samples[(j*512) + i]);
		}
		j = j++;*/
				

Worked perfectly. Replacing all of that with

res =	f_read(&fsrc, &bufor0, 512, &bw);

means that it no longer flips buffer. What is the difference a), and b) can the second way be imitated using a for loop? That way I would know whether it is the technique?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Don't forget the element of time. If it takes longer to read the block from the sdcard than it takes to empty the buffer, then your method falls over. How long does it take to read the sector from the sdcard? Only you can answer that. I'd suggest you keep a count of timer ticks incremented by your timer isr and get a copy of that before the read operation and subtract that from the value after the read operation. That will give you the time it takes to read a sector.

Your reference to "reading from the header file" is not true. You're reading from the flash memory the values that were defined at compile time in your header file.

j=j++; is legal C, but the assignment is unecessary. j++ is the same as writing j=j+1

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Both snippets of code only write to bufor0. You need to write to the buffer that is free.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Kartman, I was only showing the difference in reading it from the flash memory (the header reference) and reading from the sd card. The full code to use the buffers is:

while(1){//loop to complete playing file
//res = f_read(&fsrc, buffer, sizeof(buffer), &br);    /* Read a chunk of src file */ 
if(read==1)
	{
		if(buf_1)								//load to first buffor
		{	
		/*for (i=0;i<512; i++){
		bufor0[i]= pgm_read_byte(&pcm_samples[(j*512) + i]);
		}
		j = j++;*/
		res =	f_read(&fsrc, &bufor0, 512, &bw);
		
		/*for (i=0;i<512; i++){
		printf("buf0[{perc}d] = {perc}d\n",i, bufor0[i]);
		}*/
		}
		else									//load to second buffor
		{	
		/*for (i=0;i<512; i++){
		bufor1[i]= pgm_read_byte(&pcm_samples[(j*512) + i]);
		}
		j = j++;*/
		res =	f_read(&fsrc, &bufor1, 512, &bw);
		
		/*for (i=0;i<512; i++){
		printf("buf1[{perc}d] = {perc}d\n",i, bufor1[i]);
		}*/
		}	
		read=0;

	}
	
	
}

clawson, I still dont get why that would mean the buffers dont swap when they have been sent to OCR1A, surely what you are reffering to would just make the sound, sound broken up?

P.S what does it mean if the result of res
where res = f_read...
is 2?
Got it to print which buffer its using:

res0 = 2read = 1
buf_1 = 0
res1 = 2read = 1
buf_1 = 1
res0 = 2read = 1
buf_1 = 0
res1 = 2read = 1
buf_1 = 1
res0 = 2read = 1
buf_1 = 0
res1 = 2read = 1
buf_1 = 1
res0 = 2

res0 and res1 are coming out as 2

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I'm not sure what your point is? I think some time with the simulator might give you a better idea of what is really happening with your code. You're leaning on the forum way too much and not doing enough learning yourself. Basically we're writing your code by proxy.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I only mentioned it because you mentioned that both bits of code were using bufor0. That was the point though, they were both supposed to be using bufor0, just reading from different places.
Anyway Ive narrowed the problem down quite severely. The initial filling of the buffer (outside of the while loop) is filling successfully - res = 0.
Also, as can be seen in the above code, the buffer is switching as it is supposed to, but the result of reading from the file is an error - not sure which one but res = 2 in all cases. When I find out what error 2 means I can maybe work out what the problem is. But what I therefore think is happening is it is not overwriting the buffer from before - because of the error, so the same thing is continually sent to the OCR1A. When res = 0, it will work.

So I suppose then the only question I have is whether I should open and close the file with each f_read? I know that leaving files open after reading can cause issues - I found this out when trying to write to a file and not closing it. Is it the same when reading?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Lets say the file is 32K... 2 seconds at 16K sample rate... 64 sectors. The pwm is 8 bit fast, CTC, no compare interrupt. The timer interrupt is 16Khz... 62.5usec. Every buffer takes 32ms to play, but only a few ms to read and fill, and the timer interrupt ticks right through the fread. In the timer tick handler, pick up a byte from the buffer and store it in OCR1A.

Imagecraft compiler user

Last Edited: Mon. Nov 14, 2011 - 03:26 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
typedef enum {
	FR_OK = 0,				/* (0) Succeeded */
	FR_DISK_ERR,			/* (1) A hard error occured in the low level disk I/O layer */
	FR_INT_ERR,				/* (2) Assertion failed */
	FR_NOT_READY,			/* (3) The physical drive cannot work */
	FR_NO_FILE,				/* (4) Could not find the file */
	FR_NO_PATH,				/* (5) Could not find the path */
	FR_INVALID_NAME,		/* (6) The path name format is invalid */
	FR_DENIED,				/* (7) Acces denied due to prohibited access or directory full */
	FR_EXIST,				/* (8) Acces denied due to prohibited access */
	FR_INVALID_OBJECT,		/* (9) The file/directory object is invalid */
	FR_WRITE_PROTECTED,		/* (10) The physical drive is write protected */
	FR_INVALID_DRIVE,		/* (11) The logical drive number is invalid */
	FR_NOT_ENABLED,			/* (12) The volume has no work area */
	FR_NO_FILESYSTEM,		/* (13) There is no valid FAT volume */
	FR_MKFS_ABORTED,		/* (14) The f_mkfs() aborted due to any parameter error */
	FR_TIMEOUT,				/* (15) Could not get a grant to access the volume within defined period */
	FR_LOCKED,				/* (16) The operation is rejected according to the file shareing policy */
	FR_NOT_ENOUGH_CORE,		/* (17) LFN working buffer could not be allocated */
	FR_TOO_MANY_OPEN_FILES,	/* (18) Number of open files > _FS_SHARE */
	FR_INVALID_PARAMETER	/* (19) Given parameter is invalid */
} FRESULT;

That suggests "2 = internal assertion failure".

(the lesson here is ALWAYS check return values and ring the alarm bells if you ever get !=0 )

Now if you look at the source of f_read() in ff.c it would appear that the only reasons it would ever return FR_INT_ERR are:

1)

	if (fp->flag & FA__ERROR)					/* Aborted file? */
		LEAVE_FF(fp->fs, FR_INT_ERR);

You can check this one before the call to f_read - does the "flag" element of the file control structure have FA_ERROR already set?

2)

				if (clst < 2) ABORT(fp->fs, FR_INT_ERR);

It's *unlikely* to be this as the code is only executed when it traverses a cluster boundary and that probably won't happen until you read 32K from the file. But again put a printf() in there and see is this is the reason.

3)

			sect = clust2sect(fp->fs, fp->clust);	/* Get current sector */
			if (!sect) ABORT(fp->fs, FR_INT_ERR);

Again this only happens at a cluster boundary so it's unlikely to be this.

It therefore looks like it's probably (1). If you confirm this you need to determine why the previous call to f_read() set FA_ERROR in the first place.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Why would you open and close the file for each read? You would then have to f_seek to the point in the file you want. With a write, closing the file ensures all the housekeeping is done so the filesystem is intact when you remove the card. This is not required when reading.

According to Bobs figures, you should have enough time to read the card. A measurement will confirm.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Yeah I spotted in the first one in the code, I noticed that the error was changing to start with:

1st res = 0 //res from f_read outside of while(1)
read = 1
buf_1 = 1
res0 = 1 //first f_read filling bufor0
buf_1 = 0
res1 = 2 //f_read filling bufor1
buf_1 = 1
res0 = 2

So the res goes from success to hard error to internal error and then stays on that. Im now worrying about my sd card - although it was alright to start.
This line though:

if (fp->flag & FA__ERROR)

is that saying if fp goes to the value of flag AND FA__ERROR is equal to 1 then do

LEAVE_FF(fp->fs, FR_INT_ERR);

I.e if an error occured on the time before that f_read was called, then LEAVE_FF this time whatever?
That would explain res = 2 if thats what it is saying, because I got a res = 1 = which would set FA__ERROR to 1 would it not.
On that basis the question would become why did res = 1.

From your legend above:

Quote:

A hard error occured in the low level disk I/O layer */

That doesnt sound so good then...

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

That doesnt sound so good then...

Indeed it does not.

Put the card in a PC and run chkdsk/scandisk on it and see if that reports/repairs errors.

In all your use of FatFs have you ever used any write functions to the card? If you got it wrong you may have buggered up (technical term!) the filesystem.

For development it may be safest to set ffconf.h to:

#define _FS_READONLY	1

which would prevent you doing anything to the card that might damage the file structure.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I have written to the card, but a long time ago (relative) - one of Chan's examples he copies a file on the on the card - I tried it. I'll check it on the computer - being able to open it on a computer doesnt necessarily prove the card is ok does it?
Does it surprise you that the INITIAL f_read - before the while loop works, but the next one produces res = 1? I would have thought if its broke its broke.

Could a nice little format help do you think? Or if Ive 'buggered' the file system is that game over for that card?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

Does it surprise you that the INITIAL f_read - before the while loop works, but the next one produces res = 1? I would have thought if its broke its broke.


It doesn't surprise me - clearly it has to try reading something to discover damage (if that's what it is). Do you have other SD/MMC cards you could try. I'd just switch to another one and see how that pans out - maybe one you don't mind reformatting. If you don't have cards I think you'll find your local Tesco has them for about £2-£3 upwards. Probably worth getting a few.

If you have damaged the filesystem then yes a format will always cure it as I doubt it is a physical error - only a logical one.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

OK, will go through those steps and see what happens. I'll also get a couple more cards.
A physical error would occur if say I put a too higher voltage or something right? Logical errors occur when you make a coding error?
I have been popping it in and out of my computer, adding and deleting files so perhaps Ive just been a bit scrappy somewhere.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

A physical error would occur if say I put a too higher voltage or something right? Logical errors occur when you make a coding error?

A physical error is when something is physically wrong - like a contact's become detached or one or more of the sectors has become unreadable (though this only happens on hard drives, not memory cards as they have the brains to remap bad sectors)

A logical error is something like a field in the BPB holds an invalid number, or one of the entries in the FATs has a meaningless number in it or a directory entry has an invalid first cluster number or something along these lines. As it's just 0's and 1's at the end of the day returning all the 0's and 1's to sensible values (which is what formatting does) clears any such error.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Well thats a shame. I ran checks, it said they were fixed, i tried again - nothing, i formatted, tried again and nothing better. Still get res = 0, then 1, then 2
yeah initializing the disk was a success

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Time to add those printf()s I suggested then

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

What is the size of your test file again? You could make a file with 512 0x30s then 512 0x31s then 512 0x32s and write a program to read a sector, print the first byte, read the next sector, print the first byte. Should print out 0123456789 etc

Imagecraft compiler user

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Has anyone made a command line interpreter for the file system? dir, copy, type, del, etc?

Imagecraft compiler user

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

Has anyone made a command line interpreter for the file system? dir, copy, type, del, etc?

That comes with FatFs when you use ffsample.zip - it's main() is just a serial monitor with commands allowing you to try "one of everything" in the library:

root@ws-czc1138h0b:/windows/ffsample/avr# grep case main.c
case 'd' :
		case 'd' :      /* dd  [] - Dump secrtor */
		case 'i' :      /* di  - Initialize disk */
		case 's' :      /* ds  - Show disk status */
case 'b' :
		case 'd' :      /* bd  - Dump R/W buffer */
		case 'e' :      /* be  [] ... - Edit R/W buffer */
		case 'r' :      /* br   [] - Read disk into R/W buffer */
		case 'w' :      /* bw   [] - Write R/W buffer into disk */
		case 'f' :      /* bf  - Fill working buffer */
case 'f' :
		case 'i' :      /* fi  - Initialize logical drive */
		case 's' :      /* fs [] - Show logical drive status */
		case 'l' :      /* fl [] - Directory listing */
		case 'o' :      /* fo   - Open a file */
		case 'c' :      /* fc - Close a file */
		case 'e' :      /* fe - Seek file pointer */
		case 'r' :      /* fr  - read file */
		case 'd' :      /* fd  - read and dump file from current fp */
		case 'w' :      /* fw   - write file */
		case 'v' :      /* fv - Truncate file */
		case 'n' :      /* fn   - Change file/dir name */
		case 'u' :      /* fu  - Unlink a file or dir */
		case 'k' :      /* fk  - Create a directory */
		case 'a' :      /* fa    - Change file/dir attribute */
		case 't' :      /* ft        */
		case 'x' : /* fx   - Copy file */
		case 'g' :      /* fg  - Change current directory */
		case 'j' :      /* fj  - Change current drive */
		case 'q' :      /* fq - Show current dir path */
		case 'm' :      /* fm    - Create file system */
case 't' :      /* t [     ] */
=================================================================================

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ok. an early(er) rise this morning to try and pin point the issue. What I have come to realise is that it is not so much the sd card that is the problem. If I donet enable pwm_init - i.e OCR1A and the timer, and then run it - i.e just filling the buffers and flipping between them each run through of while(1), then they fill up perfectly doing exactly what is expected - no res ~= 0 anywhere.
So my inexperience tells me that maybe it is not 'synced' as to when it is filling a buffer and when it is calling the timer interupt, even though there maybe enough time between timer calls for it to fill a buffer, maybe it is not doing it in the gap but across the switch over - that might mess up a buffer?

However I have just had another thought. The way it was written was to initiate everything to do with the filing system, then disable interupts, initiate the pwm and timer0, then sei, look:


IoInit();
sei();
disk_initialize(0);//initialize disk
_delay_ms(500);
f_err_code = f_mount(0, &FATFS_Obj);//error code when trying to mount
printf("f_err = {perc}d\n", f_err_code);//print error code
_delay_ms(800);

res = f_open(&fsrc, "0:testing.raw", FA_OPEN_EXISTING | FA_READ);
f_lseek(&fsrc, 0x1000UL);  
buf_1=1;
read=1;
res =	f_read(&fsrc, &bufor1, 512, &bw1);//load bufor1 for the first round
printf("1st res = {perc}d", res);

cli();
pwm_init();
sei();
while(1){
...
..

By removing the initiation of pwm (and cli, sei surrounding it) the buffer reads perfectly. So perhaps although it has all been initiated, I need to disable the timer from running until the buffer is full each time. I dont know if that makes sense, but the first error occurs after filling the second buffer load (the first time in while(1)) where res == 1.
Maybe interupts are firing at the same time.
I am writing this because I have never dealt with two seperate interupts in the same program before, and I dont know whether certain issues can occur - I can imagine they can though!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

What does pwm_init() do? It couldn't be that you are trying to reuse the same timer that FatFs already uses could it?

A quick check:

E:\ffsample\avr>grep TCC *
main.c: TCCR1A = 0b00010000;
main.c: TCCR1B = 0b00001010;
main.c: TCCR2 = 0b00001101;

seems to suggest FatFs may be using timer 1 and timer 2 already.

EDIT: perhaps not - the downside of grep is that it doesn't tell you which lines are active. Looking at the code:

/*
	OCR1A = 51;			// Timer1: LCD bias generator (OC1B)
	OCR1B = 51;
	TCCR1A = 0b00010000;
	TCCR1B = 0b00001010;
*/
	OCR2 = 90-1;		// Timer2: 100Hz interval (OC2)
	TCCR2 = 0b00001101;

	TIMSK = 0b10000000;	// Enable TC2.oc, interrupt

So the timer 1 stuff is commented and only timer 2 is used in the main.c of FatFs.

One thing that *might* be happening is that if you have long ISR code it could be delaying/impinging on FatFs's own timer code.

Last Edited: Tue. Nov 15, 2011 - 12:27 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Try timing 10000 reads of sector 1 to see how long one of them takes?

Imagecraft compiler user

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ahh, so it could be that the two interupts are interupting each other and messing up the filing?
Would it be "bad practice" or something to disable the other timer each time it goes into an ISR, i.e the pwm isnt required while fatfs ISR is running, and FATFS ISR isnt required when it goes into pwm, so disable them a tbe beginning and enable again at the end.
Bob, are you saying use another timer to time 1000 reads of the first sector?
This is pwm_init():

void pwm_init(void)
{
    /* use OC1A pin as output */
    DDRB = _BV(PB5);

    /* 
    * clear OC1A on compare match 
    * set OC1A at BOTTOM, non-inverting mode
    * Fast PWM, 8bit
    */
    TCCR1A = _BV(COM1A1) | _BV(WGM10);//COM1A1 = clear at TOP, WGM10 = fast pwm
    
     TCCR1B = _BV(WGM12) | _BV(CS10);//prescaling. WGM12 = fast pwm

    OCR1A = 0;
    
    /* Setup Timer0 */
  
    TCCR0|=(1<<CS00);
    TCNT0=0;
    TIMSK|=(1<<TOIE0);
	sample_count = 8;


}

I am using timer0 and timer1 for the pwm setup, timer2 for the fatfs, so I think Im out of timers?

EDIT: Oh no, I just remembered - interupts cant interupt each other can they so I dont need to disable and renable timers...

EDIT 2:

Quote:

There's another subtle bug in the above code sample; it's possible at the machine code level for an interrupt to occur half way between the fetching of the two bytes of the integer variable MyValue, causing corruption if the variable's value is altered inside the ISR. This can be avoided by making the fetching of the variable's value in the main program code "atomic", by disabling the global interrupt enable bit while the fetching is taking place.

http://tom-itx.dyndns.org:81/~we...

Could this be the problem? Do I need to set something to 'atomic'?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Remember that FatFs needs its timer running (and interrupting) when you use filing functions. So if you are calling them from another timer ISR then stop that timer and re-enable interrupts shile calling FatFs functions.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

So disable the pwm timer while reading from the sd card into the buffer? Then re-enable the pwm timer ONCE the buffer has been read. Therefore the f_read has no chance of being interupted.

Another thought.
I tried to disable the timer dealing with the pwm while reading from the sd card:

TCCR0|=(0<<CS00); //disable pwm timer
    	TIMSK|=(0<<TOIE0);
		res =	f_read(&fsrc, &bufor0, 512, &bw2);
		TCCR0|=(1<<CS00); //enable pwm timer
		TCNT0=0;
    	TIMSK|=(1<<TOIE0);

However res=2 still then.
But I noticed that both timer 2 (fatfs) and timer0 (pwm) are setting TIMSK seperately. One wouldnt be overwriting the other would it?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

No that wouldnt work would it. Do I need to leave the timer on the last value it was on, and just re-enable it, rather than TCNT0=0 again?

What about uart! Surely thats using a timer, maybe thats bumping into pwm or fatfs timer?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

What about uart! Surely thats using a timer,

Why would UART be using a timer?

I know it's an old fashioned concept and all that but how about sketching out a flowchart of what you are trying to do here (a separate part for the interrupts that run in parallel) and see if you cannot spot where there may be interactions that cause problems.

It does seem to me that you are making this whole thing far more complex than it really needs to be. I'm half tempted to dig out an STK500, an SD card holder and a speaker and try this myself.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Cool. Maybe I am over complicating it, I just read through the comments on the sample code on the AVR335 datasheet and I believe mine is doing the same thing.
I have here in front of me a flow chart (albeit on paper, with a biro) of how I think the program is moving.
It lies in the fact that (I believe) fatfs is trying to read from the sd card, the pwm interupts it, the buffer isnt full and from then on res ~= 0.
I have initially filled both buffers before the while loop and before pwm is initialised, so now I get two sounds alternating coming out of my speakers. I get that. The moment however pwm is also initialised res goes to 1, and then 2 and then never fixes itself.

Im imagining the two interupts as two square waves that are 90 deg out of sync, so one hasnt finished when the other starts - leading to confusion. I tried disabling pwm timer while f_reads are happening, but that didnt do anything.

Elm Chan wrote some code to do this on an ATTINY and I also followed his thorugh - more complex but you can see where the buffers are being filled, and info being passed to the pwm - I cant see what his does that mine doesnt though.
I am noticing more and more though examples using WDT_reset. I dont know if this is the crucial move, however I dont believe a restart is required.
BTW, one last thing:
res = f_read(&fsrc, &bufor0, 256, &bw);
I should use the same bw for reading to both buffers so that it knows where it is on the next buffer read right? [EDIT: No that wouldnt be the prob any how].

So you got a 128 and an sd card? You're being very helpful btw, thanks. Hopefully once Im done here, someone will come and read this thread and all their questions will be answered!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

OK I rounded up the bits I think I may need...

Oh and a gratuitous picture of Amelia who likes to get in the way of the camera:

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Some serious gear. That makes my little setup look pathetic!
I can see the Wiz on the left there, nice. Do you want the code? I can comment out the initiation of the pwm, and you can see the buffers are filling up correctly?

Hello Amelia, suppose your wondering why your not getting the attention?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

a.mlw.walker wrote:
Elm Chan wrote some code to do this on an ATTINY and I also followed his thorugh - more complex but you can see where the buffers are being filled, and info being passed to the pwm - I cant see what his does that mine doesnt though.
Looking closer at chan's code I'd reckon it should be quite easy to adapt for mega avr.
The only real issue is with the timers since the mega128 has no internal PLL so you may restricted to lower sample rates, which of course you are anyway.

Edit: Actually, I see there are some asm functions required so maybe not so easy after all.

Last Edited: Tue. Nov 15, 2011 - 07:34 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
FatFs module test monitor for AVR
LFN Disabled, Code page: 1
>di 0
     rc=1
>

Now why am I getting RC=1 which is

RES_ERROR,		/* 1: R/W Error */

And I thought this was going to be easy? Perhaps it's my card or a wiring fault? Looks like a job for tomorrow.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I got that clawson when I had forgotten to detach my AVR ISPMK II. Took me hours to realise that it was still connected.
Yeah its the Assembly that throws me in Chans. Plus I havent spent much time looking at the watch dog timer, and he's manipulating it left right and centre. I dont know whether that's the key to my problems. However I think I may have made a mistake disabling my timer earlier:
I think to disable the timer I should have done:

TCCR0 &= ~(1<<CS00); //trying to disable pwm timer

???
I'll upload the code anyway. Later version on here shouldnt harm anyone?

Attachment(s): 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

I got that clawson when I had forgotten to detach my AVR ISPMK II. Took me hours to realise that it was still connected.

Not in my case - I'm programming/debugging with JTAGICEmkII

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

I think to disable the timer I should have done:
Code:

TCCR0 &= ~(1<<CS00); //trying to disable pwm timer

???


in vain...

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

a.mlw.walker wrote:

TCCR0 &= ~(1<<CS00); //trying to disable pwm timer


That should stop Timer0 provided the other CS bits are 0. But this makes me question how fast your are running Timer0. It looks to me like you use no pre-scaling and depending on system clock frequency it could be running way too fast. What sample rate are you using here?

Hint: Timers are started & stopped, interrupts are enabled & disabled.

Edit: Thinking about it, with no pre-scaling you would get about 256 cycles between each overflow irrespective of the system clock speed, right?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

yeah i think thats right. sorry if lingo not quite right. Yeah Im not using a prescaler, im not sure of IRRESPECTIVE of clock speed, but my clock speed is 16MHz. Did you manage to have a look at my code?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

a.mlw.walker wrote:
not sure of IRRESPECTIVE of clock speed, but my clock speed is 16MHz. Did you manage to have a look at my code?
I mean it wouldn't make a difference whatever clock speed you are running. Try slowing it down with a div/8 pre-scaler and see what happens.

I did download your code but unfortunately I am having trouble atm with rar files on Ubuntu.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Oh right, zip?

Quote:

I mean it wouldn't make a difference whatever clock speed you are running. Try slowing it down with a div/8 pre-scaler and see what happens.

Yeah it does change it - gives high pitched squeak over the top, but doesnt fix it - without it, it sounds like its 'closer' to the real thing so Im gonna keep it without a prescaler

Attachment(s): 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson did you solve your res = 1 issue?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Nope, I've been involved in conf. calls all day so far :-( (and don't ask me about cars with flat batteries - I discovered a design classic by Porsche this morning!)

I think I need to dig out a 'scope to check the electronics.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ok, I'll take the bait. What is the classic bit of Porsche design, and what do you know about cars with flat batteries?
I will be very interested to see what I have clearly completely missed with these double buffers...
(I wish I had a scope. I think literally days of my life would have been saved - instead I have to rewrite the code to generate a signal for longer, and then get the ol' voltmeter out.)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

hat is the classic bit of Porsche design, and what do you know about cars with flat batteries?

Right well if something (and I don't yet know what) happens to drain the battery in your Porsche then the remote locking does not work. The car has two boots, one at the front under the bonnet, one at the rear as a more "normal" (but smaller) boot. It's in the front boot that a set of terminals that connect to the battery are located. You need access to these to attach charging or jump leads. But the bonnet/boot remain firmly locked without battery power. So the user manual for the car tells you that in the drivers footwell there is a covered panel that can be removed to access the cars fuses. It tells you that there is a pull out terminal there to which the positive lead of jump leads can be connected to inject enough juice into the car to operate the bonnet lock to get in so you can access the real battery terminals. However that terminal does NOT exist in my model of the car!

So I just went to Halfords to buy a cigarette lighter to cigarette lighter jumper cable in the HOPE that I can inject sufficient power to operate the bonnet lock. If this doesn't work my next hope is that there is apparently an emergency release cable hidden up in one of the front wheel arches but to reach it may, apparently involve removing a wheel (and I just know that the special unlocking nut I need to remove a wheel is bound to be in the tool pack that is also in the front boot!)

Not amused by Porsche right now!

(but off outside to see if the cable will do the trick - fingers crossed).

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Well if you can find a way to connect to a positive wire somewhere, you can just connect the negative to the chassis wherever's easiest...

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Well the cigarette connection didn't work (I assume the Porsche hides this behind a diode or something?) so I had a look at replacing a fuse with a hoop of heavy gauge copper wire to which I could attach a +ve jump lead but that is proving tricky. I think I'll give in and call out the AA.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Did the AA Team come to the rescue? Porsche up and running?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Guys, looking at another example of this
http://www.pro-qwerty.com/files/...
If you do a search on that page for

Quote:

__ramfunc void przerwanie()

You can see that inside that is the line

ii++

ii is next used by the function time():

void time(int t)
{
	char buf[10];
	if(time1!=(ii/22500))
	{
		LCD_XY(1, 8);  LCD("    ");
		LCD_XY(1,8);
		intToStr(buf, ii/22500);
		LCD(buf);
		time1=ii/22500;
	}
}

Now I think this function is an equivelent of interupts, somehow controlling the frequency of interupting and playing to the pwm pin. Im not sure, however I am wondering whether ii is the key between the way I have explained I am doing it and success?

And this example is in Bascom
http://bahbots.com/?page_id=174
however its readable, and the only thing this author seems to do i dont is say when the buffer is full, however Im pretty sure f_read deals with that.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

a.mlw.walker wrote:
Yeah it does change it - gives high pitched squeak over the top, but doesnt fix it - without it, it sounds like its 'closer' to the real thing so Im gonna keep it without a prescaler
But, unless my calculations are way off, this means you are trying to play back 62500 samples per second. That is never going to work.

So, I ask again, what is the sample rate of the audio file? Please bear in mind that this should be a maximum of 8KHz for any real chance of success here.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Are you saying that it would NOT work if it was higher than 8KHz. I.e that fatfs would fail to read from the sd card if the sample rate of the info on the sd card is higher than 16KHz. How would fatfs even know?
In answer to your question, the file began as a 16KHz wav file. using Switch software I converted it to 8KHz mono pcm, 8 bit unsigned.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

a.mlw.walker wrote:
Are you saying that it would NOT work if it was higher than 8KHz. I.e that fatfs would fail to read from the sd card if the sample rate of the info on the sd card is higher than 16KHz.
No, this is nothing to do with fatfs. The main issue is with pwm speed (Timer1), this must be at least twice sample rate though 4 times faster is recommended. The other thing is making sure there are enough free cpu cycles between samples to do all the buffering and file system work.
a.mlw.walker wrote:
In answer to your question, the file began as a 16KHz wav file. using Switch software I converted it to 8KHz mono pcm, 8 bit unsigned.
So why are you running Timer0 so fast?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

you mean with no prescaler? because as I said before it sounds awful, and doesnt resolve my problem.
In the code if you see the variable sample_count - by changing that it changes the speed at which info is sent to the pwm, I have it set at 8, setting it at 4 means the sound comes out twice as fast.

I think what you are saying is the actual 'clock ticks' are too fast and thereofore the buffers dont get filled in the gaps.
how fast would you say to run it?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

a.mlw.walker wrote:
In the code if you see the variable sample_count - by changing that it changes the speed at which info is sent to the pwm, I have it set at 8, setting it at 4 means the sound comes out twice as fast.
Your code is a little dis-jointed, I had actually missed that. Why did you not just use the pre-scaler to divide by 8 then you would not need to use sample_count.
Note that for sample_count to work as you expect it should have been declared statically inside the ISR or at least as a volatile global.
a.mlw.walker wrote:
I think what you are saying is the actual 'clock ticks' are too fast and thereofore the buffers dont get filled in the gaps.
how fast would you say to run it?
Well I am not sure if that is the whole problem but it certainly wont help. To get the exact frequency at 16MHz you would typically pre-scale by 8 and use output compare with OCR0 = 250.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
To get the exact frequency at 16MHz you would typically pre-scale by 8 and use output compare with OCR0 = 250.

Sorry Im not sure I understand the v. last bit about OCR0. Did you download my code then?

So are you saying I have two options:
1. Make sample_count static within ISR(timer0)
2. Remove samople_count completely from the code, and prescale by 8.

What does OCR0 have to do with it?
yeah its distointed. Infact there's a broken joint somewhere...!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

a.mlw.walker wrote:
Sorry Im not sure I understand the v. last bit about OCR0. Did you download my code then?
Please refer to the section on Output Compare in the ATmega128 datasheet. Yes, I did download your code.
a.mlw.walker wrote:

So are you saying I have two options:
1. Make sample_count static within ISR(timer0)
2. Remove sample_count completely from the code, and prescale by 8.
I would say that the second option is a much better one.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Did you have a chance to run it or are you just reading it (my code I mean)?
Ok, i'll adjust the code so that sample_count is non existent, and prescale by 8, but if thats doing the same thing in a neater way - that doesnt sound like a solution, more a 'better programming' exercise?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

To summarize, you recorded a wav file at ?? bits an 16khz sample rate. Then you converted it to 8 bit unsigned, 8khz sample rate. I think there are 2 ways to play this.... get the pwm initialized for 8khz ctc mode, no interrupt, and use am 8khz timer interrupt to grab a sample and store it into OCRx. Other way is init the output compare interrupt and store a sample in the OCR every 125usec. Note that this is just for pcm playing, not reading the wav file from the card. This is done in the main loop. You initially fill buffer0, start it playing, then fill buffer1. Obviously, when the last sample is played from buffer0, the next sample is fetched from buffer1, and you can see this in the main loop and fill buffer0 with the 3rd sector from the file, etc etc.

Imagecraft compiler user

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

So I think what you both (Bob and maximax) are saying is that my timer is too fast. Because as far as I can tell from your explanation there Bob is, that's what I'm doing (bar I am using sample_count as maximax pointed out earlier, but I'm going to change that). In terms of the buffer's, yeah I'm filling one, then the other and so on...
One thing I am wondering is whether I've set the value in timer0 to go back to 0 at the end of it's count? I'm not convinced that it is resetting itself.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You initialize the timer to interrupt every 125usec, and the handler is something like:

if(playing){
  if(curbufis0){
    OCR1A=buffer0[ndx++]
  }else{
    OCR1A=buffer1[ndx++]
  }
  if(ndx > 511){
    ndx=0;
    curbufis0 = !curbufis0;
  }
}

or similar

Imagecraft compiler user

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Use the timer's CTC mode then.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

BTW clawson, rc = 1 - thats a R/W issue I think, could be the thing where you need to rewrite disk_timerproc();
because it is looking to see write protection but you dont have it connected???

Guys I think I might have made an error but Im not sure. Been reading the timer tuts again.
Let me just get this straight.

TCCR1A = _BV(COM1A1) | _BV(WGM10);//COM1A1 = clear at TOP, WGM10 = fast pwm

    TCCR1B = _BV(WGM12) | _BV(CS10) ;//| _BV(CS11);//prescaling. WGM12 = fast pwm
    //TCCR1B =(1<<WGM13)|(1<<WGM12); 
    /* set initial duty cycle to zero */
    OCR1A = 0;

This code is setting up the pwm right? the line OCR1A = 0 is just initiating the duty cycle to 0% I understand.
I understand from the datasheet, WGM12 sets the register to fast pwm, and CS10 is not prescaling, OK?
firstly, on the line TCCR1A COM1A1 is telling it to clear at top - which I haven't set, but i suppose it just fills up and when its full it goes back to 0.
The next bit of code is:

/* Setup Timer0 */
  
    TCCR0|=(1<<CS01);//prescaler of 8
    TCNT0=0;
    TIMSK|=(1<<TOIE0);

i have removed the sample_count variable, and prescaled Timer0 by 8 instead as maximax said.
My worry is coming after reading OCR1A is 16 bit and therefore has two channels each of 8 bits. Nowhere have I set the other channel to anything - I just use OCR1A
Is it not OCR1AH and OCR1AL. If so what am I doing?

EDIT: To be honest, fiddled with all of this, and none of it is affecting the result. This is just quality control really. There is another reason why having the pwm running is meaning fatfs cant fill the buffer and gets an error. I dont know what it is but fiddling with prescalers and channels doesnt stop the reading of the sd card failing after the first initiation...

Last Edited: Thu. Nov 17, 2011 - 01:11 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

OCR1A is a combined 16bit version of OCR1AH and OCR1aL. So:

OCR1A = 12345;

is the same as:

OCR1AH = (12345 >> 8);
OCR1AL = (12345 & 0xFF);

Clearly it's easier to use OCR1A!

BTW if you are using a 16 bit timer but playing 8bit samples then you probably want to load them only into OCR1AH alone to effectively promote the data to 16 bit.

Also note that with the timer you want to set the prescaler and the TOP value so that the timer runs at 4 times the sample rate (that is 32kHz) then just reload 0CR1AH on every 4th overlow. The reason for this is that you want the PWM base frequency to be outside of audio range.

But just a minute:

I could have sworn you said you'd already had PROGMEM playing code that said something audible like "it's working" or something? If that worked then stick with it and don't change anything. This is the way to do software development. Work on small pieces and get them to the state where they are tried/tested/working - then leave well alone and work on the next bit.

(BTW, AA man has been here for a couple of hours - he cannot get into the bonnet! :-()

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Is it a new Porsche with flashing lights and high security? SURELY this must be a standard issue - Its still common (enough) for car batteries to lose there charge, have you spoken with porsche. I can't believe the design flaw.
Yeah I had it working reading from the progmem and saying its working.
I have made a few changes, but nothing drastic, and I can still here it is making "normal" noises, it just sounds like a cd is scratched... but its not high pitched stuff or static.
The changes I made were quite minor. I got rid of sample_count, and instead prescaled Timer0 by 8:

TCCR0|=(1<<CS01);//prescaler of 8

before there was no prescaler. Timer1 for the pwm is not prescaled:

TCCR1A = _BV(COM1A1) | _BV(WGM10);//COM1A1 = clear at TOP, WGM10 = fast pwm
TCCR1B = _BV(WGM12) | _BV(CS10) ;//no prescaling. WGM12 = fast pwm

instead of using OCR1A, I have changed it to OCR1AH:

OCR1AH=bufor1[sample++];

I am disabling timer0 and renabling it either side of filling the buffers

TCCR0 &= ~(1<<CS01); //trying to disable pwm timer
//check whether to fill bufor1, or bufor0
//f_read into correct bufor...
TCCR0|=(1<<CS01);

The only thing is I think TOP is set by max value of Timer0, rather than me setting a TOP value.

Thats just how I see it, but i am getting sounds - just the first two buffers worth though. The problem is why is res coming back ~= 0. If i dont execute pwm_init, the buffers fill fine, res = 0 every time. The question is what is the pwm stuff doing to stop f_read executing successfully.
EDIT: Wo! loads of posts right there sorry missed one.
Bob thats what I think my handler is doing - exactly like yours:

ISR(TIMER0_OVF_vect) 

{ 
			if(buf_1)								//play from first buffor
		{	
			OCR1AH=bufor1[sample++];		//set PWM VAlue
		//	OCR1AL=bufor1[sample++];			
			if(sample>510)
			{
				sample=0;
				buf_1=0;
				read=1;
				
			}
		}else//buf_1 = 0									//play from second buffor
		{	
			OCR1AH=bufor0[sample++];		//set PWM VAlue
		//	OCR1AL=bufor0[sample++];			
			if(sample>510)
			{
				sample=0;
				buf_1=1;	
				read=1;			

			}
		}
}

CTC mode, will that fix it?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

I got rid of sample_count, and instead prescaled Timer0 by 8:

That was a bad idea. The whole idea of sample_count was to allow the timer to run at a higher frequency (outside of audio range) then divide it down so it only changed OCR on every 4th overflow. it would be a mistake to remove this mechanism.
Quote:

The only thing is I think TOP is set by max value of Timer0, rather than me setting a TOP value.

That should be OK - it just means you don't have absolute control over frequency - but as long as it's around 32kHz (assuming playing 8kz samples every 4th tick) it should be OK. If you want more control use a PWM mode where either OCR1A or ICR1 set "TOP" (to vary the frequency) but (if using OCR1A for this) you may need switch the actual output to OC1B and use OCR1B to control it.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

OK I got rid of sample_count and prescaled timer0 by 8 as a recomendation from here. I can change it back though.
I have changed over to CTC mode from fast PWM because the flag is automatically set to 1 and OCR1A is updated immediately, automatically. (according to datasheet).
Quick confirmation:
Timer0 is comparing to timer1 right? And it is the frequency of timer0's flag that controls the frequency that OCR1A(H) is updated right - because it is inside timer0's ISR?
So it is the speed at which timer0 updates that sets whether it sounds slow or fast?
From the TUT on timers here:

Quote:

Target Timer Count = (1 / Target Frequency) / (1 / Timer Clock Frequency) - 1

that means for 32000Hz and a 16MHz clock, i need it to role over every time timer0 gets to 499.
I have adjusted Timer0 to use CTC mode, and to 'tick' when it reaches 499:

    TCCR0|= (1<<WGM01) | (1<<CS01);//prescaler of 8, CTC mode
    TCNT0=0;//start count at 0
	OCR0 = 499;
    TIMSK|=(1<<OCIE0);

That should keep it ticking at 32KHz if I understand it right.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I adjusted my handler to be more like Bob's - much neater version

ISR(TIMER0_OVF_vect) 

{ 
			if(buf_1)								//play from first buffor
		{	
			OCR1A=bufor1[sample++];		//set PWM VAlue
		}else//buf_1 = 0									//play from second buffor
		{	
			OCR1A=bufor0[sample++];		//set PWM VAlue
		}
			if(sample>510)
			{
				sample=0;
				buf_1= !buf_1;	
				read=1;			

			}
		
}

Havent brought sample_count back in, if I get rid of the res ~= 0 issue, I cant bring that back in then, its currently not solving my major problem.

Secondly when Timer2 is setup by IoInit() for fatfs the line

	TIMSK = 0b10000000;	// Enable TC2.oc, interrupt

in pwm_init(), This line occurs:

 TIMSK|=(1<<TOIE0);

Now my question is, is the second time overwriting the settings from the first and therefore stopping Timer2 running correctly? Because not initialising pwm_init() means that res = 0 every time. So there must be a conflict somewhere.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

is the second time overwriting the settings from the first and therefore stopping Timer2 running correctly?

Nope. It uses OR to combine the bits.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Yeah spotted the |.
Did you get res = 0?
Fundamentally what could stop fatfs performing due to pwm_init().
pwm is interupting f_read during a read but thats why i put

TCCR0 &= ~(1<<CS01);

before the f_read begins and

TCCR0|=(1<<CS01);

However i think i understand, when an interupt is called it finishes the line it was on, before going to the interupt handler. So would it mess it up?
What if Timer 1 (i.e TCCR1A/B) were stopped, not Timer0? - Or both? But then you would still get breaks in the sound - so that cant be the solution?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Just out of interest have you ever done the test that Bob suggested? Either do 10,000 small f_read()s or perhaps better 10,000 disk_read()s then divide the time by 10,000. What is the time to read one "chunk"/sector? IOW is it quick enough that it can be done synchronously? Another idea is that you are only going to change OCR on every 4th overflow. Leaving you 3 overflows (and all the time in between) when you could be doing something else like either reading a whole 512 byte buffer or perhaps 3 lots of about 170ish.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

ill do it now - with a stopwatch so that i am only using interupts for f_read

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Could it be some sort of timeout in fatfs? Running the pwm isr will present some load to the cpu, thus fatfs code will run a bit slower. What does fatfs use the timer tick for? It has been some time since i've fiddled with it.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

mmc.c has this:

/* This function must be called in period of 10ms                        */

void disk_timerproc (void)
{
	BYTE n, s;


	n = Timer1;				/* 100Hz decrement timer */
	if (n) Timer1 = --n;
	n = Timer2;
	if (n) Timer2 = --n;

	s = Stat;

	if (SOCKWP)				/* Write protected */
		s |= STA_PROTECT;
	else					/* Write enabled */
		s &= ~STA_PROTECT;

	if (SOCKINS)			/* Card inserted */
		s &= ~STA_NODISK;
	else					/* Socket empty */
		s |= (STA_NODISK | STA_NOINIT);

	Stat = s;				/* Update MMC status */
}

As well as checking the write protect and card present signals from time to time notice that it's running soft timers "Timer1" and "Timer2".

Then the code has (for example):

/*-----------------------------------------------------------------------*/
/* Receive a data packet from MMC                                        */
/*-----------------------------------------------------------------------*/

static
int rcvr_datablock (
	BYTE *buff,			/* Data buffer to store received data */
	UINT btr			/* Byte count (must be multiple of 4) */
)
{
	BYTE token;


	Timer1 = 20;
	do {							/* Wait for data packet in timeout of 200ms */
		token = rcvr_spi();
	} while ((token == 0xFF) && Timer1);

which is using Timer1 to perform a timeout on the operation. The disk_initialize() also makes use of Timer1 for timeout operations:

	ty = 0;
	if (send_cmd(CMD0, 0) == 1) {			/* Enter Idle state */
		Timer1 = 100;						/* Initialization timeout of 1000 msec */
		if (send_cmd(CMD8, 0x1AA) == 1) {	/* SDv2? */
			for (n = 0; n < 4; n++) ocr[n] = rcvr_spi();		/* Get trailing return value of R7 resp */
			if (ocr[2] == 0x01 && ocr[3] == 0xAA) {				/* The card can work at vdd range of 2.7-3.6V */
				while (Timer1 && send_cmd(ACMD41, 1UL << 30));	/* Wait for leaving idle state (ACMD41 with HCS bit) */
				if (Timer1 && send_cmd(CMD58, 0) == 0) {		/* Check CCS bit in the OCR */
					for (n = 0; n < 4; n++) ocr[n] = rcvr_spi();
					ty = (ocr[0] & 0x40) ? CT_SD2 | CT_BLOCK : CT_SD2;	/* SDv2 */
				}
			}
		} else {							/* SDv1 or MMCv3 */
			if (send_cmd(ACMD41, 0) <= 1) 	{
				ty = CT_SD1; cmd = ACMD41;	/* SDv1 */
			} else {
				ty = CT_MMC; cmd = CMD1;	/* MMCv3 */
			}
			while (Timer1 && send_cmd(cmd, 0));			/* Wait for leaving idle state */
			if (!Timer1 || send_cmd(CMD16, 512) != 0)	/* Set R/W block length to 512 */
				ty = 0;
		}

Timer2 is used in:

/*-----------------------------------------------------------------------*/
/* Wait for card ready                                                   */
/*-----------------------------------------------------------------------*/

static
int wait_ready (void)	/* 1:OK, 0:Timeout */
{
	BYTE d;


	Timer2 = 50;	/* Wait for ready in timeout of 500ms */
	do
		d = rcvr_spi();
	while (d != 0xFF && Timer2);

	return (d == 0xFF) ? 1 : 0;
}

To be honest, if the TCNT2 interrupt is delayed a bit and these timers tick "late" I don't think it should have a particularly devastating effect.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:
Quote:

I got rid of sample_count, and instead prescaled Timer0 by 8:
That was a bad idea. The whole idea of sample_count was to allow the timer to run at a higher frequency (outside of audio range) then divide it down so it only changed OCR on every 4th overflow. it would be a mistake to remove this mechanism.
Why does it matter if Timer0 runs inside audio range, I'm puzzled?
I agree that the pwm frequency (Timer1) needs to be outside audio range.

OP should perhaps use the sample_count when Timer1 overflows, like in AVR335.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

Why does it matter if Timer0 runs inside audio range, I'm puzzled?

Because you can hear it as a high pitched whine on top of the audio.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:
Quote:

Why does it matter if Timer0 runs inside audio range, I'm puzzled?

Because you can hear it as a high pitched whine on top of the audio.
Really, I was not aware that a timer would cause audible switching noise on a separate pwm channel. :?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Cliff's assertion puzzles me too. A separate timer, not switching anything, has an effect on an OCR output? (and one that then goes into a filter?)

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

All I can tell you is I've tried it and you can hear it if the PWM is in audio range. Also AVR335 has this to say:

Quote:
The PWM frequency has to be at least twice the signal frequency. A PWM frequency at least four times higher is recommended, depending on the output filter.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
The PWM frequency has to be at least twice the signal frequency. A PWM frequency at least four times higher is recommended, depending on the output filter.
That was never in dispute. OP uses a different timer (Timer0) for sequencing.
Are you still saying that this would be audible over the pwm output (Timer1)?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Have you told us the value of the R and C on the OC pin so we can double check your lowpass frequency calcs?

Imagecraft compiler user

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi guys.
so Cliff reckons Timer1 should be four times audio frequency and the others are questioning it - Doesnt matter as Timer1 is outside audible frequency (I think)
With Timer0 I have added sample_count back in, again, Ive heard it working using the data from a header file with sample_count, so i dont see any reason why it cant stay. In fact I have heard the corect sound come over the pwm the way Timer0 and Timer1 are setup - I dont think they are the problem.
I think Cliff might be on to something in his earlier post at 6.49pm.
I have changed Timer_proc so it doesnt check write protection, because I am using microsd:

void disk_timerproc (void)
{
//    static BYTE pv;
    BYTE n, s = 0;
 
 
    n = Timer1; /* 100Hz decrement timer */
    if (n) Timer1 = --n;
    n = Timer2;
    if (n) Timer2 = --n;
 
    s &= ~STA_NODISK;
    s &= ~STA_PROTECT;
 
    Stat = s;
}

But what are 'soft timers'? Are they using the real timers then? Because if they are surely there must be a collision?
Disk_initialize happens before pwm_init occurs so I dont think that can be the problem - especially as the first f_read works (outside the while loop).
So it sounds like from what Cliff wrote it could here where Cliff pointed out because it has to grab packets from the sd card?


/*-----------------------------------------------------------------------*/ 
/* Receive a data packet from MMC                                        */ 
/*-----------------------------------------------------------------------*/ 

static 
int rcvr_datablock ( 
   BYTE *buff,         /* Data buffer to store received data */ 
   UINT btr         /* Byte count (must be multiple of 4) */ 
) 
{ 
   BYTE token; 


   Timer1 = 20; 
   do {                     /* Wait for data packet in timeout of 200ms */ 
      token = rcvr_spi(); 
   } while ((token == 0xFF) && Timer1); 

But Im not sure what a soft timer is and whether this therefore could be the problem?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Sorry Bob was that to me? How will that affect Fatfs' ability to read the sd card? I think from memory its 20uF and 4000 Ohm...

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

a.mlw.walker wrote:
Hi guys.
so Cliff reckons Timer1 should be four times audio frequency and the others are questioning it - Doesnt matter as Timer1 is outside audible frequency (I think)
I reckon Cliff is confusing your code with AVR335 which uses the same timer for both pwm and sequencing.
Skipping 7 out of 8 Timer0 overflow interrupts (in your case) is just a waste of cpu cycles (and timers) IMO.

If you insist on doing it the hard way then make sure you declare sample_count static inside the ISR or as a volatile global.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Soft timer is simply a counter variable you decrement in the ISR for a hardware timer. An AVR may only have 3 hardware timers but can have 10's or even 100's of soft timers - in fact they could all be slaved off a single hardware timer interrupt.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ok whichever - I would ask which way is 'better' but that seems to be a matter of opinion.
So Chan's 'soft timers', they then AREN'T using timer0 and 1 and therefore are 'colliding' with the pwm timers, but there COULD be timeout issues?
Did you fix your Porsche/res=1 issues?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The freq of the lowpass filter is 1/twopi*RC. 8khz sample rate -> 4k filter freq. Try .01uf and 4.7k. Your values were way too low a freq.

Imagecraft compiler user

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

ok but I know from the result of f_read that it cant access the sector on the sd card. Sound will still come out of my speaker even it if does sound crap. res =1 at the mo, and should equal 0, and I know it is due to the pwm code, because if i dont init it, it works... Just had another look, I have 2 0.1uF in parralel (which is 0.2uF), and the resistor is yellow purple red - which if Im not mistaken is 4.7KOhms.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

170Hz. Too low. Needs to be about 4KHz.

Imagecraft compiler user

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Can we confirm the setup of the timer for the fatfs timer tick? Are we using ctc mode? Is it actually giving us 10ms ticks? If it is kosher, set it to 20ms and see if there is a difference. If simply having the pwm isr working causes fatfs to fail then that suggests a memory, performance or stack related issue.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ok so here is the timer setup

	
OCR2 = 90-1;		// Timer2: 100Hz interval (OC2)
	TCCR2 = 0b00001101;

	TIMSK = 0b10000000;	// Enable TC2.oc, interrupt

I did this calc to check it - the comment says 100Hz, however to set up timers I have always used

Quote:
Target Timer Count = (1 / Target Frequency) / (1 / Timer Clock Frequency) - 1

And I dont know why it's 90 - 1, why wouldnt you just write 89?
He is setting WGM21 to 1, so that means CTC is on yes.
CS22 and CS20 mean from the book here in front of me
I think is a prescaler of 1024, and CS20 says toggle OC2 on compare match?

this has been done before though - reading from sd card to pwm???
Come to think of it, I dont think that is going to be 100Hz for a 16MH oscillator?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Well good news:

1) my car is fixed (new battery and they fixed a problem with the electric window and a rattle from the exhaust at the same time)

2) I think I know why my SD reading was failing. The power_on() and power_off() that controls Vcc to the card using PORTE.7 was using inverse logic but my wiring wasn't so the card was powered off when it should be on and vicky verky.

EDIT: too cool for cucumbers! It turns out it was that simple:

FatFs module test monitor for AVR
LFN Disabled, Code page: 1
>di 0
     rc=0
>dd 0 0
       Sector:0
00000000  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00000010  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00000020  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00000030  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00000040  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00000050  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00000060  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00000070  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00000080  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00000090  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
000000A0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
000000B0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
000000C0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
000000D0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
000000E0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
000000F0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00000100  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00000110  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00000120  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00000130  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00000140  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00000150  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00000160  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00000170  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00000180  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00000190  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
000001A0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
000001B0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 03 ................
000001C0  3F 00 06 1B DB D6 FB 00 00 00 05 3B 1E 00 00 00 ?..........;....
000001D0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
000001E0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
000001F0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 55 AA ..............U.

That's a pretty valid looking MBR :-)

EDIT2: higher level works too:

>fi 0
     rc=0 FR_OK
>fl
   ----A 2008/11/30 13:42        20  1GB_TEST.TXT
   1 File(s),        20 bytes total
   0 Dir(s)
>fo 1 1GB_TEST.TXT
                  rc=0 FR_OK
>fd 20
      00000000  54 68 69 73 20 69 73 20 74 68 65 20 31 47 42 20 This is the 1GB
      00000010  63 61 72 64 card
>

Last Edited: Fri. Nov 18, 2011 - 01:22 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hmm I didnt have to worry about that. Maybe i was lucky.
So at least you got a couple of things dealt with then on your car - ven if it did give you grief...
Do you have a choice of terminal program? I have been using termite because it was the first one that just worked when i was learning about serial, however if its passed too much data it crashes and not even an "End Process" can kill it meaning I no longer can access that com port until I log out and back in. So I am on the hunt for a new one.

Just running 30000 f_reads now...
took less than half a second to run through them all. I just set up
while(count<30000)
{f_read...}
printf a start and a stop, and used a stopwatch - not very techincal i know, but issues with using two interupts etc etc. Even If my reactions were delayed the time came in at 0.408 seconds with a 20% innacuracy that's still less than 0.5 seconds for 30000 f_reads, meaning each one is in the 0.00001-0.00002 second region

EDIT: Ahh no. If i pull the cable out it closes...
So you're off. You have it working on your mega128? Whats your clock speed there? 16?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

Do you have a choice of terminal program?

For years and years now I've always favoured Teraterm. There's quite a few versions out there - I use a 4.x version - in fact currently I have 4.6.9

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I changed OCR2 to

OCR2 = 159999;

just to see if it had any affect (instead of 90-1), with pwm off, it still reads fine from f_read, but alas, with pwm initiated, res = 2...

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

OCR2 = 159999;

It'd be a clever trick to put that number into an 8bit register than can only hold 0..255!

By the way his 90-1 is the value for the weird 9.something MHz he was using. I'm using 8MHz and a value of OCR2 = 0x4E looks right (seems to agree with the second-hand on my my watch over about 20 seconds). With that in place I added this to the command interface:

	case 'a':
		{
			int i;
			f_open(&file1, "files.txt", FA_READ);
			xputs(PSTR("\nStart\n"));
			Timer = 0;
			for (i=0; i < 1000; i++) {
				f_read(&file1, Buff, 512, &s2);
				f_lseek(&file1, 0);
			}
			xprintf(PSTR("\nEnd, time = %u\n"), Timer / 100);
		}
		break;

when run I get:

>a

Start

End, time = 18

So about 18 seconds (the watch agreed) to read 1000 lots of 512 (and seek back to the start of the file each time - but I think that overhead will be negligible). So that's 18ms to read each lot of 512 bytes. Now at 8kHz it should only have played 144 of 512 bytes in 18ms. So the bottom line is that the SD should easily be able to keep up with the playback speed.

Next I have to get some audio playback working. That starts with trying to find a speaker I can use (I think I'll just drive it direct off the OC output pin and hope!). Then I'll do what you did and start by playing a PROGMEM array of 8bit 8KHz PCM.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

It'd be a clever trick to put that number into an 8bit register than can only hold 0..255!
hahahaha
who's the fool....
So Im clearly doing the wrong calc here. What did you do to get 0x4E (78)? I got my calc from the tut here on timers...

Im not using chans example code anymore, is Timer a soft timer you/he put in place that is incremented in Timer2 ISR every time it clicks over?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

OK I have written this "theoretically" and parted tested it in my board - the one bit I don't have is a way to test audio output yet (I need to find the box that has the speakers in it still!) but this appears, on the surface to work. I certainly see the LED toggling which seems to suggest the buffers are toggling and that the reads are working. Apart from the sign on message I see no output in the terminal so I assume res=FR_OK every time.

void start_playback(void) {
	// Start timer 0 8Mhz/256 means it will overflow at 31,250Hz
	TIMSK |= (1 << TOIE0);
	TCCR0 = (1 << WGM0) | (1 << CS00); // Fast PWM and no prescale
	play_index = 0;
	active = 1;
}

ISR(TIMER0_OVF_vect) {
	if (active == 1) {
		OCR0 = buff1[play_index++];
	}
	else {
		OCR0 = buff1[play_index++];
	}
	if (play_index >= 512) {
		if (active == 1) {
			active = 2;
		}
		else {
			active = 1;
		}
		play_index = 0;
	}
	if (play_index == 100) {
		// trigger read of the other buffer when one part used
		read_next = 1;
	}
}

/*-----------------------------------------------------------------------*/
/* Main                                                                  */


int main (void)
{
	UINT s2;
	FIL file1;				/* File object */
	FRESULT res;

	IoInit();

	xfunc_out = uart_put;
	xputs(PSTR("\nFatFs module test monitor for AVR\n"));
	xputs(_USE_LFN ? PSTR("LFN Enabled") : PSTR("LFN Disabled"));
	xprintf(PSTR(", Code page: %u\n"), _CODE_PAGE);

	res = f_mount((BYTE)0, &Fatfs[0]);
	if (res != FR_OK) {
		xprintf(PSTR("mount failed, res=%u\n"), res);
	}
	res = f_open(&file1, "test.wav", FA_READ);
	if (res != FR_OK) {
		xprintf(PSTR("open failed, res=%u\n"), res);
	}
	res = f_read(&file1, buff1, 512, &s2);
	if (res != FR_OK) {
		xprintf(PSTR("first read failed, res=%u\n"), res);
	}
	start_playback();
	while(1) {
		if (read_next) {
			PORTG ^= (1 << PG3); // toggle the WIZ200WEB LED
			if (active == 1) {
				// read to the "other" one...
//				xputs(PSTR("Read to 2\n"));
				res = f_read(&file1, buff2, 512, &s2);
				if (res != FR_OK) {
					xprintf(PSTR("Read to 2 failed, res=%u\n"), res);
				}
			}
			else {
//				xputs(PSTR("Read to 1\n"));
				res = f_read(&file1, buff1, 512, &s2);
				if (res != FR_OK) {
					xprintf(PSTR("Read to 1 failed, res=%u\n"), res);
				}
			}
			if (s2 != 512) {
				f_lseek(&file1, 0); // loop back to start of file
			}
			read_next = 0;
		}
	}
}

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Should the second one be buff2?:

 if (active == 1) { 
      OCR0 = buff1[play_index++]; 
   } 
   else { 
      OCR0 = buff1[play_index++]; 

?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

So I swapped your code in for mine, stuck mine in a notepad file for the time being and ran yours. I got loads and loads of [2][2][2][2][2][2][2][2][2][2] on the serial line. Searched your code for somewhere that might output something like this and couldnt find it. I think I overloaded the serial because my computer reset itself and told me it did so because it thought something was gonna be damaged... I lost my notepad file, so now working with your code.
I added in a couple of lines so it would tell me success instead of fail

res = f_mount((BYTE)0, &Fatfs[0]); 
   if (res = !FR_OK) { 
      printf("mount failed, res={perc}u\n", res); 
   } 
   else{
   printf("mount success, res={perc}u\n", res); 
   }

and strangely I get:

Quote:

FatFs module test monitor for AVR
mount failed, res=1
open success, res=0
read success, res=0

BTW Im running at 16MHz so I am going to add a soft timer into your ISR handler so it does that every two clicks of timer 0, and how did you get 0x4E; which is 78, for timer2. If I cant hold a number bigger than 256, I think i need a prescaler?
EDIT: Didnt spot the 1024 prescaler - got it, 155 for me...

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

And in the line:

  TCCR0 = (1 << WGM0) | (1 << CS00); // Fast PWM and no prescale 

should WGM0 instead actually say

  TCCR0 = (1 << WGM00) | (1 << WGM01)| (1 << CS00); // Fast PWM and no prescale 

??

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You are right that it suffered a copy/paste error (two buff1's) but I already corrected that and it makes no difference. One thing I realise I have omitted is the divide 4 on the timer interrupt - I did think the LED indicating 512 byte buffer reading was flashing a bit maniacally!

Quote:

should WGM0 instead actually say

Not according to iom128.h

EDIT: yup, LED now flashing at about 8Hz which is what one would expect to play 8192 bytes (16 lots of 512,. 16 LED changes) per second.

EDIT2: Oh I see what you mean - WGM1 and WGM0 - yes it should have been. Turns out you can use either by the way:

/* Timer/Counter Control Register (generic) */
#define    WGM0         6
#define    WGM1         3

...

/* Timer/Counter 0 Control Register - TCCR0 */
#define    WGM00        6
#define    WGM01        3

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

One thing I realise I have omitted is the divide 4 on the timer interrupt - I did think the LED indicating 512 byte buffer reading was flashing a bit maniacally!

Sorry where's this?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

OK, for completeness this is my complete main.c (which still needs more of the original crap cut out of it). Search "PG3" to find where I'm flashing the LED.

#include 
#include 
#include 
#include 
#include "uart.h"
#include "xitoa.h"
#include "ff.h"
#include "diskio.h"

FUSES = {0xAF, 0xC3, 0xFF};	/* Fuse values: Low, High, Ext */
/* This is the fuse settings for this project. The fuse data will be output into the
   hex file with program code. However some flash programmers may not support this
   sort of hex files. If it is the case, use these values to program the fuse bits.
*/


DWORD AccSize;				/* Work register for fs command */
WORD AccFiles, AccDirs;
FILINFO Finfo;
#if _USE_LFN
char Lfname[_MAX_LFN+1];
#endif


char Line[128];				/* Console input buffer */

FATFS Fatfs[_VOLUMES];		/* File system object for each logical drive */
BYTE buff1[512], buff2[512];	/* Working buffers */

volatile WORD Timer;		/* 100Hz increment timer */

volatile uint8_t read_next;
volatile uint8_t active;
uint8_t div4;
uint16_t play_index;

#if _MULTI_PARTITION != 0
const PARTITION Drives[] = { {0,0}, {0,1} };
#endif

/*---------------------------------------------------------*/
/* 100Hz timer interrupt generated by OC2                  */
/*---------------------------------------------------------*/


ISR(TIMER2_COMP_vect)
{
	Timer++;			/* Performance counter for this module */
	disk_timerproc();	/* Drive timer procedure of low level disk I/O module */
}



/*---------------------------------------------------------*/
/* User Provided Timer Function for FatFs module           */
/*---------------------------------------------------------*/
/* This is a real time clock service to be called from     */
/* FatFs module. Any valid time must be returned even if   */
/* the system does not support a real time clock.          */
/* This is not required in read-only configuration.        */


DWORD get_fattime (void)
{
	/* Pack date and time into a DWORD variable */
	return	  ((DWORD)(2011 - 1980) << 25)
			| ((DWORD)1 << 21)
			| ((DWORD)1 << 16)
			| ((DWORD)12 << 11)
			| ((DWORD)34 << 5)
			| ((DWORD)56 >> 1);
}


static
void put_rc (FRESULT rc)
{
	const prog_char *p;
	static const prog_char str[] =
		"OK\0" "DISK_ERR\0" "INT_ERR\0" "NOT_READY\0" "NO_FILE\0" "NO_PATH\0"
		"INVALID_NAME\0" "DENIED\0" "EXIST\0" "INVALID_OBJECT\0" "WRITE_PROTECTED\0"
		"INVALID_DRIVE\0" "NOT_ENABLED\0" "NO_FILE_SYSTEM\0" "MKFS_ABORTED\0" "TIMEOUT\0"
		"LOCKED\0" "NOT_ENOUGH_CORE\0" "TOO_MANY_OPEN_FILES\0";
	FRESULT i;

	for (p = str, i = 0; i != rc && pgm_read_byte_near(p); i++) {
		while(pgm_read_byte_near(p++));
	}
	xprintf(PSTR("rc=%u FR_%S\n"), rc, p);
}

static
void IoInit ()
{
	PORTA = 0b11111111;	// Port A

	PORTB = 0b10110000; // Port B
	DDRB  = 0b11000000;

	PORTC = 0b11111111;	// Port C

	PORTD = 0b11111111; // Port D

	PORTE = 0b11110010; // Port E
	DDRE  = 0b10000010;

	PORTF = 0b11111111;	// Port F

	PORTG = 0b11111; 	// Port G
	DDRG = (1 << PG3);

	uart_init();		// Initialize UART driver

	OCR2 = 0x4E;		// Timer2: 100Hz interval (OC2)
	TCCR2 = 0b00001101;

	TIMSK = 0b10000000;	// Enable TC2.oc, interrupt

	sei();
}

void start_playback(void) {
	// Start timer 0 8Mhz/256 means it will overflow at 31,250Hz
	TIMSK |= (1 << TOIE0);
	TCCR0 = (1 << WGM01) | (1 << WGM00) | (1 << CS00); // Fast PWM and no prescale
	play_index = 0;
	active = 1;
	div4 = 0;
}

ISR(TIMER0_OVF_vect) {
	div4++;
	if (div4 == 3) {
		if (active == 1) {
			OCR0 = buff1[play_index++];
		}
		else {
			OCR0 = buff2[play_index++];
		}
		if (play_index >= 512) {
			if (active == 1) {
				active = 2;
			}
			else {
				active = 1;
			}
			play_index = 0;
		}
		if (play_index == 100) {
			// trigger read of the other buffer when one part used
			read_next = 1;
		}
		div4 = 0;
	}
}

/*-----------------------------------------------------------------------*/
/* Main                                                                  */


int main (void)
{
	UINT s2;
	FIL file1;				/* File object */
	FRESULT res;

	IoInit();

	xfunc_out = uart_put;
	xputs(PSTR("\nFatFs module test monitor for AVR\n"));
	xputs(_USE_LFN ? PSTR("LFN Enabled") : PSTR("LFN Disabled"));
	xprintf(PSTR(", Code page: %u\n"), _CODE_PAGE);

	res = f_mount((BYTE)0, &Fatfs[0]);
	if (res != FR_OK) {
		xprintf(PSTR("mount failed, res=%u\n"), res);
	}
	res = f_open(&file1, "test.wav", FA_READ);
	if (res != FR_OK) {
		xprintf(PSTR("open failed, res=%u\n"), res);
	}
	res = f_read(&file1, buff1, 512, &s2);
	if (res != FR_OK) {
		xprintf(PSTR("first read failed, res=%u\n"), res);
	}
	start_playback();
	Timer = 0;
	while(1) {
		if (read_next) {
			PORTG ^= (1 << PG3); // toggle the WIZ200WEB LED
			if (active == 1) {
				// read to the "other" one...
//				xputs(PSTR("Read to 2\n"));
				res = f_read(&file1, buff2, 512, &s2);
				if (res != FR_OK) {
					xprintf(PSTR("Read to 2 failed, res=%u\n"), res);
				}
			}
			else {
//				xputs(PSTR("Read to 1\n"));
				res = f_read(&file1, buff1, 512, &s2);
				if (res != FR_OK) {
					xprintf(PSTR("Read to 1 failed, res=%u\n"), res);
				}
			}
			if (s2 != 512) {
				f_lseek(&file1, 0); // loop back to start of file
				xprintf(PSTR("Timer = %u\n"), Timer);
				Timer = 0;
			}
			read_next = 0;
		}
	}
}

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Yep seems to be working in code. Not getting any sound, but may have a pin connection wrong, i'll check...

EDIT. Nope, OCR0, PB4, pin 9 on my external header. Connected up no probs. Why no sound. You got sound yet?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

You got sound yet?


I've yet to find a transducer to play it into.

EDIT: but I just tried this reality check:

uint8_t obuff[1400];
int ob_ptr;

...

ISR(TIMER0_OVF_vect) {
	div4++;
	if (div4 == 3) {
		if (active == 1) {
//			OCR0 = buff1[play_index++];
			obuff[ob_ptr++] = buff1[play_index++];
		}
		else {
//			OCR0 = buff2[play_index++];
			obuff[ob_ptr++] = buff1[play_index++];
		}
		if (ob_ptr >= 1399) {
			ob_ptr = 0;
		}

So instead of outputting bytes to OCR0 I store them into a circular 1400 byte buffer. I then ran the code for a bit then stopped it and examined the buffer in memory at 0x7EA and searched for a similar sequence in the test.wav file (using WinHex). It definitely looks like I'll be loading the correct data into OCR0 when that is enabled...

(BTW this shows the power of using a JTAG debugger!)

Attachment(s): 

Last Edited: Fri. Nov 18, 2011 - 09:12 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hmm, No Im getting zilch through my speaker. Connections are all good, OCR0 not got two registers?
EDIT:
Nope it doesn't. Just checked my book.

Last Edited: Fri. Nov 18, 2011 - 09:15 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

See my edit above including the picture - I'm pretty happy the right values are being loaded into OCR0.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Yeah, I agree, did my own non JTAG test and I think the right values are being passed aswell, however...
reading my book i see for fast PWM do we need to set COM01 and COM00 to 1 to set OC0 on a compare match? Or something to that effect?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Have you configured the pin's DDR as output (I seem to remember that is necessary)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Yeah, I agree, did my own non JTAG test and I think the right values are being passed aswell, however...
reading my book i see for fast PWM do we need to set COM01 and COM00 to 1 to set OC0 on a compare match? Or something to that effect?
According to my book, when set to Fast pwm, if COM00 and COM01 are set to 0, this is normal mode and OC0 is disconnected...

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
DDRB  = 0b11010000;
PORTB = 0b10110000; // Port B

?
and set this also just to cover me bases...

DDRB = _BV(PB4);

Are you sure we can use one timer? Before we needed two?
Have you not got desktop speakers? Or even stick a scope on there...

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I have a 32 ohm speaker. If you use a 100 ohms in series from the port pin, that limits the pin current to about 20ma.

Imagecraft compiler user

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Maybe its because Im on 16Mhz.

OCR2 = 0x9b;		// Timer2: 100Hz interval (OC2)

thats 155. Double your 78 Clawson?
My div4 is now div8 - and resets to 0 at 7 not 3 like yours, I think that should compensate for the 16Mhz.
I have DDRB = _BV(PB4); set and yet just background fizzle very quietly on my speakers.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

BTW I think I found the cause of my res=2 from before, but it doesnt solve the problem now. If I include

DDRB = _BV(PB4);

then I get res = 2 ! I dont know why. That suggests that if OC0 is setup as an output then res = 2
However when I change

DDRB  = 0b11000000;
	PORTB = 0b10110000; // Port B

to

DDRB  = 0b11010000;
	PORTB = 0b10110000; // Port B

then i get res = 0, but wouldnt that be the same thing?
(BTW still no sound)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

bobgardner wrote:
I have a 32 ohm speaker. If you use a 100 ohms in series from the port pin, that limits the pin current to about 20ma.
The ohmic resistance of a loudspeaker will be much higher at these frequencies so it is considered reasonably safe to connect a small 8R speaker directly to a pwm pin, for experimentation purposes anyhow.

Really you would be better off using amplified pc or ipod style speakers after the RC filter, although I do remember seeing a design that used an LC filtering and class D style mosfet amp but just can't locate the source of that right now.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The problem is with setting up OC0. Not with the filter after the pin though

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Shouldn't

TIMSK |= (1 << TOIE0); 

be

TIMSK |= (1 << OCIE0); 

because we are doing a compare match arent we and want to set the output value on compare match?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Nope, you load OCR at the overflow frequency

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ok. What about COM01 and COM00. We need to say when to set OC0? On Compare match or at Top? Cos I dont know if you found a speaker, but Im not getting anything through mine - a very slight hiss.
I havent forgot to set it as output:

DDRB = _BV(PB4);

- thats at the top of main.
And as you said, the buffers are filling correctly, so the only thing I can think is the connection to the pin isnt complete - in the settings for fast PWM. Reading the datasheet, the only thing we havent taken account of is fastpwm setup which reslies on the settings of COM01 and COM00?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I should be able to get access to both a speaker and my 'scopes tomorrow so will try then.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Phew, finally got it working. I thought I was losing my marbles when I couldn't get the PWM output pins to drive - but I just happened to hit "Stop" in the debugger and it landed on:
static

void power_on (void)
{
	PORTE &= ~0x80;				/* Socket power on */
	for (Timer1 = 2; Timer1; );	/* Wait for 20ms */
	PORTB = 0b10110101;			/* Enable drivers */
	DDRB  = 0b11000111;

	SPCR = 0x52;			/* Enable SPI function in mode 0 */
	SPSR = 0x01;			/* SPI 2x mode */
}

static
void power_off (void)
{
	SPCR = 0;				/* Disable SPI function */
	DDRB  = 0b11000000;		/* Disable drivers */
	PORTB = 0b10110000;

	PORTE |=  0x80;			/* Socket power off */
	Stat |= STA_NOINIT;
}

This is real sledge-hammer to crack a nut stuff. Why would he be writing DDRB in anything but the original IoInit()? And if the intention is simply to switch the state of PORTB.0 (CS signal to card) then why blast all the bits in PORTB? I changed this to be:

static
void power_on (void)
{
	PORTE |= 0x80;				/* Socket power on */
	for (Timer1 = 2; Timer1; );	/* Wait for 20ms */
	PORTB |= 1; // CS the card

	SPCR = 0x52;			/* Enable SPI function in mode 0 */
	SPSR = 0x01;			/* SPI 2x mode */
}

static
void power_off (void)
{
	SPCR = 0;				/* Disable SPI function */
	PORTB &= ~1;			// remove CS to the card

	PORTE &=  ~0x80;			/* Socket power off */
	Stat |= STA_NOINIT;
}

and finally heard my Pinky and Perky voice coming (very quietly!) from the speaker I had attached. That's when I realised that I wasn't doing the /4 correctly. So the code in main.c now looks like:

/*----------------------------------------------------------------------*/
/* FAT file system sample project for FatFs            (C)ChaN, 2010    */
/*----------------------------------------------------------------------*/
#include 
#include 
#include 
#include 
#include 
#include "uart.h"
#include "xitoa.h"
#include "ff.h"
#include "diskio.h"

FUSES = {0xAF, 0xC3, 0xFF};	/* Fuse values: Low, High, Ext */
/* This is the fuse settings for this project. The fuse data will be output into the
   hex file with program code. However some flash programmers may not support this
   sort of hex files. If it is the case, use these values to program the fuse bits.
*/


FATFS Fatfs[_VOLUMES];		/* File system object for each logical drive */
BYTE buff1[512], buff2[512];	/* Working buffers */
volatile WORD Timer;		/* 100Hz increment timer */

volatile uint8_t read_next;
volatile uint8_t active;
uint8_t div4;
uint16_t play_index;

/*---------------------------------------------------------*/
/* 100Hz timer interrupt generated by OC2                  */
/*---------------------------------------------------------*/


ISR(TIMER2_COMP_vect)
{
	Timer++;			/* Performance counter for this module */
	disk_timerproc();	/* Drive timer procedure of low level disk I/O module */
}

static
void put_rc (FRESULT rc)
{
	const prog_char *p;
	static const prog_char str[] =
		"OK\0" "DISK_ERR\0" "INT_ERR\0" "NOT_READY\0" "NO_FILE\0" "NO_PATH\0"
		"INVALID_NAME\0" "DENIED\0" "EXIST\0" "INVALID_OBJECT\0" "WRITE_PROTECTED\0"
		"INVALID_DRIVE\0" "NOT_ENABLED\0" "NO_FILE_SYSTEM\0" "MKFS_ABORTED\0" "TIMEOUT\0"
		"LOCKED\0" "NOT_ENOUGH_CORE\0" "TOO_MANY_OPEN_FILES\0";
	FRESULT i;

	for (p = str, i = 0; i != rc && pgm_read_byte_near(p); i++) {
		while(pgm_read_byte_near(p++));
	}
	xprintf(PSTR("rc=%u FR_%S\n"), rc, p);
}

static
void IoInit ()
{
	PORTA = 0b11111111;	// Port A

	PORTB = 0b00000000; // Port B PB7=WP, PB6=CP, PB5=OC1A, PB4=OC0, PB2=MOSI, PB1=SCK, PB0=CS
	DDRB  = 0b00010111;

	PORTC = 0b11111111;	// Port C

	PORTD = 0b11111111; // Port D

	PORTE = 0b11110010; // Port E
	DDRE  = 0b10000010;

	PORTF = 0b11111111;	// Port F

	PORTG = 0b11111; 	// Port G
	DDRG = (1 << PG3);

	uart_init();		// Initialize UART driver

	OCR2 = 0x4E;		// Timer2: 100Hz interval (OC2)
	TCCR2 = 0b00001101;

	TIMSK = 0b10000000;	// Enable TC2.oc, interrupt

	sei();
}

void start_playback(void) {
	// Start timer 0 8Mhz/256 means it will overflow at 31,250Hz
	TIMSK |= (1 << TOIE0);
	TCCR0 = (1 << COM01) | (1 << WGM01) | (1 << WGM00) | (1 << CS00); // Fast PWM and no prescale
	play_index = 0;
	active = 1;
	div4 = 0;
}

ISR(TIMER0_OVF_vect) {
//ISR(TIMER1_OVF_vect) {
	div4++;
	if (div4 == 4) {
		if (active == 1) {
#ifndef TEST_OP
			OCR0 = buff1[play_index++];
#else
			obuff[ob_ptr++] = buff1[play_index++];
#endif
		}
		else {
#ifndef TEST_OP
			OCR0 = buff2[play_index++];
#else
			obuff[ob_ptr++] = buff1[play_index++];
		}
		if (ob_ptr >= 1399) {
			ob_ptr = 0;
#endif
		}
		if (play_index >= 512) {
			if (active == 1) {
				active = 2;
			}
			else {
				active = 1;
			}
			play_index = 0;
		}
		if (play_index == 100) {
			// trigger read of the other buffer when one part used
			read_next = 1;
		}
		div4 = 0;
	}
}

/*-----------------------------------------------------------------------*/
/* Main                                                                  */


int main (void)
{
	UINT s2;
	FIL file1;				/* File object */
	FRESULT res;

	IoInit();

	xfunc_out = uart_put;
	xputs(PSTR("\nFatFs module test monitor for AVR\n"));
	xputs(_USE_LFN ? PSTR("LFN Enabled") : PSTR("LFN Disabled"));
	xprintf(PSTR(", Code page: %u\n"), _CODE_PAGE);

	res = f_mount((BYTE)0, &Fatfs[0]);
	if (res != FR_OK) {
		xprintf(PSTR("mount failed, res=%u\n"), res);
	}
	res = f_open(&file1, "test.wav", FA_READ);
	if (res != FR_OK) {
		xprintf(PSTR("open failed, res=%u\n"), res);
	}
	res = f_read(&file1, buff1, 512, &s2);
	if (res != FR_OK) {
		xprintf(PSTR("first read failed, res=%u\n"), res);
	}
	start_playback();
	Timer = 0;
	while(1) {
		if (read_next) {
			PORTG ^= (1 << PG3); // toggle the WIZ200WEB LED
			if (active == 1) {
				// read to the "other" one...
//				xputs(PSTR("Read to 2\n"));
				res = f_read(&file1, buff2, 512, &s2);
				if (res != FR_OK) {
					xprintf(PSTR("Read to 2 failed, res=%u\n"), res);
				}
			}
			else {
//				xputs(PSTR("Read to 1\n"));
				res = f_read(&file1, buff1, 512, &s2);
				if (res != FR_OK) {
					xprintf(PSTR("Read to 1 failed, res=%u\n"), res);
				}
			}
			if (s2 != 512) {
				f_lseek(&file1, 0); // loop back to start of file
				xprintf(PSTR("Timer = %u\n"), Timer);
				Timer = 0;
			}
			read_next = 0;
		}
	}
}

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Oh and on the issue of audible PWM frequency. I changed the code so it did nothing more than set CS01 instead of CS00 (/8 instead of /1) and then took out the "div4" stuff in the overflow interrupt. I did realise that this would play the audio at half frequency as I replaced a /4 with a /8 but actually there was no chance of hearing any recorded audio as I got nothing but a loud, high pitched tone in my earphones (I gave up on the speaker) completely swamping any other audio.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

So to conclude you left it with a prescaler of 1?
Changed power_on and _off and changed if (div4 == 3) to if (div4 == 4)
is that it, and you got sound?
I'm gonna try it!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

If you are able to use a unified-diff then I took the precaution of putting ffsample/avr into a local SVN repository before I started so can diff with the original files.

Bbut in the short term I think the only other changes I made were in mmc.c at:

#define CS_LOW()	PORTB &= ~1			/* MMC CS = L */
#define	CS_HIGH()	PORTB |= 1			/* MMC CS = H */
#define SOCKWP		(PINB & 0x80)		/* Write protected PB7. yes:true, no:false, default:false */
#define SOCKINS		(!(PINB & 0x40))	/* Card detected PB6.   yes:true, no:false, default:true */

which moved WP and CD to bits 7 and 6 simply because it suited the way I wanted things to be wired.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thank you so much Clawson for all your help. I dont think I would have come to find the power_on/_off issue myself and am very pleased you stepped in when you did. I can hear "its working" my end.
One thing though. I notice in your code you are playing a .wav. I tried a .wav just now and it sounds like the texas chainsaw massacre. I changed it to a .raw file - I had converted a .wav to pcm and it saved as .raw and that worked. Just wondered whether it is possible to play direct .wav files
Once again thank you very much.

Anyone after the code to read into double buffers from a memory card and, in this case pass it to a pin I can upload the code. It includes a, in my opinion very neat version of Fatfs which is much more user friendly and versatile than the one available from chan's website. Although Chan's is very very good, it is quite hard to implement.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Note that WAV is only a container format for RIFF files. What it actually contains can be one of about 50 or more different sound formats. The ONLY format that will work for this is 8bit mono 8KHz PCM sound samples. What I do is use Audacity (free to download) to downsample and convert. I just did this with Larry Adler playing the theme tune to Genevieve on a harmonica and it sounds great played from the mega128. This is what I did in Audacity..

Attachment(s):