integer to string

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

This has been covered a few times but I can't seem to find an answer that works.

 

I'm using

void lcd_write_int(unsigned int data)
{
	 char st[16] = "";  //  Make space
	 itoa(data,st,10);
	 lcd_write_string(st);
}

to read data from and A to D and then I'm sending the data to my LCD display. Once over 32,768 the LCD displays - negative numbers. I tried with long but the same negative numbers.

 

Is there a long to string ?

 

Tried this but no numbers at all were displayed.

 

void lcd_write_int(unsigned int data)
{
    char st[10];
    sprintf(st,"%s",data);
    lcd_write_string(st);
}

 

 

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

marsheng wrote:
Is there a long to string ?

ltoa()

ɴᴇᴛɪᴢᴇᴎ

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

An int goes -32768 to +32767.

 

An unsigned int goes 0 to 65535 but you need to use utoa instead of itoa.

 

A long goes -2147483647 to 2147483648 and you use ltoa.

 

An unsigned long goes 0 to 4294967296 and you use ultoa to make a string.

 

see http://www.atmel.com/webdoc/AVRL...

If you don't know my whole story, keep your mouth shut.

If you know my whole story, you're an accomplice. Keep your mouth shut. 

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

… and they all live in the very same stdlib.h file. It's not like they're hard to find. ;-)

ɴᴇᴛɪᴢᴇᴎ

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

In GCC there is UTOA(Unsigned TO Ascii).  Here is a snippet of my use of it:

 

int main(void)
{
    AVR_INIT();
    spi_init();
    DISPLAY_INIT();

    while(1)
    {
        TCNT0 = 0X00;	//CLEAR TIMER0
        TCCR0B = 0X07;	//CONNECT T0 PIN TO COUNTER

        _delay_ms(1000);	//WAIT 1 SECOND

        TCCR0B = 0X00;	//DISCONNECT T0

        if(TCNT0 < 10000)
        {
            utoa(TCNT0+10000, buffer, 10);
            buffer[0]='0';
        }

        uint8_t cnt;
        dig = 0x05;
        cnt = 0x00;
        while(dig > 0)
        {
            inf = buffer[cnt++];
            Display_Digit(dig, inf);
            dig--;
        }
    }
}

 

I used it somewhere else as well, but I would have to dig for it.

 

JIm

I would rather attempt something great and fail, than attempt nothing and succeed - Fortune Cookie

 

"The critical shortage here is not stuff, but time." - Johan Ekdahl

 

"Step N is required before you can do step N+1!" - ka7ehk

 

"If you want a career with a known path - become an undertaker. Dead people don't sue!" - Kartman

"Why is there a "Highway to Hell" and only a "Stairway to Heaven"? A prediction of the expected traffic load?"  - Lee "theusch"

 

Speak sweetly. It makes your words easier to digest when at a later date you have to eat them ;-)  - Source Unknown

Please Read: Code-of-Conduct

Atmel Studio6.2/AS7, DipTrace, Quartus, MPLAB, RSLogix user

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

netizen wrote:
… and they all live in the very same stdlib.h file. It's not like they're hard to find. ;-)

Remember that the family of utility routines under discussion isn't part of standardized C.  So a particular toolchain may or may not have the mentioned routines, and they may or may not have prototypes in stdlib.

 

That said, for LCD work I'd want my numbers right-justified and leading-0 suppressed.  OP's approach gives suspicions of tracks being left behind -- what if the value changes from 109 to 99 to 8?

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.

Last Edited: Sun. Aug 28, 2016 - 01:31 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

theusch wrote:
Remember that the family of utility routines under discussion isn't part of standardized C. So a particular toolchain may or may not have the mentioned routines, and they may or may not have prototypes in stdlib.

True. But without any mention to the contrary, I'm assuming the standard avr-gcc/libc toolchain. I believe that's reasonable. :-)

ɴᴇᴛɪᴢᴇᴎ

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

I know it's a bit of a radical concept but if one really wanted to know about such things it might always be possible to consult the manual...

 

http://nongnu.org/avr-libc/user-...

 

(and yes, I checked, OP is a GCC user - a June post says WInAVR in fact ;-)

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

clawson wrote:
OP is a GCC user

A hint:

marsheng wrote:
itoa(data,st,10);

Recent deja vu: https://www.avrfreaks.net/comment...

 

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

Thanks chaps. I learned something new. To me, an integer is a whole number. I'm reminded that integer in c and integer is -32768 to +32767 and I see that itoa follows that same format. I never even thought of looking for a ltoa.

 

That said, for LCD work I'd want my numbers right-justified and leading-0 suppressed.  OP's approach gives suspicions of tracks being left behind -- what if the value changes from 109 to 99 to 8?

 

Quite right, I write a blank line before I write the actual data.

 

What is OP ?

 

Lastly, what is wrong with, why no data to the LCD ?

 

void lcd_write_int(unsigned int data)
{
    char st[10];
    sprintf(st,"%s",data);
    lcd_write_string(st);
}

 

 

 

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

Just realized that itoa is for a signed integer, I presume there is no unsigned to string conversion.

 

 

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

OP is original poster, in this instance -you.

%s expects a null terminated string, you gave it an int. %d might be more useful.

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

marsheng wrote:
Just realized that itoa is for a signed integer, I presume there is no unsigned to string conversion

 

Read what I wrote in post #5

 

Jim

I would rather attempt something great and fail, than attempt nothing and succeed - Fortune Cookie

 

"The critical shortage here is not stuff, but time." - Johan Ekdahl

 

"Step N is required before you can do step N+1!" - ka7ehk

 

"If you want a career with a known path - become an undertaker. Dead people don't sue!" - Kartman

"Why is there a "Highway to Hell" and only a "Stairway to Heaven"? A prediction of the expected traffic load?"  - Lee "theusch"

 

Speak sweetly. It makes your words easier to digest when at a later date you have to eat them ;-)  - Source Unknown

Please Read: Code-of-Conduct

Atmel Studio6.2/AS7, DipTrace, Quartus, MPLAB, RSLogix user

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

Or even follow the advice you got in #8 (though I realise that manuals are only for cissies who have no spirit of adventure). 

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

And if you don't like the standard versions you can always roll your own...

Below are 2 functions I've used for formatting numbers into a string.

 

//===========================================================================
// String Print a number with Fixed widht and precision (decimal point).
// Signed version.
void SPrintFixed( char* Buf, int32_t n, int8_t Length, int8_t Precision) {
	char Sign;
	ldiv_t	Number;

	if( n < 0) {
		n = -n;
		Sign = '-';
	}
	else
		Sign = ' ';

	Buf[Length] = 0x00;
	Number.quot = n;
	do {
		Number = ldiv( Number.quot, 10);
		Buf[--Length] = Number.rem + '0';
		if( --Precision == 0)
			Buf[--Length] = '.';
	}while( (Length > 0) && ((Number.quot) || (Precision >= 0)) );

	if( Sign == '-') {
		if( Length > 0)
			Buf[--Length] = '-';
	}

	while(Length > 0)		// Fill remaining space with spaces.
		Buf[--Length] = ' ';
}

//===========================================================================
// Integer to ascii, Fixed length.
// Copy from snippets.c Version: 2014-05-06.
static void ItoaF( char* Dest, uint32_t Number, uint8_t Digits, uint8_t Base){
	ldiv_t N;

	if(!Digits )
		return;		// No room to store the number.

	Dest += Digits -1;	// Write the number from back to front.

	if( Number == 0) {
		// Write the first digit.
		// Rest will be filled with spaces.
		*Dest-- = '0';
		Digits--;
		// Bug: Debugged, but this line is still missing in some other projects!.
		// Forgetting to decrement digits writes a space to many to the output.
		// 2014-05-06.
	}
	else {
		for( N.quot = Number; N.quot; Digits--, Dest--) {
			N = ldiv( N.quot, Base);
			if( N.rem > 9)				// Hex digit or similar.
				*Dest = N.rem - 10 + 'a';
			else
				*Dest = N.rem + '0';
		}
	}

	while( Digits--) {
		if( Base > 10) {
			if( Digits == 0)
				*Dest = '.';
			else if (Digits == 1)	// Prefix ".x" instead of the more common "0x"
				*Dest = 'x';		// makes printing columns with numbers more compact.
			else
				*Dest = '0';		// Hex digits always filled with zero's.
		}
		else
			*Dest = ' ';

		Dest --;
	}
}

 

Doing magic with a USD 7 Logic Analyser: https://www.avrfreaks.net/comment/2421756#comment-2421756

Bunch of old projects with AVR's: http://www.hoevendesign.com

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

