F_CPU and Chan's MMC wave player

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

I have been using Elm Chan's tiny88 player for a convention greeting card like project due in 2013. The prototypes have been working for over a year.

Mostly I write assembly code. In wanting to learn more about C, I looked at this code which pretty much worked out of the box. I stripped some of the overhead guff out and made it work on a tiny45, although the price difference to the 85 had me return to the full bloated code.

Question is, the compiler flag F_CPU. Seeing this mentioned in other threads it looks like it should be set to the processor clock. Since all the pins of the tiny85 are used for I/O this should be set to 8000000L right?

The makefile has this at 16000000L, which seems to be working as the songs play at normal speed. I did a search here and the other usual places, but there is no real explanation of this compiler directive, mostly the usual snarky comments. Which sort of form a recursive loop.

Is this an error in the makefile? or would changing it require all the values in the timing loops to be changed too?

Ideally I would like to get rid of the delay loops (which are bad programming) and use timer semaphores and low power sleep modes. Something easy to do in ASM. Not quite sure in C how much overhead in the ISR is required as C tends to want to save the whole stack frame.

Are there any directives, that can reserve a register so that it is not pooled, but owned by the ISR and the wait time out function? These would probably be written in ASM.

I did find the tutorial on C interrupts helpful, but most deal with obfuscating the details from the newer user. With *caugh* close to 40 years of programming, It is too ingrained in me to not save every byte or processor cycle possible.

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

Quote:

Since all the pins of the tiny85 are used for I/O this should be set to 8000000L right?

That depends on the CKDIV8 fuse and whether the code touches CLKPR. Certainly if there's no pins used for a crystal or oscillator then it must be working form intRC but that could easily be 1MHz (or some other division of 8MHz if CLKPR is programmed).

BTW just because the Makefile defines this to 16MHz nothing says it is necessarily used anywhere in the code. My guess is it's just a generic Makefile and because F_CPU is not actually used in any calculation it was just left at a default value carried over from some previous project/Makefile,

An ISR in C only goes mad saving registers if the code calls a function that's not in the same source file. Otherwise expect something like 30 cycles of prologue/epilogue overhead.

volatile register uint8_t foo asm("r2");

will in theory bind the variable 'foo' to R2 as explained in the manual:

http://www.nongnu.org/avr-libc/u...

but note that precompiled library code you might link to will not have been built with this knowledge and could well be using R2 or whatever so this is not a great idea. Far better to just code the whole ISR in Asm as explained in the user manual:

http://www.nongnu.org/avr-libc/u...

Cliff

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

Quote:

Something easy to do in ASM. Not quite sure in C how much overhead in the ISR is required as C tends to want to save the whole stack frame.

Trying to be polite, as a guest on this Forum, let me reword that a bit:
"The C toolchain that I have chosen to use IME tends to want to save the whole stack frame."

Quote:

Are there any directives, that can reserve a register so that it is not pooled ...

Again, that depends on your choice of toolchain.

Quote:

Something easy to do in ASM.

I don't want to re-start the entire C vs. ASM Holy Crusade. But for simple, straightforward stuff like you described I can think of no reason why ASM would have any advantage over C. (The traditional caveats apply: if you are doing things that C has no concept of -- e.g., 24-bit arithmetic, or algorithms that can take great advantage of the carry bit or rotate -- then I'd drop into ASM in a heartbeat.) I've participated in the "you can't do skinny ISRs in C" battles on these forums over the years, and feel that I can hold my own. Whether you can do it with your chosen toolchain, the gurus will need to divine.

So, show me this "simple to do in ASM" ISR.

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

Quote:

Far better to just code the whole ISR in Asm

I stand behind my comments above. And my minimal ISR-servicing overhead is more like 12 cycles.

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

Quote:
"The C toolchain that I have chosen to use IME tends to want to save the whole stack frame."
As Cliff said above, that depends on what you do in the ISR. With minimal code in the ISR (in this case PORTD = 0xFF), the overhead is small:

PUSH      R1      
PUSH      R0      
IN        R0,0x3F 
PUSH      R0      
CLR       R1      
PUSH      R24     

SER       R24     
OUT       0x12,R24

POP       R24     
POP       R0      
OUT       0x3F,R0 
POP       R0      
POP       R1      
RETI         

