My memory is gone!!

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

..and the one from my chip too. :( Did I post this before? Can't remember.

So after a while I get back to my C project. Just include math.h, plug in a little formula to work out dBs (need log10..) and start working on the main program. A few minutes work or so I thought.

So I learned a lot in the next 4 hours :shock: on promoting and demoting variables to work with log10 and print just a 2 digit result. Finally it works but flash usage has gone from 7986 bytes to 12180 bytes (+2k for bootloader) and ram usage from 192 bytes to 474 bytes.

I did expect some jump in memory usage but not that much. So (inferirioty complex on) I must be doing something wrong.

Of course I would be able to do all the calcs in asm with about 100 bytes using a lookup table for the dBs and could do a similar thing in C. (getting very close to giving up now. :? )

Where did I mess up oh great gurus?? (apart from thinking that I could do this)

	read_ambient_noise();					//Get level from microphone circuit

	if (ambient_noise > 204)
		{
		ambient_noise=204;					//Max 85dB
		}

	if (ambient_noise < 4)
		{
		ambient_noise=4;					//Min ~51dB
		}

	printf("Ambient noise %2ddB\n\r", (uint8_t) ( 85 - (20* (log10(204/ambient_noise) ) )) );

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

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

The mistake is using log10. The calculation is slow, and uses floating point, thus your jump in memory & code size. The table-lookup will be much smaller, and comparable to what you expect if you were writing it in ASM.

"Think in Assembler... Write in C"

Writing code is like having sex.... make one little mistake, and you're supporting it for life.

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

glitch wrote:
"Think in Assembler... Write in C"
Good advice. As you get more experienced, John, with C and the comparison of your C code with the resultant assembler output, you'll soon be able to anticipate what assembly output you'll get from your C code and why C is often referred to as a high-level assembly language.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
log10(204/ambient_noise)

And this might not give you a good result, anyways. If ambient_noise is an integer, then the result of the divide will be an integer.

Regards,
Steve A.

The Board helps those that help themselves.

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

This is so annoying, I'm getting reply to topics notifications up to several days later. :evil:

Anyway...using a table makes things a little smaller, 228 bytes instead of 4k and only another 9 bytes of ram used instead of ~270.

I will fix up the table later and use 10bit ADC values instead of 8 bits as it gets a little cramped toward the bottom with some values repeating. Apart from that it is pretty accurate against a real dB meter.

prog_uint8_t dB_Table[35] = 

	{
	204, 182, 162, 144, 129, 115, 102, 91, 81, 72,
	65, 57, 51, 46, 41, 36, 32, 29, 26, 23,
	20, 18, 16, 14, 13, 11, 10, 9, 8, 7,
	6, 6, 5, 5, 4,
	};

void db_calcs ()

	{
	volatile uint8_t j;

		db=0;

		for (j=0; j < 35; j++)

		if ((ambient_noise) >= pgm_read_byte(&dB_Table[j]) )
			{
			break;
			}
		else db++;
	}

Quote:
If ambient_noise is an integer, then the result of the divide will be an integer.
Good enough, +-3dB would be considered ok, I'm doing 1dB steps.
Quote:
Think in Assembler... Write in C"
Much rather Think in Assembler... Write in Assembler" :?

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

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

Quote:

Much rather Think in Assembler... Write in Assembler

:D

If you think education is expensive, try ignorance.

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

js wrote:
Much rather Think in Assembler... Write in Assembler" :?
Then why are you bothering with C?

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

John, I understand you have already a table-driven implementation.

For the general interest I want to point out the two main resources for this kind of bit twiddling:
http://www.hackersdelight.org/
and
http://www-graphics.stanford.edu/~seander/bithacks.html

For example, here are some examples for the integer logarithm basis 10.
http://www.hackersdelight.org/HDcode/ilog.c
http://www-graphics.stanford.edu/~seander/bithacks.html#IntegerLog10

Just wanted to share the links, in case they were not known yet.
Thomas

pycrc -- a free CRC calculator and C source code generator

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

Quote:
Then why are you bothering with C?
Because I have been bullied into it, oh the pain, the name calling, the idignitites I had to endure for years. :wink:

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

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

You are obviously getting the hang of this pretty well.

uint8_t db_calcs (uint8_t ambient_noise)
{
   uint8_t db;   
   for (db=0; db < 35; db++) {
      if ((ambient_noise) >= pgm_read_byte(&dB_Table[db]) )
         break;
   }
   return db;
}

or with 10-bit tables:

prog_uint16_t dB_Table[35] = { 456, 568, ... };

uint8_t db_calcs (uint16_t ambient_noise)
{
   uint8_t db;   
   for (db=0; db < 35; db++) {
      if ((ambient_noise) >= pgm_read_word(&dB_Table[db]) )
         break;
   }
   return db;
}

If you choose to use 10-bit ADC, you just have to alter the type and values of the table, and use pgm_readword().

I used to use 6502 assembler, and every subroutine would be written in terms of return values albeit in Carry, A, X, Y. C is similar in that you are only going to return a simple type or pointer. You will find that void functions are for girls.

If you pass parameters then your sub can be re-used. If you use global values then you have to remember too much and make life hard to re-use. ( girls are better at remembering things )

David.

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

By the way, in the original program size jump when using log10() can you confirm whether you are linking with libm.a. That is in the build output does the very last line that mentions avr-gcc.exe include "-lm" and look something like:

avr-gcc.exe -mmcu=atmega16 -Wl,-Map=test.map test.o    -lm  -o test.elf

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

Quote:
in the original program size jump when using log10() can you confirm whether you are linking with libm.a.
Not really, I dropped using log10() this morning. I only added math.h and nothing else. Anyway its working fine now and using 10 bits.

Just done a massive re-arrangent of the code using multiple files the way I would do it in asm. Probably using too many volatile keywords, is it possible? Without that it seems impossible to pass variable between modules.

The db calculations are now done in a function file along with other core functions that can be accessed by various other modules.

prog_uint16_t dB_Table[35] = 

	{
	818, 729, 650, 579, 516, 460, 410, 365, 326, 290,
	259, 231, 205, 183, 163, 145, 130, 116, 103, 92,
	82, 73, 65, 58, 52, 46, 41, 37, 33, 29,
	26, 23, 21, 18, 16,
	};

//Returns dB value in dB. dB is volatile extern
void read_ambient_noise (void)
{
	uint8_t a, j;
	uint32_t b=0;
	uint16_t c;

	for (a=0; a < 120; a++)
	
	{
	ADMUX=(1<<REFS0 | ADC_0);				//Conversion on channel 0, AVCC reference,  10 bit mode
	ADCSRA |= (1<<ADSC);					//Start conversion
	loop_until_bit_is_set(ADCSRA, ADIF);	//Wait for conversion complete
	ADCSRA |= (1<<ADIF);					//Clear conversion flag
	c=ADC;									//Get last conversion result.

	b=b+c;
	_delay_ms(1);

	}

	ambient_noise=b/120;					//Get average 

	if (ambient_noise > 818)
		{
		ambient_noise=818;					//Max 85dB
		}

	if (ambient_noise < 16)
		{
		ambient_noise=16;					//Min ~51dB
		}

		dB=0;

		for (j=0; j < 35; j++)

		if ((ambient_noise) >= pgm_read_word(&dB_Table[j]) )
			{
			break;
			}
		else dB++;

}

I can see how I can save a bit of space by using dB in the loop instead of j.

Off to bed now...9:45pm here. I will leave evryone alone for a couple of days while I get other stuff out the door.

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

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

I might do one or two things differently:
I'd write a program to generate the table.
In my case I'd use python.
I'd consider making the table a table of strings,
but I suspect it would just use more flash.
Definitely if printf is needed elsewhere.

"SCSI is NOT magic. There are *fundamental technical
reasons* why it is necessary to sacrifice a young
goat to your SCSI chain now and then." -- John Woods

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

Just to say that my point about libm.a - should you ever be tempted to use anything out of math.h again in the future - was simply that if your Makefile (or Studio project) is not set up to link against the hand optmised AVR assembler in libm.a it'll end up linking against some (crude for the AVR) generic stuff out the GCC maths support library which is know to (a) be wrong in some cases and (b) be a code bloater.

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

Quote:
Probably using too many volatile keywords, is it possible? Without that it seems impossible to pass variable between modules.

Volatile should only be necessary if the variable can be modified outside of the current code path. If the variable is not used inside an ISR and is not an AVR register, then volatile is not needed (unless you are doing something fancy with external ram that can be accessed by some other device).

Regards,
Steve A.

The Board helps those that help themselves.

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

js wrote:
Because I have been bullied into it, oh the pain, the name calling, the idignitites I had to endure for years. :wink:
Oh dear, I see. Well, I have two thoughts. First, buck up and ignore the language nazis and continue in your mono-language approach. Second, use to opportunity to learn to code with a different level of abstraction and you might find for some projects that assembly is a better choice and for others a higher level language may work best for your project.

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

js wrote:
Probably using too many volatile keywords, is it possible? Without that it seems impossible to pass variable between modules.
It's not horrible to use too many keywords -- the effect is that you might loose optimizations that would have otherwise been correctly employed. However, using any keyword like that (adding where ever it maybe necessary) will be anxiety-inducing and remind you that you don't really know the language. Further, using the volatile keyword is a documentation to you that a variable's value can be changed in more than one thread of code execution; which on an AVR, means your main program's thread and an interrupt. An exception to that would be to also use volatile to do something without side-effects that you don't want optimized away, like a delay loop.

But, I don't see why volatile would be an issue in sharing variables between modules (unless you the value will be changed during an ISR). For sharing variables via function call, there are only two possibilities: first, if you are passing by value, then just a value is passed to the function. Second, if you are passing by reference, then a pointer is passed and the compiler expects the value pointed to can change when a function is called.

Can you give us a good sample showing where you found that you need to use volatile with using variables and modules?

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

Quote:
I'd write a program to generate the table.
In my case I'd use python.
I used the left index finger 35 times to push the = key in the calculator, as the values entered my left eye and out my right eye, the brain did the rounding which caused my right index finfer to type the values into the table. How I miss the good old days of entering data with toggle switches... :lol:

I do have some BASICA programs somewhere that I wrote years ago to generates such tables, in fact a lot more complicated than this, to generate exponentially decaying sinewaves. But this was easy enough to do on the old Casio calculator.

As far as using volatile for variables: I'm used to have a ram location designated for a task and it will stay such for the whole program. Using local variables that come and go makes me feel uneasy. In asm I would probably use one or more registers for local variables but with C I don't want to think about that, just let the compiler do it's thing.

I haven't mastered passing values with functions yet. So all my functions are void, whatever is passed to them or returned by them is by means of global, volatile variables.

ie dB, supply_voltage, output_port_data. These variable are used in a "functions" file shared by other files like the main file, the monitor program file etc.

..as you can see a rough diamond...

I would like to get all my interrupt service routines in another file but I'm not quite sure if I can put the ISR function names in the header file like I do with other functions. I will need to try it out in my next episode.

ie for the functions file I can put

//Various system functions

void write_to_output_port (void);
void read_supply_voltage (void);
void read_ambient_noise (void);

in it's header file and then add this file to the modules that need to access those functions. Can I do the same thing with ISRs?

ISR(PCINT3_vect)

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
prog_uint8_t dB_Table[35] =

   {
   204, 182, 162, 144, 129, 115, 102, 91, 81, 72, 

Not really relating to your problem but this caught my eye. Does this format work without the PROGMEM after the name?

i.e.

prog_uint8_t dB_Table[35] PROGMEM =
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Never mind, just tried it. Learn something new every day - Definitely looks better :)

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

Quote:

Never mind, just tried it. Learn something new every day - Definitely looks better Smile

prog_uint8_t is defined in the same header that defines PROGMEM as:

typedef uint8_t PROGMEM prog_uint8_t

And so it makes any other mentions of PROGMEM for the same variable redundant. I prefer to use the simple C99 types (uint8_t, int16_t, etc.) and mention the PROGMEM explicitly myself.

- Dean :twisted:

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

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

prog_uint8_t seems to have the PROGMEM built in....I see where someone could use this, say in a bootloader? :-)

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

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