Need help ??

 

I have used this ADC program before and I got 0-255 as an output.

 

void init_adc()
{
	ADMUX = 0x00;					// Select Channel
	ADMUX = ADMUX | 0x40; 		// Selecting voltage reference
	ADMUX = ADMUX | 0x20;  		// Selecting Justification result
	ADCSRA = 0x07;					// Select Clock Frequency
	ADCSRA = ADCSRA | 0x80; 	// Enable ADC
}

unsigned char read_adc()		// Reading ADC and Returns a value
{
	unsigned char a;
	ADCSRA =ADCSRA | 0x40; 		// Starting Conversion
	while((ADCSRA & 0x10)==0);	// Wait for conversion to complete
	a=ADCH;							// Only read 8 bits
	ADCSRA = ADCSRA | 0x10;		// clearing the flag
	return a;
}

 

I'm using the same ADC routione but the LCD displays 0-65472.  The lowerst value above 0  was 64 but this still gives a resolution of 1024.

		ADC = read_adc();
		lcd_write_int(ADC);

 

 

void lcd_write_string(uint8_t String[])
{
	int i = 0;                             // character counter*/
	while (String[i] != 0)
	{
		lcd_write_char(String[i]);
                i++;
	}
}

void lcd_write_int(long data)
{
	char st[10];
	ltoa(data,st,10);
	lcd_write_string(st);
}

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
		ADC = read_adc();
		lcd_write_int(ADC);

Intriguing. Which AVR C compiler is this? Almost all of them predefine "ADC" as a dereferenced pointer to the 16 bit data register of the ADC so do you REALLY think it's wise to use such a name for your own variable or do you really mean to try to write back to the ADC register?!?

 

(apart from anything else you should reserve all upper case in C programs for the name of preprocessor macro names so it is clear to the reader/maintainer what a symbol name is).

 

By the way:

void lcd_write_string(uint8_t String[])
{
	int i = 0;                             // character counter*/
	while (String[i] != 0)
	{
		lcd_write_char(String[i]);
                i++;
	}
}

is more traditionally written as:

void lcd_write_string(uint8_t *String)
{
	while (*String)
	{
		lcd_write_char(*String++);
	}
}

You don't need "i" in this.

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

You are spot on.

 

ADC is a reserved word and contains the result. After a bit of expreimenting, this works fine. 0-65472.

 

 

void init_adc();

MAIN PROGRAM
	read_adc();
	lcd_write_int(ADC);	      



void init_adc()
{
	ADMUX = 0x00;					// Select Channel
	ADMUX = ADMUX | 0x40; 		// Selecting voltage reference
	ADMUX = ADMUX | 0x20;  		// Selecting Justification result
	ADCSRA = 0x07;					// Select Clock Frequency
	ADCSRA = ADCSRA | 0x80; 	// Enable ADC
}

void read_adc()		// Reading ADC and Returns a value
{
	ADCSRA =ADCSRA | 0x40; 		// Starting Conversion
	while((ADCSRA & 0x10)==0);	// Wait for conversion to complete
	ADCSRA = ADCSRA | 0x10;		// clearing the flag
}

 

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

With the above code I get a few warnings.  One is on the function void read_adc() line which is

 

../LCD_Ver1.c:144: warning: return type defaults to 'int'

If the function void read_adc() is just running a routine, why does the compiler say of type int ?

 

If I put a void before the calling line read_adc(), the output is always zero.

 

 

Last Edited: Thu. Sep 1, 2016 - 02:05 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
void init_adc(void);
int read_adc(uint8_t chan);

MAIN PROGRAM
	lcd_write_int(read_adc(0));	      

void init_adc(void)
{
	ADMUX = 0x00;					// Select Channel
	ADMUX = ADMUX | 0x40; 		// Selecting voltage reference
	ADMUX = ADMUX | 0x20;  		// Selecting Justification result
	ADCSRA = 0x07;					// Select Clock Frequency
	ADCSRA = ADCSRA | 0x80; 	// Enable ADC
}

int read_adc(uint8_t chan)		// Reading ADC and Returns a value
{
    ADMUX = chan | 0x60;
	ADCSRA = ADCSRA | 0x40; 		// Starting Conversion
	while((ADCSRA & 0x10)==0);	// Wait for conversion to complete
	ADCSRA = ADCSRA | 0x10;		// clearing the flag
        return ADC;
}

Firstly, why would you have a function called read_adc that doesn't read the ADC? Who are you lying to?

The warning comes from you not having a prototype for your function read_adc, so it defaults to int.

Last Edited: Thu. Sep 1, 2016 - 03:30 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Also don't use "magic numbers" like 0x10 and 0x40. Call them by their proper names which are ADIF and ADSC. It makes the code easier to follow. Also there is no need to use ADIF at all. Just block on ADSC (being set) and it auto-clears (unlike ADIF).

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

After a bit of reading, I can't find any register in ATMELs handbook called ADC. Is ADC a routine in an XXX.h file ? If so, it is reads both ADCH and ADCL.

 

I need to rename read_adc() to run_adc().

 

I'm still confused why the procedure run_adc() needs to beof  type int.

 

It appears that nothing needs to be returned as the result of running the routine puts the result into ADC.

 

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

As pointed out by Kartman, if a routine is not declared (or defined) prior to its usage, it will be assumed to return an int. Placing a declaration of the void function prior to its first usage will eliminate the error about it needing to be an int. ADC is defined as the 16-bit register comprised of ADCH and ADCL.

David

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

I found the mistake. As you have said, I forgot to define the function before using it. 

 

ADC is defined as the 16-bit register comprised of ADCH and ADCL.

Where did you find this ? I can't find it in the manual. When and where is ADC getting its value. Is it one of those atomic statements when you read ADC? 

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

You didn't find anything - the error was pointed out to you. A number of other fundamental things were pointed out.

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

first on this computer I only use studio 4, and it has an other way for register setup.

 

for tiny85 there is a iotn85.h file and for things family it use iotnx5.h file that have this

#ifndef __ASSEMBLER__
#define ADC     _SFR_IO16(0x04)
#endif
#define ADCW    _SFR_IO16(0x04)
#define ADCL    _SFR_IO8(0x04)
#define ADCH    _SFR_IO8(0x05)

 

and that is why I use ADCW (and not ADC) in some of my code. 

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

marsheng wrote:
Where did you find this ?

In the documentation for the toolchain that >>you<< have chosen to use.

 

This newsgroup thread is interesting:

https://lists.nongnu.org/archive...

 

And many prior discussions:

https://www.avrfreaks.net/forum/a...

https://www.avrfreaks.net/forum/a...

https://www.avrfreaks.net/forum/a...

...

 

 

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.

Last Edited: Thu. Sep 1, 2016 - 01:02 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ok I see. I was wondering where ADC was in the ATMEL manual. It's not in there, it is in the compiler manual.

 

I guess saying it is the document is not sufficient, prefixing it with Compiler or Atmel would help.

 

I haven't worked with h files before other than a quick glance.

 

Thanks for the links. I now sort of understand.

Last Edited: Thu. Sep 1, 2016 - 09:07 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Can some one explain exactly what this is (for any future reference). I see the timers could be read in the same way.

 

#define ADC     _SFR_IO16(0x04).

 

Thanks Wallace

 

 

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

If we Google avr-gcc sfr

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

Thanks - Special Function Register.

 

I wonder if I'm ever going to get on top of programming ATMEGAs (or other micros) without throwing out a rescue line to you guys every time I try something new.

 

I have to say that getting something to work is quite rewarding. Takes me back  45 years when I played with 74XXX logic gates and got an LED flashing.

 

Cheers Wallace.

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

Just about the time I think I've got it pretty well figured out, I get thrown a brand new surprise!.

David

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

I'm hoping that in writing this down I'll see my mistake. This is a dummy routine to check syntax. I expect to print 51 but I get 50.

 

int tov_check(int);
int cntH;

/******************************* Main Program Code *************************/
int main(void)
{
   intH=0;  // Initial Value
   while(1)   
   {
	while(!(PIND & (1<<PD0)))      // wait for rising edge
	{
		cntH=50;
		tov_check(cntH);
		lcd_write_long(cntH);
		//code
	}
	while((PIND & (1<<PD0)))  	        // wait for falling edge
	{
	    //code
	}
    }
}

int tov_check(int data)
{
   data++;
   return(data);
}

Nope, still can't see the fault.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
cntH = tov_check(cntH);
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks. Never even thought of that option.

 

Now all working.

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

Understand the difference between pass by value and pass by reference.

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

…otherwise you're gonna get caught again and again…