So 10 clocks coming in and 9 clocks (not including the RETI) going out. More than half of that is the save/restore of SREG and the register actually used in the body of the ISR, so they would need to be done anyways in any code that would affect SREG.

Regards,
Steve A.

The Board helps those that help themselves.

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

Quote:

More than half of that is the save/restore of SREG and the register actually used in the body of the ISR, so they would need to be done anyways in any code that would affect SREG.


Again, it depends on what you call a minimal ISR. There is no way (AFAIK ;) ) on an AVR8 to do your PORTD=0xff; without using a GP register. However, the SREG part isn't needed, and with this given toolchain it wants to fuss with R0.

A bit tighter ISR for the same code:

                 _timer1_ovf_isr:
000025 93ea      	ST   -Y,R30
                 ; 0000 0014 PORTD = 0xff;
000026 efef      	LDI  R30,LOW(255)
000027 b9eb      	OUT  0xB,R30
                 ; 0000 0015 }
000028 91e9      	LD   R30,Y+
000029 9518      	RETI

The truly minimalist ISR writer will dedicate register(s) to ISR work, as Julie asked for, and also avoid instructions that change flag registers. thus I strive for this minimal ISR that isn't empty:

                 _timer1_ovf_isr:
                 ; 0000 0014  flag = 1;
000025 9af0      	SBI  0x1E,0
                 ; 0000 0015 }
000026 9518      	RETI

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

This is why I suggested writing it in Asm

TIMER1_OVF_vect:
  push r24
  ldi r24, 0x55
  out _SFR_IO_ADDR(PORTB), r24
  pop r24
  reti

Like I say you could try binding a register (say r2?) and having it pre-loaded with the 0xFF but you'd then have to check any library code being used and make sure the register were never accessed there (maybe pick a different one if you can find one that's never otherwise used).

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

Quote:

This is why I suggested writing it in Asm

??? Do you really mean "This is why I suggested writing it in Asm if you are using this toolchain"?

Julie made a statement about easy to do in ASM; bloat in C. Essentially, your vote for ASM is confirming that.

My claim is that those blanket statements only apply to a particular toolchain, and how that toolchain does ISR code generation.

As my fragments are intended to show, not all toolchains generate unnecessarily bloated ISR code.

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

Quote:

My claim is that those blanket statements only apply to a particular toolchain,

Yes - this is the GCC forum - it's a pretty fair bet Julie is using and asking the question about GCC isn't it?!?

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

Quote:
Again, it depends on what you call a minimal ISR. There is no way (AFAIK ) on an AVR8 to do your PORTD=0xff; without using a GP register. However, the SREG part isn't needed
You should read more carefully:
Quote:
so they would need to be done anyways in any code that would affect SREG.

Regards,
Steve A.

The Board helps those that help themselves.

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

clawson wrote:
Quote:

My claim is that those blanket statements only apply to a particular toolchain,

Yes - this is the GCC forum - it's a pretty fair bet Julie is using and asking the question about GCC isn't it?!?

Yes specifically GCC. More specific is the studio 4.19 tool chain. (I am not quite sure 5.1b is up to the task though.)

Most of the suggestions here are helpful. (guess the new policies are working.)

It looks though if I want to run some of the petite FatFs code with my older Microkernal (I hate calling it an Rtos) is be to compile the code then extract the asm from the listing file. I could also continue to use my old asm Fat code, which keeps most of the fat data structures in Ram. This was written for a mega8515 with 128k of external ram. I'd like to run this on a mega168.

The goal of this is to record the pipe organ data onto an SD card. Unlike MIDI there is a lot of data in an 18 rank pipe organ (18x61 = over 1kbits of data) this is scanned in 6 microsecond packets, with the 3 microseconds reading and processing the keys and the second sending the data. Analysis of the protocol on the RS485 seems to be 416666.2/3 baud. Search indicating this the fastest the 80C320 can generate. At least there is some 15 or more microseconds between shift register latch pulses.

As can be seen, even with a 20MHz clock there is not a lot of time for ISR functions.

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

Quote:

Yes - this is the GCC forum - it's a pretty fair bet Julie is using and asking the question about GCC isn't it?!?

sigh--

For Cliff--
1) YES, I realize it is the GCC forum.
2) YES, I noted this in my first reply.
3) THE FACT STILL REMAINS that Julie's blanket "C" remark HAS TO DO ONLY WITH HOW THAT PARTICULAR TOOLCHAIN GENERATES ISR CODE, and >>not<< how "C" does it.

