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;ちょうどは、上位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

今のところ、私は少し騙して、各テーブル(factorsおよびshifts)の先頭に追加の項目があると想定してコードを記述しました。これは厳密には必要ではありませんが、8バイトのデータを浪費するという犠牲を払ってコードを単純化します。それを取り除くのもかなり簡単ですが、私は今のところ気にしません。

いずれにせよ、分割を廃止すると、専用の分割ハードウェアがないかなりの数のローからミッドレンジのプロセッサで、これがかなり速くなります。

于 2012-02-10T12:07:33.587 に答える
1

もう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 に答える