ɴᴇᴛɪᴢᴇᴎ

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

Yes I see it is like arrays and pointers.

 

ltoa seems to not be working correctly - I get -31072 with this code.

 

 

void lcd_write_long(long);
	

/******************************* Main Program Code *************************/
int main(void)
	CODE
	cntH=100000;
	lcd_write_long(cntH);

}


void lcd_write_long(long data)
{
	char st[50];
	ltoa(data,st,10);
	lcd_write_string(st);
}

if I change the line to

	lcd_write_string("100000");

I get the right result.

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

Hmm I tried

 

void lcd_write_long(long data)
{
	data = 1000000
        char st[50];
	ltoa(data,st,10);
	lcd_write_string(st);
}

and it gave the correct result, so it is something to do with the way cntH and data work.

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

oops cntH is int not long !!!!

 

 

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

marsheng wrote:
Yes I see it is like arrays and pointers.

Err no. When you supply a value to a function, C does this by 'pass by value', so if you have the variable 'a', it copies the value that is in 'a' and passes that to the function. The function can do anything it wants with that value but it doesn't affect 'a'. Pass by reference means the address of the variable 'a' is passed to the function. That means that the function can modify that variable and 'a' is changed. Pass by reference is using a pointer.

 

//Pass by value:
uint32_t a = 10;

    incval(a);      //this effectively does nothing
    a = incval(a);  //this increments a as we assign a value back to it

uint32_t incval( uint32_t val)
{
    val++;
}


//Pass by reference:
uint32_t a = 10;

    incval(&a);         // & is the 'address of' a
    //a now equals 11
    
void incval(uint32_t *val)
{
    *val++;
}

 

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

I'm a tad curious

 

How many clk does the ltoa take? (just the ballpark of AVG and max).

 

I remember that I looked at it using mul with 1/x to solve it. It can be done in about 300 clk max (AVG is close to the same). 

(it will need the HW mul instruction)

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

sparrow2 wrote:

How many clk does the ltoa take? (just the ballpark of AVG and max).

 

I don't have avg or max, but ltoa (1000000, buff, 10) took 147us with 16MHz clock on an ATmega328P.

The ltoa() used 148 bytes of flash.

 

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

Chuck99 wrote:
ltoa (1000000, buff, 10) took 147us with 16MHz clock on an ATmega328P.

That's 147x16=2352 clock cycles!

LOL

ɴᴇᴛɪᴢᴇᴎ

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

netizen wrote:

Chuck99 wrote:

ltoa (1000000, buff, 10) took 147us with 16MHz clock on an ATmega328P.

 

That's 147x16=2352 clock cycles!

LOL

 

I encourage you to run your own test so that we can compare the results.

 

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

Chuck99 wrote:
I encourage you to run your own test so that we can compare the results.

I am in no way criticizing your test result. I think it's laughable precisely because it's likely accurate.

ɴᴇᴛɪᴢᴇᴎ

Last Edited: Mon. Sep 12, 2016 - 10:37 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

iota/ltoa/etc are aimed at human eyeballs so I don't think they are the target for strong optimisation. It also does not help that they offer radix 2..36, if they just did decimals he code could likely be optimised for that.

 

But as it is human eyeballs why does it matter?

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

clawson wrote:
But as it is human eyeballs why does it matter?

It of course does not in this regard. It might in so far as it is stealing (potentially precious) cycles from other parts of the code.

Had I been the one who coded it, it would itch like hell… Not that you need to care.

Anyway, it'd probably be much faster with a simpler feature set indeed.

ɴᴇᴛɪᴢᴇᴎ

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

The great thing about AVR-LibC is that it is open source and a collaborative development. If anyone finds parts to be deficient they can easily pull the source, improve it then push the change. :-)

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

clawson wrote:
If anyone finds parts to be deficient

This stretch is yours alone. :-)

I wouldn't give up 2300+ cycles to put 1000000 in a string. But then I'd probably have to settle for a simpler feature set than ltoa provides. And we both know the reasons it does so go beyond avr-libc…

ɴᴇᴛɪᴢᴇᴎ

Last Edited: Mon. Sep 12, 2016 - 12:27 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

It's pretty slow. I estimate a dedicated utoa specialized for base 10 result would take about 20-25% of those 2300 cycles. It would need 5 16x16 bit multiplies, according to AVR200 each such multiply takes about 100 cycles, so 500 cycles total. And I'm sure this is a worst case estimate with plenty of room for optimization, if written in assembly.

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

Do you talk about utoa(16bit) or ltoa (32bit)?

 

There is a big difference, a utoa can be done in about 70 clk (worst case)

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

sparrow2 wrote:

Do you talk about utoa(16bit) or ltoa (32bit)?

 

There is a big difference, a utoa can be done in about 70 clk (worst case)

"Here we go again!"  ;)

 

Indeed, what are the rules for the "contest"?  Obviously there will be a difference in time/space between 16-bit and 32-bit.  Also a difference between unsigned and signed.  And are you going to right-justify?  Leading-zero-supress?  And one always needs to tell what number is being converted, as that will change the cycles for most approaches.

 

Starting with

https://www.avrfreaks.net/forum/s...

...one toolchain took a bit over 100 cycles for itoa(9999).  https://www.avrfreaks.net/comment...

sparrow2, what is your worst-case number to convert?

 

In that thread, danni gave a routine that handles up to 42000 in a couple hundred cycles and is small.

 

sparrow2 referred to a different approach, and posted the result in

https://www.avrfreaks.net/comment...

 

 

 

 

...

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

remember this thread is 100% 32 bit!

that was why I made a comment.

I would say that anything about 16 bit in this thread would be noise!

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

sparrow2 wrote:

remember this thread is 100% 32 bit!

that was why I made a comment.

I would say that anything about 16 bit in this thread would be noise!

???

 

void lcd_write_int(unsigned int data)

It looks like unsigned 16-bit to me...  "utoa"

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

Chill out, the OP said:

marsheng wrote:
Once over 32,768 the LCD displays - negative numbers.

 

This is the behaviour of a signed 16 bit, so I guess I jumped to the conclusion this thread was about 16bit numbers. Now I will read these other threads, I like to learn algos.

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

sorry but the last 20 post's have been about ltoa.

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

sparrow2, what is your worst-case number to convert?

As you have in the last link about 70 clk for a full 16 bit

 

and if max is 9999 it's about 50 clk

 

And about 300 if it's extented to a ltoa (use same rutine and make two div with 10000)

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

sparrow2 wrote:
remember this thread is 100% 32 bit! 
sparrow2 wrote:
sorry but the last 20 post's have been about ltoa.

Now I'll suspect your clock counts, if 20/50 is 100%. ;)

 

I'll have to examine your code.  Will this give exact results for all input numbers?

 

 

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

For the record this is what you are calling when you call ltoa() in AVR-LibC:

#define val_lo	r22
#define str_lo	r20
#define rdx_lo	r18
#define rdx_hi	r19


ENTRY	ltoa
ENTRY	__ltoa

	cpi	rdx_lo, 37
	cpc	rdx_hi, __zero_reg__
	brsh	1f
	cpi	rdx_lo, 2
	brlo	1f
	XJMP	_U(__ltoa_ncheck)

1:	X_movw	ZL, str_lo
	st	Z, __zero_reg__
	X_movw	r24, str_lo
	ret
#define val_lo	r22
#define val_hi	r23
#define val_hlo	r24
#define val_hhi	r25
#define str_lo	r20
#define rdx_lo	r18

#define sign	r27	/* Argument for __ultoa_common()	*/

ENTRY	__ltoa_ncheck

	clr	sign
	cpi	rdx_lo, 10
	brne	1f
	tst	val_hhi
	brpl	1f

    ; radix == 10 && val < 0: sign = '-' and val = -val
	ldi	sign, '-'
	com	val_hhi
	com	val_hlo
	com	val_hi
	neg	val_lo
	sbci	val_hi, -1
	sbci	val_hlo, -1
	sbci	val_hhi, -1
1:	XJMP	_U(__ultoa_common)
#define val_lo	r22
#define val_hi	r23
#define val_hlo	r24
#define val_hhi	r25
#define str_lo	r20
#define radix	r18

#define counter	r19
#define digit	r26
#define sign	r27


ENTRY	__ultoa_ncheck
	clr	sign

ENTRY	__ultoa_common
	X_movw	ZL, str_lo

