-
Hopefully this topic will get some decent replies and anyone else like me will find it in the future and find it helpful :popcorn:
I need to print a variables value on an LCD. Peters library can only print characters and strings presumably relying on itoa() rather than rewrite what already exists:
lcd_puts (const char *s)
char* itoa ( int val,
char * s,
int radix
)
So what do I do with this? I have an 8 bit value (0-100) that I need to put on the screen. Do I do something like:
lcd_puts (itoa(my_value, out_put, 4);
I think I have seen the output (char * s) setup as an array on other sites trying to explain it.
-
I don't think you want radix 4 - is that supposed to be the string length?
If so, try something like this:
char buf[5];
sprintf(buf, "%4d", my_value);
lcd_puts(buf);
-
sorry radix should have been 10. I need a buffer of 4, 3 characters plus the string termination
-
so is "s" supposed to be the buffer length? and the function returns the start address of the string?
-
The third parameter in itoa() is the base, so you would probably want to have 10 there instead of 4.
You need to have empty array for the resulting string, and give pointer to that as parameter to the itoa.
int my_value = 3;
char display_message[5] = {""};
itoa(my_value, display_message, 10);
lcd_puts(display_message);
You could also use sprintf, if you want to have leading zero's or other formatting to the number string.
int my_value = 3;
char display_message[5] = {""};
sprintf(display_message, "%d", my_value);
lcd_puts(display_message);
If you want to have it with leading zeros and 4 digit precision, you can format the sprintf like this:
sprintf(display_message, "%04d", my_value);
-
I would expect sprintf() to bring in a LOT of code whereas itoa() is fairly lightweight. In fact, I usually copy the code for itoa() right out of "The C Programming Language" (Kernighan and Ritchie) so that I don't bring in ANY library code. For memory constrained projects, you are often better off writing your own string and conversion functions. Among other things, your own string functions won't require a heap.
Just about everything you will ever need in the way of library code is in that book.
#include <stdio.h>
int mystrlen(char *s) /* return length of string s */
{
int n;
for (n = 0; *s != '\0'; s++)
n++;
return (n);
}
void reverse(char s[]) /* reverse string s in place */
{
int c, i, j;
for (i = 0, j = mystrlen(s)-1; i < j; i++, j--) {
c = s[i];
s[i] = s[j];
s[j] = c;
}
}
void itoa(int n, char s[]) /* convert n to characters in s */
{
int i, sign;
if ((sign = n) < 0) /* record sign */
n = -n;
i = 0;
do { /* generate digits in reverse order */
s[i++] = n % 10 + '0'; /* get next digit */
} while ((n /= 10) > 0); /* delete it */
if (sign < 0)
s[i++] = '-';
s[i] = '\0';
reverse(s);
}
int main(void)
{
char s[12]; /* changed length to accommodate a 32 bit integer plus a sign plus a NULL */
itoa(-123,s);
puts(s);
return (0);
}
[/size][/font]
REVISED to ANSI C You should probably have the original version of the book plus the newer ANSI version.
This code is known to work BUT... The strlen() function has to be renamed to avoid conflict with the library function so I called it mystrlen()
This is pretty short code and it doesn't use the heap.
===========================================================================================]
Be aware that this algorithm pukes on -2147483648 which is the maximum negative number. You can't do n = -n when n is the maximum negative number because it exceeds the range of the maximum positive number. I suppose there is a way to code around it and, obviously, it's been done. I just don't know if it matters in any application where we're dealing with much smaller numbers.
-
There are lightweight printf libraries, I used this one: https://github.com/mpaland/printf
-
Just about everything you will ever need in the way of library code is in that book.
K&R's code is elegant. Especially how it adapts depending on the magnitude of n, avoiding wasted operations.
I once re-implemented atoi() to avoid its modulos and divisions, which are slow on AVR ... a total of thousands of cycles if fed a large 32-bit number. The algorithm counted the value of each decimal place by iteratively subtracting progressively smaller powers of 10 from it. This was indeed tighter and much quicker for large numbers, but in hindsight I would have learned more from meditating upon K&R's algorithm ... and the debug time would have been shorter. ;D
If I have seen farther than others, it is because I was standing on the shoulders of giants. -Newton
-
Mind, using built in library functions usually results in tighter code -- it's tightly integrated with the compiler so that minimal overhead can be emitted. Here's an example from the project I'm playing with right now;
00000fcc <__itoa_ncheck>:
fcc: bb 27 eor r27, r27
fce: 4a 30 cpi r20, 0x0A ; 10
fd0: 31 f4 brne .+12 ; 0xfde <__itoa_ncheck+0x12>
fd2: 99 23 and r25, r25
fd4: 22 f4 brpl .+8 ; 0xfde <__itoa_ncheck+0x12>
fd6: bd e2 ldi r27, 0x2D ; 45
fd8: 90 95 com r25
fda: 81 95 neg r24
fdc: 9f 4f sbci r25, 0xFF ; 255
fde: 0c 94 f2 07 jmp 0xfe4 ; 0xfe4 <__utoa_common>
00000fe2 <__utoa_ncheck>:
fe2: bb 27 eor r27, r27
00000fe4 <__utoa_common>:
fe4: fb 01 movw r30, r22
fe6: 55 27 eor r21, r21
fe8: aa 27 eor r26, r26
fea: 88 0f add r24, r24
fec: 99 1f adc r25, r25
fee: aa 1f adc r26, r26
ff0: a4 17 cp r26, r20
ff2: 10 f0 brcs .+4 ; 0xff8 <__utoa_common+0x14>
ff4: a4 1b sub r26, r20
ff6: 83 95 inc r24
ff8: 50 51 subi r21, 0x10 ; 16
ffa: b9 f7 brne .-18 ; 0xfea <__utoa_common+0x6>
ffc: a0 5d subi r26, 0xD0 ; 208
ffe: aa 33 cpi r26, 0x3A ; 58
1000: 08 f0 brcs .+2 ; 0x1004 <__utoa_common+0x20>
1002: a9 5d subi r26, 0xD9 ; 217
1004: a1 93 st Z+, r26
1006: 00 97 sbiw r24, 0x00 ; 0
1008: 79 f7 brne .-34 ; 0xfe8 <__utoa_common+0x4>
100a: b1 11 cpse r27, r1
100c: b1 93 st Z+, r27
100e: 11 92 st Z+, r1
1010: cb 01 movw r24, r22
1012: 0c 94 0b 08 jmp 0x1016 ; 0x1016 <strrev>
As you can see, it's no ordinary function, it has multiple entry points! There is no preamble, saving registers or setting a stack frame, as it doesn't use either (only clobber registers (r18-r25) are used, and the buffer is external).
Heh. That's silly, and a little annoying: strrev isn't called from anywhere else, and it immediately follows this call (0x1016 is the very next instruction). Wasted jump. One of the downsides to linking premade object (I think?), it doesn't try to optimize it.
Anyway, can be better than compiled code, but clearly, also can be worse in some respects. YMMV.
One notable downside is whether the libraries include different platform support or not. ATtiny doesn't do MUL (IIRC?). So you have to use subtraction or (shift-accumulate) division.
If you're only using chars and not ints, you could save even more (only needs to parse 3 digits, not 5?), but if you need both sizes, you're stuck with the bigger one.
If you do have MUL handy, you can implement division by 10 as multiplication by 6553, keeping a few extra bits to account for the fact that it's actually 6553.6 in fixed point (which requires 24 bits; avr-gcc supports __uint24 if you wish to do it in C). I've written this before (in asm) and it's competitive in speed and size.
Tim
-
I need to print a variables value on an LCD. Peters library can only print characters and strings presumably relying on itoa() rather than rewrite what already exists:
lcd_puts (const char *s)
char* itoa ( int val,
char * s,
int radix
)
So what do I do with this? I have an 8 bit value (0-100) that I need to put on the screen.
Create a wrapper function:
void lcd_put_uint8(uint8_t value)
{
char buffer[4];
char *p = buffer + sizeof buffer;
/* Construct number from right to left, starting with the end-of-string mark. */
*(--p) = '\0';
do {
*(--p) = '0' + (value % 10);
value /= 10;
} while (value > 0);
lcd_puts(p);
}
The buffer needs to be one longer than the longest value supplied. For int32_t:
void lcd_put_int32(int32_t value)
{
char buffer[12];
char *p = buffer + sizeof buffer;
uint32_t u = (value < 0) ? -value : value;
/* Construct number from right to left, starting with the end-of-string mark. */
*(--p) = '\0';
do {
*(--p) = '0' + (u % 10);
u /= 10;
} while (u > 0);
/* Prepend sign, if necessary. */
if (value < 0)
*(--p) = '-';
lcd_puts(p);
}
Note that p initially points to past the end of the buffer, and then to the first character in the buffer, so it is important to decrement p before prepending a new character. That is what *(--p) = CHAR; does: decrements p first, then dereferences it, assigning a character there.
Because of the way they work, itoa() must use a static (global) buffer. The wrapper function, on the other hand, uses the stack for the small buffer, and only for the duration of the lcd_puts() call.
-
If you are working with a display you're going to need some fancy string printing soon anyway.
Just use snprintf. It's easy. It should be included in your toolchain.
char display[17];
int value;
snprintf(display, sizeof(display),"%03d", value);
You may be able to choose whether you want include the %f option with linker flags. Since that takes the most ROM.
If it then still doesn't fit in ROM, then maybe go find a lighter version, but I'm afraid you might have made the wrong choice somewhere leading up to this.
-
const char str1[ 3 ] = {' ',' ','1'};
..
const char str100[ 3 ] = {'1','0','0'};
const char*const pText[ 100 ] = {
str1,
..
str100,
};