15

我必须将一个数字分成几个数字才能在 LCD 上显示。现在我使用以下方法:

pos = 7;

do
{
    LCD_Display(pos, val % 10);
    val /= 10;
    pos--;
} while (pos >= 0 && val);

这种方法的问题是除法和模运算在 MSP430 微控制器上非常慢。这种方法是否有任何替代方法,或者不涉及除法或减少操作数量的方法?

注意:我不能使用任何库函数,例如itoa. 这些库很大,而且函数本身也很耗资源(在周期数和 RAM 使用方面)。

4

5 回答 5

13

您可以使用预定义的以 10 为底的值在循环中进行减法运算。

我的 C 有点生锈了,但是是这样的:

int num[] = { 10000000,1000000,100000,10000,1000,100,10,1 };

for (pos = 0; pos < 8; pos++) {
  int cnt = 0;
  while (val >= num[pos]) {
    cnt++;
    val -= num[pos];
  }
  LCD_Display(pos, cnt);
}
于 2012-02-10T08:26:19.660 回答
6

是的,还有另一种方式,最初是由 Terje Mathiesen 发明的(至少是 AFAIK)。你(有点)乘以倒数,而不是除以 10。当然,诀窍在于整数不能直接表示倒数。为了弥补这一点,您使用缩放整数。如果我们有浮点,我们可以提取数字,例如:

input = 123

first digit = integer(10 * (fraction(input * .1))
second digit = integer(100 * (fraction(input * .01))

...依此类推,根据需要获得尽可能多的数字。为了用整数做到这一点,我们基本上只是将它们缩放 2 32(并将每个向上四舍五入,因为我们将使用截断数学)。在 C 中,算法如下所示:

#include <stdio.h>

// here are our scaled factors
static const unsigned long long factors[] = { 
    3435973837,  // ceil((0.1 * 2**32)<<3)
    2748779070,  // ceil((0.01 * 2**32)<<6)
    2199023256,  // etc.
    3518437209,
    2814749768,
    2251799814,
    3602879702,
    2882303762,
    2305843010
};

static const char shifts[] = {
    3, // the shift value used for each factor above
    6,
    9,
    13,
    16,
    19,
    23,
    26,
    29
};

int main() { 
    unsigned input = 13754;

    for (int i=8; i!=-1; i--) {
        unsigned long long inter = input * factors[i];
        inter >>= shifts[i];
        inter &= (unsigned)-1;
        inter *= 10;
        inter >>= 32;
        printf("%u", inter);
    }
    return 0;
}

循环中的操作将直接映射到大多数 32 位处理器上的指令。您的典型乘法指令将采用 2 个 32 位输入,并产生 64 位结果,这正是我们需要的。它通常也比除法指令快很多。在典型情况下,某些操作将(或至少在某些情况下可以)在汇编语言中消失。例如,在我已经完成的地方inter &= (unsigned)-1;,在汇编语言中,您通常可以只使用存储结果的低 32 位寄存器,而忽略保存高 32 位的任何内容。同样,inter >>= 32;just 表示我们使用高 32 位寄存器中的值,而忽略低 32 位寄存器。

例如,在 x86 汇编语言中,结果如下:

    mov ebx, 9 ; maximum digits we can deal with.
    mov esi, offset output_buffer
next_digit:
    mov eax, input
    mul factors[ebx*4]
    mov cl, shifts[ebx]
    shrd eax, edx, cl
    mov edx, 10 ; overwrite edx => inter &= (unsigned)-1
    mul edx 
    add dl, '0'
    mov [esi], dl ; effectively shift right 32 bits by ignoring 32 LSBs in eax
    inc esi
    dec ebx
    jnz next_digit
    mov [esi], bl ; zero terminate the string

目前,我作弊了一点,并编写了代码,假设每个表的开头都有一个额外的项目(factorsshifts)。这不是绝对必要的,但以浪费 8 字节数据为代价简化了代码。消除它也很容易,但我暂时没有打扰。

在任何情况下,取消分区可以在相当多的缺乏专用分区硬件的中低端处理器上更快地实现这一点。

于 2012-02-10T12:07:33.587 回答
1

另一种方法是使用双重涉猎。这是一种仅通过加法和位移将二进制转换为 BCD 的方法,因此非常适合微控制器。拆分为 BCD 后,您可以轻松打印出每个数字

于 2013-08-02T15:36:51.020 回答
0

我会使用一个临时字符串,例如:

char buffer[8];
itoa(yourValue, buffer, 10);
int pos;

for(pos=0; pos<8; ++pos)
    LCD_Display(pos, buffer[pos]); /* maybe you'll need a cast here */

编辑:由于您不能使用库的 itoa,所以我认为您的解决方案已经是最好的,为您提供打开最大优化的编译。

你可以看看这个:在 C 中计算模量的最优化方法

于 2012-02-10T08:33:24.957 回答
0

这是我对完整解决方案的尝试。信用应该归功于 Guffa 提供的总体思路。这应该适用于 32 位整数,有符号或其他类型以及 0。

#include <stdlib.h>
#include <stdio.h>

#define MAX_WIDTH (10)

static unsigned int uiPosition[] = {
  1u,
  10u,
  100u,
  1000u,
  10000u,
  100000u,
  1000000u,
  10000000u,
  100000000u,
  1000000000u,
};

void uitostr(unsigned int uiSource, char* cTarget)
{
  int i, c=0;

  for( i=0; i!=MAX_WIDTH; ++i )
  {
    cTarget[i] = 0;
  }

  if( uiSource == 0 )
  {
    cTarget[0] = '0';
    cTarget[1] = '\0';
    return;
  }

  for( i=MAX_WIDTH -1; i>=0; --i )
  {
    while( uiSource >= uiPosition[i] )
    {
      cTarget[c] += 1;
      uiSource -= uiPosition[i];
    }

    if( c != 0 || cTarget[c] != 0 )
    {
      cTarget[c] += 0x30;
      c++;
    }
  }

  cTarget[c] = '\0';
}

void itostr(int iSource, char* cTarget)
{
  if( iSource < 0 )
  {
    cTarget[0] = '-';
    uitostr((unsigned int)(iSource * -1), cTarget + 1);
  }
  else
  {
    uitostr((unsigned int)iSource, cTarget);
  }
}

int main()
{
  char szStr[MAX_WIDTH +1] = { 0 };

  // signed integer
  printf("Signed integer\n");

  printf("int: %d\n", 100);
  itostr(100, szStr);
  printf("str: %s\n", szStr);

  printf("int: %d\n", -1);
  itostr(-1, szStr);
  printf("str: %s\n", szStr);

  printf("int: %d\n", 1000000000);
  itostr(1000000000, szStr);
  printf("str: %s\n", szStr);

  printf("int: %d\n", 0);
  itostr(0, szStr);
  printf("str: %s\n", szStr);

  return 0;
}
于 2012-02-10T14:42:44.700 回答