Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is this C function written in poor form?

char byte_to_ascii(char value_to_convert, volatile char *converted_value) {

 if (value_to_convert < 10) {
  return (value_to_convert + 48);
 } else {
  char a = value_to_convert / 10;
  double x = fmod((double)value_to_convert, 10.0);
  char b = (char)x;
  a = a + 48;
  b = b + 48;
  *converted_value = a;
  *(converted_value+1) = b;
  return 0;
 }
}

The purpose of this function is to take an unsigned char value of 0 through 99 and return either it's ascii equivalent in the case it is 0-9 or manipulate a small global character array that can be referenced from the calling code following function completion.

I ask this question because two compilers from the same vendor interpret this code in different ways.

This code was written as a way to parse address bytes sent via RS485 into strings that can easily be passed to a send-lcd-string function.

This code is written for the PIC18 architecture (8 bit uC).

The problem is that the free/evaluation version of a particular compiler generates perfect assembly code that works while suffering a performance hit, but the paid and supposedly superior compiler generates code more efficiently at the expense of being able reference the addresses of all my byte arrays used to drive the graphics on my lcd display.

I know I'm putting lots of mud in the water by using a proprietary compiler for a less than typical architecture, but I hope someone out there has some suggestions.

Thanks.

like image 805
Nate Avatar asked Dec 04 '09 03:12

Nate


2 Answers

I would definitely avoid using floating point anything on a PIC. And I would -try not to- use any divisions. How many times do you see sending a non-ascii char to the LCD? Can you save it to the LCD's memory and then call it by it's memory position?

Here's what a divide by 10 looks like in my code, note the 17 cycles it needs to complete. Think about how long that will take, and make sure there is nothing else waiting on this.

61:                         q = d2 / 10;
 01520  90482E     mov.b [0x001c+10],0x0000
 01522  FB8000     ze 0x0000,0x0000
 01524  2000A2     mov.w #0xa,0x0004
 01526  090011     repeat #17
 01528  D88002     div.uw 0x0000,0x0004
 0152A  984F00     mov.b 0x0000,[0x001c+8]

If you do a floating point anything in your code, look in the program memory after you've compiled it, on the Symbolic tab (so you can actually read it) and look for the floating point code that will need to be included. You'll find it up near the top (depending on your code), soon(ish) after the _reset label.

Mine starts at line number 223 and memory address of 001BC with _ floatsisf, continues through several additional labels (_fpack, _divsf3, etc) and ends in _funpack, last line at 535 and memory address 0042C. If you can handle (42C-1BC = 0x270 =) 624 bytes of lost program space, great, but some chips have just 2k of space and that's not an option.

Instead of floating point, if it's possible, try to use fixed point arithmetic, in base 2.

As far as not being able to reference all the byte arrays in your LCD, have you checked to make sure that you're not trying to send a null (which is a fine address) but it get's stopped by code checking for the end of an ascii string? (it's happened to me before).

like image 107
ArielP Avatar answered Oct 09 '22 23:10

ArielP


modulo and integer division can be very very expensive. I have do not know about your particular architecture, but my guess it is expensive there as well.

If you need both, division and modulo, do one of them and get the other one by multiplication/difference.

q =p/10;
r = p - q*10;
like image 27
Anycorn Avatar answered Oct 10 '22 00:10

Anycorn