For Steve--

Quote:

You should read more carefully:
Quote:
so they would need to be done anyways in any code that would affect SREG.

Perhaps read again. Of course SREG needs to be saved, if there is code that affects it. GCC, in that example, chose to emit code that affets SREG. There are other code sequences, as shown, that do not affect SREG. The result is a skinnier ISR. Which directly addresses Julie's complaint about "C". It ain't "C", folks.
Quote:

As can be seen, even with a 20MHz clock there is not a lot of time for ISR functions.


I've said enough, being on this Forum. But I will again extend the invitation to post your "bloated" ISRs for analysis.

BTW, can SD writes be sustained at that data rate? 512kbps is one 512-byte sector write each millisecond.

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

theusch wrote:

BTW, can SD writes be sustained at that data rate? 512kbps is one 512-byte sector write each millisecond.

There is at least 15 probably more milliseconds between the frames. Only frames that differ using xor are written to the card with time code.

The frames also have a lot of redundancy in them such as nills, These can be compressed if needed with run lengths. Of course this processing takes time, which is why I started this thread...

At the moment I am leaning more to a state machine approach rather than an RTOS. The time is in effect the state. It gets more complicated as SPI is shared between reading the keys and the SD system. Display and button tasks make this even more fun.

As for SD write speed there is a lot written here in the appropriate threads. That seem to be a subject unto itself. A lot seems to depend on the card.

-julie

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

Quote:

3) THE FACT STILL REMAINS that Julie's blanket "C" remark HAS TO DO ONLY WITH HOW THAT PARTICULAR TOOLCHAIN GENERATES ISR CODE, and >>not<< how "C" does it.

I still don't get it. Julie came to the GCC forum to ask about GCC's operation. What difference does it make how other toolchains may choose to operate? It's of no interest to a GCC user. Or was this a veiled advert trying to sell her a copy of CodeVision? ;-)
Quote:
As for SD write speed there is a lot written here in the appropriate threads. That seem to be a subject unto itself. A lot seems to depend on the card.

Indeed (the following should be a picture from Chan's site) but if it doesn't work access:

http://elm-chan.org/fsw/ff/img/r...

Last Edited: Mon. Feb 13, 2012 - 02:59 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

Or was this a veiled advert trying to sell her a copy of CodeVision?

It is indeed a delicate balancing act. If you look at my re-phrase of her statement, which you quoted, I'll let things die.

What I objected to was the blanket statement, rephrased from Julie's and your postings:
"You can't write skinny ISRs in C without bloat, so write them in ASM." That statement is not true for C per se. That statement may well be true for C written with the GCC toolchain for the AVR target. That statement may not be true for other C toolchains with AVR targets.

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

Update:

I managed to port the petit FAT over to a mega168 for the pipe organ recorder. (Not MIDI.)

The solution is to not deal with interrupts and poll on the SFR flags. This way I can read the serial data in by polling it. For output I poll on the timer flag and output the data at fixed intervals.

I had to write the code to create the file and allocate the FAT sectors on the fly. There is 24 milliseconds between the frame header and the data. This is more than enough to write the record as there is only 128 bytes per frame. Most writes seem to take about 6 to 8 milliseconds. Even the fat search and allocate only takes part of the time between frames.

Never thought I would write C on such a small processor, but it is nice to use functions like fopen and fwrite. Better yet is the ability on a large SD card to open a file using a path.

Debugging is not quite to my liking. With the code optimization, it is hard to step through the code as often the sections I want to trace it is impossible to put a breakpoint into.

For some reason, the watch will only show globals. I was taught that globals are bad code practice. On the micro, though they seem to be necessary as the stack space is limited.

I tend to program with pointer casts anyway. So passing a pointer makes much more sense than passing a whole struct. There are times such as reading a file directory entry, where allocating the 32 bytes does make sense. Using an overlay pointer with a global address, allows me to watch the data inside these structs with a cast.

Not quite sure I am ready to return from the dark side, but there is promise using C, especially if I make the transition to some of the ARM chips, although the megaX8 family of 28 pin chips is possibly one of the best ever designed. Such will do almost anything an 8051 can do, without the need for external memory or PLD devices. SPI and I2C is your friend.

Or as we used to say in the 1960s; "Less is more."