1:  ; Saves one iteration of the digit-loop:
    ; If val < radix we can use the low byte of val as digit
	mov	digit, val_lo
	cp	val_lo, radix
	cpc	val_hi, __zero_reg__
	cpc	val_hlo, __zero_reg__
	cpc	val_hhi, __zero_reg__
    ; now C is set, if val < radix
	sbc	counter, counter
	bst	counter, 0
	brts	4f
    ; counter == 0 here

    ; If val >= radix, then pop one digit from val
	clr	digit

2:  ; Vanilla 32:8 quotient and remainder to pop the digit
    ; digit <- val % radix
    ; val   <- val / radix
	lsl	val_lo
	rol	val_hi
	rol	val_hlo
	rol	val_hhi
	rol	digit
	cp	digit, radix
	brlo	3f
	sub	digit, radix
    ; val |= 1
	inc	val_lo
3:  ; Loop the 32 bits
	subi	counter, 8		; 256 / 8 == 32 loops
	brne	2b

4:  ; Convert the digit to ASCII...
	subi	digit, -'0'
	cpi	digit, '9'+1
	brlo	5f
	subi	digit, '0'-'a'+10
5:  ; ... and store it to the reversed string
	st	Z+, digit

    ; Popped all digits?
	brtc    1b

    ; Yes:  Store the sign (if any)
	cpse	sign, __zero_reg__
	st	Z+, sign

    ; Terminate the string with '\0'
7:	st	Z, __zero_reg__

    ; Reverse the string and return the original string pointer
	X_movw	r24, str_lo
	XJMP	_U(strrev)
#include "macros.inc"

#define str_hi r25
#define str_lo r24
#define ltemp  r23
#define rtemp  r22

    ASSEMBLY_CLIB_SECTION
    .global _U(strrev)
    .type   _U(strrev), @function

_U(strrev):
	X_movw	XL, str_lo	; X is start of string
	X_movw	ZL, str_lo	; Z becomes end of string
  ; find end of string
1:	mov	rtemp, ltemp	; to obtain right nonzero character
	ld	ltemp, Z+
	tst	ltemp
	brne	1b
	sbiw	ZL, 2		; to last nonzero byte
	rjmp	3f
  ; swap bytes
2:	ld	ltemp, X
	st	X+, rtemp
	st	Z, ltemp
	ld	rtemp, -Z
3:	cp	XL, ZL
	cpc	XH, ZH
	brlo	2b
	
	ret

If anyone can spot ways to speed/improve this feel free to say - you can become famous as one of the AVR-LibC authors and you get your name on this page...

 

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

 

(note that you have to willing to make the code available under a BSD licence).

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

clawson wrote:
If anyone can spot ways to speed/improve this feel free to say

+1

Let's make this interesting…  ^^

ɴᴇᴛɪᴢᴇᴎ

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

Yes 

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

After staring at this code for quite a while, I concluded they are doing a long division using some clever shifting of the dividend instead of the divisor. This cleverness means the code can probably not be much improved unless a different division algorithm is used (not long division).

I'm not sure if an alternative using the multiply by inverse algo would be faster, it would probably require a lookup table with "magic numbers" for all the valid inverses (1/2, 1/3....1/36).

I think I'll pass on this one.

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

But in reality who want other than 2 10 and 16 as base numbers?

and 2 and 16 are easy

so it's only 10 we need to look at. 

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

I've had 1G37T30 times I've needed to use base 30!

 

But as the AVR-LibC [i|l]toa() functions has offered radix 36 since time immemorial I guess any replacement needs to continue to do so for the one person who's used a non 2/10/16 radix. Of course the code could look out for 2/10/16 and use a more optimal solution for those specific bases.

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

clawson wrote:
But as the AVR-LibC [i|l]toa() functions has offered radix 36 since time immemorial I guess any replacement needs to continue to do so

That's the issue. The code is probably fine, although we might be able to save a cycle here or there. The generic algo is likely good too.

The problem lies in the library interface itself. It makes sense to offer free radix in a powerful library, but we MCU users don't need it as much as we need it fast and small.

You said it yourself, Cliff: most ltoa usage is for user feedback, which means it's gonna be base 10 99% of the time. The need for base 2 and 16 is much smaller already (debugging?) and should not impact our "main" library code. The need for other radices pretty much does not exist (if you need it, just use ltoa and its sisters). If we kept to these radices, as sparrow2 mentioned, we could get much better/faster library code.

That would be the sort of library we really need, despite all the merits of a standardized libc.

 

ɴᴇᴛɪᴢᴇᴎ

Last Edited: Mon. Sep 12, 2016 - 08:21 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Just for fun: how many cycles does it take ltoa to convert 1000000 to binary?

ɴᴇᴛɪᴢᴇᴎ

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

clawson wrote:
For the record this is what you are calling when you call ltoa() in AVR-LibC:

You discarded an important part (stdlib.h):

extern __inline__ __ATTR_GNU_INLINE__
char *ltoa (long __val, char *__s, int __radix)
{
    if (!__builtin_constant_p (__radix)) {
	extern char *__ltoa (long, char *, int);
	return __ltoa (__val, __s, __radix);
    } else if (__radix < 2 || __radix > 36) {
	*__s = 0;
	return __s;
    } else {
	extern char *__ltoa_ncheck (long, char *, unsigned char);
	return __ltoa_ncheck (__val, __s, __radix);
    }
}

There is provision here to do further radix checks and branch to radix-optimized code.

ɴᴇᴛɪᴢᴇᴎ

Last Edited: Mon. Sep 12, 2016 - 08:47 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

netizen wrote:

I wouldn't give up 2300+ cycles to put 1000000 in a string.

 

It's interesting that you consider 2300+ cycles (147us at 16MHz) to be a great burden that must be avoided.

 

Every program that I have written for microcontrollers has periods where it wastes cycles waiting for something to happen.

It is during these periods that I schedule operations such as ltoa(), so the burden is nil.

 

What is nice about having ltoa() in the library is that it's an alternative to sprinf() that uses less flash.  Also, being in the library it is likely to be bug free.

 

If there was a smaller and faster equivalent function in the library that only did radix 10, I would gladly use that.

 

 

 

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

Chuck99 wrote:
It's interesting that you consider 2300+ cycles (147us at 16MHz) to be a great burden that must be avoided.

Well, I get your point but I don't want to get drowned into that. Surely you'll agree that you can't implement a library like libc with this mindset though…

 

Chuck99 wrote:
If there was a smaller and faster equivalent function in the library that only did radix 10, I would gladly use that.

I'm glad to hear that. :-)

In fact, another non-libc dedicated function would be the best choice: I don't think it's realistic to imagine freaks will get to weight on the libc interface though. And if we keep to the current interface, even with branching to radix-optimized code, we'll still be clobering a parameter register and wasting a few cycles because we have to specify a 10 radix. It's not much, but it will happen 99% of the time…

Nevertheless, what we could do is propose alternative implementation for existing libc functions that do provide the benefits we need. That would be an improvement.

ɴᴇᴛɪᴢᴇᴎ

Last Edited: Mon. Sep 12, 2016 - 09:08 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

netizen wrote:

 

In fact, another non-libc dedicated function would be the best choice: I don't think it's realistic to imagine freaks will get to weight on the libc interface though. And if we keep to the current interface, even with branching to radix-optimized code, we'll still be clobering a parameter register and wasting a few cycles because we have to specify a 10 radix. It's not much, but it will happen 99% of the time…

Nevertheless, what we could do is propose alternative implementation for existing libc functions that do provide the benefits we need. That would be an improvement.

 

Maybe not, since the base is a known constant at compile time, the compiler might be smart enough to optimize all the overhead away, and directly call the "right" alternative function. Who knows, only with testing...

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

El Tangas wrote:
Maybe not, since the base is a known constant at compile time, the compiler might be smart enough to optimize all the overhead away, and directly call the "right" alternative function. Who knows, only with testing...

Good point. I don't think GCC will mess with a function definition (remove an unused/constant parameter) though…

ɴᴇᴛɪᴢᴇᴎ

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

For fast and small take a look at danni's code there https://www.avrfreaks.net/comment...

 

It is small  has a max of 170 clk.

 

If worst case needs to be improved some extra code can be added