Quote:
prog_uint8_t seems to have the PROGMEM built in....I see where someone could use this, say in a bootloader?

;)

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

Quote:
And so it makes any other mentions of PROGMEM for the same variable redundant. I prefer to use the simple C99 types (uint8_t, int16_t, etc.) and mention the PROGMEM explicitly myself.

Yeah, that big PROGMEM staring at you definitely makes it clear where you want it :)

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

Quote:
I haven't mastered passing values with functions yet. So all my functions are void, whatever is passed to them or returned by them is by means of global, volatile variables.

You will obviously have global variables but you will find it easier to pass parameters by value and assign by return value. Some higher level application specific functions may well work directly with your global variables. It is just that these will not be re-usable with a different program.
I suspect that you already do this in your ASM programming. I cannot believe that you have an unlimited supply of global variable names to cover every program that you have written.

You only need volatile for things that may be altered by an ISR.

Quote:

ie dB, supply_voltage, output_port_data. These variable are used in a "functions" file shared by other files like the main file, the monitor program file etc.

Yes, these look like sensible globals.

Quote:
I would like to get all my interrupt service routines in another file but I'm not quite sure if I can put the ISR function names in the header file like I do with other functions. I will need to try it out in my next episode.

Yes, you just add ISR(TIMER1_COMPA_vect); to your project.h.
You also add your extern global variables here with your structure declarations, extern functions etc.

In ASM programs, I have names like temp that make it pretty obvious that I can never rely on its value. It also hints that there is no need to stack them. If you find that you have any C global variables like this, remove them from your project.h completely. Place these as auto variables inside only those functions that need them.

Soon you will be writing C and I will be doing ASM.

David.