5
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;
 }
}

此函数的目的是获取一个 0 到 99 的无符号字符值,并在它为 0-9 的情况下返回它的 ascii 等效值,或者操作一个小的全局字符数组,该数组可以在函数完成后从调用代码中引用。

我问这个问题是因为来自同一供应商的两个编译器以不同的方式解释此代码。

编写此代码是为了将通过 RS485 发送的地址字节解析为可以轻松传递给 send-lcd-string 函数的字符串。

此代码是为 PIC18 架构(8 位 uC)编写的。

问题是特定编译器的免费/评估版本会生成完美的汇编代码,虽然会受到性能影响,但付费且据称更出色的编译器会更有效地生成代码,但代价是能够引用我所有字节数组的地址用于驱动我的液晶显示器上的图形。

我知道通过使用专有编译器来构建不那么典型的架构,我在水中投入了大量的泥浆,但我希望有人能提出一些建议。

谢谢。

4

11 回答 11

7

我肯定会避免在 PIC 上使用浮点任何东西。我会 - 尽量不要 - 使用任何部门。您看到多少次向 LCD 发送非 ascii 字符?你能把它保存到液晶显示器的内存中,然后通过它的内存位置调用它吗?

这是我的代码中除以 10 的样子,注意它需要完成 17 个周期。想想这需要多长时间,并确保没有其他等待。

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]

如果您在代码中执行任何浮点操作,请在编译后查看程序内存,在 Symbolic 选项卡上(以便您可以实际阅读它)并查找需要包含的浮点代码。您会在 _reset 标签之后很快(ish)在顶部附近找到它(取决于您的代码)。

我的从第 223 行和 001BC 的内存地址开始,带有 _floatsisf,继续通过几个附加标签(_fpack、_divsf3 等)并以 _funpack 结束,最后一行在 535 和内存地址 0042C。如果您可以处理 (42C-1BC = 0x270 =) 624 字节的程序空间丢失,那很好,但有些芯片只有 2k 的空间,这不是一个选择。

如果可能,请尝试使用以 2 为底的定点算术,而不是浮点数。

至于无法引用 LCD 中的所有字节数组,您是否检查过以确保您没有尝试发送空值(这是一个很好的地址),但它被代码检查停止一个ASCII字符串?(它以前发生在我身上)。

于 2009-12-04T14:19:22.447 回答
4

我可能会这样写:

char byte_to_ascii(char value_to_convert, volatile char *converted_value)
{
 if (value_to_convert < 10) {
  return value_to_convert + '0';
 } else {
  converted_value[0] = (value_to_convert / 10) + '0';
  converted_value[1] = (value_to_convert % 10) + '0';
  return 0;
 }
}
于 2009-12-04T04:32:39.980 回答
4

模数和整数除法可能非常昂贵。我不知道你的特定架构,但我猜那里也很贵。

如果您同时需要除法和模数,请执行其中一项并通过乘法/差法获得另一项。

q =p/10;
r = p - q*10;
于 2009-12-04T04:46:20.937 回答
3

转换为浮点数、调用 fmod 并转换为整数而不是仅使用 % 运算符是不是很糟糕?我会说是的。有更多可读的方法来减慢程序以满足某些时间要求,例如在 for 循环中休眠。无论使用什么编译器,或者对汇编代码进行什么调整或其他任何东西,这都是一种高度混淆的方式来控制程序的执行速度,我称之为糟糕的形式。

如果完美的汇编代码意味着它可以正常工作,但它甚至比转换为浮点数和返回还要慢,那么使用整数并在 for 循环中休眠。

至于不完善的汇编代码,有什么问题?“以能够引用我所有字节数组的地址为代价”?看起来 char* 类型正在您的代码中工作,因此您似乎可以按照 C 标准所说的方式处理所有字节数组。有什么问题?

于 2009-12-04T04:06:54.077 回答
2

坦率地说,我会说是的..

如果您希望b成为余数,请使用 MOD 或自己滚动:

char a = value_to_convert / 10;
char b = value_to_convert - (10 * a);

除非您的值确实是浮点数,否则转换到/从浮点数永远不是做事的方式。

此外,我强烈建议坚持将数据类型明确称为“有符号”或“无符号”的约定,并在它实际上字符(字符串的一部分)时保留裸露的“字符”。您正在传递原始数据,我认为它应该是一个无符号字符(当然,假设源无符号的!)。很容易忘记某些内容是否应该签名/未签名,并且使用裸字符,您会遇到各种翻转错误。

大多数 8 位微控制器都需要永远进行乘法(而除法则需要更多时间),因此请尽量减少这些。

希望这可以帮助..

于 2009-12-04T04:32:16.530 回答
1

该代码似乎在做两件非常不同的事情,具体取决于它是否给出了 0-9 或 10-99 范围内的数字。出于这个原因,我会说这个函数写得很糟糕:我会把你的函数分成两个函数。

于 2009-12-04T04:18:05.687 回答
1

因为我们在这里讨论除以 10..

这是我的看法。它只需要简单的操作,甚至不需要宽寄存器。

unsigned char divide_by_10 (unsigned char value)
{
  unsigned char q;
  q = (value>>1) + (value>>2);
  q += (q>>4);
  q >>= 3;
  value -= (q<<3)+q+q;
  return q+((value+6)>>4);
}

干杯,尼尔斯

于 2009-12-05T10:16:00.557 回答
0

如果您在内部四处寻找,优化器通常会不时地做一些不需要的事情。

您的 convert_value 是全局值还是以编译器知道不触摸它的方式分配?

于 2009-12-04T03:59:36.833 回答
0

PIC 不喜欢做指针运算。

正如 Windows 程序员指出的那样,使用 mod 运算符(见下文。)

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;  
  char b = value_TO_convert%10;
  a = a + 48;
  b = b + 48;
  *converted_value = a;
  *(converted_value+1) = b;
  return 0;
 }
}
于 2009-12-04T04:12:57.030 回答
0

是的,我相信你的功能:
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;

}
}
is in poor form:

Don't use decimal numbers for ASCII chars, use the character, i.e. '@' instead of 0x40.
There is no need for using the fmode function.

Here is my example:
// Assuming 8-bit octet
char value;
char largeValue;
value = value_to_convert / 100;
value += '0';
converted_value[0] = value;
largeValue = value_to_convert - value * 100;
value = largeValue / 10; value += '0';
converted_value[1] = value;
largeValue = largeValue - value * 10; value += '0';
converted_value[2] = value;
converted_value[3] = '\0'; // Null terminator.

Since there are only 3 digits, I decided to unroll the loop. There are no branches to interrupt the prefetching of instructions. No floating point exceptions, just integer arithmetic.

If you leading spaces instead of zeros, you can try this:
value = (value == 0) ? ' ' : value + '0';

于 2009-12-05T21:04:13.170 回答
0

只是作为一个傻瓜,但是return来自同一函数的多个语句可以被认为是错误的形式(MISRA)。

此外,上面的一些讨论是关于永久优化的限制。有些任务必须留给编译器。然而,在这样一个极简的嵌入式环境中,这些技巧可能仍然有效。

于 2009-12-20T13:01:23.347 回答