either first (forget first digit it's not full) do sub 4000 until negative and then add 1000 until positiv  then with 400 100 40 10 4 1.

or do some bit checks if bit 12 is set (and first digit found) init with 4 and sub 5000 (we know what we can do 4000) and check etc.

I have not made it but I guess about 100- 120 clk for worst-case can be done.

The good thing is that this code works on tiny's.

 

For mega's the HW mul can be used to make div by mul  with 1/x.

And that can the be done in worst-case of 65 clk (my old version can be improved) 

 

Both can be made for 32 bit  

(with the 1/x version I would div 32 bit with 10000 and the use the 16 bit rutine without first digit, then div with 10000 again  .. .. and the the last 2 digits) 

 

 

 

Last Edited: Mon. Sep 12, 2016 - 09:38 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I don't think that the radix needs to be known at compile time, radix is not a const.

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

Thanks, sparrow2.

 

I personally won't bother spending time "fixing" this unless the powers that be agree on the principle beforehand. In which case, they'll probably implement it themselves —but that's another story.

Do we have an agreement here that optimized code for radices 10, 2 and 16 would be beneficial, and provide more opportunities for these stdlib conversion functions to be used more widely?

ɴᴇᴛɪᴢᴇᴎ

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

netizen wrote:
Do we have an agreement here that optimized code for radices 10, 2 and 16 would be beneficial, and provide more opportunities for these stdlib conversion functions to be used more widely?

FWIW I don't use any of these XtoY functions for this reason alone, but perhaps that's just me…

ɴᴇᴛɪᴢᴇᴎ

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

netizen wrote:
Do we have an agreement here that optimized code for radices 10, 2 and 16 would be beneficial, and provide more opportunities for these stdlib conversion functions to be used more widely?
The way collaborative software tends to work is that when someone finds some missing/deficient feature in what exists that they simply cannot live without they put in the time/effort to implement it and then if they are a generous soul they donate their work back for others to share later. While it sounds like you may consider Xtoa() to be deficient I get the sneaking feeling that your need to have it "corrected" is possibly not great enough to drive you to be the person that must have it fixed at all costs ;-) If someone comes along saying "I need radix 10/16 as fast as possible at any cost possible!!" then I guess we may have found our candidate to make changes?

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

clawson wrote:
The way collaborative software tends to work is that when someone finds some missing/deficient feature in what exists that they simply cannot live without they put in the time/effort to implement it and then if they are a generous soul they donate their work back for others to share later.

My experience in dealing with established open source projects is slightly different: it is likely that your personal improvements will be refused because in the general case there are reasons not to implement them that way. It could be constraints you're not aware of, because you only see part of the picture. It could be some less significant habits that someone higher up in the food chain happens to have. Sometimes it might just be an ego trip from someone powerful enough to impose it's tantrum on the rest of us. I could link to examples of each from my own experience, but that wouldn't help. :-)

The thing is, coding something for one project and coding it for something like stdlib is not the same: of course the later requires much higher standards, and they obviously come at a cost. Thus my question: I'm not gonna bear that cost to play bingo and eventually see it all go to waste…

ɴᴇᴛɪᴢᴇᴎ

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

But I notice you conveniently dodged the question: do you support the effort or not?

ɴᴇᴛɪᴢᴇᴎ

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

By the way, Cliff: you've got mail. :-)

ɴᴇᴛɪᴢᴇᴎ

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

netizen wrote:
do you support the effort or not?

How many times has anyone here used conversion to other than base 10 and an occasional base 16 in any application?

 

And as a "real" part of the application and not a curiosity or "don't care about the cycles"?

 

There are some that like their base 2 displays.  I cannot remember ever doing that myself.

 

I can't remember doing octal since PDP and VAX days.

 

I remember a base 36 in a past life.  Let's call that a curiosity.

 

So IMO your effort would be a solution in search of a problem.

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:
So IMO your effort would be a solution in search of a problem.

Thanks. We happen to disagree, but at least you made your opinion clear. :-)

ɴᴇᴛɪᴢᴇᴎ

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

By the way, Lee, in light of this opinion: how come you're using your own version instead of a stdlib ready-made function?

ɴᴇᴛɪᴢᴇᴎ

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

I've used base 2 sometimes but it sure would help if there'd also been a leading 0 padding option! You end up itoa() and then doing str*() to put in the leading zeroes anyway.

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

Cliff: did you get my private message?

ɴᴇᴛɪᴢᴇᴎ

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

clawson wrote:
I've used base 2 sometimes but it sure would help if there'd also been a leading 0 padding option! You end up itoa() and then doing str*() to put in the leading zeroes anyway.

I feel you, but this ain't going to happen: we're not gonna change the stdlib API. What about optimized code for common radices?

ɴᴇᴛɪᴢᴇᴎ

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

netizen wrote:

Cliff: did you get my private message?

I did eventually - there's something very odd about this whole Freaks/Communities site thing. It seemed to believe I *was* logged into Freaks but was not logged into Communities.
netizen wrote:
What about optimized code for common radices?
I'm not really the person to ask (just another user). The people who develop AVR-LibC gather here:

 

https://lists.nongnu.org/mailman...

 

There is this too:

 

https://lists.nongnu.org/mailman...

 

(several say they prefer to deal with this stuff via email rather than internet fora).

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

clawson wrote:
I'm not really the person to ask (just another user). The people who develop AVR-LibC gather here

I know where they gather. I just want to know where freaks stand before I bother them: it's not the same if it's just me, or experienced freaks backing up the effort.

ɴᴇᴛɪᴢᴇᴎ

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

Well, for binary keeping leading zeroes would be trivially simple, you just need to shift the bits to the carry flag, then adc with '0' char, repeat 32 times, add the zero string terminator, and that's it. For other bases that are powers of 2, it would also be very easy.

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

netizen wrote:
By the way, Lee, in light of this opinion: how come you're using your own version instead of a stdlib ready-made function?

Isn't that all explored in the link I gave in #53?  (#21; #23; ...)

 

https://www.avrfreaks.net/comment...

theusch wrote:

Nearly always I want my display numbers to be right-justified [and fixed width]. The biggest exception might be free--format "dump" strings, but then size/speed don't matter that much. So my home-built routines are almost always adaptations of "itoa()"-type.

The same goes for leading-zero suppression. Usually I want it for display, but not for manipulating setpoints and a few other situations. So I've got an option for that.

When catered for, both of those are virtually free on top of the actual conversion itself.  IMO/IME a lot cheaper when integrated into the conversion than with a post-processing pass.  Suit yourself.  If the app has a need for (s)printf otherwise, I'll tend to use that and hang the extra cycles a few times a second.  If the app is updating a many-digit 7-seg high-speed counter https://www.marshbellofram.com/a... then indeed I'll try to keep the binary=>BCD time down.

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.

Last Edited: Wed. Sep 14, 2016 - 03:41 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

netizen wrote:

clawson wrote:
I'm not really the person to ask (just another user). The people who develop AVR-LibC gather here

I know where they gather. I just want to know where freaks stand before I bother them: it's not the same if it's just me, or experienced freaks backing up the effort.

Can I refer Sir back to:

 

#47: https://www.avrfreaks.net/comment...

 

My personal opinion has not changed from there I'm afraid.

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

theusch wrote:
netizen wrote:
By the way, Lee, in light of this opinion: how come you're using your own version instead of a stdlib ready-made function?

Isn't that all explored in the link I gave in #53? (#21; #23; ...)

Indeed it is. But I don't want to take chances and let misinterpretation blur my point: I want your explicit opinion on this particular matter.

 

theusch wrote:
When catered for, both of those are virtually free on top of the actual conversion itself. IMO/IME a lot cheaper when integrated into the conversion than with a post-processing pass.

So, you think having optimized code for common radices is a "solution in search of a problem" (#81), yet you roll your own code with integrated right-justified and fixed width to save a few cycles of post-processing. Am I getting this right?

ɴᴇᴛɪᴢᴇᴎ

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

netizen wrote:
Am I getting this right?

Define "few".  The best itoa/utoa implementations, depending on the rules, are a couple hundred cycles.  I'll wager that taking that output, placing right-justified into a fixed-width field, and leading-zero-supress will double that.

 

And indeed, IMO/IME radices other than 10 are rare.  Base 16, once in a while.  [BUT THAT IS SUCH A TRIVIAL CASE THAT IT WILL BLOW THE SHORTS OFF ANY GENERIC IMPLEMENTATION--the value is already in packed-BCD format.]

 

I don't know what you are pounding at.  If you care to propose changes to this particular toolchain for consideration, that is up to you.  No blessing from me is needed.  It ain't my usual toolchain anyway. 

 

netizen wrote:
instead of a stdlib ready-made function?

Remember that itoa and variants aren't part of any standard C library.  You are free to extend the language however you see fit.

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.

Last Edited: Wed. Sep 14, 2016 - 04:15 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

theusch wrote:
Define "few". The best itoa/utoa implementations, depending on the rules, are a couple hundred cycles. I'll wager that taking that output, placing right-justified into a fixed-width field, and leading-zero-supress will double that.

First, remember the initial case for this thread is ltoa, not itoa/utoa. And the cycles count is 2300, thus ±200 would indeed be "few".

But let's make it easy on you and consider the fastest: utoa, so you might have a point on a relative basis.

The post-processing function would be something like:

_utoa(char *src, char *dst, char fill);

Its job is to move a pointer through *src until it encounters /0 (6 steps max), then move backward copying *src to *dst+i and filling what's left with fill (5 steps max). That, according to you, is 200 cycles long? Challenge accepted: show me the code!

 

theusch wrote:
And indeed, IMO/IME radices other than 10 are rare. Base 16, once in a while.

Alright, so you actually are agreeing with me/us. :-)

 

theusch wrote:
I don't know what you are pounding at. If you care to propose changes to this particular toolchain for consideration, that is up to you. No blessing from me is needed.

I need feedback from experienced people to support my case. You happen to be one of them, although you claim not to. :-)

ɴᴇᴛɪᴢᴇᴎ

Last Edited: Wed. Sep 14, 2016 - 05:24 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

netizen wrote:
First, remember the initial case for this thread is ltoa, not itoa/utoa.

Sigh.

(unsigned int data)

and then the complaint "Once over 32,768 the LCD displays - negative numbers."

netizen wrote:
And the cycles count is 2300,

The cycles count for what?  Many cycle counts given in the other thread, and in this one.

 

 

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

Alright, nevermind. You have chosen to object, no matter what. It's your choice and I respect that. Let's leave it at that. :-)

ɴᴇᴛɪᴢᴇᴎ

Last Edited: Wed. Sep 14, 2016 - 05:44 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

netizen wrote:

Alright, nevermind. You have chosen to object, no matter what. It's your choice and I respect that. Let's leave it at that. :-)

No.  The 2300+ cycles that you are using was your number, from Chuck's ltoa() experiment with 1000000.  How do you relate that to itoa or a utoa implementation?

 

In the other thread, my toolchain's itoa() was 341 cycles for 9999.

danni's approach was 165 cycles for 9999.

sparrow2's approach is about 100, right?

 

Mixing apples and oranges will give fruit salad.  And my post-processing comments stand.  As well as how widely arcane bases might be used.

 

 

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:
And my post-processing comments stand.

Your post-processing comments will be validated when you accept the challenge I gave you. Until then, they're just FUD.

 

theusch wrote:
No. The 2300+ cycles that you are using was your number, from Chuck's ltoa() experiment with 1000000. How do you relate that to itoa or a utoa implementation?

You wouldn't ask if you had followed this thread: they're all under-optimized for common radices because they use a generic algorithm that supports uncommon ones.

 

theusch wrote:
In the other thread, my toolchain's itoa() was 341 cycles for 9999. danni's approach was 165 cycles for 9999. sparrow2's approach is about 100, right?

Right. And precisely my point: I'd like to have optimized code such as these when I use a XtoY function without thinking. That's what stdlib should be about in AVR platforms anyway.

 

Look, I'm getting tired of this. Please, let's leave it at that.

ɴᴇᴛɪᴢᴇᴎ

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

If it only needs to work up to 9999 my routine take 45 clk (it's relatively hard to find the first digit)

and for all 16 bit it can be made in 65 clk max (min is almost the same 2 less ).

 

I was looking on some  6502 code for div a byte with 10

It's correct for all numbers (I have checked it)

input in r24

output in r24

r25 change 

 

 lsr r24        ;1/2
 mov r25,r24
 lsr r24        ;1/4
 adc r24,r25    ;3/4
 lsr r24        ;3/8
 lsr r24        ;3/16
 lsr r24        ;3/32
 adc r24,r25    ;19/32
 lsr r24        ;19/64
 adc r24,r25    ;51/64
 lsr r24        ;51/128
 lsr r24        ;51/256
 lsr r24        ;51/512

It take 13 clk.

 

for the itoa it only needs to be correct up to 99 are there there anyone that know if it can be done faster ? (with a bit more sloppy fraction code)   

 

add:

fractions to the code

 

Last Edited: Wed. Sep 14, 2016 - 09:37 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

This one uses the hardware multiplier, so it's not for ATtinys. And it's not working yet, most results differ from the right one by 1 unit less, needs a less sloppy division, so it will take more cycles. Working on it.

 

8 bit input in r16. Result in r18:r17:r16 (3 digits)

 

ldi	r17, 164
fmul	r16, r17
adc	r18, r18
add	r1, r1
adc	r18, r18
andi	r18, 3
ldi	r16, 10
mul	r16, r1
mov	r17, r1
mul	r16, r0
mov	r16, r1

This incorrect version takes 14 cycles for 3 digits, less significant digit is in error by 1 unit. Sorry for no comments, but I'm a bit sleepy now, maybe tomorrow.

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

Thanks for the code guys.

There is no byte-to-string facility in stdlib, though: candidates are int-to-string and long-to-string, both signed and unsigned.

 

Even if there's probably less to be gained than with ltoa —but it's probably more commonly used, here is utoa for reference (utoa_ncheck.S):

#if	!defined (__AVR_TINY__)

#include "asmdef.h"

/* char *__utoa_ncheck (unsigned val, char *s, unsigned char radix)
   This function is the utoa() without checking the radix for 2..36 margins.
 */

#define val_lo	r24
#define val_hi	r25
#define str_lo	r22
#define radix	r20

#define counter	r21
#define digit	r26
#define sign	r27
                                                                // clock cycles
ENTRY	__utoa_ncheck
	clr	sign                                            // 1

ENTRY	__utoa_common
	X_movw	ZL, str_lo                                      // 1
	clr	counter                                         // 1

1:  ; Vanilla 16:8 quotient and remainder to pop the digit
    ; digit <- val % radix
    ; val   <- val / radix
	clr	digit                                           // 1

2:	lsl	val_lo                                          // 1
	rol	val_hi                                          // 1
	rol	digit                                           // 1
	cp	digit, radix                                    // 1
	brlo	3f                                              // 1/2
	sub	digit, radix                                    // 1
	inc	val_lo                                          // 1
3:	subi	counter, 16		; 256 / 16 == 16 loops  // 1
	brne	2b                                              // 1/2

    ; Convert the digit to ASCII...
	subi	digit, -'0'                                     // 1
	cpi	digit, '9'+1                                    // 1
	brlo	4f                                              // 1/2
	subi	digit, '0'-'a'+10                               // 1
4:  ; ... and store it to the reversed string
	st	Z+, digit                                       // 1(tiny/xmega)/2

    ; Iterate until all digits are sucked out of VAL.
	sbiw	val_lo, 0                                       // 2
	brne	1b                                              // 1/2

    ; Store the sign (if any)
	cpse	sign, __zero_reg__                              // 1/2
	st	Z+, sign                                        // 1(tiny/xmega)/2

    ; Terminate the string with '\0'
	st	Z+, __zero_reg__                                // 1(tiny/xmega)/2

    ; Reverse the string and return the original string pointer
	X_movw	r24, str_lo                                     // 1
	XJMP	_U(strrev)                                      // …

ENDFUNC

#endif	/* !__AVR_TINY__ */

 

And its front-end (stdlib.h):

/**
 \ingroup avr_stdlib

   \brief Convert an unsigned integer to a string.

   The function utoa() converts the unsigned integer value from \c val into an
   ASCII representation that will be stored under \c s.  The caller
   is responsible for providing sufficient storage in \c s.

   \note The minimal size of the buffer \c s depends on the choice of
   radix. For example, if the radix is 2 (binary), you need to supply a buffer
   with a minimal length of 8 * sizeof (unsigned int) + 1 characters, i.e. one
   character for each bit plus one for the string terminator. Using a larger
   radix will require a smaller minimal buffer size.

   \warning If the buffer is too small, you risk a buffer overflow.

   Conversion is done using the \c radix as base, which may be a
   number between 2 (binary conversion) and up to 36.  If \c radix
   is greater than 10, the next digit after \c '9' will be the letter
   \c 'a'.

   The utoa() function returns the pointer passed as \c s.
*/
#ifdef  __DOXYGEN__
extern char *utoa(unsigned int val, char *s, int radix);
#else
extern __inline__ __ATTR_GNU_INLINE__
char *utoa (unsigned int __val, char *__s, int __radix)
{
    if (!__builtin_constant_p (__radix)) {
	extern char *__utoa (unsigned int, char *, int);
	return __utoa (__val, __s, __radix);
    } else if (__radix < 2 || __radix > 36) {
	*__s = 0;
	return __s;
    } else {
	extern char *__utoa_ncheck (unsigned int, char *, unsigned char);
	return __utoa_ncheck (__val, __s, __radix);
    }
}
#endif

 

ɴᴇᴛɪᴢᴇᴎ

Last Edited: Thu. Sep 15, 2016 - 10:19 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Weird! This is an unsigned conversion, yet we have:

ENTRY	__utoa_ncheck
	clr	sign                                            // 1

And:

    ; Store the sign (if any)
	cpse	sign, __zero_reg__                              // 1/2
	st	Z+, sign                                        // 1(tiny/xmega)/2

ɴᴇᴛɪᴢᴇᴎ

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

My question was just for a part of the 16 solution (for the last 2 digits) that was why I only needed to get it correct upto 99

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

I'm not posting this code as solution to the stated problem, I'm just experimenting to get a feel for the problem and possible solutions. Better try first on a simple 8bit version. Already I've found several problems that may come up, mostly rounding errors and limitations of the AVR instruction set.

Can't load immediates on r0-r15, destination of multiplies is fixed at r1:r0, fmul can't take all regs as arguments, adci instruction doesn't exist, etc. all these cost cycles...

 

Well, for now I solved the rounding errors, this code takes an 8bit unsigned in r19 and returns correct decimal digits in r18:r17:r1 for all values 0-255

 

;multiply
ldi	r17, 164
fmul	r19, r17
adc	r18, r18

;shift
add	r0, r0
adc	r1, r1
adc	r18, r18

;rounding correction
ldi	r16, 0xFF
sub	r1, r16
sbc	r18, r16

;in case r18 was not cleared on entry
andi	r18, 3

;final multiplies for tens and units
ldi	r16, 10
mul	r16, r1
mov	r17, r1
mul	r16, r0

The algorithm is:

multiply by 1/100 * 2^14 *2 with fmul

multiply by 2 with shift, final result is value * 1/100 * 2^16 = hundreds digit

do rounding correction

multiply fractional remainder by 10 = tens digit

multiply fractional remainder by 10 = units digit

 

 

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

I guess I should warn you asap, El Tangas: I've run a quick check and it seems fmul isn't used once in the whole libC source code.

avr-libc-1.8.1$ grep -rIn fmul .
./include/avr/builtins.h:90:extern unsigned int __builtin_avr_fmul(unsigned char __a, unsigned char __b);
./include/avr/builtins.h:96:extern int __builtin_avr_fmuls(char __a, char __b);
./include/avr/builtins.h:102:extern int __builtin_avr_fmulsu(char __a, unsigned char __b);
./include/avr/builtins.h.in:90:extern unsigned int __builtin_avr_fmul(unsigned char __a, unsigned char __b);
./include/avr/builtins.h.in:96:extern int __builtin_avr_fmuls(char __a, char __b);
./include/avr/builtins.h.in:102:extern int __builtin_avr_fmulsu(char __a, unsigned char __b);

I suppose instructions must be compatible with the whole range of AVRs to make it into libC…?

ɴᴇᴛɪᴢᴇᴎ

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

netizen wrote:
I suppose instructions must be compatible with the whole range of AVRs to make it into libC…?

Or maybe not: there are mechanisms in place in asmdef.h to mimic an enhanced instruction that is not available on every chip.

For example:



/* Macro 'X_movw' extends enhanced movw instruction for classic chips.
 */
.macro	X_movw	dst,src
#if  __AVR_HAVE_MOVW__
	movw	\dst,\src
#else
  REGNO	.L__movw_dst, \dst
  .if	.L__movw_dst < 0
    .exitm
  .endif
  .if	.L__movw_dst % 1
    .err	; Invalid dst arg in X_movw macro.
    .exitm
  .endif

  REGNO	.L__movw_src, \src
  .if	.L__movw_src < 0
    .exitm
  .endif
  .if	.L__movw_src % 1
    .err	; Invalid src arg in X_movw macro.
    .exitm
  .endif
	mov	.L__movw_dst, .L__movw_src
	mov	.L__movw_dst + 1, .L__movw_src + 1
#endif
.endm

 

ɴᴇᴛɪᴢᴇᴎ

Last Edited: Thu. Sep 15, 2016 - 03:49 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

netizen wrote:
I suppose instructions must be compatible with the whole range of AVRs to make it into libC…?
There are 17 architectures and I sort of assume that the lib code must be written for the lowest common denominator. Having said that I'm not sure what the implications are for "brain dead" tiny chips? It would seem to imply you wouldn't ever be able to use anything beneath R16. I suppose some of the LibC source may have some kind of "#ifdef BRAIN_DEAD .. do something really stupid ... #else ..." ? Clearly it would seem unlikely that all AVRs would be forced to do multiplcation (say) without the use of MUL though.

 

Not sure if there bis some kind of coding rules written down for it somewhere? Perhaps people "in the know" just know what these restrictions are they passed by word of mouth?

 

Actually I'm intrigued now. I just picked a file at random:

 

http://svn.savannah.nongnu.org/v...

 

It seems the whole code is:

#if !defined(__AVR_TINY__)

so do Tiny simply not get this stuff at all?

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

Oh, c'mon, I've always wanted to use fmul for somethingcheeky. So is mul used anywhere (I don't have the source here, could yo check)? It's also not available in every AVR. Also adiw and sbiw, probably others.

 

edit: nvm, just saw the file clawson linked has mul in it. Every AVR with mul also has fmul, so...

 

edit 2: You know, just remembered, I've done this before, several years ago, for x86 assembly: https://board.flatassembler.net/topic.php?t=3924

There was even some guy claiming he invented this algorithm in 1999...

Last Edited: Thu. Sep 15, 2016 - 04:16 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

@Cliff, we cross-posted. I believe my last post answers one of your question. Have been intrigued by __AVR_TINY__ too, haven't looked into it yet… :-p

 

@El Tangas, it seems fmul is going to be ok if you can provide a "proxy" macro with alternate code for chips that don't have it, as in the movw case. I kind of found it suspicious however that it did not exist yet…

As indicated above, this was from libc-1.8.1. I guess I should run it again on 2.0.0.

 

Also relevant to this discussion: official benchmarks for libc functions (among which those of interest to us). Excerpt:

Function

  Units Avr2 Avr25 Avr4
atoi ("12345") Flash bytes
Stack bytes
MCU clocks
82 (82)
2
155
78 (78)
2
149
74 (74)
2
149
atol ("12345") Flash bytes
Stack bytes
MCU clocks
122 (122)
2
221
118 (118)
2
219
118 (118)
2
219
         
         
itoa (12345, s, 10) Flash bytes
Stack bytes
MCU clocks
110 (110)
2
879
102 (102)
2
875
102 (102)
2
875
ltoa (12345L, s, 10) Flash bytes
Stack bytes
MCU clocks
134 (134)
2
1597
126 (126)
2
1593
126 (126)
2
1593
         
         
         
sprintf_min (s, "%d", 12345) Flash bytes
Stack bytes
MCU clocks
1224 (1112)
53
1841
1092 (982)
53
1694
1088 (978)
53
1689
sprintf (s, "%d", 12345) Flash bytes
Stack bytes
MCU clocks
1614 (1502)
58
1647
1476 (1366)
58
1552
1454 (1344)
58
1547
sprintf_flt (s, "%e", 1.2345) Flash bytes
Stack bytes
MCU clocks
3228 (3116)
67
2573
2990 (2880)
67
2311
2968 (2858)
67
2311

ɴᴇᴛɪᴢᴇᴎ

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

Also relevant to the discussion: there have been talks about improving these functions on the mailing lists.

For example:

  • optimization for ltoa/printf (2011) mentions optimization for certain radices only. Also used lookup tables. Was eventually refused it seems because of code size vs speed trade-of. No one seemed to argue against optimized code for certain radices (10 in particular) on principle.
  • Speeding [u]ltoa by 35% (2012) introduced radix checks in stdlib.h (I believe has been committed)

 

ɴᴇᴛɪᴢᴇᴎ

Last Edited: Thu. Sep 15, 2016 - 04:40 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

El Tangas wrote:
edit 2: You know, just remembered, I've done this before, several years ago, for x86 assembly: https://board.flatassembler.net/... There was even some guy claiming he invented this algorithm in 1999...

It seems to me you're the right man for this job. :-)

ɴᴇᴛɪᴢᴇᴎ

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

Let me not make any assumptions on what os wrong and just give the facts (As I see them)

 

I have and atmega with a pot giving out a pulse that ranges from approx  1 to 40 hz. I have another atmeg reading the time between cycles every second. 

 

When at 40 HZ, I get a count of 91516 (my clock's prescaler is 4)

 

When I start decreasing the frequency, the timer count goes up and then around 2 Hz, the value just keeps increasing each second. It is like both cntH and cntL are never reset.

Every now and then, I get a count much larger that what it should be.

 

91516	40Hz
91516
91514
163443
146897
155983
117972
215110
215112
215114
215112
116696
170956
170959
170957
170957  20Hz
290749 <------- out of sequence
192329
192331
193355
128721 <--------
180942
180940
180940
180941
333496
426928 <------- out of sequence
426664
426669
189002
264769
579356
609680
692722
775762
858804
1007380  2 hz from now on the count just keeps increasing even though the input is still 2 hz.
1090422
1173464
1256505
1405082
1488122
1571164
1654204
1737245
1820286
1903328
2051905
2134947
2217988
2301029
2449606
2532647
2615689
2698729
2847306
2930347
3013389
3096430
3179471
3262513
3345553
3494131
3577173
3660213
3743255
3891832
3974873
4057914
4140955
4289532
4372573
4455615
4538655

 

/*
Read input pulse length using PD0
*/

#define F_CPU 16E6
#define USART_BAUDRATE 57600
#define F_BAUD_PRESCALE (((F_CPU/(USART_BAUDRATE*16UL)))-1)  // from manual

#include <avr/io.h>
#include <util/delay.h>
#include <string.h>
#include <stdlib.h>

#define lcd_SetCursor   0x80    // set cursor position
#define lcd_Clear       0x01

// Functions
void lcd_write_nibble(uint8_t);
void lcd_write_inst(uint8_t);
void lcd_write_char(uint8_t);
void lcd_write_string(uint8_t *);
void lcd_write_long(long);
void lcd_init_4d(void);

void serial_init(unsigned int BAUD_PRESCALE);
void serial_send(char* data);

void init_timer1();
int tov_check(int);

unsigned char sndtime[20];
long cntL;
long cntH;
int edge;			// first occurrence
int i;				// pointer

/******************************* Main Program Code *************************/
int main(void)
{
	lcd_init_4d();        							// initialize the LCD display for a 4-bit interface
	timer1_init();
	serial_init(F_BAUD_PRESCALE);
	while(1)
   {
		edge=1;
		while (!(PIND & (1<<PD0)))      // stay in loop while positive signal
		{
			if (edge == 1)
			{
				TCNT1=0;    // Reset Timer
				cntH=0;
				edge=0;
			}
		 	cntH=tov_check(cntH);     // Count overflows
		}
		while ((PIND & (1<<PD0)))  	 // stay in loop while negative signal
		{
		 	cntH=tov_check(cntH);
		}
		if (!(PIND & (1<<PD0)))      // wait for rising edge
		{
			cntL=TCNT1;           // store timer
			lcd_write_inst(0x01);  // clear display
			lcd_write_inst(0x80);
			_delay_ms(2);
			lcd_write_inst(lcd_SetCursor | 0x00); 	// Line 1
			lcd_write_long(cntH);
			lcd_write_inst(lcd_SetCursor | 0x40); 	// Line 2
			lcd_write_long(cntL);
			cntL=cntL+(cntH<<16);                // Turn into Long
 			ltoa(cntL,sndtime,10);
			serial_send("SS");
			serial_send(sndtime);
			serial_send("EE");
		}
		_delay_ms(1000);
	}
}

void timer1_init()
{
    TCCR1A = 0x00; 								// Normal count Mode and waveform
    TCCR1B = (1 << CS11);						// CS12 and CS10 = 1 Prescaler 4
    TCNT1 = 0;										// Initalize
}

int tov_check(int data)
{
	if (TIFR1 & (1 << TOV1))
	{
		data++;
		TIFR1 = (1 << TOV1);
	}
	return(data);
}

// LCD Setup
void lcd_init_4d(void)
{
	DDRD = 0xF0;			// 4 data lines - output
	DDRB = 0x03;			// Control Lines

	_delay_ms(100);               // initial 40 mSec delay
	lcd_write_nibble(0x30);       // reset sequence
   _delay_ms(10);
	lcd_write_nibble(0x30);
	_delay_us(200);
	lcd_write_nibble(0x30);
	_delay_us(200);  

	lcd_write_inst(0x28);  // Set interface length = 4, Display Lines, Font 5x7
	_delay_us(80);
	lcd_write_inst(0x08);  // turn display OFF
	_delay_us(80);
	lcd_write_inst(0x01);  // clear display RAM
	_delay_ms(4);
	lcd_write_inst(0x06);  // shift cursor from left to right on read/write
	_delay_us(80);
	lcd_write_inst(0x0C);  // turn the display ON
	_delay_us(80);
}                                 

void lcd_write_inst(uint8_t inst)
{
	PORTB &= ~(1<<PORTB0);    		// select the inst Register (RS low)
	lcd_write_nibble(inst);       // write the upper 4-bits of the data
	lcd_write_nibble(inst << 4);  // write the lower 4-bits of the data
	_delay_us(80);
}

void lcd_write_char(uint8_t Data)
{
	PORTB |= (1<<PORTB0);      	// select the Data Register (RS high)
	lcd_write_nibble(Data);       // write the upper 4-bits of the data
	lcd_write_nibble(Data << 4);  // write the lower 4-bits of the data
	_delay_us(80);
}

void lcd_write_nibble(uint8_t theByte)
{
	if (theByte & 1<<7)
		PORTD |= (1<<PORTD7);   //Write 1 which is the actual data
	 else
	 	PORTD &= ~(1<<PORTD7);  //Write 0
	if (theByte & 1<<6)
		PORTD |= (1<<PORTD6);
	 else
	 	PORTD &= ~(1<<PORTD6);
	if (theByte & 1<<5)
		PORTD |= (1<<PORTD5);
	 else
	 	PORTD &= ~(1<<PORTD5);
	if (theByte & 1<<4)
		PORTD |= (1<<PORTD4);
	 else
	 	PORTD &= ~(1<<PORTD4);  

   PORTB &= ~(1<<PORTB1);      // make sure E is initially low
   _delay_us(1);
	PORTB |= (1<<PORTB1);       // Enable pin high
   _delay_us(1);
  	PORTB &= ~(1<<PORTB1);      // Enable pin low
    _delay_us(1);
}

void lcd_write_string(uint8_t String[])
{
	int i = 0;                             // character counter
	while (String[i] != 0)
	{
		lcd_write_char(String[i]);
      i++;
	}
}

void lcd_write_long(long data)
{
	char st[50];
	ltoa(data,st,10);
	lcd_write_string(st);
}

// Serial setup

void serial_init(unsigned int BAUD_PRESCALE)
{
	UBRR0H = (unsigned char)(BAUD_PRESCALE >> 8); 	//Upper 8-bits baud rate, Shift Right 8 times
	UBRR0L = (unsigned char)(BAUD_PRESCALE); 	 		//lower 8-bits.
	UCSR0B = (1 << RXEN0) | (1 << TXEN0); 				// Turn on the transmission and reception circuitry
	UCSR0C = (0 << USBS0) | (3 << UCSZ00); 			// Stop Bits =1  8-bit
}

void serial_send(char *data)   // send a string
{
   while(*data != 0)
   {
		while( !(UCSR0A & (1<<UDRE0))); 						// wait for empty transmit buffer.
		UDR0 = data[i] ;
      data++;
   }
}

 

Last Edited: Sat. Sep 17, 2016 - 11:52 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I would use ISR for this kind of problems!

 

in your code you have this:

int tov_check(int data)

but you call in with a long!

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

Sorry, we've hijacked your thread with this discussion about optimizing libc. It's a bit of a mess now that you need to get back to your issue… :-/

 

ɴᴇᴛɪᴢᴇᴎ

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

I can't see how your edge detection works.

Basically, you want to sample the input and store the previous value.

curr = port_pin

if (prev ==0 and curr ==1)

{

 //rising edge

}

prev = curr

 

also, tov_check accepts an int and returns an int, but you feed it longs. With this function you want to pass by reference.

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

After a break, I found the problem. If the input pulse is long, then the code misses the positive edge and goes straight into the negative section which just increases the counter.

 

Adding this after the 1 sec delay fixed one problem.

		while ((PIND & (1<<PD0)))  	 // stay in loop while negative signal
		{
		 	cntH=tov_check(cntH);
		}

I have another part of the project which will be using an ISR and that timing is critical.

 

The input is a square wave so the edges are well defined.  However as I rotate the pot, (with a scope on the input, the wave appears to increase nice and linearly ) but I still get these large numbers appearing between valid data. I'm not sure how to look for where they are coming from.

 

Thanks Wallace

 

 

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

I've suggested how you should fix your edge detection.